-
Notifications
You must be signed in to change notification settings - Fork 789
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
[Draft PR] Added X-Ray trace id generator #1268
Closed
Closed
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
123 changes: 123 additions & 0 deletions
123
src/OpenTelemetry/Trace/AWSXRayActivityTraceIdGenerator.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,123 @@ | ||
using System; | ||
using System.Diagnostics; | ||
using System.Globalization; | ||
using System.Linq; | ||
using System.Threading; | ||
|
||
namespace OpenTelemetry.Trace | ||
{ | ||
public static class AWSXRayActivityTraceIdGenerator | ||
{ | ||
private const int RandomNumberHexDigits = 24; // 96 bits | ||
|
||
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); | ||
}); | ||
|
||
/// <summary> | ||
/// Replace the trace id of root activity. | ||
/// </summary> | ||
/// <param name="builder">Instance of <see cref="TracerProviderBuilder"/>.</param> | ||
/// <returns>The instance of <see cref="TracerProviderBuilder"/>.</returns> | ||
public static TracerProviderBuilder AddXRayActivityTraceIdGenerator(this TracerProviderBuilder builder) | ||
{ | ||
var awsXRayActivityListener = new ActivityListener | ||
{ | ||
ActivityStarted = (activity) => | ||
{ | ||
// Replace every root activity's trace id with X-Ray compatiable trace id | ||
if (string.IsNullOrEmpty(activity.ParentId)) | ||
{ | ||
var awsXRayTraceId = GenerateAWSXRayCompatiableActivityTraceId(); | ||
|
||
// Root node's parent id is no longer null, which will fail the sampler checker | ||
// 00-traceid-0000000000000000-00 | ||
activity.SetParentId(awsXRayTraceId, default, activity.ActivityTraceFlags); | ||
} | ||
}, | ||
|
||
ShouldListenTo = (_) => true, | ||
}; | ||
|
||
ActivitySource.AddActivityListener(awsXRayActivityListener); | ||
|
||
return builder; | ||
} | ||
|
||
public static ActivityTraceId GenerateAWSXRayCompatiableActivityTraceId() | ||
{ | ||
var epoch = (int)DateTime.UtcNow.ToUnixTimeSeconds(); // first 8 digit as time stamp | ||
|
||
var randomNumber = GenerateHexNumber(RandomNumberHexDigits); // remaining 24 random digit | ||
|
||
return ActivityTraceId.CreateFromString(string.Concat(epoch.ToString("x", CultureInfo.InvariantCulture), randomNumber).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); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -133,7 +133,7 @@ private void RunGetRequestedDataAlwaysOffSampler(Activity activity) | |
private void RunGetRequestedDataOtherSampler(Activity activity) | ||
{ | ||
ActivityContext parentContext; | ||
if (string.IsNullOrEmpty(activity.ParentId)) | ||
if (string.IsNullOrEmpty(activity.ParentId) || activity.ParentSpanId.ToHexString().Equals("0000000000000000")) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this maynot be a perf issue, if ToHexString() is not actually allocating a string, but returns the caches string value. to be confirmed. |
||
{ | ||
parentContext = default; | ||
} | ||
|
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure if would prevent a duplication, today we have in OpenTelemetry/Internal/DateTimeOffsetExtensions.net452.cs a method similar to this. The only difference is that an extension for
DateTimeOffset
instead ofDateTime