Coverage Report

Created: 2025-07-10 19:59

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/build/source/nativelink-metric/src/lib.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 core::hash::BuildHasher;
16
use core::sync::atomic::{AtomicI64, AtomicU64, Ordering};
17
use core::time::Duration;
18
use std::borrow::Cow;
19
use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet};
20
use std::sync::{Arc, Weak};
21
use std::time::{SystemTime, UNIX_EPOCH};
22
23
pub use nativelink_metric_macro_derive::MetricsComponent;
24
pub use tracing::{
25
    error as __metric_error, info as __metric_info, info_span as __metric_info_span,
26
};
27
28
/// Error type for the metrics library.
29
// Note: We do not use the nativelink-error struct because
30
// we'd end up in a circular dependency if we did, because
31
// nativelink-error uses the metrics library.
32
#[derive(Debug)]
33
pub struct Error(String);
34
35
impl core::fmt::Display for Error {
36
0
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
37
0
        write!(f, "{}", self.0)
38
0
    }
39
}
40
41
impl core::error::Error for Error {}
42
43
/// Holds metadata about the field that is being published.
44
#[derive(Debug, Default, Clone)]
45
pub struct MetricFieldData<'a> {
46
    pub name: Cow<'a, str>,
47
    pub help: Cow<'a, str>,
48
    pub group: Cow<'a, str>,
49
}
50
51
/// The final primitive data that is being published with the kind.
52
#[derive(Debug)]
53
pub enum MetricPublishKnownKindData {
54
    Counter(u64),
55
    String(String),
56
    Component,
57
}
58
59
/// The kind of metric that is being published.
60
// Note: This enum will be translate in-and-out
61
// of a u64 when traversing the `tracing::event`
62
// boundary for efficiency reasons.
63
#[derive(Clone, Copy, Debug)]
64
#[repr(u8)]
65
pub enum MetricKind {
66
    Default = 0,
67
    Counter = 1,
68
    String = 2,
69
    Component = 3,
70
}
71
72
impl From<u64> for MetricKind {
73
0
    fn from(value: u64) -> Self {
74
0
        match value {
75
0
            0 | 4_u64..=u64::MAX => Self::Default,
76
0
            1 => Self::Counter,
77
0
            2 => Self::String,
78
0
            3 => Self::Component,
79
        }
80
0
    }
81
}
82
83
impl MetricKind {
84
    #[must_use]
85
0
    pub fn into_known_kind(&self, default_kind: Self) -> MetricPublishKnownKindData {
86
0
        let this = if matches!(self, Self::Default) {
87
0
            default_kind
88
        } else {
89
0
            *self
90
        };
91
0
        match this {
92
0
            Self::Counter => MetricPublishKnownKindData::Counter(0),
93
0
            Self::String => MetricPublishKnownKindData::String(String::new()),
94
0
            Self::Component => MetricPublishKnownKindData::Component,
95
0
            Self::Default => unreachable!("Default should have been handled"),
96
        }
97
0
    }
98
}
99
100
/// The trait that all components that can be published must implement.
101
pub trait MetricsComponent {
102
    /// # Errors
103
    ///
104
    /// Will return `Err` if we can't publish the metric.
105
    fn publish(
106
        &self,
107
        kind: MetricKind,
108
        _field_metadata: MetricFieldData,
109
    ) -> Result<MetricPublishKnownKindData, Error>;
110
}
111
112
pub trait RootMetricsComponent: MetricsComponent + Send + Sync {
113
    /// # Errors
114
    ///
115
    /// Will return `Err` if we can't publish the metric.
116
0
    fn publish(
117
0
        &self,
118
0
        kind: MetricKind,
119
0
        field_metadata: MetricFieldData,
120
0
    ) -> Result<MetricPublishKnownKindData, Error> {
121
0
        MetricsComponent::publish(self, kind, field_metadata)
122
0
    }
123
}
124
125
impl<T: MetricsComponent> MetricsComponent for Option<T> {
126
0
    fn publish(
127
0
        &self,
128
0
        kind: MetricKind,
129
0
        field_metadata: MetricFieldData,
130
0
    ) -> Result<MetricPublishKnownKindData, Error> {
131
0
        self.as_ref()
132
0
            .map_or(Ok(MetricPublishKnownKindData::Component), |value| {
133
0
                value.publish(kind, field_metadata)
134
0
            })
135
0
    }
136
}
137
138
impl<T: MetricsComponent> MetricsComponent for tokio::sync::watch::Sender<T> {
139
0
    fn publish(
140
0
        &self,
141
0
        kind: MetricKind,
142
0
        field_metadata: MetricFieldData,
143
0
    ) -> Result<MetricPublishKnownKindData, Error> {
144
0
        self.borrow().publish(kind, field_metadata)
145
0
    }
146
}
147
148
impl<T: MetricsComponent + ?Sized> MetricsComponent for Arc<T> {
149
0
    fn publish(
150
0
        &self,
151
0
        kind: MetricKind,
152
0
        field_metadata: MetricFieldData,
153
0
    ) -> Result<MetricPublishKnownKindData, Error> {
154
0
        self.as_ref().publish(kind, field_metadata)
155
0
    }
156
}
157
158
impl<T: MetricsComponent, S: BuildHasher> MetricsComponent for HashSet<T, S> {
159
0
    fn publish(
160
0
        &self,
161
0
        kind: MetricKind,
162
0
        field_metadata: MetricFieldData,
163
0
    ) -> Result<MetricPublishKnownKindData, Error> {
164
0
        for (i, item) in self.iter().enumerate() {
165
0
            let guard = group!(i).entered();
166
0
            let publish_result = item.publish(kind, field_metadata.clone())?;
167
0
            drop(guard);
168
0
            match publish_result {
169
0
                MetricPublishKnownKindData::Counter(value) => {
170
0
                    publish!(
171
0
                        i,
172
0
                        &value,
173
0
                        MetricKind::Counter,
174
0
                        field_metadata.help.to_string()
175
                    );
176
                }
177
0
                MetricPublishKnownKindData::String(value) => {
178
0
                    publish!(
179
0
                        i,
180
0
                        &value,
181
0
                        MetricKind::String,
182
0
                        field_metadata.help.to_string()
183
                    );
184
                }
185
0
                MetricPublishKnownKindData::Component => {}
186
            }
187
        }
188
0
        Ok(MetricPublishKnownKindData::Component)
189
0
    }
190
}
191
192
impl<U: ToString, T: MetricsComponent, S: BuildHasher> MetricsComponent for HashMap<U, T, S> {
193
0
    fn publish(
194
0
        &self,
195
0
        kind: MetricKind,
196
0
        field_metadata: MetricFieldData,
197
0
    ) -> Result<MetricPublishKnownKindData, Error> {
198
0
        for (key, item) in self {
199
0
            let guard = group!(key).entered();
200
0
            let publish_result = item.publish(kind, field_metadata.clone())?;
201
0
            drop(guard);
202
0
            match publish_result {
203
0
                MetricPublishKnownKindData::Counter(value) => {
204
0
                    publish!(
205
0
                        key,
206
0
                        &value,
207
0
                        MetricKind::Counter,
208
0
                        field_metadata.help.to_string()
209
                    );
210
                }
211
0
                MetricPublishKnownKindData::String(value) => {
212
0
                    publish!(
213
0
                        key,
214
0
                        &value,
215
0
                        MetricKind::String,
216
0
                        field_metadata.help.to_string()
217
                    );
218
                }
219
0
                MetricPublishKnownKindData::Component => {}
220
            }
221
        }
222
0
        Ok(MetricPublishKnownKindData::Component)
223
0
    }
224
}
225
226
impl<U: ToString, T: MetricsComponent> MetricsComponent for BTreeMap<U, T> {
227
0
    fn publish(
228
0
        &self,
229
0
        kind: MetricKind,
230
0
        field_metadata: MetricFieldData,
231
0
    ) -> Result<MetricPublishKnownKindData, Error> {
232
0
        for (key, item) in self {
233
0
            group!(key).in_scope(|| item.publish(kind, field_metadata.clone()))?;
234
        }
235
0
        Ok(MetricPublishKnownKindData::Component)
236
0
    }
237
}
238
239
impl<T: MetricsComponent> MetricsComponent for BTreeSet<T> {
240
0
    fn publish(
241
0
        &self,
242
0
        kind: MetricKind,
243
0
        field_metadata: MetricFieldData,
244
0
    ) -> Result<MetricPublishKnownKindData, Error> {
245
0
        for (i, item) in self.iter().enumerate() {
246
0
            group!(i).in_scope(|| item.publish(kind, field_metadata.clone()))?;
247
        }
248
0
        Ok(MetricPublishKnownKindData::Component)
249
0
    }
250
}
251
252
impl<T: MetricsComponent> MetricsComponent for Vec<T> {
253
0
    fn publish(
254
0
        &self,
255
0
        kind: MetricKind,
256
0
        field_metadata: MetricFieldData,
257
0
    ) -> Result<MetricPublishKnownKindData, Error> {
258
0
        for (i, item) in self.iter().enumerate() {
259
0
            group!(i).in_scope(|| item.publish(kind, field_metadata.clone()))?;
260
        }
261
0
        Ok(MetricPublishKnownKindData::Component)
262
0
    }
263
}
264
265
impl<T: MetricsComponent + ?Sized> MetricsComponent for Weak<T> {
266
0
    fn publish(
267
0
        &self,
268
0
        kind: MetricKind,
269
0
        field_metadata: MetricFieldData,
270
0
    ) -> Result<MetricPublishKnownKindData, Error> {
271
0
        let Some(this) = self.upgrade() else {
  Branch (271:13): [Folded - Ignored]
  Branch (271:13): [True: 0, False: 0]
  Branch (271:13): [Folded - Ignored]
272
0
            return Ok(MetricPublishKnownKindData::Component);
273
        };
274
0
        this.as_ref().publish(kind, field_metadata)
275
0
    }
276
}
277
278
impl<T, E> MetricsComponent for Result<T, E>
279
where
280
    T: MetricsComponent,
281
    E: MetricsComponent,
282
{
283
0
    fn publish(
284
0
        &self,
285
0
        kind: MetricKind,
286
0
        field_metadata: MetricFieldData,
287
0
    ) -> Result<MetricPublishKnownKindData, Error> {
288
0
        match self {
289
0
            Ok(value) => value.publish(kind, field_metadata),
290
0
            Err(value) => value.publish(kind, field_metadata),
291
        }
292
0
    }
293
}
294
295
impl MetricsComponent for Duration {
296
0
    fn publish(
297
0
        &self,
298
0
        kind: MetricKind,
299
0
        field_metadata: MetricFieldData,
300
0
    ) -> Result<MetricPublishKnownKindData, Error> {
301
0
        self.as_secs_f64().publish(kind, field_metadata)
302
0
    }
303
}
304
305
impl MetricsComponent for SystemTime {
306
0
    fn publish(
307
0
        &self,
308
0
        kind: MetricKind,
309
0
        field_metadata: MetricFieldData,
310
0
    ) -> Result<MetricPublishKnownKindData, Error> {
311
0
        Self::now().duration_since(UNIX_EPOCH).map_or_else(
312
0
            |_| Err(Error("SystemTime before UNIX EPOCH!".to_string())),
313
0
            |n| n.as_secs().publish(kind, field_metadata),
314
        )
315
0
    }
316
}
317
318
impl MetricsComponent for f64 {
319
0
    fn publish(
320
0
        &self,
321
0
        _kind: MetricKind,
322
0
        _field_metadata: MetricFieldData,
323
0
    ) -> Result<MetricPublishKnownKindData, Error> {
324
0
        Ok(MetricPublishKnownKindData::String(self.to_string()))
325
0
    }
326
}
327
328
impl MetricsComponent for bool {
329
0
    fn publish(
330
0
        &self,
331
0
        kind: MetricKind,
332
0
        field_metadata: MetricFieldData,
333
0
    ) -> Result<MetricPublishKnownKindData, Error> {
334
0
        let value = u64::from(*self);
335
0
        value.publish(kind, field_metadata)
336
0
    }
337
}
338
339
impl MetricsComponent for i32 {
340
0
    fn publish(
341
0
        &self,
342
0
        kind: MetricKind,
343
0
        field_metadata: MetricFieldData,
344
0
    ) -> Result<MetricPublishKnownKindData, Error> {
345
0
        let value = u64::try_from(*self)
346
0
            .map_err(|_| Error(format!("Could not convert {self} to u64 in metrics lib")))?;
347
0
        value.publish(kind, field_metadata)
348
0
    }
349
}
350
351
impl MetricsComponent for u64 {
352
0
    fn publish(
353
0
        &self,
354
0
        kind: MetricKind,
355
0
        _field_metadata: MetricFieldData,
356
0
    ) -> Result<MetricPublishKnownKindData, Error> {
357
0
        let mut known_kind_data = kind.into_known_kind(MetricKind::Counter);
358
0
        match &mut known_kind_data {
359
0
            MetricPublishKnownKindData::Counter(data) => {
360
0
                *data = *self;
361
0
            }
362
0
            MetricPublishKnownKindData::String(data) => {
363
0
                *data = self.to_string();
364
0
            }
365
0
            MetricPublishKnownKindData::Component => {}
366
        }
367
0
        Ok(known_kind_data)
368
0
    }
369
}
370
371
impl MetricsComponent for i64 {
372
0
    fn publish(
373
0
        &self,
374
0
        kind: MetricKind,
375
0
        field_metadata: MetricFieldData,
376
0
    ) -> Result<MetricPublishKnownKindData, Error> {
377
0
        let value = u64::try_from(*self)
378
0
            .map_err(|_| Error(format!("Could not convert {self} to u64 in metrics lib")))?;
379
0
        value.publish(kind, field_metadata)
380
0
    }
381
}
382
383
impl MetricsComponent for u32 {
384
0
    fn publish(
385
0
        &self,
386
0
        kind: MetricKind,
387
0
        field_metadata: MetricFieldData,
388
0
    ) -> Result<MetricPublishKnownKindData, Error> {
389
0
        u64::from(*self).publish(kind, field_metadata)
390
0
    }
391
}
392
393
impl MetricsComponent for usize {
394
0
    fn publish(
395
0
        &self,
396
0
        kind: MetricKind,
397
0
        field_metadata: MetricFieldData,
398
0
    ) -> Result<MetricPublishKnownKindData, Error> {
399
0
        let value = u64::try_from(*self)
400
0
            .map_err(|_| Error(format!("Could not convert {self} to u64 in metrics lib")))?;
401
0
        value.publish(kind, field_metadata)
402
0
    }
403
}
404
405
impl MetricsComponent for AtomicU64 {
406
0
    fn publish(
407
0
        &self,
408
0
        kind: MetricKind,
409
0
        field_metadata: MetricFieldData,
410
0
    ) -> Result<MetricPublishKnownKindData, Error> {
411
0
        self.load(Ordering::Acquire).publish(kind, field_metadata)
412
0
    }
413
}
414
415
impl MetricsComponent for AtomicI64 {
416
0
    fn publish(
417
0
        &self,
418
0
        kind: MetricKind,
419
0
        field_metadata: MetricFieldData,
420
0
    ) -> Result<MetricPublishKnownKindData, Error> {
421
0
        self.load(Ordering::Acquire).publish(kind, field_metadata)
422
0
    }
423
}
424
425
impl MetricsComponent for String {
426
0
    fn publish(
427
0
        &self,
428
0
        kind: MetricKind,
429
0
        _field_metadata: MetricFieldData,
430
0
    ) -> Result<MetricPublishKnownKindData, Error> {
431
0
        let mut known_kind_data = kind.into_known_kind(MetricKind::String);
432
0
        match &mut known_kind_data {
433
0
            MetricPublishKnownKindData::Counter(data) => {
434
0
                *data = self.parse::<u64>().map_err(|_| {
435
0
                    Error(format!(
436
0
                        "Could not convert String '{self}' to u64 in metrics lib"
437
0
                    ))
438
0
                })?;
439
            }
440
0
            MetricPublishKnownKindData::String(data) => {
441
0
                data.clone_from(self);
442
0
            }
443
0
            MetricPublishKnownKindData::Component => {}
444
        }
445
0
        Ok(known_kind_data)
446
0
    }
447
}
448
449
impl<T: MetricsComponent> MetricsComponent for async_lock::Mutex<T> {
450
0
    fn publish(
451
0
        &self,
452
0
        kind: MetricKind,
453
0
        field_metadata: MetricFieldData,
454
0
    ) -> Result<MetricPublishKnownKindData, Error> {
455
        // It is safe to block in the publishing thread.
456
0
        let lock = self.lock_blocking();
457
0
        lock.publish(kind, field_metadata)
458
0
    }
459
}
460
461
impl<T: MetricsComponent> MetricsComponent for parking_lot::Mutex<T> {
462
0
    fn publish(
463
0
        &self,
464
0
        kind: MetricKind,
465
0
        field_metadata: MetricFieldData,
466
0
    ) -> Result<MetricPublishKnownKindData, Error> {
467
        // It is safe to block in the publishing thread.
468
0
        let lock = self.lock();
469
0
        lock.publish(kind, field_metadata)
470
0
    }
471
}
472
473
impl<T: MetricsComponent> MetricsComponent for parking_lot::RwLock<T> {
474
0
    fn publish(
475
0
        &self,
476
0
        kind: MetricKind,
477
0
        field_metadata: MetricFieldData,
478
0
    ) -> Result<MetricPublishKnownKindData, Error> {
479
        // It is safe to block in the publishing thread.
480
0
        let lock = self.read();
481
0
        lock.publish(kind, field_metadata)
482
0
    }
483
}
484
485
#[macro_export]
486
macro_rules! group {
487
    ($name:expr) => {
488
        $crate::__metric_info_span!(target: "nativelink_metric", "", __name = $name.to_string())
489
    };
490
}
491
492
#[macro_export]
493
macro_rules! publish {
494
    ($name:expr, $value:expr, $metric_kind:expr, $help:expr) => {
495
        $crate::publish!($name, $value, $metric_kind, $help, "")
496
    };
497
    ($name:expr, $value:expr, $metric_kind:expr, $help:expr, $group:expr) => {
498
        {
499
            let _maybe_entered = if !$group.is_empty() {
500
                Some($crate::group!($group).entered())
501
            } else {
502
                None
503
            };
504
            let name = $name.to_string();
505
            let field_metadata = $crate::MetricFieldData {
506
                name: ::std::borrow::Cow::Borrowed(&name),
507
                help: $help.into(),
508
                group: $group.into(),
509
            };
510
            match $crate::MetricsComponent::publish($value, $metric_kind, field_metadata)? {
511
                $crate::MetricPublishKnownKindData::Counter(value) => {
512
                    $crate::__metric_info!(
513
                        target: "nativelink_metric",
514
                        __value = value,
515
                        __type = $crate::MetricKind::Counter as u8,
516
                        __help = $help.to_string(),
517
                        __name = name
518
                    );
519
                }
520
                $crate::MetricPublishKnownKindData::String(value) => {
521
                    $crate::__metric_info!(
522
                        target: "nativelink_metric",
523
                        __value = value,
524
                        __type = $crate::MetricKind::String as u8,
525
                        __help = $help.to_string(),
526
                        __name = name
527
                    );
528
                }
529
                $crate::MetricPublishKnownKindData::Component => {
530
                    // Do nothing, data already published.
531
                }
532
            }
533
        }
534
    };
535
}