Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

OpenTelemetry support #9985

Merged
merged 25 commits into from
Apr 13, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
6c76fe5
Initial commit
RohitRanjanMS Apr 10, 2024
93a98af
Fixed UT
RohitRanjanMS Apr 10, 2024
24b7ddc
-
RohitRanjanMS Apr 10, 2024
acba142
Apply suggestions from code review
RohitRanjanMS Apr 10, 2024
7113c05
Code review comments.
RohitRanjanMS Apr 11, 2024
cffa41f
Code review.
RohitRanjanMS Apr 11, 2024
1326913
Fixed UT
RohitRanjanMS Apr 11, 2024
22dfdaa
Adding handling of EventSources that were created before we set the E…
RohitRanjanMS Apr 11, 2024
b2eec42
Updated trace and logs filetring.
RohitRanjanMS Apr 12, 2024
8fd637a
Fixed build.
RohitRanjanMS Apr 12, 2024
109cccd
Do not assign permissions when file does not exist (#9982)
jviau Apr 10, 2024
4657a2a
Ensuring non http invocation responses are not processed by IHttpProx…
kshyju Apr 10, 2024
0cb401d
DotnetIsolated worker artifact clean up - moving symbols (#9983)
kshyju Apr 10, 2024
f42f2b5
Special case EventGrid sourced blob triggers for grouping (#9987)
jviau Apr 11, 2024
7c390e8
Fix CI branches and missing 'minorVersionPrefix' (#9995)
jviau Apr 11, 2024
8c95991
Remove crank tool (#9992)
jviau Apr 11, 2024
5c59f22
Minor code cleanup in FunctionsSyncManager (#9991)
kshyju Apr 11, 2024
44c4a62
Update Node.js Worker to 3.10.0 (#9999)
castrodd Apr 12, 2024
2350dc9
Update WebHost.deps.json (#10005)
jviau Apr 12, 2024
2880141
Merge branch 'dev' of https://github.com/Azure/azure-functions-host i…
RohitRanjanMS Apr 12, 2024
d1e141a
Revert "Fixed build."
RohitRanjanMS Apr 12, 2024
147f7d9
Dev changes.
RohitRanjanMS Apr 12, 2024
5afdf55
Updating deps.json
RohitRanjanMS Apr 12, 2024
85dde60
Updated runassenblies
RohitRanjanMS Apr 12, 2024
161f977
Fixed RuntimeAssembliesUpdateTests
RohitRanjanMS Apr 12, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 3 additions & 12 deletions src/WebJobs.Script.Grpc/Channel/GrpcWorkerChannel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1678,16 +1678,7 @@ private void AddSample<T>(List<T> samples, T sample)
private void AddAdditionalTraceContext(MapField<string, string> attributes, ScriptInvocationContext context)
{
bool isOtelEnabled = _scriptHostOptions?.Value.TelemetryMode == TelemetryMode.OpenTelemetry;
if (_scriptHostOptions?.Value.TelemetryMode == TelemetryMode.OpenTelemetry)
{
isOtelEnabled = true;
}

bool isAIEnabled = _environment.IsApplicationInsightsAgentEnabled();
RohitRanjanMS marked this conversation as resolved.
Show resolved Hide resolved
if (_environment.IsApplicationInsightsAgentEnabled())
{
isAIEnabled = true;
}

if (isOtelEnabled || isAIEnabled)
{
Expand All @@ -1697,7 +1688,7 @@ private void AddAdditionalTraceContext(MapField<string, string> attributes, Scri

if (isOtelEnabled)
{
Activity.Current?.AddTag(ResourceAttributeConstants.AttributeInstance, id);
Activity.Current?.AddTag(ResourceSemanticConventions.FaaSInstance, id);
}
if (isAIEnabled)
{
Expand All @@ -1724,8 +1715,8 @@ private void AddAdditionalTraceContext(MapField<string, string> attributes, Scri

if (isOtelEnabled)
{
Activity.Current?.AddTag(ResourceAttributeConstants.AttributeName, context.FunctionMetadata.Name);
Activity.Current?.AddTag(ResourceAttributeConstants.AttributeTrigger, ResourceAttributeConstants.ResolveTriggerType(context.FunctionMetadata?.Trigger?.Type));
Activity.Current?.AddTag(ResourceSemanticConventions.FaaSName, context.FunctionMetadata.Name);
Activity.Current?.AddTag(ResourceSemanticConventions.FaaSTrigger, OpenTelemetryConstants.ResolveTriggerType(context.FunctionMetadata?.Trigger?.Type));
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ public async Task ExecuteAsync(HttpRequest request, CancellationToken cancellati
}

// Add tag for cold start
Activity.Current?.AddTag(ResourceAttributeConstants.AttributeColdStart, true);
Activity.Current?.AddTag(ResourceSemanticConventions.FaaSColdStart, true);
}

var sw = ValueStopwatch.StartNew();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ public void Start()
{
listener.EnableMeasurementEvents(instrument);

var instanceIdTag = instrument.Meter.Tags.FirstOrDefault(t => t.Key == ResourceAttributeConstants.ServiceInstanceId);
var instanceIdTag = instrument.Meter.Tags.FirstOrDefault(t => t.Key == ResourceSemanticConventions.ServiceInstanceId);
InstanceId = instanceIdTag.Value?.ToString() ?? string.Empty;
}
};
Expand Down
2 changes: 1 addition & 1 deletion src/WebJobs.Script.WebHost/WebJobsScriptHostService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -875,10 +875,10 @@ private async Task Orphan(IHost instance, CancellationToken cancellationToken =
else
{
DisposeDependencyTrackingModule(instance);
FlushOpenTelemetry(instance);
instance.Dispose();
_logger.LogDebug("ScriptHost disposed");
}
FlushOpenTelemetry(instance);
}
}

Expand Down
4 changes: 4 additions & 0 deletions src/WebJobs.Script/Config/ScriptJobHostOptionsSetup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,10 @@ public void Configure(ScriptJobHostOptions options)
{
options.TelemetryMode = telemetryMode;
}
else
{
options.TelemetryMode = TelemetryMode.ApplicationInsights;
}
}

private void ConfigureFunctionTimeout(ScriptJobHostOptions options)
Expand Down
6 changes: 0 additions & 6 deletions src/WebJobs.Script/Config/ScriptSettingsManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -70,12 +70,6 @@ public virtual string OtlpEndpoint
set => SetSetting(EnvironmentSettingNames.OtlpEndpoint, value);
}

public virtual string TelemetryMode
{
get => GetSetting(EnvironmentSettingNames.TelemetryMode);
set => SetSetting(EnvironmentSettingNames.TelemetryMode, value);
}

public virtual string GetSetting(string settingKey)
{
if (string.IsNullOrEmpty(settingKey))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ namespace Microsoft.Azure.WebJobs.Script.Diagnostics.OpenTelemetry
{
internal class ActivitySanitizingProcessor : BaseProcessor<Activity>
{
private static readonly ImmutableArray<string> TagsToSanitize = ImmutableArray.Create("url.query", "url.full");
private static readonly ImmutableArray<string> TagsToSanitize = ImmutableArray.Create(OpenTelemetryConstants.QueryUrl, OpenTelemetryConstants.FullUrl);
RohitRanjanMS marked this conversation as resolved.
Show resolved Hide resolved

private ActivitySanitizingProcessor() { }

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,44 +3,44 @@

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using OpenTelemetry.Resources;

namespace Microsoft.Azure.WebJobs.Script.Diagnostics.OpenTelemetry
{
internal sealed class FunctionsResourceDetector : IResourceDetector
{
internal static readonly IReadOnlyDictionary<string, string> ResourceAttributes = new Dictionary<string, string>(1)
{
{ ResourceAttributeConstants.CloudRegion, ResourceAttributeConstants.RegionNameEnvVar },
};

public Resource Detect()
{
List<KeyValuePair<string, object>> attributeList = new(5);
List<KeyValuePair<string, object>> attributeList = new(9);
try
{
var siteName = Environment.GetEnvironmentVariable(ResourceAttributeConstants.SiteNameEnvVar);
attributeList.Add(new KeyValuePair<string, object>(ResourceAttributeConstants.CloudProvider, ResourceAttributeConstants.AzureCloudProviderValue));
attributeList.Add(new KeyValuePair<string, object>(ResourceAttributeConstants.CloudPlatform, ResourceAttributeConstants.AzurePlatformValue));
string serviceName = Environment.GetEnvironmentVariable(OpenTelemetryConstants.SiteNameEnvVar);
string version = typeof(ScriptHost).Assembly.GetName().Version.ToString();

var version = Assembly.GetEntryAssembly()?.GetName().Version.ToString() ?? "0:0:0";
attributeList.Add(new KeyValuePair<string, object>(ResourceAttributeConstants.AttributeVersion, version));
if (!string.IsNullOrEmpty(siteName))
attributeList.Add(new KeyValuePair<string, object>(ResourceSemanticConventions.ServiceVersion, version));
attributeList.Add(new KeyValuePair<string, object>(OpenTelemetryConstants.AISDKPrefix, $@"{OpenTelemetryConstants.SDKPrefix}:{version}"));
attributeList.Add(new KeyValuePair<string, object>(ResourceSemanticConventions.ProcessId, Process.GetCurrentProcess().Id));

// Add these attributes only if running in Azure.
if (!string.IsNullOrEmpty(serviceName))
{
var azureResourceUri = GetAzureResourceURI(siteName);
if (azureResourceUri != null)
attributeList.Add(new KeyValuePair<string, object>(ResourceSemanticConventions.ServiceName, serviceName));
RohitRanjanMS marked this conversation as resolved.
Show resolved Hide resolved
attributeList.Add(new KeyValuePair<string, object>(ResourceSemanticConventions.CloudProvider, OpenTelemetryConstants.AzureCloudProviderValue));
attributeList.Add(new KeyValuePair<string, object>(ResourceSemanticConventions.CloudPlatform, OpenTelemetryConstants.AzurePlatformValue));
attributeList.Add(new KeyValuePair<string, object>(ResourceSemanticConventions.FaaSVersion, version));
RohitRanjanMS marked this conversation as resolved.
Show resolved Hide resolved

string region = Environment.GetEnvironmentVariable(OpenTelemetryConstants.RegionNameEnvVar);
if (!string.IsNullOrEmpty(region))
{
attributeList.Add(new KeyValuePair<string, object>(ResourceAttributeConstants.CloudResourceId, azureResourceUri));
attributeList.Add(new KeyValuePair<string, object>(ResourceSemanticConventions.CloudRegion, region));
}

foreach (var kvp in ResourceAttributes)
var azureResourceUri = GetAzureResourceURI(serviceName);
if (azureResourceUri != null)
{
var attributeValue = Environment.GetEnvironmentVariable(kvp.Value);
if (attributeValue != null)
{
attributeList.Add(new KeyValuePair<string, object>(kvp.Key, attributeValue));
}
attributeList.Add(new KeyValuePair<string, object>(ResourceSemanticConventions.CloudResourceId, azureResourceUri));
}
}
}
Expand All @@ -55,14 +55,9 @@ public Resource Detect()

private static string GetAzureResourceURI(string websiteSiteName)
{
string websiteResourceGroup = Environment.GetEnvironmentVariable(ResourceAttributeConstants.ResourceGroupEnvVar);
string websiteOwnerName = Environment.GetEnvironmentVariable(ResourceAttributeConstants.OwnerNameEnvVar) ?? string.Empty;

#if NET6_0_OR_GREATER
string websiteResourceGroup = Environment.GetEnvironmentVariable(OpenTelemetryConstants.ResourceGroupEnvVar);
string websiteOwnerName = Environment.GetEnvironmentVariable(OpenTelemetryConstants.OwnerNameEnvVar) ?? string.Empty;
int idx = websiteOwnerName.IndexOf('+', StringComparison.Ordinal);
RohitRanjanMS marked this conversation as resolved.
Show resolved Hide resolved
#else
int idx = websiteOwnerName.IndexOf("+", StringComparison.Ordinal);
#endif
string subscriptionId = idx > 0 ? websiteOwnerName.Substring(0, idx) : websiteOwnerName;

if (string.IsNullOrEmpty(websiteResourceGroup) || string.IsNullOrEmpty(subscriptionId))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,10 +46,8 @@ internal static void ConfigureOpenTelemetry(this ILoggingBuilder loggingBuilder,
})
// These are messages piped back to the host from the worker - we don't handle these anymore if the worker has OpenTelemetry enabled.
// Instead, we expect the user's own code to be logging these where they want them to go.
.AddFilter<OpenTelemetryLoggerProvider>("Host.*", _ => !ScriptHost.WorkerOpenTelemetryEnabled)
.AddFilter<OpenTelemetryLoggerProvider>("Function.*", _ => !ScriptHost.WorkerOpenTelemetryEnabled)
.AddFilter<OpenTelemetryLoggerProvider>("Azure.*", _ => !ScriptHost.WorkerOpenTelemetryEnabled)
.AddFilter<OpenTelemetryLoggerProvider>("Microsoft.Azure.WebJobs.*", _ => !ScriptHost.WorkerOpenTelemetryEnabled);
.AddFilter<OpenTelemetryLoggerProvider>("Azure.*", _ => !ScriptHost.WorkerOpenTelemetryEnabled);

// Azure SDK instrumentation is experimental.
AppContext.SetSwitch("Azure.Experimental.EnableActivitySource", true);
Expand Down Expand Up @@ -86,7 +84,7 @@ internal static void ConfigureOpenTelemetry(this ILoggingBuilder loggingBuilder,
{
if (Enum.TryParse(eventLogLevel, ignoreCase: true, out EventLevel level))
{
loggingBuilder.Services.AddHostedService(new OpenTelemetryEventListenerService(level));
loggingBuilder.Services.AddHostedService(service => new OpenTelemetryEventListenerService(level));
}
else
{
Expand All @@ -96,23 +94,15 @@ internal static void ConfigureOpenTelemetry(this ILoggingBuilder loggingBuilder,
else
{
// Log all warnings and above by default.
loggingBuilder.Services.AddHostedService(new OpenTelemetryEventListenerService(EventLevel.Warning));
loggingBuilder.Services.AddHostedService(service => new OpenTelemetryEventListenerService(EventLevel.Warning));
}

static ResourceBuilder ConfigureResource(ResourceBuilder r)
{
string serviceName = Environment.GetEnvironmentVariable(ResourceAttributeConstants.SiteNameEnvVar) ?? "azureFunctions";
string version = typeof(ScriptHost).Assembly.GetName().Version.ToString();
r.AddService(serviceName, serviceVersion: version);
r.AddDetector(new FunctionsResourceDetector());

// Set the AI SDK to a key so we know all the telemetry came from the Functions Host
// NOTE: This ties to \azure-sdk-for-net\sdk\monitor\Azure.Monitor.OpenTelemetry.Exporter\src\Internals\ResourceExtensions.cs :: AiSdkPrefixKey used in CreateAzureMonitorResource()
r.AddAttributes([
new(ResourceAttributeConstants.AttributeSDKPrefix, $@"{ResourceAttributeConstants.SDKPrefix}: {version}"),
new(ResourceAttributeConstants.AttributeProcessId, Process.GetCurrentProcess().Id)
]);

return r;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,28 @@ namespace Microsoft.Azure.WebJobs.Script.Diagnostics.OpenTelemetry
{
internal class OpenTelemetryConstants
{
internal const string OpenTelemetry = "openTelemetry";
internal const string QueryUrl = "url.query";
internal const string FullUrl = "url.full";

internal const string AzureCloudProviderValue = "azure";
internal const string AzurePlatformValue = "azure_functions";
internal const string AISDKPrefix = "ai.sdk.prefix";
internal const string SDKPrefix = "azurefunctions";
internal const string SiteNameEnvVar = "WEBSITE_SITE_NAME";
internal const string RegionNameEnvVar = "REGION_NAME";
internal const string ResourceGroupEnvVar = "WEBSITE_RESOURCE_GROUP";
internal const string OwnerNameEnvVar = "WEBSITE_OWNER_NAME";
internal const string AzureFunctionsGroup = "azure.functions.group";
jviau marked this conversation as resolved.
Show resolved Hide resolved

internal static string ResolveTriggerType(string trigger)
{
switch (trigger)
{
case "httpTrigger":
return "http";
default:
return trigger;
}
}
}
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.

namespace Microsoft.Azure.WebJobs.Script.Diagnostics.OpenTelemetry
{
internal class ResourceSemanticConventions
RohitRanjanMS marked this conversation as resolved.
Show resolved Hide resolved
{
// Service
internal const string ServiceName = "service.name";
internal const string ServiceVersion = "service.version";
internal const string ServiceInstanceId = "service.instance.id";

// Cloud
internal const string CloudProvider = "cloud.provider";
internal const string CloudPlatform = "cloud.platform";
internal const string CloudRegion = "cloud.region";
internal const string CloudResourceId = "cloud.resource.id";

//FaaS
internal const string FaaSTrigger = "faas.trigger";
internal const string FaaSInvocationId = "faas.invocation_id";
internal const string FaaSColdStart = "faas.coldstart";
internal const string FaaSName = "faas.name";
internal const string FaaSVersion = "faas.version";
internal const string FaaSInstance = "faas.instance";

// Process
internal const string ProcessId = "process.pid";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,6 @@ internal enum TelemetryMode
{
None = 0, // or Default
ApplicationInsights = 1,
OpenTelemetry = 0b0001
OpenTelemetry = 2
}
}
Loading
Loading