Group
Extension

Log-Sentry/lib/Log/Sentry.pm

package Log::Sentry;

=head1 NAME

Log::Sentry - sending log messages to Sentry.

=cut

our $VERSION = '0.01';


=head1 SYNOPSIS


 my $raven = Log::Sentry->new(
    sentry_public_key => "public",
    sentry_secret_key => "secret",
    remote_url        => "sentry url"
 );


 $raven->message({ message => "Panic!" });

=head1 EXPORT


=cut

use strict;
use warnings;

use HTTP::Request::Common;
use LWP::UserAgent;
use JSON;
use MIME::Base64 'encode_base64';
use Time::HiRes (qw(gettimeofday));
use DateTime;
use Sys::Hostname;

=head4 new

Constructor. Use like:

    my $raven = Log::Sentry->new(
        sentry_public_key => "public",
        sentry_secret_key => "secret",
        remote_url        => "sentry url",
        sentry_version    => 3 # can be omitted
    );

=cut
sub new {
    my ( $class, %options ) = @_;

    foreach (qw(sentry_public_key sentry_secret_key remote_url)) {
        if (!exists $options{$_}) {
            die "Mandatory paramter '$_' not defined";
        }
    }
 
    my $self = {
        ua => LWP::UserAgent->new(),
        %options,
    };

    $self->{'sentry_version'} ||= 3;

    bless $self, $class;
}

=head4 message

Send message to Sentry server.

  $raven->message( { 
    'message'     => "Message", 
    'logger'      => "Name of the logger",                  # defult "root"
    'level'       => "Error level",                         # default 'error'
    'platform'    => "Platform name",                       # default 'perl',
    'culprit'     => "Module or/and function raised error", # default ""
    'tags'        => "Arrayref of tags",                    # default []
    'server_name' => "Server name where error occured",     # current host name is default
    'modules'     => "list of relevant modules",
    'extra'       => "extra params described below"
  } );

The structure of 'modules' list is:

    [
        {
            "my.module.name": "1.0"
        }
    ]

The structure of 'extra' field is:

  {
    "my_key"           => 1,
    "some_other_value" => "foo bar"
  }


=cut
sub message {
    my ( $self, $params ) = @_;
    
    my $message = $self->buildMessage( $params );
    my $stamp = gettimeofday();
    $stamp = sprintf ( "%.12g", $stamp );

    my $header_format = sprintf ( 
            "Sentry sentry_version=%s, sentry_timestamp=%s, sentry_key=%s, sentry_client=%s, sentry_secret=%s",
            $self->{sentry_version},
            time(),
            $self->{'sentry_public_key'},
            "perl_client/0.02",
            $self->{'sentry_secret_key'},
        );
    my %header = ( 'X-Sentry-Auth' => $header_format );

    my $request = POST($self->{remote_url}, %header, Content => $message);
    my $response = $self->{'ua'}->request( $request );

    return $response;
}


sub buildMessage {
    my ( $self, $params ) = @_;
 
    my $data = {
        'event_id'    => sprintf("%x%x%x", time(), time() + int(rand()), time() + int(rand())),
        'message'     => $params->{'message'},
        'timestamp'   => time(),
        'level'       => $params->{'level'} || 'error',
        'logger'      => $params->{'logger'} || 'root',
        'platform'    => $params->{'platform'} || 'perl',
        'culprit'     => $params->{'culprit'} || "",
        'tags'        => $params->{'tags'} || [],
        'server_name' => $params->{server_name}||hostname,
        'modules'     => $params->{'modules'},
        'extra'       => $params->{'extra'} || {}
    };

    my $json = JSON->new->utf8(1)->pretty(1)->allow_nonref(1);
    return $json->encode( $data );
}

1;

=head1 LICENSE AND COPYRIGHT

Copyright 2013 Danil Orlov.

This program is free software; you can redistribute it and/or modify it
under the terms of the the Artistic License (2.0). You may obtain a
copy of the full license at:

L<http://www.perlfoundation.org/artistic_license_2_0>


Powered by Groonga
Maintained by Kenichi Ishigaki <ishigaki@cpan.org>. If you find anything, submit it on GitHub.