File Coverage

blib/lib/OpenTelemetry/Attributes.pm
Criterion Covered Total %
statement 54 54 100.0
branch 12 12 100.0
condition 8 9 88.8
subroutine 15 15 100.0
pod 2 2 100.0
total 91 92 98.9


line stmt bran cond sub pod time code
1 3     3   13657 use Object::Pad ':experimental(init_expr)';
  3         6  
  3         34  
2             # ABSTRACT: A class encapsulating attribute validation for OpenTelemetry
3              
4             package OpenTelemetry::Attributes;
5              
6             our $VERSION = '0.033';
7              
8             class OpenTelemetry::AttributeMap {
9 3     3   2309 use Log::Any;
  3         24360  
  3         19  
10 3     3   1517 use OpenTelemetry::Common ();
  3         13  
  3         252  
11              
12             my $logger = OpenTelemetry::Common::internal_logger;
13              
14 3     3   25 use List::Util qw( any pairs );
  3         6  
  3         286  
15 3     3   36 use Ref::Util qw( is_hashref is_arrayref );
  3         6  
  3         200  
16 3     3   2575 use Storable 'dclone';
  3         17505  
  3         4046  
17              
18             field $max_fields :param = undef;
19             field $max_field_length :param = undef;
20 21     21   35 field $dropped_fields :reader = 0;
21 21         86 field $data = {};
22              
23             ADJUSTPARAMS ($params) {
24             $self->set( %{ delete $params->{data} // {} } );
25             }
26              
27             method $validate_attribute_value ( $value ) {
28             # Attribute values cannot be undefined but logging this is noisy
29             return unless defined $value;
30              
31             if ( is_arrayref $value ) {
32             if ( any { ref } @$value ) {
33             $logger->trace('Attribute values that are lists cannot themselves hold references');
34             return;
35             }
36              
37             # Make sure we do not store the same reference that was
38             # passed as a value, since the list on the other side of
39             # that reference can be modified without going through
40             # our checks
41             $value = $max_field_length ? [
42             map {
43             defined ? substr( $_, 0, $max_field_length ) : $_
44             } @$value
45             ] : [ @$value ];
46             }
47             elsif ( ref $value ) {
48             $logger->trace('Attribute values cannot be references');
49             return;
50             }
51             elsif ( $max_field_length ) {
52             $value = substr $value, 0, $max_field_length;
53             }
54              
55             ( 1, $value );
56             }
57              
58 28     28   1162 method set ( %args ) {
  28         49  
  28         79  
  28         31  
59 28         39 my $recorded = 0;
60 28         231 for ( pairs %args ) {
61 27         61 my ( $key, $value ) = @$_;
62              
63 27   66     69 $key ||= do {
64 1         6 $logger->debugf("Attribute names should not be empty. Setting to 'null' instead");
65 1         63 'null';
66             };
67              
68 27         35 my $fields = scalar %$data;
69 27 100       56 $fields++ unless exists $data->{$key};
70              
71 27 100 100     90 next if $max_fields && $fields > $max_fields;
72              
73 24         28 my $ok;
74 24         50 ( $ok, $value ) = $self->$validate_attribute_value($value);
75              
76 24 100       45 next unless $ok;
77              
78 20         23 $recorded++;
79 20         51 $data->{$key} = $value;
80             }
81              
82 28         71 my $dropped = +( keys %args ) - $recorded;
83              
84 28 100 100     120 $logger->debugf(
    100          
    100          
85             'Dropped %s attribute entr%s because %s invalid%s',
86             $dropped,
87             $dropped > 1 ? ( 'ies', 'they were' ) : ( 'y', 'it was' ),
88             $max_fields
89             ? " or would have exceeded field limit ($max_fields)" : '',
90             ) if $logger->is_debug && $dropped > 0;
91              
92 28         897 $dropped_fields += $dropped;
93              
94 28         205 return $self;
95             }
96              
97 27     27   36 method to_hash () {
  27         45  
  27         31  
98 27         1187 dclone $data;
99             }
100             }
101              
102             role OpenTelemetry::Attributes {
103             field $attributes;
104              
105             ADJUSTPARAMS ( $params ) {
106             $attributes = OpenTelemetry::AttributeMap->new(
107             data => delete $params->{attributes} // {},
108             max_fields => delete $params->{attribute_count_limit},
109             max_field_length => delete $params->{attribute_length_limit},
110             );
111             }
112              
113 21     21 1 3777 method dropped_attributes () { $attributes->dropped_fields }
  21     21   44  
  21     21   22  
  21         45  
114              
115 27     27 1 23224 method attributes () { $attributes->to_hash }
  27     27   82  
  27     25   54  
  27         75  
116             }