Coverage Report

Created: 2025-04-19 16:54

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/build/source/nativelink-metric-collector/src/tracing_layers.rs
Line
Count
Source
1
// Copyright 2024 The NativeLink Authors. All rights reserved.
2
//
3
// Licensed under the Apache License, Version 2.0 (the "License");
4
// you may not use this file except in compliance with the License.
5
// You may obtain a copy of the License at
6
//
7
//    http://www.apache.org/licenses/LICENSE-2.0
8
//
9
// Unless required by applicable law or agreed to in writing, software
10
// distributed under the License is distributed on an "AS IS" BASIS,
11
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
// See the License for the specific language governing permissions and
13
// limitations under the License.
14
15
use std::borrow::Cow;
16
use std::collections::HashMap;
17
use std::fmt::Debug;
18
use std::marker::PhantomData;
19
use std::sync::Arc;
20
21
use parking_lot::Mutex;
22
use tracing::span::Attributes;
23
use tracing::subscriber::Interest;
24
use tracing::{Event, Id, Metadata, Subscriber};
25
use tracing_subscriber::Layer;
26
use tracing_subscriber::layer::Context;
27
use tracing_subscriber::registry::SpanRef;
28
29
use crate::metrics_collection::{
30
    CollectedMetricChildren, CollectedMetricPrimitive, CollectedMetrics, RootMetricCollectedMetrics,
31
};
32
use crate::metrics_visitors::{MetricDataVisitor, SpanFields};
33
34
/// The layer that is given to `tracing` to collect metrics.
35
/// The output of the metrics will be populated in the `root_collected_metrics`
36
/// field.
37
#[derive(Debug)]
38
pub struct MetricsCollectorLayer<S> {
39
    spans: Mutex<HashMap<Id, SpanFields>>,
40
    root_collected_metrics: Arc<Mutex<RootMetricCollectedMetrics>>,
41
    _subscriber: PhantomData<S>,
42
}
43
44
impl<S> MetricsCollectorLayer<S> {
45
    /// Creates a new `MetricsCollectorLayer` and returns it along with the
46
    /// `root_collected_metrics` that will be populated with the collected metrics.
47
2
    pub fn new() -> (Self, Arc<Mutex<RootMetricCollectedMetrics>>) {
48
2
        let root_collected_metrics = Arc::new(Mutex::new(RootMetricCollectedMetrics::default()));
49
2
        (
50
2
            MetricsCollectorLayer {
51
2
                spans: Mutex::new(HashMap::new()),
52
2
                root_collected_metrics: root_collected_metrics.clone(),
53
2
                _subscriber: PhantomData,
54
2
            },
55
2
            root_collected_metrics,
56
2
        )
57
2
    }
58
}
59
60
impl<S> Layer<S> for MetricsCollectorLayer<S>
61
where
62
    S: Subscriber + for<'a> tracing_subscriber::registry::LookupSpan<'a> + Debug,
63
{
64
6
    fn enabled(&self, metadata: &Metadata<'_>, _ctx: Context<'_, S>) -> bool {
65
6
        metadata.target() == "nativelink_metric"
66
6
    }
67
68
3
    fn on_new_span(&self, attrs: &Attributes<'_>, id: &Id, _ctx: Context<'_, S>) {
69
3
        let mut span_fields = SpanFields {
70
3
            name: Cow::Borrowed(attrs.metadata().name()),
71
3
        };
72
3
        // Store the current metadata values map representing the current span.
73
3
        // We need to 'snapshot' the current span, because when a more recent
74
3
        // span (such as the one being initialized) updates, these values will
75
3
        // be overwritten.
76
3
        attrs.values().record(&mut span_fields);
77
3
78
3
        self.spans.lock().insert(id.clone(), span_fields);
79
3
    }
80
81
10
    fn on_event(&self, event: &Event<'_>, ctx: Context<'_, S>) {
82
10
        let mut event_visitor = MetricDataVisitor::default();
83
10
        // First, we populate the MetricDataVisitor we are interested
84
10
        // in from the event.
85
10
        event.record(&mut event_visitor);
86
10
        // This represents the field we are concerned with updating or
87
10
        // initializing.
88
10
        let name = event_visitor.name.clone();
89
10
90
10
        let mut root_collected_metrics = self.root_collected_metrics.lock();
91
10
        let collected_component = &mut **root_collected_metrics;
92
93
        // Find out which span we are currently in and retrieve its metadata.
94
        // It is possible to not be in a span in the tracing library.
95
        // If we are not in a span, we assume you want your metrics published
96
        // in the root of the collected metrics.
97
10
        if let Some(
current_span6
) = ctx.lookup_current() {
  Branch (97:16): [True: 2, False: 4]
  Branch (97:16): [Folded - Ignored]
  Branch (97:16): [Folded - Ignored]
  Branch (97:16): [True: 4, False: 0]
98
6
            let mut known_spans = self.spans.lock();
99
6
            // By default tracing starts you at the bottom of the span tree,
100
6
            // but we want to start at the root of the tree and walk down,
101
6
            // so invert it.
102
6
            let span_iter = current_span.scope().from_root();
103
6
            // Find the layer in our output struct we are going to populate
104
6
            // the data into.
105
6
            let collected_component =
106
6
                find_component(span_iter, &mut known_spans, collected_component);
107
6
108
6
            // Get the new value from the event and update it in the component.
109
6
            let primitive = CollectedMetricPrimitive::from(event_visitor);
110
6
            collected_component.insert(name, CollectedMetrics::Primitive(primitive));
111
6
        } else {
112
4
            let primitive = CollectedMetricPrimitive::from(event_visitor);
113
4
            collected_component.insert(name, CollectedMetrics::Primitive(primitive));
114
4
        }
115
10
    }
116
117
15
    fn register_callsite(&self, _metadata: &'static Metadata<'static>) -> Interest {
118
15
        Interest::always()
119
15
    }
120
}
121
122
16
fn find_component<'a, 'b, S, I>(
123
16
    mut iter: I,
124
16
    known_spans: &'a mut HashMap<Id, SpanFields>,
125
16
    mut collected_component: &'a mut CollectedMetricChildren,
126
16
) -> &'a mut CollectedMetricChildren
127
16
where
128
16
    S: Subscriber + for<'c> tracing_subscriber::registry::LookupSpan<'c> + Debug,
129
16
    I: Iterator<Item = SpanRef<'b, S>>,
130
16
{
131
16
    let Some(
span10
) = iter.next() else {
  Branch (131:9): [True: 2, False: 2]
  Branch (131:9): [Folded - Ignored]
  Branch (131:9): [Folded - Ignored]
  Branch (131:9): [True: 8, False: 4]
132
        // Once there are no more nested spans, we have reached a leaf field.
133
6
        return collected_component;
134
    };
135
10
    let span_fields = known_spans.get(&span.id()).expect("Span not found");
136
10
    // LayerMap<Name, Either<LayerMap, Primitive>>
137
10
    // This is a hashmap of the existing data for the layer
138
10
    let collected_metric = collected_component
139
10
        .entry(span_fields.name.to_string())
140
10
        .or_insert_with(CollectedMetrics::new_component);
141
10
142
10
    collected_component = match collected_metric {
143
10
        CollectedMetrics::Component(component) => &mut **component,
144
0
        CollectedMetrics::Primitive(_) => panic!("Expected to be component"),
145
    };
146
    // DFS the iterator of keys and return the first leaf found matching the name query.
147
10
    find_component(iter, known_spans, collected_component)
148
16
}