Skip to content
Draft
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
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
SpanId = 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
SpanId = 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
188 changes: 180 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()
SpanId = SpanId,
};
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",
};
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";
options.ServerName = "my-server-address";
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")),
SpanId = SpanId,
};
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, SpanId!.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,8 @@ 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);
log.SetOrigin("auto.log.sentry_tests");

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

Expand Down Expand Up @@ -266,6 +386,58 @@ public void WriteTo_EnvelopeItem_MaximalSerializedSentryLog()
"sentry.sdk.version": {
"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.origin": {
"value": "auto.log.sentry_tests",
"type": "string"
}
}
}
Expand Down
Loading