• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

jjatria / perl-opentelemetry / 6713428868

31 Oct 2023 11:06PM UTC coverage: 92.809% (+0.5%) from 92.266%
6713428868

push

github

jjatria
Bump version

684 of 737 relevant lines covered (92.81%)

6.34 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

100.0
/lib/OpenTelemetry/Integration/HTTP/Tiny.pm
1
package OpenTelemetry::Integration::HTTP::Tiny;
2
# ABSTRACT: OpenTelemetry integration for HTTP::Tiny
3

4
our $VERSION = '0.011';
5

6
use strict;
2✔
7
use warnings;
2✔
8
use experimental 'signatures';
2✔
9

10
use Class::Inspector;
2✔
11
use Class::Method::Modifiers 'install_modifier';
2✔
12
use Feature::Compat::Defer;
2✔
13
use List::Util 'any';
2✔
14
use OpenTelemetry::Constants qw( SPAN_KIND_CLIENT SPAN_STATUS_ERROR SPAN_STATUS_OK );
2✔
15
use OpenTelemetry::Context;
2✔
16
use OpenTelemetry::Trace;
2✔
17
use OpenTelemetry;
2✔
18
use Ref::Util 'is_arrayref';
2✔
19
use Syntax::Keyword::Dynamically;
2✔
20

21
use parent 'OpenTelemetry::Integration';
2✔
22

23
sub dependencies { 'HTTP::Tiny' }
2✔
24

25
my sub get_headers ( $have, $want, $prefix ) {
12✔
26
    return unless @$want;
12✔
27

28
    map {
29
        my ( $k, $v ) = ( $_->[0], $have->{ $_->[1] } );
5✔
30
        "$prefix.$k" => is_arrayref $v ? $v : [ $v ]
5✔
31
    }
32
    grep { my $k = $_->[0]; any { $k =~ $_ } @$want }
9✔
33
    map { [ lc tr/-/_/r, $_ ] }
3✔
34
    keys %$have;
35
}
36

37
my ( $original, $loaded );
38

39
sub uninstall ( $class ) {
6✔
40
    return unless $original;
6✔
41
    no strict 'refs';
2✔
42
    no warnings 'redefine';
2✔
43
    delete $Class::Method::Modifiers::MODIFIER_CACHE{'HTTP::Tiny'}{request};
5✔
44
    *{'HTTP::Tiny::request'} = $original;
5✔
45
    undef $loaded;
5✔
46
    return;
5✔
47
}
48

49
sub install ( $class, %config ) {
6✔
50
    return if $loaded;
6✔
51
    return unless Class::Inspector->loaded('HTTP::Tiny');
6✔
52

53
    require URI;
5✔
54

55
    my @wanted_request_headers = map qr/^\Q$_\E$/i, map tr/-/_/r,
56
        @{ delete $config{request_headers}  // [] };
5✔
57

58
    my @wanted_response_headers = map qr/^\Q$_\E$/i, map tr/-/_/r,
59
        @{ delete $config{response_headers} // [] };
5✔
60

61
    $original = \&HTTP::Tiny::request;
5✔
62
    install_modifier 'HTTP::Tiny' => around => request => sub {
63
        my ( $code, $self, $method, $url, $options ) = @_;
4✔
64

65
        my $uri = URI->new("$url");
4✔
66

67
        $uri->userinfo('REDACTED:REDACTED') if $uri->userinfo;
4✔
68

69
        my $span = OpenTelemetry->tracer_provider->tracer(
70
            name    => __PACKAGE__,
71
            version => $VERSION,
72
        )->create_span(
73
            name       => $method,
74
            kind       => SPAN_KIND_CLIENT,
75
            attributes => {
76
                # As per https://github.com/open-telemetry/semantic-conventions/blob/main/docs/http/http-spans.md
77
                'http.request.method'      => $method,
78
                'network.protocol.name'    => 'http',
79
                'network.protocol.version' => '1.1',
80
                'network.transport'        => 'tcp',
81
                'server.address'           => $uri->host,
82
                'server.port'              => $uri->port,
83
                'url.full'                 => "$uri", # redacted
84
                'user_agent.original'      => $self->agent,
85

86
                # This does not include auto-generated headers
87
                # Capturing those would require to hook into the
88
                # handle's write_request method
89
                get_headers(
90
                    $self->{default_headers}, # Apologies to the encapsulation gods
91
                    \@wanted_request_headers,
92
                    'http.request.header'
93
                ),
94

95
                get_headers(
96
                    $options->{headers},
97
                    \@wanted_request_headers,
98
                    'http.request.header'
99
                ),
100

101
                # Request body can be generated with a data_callback
102
                # parameter, in which case we don't set this attribute
103
                # Setting it would likely involve us hooking into the
104
                # handle's write_body method
105
                $options->{content}
106
                    ? ( 'http.request.body.size' => length $options->{content} )
107
                    : (),
4✔
108
            },
109
        );
110

111
        dynamically OpenTelemetry::Context->current
4✔
112
            = OpenTelemetry::Trace->context_with_span($span);
113

114
        defer { $span->end }
4✔
115

116
        my $res = $self->$code( $method, $url, $options );
4✔
117
        $span->set_attribute( 'http.response.status_code' => $res->{status} );
4✔
118

119
        # TODO: this should include retries
120
        $span->set_attribute( 'http.resend_count' => scalar @{ $res->{redirects} } )
1✔
121
            if $res->{redirects};
4✔
122

123
        my $length = $res->{headers}{'content-length'};
4✔
124
        $span->set_attribute( 'http.response.body.size' => $length )
4✔
125
            if defined $length;
126

127
        if ( $res->{success} ) {
4✔
128
            $span->set_status( SPAN_STATUS_OK );
2✔
129
        }
130
        else {
131
            my $description = $res->{status} == 599
132
                ? ( $res->{content} // '' )
133
                : $res->{status};
2✔
134

135
            $span->set_status( SPAN_STATUS_ERROR, $description );
2✔
136
        }
137

138
        $span->set_attribute(
139
            get_headers(
140
                $res->{headers},
141
                \@wanted_response_headers,
4✔
142
                'http.response.header'
143
            )
144
        );
145

146
        return $res;
4✔
147
    };
5✔
148

149
    return $loaded = 1;
5✔
150
}
151

152
1;
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc