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

Distributed Tracing #1270

Merged
merged 19 commits into from
May 14, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
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
25 changes: 25 additions & 0 deletions samples/correlation-csharp/FunctionAppCorrelation.sln
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.29509.3
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FunctionAppCorrelation", "FunctionAppCorrelation\FunctionAppCorrelation.csproj", "{F9428663-9742-4746-A434-D073CCD5395C}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{F9428663-9742-4746-A434-D073CCD5395C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F9428663-9742-4746-A434-D073CCD5395C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F9428663-9742-4746-A434-D073CCD5395C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F9428663-9742-4746-A434-D073CCD5395C}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {6D2AE1B1-FA1A-4E46-B75D-B9B01E570B50}
EndGlobalSection
EndGlobal
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
local.settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"IsEncrypted": false,
TsuyoshiUshio marked this conversation as resolved.
Show resolved Hide resolved
"Values": {
"AzureWebJobsStorage": "UseDevelopmentStorage=true",
"FUNCTIONS_WORKER_RUNTIME": "dotnet",
"APPINSIGHTS_INSTRUMENTATIONKEY": "<YOUR_APPLICATION_INSIGHTS_INSTRUMENTATION_KEY_HERE"
}
}
TsuyoshiUshio marked this conversation as resolved.
Show resolved Hide resolved
2 changes: 1 addition & 1 deletion src/WebJobs.Extensions.DurableTask/AssemblyInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,4 @@

[assembly: InternalsVisibleTo("WebJobs.Extensions.DurableTask.Tests.V1, PublicKey=0024000004800000940000000602000000240000525341310004000001000100cd1dabd5a893b40e75dc901fe7293db4a3caf9cd4d3e3ed6178d49cd476969abe74a9e0b7f4a0bb15edca48758155d35a4f05e6e852fff1b319d103b39ba04acbadd278c2753627c95e1f6f6582425374b92f51cca3deb0d2aab9de3ecda7753900a31f70a236f163006beefffe282888f85e3c76d1205ec7dfef7fa472a17b1")]
[assembly: InternalsVisibleTo("WebJobs.Extensions.DurableTask.Tests.V2, PublicKey=0024000004800000940000000602000000240000525341310004000001000100cd1dabd5a893b40e75dc901fe7293db4a3caf9cd4d3e3ed6178d49cd476969abe74a9e0b7f4a0bb15edca48758155d35a4f05e6e852fff1b319d103b39ba04acbadd278c2753627c95e1f6f6582425374b92f51cca3deb0d2aab9de3ecda7753900a31f70a236f163006beefffe282888f85e3c76d1205ec7dfef7fa472a17b1")]
[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2,PublicKey=0024000004800000940000000602000000240000525341310004000001000100c547cac37abd99c8db225ef2f6c8a3602f3b3606cc9891605d02baa56104f4cfc0734aa39b93bf7852f7d9266654753cc297e7d2edfe0bac1cdcf9f717241550e0a7b191195b7667bb4f64bcb8e2121380fd1d9d46ad2d92d2d15605093924cceaf74c4861eff62abf69b9291ed0a340e113be11e6a7d3113e92484cf7045cc7")]
[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c547cac37abd99c8db225ef2f6c8a3602f3b3606cc9891605d02baa56104f4cfc0734aa39b93bf7852f7d9266654753cc297e7d2edfe0bac1cdcf9f717241550e0a7b191195b7667bb4f64bcb8e2121380fd1d9d46ad2d92d2d15605093924cceaf74c4861eff62abf69b9291ed0a340e113be11e6a7d3113e92484cf7045cc7")]
Original file line number Diff line number Diff line change
Expand Up @@ -418,6 +418,10 @@ void IDurableOrchestrationContext.SignalEntity(EntityId entity, DateTime startTi
/// <inheritdoc/>
string IDurableOrchestrationContext.StartNewOrchestration(string functionName, object input, string instanceId)
{
// correlation
#if NETSTANDARD2_0
var context = CorrelationTraceContext.Current;
#endif
this.ThrowIfInvalidAccess();
var actualInstanceId = string.IsNullOrEmpty(instanceId) ? this.NewGuid().ToString() : instanceId;
var alreadyCompletedTask = this.CallDurableTaskFunctionAsync<string>(functionName, FunctionType.Orchestrator, true, actualInstanceId, null, null, input, null);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,319 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT License. See LICENSE in the project root for license information.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using DurableTask.Core;
using DurableTask.Core.Settings;
using Microsoft.ApplicationInsights.Channel;
using Microsoft.ApplicationInsights.DataContracts;
using Microsoft.ApplicationInsights.Extensibility;
using Microsoft.ApplicationInsights.Extensibility.Implementation;

namespace Microsoft.Azure.WebJobs.Extensions.DurableTask.Correlation
{
/// <summary>
/// Telemetry Initializer that sets correlation ids for W3C.
/// This source is based on W3COperationCorrelationTelemetryInitializer.cs
/// 1. Modified with CorrelationTraceContext.Current
/// 2. Avoid to be overriden when it is RequestTelemetry
/// Original Source is here https://github.com/microsoft/ApplicationInsights-dotnet-server/blob/2.8.0/Src/Common/W3C/W3COperationCorrelationTelemetryInitializer.cs.
/// </summary>
internal
class DurableTaskCorrelationTelemetryInitializer : ITelemetryInitializer
{
private const string RddDiagnosticSourcePrefix = "rdddsc";
private const string SqlRemoteDependencyType = "SQL";

/// These internal property is copied from W3CConstants
/// <summary>Trace-Id tag name.</summary>
internal const string TraceIdTag = "w3c_traceId";

/// <summary>Span-Id tag name.</summary>
internal const string SpanIdTag = "w3c_spanId";

/// <summary>Parent span-Id tag name.</summary>
internal const string ParentSpanIdTag = "w3c_parentSpanId";

/// <summary>Version tag name.</summary>
internal const string VersionTag = "w3c_version";

/// <summary>Sampled tag name.</summary>
internal const string SampledTag = "w3c_sampled";

/// <summary>Tracestate tag name.</summary>
internal const string TracestateTag = "w3c_tracestate";

/// <summary>Default version value.</summary>
internal const string DefaultVersion = "00";

/// <summary>
/// Default sampled flag value: may be recorded, not requested.
/// </summary>
internal const string TraceFlagRecordedAndNotRequested = "02";

/// <summary>Recorded and requested sampled flag value.</summary>
internal const string TraceFlagRecordedAndRequested = "03";

/// <summary>Requested trace flag.</summary>
internal const byte RequestedTraceFlag = 1;

/// <summary>Legacy root Id tag name.</summary>
internal const string LegacyRootIdProperty = "ai_legacyRootId";

/// <summary>Legacy root Id tag name.</summary>
internal const string LegacyRequestIdProperty = "ai_legacyRequestId";

/// <summary>
/// Constructor.
/// </summary>
public DurableTaskCorrelationTelemetryInitializer()
{
this.ExcludeComponentCorrelationHttpHeadersOnDomains = new HashSet<string>();
}

/// <summary>
/// Set of suppress telemetry tracking if you add Host name on this.
/// </summary>
public HashSet<string> ExcludeComponentCorrelationHttpHeadersOnDomains { get; set; }

/// <summary>
/// Initializes telemetry item.
/// </summary>
/// <param name="telemetry">Telemetry item.</param>
public void Initialize(ITelemetry telemetry)
{
if (this.IsSuppressedTelemetry(telemetry))
{
this.SuppressTelemetry(telemetry);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This would suppress the telemetry within the durable task.
Additional work is required to communicate back to the host and suppress the telemetry there:

  1. Durable task puts the suppression flag in the payload before returning back to the Functions host.
  2. Once Functions host received the suppression flag, respect the flag and stop emitting telemetry events (context propagation is still required).

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @reyang
Thank you for the notice! Very good point.

This issue might be related.
#593

Maybe this TelemetryInitializer might be the one fix on the functions host side.
https://github.com/Azure/azure-webjobs-sdk/blob/dev/src/Microsoft.Azure.WebJobs.Logging.ApplicationInsights/Initializers/WebJobsTelemetryInitializer.cs

return;
}

if (!(telemetry is RequestTelemetry))
{
Activity currentActivity = Activity.Current;
if (telemetry is ExceptionTelemetry)
{
Console.WriteLine("exception!");
}

if (currentActivity == null)
{
if (CorrelationTraceContext.Current != null)
{
UpdateTelemetry(telemetry, CorrelationTraceContext.Current);
}
}
else
{
if (CorrelationTraceContext.Current != null)
{
UpdateTelemetry(telemetry, CorrelationTraceContext.Current);
}
else if (CorrelationSettings.Current.Protocol == Protocol.W3CTraceContext)
{
UpdateTelemetry(telemetry, currentActivity, false);
}
else if (CorrelationSettings.Current.Protocol == Protocol.HttpCorrelationProtocol
&& telemetry is ExceptionTelemetry)
{
UpdateTelemetryExceptionForHTTPCorrelationProtocol((ExceptionTelemetry)telemetry, currentActivity);
}
}
}
}

internal static void UpdateTelemetry(ITelemetry telemetry, TraceContextBase contextBase)
{
switch (contextBase)
{
case NullObjectTraceContext nullObjectContext:
return;
case W3CTraceContext w3cContext:
UpdateTelemetryW3C(telemetry, w3cContext);
break;
case HttpCorrelationProtocolTraceContext httpCorrelationProtocolTraceContext:
UpdateTelemetryHttpCorrelationProtocol(telemetry, httpCorrelationProtocolTraceContext);
break;
default:
return;
}
}

internal static void UpdateTelemetryHttpCorrelationProtocol(ITelemetry telemetry, HttpCorrelationProtocolTraceContext context)
{
OperationTelemetry opTelemetry = telemetry as OperationTelemetry;

bool initializeFromCurrent = opTelemetry != null;

if (initializeFromCurrent)
{
initializeFromCurrent &= !(opTelemetry is DependencyTelemetry dependency &&
dependency.Type == SqlRemoteDependencyType &&
dependency.Context.GetInternalContext().SdkVersion
.StartsWith(RddDiagnosticSourcePrefix, StringComparison.Ordinal));
}

if (initializeFromCurrent)
{
opTelemetry.Id = !string.IsNullOrEmpty(opTelemetry.Id) ? opTelemetry.Id : context.TelemetryId;
telemetry.Context.Operation.ParentId = !string.IsNullOrEmpty(telemetry.Context.Operation.ParentId) ? telemetry.Context.Operation.ParentId : context.TelemetryContextOperationParentId;
}
else
{
telemetry.Context.Operation.Id = !string.IsNullOrEmpty(telemetry.Context.Operation.Id) ? telemetry.Context.Operation.Id : context.TelemetryContextOperationId;
if (telemetry is ExceptionTelemetry)
{
telemetry.Context.Operation.ParentId = context.TelemetryId;
}
else
{
telemetry.Context.Operation.ParentId = !string.IsNullOrEmpty(telemetry.Context.Operation.ParentId) ? telemetry.Context.Operation.ParentId : context.TelemetryContextOperationParentId;
}
}
}

internal static void UpdateTelemetryW3C(ITelemetry telemetry, W3CTraceContext context)
{
OperationTelemetry opTelemetry = telemetry as OperationTelemetry;

bool initializeFromCurrent = opTelemetry != null;

if (initializeFromCurrent)
{
initializeFromCurrent &= !(opTelemetry is DependencyTelemetry dependency &&
dependency.Type == SqlRemoteDependencyType &&
dependency.Context.GetInternalContext().SdkVersion
.StartsWith(RddDiagnosticSourcePrefix, StringComparison.Ordinal));
}

if (!string.IsNullOrEmpty(context.TraceState))
{
opTelemetry.Properties["w3c_tracestate"] = context.TraceState;
}

TraceParent traceParent = TraceParent.FromString(context.TraceParent);

if (initializeFromCurrent)
{
if (string.IsNullOrEmpty(opTelemetry.Id))
{
opTelemetry.Id = traceParent.SpanId;
}

if (string.IsNullOrEmpty(context.ParentSpanId))
{
telemetry.Context.Operation.ParentId = telemetry.Context.Operation.Id;
}
}
else
{
if (telemetry.Context.Operation.Id == null)
{
telemetry.Context.Operation.Id = traceParent.TraceId;
}

if (telemetry.Context.Operation.ParentId == null)
{
telemetry.Context.Operation.ParentId = traceParent.SpanId;
}
}
}

internal void SuppressTelemetry(ITelemetry telemetry)
{
// TODO For suppressing Dependency, I make the Id as suppressed. This stragey increase the number of telemetery.
// However, new implementation already supressed. Once it fully tested the logic, remove the suppression logic on this class.
telemetry.Context.Operation.Id = "suppressed";
telemetry.Context.Operation.ParentId = "suppressed";
#pragma warning disable 618

// Context. Properties. ai_legacyRequestId , ai_legacyRequestId
foreach (var key in telemetry.Context.Properties.Keys)
{
if (key == "ai_legacyRootId" ||
key == "ai_legacyRequestId")
{
telemetry.Context.Properties[key] = "suppressed";
}
}
#pragma warning restore 618

((OperationTelemetry)telemetry).Id = "suppressed";
}

internal bool IsSuppressedTelemetry(ITelemetry telemetry)
{
OperationTelemetry opTelemetry = telemetry as OperationTelemetry;
if (telemetry is DependencyTelemetry)
{
DependencyTelemetry dTelemetry = telemetry as DependencyTelemetry;
#pragma warning disable 618
if (!string.IsNullOrEmpty(dTelemetry.CommandName))
{
var host = new Uri(dTelemetry.CommandName).Host;
#pragma warning restore 618
if (this.ExcludeComponentCorrelationHttpHeadersOnDomains.Contains(host))
{
return true;
}
}
}

return false;
}

internal static void UpdateTelemetryExceptionForHTTPCorrelationProtocol(ExceptionTelemetry telemetry, Activity activity)
{
telemetry.Context.Operation.ParentId = activity.Id;
telemetry.Context.Operation.Id = activity.RootId;
}

[SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", Justification = "This method has different code for Net45/NetCore")]
internal static void UpdateTelemetry(ITelemetry telemetry, Activity activity, bool forceUpdate)
{
if (activity == null)
{
return;
}

// Requests and dependencies are initialized from the Activity.Current.
// (i.e. telemetry.Id = current.Id). Activity is created for such requests specifically
// Traces, exceptions, events on the other side are children of current activity
// There is one exception - SQL DiagnosticSource where current Activity is a parent
// for dependency calls.

OperationTelemetry opTelemetry = telemetry as OperationTelemetry;
bool initializeFromCurrent = opTelemetry != null;

if (initializeFromCurrent)
{
initializeFromCurrent &= !(opTelemetry is DependencyTelemetry dependency &&
dependency.Type == SqlRemoteDependencyType &&
dependency.Context.GetInternalContext().SdkVersion
.StartsWith(RddDiagnosticSourcePrefix, StringComparison.Ordinal));
}

if (telemetry is OperationTelemetry operation)
{
operation.Properties[TracestateTag] = activity.TraceStateString;
}

if (initializeFromCurrent)
{
opTelemetry.Id = activity.SpanId.ToHexString();
if (activity.ParentSpanId != null)
{
opTelemetry.Context.Operation.ParentId = activity.ParentSpanId.ToHexString();
}
}
else
{
telemetry.Context.Operation.ParentId = activity.SpanId.ToHexString();
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT License. See LICENSE in the project root for license information.

namespace Microsoft.Azure.WebJobs.Extensions.DurableTask.Correlation
{
/// <summary>
/// ITelemetryActivator is an interface.
/// </summary>
public interface ITelemetryActivator
{
/// <summary>
/// Initialize is initialize the telemetry client.
/// </summary>
void Initialize();
}
}
Loading