Skip to content

Commit

Permalink
Distributed Tracing (#1270)
Browse files Browse the repository at this point in the history
Implementation for private preview release of the Distributed Tracing feature.
  • Loading branch information
TsuyoshiUshio authored May 14, 2020
1 parent cf9f4c2 commit 49aac11
Show file tree
Hide file tree
Showing 26 changed files with 9,039 additions and 29 deletions.
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,
"Values": {
"AzureWebJobsStorage": "UseDevelopmentStorage=true",
"FUNCTIONS_WORKER_RUNTIME": "dotnet",
"APPINSIGHTS_INSTRUMENTATIONKEY": "<YOUR_APPLICATION_INSIGHTS_INSTRUMENTATION_KEY_HERE"
}
}
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);
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

0 comments on commit 49aac11

Please sign in to comment.