Skip to content
Draft
Show file tree
Hide file tree
Changes from 3 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
2 changes: 1 addition & 1 deletion src/Sentry.Extensions.Logging/SentryStructuredLogger.cs
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Except
ParentSpanId = spanId,
};

log.SetDefaultAttributes(_options, _sdk);
log.SetDefaultAttributes(_options, _hub.GetScope(), _sdk);
log.SetOrigin("auto.log.extensions_logging");

if (_categoryName is not null)
Expand Down
2 changes: 1 addition & 1 deletion src/Sentry.Serilog/SentrySink.Structured.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ private static void CaptureStructuredLog(IHub hub, SentryOptions options, LogEve
ParentSpanId = spanId,
};

log.SetDefaultAttributes(options, Sdk);
log.SetDefaultAttributes(options, hub.GetScope(), Sdk);
log.SetOrigin("auto.log.serilog");

foreach (var attribute in attributes)
Expand Down
7 changes: 6 additions & 1 deletion src/Sentry/Internal/DefaultSentryStructuredLogger.cs
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,12 @@ private protected override void CaptureLog(SentryLogLevel level, string template
}

var scope = _hub.GetScope();
log.SetDefaultAttributes(_options, scope?.Sdk ?? SdkVersion.Instance);
var sdk = scope?.Sdk;
if (sdk is null || (sdk.Name is null && sdk.Version is null))
{
sdk = SdkVersion.Instance;
}
log.SetDefaultAttributes(_options, scope, sdk);

CaptureLog(log);
}
Expand Down
77 changes: 74 additions & 3 deletions src/Sentry/SentryLog.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ internal SentryLog(DateTimeOffset timestamp, SentryId traceId, SentryLogLevel le
TraceId = traceId;
Level = level;
Message = message;
// 7 is the number of built-in attributes, so we start with that.
_attributes = new Dictionary<string, SentryAttribute>(7);
// we currently set up to 18 default attributes, the next prime number is 23
_attributes = new Dictionary<string, SentryAttribute>(23);
}

/// <summary>
Expand Down Expand Up @@ -163,7 +163,12 @@ internal void SetAttribute(string key, int value)
_attributes[key] = new SentryAttribute(value, "integer");
}

internal void SetDefaultAttributes(SentryOptions options, SdkVersion sdk)
internal void SetDefaultAttributes(SentryOptions options, Scope scope)
{
SetDefaultAttributes(options, scope, scope.Sdk);
}

internal void SetDefaultAttributes(SentryOptions options, Scope? scope, SdkVersion sdk)
{
var environment = options.SettingLocator.GetEnvironment();
SetAttribute("sentry.environment", environment);
Expand All @@ -182,6 +187,72 @@ internal void SetDefaultAttributes(SentryOptions options, SdkVersion sdk)
{
SetAttribute("sentry.sdk.version", version);
}

if (scope is null)
{
return;
}

if (scope.PropagationContext._dynamicSamplingContext is { } dynamicSamplingContext)
{
if (dynamicSamplingContext.Items.TryGetValue("replay_id", out var replayId))
{
SetAttribute("sentry.replay_id", replayId);
}
}
Comment on lines +196 to +202
Copy link
Member Author

Choose a reason for hiding this comment

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

note: Could alternatively get from IReplaySession.ActiveReplayId.

But I guess reading it via the Scope unifies the source, and avoid a dependency to IReplaySession.


if (scope.User.Id is { } userId)
{
SetAttribute("user.id", userId);
}
if (scope.User.Username is { } userName)
{
SetAttribute("user.name", userName);
}
if (scope.User.Email is { } userEmail)
{
SetAttribute("user.email", userEmail);
}
Comment on lines +204 to +215
Copy link
Member Author

@Flash0ver Flash0ver Dec 2, 2025

Choose a reason for hiding this comment

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

question: PII

Do we need to check SentryOptions.SendDefaultPii?
Or do/can/should we assume that the data is set only when enabled already, or explicitly set by user code.


if (scope.Contexts.Browser.Name is { } browserName)
{
SetAttribute("browser.name", browserName);
}
if (scope.Contexts.Browser.Version is { } browserVersion)
{
SetAttribute("browser.version", browserVersion);
}

var serverAddress = options.ServerName;
if (!string.IsNullOrEmpty(serverAddress))
{
SetAttribute("server.address", serverAddress);
}
else if (options.SendDefaultPii)
{
SetAttribute("server.address", Environment.MachineName);
}

if (scope.Contexts.OperatingSystem.Name is { } osName)
{
SetAttribute("os.name", osName);
}
if (scope.Contexts.OperatingSystem.Version is { } osVersion)
{
SetAttribute("os.version", osVersion);
}
if (scope.Contexts.Device.Brand is { } deviceBrand)
{
SetAttribute("device.brand", deviceBrand);
}
if (scope.Contexts.Device.Model is { } deviceModel)
{
SetAttribute("device.model", deviceModel);
}
if (scope.Contexts.Device.Family is { } deviceFamily)
{
SetAttribute("device.family", deviceFamily);
}
}

internal void SetOrigin(string origin)
Expand Down
183 changes: 175 additions & 8 deletions test/Sentry.Tests/SentryLogTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,13 @@ public void Protocol_Default_VerifyAttributes()
Environment = "my-environment",
Release = "my-release",
};
var sdk = new SdkVersion
var scope = new Scope(options)
{
Name = "Sentry.Test.SDK",
Version = "1.2.3-test+Sentry"
Sdk =
{
Name = "Sentry.Test.SDK",
Version = "1.2.3-test+Sentry",
}
};

var log = new SentryLog(Timestamp, TraceId, (SentryLogLevel)24, "message")
Expand All @@ -43,7 +46,7 @@ public void Protocol_Default_VerifyAttributes()
ParentSpanId = ParentSpanId,
};
log.SetAttribute("attribute", "value");
log.SetDefaultAttributes(options, sdk);
log.SetDefaultAttributes(options, scope);

log.Timestamp.Should().Be(Timestamp);
log.TraceId.Should().Be(TraceId);
Expand All @@ -63,13 +66,87 @@ public void Protocol_Default_VerifyAttributes()
log.TryGetAttribute("sentry.release", out string release).Should().BeTrue();
release.Should().Be(options.Release);
log.TryGetAttribute("sentry.sdk.name", out string name).Should().BeTrue();
name.Should().Be(sdk.Name);
name.Should().Be(scope.Sdk.Name);
log.TryGetAttribute("sentry.sdk.version", out string version).Should().BeTrue();
version.Should().Be(sdk.Version);
version.Should().Be(scope.Sdk.Version);
log.TryGetAttribute("not-found", out object notFound).Should().BeFalse();
notFound.Should().BeNull();
}

[Theory]
[InlineData(true)]
[InlineData(false)]
public void Protocol_Default_VerifyAdditionalAttributes(bool hasAdditionalDefaultAttributes)
{
var options = new SentryOptions
{
Environment = "my-environment",
Release = "my-release",
ServerName = hasAdditionalDefaultAttributes ? "my-server-address" : null,
};
var scope = new Scope(options);

if (hasAdditionalDefaultAttributes)
{
options.Dsn = ValidDsn;
var replaySession = Substitute.For<IReplaySession>();
replaySession.ActiveReplayId.Returns(SentryId.Parse("f18176ecbb544e549fd23fbbe39064cc"));
_ = scope.PropagationContext.GetOrCreateDynamicSamplingContext(options, replaySession);

scope.User = new SentryUser
{
Id = "my-user-id",
Username = "my-user-name",
Email = "my-user-email",
};
scope.Contexts.Browser.Name = "my-browser-name";
scope.Contexts.Browser.Version = "my-browser-version";
scope.Contexts.OperatingSystem.Name = "my-os-name";
scope.Contexts.OperatingSystem.Version = "my-os-version";
scope.Contexts.Device.Brand = "my-device-brand";
scope.Contexts.Device.Model = "my-device-model";
scope.Contexts.Device.Family = "my-device-family";
}

var log = new SentryLog(Timestamp, TraceId, (SentryLogLevel)24, "message")
{
Template = "template",
Parameters = ImmutableArray.Create(new KeyValuePair<string, object>("param", "params")),
ParentSpanId = ParentSpanId,
};
log.SetAttribute("attribute", "value");
log.SetDefaultAttributes(options, scope);

log.TryGetAttribute("sentry.replay_id", out string replayId).Should().Be(hasAdditionalDefaultAttributes);
log.TryGetAttribute("user.id", out string userId).Should().Be(hasAdditionalDefaultAttributes);
log.TryGetAttribute("user.name", out string userName).Should().Be(hasAdditionalDefaultAttributes);
log.TryGetAttribute("user.email", out string userEmail).Should().Be(hasAdditionalDefaultAttributes);
log.TryGetAttribute("browser.name", out string browserName).Should().Be(hasAdditionalDefaultAttributes);
log.TryGetAttribute("browser.version", out string browserVersion).Should().Be(hasAdditionalDefaultAttributes);
log.TryGetAttribute("server.address", out string serverAddress).Should().Be(hasAdditionalDefaultAttributes);
log.TryGetAttribute("os.name", out string osName).Should().Be(hasAdditionalDefaultAttributes);
log.TryGetAttribute("os.version", out string osVersion).Should().Be(hasAdditionalDefaultAttributes);
log.TryGetAttribute("device.brand", out string deviceBrand).Should().Be(hasAdditionalDefaultAttributes);
log.TryGetAttribute("device.model", out string deviceModel).Should().Be(hasAdditionalDefaultAttributes);
log.TryGetAttribute("device.family", out string deviceFamily).Should().Be(hasAdditionalDefaultAttributes);

if (hasAdditionalDefaultAttributes)
{
replayId.Should().Be("f18176ecbb544e549fd23fbbe39064cc");
userId.Should().Be("my-user-id");
userName.Should().Be("my-user-name");
userEmail.Should().Be("my-user-email");
browserName.Should().Be("my-browser-name");
browserVersion.Should().Be("my-browser-version");
serverAddress.Should().Be("my-server-address");
osName.Should().Be("my-os-name");
osVersion.Should().Be("my-os-version");
deviceBrand.Should().Be("my-device-brand");
deviceModel.Should().Be("my-device-model");
deviceFamily.Should().Be("my-device-family");
}
}

[Theory]
[InlineData(true)]
[InlineData(false)]
Expand Down Expand Up @@ -101,10 +178,12 @@ public void WriteTo_Envelope_MinimalSerializedSentryLog()
{
Environment = "my-environment",
Release = "my-release",
SendDefaultPii = false,
};
var scope = new Scope(options);

var log = new SentryLog(Timestamp, TraceId, SentryLogLevel.Trace, "message");
log.SetDefaultAttributes(options, new SdkVersion());
log.SetDefaultAttributes(options, scope);

var envelope = Envelope.FromLog(new StructuredLog([log]));

Expand Down Expand Up @@ -170,6 +249,46 @@ public void WriteTo_EnvelopeItem_MaximalSerializedSentryLog()
{
Environment = "my-environment",
Release = "my-release",
ServerName = "my-server-address",
};
var replaySession = Substitute.For<IReplaySession>();
var replayId = SentryId.Create();
replaySession.ActiveReplayId.Returns(replayId);
var dynamicSamplingContext = DynamicSamplingContext.Empty();
dynamicSamplingContext.SetReplayId(replaySession);
var propagationContext = new SentryPropagationContext(TraceId, ParentSpanId!.Value, dynamicSamplingContext);
var scope = new Scope(options, propagationContext)
{
Sdk =
{
Name = "Sentry.Test.SDK",
Version = "1.2.3-test+Sentry",
},
User = new SentryUser
{
Id = "my-user-id",
Username = "my-user-name",
Email = "my-user-email",
},
Contexts = new SentryContexts
{
Browser =
{
Name = "my-browser-name",
Version = "my-browser-version",
},
OperatingSystem =
{
Name = "my-os-name",
Version = "my-os-version",
},
Device =
{
Brand = "my-device-brand",
Model = "my-device-model",
Family = "my-device-family",
},
},
};

var log = new SentryLog(Timestamp, TraceId, (SentryLogLevel)24, "message")
Expand All @@ -182,7 +301,7 @@ public void WriteTo_EnvelopeItem_MaximalSerializedSentryLog()
log.SetAttribute("boolean-attribute", true);
log.SetAttribute("integer-attribute", 3);
log.SetAttribute("double-attribute", 4.4);
log.SetDefaultAttributes(options, new SdkVersion { Name = "Sentry.Test.SDK", Version = "1.2.3-test+Sentry" });
log.SetDefaultAttributes(options, scope);

var envelope = EnvelopeItem.FromLog(new StructuredLog([log]));

Expand Down Expand Up @@ -266,6 +385,54 @@ public void WriteTo_EnvelopeItem_MaximalSerializedSentryLog()
"value": "1.2.3-test+Sentry",
"type": "string"
},
"sentry.replay_id": {
"value": "{{replayId.ToString()}}",
"type": "string"
},
"user.id": {
"value": "my-user-id",
"type": "string"
},
"user.name": {
"value": "my-user-name",
"type": "string"
},
"user.email": {
"value": "my-user-email",
"type": "string"
},
"browser.name": {
"value": "my-browser-name",
"type": "string"
},
"browser.version": {
"value": "my-browser-version",
"type": "string"
},
"server.address": {
"value": "my-server-address",
"type": "string"
},
"os.name": {
"value": "my-os-name",
"type": "string"
},
"os.version": {
"value": "my-os-version",
"type": "string"
},
"device.brand": {
"value": "my-device-brand",
"type": "string"
},
"device.model": {
"value": "my-device-model",
"type": "string"
},
"device.family": {
"value": "my-device-family",
"type": "string"
},
"sentry.trace.parent_span_id": {
"value": "{{ParentSpanId.ToString()}}",
"type": "string"
Expand Down
Loading