Skip to content

Commit 70571a9

Browse files
Refactor telemetry & exporter setup: deprecations, noise reduction, and API updates (#11260)
1 parent 4c71050 commit 70571a9

File tree

6 files changed

+83
-272
lines changed

6 files changed

+83
-272
lines changed

release_notes.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,4 @@
44
- My change description (#PR)
55
-->
66
- Rev `Microsoft.Azure.Functions.DotNetIsolatedNativeHost` to `1.0.13` (#11269)
7+
- Refactor telemetry & exporter setup: deprecations, noise reduction, and API updates (#11260)

src/WebJobs.Script.Grpc/Channel/GrpcWorkerChannel.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1284,7 +1284,7 @@ internal void Log(GrpcEvent msg)
12841284
// TODO fix RpcException catch all https://github.com/Azure/azure-functions-dotnet-worker/issues/370
12851285
var exception = new Workers.Rpc.RpcException(rpcLog.Message, rpcLog.Exception.Message, rpcLog.Exception.StackTrace);
12861286
context.Logger.Log(logLevel, new EventId(0, rpcLog.EventId), rpcLog.Message, exception, (state, exc) => state);
1287-
Activity.Current?.RecordException(exception);
1287+
Activity.Current?.AddException(exception);
12881288
Activity.Current?.SetStatus(ActivityStatusCode.Error, exception.Message);
12891289
}
12901290
else

src/WebJobs.Script.WebHost/DependencyInjection/DependencyValidator/DependencyValidator.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,10 +55,11 @@ private static ExpectedDependencyBuilder CreateExpectedDependencies()
5555
.Optional<FunctionsSyncService>() // Conditionally registered.
5656
.OptionalExternal("Microsoft.AspNetCore.DataProtection.Internal.DataProtectionHostedService", "Microsoft.AspNetCore.DataProtection", "adb9793829ddae60") // Popularly-registered by DataProtection.
5757
.OptionalExternal("Microsoft.Extensions.Diagnostics.HealthChecks.HealthCheckPublisherHostedService", "Microsoft.Extensions.Diagnostics.HealthChecks", "adb9793829ddae60") // Popularly-registered by Health Check Monitor.
58-
.OptionalExternal("OpenTelemetry.Extensions.Hosting.Implementation.TelemetryHostedService", "OpenTelemetry.Extensions.Hosting", "7bd6737fe5b67e3c") // Enable OpenTelemetry.Net instrumentation library
5958
.OptionalExternal("Microsoft.Azure.WebJobs.Hosting.PrimaryHostCoordinator", "Microsoft.Azure.WebJobs.Host", "31bf3856ad364e35")
6059
.OptionalExternal("Microsoft.Azure.WebJobs.Host.Scale.ConcurrencyManagerService", "Microsoft.Azure.WebJobs.Host", "31bf3856ad364e35")
61-
.OptionalExternal("Microsoft.Azure.WebJobs.Host.Scale.ScaleMonitorService", "Microsoft.Azure.WebJobs.Host", "31bf3856ad364e35");
60+
.OptionalExternal("Microsoft.Azure.WebJobs.Host.Scale.ScaleMonitorService", "Microsoft.Azure.WebJobs.Host", "31bf3856ad364e35")
61+
.OptionalExternal("OpenTelemetry.Extensions.Hosting.Implementation.TelemetryHostedService", "OpenTelemetry.Extensions.Hosting", "7bd6737fe5b67e3c") // Enable OpenTelemetry.Net instrumentation library
62+
.OptionalExternal("Azure.Monitor.OpenTelemetry.Exporter.OpenTelemetryBuilderExtensions", "Azure.Monitor.OpenTelemetry.Exporter", "92742159e12e44c8"); // Enable live metrics
6263

6364
expected.ExpectSubcollection<ILoggerProvider>()
6465
.Expect<FunctionFileLoggerProvider>()

src/WebJobs.Script.WebHost/DependencyInjection/DependencyValidator/ServiceMatch.cs

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright (c) .NET Foundation. All rights reserved.
1+
// Copyright (c) .NET Foundation. All rights reserved.
22
// Licensed under the MIT License. See License.txt in the project root for license information.
33

44
using System;
@@ -218,7 +218,7 @@ private static IEnumerable<InvalidServiceDescriptor> FindCollectionDifferences(I
218218
{
219219
return !expectedServices.Any(e => IsMatch(e, r))
220220
&& !optionalServices.Any(o => IsMatch(o, r))
221-
&& !optionalExternalServices.Any(o => o.IsMatch(r.ImplementationType));
221+
&& !optionalExternalServices.Any(o => o.IsMatch(r));
222222
})
223223
.Select(p => new InvalidServiceDescriptor(p, InvalidServiceDescriptorReason.Invalid));
224224

@@ -247,18 +247,26 @@ public ExternalService(string typeName, string assemblyName, string assemblyPubl
247247

248248
public string AssemblyPublicKey { get; }
249249

250-
public bool IsMatch(Type serviceType)
250+
public bool IsMatch(ServiceDescriptor descriptor)
251251
{
252-
if (serviceType == null)
252+
if (descriptor.ImplementationType is { } serviceType)
253253
{
254-
return false;
254+
var serviceAssemblyName = serviceType.Assembly.GetName();
255+
256+
return serviceType.FullName == TypeName
257+
&& serviceAssemblyName.Name == AssemblyName
258+
&& GetPublicKeyTokenString(serviceAssemblyName.GetPublicKeyToken()) == AssemblyPublicKey;
255259
}
260+
else if (descriptor.ImplementationFactory?.Target?.GetType() is { } implementationFactory)
261+
{
262+
var factoryAssemblyName = implementationFactory.Assembly.GetName();
256263

257-
var serviceAssemblyName = serviceType.Assembly.GetName();
264+
return implementationFactory.DeclaringType.FullName == TypeName
265+
&& factoryAssemblyName.Name == AssemblyName
266+
&& GetPublicKeyTokenString(factoryAssemblyName.GetPublicKeyToken()) == AssemblyPublicKey;
267+
}
258268

259-
return serviceType.FullName == TypeName
260-
&& serviceAssemblyName.Name == AssemblyName
261-
&& GetPublicKeyTokenString(serviceAssemblyName.GetPublicKeyToken()) == AssemblyPublicKey;
269+
return false;
262270
}
263271

264272
private static string GetPublicKeyTokenString(byte[] token)

src/WebJobs.Script/Diagnostics/OpenTelemetry/OpenTelemetryConfigurationExtensions.cs

Lines changed: 61 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright (c) .NET Foundation. All rights reserved.
1+
// Copyright (c) .NET Foundation. All rights reserved.
22
// Licensed under the MIT License. See License.txt in the project root for license information.
33

44
using System;
@@ -8,7 +8,6 @@
88
using Azure.Core;
99
using Azure.Identity;
1010
using Azure.Monitor.OpenTelemetry.Exporter;
11-
using Azure.Monitor.OpenTelemetry.LiveMetrics;
1211
using Microsoft.AspNetCore.Routing;
1312
using Microsoft.Azure.WebJobs.Script.Metrics;
1413
using Microsoft.Extensions.Configuration;
@@ -34,122 +33,106 @@ internal static void ConfigureOpenTelemetry(this ILoggingBuilder loggingBuilder,
3433
bool enableOtlp = !string.IsNullOrWhiteSpace(otlpEndpoint);
3534
bool enableAzureMonitor = !string.IsNullOrWhiteSpace(azMonConnectionString);
3635

37-
TokenCredential credential = enableAzureMonitor ? GetTokenCredential(context.Configuration) : null;
38-
3936
// If placeholder mode is disabled and both OTLP and Azure Monitor are not enabled, avoid configuring OpenTelemetry.
4037
if (!enableOtlp && !enableAzureMonitor && telemetryMode != TelemetryMode.Placeholder)
4138
{
4239
return;
4340
}
4441

45-
loggingBuilder.ConfigureLogging(enableOtlp, enableAzureMonitor, azMonConnectionString, credential, telemetryMode);
42+
loggingBuilder.ConfigureLogging();
4643

4744
loggingBuilder.Services
4845
.AddOpenTelemetry()
46+
.ConfigureExporters(context.Configuration, enableOtlp, enableAzureMonitor, azMonConnectionString, telemetryMode)
4947
.ConfigureResource(r => ConfigureResource(r))
50-
.ConfigureMetrics(enableOtlp, enableAzureMonitor, azMonConnectionString, credential, telemetryMode)
51-
.ConfigureTracing(enableOtlp, enableAzureMonitor, azMonConnectionString, credential, telemetryMode)
48+
.ConfigureMetrics()
49+
.ConfigureTracing()
5250
.ConfigureEventLogLevel(context.Configuration);
5351

5452
// Azure SDK instrumentation is experimental.
5553
AppContext.SetSwitch("Azure.Experimental.EnableActivitySource", true);
5654
}
5755

58-
private static IOpenTelemetryBuilder ConfigureMetrics(this IOpenTelemetryBuilder builder, bool enableOtlp, bool enableAzureMonitor, string azMonConnectionString, TokenCredential credential, TelemetryMode telemetryMode)
56+
private static IOpenTelemetryBuilder ConfigureExporters(this IOpenTelemetryBuilder builder, IConfiguration configuration, bool enableOtlp, bool enableAzureMonitor, string azMonConnectionString, TelemetryMode telemetryMode)
57+
{
58+
// Avoid configuring the exporter in placeholder mode, as it will default to sending telemetry to the predefined endpoint. These transmissions will be unsuccessful and create unnecessary noise.
59+
if (telemetryMode == TelemetryMode.Placeholder)
60+
{
61+
return builder;
62+
}
63+
64+
if (enableOtlp)
65+
{
66+
builder.UseOtlpExporter();
67+
}
68+
69+
if (enableAzureMonitor)
70+
{
71+
TokenCredential credential = GetTokenCredential(configuration);
72+
builder.UseAzureMonitorExporter(options => ConfigureAzureMonitorOptions(options, azMonConnectionString, credential));
73+
}
74+
75+
return builder;
76+
}
77+
78+
private static IOpenTelemetryBuilder ConfigureMetrics(this IOpenTelemetryBuilder builder)
5979
{
6080
return builder.WithMetrics(builder =>
6181
{
6282
builder.AddAspNetCoreInstrumentation()
63-
.AddRuntimeInstrumentation()
64-
.AddProcessInstrumentation()
65-
.AddMeter(HostMetrics.FaasMeterName)
66-
.AddView(HostMetrics.FaasInvokeDuration, new ExplicitBucketHistogramConfiguration
67-
{
68-
Boundaries = new double[] { 0.005, 0.01, 0.025, 0.05, 0.075, 0.1, 0.25, 0.5, 0.75, 1, 2.5, 5, 7.5, 10 }
69-
});
70-
71-
// Avoid configuring the exporter in placeholder mode, as it will default to sending telemetry to the predefined endpoint. These transmissions will be unsuccessful and create unnecessary noise.
72-
if (telemetryMode != TelemetryMode.Placeholder)
73-
{
74-
if (enableOtlp)
75-
{
76-
builder.AddOtlpExporter();
77-
}
78-
if (enableAzureMonitor)
83+
.AddRuntimeInstrumentation()
84+
.AddProcessInstrumentation()
85+
.AddMeter(HostMetrics.FaasMeterName)
86+
.AddView(HostMetrics.FaasInvokeDuration, new ExplicitBucketHistogramConfiguration
7987
{
80-
builder.AddAzureMonitorMetricExporter(opt => ConfigureAzureMonitorOptions(opt, azMonConnectionString, credential));
81-
}
82-
}
88+
Boundaries = new double[] { 0.005, 0.01, 0.025, 0.05, 0.075, 0.1, 0.25, 0.5, 0.75, 1, 2.5, 5, 7.5, 10 },
89+
RecordMinMax = true
90+
});
8391
});
8492
}
8593

86-
private static IOpenTelemetryBuilder ConfigureTracing(this IOpenTelemetryBuilder builder, bool enableOtlp, bool enableAzureMonitor, string azMonConnectionString, TokenCredential credential, TelemetryMode telemetryMode)
94+
private static IOpenTelemetryBuilder ConfigureTracing(this IOpenTelemetryBuilder builder)
8795
{
8896
return builder.WithTracing(builder =>
8997
{
9098
builder
91-
.AddSource("Azure.*")
92-
.AddSource("Microsoft.Azure.Webjobs.Extensions.*")
93-
.AddSource("WebJobs.Extensions.DurableTask")
94-
.AddSource("DurableTask.*")
95-
.AddAspNetCoreInstrumentation(o =>
96-
{
97-
o.EnrichWithHttpResponse = (activity, httpResponse) =>
99+
.AddSource("Azure.Messaging.ServiceBus.ServiceBusProcessor")
100+
.AddSource("Azure.Messaging.EventHubs.EventProcessor")
101+
.AddSource("Azure.Functions.Extensions.Mcp")
102+
.AddSource("Microsoft.Azure.WebJobs.Extensions.*")
103+
.AddSource("Microsoft.Azure.WebJobs")
104+
.AddSource("WebJobs.Extensions.DurableTask")
105+
.AddSource("DurableTask.*")
106+
.AddAspNetCoreInstrumentation(o =>
98107
{
99-
if (Activity.Current is not null)
108+
o.EnrichWithHttpResponse = (activity, httpResponse) =>
100109
{
101-
Activity.Current.AddTag(ResourceSemanticConventions.FaaSTrigger, OpenTelemetryConstants.HttpTriggerType);
102-
103-
var routingFeature = httpResponse.HttpContext.Features.Get<IRoutingFeature>();
104-
if (routingFeature is null)
110+
if (Activity.Current is not null)
105111
{
106-
return;
107-
}
108-
109-
var template = routingFeature.RouteData.Routers.FirstOrDefault(r => r is Route) as Route;
110-
Activity.Current.DisplayName = $"{Activity.Current.DisplayName} {template?.RouteTemplate}";
111-
Activity.Current.AddTag(ResourceSemanticConventions.HttpRoute, template?.RouteTemplate);
112-
}
113-
};
114-
});
115-
116-
// Avoid configuring the exporter in placeholder mode, as it will default to sending telemetry to the predefined endpoint. These transmissions will be unsuccessful and create unnecessary noise.
117-
if (telemetryMode != TelemetryMode.Placeholder)
118-
{
119-
if (enableOtlp)
120-
{
121-
builder.AddOtlpExporter();
122-
}
112+
Activity.Current.AddTag(ResourceSemanticConventions.FaaSTrigger, OpenTelemetryConstants.HttpTriggerType);
123113

124-
if (enableAzureMonitor)
125-
{
126-
builder.AddAzureMonitorTraceExporter(opt => ConfigureAzureMonitorOptions(opt, azMonConnectionString, credential));
127-
}
128-
}
114+
var routingFeature = httpResponse.HttpContext.Features.Get<IRoutingFeature>();
115+
if (routingFeature is null)
116+
{
117+
return;
118+
}
129119

130-
builder.AddProcessor(ActivitySanitizingProcessor.Instance);
120+
var template = routingFeature.RouteData.Routers.FirstOrDefault(r => r is Route) as Route;
121+
Activity.Current.DisplayName = $"{Activity.Current.DisplayName} {template?.RouteTemplate}";
122+
Activity.Current.AddTag(ResourceSemanticConventions.HttpRoute, template?.RouteTemplate);
123+
}
124+
};
125+
o.Filter = context => !context.Request.Host.Host.Equals("127.0.0.1", StringComparison.OrdinalIgnoreCase);
126+
})
127+
.AddProcessor(ActivitySanitizingProcessor.Instance);
131128
});
132129
}
133130

134-
private static ILoggingBuilder ConfigureLogging(this ILoggingBuilder builder, bool enableOtlp, bool enableAzureMonitor, string azMonConnectionString, TokenCredential credential, TelemetryMode telemetryMode)
131+
private static ILoggingBuilder ConfigureLogging(this ILoggingBuilder builder)
135132
{
136133
builder.AddOpenTelemetry(o =>
137134
{
138135
o.SetResourceBuilder(ConfigureResource(ResourceBuilder.CreateDefault()));
139-
140-
// Avoid configuring the exporter in placeholder mode, as it will default to sending telemetry to the predefined endpoint. These transmissions will be unsuccessful and create unnecessary noise.
141-
if (telemetryMode != TelemetryMode.Placeholder)
142-
{
143-
if (enableOtlp)
144-
{
145-
o.AddOtlpExporter();
146-
}
147-
if (enableAzureMonitor)
148-
{
149-
o.AddAzureMonitorLogExporter(options => ConfigureAzureMonitorOptions(options, azMonConnectionString, credential));
150-
}
151-
}
152-
153136
o.IncludeFormattedMessage = true;
154137
o.IncludeScopes = false;
155138
});

0 commit comments

Comments
 (0)