/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 | } |