• 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

51.65
/lib/OpenTelemetry/Integration/namespace.pm
1
package
2
    OpenTelemetry::Integration::namespace;
3
# ABSTRACT: OpenTelemetry integration for a namespace
4

5
our $VERSION = '0.011';
6

7
use strict;
1✔
8
use warnings;
1✔
9
use experimental 'signatures';
1✔
10

11
use Carp 'croak';
1✔
12
use Class::Inspector;
1✔
13
use Class::Method::Modifiers 'install_modifier';
1✔
14
use Feature::Compat::Defer;
1✔
15
use List::Util 'any';
1✔
16
use OpenTelemetry::Constants qw( SPAN_KIND_CLIENT SPAN_STATUS_ERROR SPAN_STATUS_OK );
1✔
17
use OpenTelemetry;
1✔
18
use Ref::Util 'is_arrayref';
1✔
19
use Module::Load;
1✔
20
use Devel::Peek;
1✔
21

22
use parent 'OpenTelemetry::Integration';
1✔
23

24
use constant {
25
    IS_TRACE => 0,
1✔
26
    IS_DEBUG => 0,
27
};
1✔
28

29
my ( %INSTALLED, $loaded );
30
sub uninstall ( $class ) {
×
31
    return unless $loaded;
×
32
    no strict 'refs';
1✔
33
    no warnings 'redefine';
1✔
34
    for my $package ( keys %INSTALLED ) {
×
35
        for my $sub ( keys %{ $INSTALLED{ $package } // {} } ) {
×
36
            delete $Class::Method::Modifiers::MODIFIER_CACHE{$package}{$sub};
×
37
            *{ $package . '::' . $sub } = delete $INSTALLED{$package}{$sub};
×
38
        }
39
    }
40
    undef $loaded;
×
41
    return;
×
42
}
43

44
my $parse_rules = sub ( $config ) {
45
    my ( $paths, $subroutines, $subpackages );
46

47
    if ( my @list = @{ $config->{paths} // [] } ) {
48
        $paths = '^(:?' . join( '|', map quotemeta, @list ) . ')';
49
        $paths = qr/$paths/;
50

51
        if ( IS_DEBUG ) {
52
            warn "Paths:\n";
53
            warn "- $_\n" for @list;
54
        }
55
    }
56

57
    if ( my %map = %{ $config->{subroutines} // {} } ) {
58
        while ( my ( $k, $v ) = each %map ) {
59
            @{ $subroutines->{$k} }{ @$v } = 1;
60
        }
61

62
        if ( IS_DEBUG ) {
63
            warn "Subroutines:\n";
64
            for my $k ( sort keys %{ $subroutines } ) {
65
                warn "- ${k}::$_\n" for sort keys %{ $subroutines->{$k} };
66
            }
67
        }
68
    }
69

70
    if ( my %map = %{ $config->{subpackages} // {} } ) {
71
        while ( my ( $k, $v ) = each %map ) {
72
            @{ $subpackages->{$k} }{ @$v } = 1;
73
        }
74

75
        if ( IS_DEBUG ) {
76
            warn "Subpackages:\n";
77
            for my $k ( sort keys %{ $subpackages } ) {
78
                warn "- ${k}:\n";
79
                warn "  - $_\n" for sort keys %{ $subpackages->{$k} };
80
            }
81
        }
82
    }
83

84
    ( paths => $paths, subroutines => $subroutines, subpackages => $subpackages )
85
};
86

87
sub install ( $class, %config ) {
1✔
88
    return if $loaded;
1✔
89

90
    my $package = $config{package};
1✔
91
    #     or croak 'Cannot automatically instrument OpenTelemetry without a package name';
92

93
    my %include = $parse_rules->( $config{include} );
1✔
94
    my %exclude = $parse_rules->( $config{exclude} );
1✔
95

96
    my $install_wrappers = sub ($package) {
1✔
97
        my $subs = namespace::clean->get_functions($package);
1✔
98

99
        while ( my ( $subname, $coderef ) = each %$subs ) {
×
100
            my $fullname = "${package}::$subname";
×
101

102
            # Skip functions we've already wrapped
103
            next if $INSTALLED{$package}{$subname};
×
104

105
            # If we are explicitly including this subroutine
106
            # none of the other checks matter
107
            if ( $include{subroutines}{$package}{$subname} ) {
×
108
                warn "Including $fullname explicitly" if IS_TRACE;
×
109
            }
110
            else {
111
                # Otherwise, perform all other additional checks
112
                next
113
                    # Skip packages we only included for some subs
114
                    if %{ $include{subroutines}{$package} // {} }
×
115
                    # Skip import and unimport
116
                    || $subname =~ /^(?:un)?import$/
117
                    # Skip uppercase functions
118
                    || uc($subname) eq $subname
119
                    # Skip "private" functions
120
                    || $subname =~ /^_/
121
                    # Skip subroutines we are explicitly excluding
122
                    || $exclude{subroutines}{$package}{$subname};
×
123

124
                # Skip imported functions.
125
                # See https://stackoverflow.com/a/3685262/807650
126
                if ( my $gv = Devel::Peek::CvGV($coderef) ) {
×
127
                    if ( *$gv{PACKAGE} ne $package ) {
×
128
                        warn "$package has dirty namespace ($subname)\n" if IS_TRACE;
×
129
                        next;
×
130
                    }
131
                }
132

133
                if ( defined prototype $coderef ) {
×
134
                    warn "Not wrapping $fullname because it has a prototype\n" if IS_TRACE;
×
135
                    next;
×
136
                }
137
            }
138

139
            $INSTALLED{$package}{$subname} = 1;
×
140

141
            install_modifier $package => around => $subname => sub {
142
                my ( $orig, $self, @rest ) = @_;
×
143
                OpenTelemetry->tracer_provider->tracer(
144
                    name    => __PACKAGE__,
145
                    version => $VERSION,
146
                )->in_span(
147
                    "${package}::$subname" => (
148
                        attributes => {
149
                            'code.function'  => $subname,
150
                            'code.namespace' => $package,
151
                        },
152
                    ),
153
                    sub { $self->$orig(@rest) },
×
154
                );
×
155
            };
×
156

157
            warn "Wrapped ${package}::$subname\n" if IS_TRACE;
×
158
        }
159
    };
1✔
160

161
    my $wrap = sub ($module) {
4✔
162
        return if
163
            lc $module eq $module   # pragma
4✔
164
            || $module =~ /^[0-9]/; # version
165

166
        my $filename = $INC{$module} or return;
4✔
167

168
        my $package = $module =~ s/\//::/gr;
4✔
169
        $package =~ s/\.p[ml]$//;
4✔
170

171
        return if exists $INSTALLED{$package};
4✔
172
        $INSTALLED{$package} = {};
4✔
173

174
        if ( my $data = $include{subpackages}{$package} ) {
4✔
175
            # If this package has any subpackages that we are interested
176
            # in wrapping, wrap those as well
177
            $install_wrappers->($_) for keys %$data;
×
178
        }
179

180
        # If we are specifically including any subroutine in this
181
        # package, then we cannot skip it wholesale
182
        unless ( $include{subroutines}{$package} ) {
4✔
183
            $package =~ /^::/ and do {
4✔
184
                warn "Skipping $package because it is not a package\n" if IS_TRACE;
×
185
                return;
×
186
            };
187

188
            $package =~ /^OpenTelemetry/ and do {
4✔
189
                warn "Skipping $package because it is ourselves\n" if IS_TRACE;
1✔
190
                return;
1✔
191
            };
192

193
            # TODO
194
            $package =~ /^(?:B|Exporter|Test2|Plack|XSLoader)(?:::|$)/ and do {
3✔
195
                warn "Skipping $package because it is not currently supported\n" if IS_TRACE;
2✔
196
                return;
2✔
197
            };
198

199
            $include{paths} && $filename !~ $include{paths} and do {
1✔
200
                warn "Skipping $package because it is not in include paths\n" if IS_TRACE;
×
201
                return;
×
202
            };
203

204
            $exclude{paths} && $filename =~ $exclude{paths} and do {
1✔
205
                warn "Skipping $package because it is in exclude paths\n" if IS_TRACE;
×
206
                return;
×
207
            };
208
        }
209

210
        $install_wrappers->($package);
1✔
211
    };
1✔
212

213
    $wrap->($_) for keys %INC;
1✔
214

215
    my $old_hook = ${^HOOK}{require__before};
×
216
    ${^HOOK}{require__before} = sub {
217
        my ($name) = @_;
×
218

219
        my $return;
×
220
        $return = $old_hook->($name) if $old_hook;
×
221

222
        return sub {
223
            $return->() if ref $return && reftype $return eq 'CODE';
×
224
            $wrap->($name);
×
225
        };
×
226
    };
×
227

228
    return $loaded = 1;
×
229
}
230

231
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