File Coverage

blib/lib/OpenTelemetry/Instrumentation.pm
Criterion Covered Total %
statement 80 80 100.0
branch 18 24 75.0
condition 10 18 55.5
subroutine 14 16 87.5
pod 2 3 66.6
total 124 141 87.9


line stmt bran cond sub pod time code
1             package OpenTelemetry::Instrumentation;
2             # ABSTRACT: Top-level interface for OpenTelemetry instrumentations
3              
4             our $VERSION = '0.033';
5              
6 3     3   224058 use strict;
  3         6  
  3         120  
7 3     3   15 use warnings;
  3         8  
  3         181  
8 3     3   557 use experimental 'signatures';
  3         4912  
  3         36  
9              
10 3     3   1060 use Feature::Compat::Try;
  3         425  
  3         22  
11 3     3   243 use List::Util 'uniqstr';
  3         7  
  3         240  
12 3     3   614 use Module::Runtime ();
  3         2352  
  3         112  
13 3         26 use Module::Pluggable search_path => [qw(
14             OpenTelemetry::Instrumentation
15             OpenTelemetry::Integration
16 3     3   1543 )];
  3         36052  
17 3     3   374 use Scalar::Util 'blessed';
  3         11  
  3         216  
18 3     3   618 use Ref::Util qw( is_coderef is_hashref is_arrayref );
  3         2873  
  3         223  
19 3     3   521 use OpenTelemetry::Common ();
  3         9  
  3         3933  
20              
21             my $logger = OpenTelemetry::Common::internal_logger;
22              
23             # To be overriden by instrumentations
24       0 1   sub dependencies { }
25       0 0   sub uninstall { } # experimental
26              
27             my %REGISTRY;
28             my sub find_instrumentations {
29 7     7   16 my $class = shift;
30 7 100       25 return if %REGISTRY; # Runs once and caches results
31              
32             # Inlined from OpenTelemetry::Common to read Perl-specific config
33 1   50     7 my $legacy_support = $ENV{OTEL_PERL_USE_LEGACY_INSTRUMENTATIONS} // 1;
34 1 50       7 $legacy_support
    50          
35             = $legacy_support =~ /^true$/i ? 1
36             : $legacy_support =~ /^false$/i ? 0
37             : $legacy_support;
38              
39             # We sort the plugins so that we prefer the Instrumentation namespace
40 1         7 for ( sort $class->plugins ) {
41 4 50 66     24 last if /^OpenTelemetry::Integration::/ && !$legacy_support;
42 4   33     32 $REGISTRY{ s/^OpenTelemetry::(?:Instrumentation|Integration):://r } ||= $_
43             }
44              
45 1         3 return;
46             }
47              
48 6     6 1 2647 sub for_package ($class, $package, @) {
  6         15  
  6         13  
  6         11  
49 6         20 find_instrumentations($class);
50 6   100     40 $REGISTRY{$package // ''};
51             }
52              
53             my @installed;
54 5     5   63 sub import ( $class, @args ) {
  5         12  
  5         14  
  5         10  
55 5 100       20 return unless @args;
56              
57 4   66     28 my $all = $args[0] =~ /^[:-]all$/ && shift @args;
58              
59 4         9 my %configuration;
60 4         17 while ( my $key = shift @args ) {
61 2 50 33     26 my $options = is_hashref($args[0]) || is_arrayref($args[0])
62             ? shift @args : {};
63              
64             # Legacy namespace support. If we are loading an integration
65             # by name which does not exist in INC in the new namespace,
66             # but does exist in the legacy namespace, we use the legacy
67             # name instead.
68 2         14 my $instrumentation = $class->for_package($key);
69              
70 2 100       7 unless ( $instrumentation ) {
71 1         13 $logger->warn(
72             "Unable to load OpenTelemetry instrumentation for $key: Can't locate any suitable module in \@INC (you may need to install OpenTelemetry::Instrumentation::$key) (\@INC entries checked: @INC)",
73             );
74 1         73 next;
75             }
76              
77 1         6 $configuration{$instrumentation} = $options;
78             }
79              
80 4 100       14 if ($all) {
81 1         5 find_instrumentations($class);
82 1   50     15 $configuration{$_} //= {} for values %REGISTRY;
83             }
84              
85 4         15 for my $package ( keys %configuration ) {
86 5         342 try {
87 5         25 $logger->tracef('Loading %s', $package);
88              
89 5         534 Module::Runtime::require_module($package);
90              
91             # We only load dependencies if we are not loading every module
92 4 100       65 unless ($all) {
93 1         13 Module::Runtime::require_module($_) for $package->dependencies;
94             }
95              
96 4         77087 my $config = $configuration{ $package };
97 4 50       55 my $ok = $package->install( is_hashref $config ? %$config : @$config );
98              
99 4 100       357 if ($ok) {
100 1         12 push @installed, $package;
101             }
102             else {
103 3         23 $logger->tracef("$package did not install itself");
104             }
105              
106             }
107             catch ($e) {
108             # Just a warning, if we're loading everything then
109             # we shouldn't cause chaos just because something
110             # doesn't happen to be available.
111 1         252 $logger->warnf('Unable to load %s: %s', $package, $e);
112             }
113             }
114             }
115              
116 5     5   14123 sub unimport ( $class, @args ) {
  5         16  
  5         9  
  5         8  
117 5 50       26 @args = @installed unless @args;
118 5         25 $_->uninstall for @args;
119 5         17 return;
120             }
121              
122             1;