| line | stmt | bran | cond | sub | pod | time | code | 
| 1 |  |  |  |  |  |  | package Log::Log4perl::Appender::Raven; | 
| 2 |  |  |  |  |  |  | $Log::Log4perl::Appender::Raven::VERSION = '0.004'; | 
| 3 | 5 |  |  | 5 |  | 23763 | use Moose; | 
|  | 0 |  |  |  |  |  |  | 
|  | 0 |  |  |  |  |  |  | 
| 4 |  |  |  |  |  |  |  | 
| 5 |  |  |  |  |  |  | use Carp; | 
| 6 |  |  |  |  |  |  | use Data::Dumper; | 
| 7 |  |  |  |  |  |  | use Sentry::Raven; | 
| 8 |  |  |  |  |  |  | use Log::Log4perl; | 
| 9 |  |  |  |  |  |  | use Devel::StackTrace; | 
| 10 |  |  |  |  |  |  |  | 
| 11 |  |  |  |  |  |  | has 'sentry_dsn' => ( is => 'ro', isa => 'Maybe[Str]' ); | 
| 12 |  |  |  |  |  |  | has 'sentry_timeout' => ( is => 'ro' , isa => 'Int' ,required => 1 , default => 1 ); | 
| 13 |  |  |  |  |  |  | has 'infect_die' => ( is => 'ro' , isa => 'Bool', default => 0 ); | 
| 14 |  |  |  |  |  |  |  | 
| 15 |  |  |  |  |  |  | has 'raven' => ( is => 'ro', isa => 'Sentry::Raven', lazy_build => 1); | 
| 16 |  |  |  |  |  |  |  | 
| 17 |  |  |  |  |  |  | # STATIC CONTEXT | 
| 18 |  |  |  |  |  |  | has 'context' => ( is => 'ro' , isa => 'HashRef', default => sub{ {}; }); | 
| 19 |  |  |  |  |  |  |  | 
| 20 |  |  |  |  |  |  | # STATIC TAGS. They will go in the global context. | 
| 21 |  |  |  |  |  |  | has 'tags' => ( is => 'ro' ,isa => 'HashRef', default => sub{ {}; }); | 
| 22 |  |  |  |  |  |  |  | 
| 23 |  |  |  |  |  |  | # Log4Perl MDC key to look for tags | 
| 24 |  |  |  |  |  |  | has 'mdc_tags' => ( is => 'ro' , isa => 'Maybe[Str]' , default => 'sentry_tags' ); | 
| 25 |  |  |  |  |  |  | # Log4perl MDC key to look for extra | 
| 26 |  |  |  |  |  |  | has 'mdc_extra' => ( is => 'ro', isa => 'Maybe[Str]' , default => 'sentry_extra' ); | 
| 27 |  |  |  |  |  |  | # Log4perl MDC key to look for user data. | 
| 28 |  |  |  |  |  |  | has 'mdc_user'  => ( is => 'ro' ,isa => 'Maybe[Str]' , default => 'sentry_user' ); | 
| 29 |  |  |  |  |  |  | # Log4perl MDC key to look for http data. | 
| 30 |  |  |  |  |  |  | has 'mdc_http' => ( is => 'ro' , isa => 'Maybe[Str]' , default => 'sentry_http' ); | 
| 31 |  |  |  |  |  |  |  | 
| 32 |  |  |  |  |  |  | my %L4P2SENTRY = ('ALL' => 'info', | 
| 33 |  |  |  |  |  |  | 'TRACE' => 'debug', | 
| 34 |  |  |  |  |  |  | 'DEBUG' => 'debug', | 
| 35 |  |  |  |  |  |  | 'INFO' => 'info', | 
| 36 |  |  |  |  |  |  | 'WARN' => 'warning', | 
| 37 |  |  |  |  |  |  | 'ERROR' => 'error', | 
| 38 |  |  |  |  |  |  | 'FATAL' => 'fatal'); | 
| 39 |  |  |  |  |  |  |  | 
| 40 |  |  |  |  |  |  | sub BUILD{ | 
| 41 |  |  |  |  |  |  | my ($self) = @_; | 
| 42 |  |  |  |  |  |  | if( $self->infect_die() ){ | 
| 43 |  |  |  |  |  |  | warn q|INFECTING SIG __DIE__ with Log4perl trickery. Ideally you should not count on that. | 
| 44 |  |  |  |  |  |  |  | 
| 45 |  |  |  |  |  |  | See perldoc Log::Log4perl::Appender::Raven, section 'CODE WIHTOUT LOG4PERL' | 
| 46 |  |  |  |  |  |  |  | 
| 47 |  |  |  |  |  |  | |; | 
| 48 |  |  |  |  |  |  |  | 
| 49 |  |  |  |  |  |  | # Infect die. This is based on http://log4perl.sourceforge.net/releases/Log-Log4perl/docs/html/Log/Log4perl/FAQ.html#73200 | 
| 50 |  |  |  |  |  |  | $SIG{__DIE__} = sub{ | 
| 51 |  |  |  |  |  |  |  | 
| 52 |  |  |  |  |  |  | ## Are we called from within log4perl at all. | 
| 53 |  |  |  |  |  |  | { | 
| 54 |  |  |  |  |  |  | my $frame_up = 0; | 
| 55 |  |  |  |  |  |  | while( my @caller = caller($frame_up++) ){ | 
| 56 |  |  |  |  |  |  | if( $caller[0] =~ /^Log::Log4perl/ ){ | 
| 57 |  |  |  |  |  |  | return; | 
| 58 |  |  |  |  |  |  | } | 
| 59 |  |  |  |  |  |  | } | 
| 60 |  |  |  |  |  |  | } | 
| 61 |  |  |  |  |  |  |  | 
| 62 |  |  |  |  |  |  |  | 
| 63 |  |  |  |  |  |  | ## warn "CALLING die Handler"; | 
| 64 |  |  |  |  |  |  | my $method = 'fatal'; | 
| 65 |  |  |  |  |  |  |  | 
| 66 |  |  |  |  |  |  | my $level_up = 1; | 
| 67 |  |  |  |  |  |  |  | 
| 68 |  |  |  |  |  |  | # In an eval, nothing is fatal: | 
| 69 |  |  |  |  |  |  | if( $^S ){ | 
| 70 |  |  |  |  |  |  | $method = 'error'; | 
| 71 |  |  |  |  |  |  | } | 
| 72 |  |  |  |  |  |  |  | 
| 73 |  |  |  |  |  |  | my ($package, $filename, $line, | 
| 74 |  |  |  |  |  |  | $subroutine, @discard )  = caller(0); | 
| 75 |  |  |  |  |  |  | # warn "CALLER PACKAGE IS $package\n"; | 
| 76 |  |  |  |  |  |  | # warn "CALLER SUBROUTINE IS $subroutine"; | 
| 77 |  |  |  |  |  |  | if( $package =~ /^Carp/ ){ | 
| 78 |  |  |  |  |  |  | # One level up please. We dont want to make Carp the culprit. | 
| 79 |  |  |  |  |  |  | # and we want to know which is the calling package (to get the logger). | 
| 80 |  |  |  |  |  |  | ($package, @discard )  = caller(1); | 
| 81 |  |  |  |  |  |  | $level_up++  ; | 
| 82 |  |  |  |  |  |  | } | 
| 83 |  |  |  |  |  |  |  | 
| 84 |  |  |  |  |  |  | my $logger = Log::Log4perl->get_logger($package || ''); | 
| 85 |  |  |  |  |  |  |  | 
| 86 |  |  |  |  |  |  | ## This will make sure the following error or | 
| 87 |  |  |  |  |  |  | ## fatal level work as usual. | 
| 88 |  |  |  |  |  |  | local $Log::Log4perl::caller_depth = | 
| 89 |  |  |  |  |  |  | $Log::Log4perl::caller_depth + $level_up ; | 
| 90 |  |  |  |  |  |  |  | 
| 91 |  |  |  |  |  |  | $logger->$method(@_); | 
| 92 |  |  |  |  |  |  |  | 
| 93 |  |  |  |  |  |  | if( $method eq 'error' ){ | 
| 94 |  |  |  |  |  |  | # Do not die. This will be catched by the enclosing eval. | 
| 95 |  |  |  |  |  |  | return undef; | 
| 96 |  |  |  |  |  |  | } | 
| 97 |  |  |  |  |  |  |  | 
| 98 |  |  |  |  |  |  | # Not in an eval, die for good. | 
| 99 |  |  |  |  |  |  | die @_; | 
| 100 |  |  |  |  |  |  | }; | 
| 101 |  |  |  |  |  |  | } | 
| 102 |  |  |  |  |  |  | } | 
| 103 |  |  |  |  |  |  |  | 
| 104 |  |  |  |  |  |  |  | 
| 105 |  |  |  |  |  |  | sub _build_raven{ | 
| 106 |  |  |  |  |  |  | my ($self) = @_; | 
| 107 |  |  |  |  |  |  |  | 
| 108 |  |  |  |  |  |  | my $dsn = $self->sentry_dsn || $ENV{SENTRY_DSN} || confess("No sentry_dsn config or SENTRY_DSN in ENV"); | 
| 109 |  |  |  |  |  |  |  | 
| 110 |  |  |  |  |  |  |  | 
| 111 |  |  |  |  |  |  | my %raven_context = %{$self->context()}; | 
| 112 |  |  |  |  |  |  | $raven_context{tags} = $self->tags(); | 
| 113 |  |  |  |  |  |  |  | 
| 114 |  |  |  |  |  |  | return Sentry::Raven->new( sentry_dsn => $dsn, | 
| 115 |  |  |  |  |  |  | timeout => $self->sentry_timeout, | 
| 116 |  |  |  |  |  |  | %raven_context | 
| 117 |  |  |  |  |  |  | ); | 
| 118 |  |  |  |  |  |  | } | 
| 119 |  |  |  |  |  |  |  | 
| 120 |  |  |  |  |  |  | sub log{ | 
| 121 |  |  |  |  |  |  | my ($self, %params) = @_; | 
| 122 |  |  |  |  |  |  |  | 
| 123 |  |  |  |  |  |  | ## Any logging within this method will be discarded. | 
| 124 |  |  |  |  |  |  | if( Log::Log4perl::MDC->get(__PACKAGE__.'-reentrance') ){ | 
| 125 |  |  |  |  |  |  | return; | 
| 126 |  |  |  |  |  |  | } | 
| 127 |  |  |  |  |  |  | Log::Log4perl::MDC->put(__PACKAGE__.'-reentrance', 1); | 
| 128 |  |  |  |  |  |  |  | 
| 129 |  |  |  |  |  |  | # use Data::Dumper; | 
| 130 |  |  |  |  |  |  | # warn Dumper(\%params); | 
| 131 |  |  |  |  |  |  |  | 
| 132 |  |  |  |  |  |  | # Look there to see what sentry expects: | 
| 133 |  |  |  |  |  |  | # http://sentry.readthedocs.org/en/latest/developer/client/index.html#building-the-json-packet | 
| 134 |  |  |  |  |  |  |  | 
| 135 |  |  |  |  |  |  | my $sentry_message = length($params{message}) > 1000 ? substr($params{message}, 0 , 1000) : $params{message}; | 
| 136 |  |  |  |  |  |  | my $sentry_logger  = $params{log4p_category}; | 
| 137 |  |  |  |  |  |  | my $sentry_level = $L4P2SENTRY{$params{log4p_level}} || 'info'; | 
| 138 |  |  |  |  |  |  |  | 
| 139 |  |  |  |  |  |  | # We are 4 levels down after the standard Log4perl caller_depth | 
| 140 |  |  |  |  |  |  | my $caller_offset = Log::Log4perl::caller_depth_offset( $Log::Log4perl::caller_depth + 4 ); | 
| 141 |  |  |  |  |  |  |  | 
| 142 |  |  |  |  |  |  | ## Stringify arguments NOW. This avoid sending huuge objects when | 
| 143 |  |  |  |  |  |  | ## Serializing this stack trace inside Sentry::Raven | 
| 144 |  |  |  |  |  |  | my $caller_frames = Devel::StackTrace->new( no_refs => 1); | 
| 145 |  |  |  |  |  |  | { | 
| 146 |  |  |  |  |  |  | ## Remove the frames from the Log4Perl layer. | 
| 147 |  |  |  |  |  |  | my @frames = $caller_frames->frames(); | 
| 148 |  |  |  |  |  |  | splice(@frames, 0, $caller_offset); | 
| 149 |  |  |  |  |  |  | $caller_frames->frames(@frames); | 
| 150 |  |  |  |  |  |  | } | 
| 151 |  |  |  |  |  |  |  | 
| 152 |  |  |  |  |  |  | my $sentry_culprit = 'main'; | 
| 153 |  |  |  |  |  |  | { | 
| 154 |  |  |  |  |  |  | my $call_depth = $caller_offset; | 
| 155 |  |  |  |  |  |  | # Go up the caller ladder until the first non eval | 
| 156 |  |  |  |  |  |  | while( my @caller_info = caller($call_depth++) ){ | 
| 157 |  |  |  |  |  |  |  | 
| 158 |  |  |  |  |  |  | # Skip evals and __ANON__ methods. | 
| 159 |  |  |  |  |  |  | # The anon method will make that compatible with the new Log::Any (>0.15) | 
| 160 |  |  |  |  |  |  | my $caller_string = $caller_info[3] || ''; | 
| 161 |  |  |  |  |  |  |  | 
| 162 |  |  |  |  |  |  | unless( ( $caller_string eq '(eval)' ) | 
| 163 |  |  |  |  |  |  | || ( scalar(reverse($caller_string)) =~ /^__NONA__/ ) | 
| 164 |  |  |  |  |  |  | #  ^ This test for the caller string to end with __ANON__ , but faster. | 
| 165 |  |  |  |  |  |  | ){ | 
| 166 |  |  |  |  |  |  | # This is good. | 
| 167 |  |  |  |  |  |  | # Subroutine name, or filename, or just main | 
| 168 |  |  |  |  |  |  | $sentry_culprit = $caller_info[3] || $caller_info[1] || 'main'; | 
| 169 |  |  |  |  |  |  | last; | 
| 170 |  |  |  |  |  |  | } | 
| 171 |  |  |  |  |  |  | } | 
| 172 |  |  |  |  |  |  | } | 
| 173 |  |  |  |  |  |  |  | 
| 174 |  |  |  |  |  |  | my $tags = {}; | 
| 175 |  |  |  |  |  |  | if( my $mdc_tags = $self->mdc_tags() ){ | 
| 176 |  |  |  |  |  |  | $tags = Log::Log4perl::MDC->get($mdc_tags) || {}; | 
| 177 |  |  |  |  |  |  | } | 
| 178 |  |  |  |  |  |  |  | 
| 179 |  |  |  |  |  |  | my $extra = {}; | 
| 180 |  |  |  |  |  |  | if( my $mdc_extra = $self->mdc_extra() ){ | 
| 181 |  |  |  |  |  |  | $extra = Log::Log4perl::MDC->get($mdc_extra) || {}; | 
| 182 |  |  |  |  |  |  | } | 
| 183 |  |  |  |  |  |  |  | 
| 184 |  |  |  |  |  |  | my $user; | 
| 185 |  |  |  |  |  |  | if( my $mdc_user = $self->mdc_user() ){ | 
| 186 |  |  |  |  |  |  | $user = Log::Log4perl::MDC->get($mdc_user); | 
| 187 |  |  |  |  |  |  | } | 
| 188 |  |  |  |  |  |  |  | 
| 189 |  |  |  |  |  |  | my $http; | 
| 190 |  |  |  |  |  |  | if( my $mdc_http = $self->mdc_http() ){ | 
| 191 |  |  |  |  |  |  | $http = Log::Log4perl::MDC->get($mdc_http); | 
| 192 |  |  |  |  |  |  | } | 
| 193 |  |  |  |  |  |  |  | 
| 194 |  |  |  |  |  |  | # OK WE HAVE THE BASIC Sentry options. | 
| 195 |  |  |  |  |  |  | $self->raven->capture_message($sentry_message, | 
| 196 |  |  |  |  |  |  | logger => $sentry_logger, | 
| 197 |  |  |  |  |  |  | level => $sentry_level, | 
| 198 |  |  |  |  |  |  | culprit => $sentry_culprit, | 
| 199 |  |  |  |  |  |  | tags => $tags, | 
| 200 |  |  |  |  |  |  | extra => $extra, | 
| 201 |  |  |  |  |  |  | Sentry::Raven->stacktrace_context( $caller_frames ), | 
| 202 |  |  |  |  |  |  | ( $user ? Sentry::Raven->user_context(%$user) : () ), | 
| 203 |  |  |  |  |  |  | ( $http ? Sentry::Raven->request_context( ( delete $http->{url} ) , %$http ) : () ) | 
| 204 |  |  |  |  |  |  | ); | 
| 205 |  |  |  |  |  |  |  | 
| 206 |  |  |  |  |  |  | Log::Log4perl::MDC->put(__PACKAGE__.'-reentrance', undef); | 
| 207 |  |  |  |  |  |  | } | 
| 208 |  |  |  |  |  |  |  | 
| 209 |  |  |  |  |  |  |  | 
| 210 |  |  |  |  |  |  | __PACKAGE__->meta->make_immutable(); | 
| 211 |  |  |  |  |  |  |  | 
| 212 |  |  |  |  |  |  |  | 
| 213 |  |  |  |  |  |  | =head1 NAME | 
| 214 |  |  |  |  |  |  |  | 
| 215 |  |  |  |  |  |  | Log::Log4perl::Appender::Raven - Append log events to your Sentry account. | 
| 216 |  |  |  |  |  |  |  | 
| 217 |  |  |  |  |  |  | =head1 BUILD STATUS | 
| 218 |  |  |  |  |  |  |  | 
| 219 |  |  |  |  |  |  | =begin html | 
| 220 |  |  |  |  |  |  |  | 
| 221 |  |  |  |  |  |  | <a href="https://travis-ci.org/jeteve/l4p-appender-raven"><img src="https://travis-ci.org/jeteve/l4p-appender-raven.svg?branch=master"></a> | 
| 222 |  |  |  |  |  |  |  | 
| 223 |  |  |  |  |  |  | =end html | 
| 224 |  |  |  |  |  |  |  | 
| 225 |  |  |  |  |  |  | =head1 WARNING(s) | 
| 226 |  |  |  |  |  |  |  | 
| 227 |  |  |  |  |  |  | This appender will send ALL the log events it receives to your | 
| 228 |  |  |  |  |  |  | Sentry DSN synchronously. If you generate a lot of logging, that can make your sentry account | 
| 229 |  |  |  |  |  |  | saturate quite quickly and your application come to a severe slowdown. | 
| 230 |  |  |  |  |  |  |  | 
| 231 |  |  |  |  |  |  | Using Log4perl appender's Threshold or L<Log::Log4perl::Filter> in your log4perl config, and | 
| 232 |  |  |  |  |  |  | experimenting a little bit is Highly Recommended. | 
| 233 |  |  |  |  |  |  |  | 
| 234 |  |  |  |  |  |  | Remember sentry is designed to record errors, so hopefully your application will | 
| 235 |  |  |  |  |  |  | not generate too many of them. | 
| 236 |  |  |  |  |  |  |  | 
| 237 |  |  |  |  |  |  | You have been warned. | 
| 238 |  |  |  |  |  |  |  | 
| 239 |  |  |  |  |  |  | =head1 SYNOPSIS | 
| 240 |  |  |  |  |  |  |  | 
| 241 |  |  |  |  |  |  | Read the L<CONFIGURATION> section, then use Log4perl just as usual. | 
| 242 |  |  |  |  |  |  |  | 
| 243 |  |  |  |  |  |  | If you are not familiar with Log::Log4perl, please check L<Log::Log4perl> | 
| 244 |  |  |  |  |  |  |  | 
| 245 |  |  |  |  |  |  | In a nutshell, here's the minimul l4p config to output anything from ERROR to Sentry: | 
| 246 |  |  |  |  |  |  |  | 
| 247 |  |  |  |  |  |  | log4perl.rootLogger=DEBUG, Raven | 
| 248 |  |  |  |  |  |  |  | 
| 249 |  |  |  |  |  |  | log4perl.appender.Raven=Log::Log4perl::Appender::Raven | 
| 250 |  |  |  |  |  |  | log4perl.appender.Raven.Threshold=ERROR | 
| 251 |  |  |  |  |  |  | log4perl.appender.Raven.sentry_dsn="https://user:key@sentry-host.com/project_id" | 
| 252 |  |  |  |  |  |  | log4perl.appender.Raven.layout=Log::Log4perl::Layout::PatternLayout | 
| 253 |  |  |  |  |  |  | log4perl.appender.Raven.layout.ConversionPattern=%X{chunk} %d %F{1} %L> %m %n | 
| 254 |  |  |  |  |  |  |  | 
| 255 |  |  |  |  |  |  |  | 
| 256 |  |  |  |  |  |  | =head1 CONFIGURATION | 
| 257 |  |  |  |  |  |  |  | 
| 258 |  |  |  |  |  |  | This is just another L<Log::Log4perl::Appender>. | 
| 259 |  |  |  |  |  |  |  | 
| 260 |  |  |  |  |  |  | =head2 Simple Configuration | 
| 261 |  |  |  |  |  |  |  | 
| 262 |  |  |  |  |  |  | The only mandatory configuration key | 
| 263 |  |  |  |  |  |  | is *sentry_dsn* which is your sentry dsn string obtained from your sentry account. | 
| 264 |  |  |  |  |  |  | See http://www.getsentry.com/ and https://github.com/getsentry/sentry for more details. | 
| 265 |  |  |  |  |  |  |  | 
| 266 |  |  |  |  |  |  | Alternatively to setting this configuration key, you can set an environment variable SENTRY_DSN | 
| 267 |  |  |  |  |  |  | with the same setting. - Not recommended - | 
| 268 |  |  |  |  |  |  |  | 
| 269 |  |  |  |  |  |  | Example: | 
| 270 |  |  |  |  |  |  |  | 
| 271 |  |  |  |  |  |  | log4perl.rootLogger=ERROR, Raven | 
| 272 |  |  |  |  |  |  |  | 
| 273 |  |  |  |  |  |  | layout_class=Log::Log4perl::Layout::PatternLayout | 
| 274 |  |  |  |  |  |  | layout_pattern=%X{chunk} %d %F{1} %L> %m %n | 
| 275 |  |  |  |  |  |  |  | 
| 276 |  |  |  |  |  |  | log4perl.appender.Raven=Log::Log4perl::Appender::Raven | 
| 277 |  |  |  |  |  |  | log4perl.appender.Raven.sentry_dsn="http://user:key@host.com/project_id" | 
| 278 |  |  |  |  |  |  | log4perl.appender.Raven.sentry_timeout=1 | 
| 279 |  |  |  |  |  |  | log4perl.appender.Raven.layout=${layout_class} | 
| 280 |  |  |  |  |  |  | log4perl.appender.Raven.layout.ConversionPattern=${layout_pattern} | 
| 281 |  |  |  |  |  |  |  | 
| 282 |  |  |  |  |  |  | =head2 Timeout | 
| 283 |  |  |  |  |  |  |  | 
| 284 |  |  |  |  |  |  | The default timeout is 1 second. Feel free to bump it up. If sending an event | 
| 285 |  |  |  |  |  |  | timesout (or if the sentry host is down or doesn't exist), a plain Perl | 
| 286 |  |  |  |  |  |  | warning will be output. | 
| 287 |  |  |  |  |  |  |  | 
| 288 |  |  |  |  |  |  | =head2 Configuration with Static Tags | 
| 289 |  |  |  |  |  |  |  | 
| 290 |  |  |  |  |  |  | You have the option of predefining a set of tags that will be send to | 
| 291 |  |  |  |  |  |  | your Sentry installation with every event. Remember Sentry tags have a name | 
| 292 |  |  |  |  |  |  | and a value (they are not just 'labels'). | 
| 293 |  |  |  |  |  |  |  | 
| 294 |  |  |  |  |  |  | Example: | 
| 295 |  |  |  |  |  |  |  | 
| 296 |  |  |  |  |  |  | ... | 
| 297 |  |  |  |  |  |  | log4perl.appender.Raven.tags.application=myproduct | 
| 298 |  |  |  |  |  |  | log4perl.appender.Raven.tags.installation=live | 
| 299 |  |  |  |  |  |  | ... | 
| 300 |  |  |  |  |  |  |  | 
| 301 |  |  |  |  |  |  | =head2 Configure and use Dynamic Tagging | 
| 302 |  |  |  |  |  |  |  | 
| 303 |  |  |  |  |  |  | Dynamic tagging is performed using the Log4Perl MDC mechanism. | 
| 304 |  |  |  |  |  |  | See L<Log::Log4perl::MDC> if you are not familiar with it. | 
| 305 |  |  |  |  |  |  |  | 
| 306 |  |  |  |  |  |  | Anywhere in your code. | 
| 307 |  |  |  |  |  |  |  | 
| 308 |  |  |  |  |  |  | ... | 
| 309 |  |  |  |  |  |  | Log::Log4perl::MDC->set('sentry_tags' , { subsystem => 'my_subsystem', ... }); | 
| 310 |  |  |  |  |  |  | $log->error("Something very wrong"); | 
| 311 |  |  |  |  |  |  | ... | 
| 312 |  |  |  |  |  |  |  | 
| 313 |  |  |  |  |  |  | Or specify which key to capture in config: | 
| 314 |  |  |  |  |  |  |  | 
| 315 |  |  |  |  |  |  | ... | 
| 316 |  |  |  |  |  |  | log4perl.appender.Raven.mdc_tags=my_sentry_tags | 
| 317 |  |  |  |  |  |  | ... | 
| 318 |  |  |  |  |  |  |  | 
| 319 |  |  |  |  |  |  |  | 
| 320 |  |  |  |  |  |  | Note that tags added this way will be added to the statically define ones, or override them in case | 
| 321 |  |  |  |  |  |  | of conflict. | 
| 322 |  |  |  |  |  |  |  | 
| 323 |  |  |  |  |  |  | Note: Tags are meant to categorize your Sentry events and will be displayed | 
| 324 |  |  |  |  |  |  | in the Sentry GUI like any other category. | 
| 325 |  |  |  |  |  |  |  | 
| 326 |  |  |  |  |  |  | =head2 Configure and use User Data | 
| 327 |  |  |  |  |  |  |  | 
| 328 |  |  |  |  |  |  | Sentry supports structured user data that can be added to your event. | 
| 329 |  |  |  |  |  |  | User data works a bit like the tags, except only three keys are supported: | 
| 330 |  |  |  |  |  |  |  | 
| 331 |  |  |  |  |  |  | id, username and email. See L<Sentry::Raven> (capture_user) for a description of those keys. | 
| 332 |  |  |  |  |  |  |  | 
| 333 |  |  |  |  |  |  |  | 
| 334 |  |  |  |  |  |  | In your code: | 
| 335 |  |  |  |  |  |  |  | 
| 336 |  |  |  |  |  |  | ... | 
| 337 |  |  |  |  |  |  | Log::Log4perl::MDC->set('sentry_user' , { id => '123' , email => 'jeteve@cpan.org', username => 'jeteve' }); | 
| 338 |  |  |  |  |  |  | $log->error("Something very wrong"); | 
| 339 |  |  |  |  |  |  | ... | 
| 340 |  |  |  |  |  |  |  | 
| 341 |  |  |  |  |  |  |  | 
| 342 |  |  |  |  |  |  | Or specify the MDC key to capture in Config: | 
| 343 |  |  |  |  |  |  |  | 
| 344 |  |  |  |  |  |  | ... | 
| 345 |  |  |  |  |  |  | log4perl.appender.Raven.mdc_user=my_sentry_user | 
| 346 |  |  |  |  |  |  | ... | 
| 347 |  |  |  |  |  |  |  | 
| 348 |  |  |  |  |  |  |  | 
| 349 |  |  |  |  |  |  | =head2 Configure and use HTTP Request data. | 
| 350 |  |  |  |  |  |  |  | 
| 351 |  |  |  |  |  |  | Sentry support HTTP Request structured data that can be added to your event. | 
| 352 |  |  |  |  |  |  | HTTP Data work a bit like tags, except only a number of keys are supported: | 
| 353 |  |  |  |  |  |  |  | 
| 354 |  |  |  |  |  |  | url, method, data, query_string, cookies, headers, env | 
| 355 |  |  |  |  |  |  |  | 
| 356 |  |  |  |  |  |  | See L<Sentry::Raven> (capture_request) or interface 'Http' in L<http://sentry.readthedocs.org/en/latest/developer/interfaces/index.html> | 
| 357 |  |  |  |  |  |  | for a full description of those keys. | 
| 358 |  |  |  |  |  |  |  | 
| 359 |  |  |  |  |  |  | In your code: | 
| 360 |  |  |  |  |  |  |  | 
| 361 |  |  |  |  |  |  | ... | 
| 362 |  |  |  |  |  |  | Log::Log4perl::MDC->set('sentry_http' , { url => 'http://www.example.com' , method => 'GET' , ... }); | 
| 363 |  |  |  |  |  |  | $log->error("Something very wrong"); | 
| 364 |  |  |  |  |  |  | ... | 
| 365 |  |  |  |  |  |  |  | 
| 366 |  |  |  |  |  |  | Or specify the MDC key to capture in Config: | 
| 367 |  |  |  |  |  |  |  | 
| 368 |  |  |  |  |  |  | ... | 
| 369 |  |  |  |  |  |  | log4perl.appender.Raven.mdc_http=my_sentry_http | 
| 370 |  |  |  |  |  |  | ... | 
| 371 |  |  |  |  |  |  |  | 
| 372 |  |  |  |  |  |  | =head2 Configure and use Dynamic Extra | 
| 373 |  |  |  |  |  |  |  | 
| 374 |  |  |  |  |  |  | Sentry allows you to specify any data (as a Single level HashRef) that will be stored with the Event. | 
| 375 |  |  |  |  |  |  |  | 
| 376 |  |  |  |  |  |  | It's very similar to dynamic tags, except its not tags. | 
| 377 |  |  |  |  |  |  |  | 
| 378 |  |  |  |  |  |  | Then anywere in your code: | 
| 379 |  |  |  |  |  |  |  | 
| 380 |  |  |  |  |  |  | ... | 
| 381 |  |  |  |  |  |  | Log::Log4perl::MDC->set('my_sentry_extra' , { session_id => ... , ...  }); | 
| 382 |  |  |  |  |  |  | $log->error("Something very wrong"); | 
| 383 |  |  |  |  |  |  | ... | 
| 384 |  |  |  |  |  |  |  | 
| 385 |  |  |  |  |  |  |  | 
| 386 |  |  |  |  |  |  | Or specify MDC key to capture in config: | 
| 387 |  |  |  |  |  |  |  | 
| 388 |  |  |  |  |  |  | ... | 
| 389 |  |  |  |  |  |  | log4perl.appender.Raven.mdc_extra=my_sentry_extra | 
| 390 |  |  |  |  |  |  | ... | 
| 391 |  |  |  |  |  |  |  | 
| 392 |  |  |  |  |  |  | =head2 Configuration with a Static Context. | 
| 393 |  |  |  |  |  |  |  | 
| 394 |  |  |  |  |  |  | You can use lines like: | 
| 395 |  |  |  |  |  |  |  | 
| 396 |  |  |  |  |  |  | log4perl.appender.Raven.context.platform=myproduct | 
| 397 |  |  |  |  |  |  |  | 
| 398 |  |  |  |  |  |  | To define static L<Sentry::Raven> context. The list of context keys supported is not very | 
| 399 |  |  |  |  |  |  | long, and most of them are defined dynamically when you use this package anyway. | 
| 400 |  |  |  |  |  |  |  | 
| 401 |  |  |  |  |  |  | See L<Sentry::Raven> for more details. | 
| 402 |  |  |  |  |  |  |  | 
| 403 |  |  |  |  |  |  | =head1 USING Log::Any | 
| 404 |  |  |  |  |  |  |  | 
| 405 |  |  |  |  |  |  | This is tested to work with Log::Any just the same way it works when you use Log4perl directly. | 
| 406 |  |  |  |  |  |  |  | 
| 407 |  |  |  |  |  |  | =head1 CODE WITHOUT LOG4PERL | 
| 408 |  |  |  |  |  |  |  | 
| 409 |  |  |  |  |  |  | Warning: Experimental feature. | 
| 410 |  |  |  |  |  |  |  | 
| 411 |  |  |  |  |  |  | If your code, or some of its dependencies is not using Log4perl, you might want | 
| 412 |  |  |  |  |  |  | to consider infecting the __DIE__ pseudo signal with some amount of trickery to have die (and Carp::confess/croak) | 
| 413 |  |  |  |  |  |  | calls go through log4perl. | 
| 414 |  |  |  |  |  |  |  | 
| 415 |  |  |  |  |  |  | This appender makes that easy for you, and provides the 'infect_die' configuration property | 
| 416 |  |  |  |  |  |  | to do so: | 
| 417 |  |  |  |  |  |  |  | 
| 418 |  |  |  |  |  |  | ... | 
| 419 |  |  |  |  |  |  | log4perl.appender.Raven.infect_die=1 | 
| 420 |  |  |  |  |  |  | ... | 
| 421 |  |  |  |  |  |  |  | 
| 422 |  |  |  |  |  |  | This is heavily inspired by L<https://metacpan.org/pod/Log::Log4perl::FAQ#My-program-already-uses-warn-and-die-.-How-can-I-switch-to-Log4perl> | 
| 423 |  |  |  |  |  |  |  | 
| 424 |  |  |  |  |  |  | While this can be convenient to quickly implement this in a non-log4perl aware piece of software, you | 
| 425 |  |  |  |  |  |  | are strongly encourage not to use this feature and pepper your call with appropriate Log4perl calls. | 
| 426 |  |  |  |  |  |  |  | 
| 427 |  |  |  |  |  |  | =head1 SEE ALSO | 
| 428 |  |  |  |  |  |  |  | 
| 429 |  |  |  |  |  |  | L<Sentry::Raven> , L<Log::Log4perl>, L<Log::Any> , L<Log::Any::Adapter::Log4perl> | 
| 430 |  |  |  |  |  |  |  | 
| 431 |  |  |  |  |  |  | =head1 AUTHOR | 
| 432 |  |  |  |  |  |  |  | 
| 433 |  |  |  |  |  |  | Jerome Eteve jeteve@cpan.com | 
| 434 |  |  |  |  |  |  |  | 
| 435 |  |  |  |  |  |  | =cut |