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

jjatria / perl-opentelemetry / 5884345983

16 Aug 2023 10:10PM UTC coverage: 91.763% (+10.0%) from 81.725%
5884345983

push

github

jjatria
Install integration dependencies when calculating coverage

713 of 777 relevant lines covered (91.76%)

5.75 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.001';
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
    map {
28
        "$prefix." . ( lc =~ s/-/_/gr ),
29
        is_arrayref $have->{$_} ? $have->{$_} : [ $have->{$_} ];
5✔
30
    }
31
    grep {
32
        my $k = $_;
3✔
33
        any { $k =~ $_ } @$want;
9✔
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,
56
        @{ delete $config{request_headers}  // [] };
5✔
57

58
    my @wanted_response_headers = map qr/^\Q$_\E$/i,
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;
153

154
__END__
155

156
=encoding utf8
157

158
=head1 NAME
159

160
OpenTelemetry::Integration::HTTP::Tiny - OpenTelemetry integration for HTTP::Tiny
161

162
=head1 SYNOPSIS
163

164
    use OpenTelemetry::Integration 'HTTP::Tiny';
165

166
    # Or pass options to the integration
167
    use OpenTelemetry::Integration 'HTTP::Tiny' => {
168
        request_headers  => [ ... ],
169
        response_headers => [ ... ],
170
    };
171

172
    HTTP::Tiny->new->get('https://metacpan.org');
173

174
=head1 DESCRIPTION
175

176
See L<OpenTelemetry::Integration> for more details.
177

178
Since this is a core module, it's included in the L<OpenTelemetry> core
179
distribution as well.
180

181
=head1 CONFIGURATION
182

183
=head2 request_headers
184

185
This integration can be configured to store specific request headers with
186
every generated span. In order to do so, set this key to an array reference
187
with the name of the request headers you want as strings.
188

189
The strings will be matched case-insesitively to the header names, but they
190
will only match the header name entirely.
191

192
Matching headers will be stored as span attributes under the
193
C<http.request.header> namespace, as described in
194
L<the semantic convention documentation|https://github.com/open-telemetry/semantic-conventions/blob/main/docs/http/http-spans.md#http-request-and-response-headers>.
195

196
=head2 response_headers
197

198
This integration can be configured to store specific response headers with
199
every generated span. In order to do so, set this key to an array reference
200
with the name of the response headers you want as strings.
201

202
The strings will be matched case-insesitively to the header names, but they
203
will only match the header name entirely.
204

205
Matching headers will be stored as span attributes under the
206
C<http.response.header> namespace, as described in
207
L<the semantic convention documentation|https://github.com/open-telemetry/semantic-conventions/blob/main/docs/http/http-spans.md#http-request-and-response-headers>.
208

209
=head1 COPYRIGHT
210

211
...
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

© 2026 Coveralls, Inc