|
1 | | -use std::{ |
2 | | - future::Future, |
3 | | - pin::Pin, |
4 | | - task::{Context, Poll}, |
5 | | - time::Instant, |
6 | | -}; |
7 | | - |
8 | | -use futures_util::future; |
9 | | -use tracing::{Instrument, instrument::Instrumented}; |
10 | | - |
11 | | -use rivet_metrics::KeyValue; |
12 | | - |
13 | | -/// Attempts to create a new future to select over a list of futures. |
14 | | -/// Non-panicking version of [futures_util::future::select_all](https://docs.rs/futures/0.3.15/futures/future/fn.select_all.html). |
15 | | -/// |
16 | | -/// If `iter` is empty, a `Pending` future is returned. |
17 | | -pub async fn select_all_or_wait<I>(iter: I) -> <I::Item as Future>::Output |
18 | | -where |
19 | | - I: IntoIterator, |
20 | | - I::Item: Future + Unpin, |
21 | | -{ |
22 | | - let futs = iter.into_iter().collect::<Vec<I::Item>>(); |
23 | | - |
24 | | - if !futs.is_empty() { |
25 | | - future::select_all(futs).await.0 |
26 | | - } else { |
27 | | - std::future::pending().await |
28 | | - } |
29 | | -} |
30 | | - |
31 | | -pub trait CustomInstrumentExt: Sized { |
32 | | - fn custom_instrument(self, span: tracing::Span) -> CustomInstrumented<Self> { |
33 | | - CustomInstrumented { |
34 | | - inner: self.instrument(span), |
35 | | - start: Instant::now(), |
36 | | - } |
37 | | - } |
38 | | -} |
39 | | - |
40 | | -impl<F: Sized> CustomInstrumentExt for F {} |
41 | | - |
42 | | -pub struct CustomInstrumented<T> { |
43 | | - inner: Instrumented<T>, |
44 | | - start: Instant, |
45 | | -} |
46 | | - |
47 | | -impl<T: Future> Future for CustomInstrumented<T> { |
48 | | - type Output = T::Output; |
49 | | - |
50 | | - fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> { |
51 | | - let this = unsafe { self.get_unchecked_mut() }; |
52 | | - let inner = unsafe { Pin::new_unchecked(&mut this.inner) }; |
53 | | - |
54 | | - let metadata = inner.span().metadata().clone(); |
55 | | - |
56 | | - match inner.poll(cx) { |
57 | | - Poll::Ready(val) => { |
58 | | - if let Some(metadata) = metadata { |
59 | | - if let (Some(file), Some(line)) = (metadata.file(), metadata.line()) { |
60 | | - metrics::INSTRUMENTED_FUTURE_DURATION.record( |
61 | | - this.start.elapsed().as_secs_f64(), |
62 | | - &[ |
63 | | - KeyValue::new("location", format!("{file}:{line}")), |
64 | | - KeyValue::new("name", metadata.name()), |
65 | | - ], |
66 | | - ); |
67 | | - } |
68 | | - } |
69 | | - Poll::Ready(val) |
70 | | - } |
71 | | - Poll::Pending => Poll::Pending, |
72 | | - } |
73 | | - } |
74 | | -} |
75 | | - |
76 | | -mod metrics { |
77 | | - use rivet_metrics::{ |
78 | | - MICRO_BUCKETS, |
79 | | - otel::{global::*, metrics::*}, |
80 | | - }; |
81 | | - |
82 | | - lazy_static::lazy_static! { |
83 | | - static ref METER: Meter = meter("rivet-util-core"); |
84 | | - |
85 | | - /// Expected attributes: "location", "name" |
86 | | - pub static ref INSTRUMENTED_FUTURE_DURATION: Histogram<f64> = METER.f64_histogram("rivet_instrumented_future_duration") |
87 | | - .with_description("Duration of a future.") |
88 | | - .with_boundaries(MICRO_BUCKETS.to_vec()) |
89 | | - .build(); |
90 | | - } |
91 | | -} |
| 1 | +pub use rivet_tracing_utils::*; |
0 commit comments