package WebService::Rollbar::Notifier; use strict; use warnings; our $VERSION = '1.002010'; # VERSION use Carp; use Scalar::Util qw/blessed/; use Mojo::Base -base; use Mojo::UserAgent; # HTTPS for some reason fails on Solaris, based on smoker tests my $API_URL = ($^O eq 'solaris'?'http':'https').'://api.rollbar.com/api/1/'; has _ua => sub { Mojo::UserAgent->new; }; has callback => sub { ## Null by default, but not undef, because undef for callback ## means we want to block }; has environment => 'production'; has [ qw/access_token code_version framework language server/ ]; sub critical { my $self = shift; $self->notify( 'critical', @_ ); } sub error { my $self = shift; $self->notify( 'error', @_ ); } sub warning { my $self = shift; $self->notify( 'warning', @_ ); } sub info { my $self = shift; $self->notify( 'info', @_ ); } sub debug { my $self = shift; $self->notify( 'debug', @_ ); } sub _parse_message_param { my $message = shift; if (ref($message) eq 'ARRAY') { return ($message->[0], $message->[1]||{}); } else { return ($message, {} ); } } sub report_message { my ($self) = shift; my ($message, $request_params) = @_; my ($body, $custom) = _parse_message_param($message); return $self->_post( { message => { body => $body, %{ $custom }, }, }, $request_params, ); } sub notify { my $self = shift; my ( $severity, $message, $custom ) = @_; return $self->report_message( [$message, $custom], {level => $severity} ); } my @frame_optional_fields = qw/lineno colno method code context argspec varargspec keywordspec locals / ; sub _parse_exception_params { my @params = @_; my $request_params = ref $params[-1] eq 'HASH' ? pop @params : {} ; my $frames = _extract_frames(pop @params); my ($class, $message, $description) = @params; return ( { class => $class, (defined $message ? (message => $message) : ()), (defined $description ? (description => $description) : ()), }, $frames, $request_params, ); } sub _devel_stacktrace_frame_to_rollbar { my $frame = shift; return { filename => $frame->filename, lineno => $frame->line, method => $frame->subroutine, # code # context {} # varargspec: args # locals: { args => ... } } } sub _extract_frames { my $trace = shift; if ( ref($trace) eq 'ARRAY' ) { # Assume rollbar-ready frames return $trace; } if ( blessed($trace) and $trace->isa("Devel::StackTrace") ) { return [ map { _devel_stacktrace_frame_to_rollbar( $_ ) } $trace->frames ]; } return (); } sub report_trace { my $self = shift; my ($exception_data, $frames, $request_params) = _parse_exception_params(@_); return $self->_post( { trace => { exception => $exception_data, frames => $frames, } }, $request_params, ); } sub _post { my $self = shift; my ( $body, $request_optionals ) = @_; my @instance_optionals = ( map +( defined $self->$_ ? ( $_ => $self->$_ ) : () ), qw/code_version framework language server/ ); my @request_optionals = ( map +( exists $request_optionals->{$_} ? ( $_ => $request_optionals->{$_} ) : () ), qw/level context request person server client custom fingerprint uuid title/ ); my $response = $self->_ua->post( $API_URL . 'item/', json => { access_token => $self->access_token, data => { environment => $self->environment, body => $body, platform => $^O, timestamp => time(), @instance_optionals, context => scalar( caller 3 ), @request_optionals, notifier => { name => 'WebService::Rollbar::Notifier', version => $VERSION, }, }, }, ( $self->callback ? $self->callback : () ), ); return $self->callback ? (1) : $response; } ' "Most of you are familiar with the virtues of a programmer. There are three, of course: laziness, impatience, and hubris." -- Larry Wall '; __END__ =encoding utf8 =for stopwords Znet Zoffix subref www.rollbar.com. =head1 NAME WebService::Rollbar::Notifier - send messages to www.rollbar.com service =head1 SYNOPSIS =for html
use WebService::Rollbar::Notifier; my $roll = WebService::Rollbar::Notifier->new( access_token => 'YOUR_post_server_item_ACCESS_TOKEN', ); $roll->debug("Testing example stuff!", # this is some optional, abitrary data we're sending { foo => 'bar', caller => scalar(caller()), meow => { mew => { bars => [qw/1 2 3 4 5 /], }, }, }); =for html
=head1 DESCRIPTION This Perl module allows for blocking and non-blocking way to send messages to L service. =head1 HTTPS ON SOLARIS Note, this module will use HTTPS on anything but Solaris, where it will switch to use plain HTTP. Based on CPAN Testers, the module fails with HTTPS there, but since I don't have a Solaris box, I did not bother investigating this fully. Patches are more than welcome. =head1 METHODS =head2 C<< ->new() >> =for html my $roll = WebService::Rollbar::Notifier->new( access_token => 'YOUR_post_server_item_ACCESS_TOKEN', # all these are optional; defaults shown: environment => 'production', code_version => undef, framework => undef, server => undef, callback => sub {}, ); Creates and returns new C object. Takes arguments as key/value pairs: =head3 C =for html my $roll = WebService::Rollbar::Notifier->new( access_token => 'YOUR_post_server_item_ACCESS_TOKEN', ); B. This is your C project access token. =head3 C =for html my $roll = WebService::Rollbar::Notifier->new( ... environment => 'production', ); B. Takes a string B. Specifies the environment we're messaging from. B C. =head3 C =for html my $roll = WebService::Rollbar::Notifier->new( ... code_version => undef, ); B. B is not specified. Takes a string up to B<40 characters long>. Describes the version of the application code. Rollbar understands these formats: semantic version (e.g. C<2.1.12>), integer (e.g. C<45>), git SHA (e.g. C<3da541559918a808c2402bba5012f6c60b27661c>). =head3 C =for html my $roll = WebService::Rollbar::Notifier->new( ... framework => undef, ); B. B is not specified. The name of the framework your code uses =head3 C =for html my $roll = WebService::Rollbar::Notifier->new( ... server => { # Rollbar claims to understand following keys: host => "server_name", root => "/path/to/app/root/dir", branch => "branch_name", code_version => "b6437f45b7bbbb15f5eddc2eace4c71a8625da8c", } ); B. B is not specified. Takes a hashref, which is used as "server" part of every Rollbar request made by this notifier instance. See L for detailed description of supported fields. =head3 C =for html # do nothing in the callback; this is default my $roll = WebService::Rollbar::Notifier->new( ... callback => sub {}, ); # perform a blocking call my $roll = WebService::Rollbar::Notifier->new( ... callback => undef, ); # non-blocking; do something usefull in the callback my $roll = WebService::Rollbar::Notifier->new( ... callback => sub { my ( $ua, $tx ) = @_; say $tx->res->body; }, ); B. B C or a subref as a value. B a null subref. If set to C, notifications to L will be blocking, otherwise non-blocking, with the C subref called after a request completes. The subref will receive in its C<@_> the L object that performed the call and L object with the response. =head2 C<< -Enotify() >> $roll->notify('debug', "Message to send", { any => 'custom', optional => 'data', to => [qw/send goes here/], }); # if we're doing blocking calls, then return value will be # the response JSON use Data::Dumper;; $roll->callback(undef); my $response = $roll->notify('debug', "Message to send"); say Dumper( $response->res->json ); Takes two mandatory and one optional arguments. Always returns true value if we're making non-blocking calls (see C argument to constructor). Otherwise, returns the response as L object. The arguments are: =head3 First argument =for html $roll->notify('debug', ... B. Specifies the type of message to send. Valid values are C, C, C, C, and C. The module provides shorthand methods with those names to call C. =head3 Second argument =for html $roll->notify(..., "Message to send", B. Takes a string that specifies the message to send to L. =head3 Third argument =for html $roll->notify( ..., ..., { any => 'custom', optional => 'data', to => [qw/send goes here/], }); B. Takes a hashref that will be converted to JSON and sent along with the notification's message. =head2 C<< ->critical() >> =for html $roll->critical( ... ); # same as $roll->notify( 'critical', ... ); =head2 C<< ->error() >> =for html $roll->error( ... ); # same as $roll->notify( 'error', ... ); =head2 C<< ->warning() >> =for html $roll->warning( ... ); # same as $roll->notify( 'warning', ... ); =head2 C<< ->info() >> =for html $roll->info( ... ); # same as $roll->notify( 'info', ... ); =head2 C<< ->debug() >> =for html $roll->debug( ... ); # same as $roll->notify( 'debug', ... ); =for html
Methods listed below are experimental and might change in future! =head2 C<< ->report_message($message, $additional_parameters) >> Sends "message" type event to Rollbar. $roll->report_message("Message to send"); Parameters: =head3 $message B. Specifies message text to be sent. In addition to text your message can contain additional custom metadata fields. In this case C<$message> must be an arrayref, where first element is message text and second is hashref with metadata. $roll->report_message(["Message to send", { some_key => "value" }); =head3 $additional_parameters B. Takes a hashref which may contain any additional top-level fields that you want to send with your message. Full list of fields supported by Rollbar is available at L. Notable useful field is C which can be used to set severity of your message. Default level is "error". See ->notify() for list of supported levels. Other example fields supported by Rollbar include: context, request, person, server. $roll->report_message("Message to send", { context => "controller#action" }); =head2 C<< ->report_trace($exception_class,..., $frames, $additional_parameters >> Reports "trace" type event to Rollbar, which is basically an exception. =head3 $exception_class B. This is exception class name (string). It can be followed by 0 to 2 additional scalar parameters, which are interpreted as exception message and exception description accordingly. $roll->report_trace("MyException", $frames); $roll->report_trace("MyException", "Total failure in module X", $frames); $roll->report_trace("MyException", "Total failure in module X", "Description", $frames); =head3 $frames B. Contains frames from stacktrace. It can be either L object (in which case we extract frames from this object) or arrayref with frames in Rollbar format (described in L) =head3 $additional_parameters B. See L for details. Note that for exceptions default level is "error". =for html
=head1 ACCESSORS/MODIFIERS =head2 C<< ->access_token() >> =for html say 'Access token is ' . $roll->access_token; $roll->access_token('YOUR_post_server_item_ACCESS_TOKEN'); Getter/setter for C argument to C<< ->new() >>. =head2 C<< ->code_version() >> =for html say 'Code version is ' . $roll->code_version; $roll->code_version('1.42'); Getter/setter for C argument to C<< ->new() >>. =head2 C<< ->environment() >> =for html say 'Current environment is ' . $roll->environment; $roll->environment('1.42'); Getter/setter for C argument to C<< ->new() >>. =head2 C<< ->callback() >> =for html $roll->callback->(); # call current callback $roll->callback( sub { say "Foo!"; } ); Getter/setter for C argument to C<< ->new() >>. =head1 SEE ALSO Rollbar API docs: L =for html
=head1 REPOSITORY =for html
Fork this module on GitHub: L =for html
=head1 BUGS =for html
To report bugs or request features, please use L If you can't access GitHub, you can email your request to C =for html
=head1 AUTHOR =for html
=for html ZOFFIX ZOFFIX =for text Zoffix Znet =for html
=head1 LICENSE You can use and distribute this module under the same terms as Perl itself. See the C file included in this distribution for complete details. =cut