Skip to content

Commit 10640b9

Browse files
committed
Refactor Metric construction
1 parent 660ed7d commit 10640b9

6 files changed

+259
-310
lines changed

src/WebJobs.Script/Diagnostics/ApplicationInsightsMeterListener.cs

Lines changed: 0 additions & 245 deletions
This file was deleted.
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the MIT License. See License.txt in the project root for license information.
3+
4+
#nullable enable
5+
6+
using System;
7+
using System.Collections.Generic;
8+
using System.Diagnostics.Metrics;
9+
using System.Numerics;
10+
using System.Threading;
11+
using System.Threading.Tasks;
12+
using Microsoft.ApplicationInsights;
13+
using Microsoft.ApplicationInsights.Extensibility;
14+
using Microsoft.Extensions.Options;
15+
16+
namespace Microsoft.Azure.WebJobs.Script.Diagnostics
17+
{
18+
/// <summary>
19+
/// A meter listener which exports metrics to Application Insights.
20+
/// </summary>
21+
public sealed class ApplicationInsightsMetricExporter : ITelemetryModule, IAsyncDisposable
22+
{
23+
private readonly MeterListener _listener;
24+
private readonly ApplicationInsightsMetricExporterOptions _options;
25+
private readonly CancellationTokenSource _shutdown = new();
26+
27+
private Task _exportTask = Task.CompletedTask;
28+
private TelemetryClient _client = null!;
29+
30+
/// <summary>
31+
/// Initializes a new instance of the <see cref="ApplicationInsightsMetricExporter"/> class.
32+
/// </summary>
33+
/// <param name="lifetime">The application lifetime.</param>
34+
/// <param name="options">The options.</param>
35+
public ApplicationInsightsMetricExporter(IOptions<ApplicationInsightsMetricExporterOptions> options)
36+
{
37+
ArgumentNullException.ThrowIfNull(options);
38+
39+
_options = options.Value;
40+
_listener = new()
41+
{
42+
InstrumentPublished = (instrument, listener) =>
43+
{
44+
if (_options.ShouldListenTo(instrument))
45+
{
46+
listener.EnableMeasurementEvents(instrument, this);
47+
}
48+
},
49+
};
50+
51+
// All of the supported instrument value types.
52+
_listener.SetMeasurementEventCallback(CreateCallback<byte>());
53+
_listener.SetMeasurementEventCallback(CreateCallback<short>());
54+
_listener.SetMeasurementEventCallback(CreateCallback<int>());
55+
_listener.SetMeasurementEventCallback(CreateCallback<long>());
56+
_listener.SetMeasurementEventCallback(CreateCallback<float>());
57+
_listener.SetMeasurementEventCallback(CreateCallback<double>());
58+
_listener.SetMeasurementEventCallback(CreateCallback<decimal>());
59+
}
60+
61+
public void Initialize(TelemetryConfiguration configuration)
62+
{
63+
ArgumentNullException.ThrowIfNull(configuration);
64+
_client = new TelemetryClient(configuration);
65+
_exportTask = CollectAsync(_shutdown.Token);
66+
_listener.Start();
67+
}
68+
69+
public async ValueTask DisposeAsync()
70+
{
71+
_listener.Dispose();
72+
73+
await _shutdown.CancelNoThrowAsync();
74+
await _exportTask.ConfigureAwait(false);
75+
await _client.FlushAsync(default).ConfigureAwait(false);
76+
_shutdown.Dispose();
77+
}
78+
79+
private static MeasurementCallback<T> CreateCallback<T>()
80+
where T : struct, INumber<T>, IConvertible
81+
{
82+
return (instrument, value, tags, state) =>
83+
{
84+
if (state is not ApplicationInsightsMetricExporter listener)
85+
{
86+
return;
87+
}
88+
89+
listener.Publish(instrument, value.ToDouble(null), tags);
90+
};
91+
}
92+
93+
private async Task CollectAsync(CancellationToken cancellation)
94+
{
95+
while (!cancellation.IsCancellationRequested)
96+
{
97+
try
98+
{
99+
_listener.RecordObservableInstruments();
100+
await Task.Delay(_options.CollectInterval, cancellation);
101+
}
102+
catch (Exception ex) when (!ex.IsFatal())
103+
{
104+
// swallow exceptions
105+
}
106+
}
107+
}
108+
109+
private void Publish(Instrument instrument, double value, ReadOnlySpan<KeyValuePair<string, object?>> tags)
110+
{
111+
if (instrument is null)
112+
{
113+
return;
114+
}
115+
116+
_client.TrackInstrument(instrument, value, tags);
117+
}
118+
}
119+
}

src/WebJobs.Script/Diagnostics/ApplicationInsightsMeterOptions.cs renamed to src/WebJobs.Script/Diagnostics/ApplicationInsightsMetricExporterOptions.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,14 @@
1010
namespace Microsoft.Azure.WebJobs.Script.Diagnostics
1111
{
1212
/// <summary>
13-
/// Options for <see cref="ApplicationInsightsMeterListener"/>.
13+
/// Options for <see cref="ApplicationInsightsMetricExporter"/>.
1414
/// </summary>
15-
public class ApplicationInsightsMeterOptions
15+
public class ApplicationInsightsMetricExporterOptions
1616
{
1717
/// <summary>
1818
/// Gets the set of meter names to listen to.
1919
/// </summary>
20-
public ISet<string> Sources { get; } = new HashSet<string>(StringComparer.Ordinal);
20+
public ISet<string> Meters { get; } = new HashSet<string>(StringComparer.Ordinal);
2121

2222
/// <summary>
2323
/// Gets or sets the interval to collect meter values. Default is 30 seconds.
@@ -40,7 +40,7 @@ public bool ShouldListenTo(Instrument instrument)
4040

4141
// TODO: consider allowing wildcards or regex
4242
// For now, just exact match on meter name
43-
return Sources.Contains(instrument.Meter.Name);
43+
return Meters.Contains(instrument.Meter.Name);
4444
}
4545
}
4646
}

0 commit comments

Comments
 (0)