File Coverage

blib/lib/Plack/Middleware/XRay.pm
Criterion Covered Total %
statement 50 52 96.1
branch 18 22 81.8
condition 2 2 100.0
subroutine 10 10 100.0
pod 1 2 50.0
total 81 88 92.0


line stmt bran cond sub pod time code
1             package Plack::Middleware::XRay;
2              
3 5     5   348365 use 5.012000;
  5         46  
4 5     5   22 use strict;
  5         7  
  5         94  
5 5     5   22 use warnings;
  5         9  
  5         126  
6 5     5   369 use parent "Plack::Middleware";
  5         232  
  5         20  
7              
8 5     5   11606 use AWS::XRay qw/ capture_from /;
  5         118430  
  5         299  
9 5     5   39 use Time::HiRes ();
  5         9  
  5         2377  
10              
11             our $VERSION = "0.05";
12             our $TRACE_HEADER_NAME = "X-Amzn-Trace-ID";
13             (my $trace_header_key = uc("HTTP_${TRACE_HEADER_NAME}")) =~ s/-/_/g;
14              
15             sub call {
16 1016     1016 1 1195796 my ($self, $env) = @_;
17              
18 1016         1420 local $AWS::XRay::SAMPLER = $AWS::XRay::SAMPLER;
19 1016 100       2427 if (ref $self->{sampler} eq "CODE") {
20 9     9   43 $AWS::XRay::SAMPLER = sub { $self->{sampler}->($env) };
  9         128  
21             }
22             else {
23 1007   100     3761 AWS::XRay->sampling_rate($self->{sampling_rate} // 1);
24             }
25              
26 1016 100       5624 if ($self->{response_filter}) {
27 4         12 AWS::XRay->auto_flush(0);
28             }
29              
30 1016         2428 my $t0 = [ Time::HiRes::gettimeofday ];
31             my $res = capture_from $env->{$trace_header_key}, $self->{name}, sub {
32 1016     1016   50739 my $segment = shift;
33              
34             # fill annotations and metadata
35 1016         1529 for my $key (qw/ annotations metadata /) {
36 2032         3416 my $code = $self->{"${key}_builder"};
37 2032 100       3939 next unless ref $code eq "CODE";
38             $segment->{$key} = {
39 6 50       18 %{$self->{$key} || {}},
40 6         18 %{$code->($env)},
  6         16  
41             }
42             }
43              
44             # HTTP request info
45             $segment->{http} = {
46             request => {
47             method => $env->{REQUEST_METHOD},
48             url => url($env),
49             client_ip => $env->{REMOTE_ADDR},
50             user_agent => $env->{HTTP_USER_AGENT},
51             },
52 1016         1766 };
53              
54             # Run app
55 1016         1430 my $res = eval {
56 1016         2093 $self->app->($env);
57             };
58 1016         3041768 my $error = $@;
59 1016 50       1737 if ($error) {
60 0         0 warn $error;
61 0         0 $res = [
62             500,
63             ["Content-Type", "text/plain"],
64             ["Internal Server Error"],
65             ];
66             }
67              
68             # HTTP response info
69 1016         2124 $segment->{http}->{response}->{status} = $res->[0];
70 1016 50       2259 my $status_key =
    50          
    100          
71             $res->[0] >= 500 ? "fault"
72             : $res->[0] == 429 ? "throttle"
73             : $res->[0] >= 400 ? "error"
74             : undef;
75 1016 100       1502 $segment->{$status_key} = Types::Serialiser::true if $status_key;
76              
77 1016         3827 return $res;
78 1016         4961 };
79              
80 1016 100       17026 if (my $func = $self->{response_filter}) {
81 4         20 my $elapsed = Time::HiRes::tv_interval($t0);
82 4 100       89 $func->($env, $res, $elapsed) && AWS::XRay->sock->flush();
83 4         128 AWS::XRay->sock->close();
84             }
85 1016         4176 return $res;
86             }
87              
88             sub url {
89 1016     1016 0 1138 my $env = shift;
90             return sprintf(
91             "%s://%s%s",
92             $env->{"psgi.url_scheme"},
93             $env->{HTTP_HOST},
94             $env->{REQUEST_URI},
95 1016         5520 );
96             }
97              
98             1;
99             __END__