/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::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 | 6 | fn enabled(&self, metadata: &Metadata<'_>, _ctx: Context<'_, S>) -> bool { |
64 | 6 | metadata.target() == "nativelink_metric" |
65 | 6 | } |
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 | 16 | fn register_callsite(&self, _metadata: &'static Metadata<'static>) -> Interest { |
117 | 16 | Interest::always() |
118 | 16 | } |
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 | } |