Coverage Report

Created: 2025-05-30 16:37

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