DBI.pm 2.9 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091
  1. package Trog::Log::DBI;
  2. use strict;
  3. use warnings;
  4. use parent qw{Log::Dispatch::DBI};
  5. use Ref::Util qw{is_arrayref};
  6. use Capture::Tiny qw{capture_merged};
  7. use POSIX qw{mktime};
  8. use POSIX::strptime qw{strptime};
  9. our ( $referer, $ua, $urchin );
  10. sub create_statement {
  11. my $self = shift;
  12. # This is a writable view. Consult schema for its behavior.
  13. my $sql = "INSERT INTO all_requests (uuid, date, ip_address, user, method, route, referer, ua, code) VALUES (?,?,?,?,?,?,?,?,?)";
  14. my $sql2 = "INSERT INTO messages (uuid, message) VALUES (?,?)";
  15. $self->{sth2} = $self->{dbh}->prepare($sql2);
  16. my $sql3 = "INSERT INTO urchin_requests (request_uuid, utm_source, utm_medium, utm_campaign, utm_term, utm_content) VALUES (?,?,?,?,?,?)";
  17. $self->{sth3} = $self->{dbh}->prepare($sql3);
  18. return $self->{dbh}->prepare($sql);
  19. }
  20. my %buffer;
  21. sub log_message {
  22. my ( $self, %params ) = @_;
  23. # Rip apart the message. If it's got any extended info, lets grab that too.
  24. my $msg = $params{message};
  25. my $message;
  26. my ( $date, $uuid, $ip, $user, $method, $code, $bytes, $route ) = $msg =~ m!^([\w|\-|:]+) \[INFO\]: RequestId ([\w|\-]+) From ([\w|\.|:]+) \|(\w+)\| (\w+) (\d+) (\d+) (.+)!;
  27. # Otherwise, let's mark it down in the "messages" table. This will be deferred until the final write.
  28. if ( !$date ) {
  29. ( $date, $uuid, $ip, $user, $message ) = $msg =~ m!^([\w|\-|:]+) \[\w+\]: RequestId ([\w|\-]+) From ([\w|\.|:]+) \|(\w+)\| (.+)!;
  30. # If we can't figure out its request, ignore the message
  31. if(length $uuid) {
  32. $buffer{$uuid} //= [];
  33. push(@{$buffer{$uuid}}, $message);
  34. }
  35. return 1;
  36. }
  37. # If this is a mangled log, forget it.
  38. return unless $date && $uuid;
  39. # 2024-01-20T22:37:41Z
  40. # Transform the date into an epoch so we can do math on it
  41. my $fmt = "%Y-%m-%dT%H:%M:%SZ";
  42. my @cracked = strptime( $date, $fmt );
  43. #XXX get a dumb warning otherwise
  44. pop @cracked;
  45. my $epoch = mktime(@cracked);
  46. # Allow callers to set quasi-tracking parameters.
  47. # We only care about this in DB context, as it's only for metrics, which are irrelevant in text logs/debugging.
  48. $referer //= 'none';
  49. $ua //= 'none';
  50. $urchin //= {};
  51. # TODO track bytes in the DB
  52. my $res = $self->{sth}->execute( $uuid, $epoch, $ip, $user, $method, $route, $referer, $ua, $code );
  53. # Dump in the accumulated messages
  54. if ( is_arrayref( $buffer{$uuid} ) && @{ $buffer{$uuid} } ) {
  55. $self->{sth2}->bind_param_array( 1, $uuid );
  56. $self->{sth2}->bind_param_array( 2, $buffer{$uuid} );
  57. $self->{sth2}->execute_array( {} );
  58. delete $buffer{$uuid};
  59. }
  60. # Record urchin data if there is any.
  61. if ( %$urchin && $urchin->{utm_source} ) {
  62. $self->{sth3}->execute( $uuid, $urchin->{utm_source}, $urchin->{utm_medium}, $urchin->{utm_campaign}, $urchin->{utm_term}, $urchin->{utm_content} );
  63. }
  64. return $res;
  65. }
  66. 1;