Skip to content

Commit

Permalink
Allow user id to be propagated (#2968)
Browse files Browse the repository at this point in the history
* Allow user id to be propagated

* Update tracer/src/Datadog.Trace/SpanExtensions.cs

Co-authored-by: Andrew Lock <andrew.lock@datadoghq.com>

* Don't add the tag to the span if it's too long

* Update tracer/src/Datadog.Trace/UserDetails.cs

Co-authored-by: Lucas Pimentel-Ordyna <lucas.pimentel@datadoghq.com>

* Changes

* fix unite tests

Co-authored-by: Andrew Lock <andrew.lock@datadoghq.com>
Co-authored-by: Lucas Pimentel-Ordyna <lucas.pimentel@datadoghq.com>
  • Loading branch information
3 people authored Jul 12, 2022
1 parent 75deafa commit 10121e1
Show file tree
Hide file tree
Showing 4 changed files with 62 additions and 28 deletions.
36 changes: 28 additions & 8 deletions tracer/src/Datadog.Trace/SpanExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@
// This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2017 Datadog, Inc.
// </copyright>

using System;
using System.Text;
using Datadog.Trace.Logging;
using Datadog.Trace.Tagging;
using Datadog.Trace.Util;

namespace Datadog.Trace
Expand All @@ -12,6 +16,8 @@ namespace Datadog.Trace
/// </summary>
public static class SpanExtensions
{
private static readonly IDatadogLogger Log = DatadogLogging.GetLoggerFor(typeof(SpanExtensions));

/// <summary>
/// Sets the details of the user on the local root span
/// </summary>
Expand All @@ -29,37 +35,51 @@ public static void SetUser(this ISpan span, UserDetails userDetails)
ThrowHelper.ThrowArgumentException(nameof(userDetails) + ".Id must be set to a value other than null or the empty string", nameof(userDetails));
}

var localRootSpan = span;
TraceContext traceContext = null;
if (span is Span spanClass)
{
localRootSpan = spanClass.Context.TraceContext?.RootSpan ?? span;
traceContext = spanClass.Context.TraceContext;
}

localRootSpan.SetTag(Tags.User.Id, userDetails.Id);
Action<string, string> setTag =
traceContext != null
? (name, value) => traceContext.Tags.SetTag(name, value)
: (name, value) => span.SetTag(name, value);

if (userDetails.PropagateId)
{
var base64UserId = Convert.ToBase64String(Encoding.UTF8.GetBytes(userDetails.Id));
const string propagatedUserIdTag = TagPropagation.PropagatedTagPrefix + Tags.User.Id;
setTag(propagatedUserIdTag, base64UserId);
}
else
{
setTag(Tags.User.Id, userDetails.Id);
}

if (userDetails.Email is not null)
{
localRootSpan.SetTag(Tags.User.Email, userDetails.Email);
setTag(Tags.User.Email, userDetails.Email);
}

if (userDetails.Name is not null)
{
localRootSpan.SetTag(Tags.User.Name, userDetails.Name);
setTag(Tags.User.Name, userDetails.Name);
}

if (userDetails.SessionId is not null)
{
localRootSpan.SetTag(Tags.User.SessionId, userDetails.SessionId);
setTag(Tags.User.SessionId, userDetails.SessionId);
}

if (userDetails.Role is not null)
{
localRootSpan.SetTag(Tags.User.Role, userDetails.Role);
setTag(Tags.User.Role, userDetails.Role);
}

if (userDetails.Scope is not null)
{
localRootSpan.SetTag(Tags.User.Scope, userDetails.Scope);
setTag(Tags.User.Scope, userDetails.Scope);
}
}
}
Expand Down
15 changes: 13 additions & 2 deletions tracer/src/Datadog.Trace/UserDetails.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ public struct UserDetails
/// <summary>
/// Initializes a new instance of the <see cref="UserDetails"/> struct.
/// </summary>
/// <param name="id">The unique identifier assoicated with the users</param>
/// <param name="id">The unique identifier associated with the users</param>
public UserDetails(string id)
{
if (string.IsNullOrEmpty(id))
Expand All @@ -25,6 +25,7 @@ public UserDetails(string id)
}

Id = id;
PropagateId = false;
Email = null;
Name = null;
SessionId = null;
Expand All @@ -43,10 +44,20 @@ public UserDetails(string id)
public string? Name { get; set; }

/// <summary>
/// Gets or sets the unique identifier assoicated with the users
/// Gets or sets the unique identifier associated with the user.
/// </summary>
public string Id { get; set; }

/// <summary>
/// Gets or sets a value indicating whether the Id field should be propagated to other services called.
/// NOTE: setting this value to true will automatically propagate the Id field to all downstream services
/// including any third-party services called by the application. Therefore this value should only be set
/// to true if the user is confident that it is safe to propagate the value to all downstream services
/// called by the application.
/// Default value is false.
/// </summary>
public bool PropagateId { get; set; }

/// <summary>
/// Gets or sets the user's session unique identifier
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -348,6 +348,7 @@ namespace Datadog.Trace
public string? Email { get; set; }
public string Id { get; set; }
public string? Name { get; set; }
public bool PropagateId { get; set; }
public string? Role { get; set; }
public string? Scope { get; set; }
public string? SessionId { get; set; }
Expand Down
38 changes: 20 additions & 18 deletions tracer/test/Datadog.Trace.Tests/TracerTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -528,7 +528,7 @@ public async Task ForceFlush()
}

[Fact]
public void SetUserOnRootSpanDirectly()
public void SetUserOnRootSpanDirectly_ShouldSetOnTrace()
{
var scopeManager = new AsyncLocalScopeManager();

Expand All @@ -538,8 +538,8 @@ public void SetUserOnRootSpanDirectly()
};
var tracer = new Tracer(settings, Mock.Of<IAgentWriter>(), Mock.Of<ISampler>(), scopeManager, Mock.Of<IDogStatsd>());

var rootTestScope = tracer.StartActive("test.trace");
var childTestScope = tracer.StartActive("test.trace.child");
var rootTestScope = (Scope)tracer.StartActive("test.trace");
var childTestScope = (Scope)tracer.StartActive("test.trace.child");
childTestScope.Dispose();

var email = "test@adventure-works.com";
Expand All @@ -560,16 +560,17 @@ public void SetUserOnRootSpanDirectly()
};
tracer.ActiveScope?.Span.SetUser(userDetails);

Assert.Equal(email, rootTestScope.Span.GetTag(Tags.User.Email));
Assert.Equal(name, rootTestScope.Span.GetTag(Tags.User.Name));
Assert.Equal(id, rootTestScope.Span.GetTag(Tags.User.Id));
Assert.Equal(sessionId, rootTestScope.Span.GetTag(Tags.User.SessionId));
Assert.Equal(role, rootTestScope.Span.GetTag(Tags.User.Role));
Assert.Equal(scope, rootTestScope.Span.GetTag(Tags.User.Scope));
var traceContext = rootTestScope.Span.Context.TraceContext;
Assert.Equal(email, traceContext.Tags.GetTag(Tags.User.Email));
Assert.Equal(name, traceContext.Tags.GetTag(Tags.User.Name));
Assert.Equal(id, traceContext.Tags.GetTag(Tags.User.Id));
Assert.Equal(sessionId, traceContext.Tags.GetTag(Tags.User.SessionId));
Assert.Equal(role, traceContext.Tags.GetTag(Tags.User.Role));
Assert.Equal(scope, traceContext.Tags.GetTag(Tags.User.Scope));
}

[Fact]
public void SetUserOnChildChildSpan_ShouldAttachToRoot()
public void SetUserOnChildChildSpan_ShouldSetOnTrace()
{
var scopeManager = new AsyncLocalScopeManager();

Expand All @@ -579,8 +580,8 @@ public void SetUserOnChildChildSpan_ShouldAttachToRoot()
};
var tracer = new Tracer(settings, Mock.Of<IAgentWriter>(), Mock.Of<ISampler>(), scopeManager, Mock.Of<IDogStatsd>());

var rootTestScope = tracer.StartActive("test.trace");
var childTestScope = tracer.StartActive("test.trace.child");
var rootTestScope = (Scope)tracer.StartActive("test.trace");
var childTestScope = (Scope)tracer.StartActive("test.trace.child");

var email = "test@adventure-works.com";
var name = "Jane Doh";
Expand All @@ -602,12 +603,13 @@ public void SetUserOnChildChildSpan_ShouldAttachToRoot()

childTestScope.Dispose();

Assert.Equal(email, rootTestScope.Span.GetTag(Tags.User.Email));
Assert.Equal(name, rootTestScope.Span.GetTag(Tags.User.Name));
Assert.Equal(id, rootTestScope.Span.GetTag(Tags.User.Id));
Assert.Equal(sessionId, rootTestScope.Span.GetTag(Tags.User.SessionId));
Assert.Equal(role, rootTestScope.Span.GetTag(Tags.User.Role));
Assert.Equal(scope, rootTestScope.Span.GetTag(Tags.User.Scope));
var traceContext = rootTestScope.Span.Context.TraceContext;
Assert.Equal(email, traceContext.Tags.GetTag(Tags.User.Email));
Assert.Equal(name, traceContext.Tags.GetTag(Tags.User.Name));
Assert.Equal(id, traceContext.Tags.GetTag(Tags.User.Id));
Assert.Equal(sessionId, traceContext.Tags.GetTag(Tags.User.SessionId));
Assert.Equal(role, traceContext.Tags.GetTag(Tags.User.Role));
Assert.Equal(scope, traceContext.Tags.GetTag(Tags.User.Scope));
}

[Fact]
Expand Down

0 comments on commit 10121e1

Please sign in to comment.