prometheus-net
82 строки · 3.1 Кб
1using System.Buffers;
2
3namespace Prometheus;
4
5internal sealed class LabelEnrichingAutoLeasingMetric<TMetric> : ICollector<TMetric>
6where TMetric : ICollectorChild
7{
8public LabelEnrichingAutoLeasingMetric(ICollector<TMetric> inner, string[] enrichWithLabelValues)
9{
10_inner = inner;
11_enrichWithLabelValues = enrichWithLabelValues;
12}
13
14private readonly ICollector<TMetric> _inner;
15private readonly string[] _enrichWithLabelValues;
16
17public TMetric Unlabelled
18{
19get
20{
21// If we are not provided any custom label values, we can be pretty sure the label values are not going to change
22// between calls, so reuse a buffer to avoid allocations when passing the data to the inner instance.
23var buffer = ArrayPool<string>.Shared.Rent(_enrichWithLabelValues.Length);
24
25try
26{
27_enrichWithLabelValues.CopyTo(buffer, 0);
28var finalLabelValues = buffer.AsSpan(0, _enrichWithLabelValues.Length);
29
30return _inner.WithLabels(finalLabelValues);
31}
32finally
33{
34ArrayPool<string>.Shared.Return(buffer);
35}
36}
37}
38
39public string Name => _inner.Name;
40public string Help => _inner.Help;
41
42// We do not display the enriched labels, they are transparent - this is only the instance-specific label names.
43public string[] LabelNames => _inner.LabelNames;
44
45public TMetric WithLabels(params string[] labelValues)
46{
47// The caller passing us string[] does not signal that the allocation is not needed - in all likelihood it is.
48// However, we do not want to allocate two arrays here (because we need to concatenate as well) so instead we
49// use the reusable-buffer overload to avoid at least one of the allocations.
50
51return WithLabels(labelValues.AsSpan());
52}
53
54public TMetric WithLabels(ReadOnlyMemory<string> labelValues)
55{
56// The caller passing us ReadOnlyMemory does not signal that the allocation is not needed - in all likelihood it is.
57// However, we do not want to allocate two arrays here (because we need to concatenate as well) so instead we
58// use the reusable-buffer overload to avoid at least one of the allocations.
59
60return WithLabels(labelValues.Span);
61}
62
63public TMetric WithLabels(ReadOnlySpan<string> labelValues)
64{
65// The ReadOnlySpan overload suggests that the label values may already be known to the metric,
66// so we should strongly avoid allocating memory here. Thus we copy everything to a reusable buffer.
67var buffer = ArrayPool<string>.Shared.Rent(_enrichWithLabelValues.Length + labelValues.Length);
68
69try
70{
71_enrichWithLabelValues.CopyTo(buffer, 0);
72labelValues.CopyTo(buffer.AsSpan(_enrichWithLabelValues.Length));
73var finalLabelValues = buffer.AsSpan(0, _enrichWithLabelValues.Length + labelValues.Length);
74
75return _inner.WithLabels(finalLabelValues);
76}
77finally
78{
79ArrayPool<string>.Shared.Return(buffer);
80}
81}
82}
83