Coverage Report

Created: 2024-11-20 10:13

/build/source/nativelink-metric-collector/src/tracing_layers.rs
Line
Count
Source (jump to first uncovered line)
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::Context;
26
use tracing_subscriber::registry::SpanRef;
27
use tracing_subscriber::Layer;
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
pub struct MetricsCollectorLayer<S> {
38
    spans: Mutex<HashMap<Id, SpanFields>>,
39
    root_collected_metrics: Arc<Mutex<RootMetricCollectedMetrics>>,
40
    _subscriber: PhantomData<S>,
41
}
42
43
impl<S> MetricsCollectorLayer<S> {
44
    /// Creates a new `MetricsCollectorLayer` and returns it along with the
45
    /// `root_collected_metrics` that will be populated with the collected metrics.
46
3
    pub fn new() -> (Self, Arc<Mutex<RootMetricCollectedMetrics>>) {
47
3
        let root_collected_metrics = Arc::new(Mutex::new(RootMetricCollectedMetrics::default()));
48
3
        (
49
3
            MetricsCollectorLayer {
50
3
                spans: Mutex::new(HashMap::new()),
51
3
                root_collected_metrics: root_collected_metrics.clone(),
52
3
                _subscriber: PhantomData,
53
3
            },
54
3
            root_collected_metrics,
55
3
        )
56
3
    }
57
}
58
59
impl<S> Layer<S> for MetricsCollectorLayer<S>
60
where
61
    S: Subscriber + for<'a> tracing_subscriber::registry::LookupSpan<'a> + Debug,
62
{
63
8
    fn enabled(&self, metadata: &Metadata<'_>, _ctx: Context<'_, S>) -> bool {
64
8
        metadata.target() == "nativelink_metric"
65
8
    }
66
67
4
    fn on_new_span(&self, attrs: &Attributes<'_>, id: &Id, _ctx: Context<'_, S>) {
68
4
        let mut span_fields = SpanFields {
69
4
            name: Cow::Borrowed(attrs.metadata().name()),
70
4
        };
71
4
        // Store the current metadata values map representing the current span.
72
4
        // We need to 'snapshot' the current span, because when a more recent
73
4
        // span (such as the one being initialized) updates, these values will
74
4
        // be overwritten.
75
4
        attrs.values().record(&mut span_fields);
76
4
77
4
        self.spans.lock().insert(id.clone(), span_fields);
78
4
    }
79
80
16
    fn on_event(&self, event: &Event<'_>, ctx: Context<'_, S>) {
81
16
        let mut event_visitor = MetricDataVisitor::default();
82
16
        // First, we populate the MetricDataVisitor we are interested
83
16
        // in from the event.
84
16
        event.record(&mut event_visitor);
85
16
        // This represents the field we are concerned with updating or
86
16
        // initializing.
87
16
        let name = event_visitor.name.clone();
88
16
89
16
        let mut root_collected_metrics = self.root_collected_metrics.lock();
90
16
        let collected_component = &mut **root_collected_metrics;
91
92
        // Find out which span we are currently in and retrieve its metadata.
93
        // It is possible to not be in a span in the tracing library.
94
        // If we are not in a span, we assume you want your metrics published
95
        // in the root of the collected metrics.
96
16
        if let Some(
current_span8
) = ctx.lookup_current() {
  Branch (96:16): [True: 4, False: 8]
  Branch (96:16): [Folded - Ignored]
  Branch (96:16): [Folded - Ignored]
  Branch (96:16): [True: 4, False: 0]
97
8
            let mut known_spans = self.spans.lock();
98
8
            // By default tracing starts you at the bottom of the span tree,
99
8
            // but we want to start at the root of the tree and walk down,
100
8
            // so invert it.
101
8
            let span_iter = current_span.scope().from_root();
102
8
            // Find the layer in our output struct we are going to populate
103
8
            // the data into.
104
8
            let collected_component =
105
8
                find_component(span_iter, &mut known_spans, collected_component);
106
8
107
8
            // Get the new value from the event and update it in the component.
108
8
            let primitive = CollectedMetricPrimitive::from(event_visitor);
109
8
            collected_component.insert(name, CollectedMetrics::Primitive(primitive));
110
8
        } else {
111
8
            let primitive = CollectedMetricPrimitive::from(event_visitor);
112
8
            collected_component.insert(name, CollectedMetrics::Primitive(primitive));
113
8
        }
114
16
    }
115
116
17
    fn register_callsite(&self, _metadata: &'static Metadata<'static>) -> Interest {
117
17
        Interest::always()
118
17
    }
119
}
120
121
20
fn find_component<'a, 'b, S, I>(
122
20
    mut iter: I,
123
20
    known_spans: &'a mut HashMap<Id, SpanFields>,
124
20
    mut collected_component: &'a mut CollectedMetricChildren,
125
20
) -> &'a mut CollectedMetricChildren
126
20
where
127
20
    S: Subscriber + for<'c> tracing_subscriber::registry::LookupSpan<'c> + Debug,
128
20
    I: Iterator<Item = SpanRef<'b, S>>,
129
20
{
130
20
    let Some(
span12
) = iter.next() else {
  Branch (130:9): [True: 4, False: 4]
  Branch (130:9): [Folded - Ignored]
  Branch (130:9): [Folded - Ignored]
  Branch (130:9): [True: 8, False: 4]
131
        // Once there are no more nested spans, we have reached a leaf field.
132
8
        return collected_component;
133
    };
134
12
    let span_fields = known_spans.get(&span.id()).expect("Span not found");
135
12
    // LayerMap<Name, Either<LayerMap, Primitive>>
136
12
    // This is a hashmap of the existing data for the layer
137
12
    let collected_metric = collected_component
138
12
        .entry(span_fields.name.to_string())
139
12
        .or_insert_with(CollectedMetrics::new_component);
140
12
141
12
    collected_component = match collected_metric {
142
12
        CollectedMetrics::Component(component) => &mut **component,
143
0
        CollectedMetrics::Primitive(_) => panic!("Expected to be component"),
144
    };
145
    // DFS the iterator of keys and return the first leaf found matching the name query.
146
12
    find_component(iter, known_spans, collected_component)
147
20
}