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

Added AWS X-Ray trace id generator and propagator. #42

Merged
merged 1 commit into from
Oct 14, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
14 changes: 14 additions & 0 deletions opentelemetry-dotnet-contrib.sln
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenTelemetry.Contrib.Instr
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenTelemetry.Contrib.Instrumentation.EntityFrameworkCore.Tests", "test\OpenTelemetry.Contrib.Instrumentation.EntityFrameworkCoreTests\OpenTelemetry.Contrib.Instrumentation.EntityFrameworkCore.Tests.csproj", "{899E506C-8525-4471-8C6D-5A9FA6B8517B}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenTelemetry.Contrib.Extensions.AWSXRay", "src\OpenTelemetry.Contrib.Extensions.AWSXRay\OpenTelemetry.Contrib.Extensions.AWSXRay.csproj", "{D8C9AD2A-5C6A-46F5-A216-3D67E6C0FA94}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenTelemetry.Contrib.Extensions.AWSXRay.Tests", "test\OpenTelemetry.Contrib.Extensions.AWSXRay.Tests\OpenTelemetry.Contrib.Extensions.AWSXRay.Tests.csproj", "{9CE513AC-CFC5-4DD1-9F16-8719EDCE9BF9}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -119,6 +123,14 @@ Global
{899E506C-8525-4471-8C6D-5A9FA6B8517B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{899E506C-8525-4471-8C6D-5A9FA6B8517B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{899E506C-8525-4471-8C6D-5A9FA6B8517B}.Release|Any CPU.Build.0 = Release|Any CPU
{D8C9AD2A-5C6A-46F5-A216-3D67E6C0FA94}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D8C9AD2A-5C6A-46F5-A216-3D67E6C0FA94}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D8C9AD2A-5C6A-46F5-A216-3D67E6C0FA94}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D8C9AD2A-5C6A-46F5-A216-3D67E6C0FA94}.Release|Any CPU.Build.0 = Release|Any CPU
{9CE513AC-CFC5-4DD1-9F16-8719EDCE9BF9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{9CE513AC-CFC5-4DD1-9F16-8719EDCE9BF9}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9CE513AC-CFC5-4DD1-9F16-8719EDCE9BF9}.Release|Any CPU.ActiveCfg = Release|Any CPU
{9CE513AC-CFC5-4DD1-9F16-8719EDCE9BF9}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand All @@ -136,6 +148,8 @@ Global
{5F10395B-DF38-438A-B5DB-5F6449184F67} = {22DF5DC0-1290-4E83-A9D8-6BB7DE3B3E63}
{BC839E07-108A-4184-B1F9-EF28A1E81088} = {2097345F-4DD3-477D-BC54-A922F9B2B402}
{899E506C-8525-4471-8C6D-5A9FA6B8517B} = {2097345F-4DD3-477D-BC54-A922F9B2B402}
{D8C9AD2A-5C6A-46F5-A216-3D67E6C0FA94} = {22DF5DC0-1290-4E83-A9D8-6BB7DE3B3E63}
{9CE513AC-CFC5-4DD1-9F16-8719EDCE9BF9} = {2097345F-4DD3-477D-BC54-A922F9B2B402}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {B0816796-CDB3-47D7-8C3C-946434DE3B66}
Expand Down
69 changes: 69 additions & 0 deletions src/OpenTelemetry.Contrib.Extensions.AWSXRay/AWSXRayEventSource.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
// <copyright file="AWSXRayEventSource.cs" company="OpenTelemetry Authors">
// Copyright The OpenTelemetry Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// </copyright>

using System;
using System.Diagnostics.Tracing;
using System.Globalization;
using System.Threading;

namespace OpenTelemetry.Contrib.Extensions.AWSXRay
{
[EventSource(Name = "OpenTelemetry-AWS-XRay")]
internal class AWSXRayEventSource : EventSource
{
public static AWSXRayEventSource Log = new AWSXRayEventSource();

[NonEvent]
public void ActivityContextExtractException(string format, Exception ex)
{
if (this.IsEnabled(EventLevel.Warning, (EventKeywords)(-1)))
{
this.FailedToExtractActivityContext(format, ToInvariantString(ex));
}
}

[Event(1, Message = "Failed to extract activity context in format: '{0}', context: '{1}'.", Level = EventLevel.Warning)]
public void FailedToExtractActivityContext(string format, string exception)
{
this.WriteEvent(1, format, exception);
}

[Event(2, Message = "Failed to inject activity context in format: '{0}', context: '{1}'.", Level = EventLevel.Warning)]
public void FailedToInjectActivityContext(string format, string error)
{
this.WriteEvent(2, format, error);
}

/// <summary>
/// Returns a culture-independent string representation of the given <paramref name="exception"/> object,
/// appropriate for diagnostics tracing.
/// </summary>
private static string ToInvariantString(Exception exception)
{
var originalUICulture = Thread.CurrentThread.CurrentUICulture;

try
{
Thread.CurrentThread.CurrentUICulture = CultureInfo.InvariantCulture;
return exception.ToString();
}
finally
{
Thread.CurrentThread.CurrentUICulture = originalUICulture;
}
}
}
}
135 changes: 135 additions & 0 deletions src/OpenTelemetry.Contrib.Extensions.AWSXRay/AWSXRayIdGenerator.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
// <copyright file="AWSXRayIdGenerator.cs" company="OpenTelemetry Authors">
// Copyright The OpenTelemetry Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// </copyright>

using System;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using System.Threading;

namespace OpenTelemetry.Contrib.Extensions.AWSXRay
{
/// <summary>
/// Generate AWS X-Ray compatible trace id and replace the trace id of root activity.
/// See https://docs.aws.amazon.com/xray/latest/devguide/xray-api-sendingdata.html#xray-api-traceids.
/// </summary>
public static class AWSXRayIdGenerator
{
private const int RandomNumberHexDigits = 24;

private const long TicksPerMicrosecond = TimeSpan.TicksPerMillisecond / 1000;
private const long MicrosecondPerSecond = TimeSpan.TicksPerSecond / TicksPerMicrosecond;

private static readonly DateTime EpochStart = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
private static readonly long UnixEpochMicroseconds = EpochStart.Ticks / TicksPerMicrosecond;
private static readonly Random Global = new Random();
private static readonly ThreadLocal<Random> Local = new ThreadLocal<Random>(() =>
{
int seed;
lock (Global)
{
seed = Global.Next();
}

return new Random(seed);
});

internal static void ReplaceTraceId()
{
var awsXRayActivityListener = new ActivityListener
{
ActivityStarted = (activity) =>
{
if (string.IsNullOrEmpty(activity.ParentId))
{
var awsXRayTraceId = GenerateAWSXRayCompatiableTraceId();

activity.SetParentId(awsXRayTraceId, default, activity.ActivityTraceFlags);
}
},

ShouldListenTo = (_) => true,
};

ActivitySource.AddActivityListener(awsXRayActivityListener);
}

internal static ActivityTraceId GenerateAWSXRayCompatiableTraceId()
{
var epoch = (int)DateTime.UtcNow.ToUnixTimeSeconds(); // first 8 digit as time stamp

var randomNumber = GenerateHexNumber(RandomNumberHexDigits); // remaining 24 random digit

var newTraceId = string.Concat(epoch.ToString("x", CultureInfo.InvariantCulture), randomNumber);

return ActivityTraceId.CreateFromString(newTraceId.AsSpan());
}

/// <summary>
/// Convert a given time to Unix time which is the number of seconds since 1st January 1970, 00:00:00 UTC.
/// </summary>
/// <param name="date">.Net representation of time.</param>
/// <returns>The number of seconds elapsed since 1970-01-01 00:00:00 UTC. The value is expressed in whole and fractional seconds with resolution of microsecond.</returns>
private static decimal ToUnixTimeSeconds(this DateTime date)
{
long microseconds = date.Ticks / TicksPerMicrosecond;
long microsecondsSinceEpoch = microseconds - UnixEpochMicroseconds;
return (decimal)microsecondsSinceEpoch / MicrosecondPerSecond;
}

/// <summary>
/// Generate a random 24-digit hex number.
/// </summary>
/// <param name="digits">Digits of the hex number.</param>
/// <returns>The generated hex number.</returns>
private static string GenerateHexNumber(int digits)
{
if (digits < 0)
{
throw new ArgumentException("Length can't be a negative number.", "digits");
}

byte[] bytes = new byte[digits / 2];
NextBytes(bytes);
string hexNumber = string.Concat(bytes.Select(x => x.ToString("x2", CultureInfo.InvariantCulture)).ToArray());
if (digits % 2 != 0)
{
hexNumber += Next(16).ToString("x", CultureInfo.InvariantCulture);
}

return hexNumber;
}

/// <summary>
/// Fills the elements of a specified array of bytes with random numbers.
/// </summary>
/// <param name="buffer">An array of bytes to contain random numbers.</param>
private static void NextBytes(byte[] buffer)
{
Local.Value.NextBytes(buffer);
}

/// <summary>
/// Returns a non-negative random integer that is less than the specified maximum.
/// </summary>
/// <param name="maxValue">Max value of the random integer.</param>
/// <returns>A 32-bit signed integer that is greater than or equal to 0, and less than maxValue.</returns>
private static int Next(int maxValue)
{
return Local.Value.Next(maxValue);
}
}
}
23 changes: 23 additions & 0 deletions src/OpenTelemetry.Contrib.Extensions.AWSXRay/AssemblyInfo.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// <copyright file="AssemblyInfo.cs" company="OpenTelemetry Authors">
// Copyright The OpenTelemetry Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// </copyright>

using System.Runtime.CompilerServices;

#if SIGNED
[assembly: InternalsVisibleTo("OpenTelemetry.Contrib.Extensions.AWSXRay.Tests, PublicKey=002400000480000094000000060200000024000052534131000400000100010051c1562a090fb0c9f391012a32198b5e5d9a60e9b80fa2d7b434c9e5ccb7259bd606e66f9660676afc6692b8cdc6793d190904551d2103b7b22fa636dcbb8208839785ba402ea08fc00c8f1500ccef28bbf599aa64ffb1e1d5dc1bf3420a3777badfe697856e9d52070a50c3ea5821c80bef17ca3acffa28f89dd413f096f898")]
#else
[assembly: InternalsVisibleTo("OpenTelemetry.Contrib.Extensions.AWSXRay.Tests")]
#endif
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFrameworks>net452;netstandard2.0</TargetFrameworks>
<Description>OpenTelemetry extensions for AWS X-Ray.</Description>
</PropertyGroup>

</Project>
Loading