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

jjatria / perl-opentelemetry / 6713140199

31 Oct 2023 10:25PM UTC coverage: 92.266% (-0.3%) from 92.537%
6713140199

push

github

jjatria
Regenerate distribution files

680 of 737 relevant lines covered (92.27%)

6.28 hits per line

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

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

4
our $VERSION = '0.010';
5

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

165
        my $filename = $INC{$module} or return;
1✔
166

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

170
        return if exists $INSTALLED{$package};
1✔
171
        $INSTALLED{$package} = {};
1✔
172

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

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

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

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

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

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

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

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

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

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

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

227
    return $loaded = 1;
×
228
}
229

230
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