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 8 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": "f152bd88-9b36-43ba-a8a6-ebf921f9fc8f"
TsuyoshiUshio marked this conversation as resolved.
Show resolved Hide resolved
}
}
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,324 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT License. See LICENSE in the project root for license information.

using DurableTask.Core.Settings;
Copy link
Member

Choose a reason for hiding this comment

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

Not related to this PR though, the namespace seems to be against the naming convention.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Do you mean <Company>.<Technology>* rule?

Copy link
Collaborator Author

@TsuyoshiUshio TsuyoshiUshio Apr 24, 2020

Choose a reason for hiding this comment

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

Sure. Then it might be a problem of the DurableTask framework side. Thank you for pointing out.

#if !FUNCTIONS_V1
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using DurableTask.Core;
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>
[Obsolete("Not ready for public consumption.")]
Copy link
Contributor

Choose a reason for hiding this comment

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

Is there a reason we are marking this as obsolete? In general, since this is internal, I don't see much benefit to this.

Copy link
Contributor

Choose a reason for hiding this comment

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

I think you may want to mark the interface ITelemetryInitializer with this attribute, as that is public. I wish it didn't have to be, but I haven't found a good way to have non-public interfaces be injected via dependency injection yet.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I remove the Obsolute attribute. The reason it was there is, I took this code from https://github.com/microsoft/ApplicationInsights-dotnet-server/blob/2.8.0/Src/Common/W3C/W3COperationCorrelationTelemetryInitializer.cs as I leave the comment on it. After that I modify it. So the first time, I just keep the same structure, however, since I modify it, it means nothing. I removed it and remove warning suppression at the TelemetryActivator.

[EditorBrowsable(EditorBrowsableState.Never)]
#if DEPENDENCY_COLLECTOR
TsuyoshiUshio marked this conversation as resolved.
Show resolved Hide resolved
public
#else
internal
#endif
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 = context.TraceParent.ToTraceParent();

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 change the strategy.
TsuyoshiUshio marked this conversation as resolved.
Show resolved Hide resolved
telemetry.Context.Operation.Id = "suppressed";
telemetry.Context.Operation.ParentId = "suppressed";

// 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";
}
}

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

internal bool IsSuppressedTelemetry(ITelemetry telemetry)
{
OperationTelemetry opTelemetry = telemetry as OperationTelemetry;
if (telemetry is DependencyTelemetry)
{
DependencyTelemetry dTelemetry = telemetry as DependencyTelemetry;

if (!string.IsNullOrEmpty(dTelemetry.CommandName))
{
var host = new Uri(dTelemetry.CommandName).Host;
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();
}
}
}
}

#endif
Loading