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

jjatria / perl-opentelemetry / 6145127567

11 Sep 2023 07:45AM UTC coverage: 92.647% (+0.5%) from 92.112%
6145127567

push

github

jjatria
Fix tracer provider on Perl 5.32

693 of 748 relevant lines covered (92.65%)

6.24 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

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;
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, and hyphens
190
and underscores will be treated indistinctly. Otherwise, names will be matched
191
literally.
192

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

197
=head2 response_headers
198

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

203
The strings will be matched case-insesitively to the header names, and hyphens
204
and underscores will be treated indistinctly. Otherwise, names will be matched
205
literally.
206

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

211
=head1 COPYRIGHT
212

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