Skip to content

Commit 9c4203d

Browse files
vishweshbankwarTimothy Mothra
andauthored
Refactor statsbeat (Azure#34443)
* refactor statsbeat * clean up * fix statsbeat issue * disable * refactor transmitter * rename * resolve PR comments * rmv using * missed update * Update sdk/monitor/Azure.Monitor.OpenTelemetry.Exporter/src/Internals/Statsbeat/Statsbeat.cs Co-authored-by: Timothy Mothra <[email protected]> --------- Co-authored-by: Timothy Mothra <[email protected]>
1 parent 8569230 commit 9c4203d

File tree

3 files changed

+119
-123
lines changed

3 files changed

+119
-123
lines changed

sdk/monitor/Azure.Monitor.OpenTelemetry.Exporter/src/Internals/AzureMonitorTransmitter.cs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,26 @@ public AzureMonitorTransmitter(AzureMonitorExporterOptions options, TokenCredent
4141
_applicationInsightsRestClient = InitializeRestClient(options, _connectionVars, credential);
4242

4343
_fileBlobProvider = InitializeOfflineStorage(options);
44+
45+
// TODO: uncomment following line for enablement.
46+
// InitializeStatsbeat(_connectionVars);
47+
}
48+
49+
private static void InitializeStatsbeat(ConnectionVars connectionVars)
50+
{
51+
try
52+
{
53+
// Do not initialize statsbeat for statsbeat.
54+
if (connectionVars != null && connectionVars.InstrumentationKey != ConnectionStringParser.GetValues(Statsbeat.Statsbeat_ConnectionString_EU).InstrumentationKey && connectionVars.InstrumentationKey != ConnectionStringParser.GetValues(Statsbeat.Statsbeat_ConnectionString_NonEU).InstrumentationKey)
55+
{
56+
// TODO: Implement IDisposable for transmitter and dispose statsbeat.
57+
_ = new Statsbeat(connectionVars);
58+
}
59+
}
60+
catch (Exception ex)
61+
{
62+
AzureMonitorExporterEventSource.Log.WriteWarning($"ErrorInitializingStatsBeatfor:{connectionVars.InstrumentationKey}", ex);
63+
}
4464
}
4565

4666
public string InstrumentationKey => _connectionVars.InstrumentationKey;
Lines changed: 88 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
// Copyright (c) Microsoft Corporation. All rights reserved.
22
// Licensed under the MIT License.
33

4-
#nullable disable // TODO: remove and fix errors
5-
64
using System;
75
using System.Collections.Generic;
86
using System.Diagnostics.Metrics;
@@ -17,39 +15,37 @@
1715

1816
namespace Azure.Monitor.OpenTelemetry.Exporter.Internals
1917
{
20-
internal static class Statsbeat
18+
internal sealed class Statsbeat : IDisposable
2119
{
22-
internal const string StatsBeat_ConnectionString_NonEU = "<Non-EU-ConnectionString>";
20+
internal const string Statsbeat_ConnectionString_NonEU = "InstrumentationKey=00000000-0000-0000-0000-000000000000;IngestionEndpoint=https://NonEU.in.applicationinsights.azure.com/";
2321

24-
internal const string StatsBeat_ConnectionString_EU = "EU-ConnectionString";
22+
internal const string Statsbeat_ConnectionString_EU = "InstrumentationKey=00000000-0000-0000-0000-000000000000;IngestionEndpoint=https://EU.in.applicationinsights.azure.com/";
2523

2624
private const string AMS_Url = "http://169.254.169.254/metadata/instance/compute?api-version=2017-08-01&format=json";
2725

28-
internal const int AttachStatsBeatInterval = 86400000;
29-
30-
private static readonly Meter s_myMeter = new("AttachStatsBeatMeter", "1.0");
26+
internal const int AttachStatsbeatInterval = 86400000;
3127

32-
private static bool s_isEnabled = true;
28+
private static readonly Meter s_myMeter = new("AttachStatsbeatMeter", "1.0");
3329

34-
internal static string s_statsBeat_ConnectionString;
30+
internal string? _statsbeat_ConnectionString;
3531

36-
private static string s_resourceProviderId;
32+
private string? _resourceProviderId;
3733

38-
private static string s_resourceProvider;
34+
private string? _resourceProvider;
3935

40-
private static string s_runtimeVersion => SdkVersionUtils.GetVersion(typeof(object));
36+
private static string? s_runtimeVersion => SdkVersionUtils.GetVersion(typeof(object));
4137

42-
private static string s_sdkVersion => SdkVersionUtils.GetVersion(typeof(AzureMonitorTraceExporter));
38+
private static string? s_sdkVersion => SdkVersionUtils.GetVersion(typeof(AzureMonitorTraceExporter));
4339

4440
private static string s_operatingSystem = GetOS();
4541

46-
private static string s_customer_Ikey;
42+
private readonly string? _customer_Ikey;
4743

48-
internal static MeterProvider s_attachStatsBeatMeterProvider;
44+
internal MeterProvider? _attachStatsbeatMeterProvider;
4945

50-
internal static Regex s_endpoint_pattern = new("^https?://(?:www\\.)?([^/.-]+)");
46+
internal static Regex s_endpoint_pattern => new("^https?://(?:www\\.)?([^/.-]+)");
5147

52-
internal static readonly HashSet<string> EU_Endpoints = new()
48+
internal static readonly HashSet<string> s_EU_Endpoints = new()
5349
{
5450
"francecentral",
5551
"francesouth",
@@ -64,7 +60,7 @@ internal static class Statsbeat
6460
"westeurope",
6561
};
6662

67-
internal static readonly HashSet<string> Non_EU_Endpoints = new()
63+
internal static readonly HashSet<string> s_non_EU_Endpoints = new()
6864
{
6965
"australiacentral",
7066
"australiacentral2",
@@ -101,6 +97,35 @@ internal static class Statsbeat
10197
"westus3",
10298
};
10399

100+
internal Statsbeat(ConnectionVars connectionStringVars)
101+
{
102+
_statsbeat_ConnectionString = GetStatsbeatConnectionString(connectionStringVars?.IngestionEndpoint);
103+
104+
// Initialize only if we are able to determine the correct region to send the data to.
105+
if (_statsbeat_ConnectionString == null)
106+
{
107+
throw new InvalidOperationException("Cannot initialize statsbeat");
108+
}
109+
110+
_customer_Ikey = connectionStringVars?.InstrumentationKey;
111+
112+
s_myMeter.CreateObservableGauge("AttachStatsbeat", () => GetAttachStatsbeat());
113+
114+
// Configure for attach statsbeat which has collection
115+
// schedule of 24 hrs == 86400000 milliseconds.
116+
// TODO: Follow up in spec to confirm the behavior
117+
// in case if the app exits before 24hrs duration.
118+
var exporterOptions = new AzureMonitorExporterOptions();
119+
exporterOptions.DisableOfflineStorage = true;
120+
exporterOptions.ConnectionString = _statsbeat_ConnectionString;
121+
122+
_attachStatsbeatMeterProvider = Sdk.CreateMeterProviderBuilder()
123+
.AddMeter("AttachStatsbeatMeter")
124+
.AddReader(new PeriodicExportingMetricReader(new AzureMonitorMetricExporter(exporterOptions), AttachStatsbeatInterval)
125+
{ TemporalityPreference = MetricReaderTemporalityPreference.Delta })
126+
.Build();
127+
}
128+
104129
private static string GetOS()
105130
{
106131
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
@@ -119,90 +144,54 @@ private static string GetOS()
119144
return "unknown";
120145
}
121146

122-
internal static void InitializeAttachStatsbeat(string connectionString)
147+
internal static string? GetStatsbeatConnectionString(string? ingestionEndpoint)
123148
{
124-
// check whether it is disabled or already initialized.
125-
if (s_isEnabled && s_attachStatsBeatMeterProvider == null)
149+
var patternMatch = s_endpoint_pattern.Match(ingestionEndpoint);
150+
string? statsbeatConnectionString = null;
151+
if (patternMatch.Success)
126152
{
127-
if (s_statsBeat_ConnectionString == null)
153+
var endpoint = patternMatch.Groups[1].Value;
154+
if (s_EU_Endpoints.Contains(endpoint))
128155
{
129-
var parsedConectionString = ConnectionStringParser.GetValues(connectionString);
130-
SetCustomerIkey(parsedConectionString.InstrumentationKey);
131-
SetStatsbeatConnectionString(parsedConectionString.IngestionEndpoint);
156+
statsbeatConnectionString = Statsbeat_ConnectionString_EU;
132157
}
133-
134-
if (!s_isEnabled)
158+
else if (s_non_EU_Endpoints.Contains(endpoint))
135159
{
136-
// TODO: log
137-
return;
160+
statsbeatConnectionString = Statsbeat_ConnectionString_NonEU;
138161
}
139-
140-
s_myMeter.CreateObservableGauge("AttachStatsBeat", () => GetAttachStatsBeat());
141-
142-
// Configure for attach statsbeat which has collection
143-
// schedule of 24 hrs == 86400000 milliseconds.
144-
// TODO: Follow up in spec to confirm the behavior
145-
// in case if the app exits before 24hrs duration.
146-
var exporterOptions = new AzureMonitorExporterOptions();
147-
exporterOptions.DisableOfflineStorage = true;
148-
exporterOptions.ConnectionString = s_statsBeat_ConnectionString;
149-
150-
s_attachStatsBeatMeterProvider = Sdk.CreateMeterProviderBuilder()
151-
.AddMeter("AttachStatsBeatMeter")
152-
.AddReader(new PeriodicExportingMetricReader(new AzureMonitorMetricExporter(exporterOptions), AttachStatsBeatInterval)
153-
{ TemporalityPreference = MetricReaderTemporalityPreference.Delta })
154-
.Build();
155162
}
156-
}
157163

158-
internal static void SetCustomerIkey(string instrumentationKey)
159-
{
160-
s_customer_Ikey = instrumentationKey;
164+
return statsbeatConnectionString;
161165
}
162166

163-
internal static void SetStatsbeatConnectionString(string ingestionEndpoint)
167+
private Measurement<int> GetAttachStatsbeat()
164168
{
165-
var patternMatch = s_endpoint_pattern.Match(ingestionEndpoint);
166-
if (patternMatch.Success)
169+
try
167170
{
168-
var endpoint = patternMatch.Groups[1].Value;
169-
if (EU_Endpoints.Contains(endpoint))
170-
{
171-
s_statsBeat_ConnectionString = StatsBeat_ConnectionString_EU;
172-
}
173-
else if (Non_EU_Endpoints.Contains(endpoint))
174-
{
175-
s_statsBeat_ConnectionString = StatsBeat_ConnectionString_NonEU;
176-
}
177-
else
171+
if (_resourceProvider == null)
178172
{
179-
// disable statsbeat
180-
s_isEnabled = false;
173+
SetResourceProviderDetails();
181174
}
182-
}
183-
}
184175

185-
private static Measurement<int> GetAttachStatsBeat()
186-
{
187-
if (s_resourceProvider == null)
176+
return
177+
new Measurement<int>(1,
178+
new("rp", _resourceProvider),
179+
new("rpId", _resourceProviderId),
180+
new("attach", "sdk"),
181+
new("cikey", _customer_Ikey),
182+
new("runtimeVersion", s_runtimeVersion),
183+
new("language", "dotnet"),
184+
new("version", s_sdkVersion),
185+
new("os", s_operatingSystem));
186+
}
187+
catch (Exception ex)
188188
{
189-
SetResourceProviderDetails();
189+
AzureMonitorExporterEventSource.Log.WriteWarning("ErrorGettingStatsbeatData", ex);
190+
return new Measurement<int>();
190191
}
191-
192-
// TODO: Add os to the list
193-
return
194-
new Measurement<int>(1,
195-
new("rp", s_resourceProvider),
196-
new("rpId", s_resourceProviderId),
197-
new("attach", "sdk"),
198-
new("cikey", s_customer_Ikey),
199-
new("runtimeVersion", s_runtimeVersion),
200-
new("language", "dotnet"),
201-
new("version", s_sdkVersion),
202-
new("os", s_operatingSystem));
203192
}
204193

205-
private static VmMetadataResponse GetVmMetadataResponse()
194+
private static VmMetadataResponse? GetVmMetadataResponse()
206195
{
207196
try
208197
{
@@ -222,17 +211,17 @@ private static VmMetadataResponse GetVmMetadataResponse()
222211
}
223212
}
224213

225-
private static void SetResourceProviderDetails()
214+
private void SetResourceProviderDetails()
226215
{
227216
var appSvcWebsiteName = Environment.GetEnvironmentVariable("WEBSITE_SITE_NAME");
228217
if (appSvcWebsiteName != null)
229218
{
230-
s_resourceProvider = "appsvc";
231-
s_resourceProviderId = appSvcWebsiteName;
219+
_resourceProvider = "appsvc";
220+
_resourceProviderId = appSvcWebsiteName;
232221
var appSvcWebsiteHostName = Environment.GetEnvironmentVariable("WEBSITE_HOME_STAMPNAME");
233222
if (!string.IsNullOrEmpty(appSvcWebsiteHostName))
234223
{
235-
s_resourceProviderId += "/" + appSvcWebsiteHostName;
224+
_resourceProviderId += "/" + appSvcWebsiteHostName;
236225
}
237226

238227
return;
@@ -241,8 +230,8 @@ private static void SetResourceProviderDetails()
241230
var functionsWorkerRuntime = Environment.GetEnvironmentVariable("FUNCTIONS_WORKER_RUNTIME");
242231
if (functionsWorkerRuntime != null)
243232
{
244-
s_resourceProvider = "functions";
245-
s_resourceProviderId = Environment.GetEnvironmentVariable("WEBSITE_HOSTNAME");
233+
_resourceProvider = "functions";
234+
_resourceProviderId = Environment.GetEnvironmentVariable("WEBSITE_HOSTNAME");
246235

247236
return;
248237
}
@@ -251,17 +240,22 @@ private static void SetResourceProviderDetails()
251240

252241
if (vmMetadata != null)
253242
{
254-
s_resourceProvider = "vm";
255-
s_resourceProviderId = s_resourceProviderId = vmMetadata.vmId + "/" + vmMetadata.subscriptionId;
243+
_resourceProvider = "vm";
244+
_resourceProviderId = _resourceProviderId = vmMetadata.vmId + "/" + vmMetadata.subscriptionId;
256245

257246
// osType takes precedence.
258247
s_operatingSystem = vmMetadata.osType.ToLower(CultureInfo.InvariantCulture);
259248

260249
return;
261250
}
262251

263-
s_resourceProvider = "unknown";
264-
s_resourceProviderId = "unknown";
252+
_resourceProvider = "unknown";
253+
_resourceProviderId = "unknown";
254+
}
255+
256+
public void Dispose()
257+
{
258+
_attachStatsbeatMeterProvider?.Dispose();
265259
}
266260
}
267261
}

sdk/monitor/Azure.Monitor.OpenTelemetry.Exporter/tests/Azure.Monitor.OpenTelemetry.Exporter.Tests/StatsbeatTests.cs

Lines changed: 11 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,6 @@
22
// Licensed under the MIT License.
33

44
using System;
5-
using System.Collections.Generic;
6-
using System.Linq;
7-
using System.Text;
8-
using System.Threading.Tasks;
9-
using Azure.Core;
105
using Azure.Monitor.OpenTelemetry.Exporter.Internals;
116
using Azure.Monitor.OpenTelemetry.Exporter.Internals.ConnectionString;
127
using Xunit;
@@ -29,15 +24,11 @@ public class StatsbeatTests
2924
[InlineData("westeurope")]
3025
public void StatsbeatConnectionStringIsSetBasedOnCustomersConnectionStringEndpointInEU(string euEndpoint)
3126
{
32-
var customer_ConnectionString = $"InstrumentationKey=1aa11111-bbbb-1ccc-8ddd-eeeeffff3333;IngestionEndpoint=https://{euEndpoint}.in.applicationinsights.azure.com/";
33-
var parsedConectionString = ConnectionStringParser.GetValues(customer_ConnectionString);
27+
var customer_ConnectionString = $"InstrumentationKey=00000000-0000-0000-0000-000000000000;IngestionEndpoint=https://{euEndpoint}.in.applicationinsights.azure.com/";
28+
var connectionStringVars = ConnectionStringParser.GetValues(customer_ConnectionString);
29+
var statsBeatInstance = new Statsbeat(connectionStringVars);
3430

35-
Statsbeat.SetStatsbeatConnectionString(parsedConectionString.IngestionEndpoint);
36-
37-
Assert.Equal(Statsbeat.StatsBeat_ConnectionString_EU, Statsbeat.s_statsBeat_ConnectionString);
38-
39-
// Reset Statsbeat Connection String
40-
Statsbeat.s_statsBeat_ConnectionString = null;
31+
Assert.Equal(Statsbeat.Statsbeat_ConnectionString_EU, statsBeatInstance._statsbeat_ConnectionString);
4132
}
4233

4334
[Theory]
@@ -76,29 +67,20 @@ public void StatsbeatConnectionStringIsSetBasedOnCustomersConnectionStringEndpoi
7667
[InlineData("westus3")]
7768
public void StatsbeatConnectionStringIsSetBasedOnCustomersConnectionStringEndpointInNonEU(string nonEUEndpoint)
7869
{
79-
var customer_ConnectionString = $"InstrumentationKey=1aa11111-bbbb-1ccc-8ddd-eeeeffff3333;IngestionEndpoint=https://{nonEUEndpoint}.in.applicationinsights.azure.com/";
80-
var parsedConectionString = ConnectionStringParser.GetValues(customer_ConnectionString);
81-
82-
Statsbeat.SetStatsbeatConnectionString(parsedConectionString.IngestionEndpoint);
70+
var customer_ConnectionString = $"InstrumentationKey=00000000-0000-0000-0000-000000000000;IngestionEndpoint=https://{nonEUEndpoint}.in.applicationinsights.azure.com/";
71+
var connectionStringVars = ConnectionStringParser.GetValues(customer_ConnectionString);
72+
var statsBeatInstance = new Statsbeat(connectionStringVars);
8373

84-
Assert.Equal(Statsbeat.StatsBeat_ConnectionString_NonEU, Statsbeat.s_statsBeat_ConnectionString);
85-
86-
// Reset Statsbeat Connection String
87-
Statsbeat.s_statsBeat_ConnectionString = null;
74+
Assert.Equal(Statsbeat.Statsbeat_ConnectionString_NonEU, statsBeatInstance._statsbeat_ConnectionString);
8875
}
8976

9077
[Fact]
9178
public void StatsbeatIsNotInitializedForUnknownRegions()
9279
{
93-
var customer_ConnectionString = "InstrumentationKey=1aa11111-bbbb-1ccc-8ddd-eeeeffff3333;IngestionEndpoint=https://foo.in.applicationinsights.azure.com/";
94-
var parsedConectionString = ConnectionStringParser.GetValues(customer_ConnectionString);
95-
96-
Statsbeat.SetStatsbeatConnectionString(parsedConectionString.IngestionEndpoint);
97-
98-
Assert.Null(Statsbeat.s_statsBeat_ConnectionString);
80+
var customer_ConnectionString = "InstrumentationKey=00000000-0000-0000-0000-000000000000;IngestionEndpoint=https://foo.in.applicationinsights.azure.com/";
9981

100-
// Reset Statsbeat Connection String
101-
Statsbeat.s_statsBeat_ConnectionString = null;
82+
var connectionStringVars = ConnectionStringParser.GetValues(customer_ConnectionString);
83+
Assert.Throws<InvalidOperationException>(() => new Statsbeat(connectionStringVars));
10284
}
10385
}
10486
}

0 commit comments

Comments
 (0)