Coverage Report

Created: 2024-11-20 10:13

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