prometheus-net
107 строк · 5.0 Кб
1namespace Prometheus;2
3/// <summary>
4/// This class implements two sets of functionality:
5/// 1. A lifetime-managed metric handle that can be used to take leases on the metric.
6/// 2. An automatically-lifetime-extending-on-use metric that creates leases automatically.
7///
8/// While conceptually separate, we merge the two sets into one class to avoid allocating a bunch of small objects
9/// every time you want to obtain a lifetime-extending-on-use metric (which tends to be on a relatively hot path).
10///
11/// The lifetime-extending feature only supports write operations because we cannot guarantee that the metric is still alive when reading.
12/// </summary>
13internal sealed class ManagedLifetimeHistogram : ManagedLifetimeMetricHandle<Histogram.Child, IHistogram>, ICollector<IHistogram>14{
15static ManagedLifetimeHistogram()16{17_assignUnlabelledFunc = AssignUnlabelled;18}19
20public ManagedLifetimeHistogram(Collector<Histogram.Child> metric, TimeSpan expiresAfter) : base(metric, expiresAfter)21{22}23
24public override ICollector<IHistogram> WithExtendLifetimeOnUse() => this;25
26#region ICollector<IHistogram> implementation (for WithExtendLifetimeOnUse)27public string Name => _metric.Name;28public string Help => _metric.Help;29public string[] LabelNames => _metric.LabelNames;30
31public IHistogram Unlabelled => NonCapturingLazyInitializer.EnsureInitialized(ref _unlabelled, this, _assignUnlabelledFunc);32private AutoLeasingInstance? _unlabelled;33private static readonly Action<ManagedLifetimeHistogram> _assignUnlabelledFunc;34private static void AssignUnlabelled(ManagedLifetimeHistogram instance) => instance._unlabelled = new AutoLeasingInstance(instance, Array.Empty<string>());35
36// These do not get cached, so are potentially expensive - user code should try avoiding re-allocating these when possible,37// though admittedly this may not be so easy as often these are on the hot path and the very reason that lifetime-managed38// metrics are used is that we do not have a meaningful way to reuse metrics or identify their lifetime.39public IHistogram WithLabels(params string[] labelValues) => WithLabels(labelValues.AsMemory());40
41public IHistogram WithLabels(ReadOnlyMemory<string> labelValues)42{43return new AutoLeasingInstance(this, labelValues);44}45
46public IHistogram WithLabels(ReadOnlySpan<string> labelValues)47{48// We are allocating a long-lived auto-leasing wrapper here, so there is no way we can just use the span directly.49// We must copy it to a long-lived array. Another reason to avoid re-allocating these as much as possible.50return new AutoLeasingInstance(this, labelValues.ToArray());51}52#endregion53
54private sealed class AutoLeasingInstance : IHistogram55{56public AutoLeasingInstance(IManagedLifetimeMetricHandle<IHistogram> inner, ReadOnlyMemory<string> labelValues)57{58_inner = inner;59_labelValues = labelValues;60}61
62private readonly IManagedLifetimeMetricHandle<IHistogram> _inner;63private readonly ReadOnlyMemory<string> _labelValues;64
65public double Sum => throw new NotSupportedException("Read operations on a lifetime-extending-on-use expiring metric are not supported.");66public long Count => throw new NotSupportedException("Read operations on a lifetime-extending-on-use expiring metric are not supported.");67
68public void Observe(double val, long count)69{70var args = new ObserveValCountArgs(val, count);71
72// We use the Span overload to signal that we expect the label values to be known already.73_inner.WithLease(_observeValCountCoreFunc, args, _labelValues.Span);74}75
76private readonly struct ObserveValCountArgs(double val, long count)77{78public readonly double Val = val;79public readonly long Count = count;80}81
82private static void ObserveValCountCore(ObserveValCountArgs args, IHistogram histogram) => histogram.Observe(args.Val, args.Count);83private static readonly Action<ObserveValCountArgs, IHistogram> _observeValCountCoreFunc = ObserveValCountCore;84
85public void Observe(double val, Exemplar? exemplar)86{87var args = new ObserveValExemplarArgs(val, exemplar);88
89// We use the Span overload to signal that we expect the label values to be known already.90_inner.WithLease(_observeValExemplarCoreFunc, args, _labelValues.Span);91}92
93private readonly struct ObserveValExemplarArgs(double val, Exemplar? exemplar)94{95public readonly double Val = val;96public readonly Exemplar? Exemplar = exemplar;97}98
99private static void ObserveValExemplarCore(ObserveValExemplarArgs args, IHistogram histogram) => histogram.Observe(args.Val, args.Exemplar);100private static readonly Action<ObserveValExemplarArgs, IHistogram> _observeValExemplarCoreFunc = ObserveValExemplarCore;101
102public void Observe(double val)103{104Observe(val, null);105}106}107}
108