diff --git a/CHANGELOG.md b/CHANGELOG.md index 5cef687a66..8026642cd4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ ### Features - Reduced memory pressure when sampling less than 100% of traces/transactions ([#4212](https://github.com/getsentry/sentry-dotnet/pull/4212)) +- Add SentrySdk.SetTag ([#4232](https://github.com/getsentry/sentry-dotnet/pull/4232)) ### Fixes diff --git a/src/Sentry/Extensibility/DisabledHub.cs b/src/Sentry/Extensibility/DisabledHub.cs index a5d7541dde..86eb6a1df5 100644 --- a/src/Sentry/Extensibility/DisabledHub.cs +++ b/src/Sentry/Extensibility/DisabledHub.cs @@ -35,6 +35,20 @@ public void ConfigureScope(Action configureScope) /// public Task ConfigureScopeAsync(Func configureScope) => Task.CompletedTask; + /// + /// No-Op. + /// + public void SetTag(string key, string value) + { + } + + /// + /// No-Op. + /// + public void UnsetTag(string key) + { + } + /// /// No-Op. /// diff --git a/src/Sentry/Extensibility/HubAdapter.cs b/src/Sentry/Extensibility/HubAdapter.cs index b21eb369ee..fb9196b718 100644 --- a/src/Sentry/Extensibility/HubAdapter.cs +++ b/src/Sentry/Extensibility/HubAdapter.cs @@ -46,6 +46,20 @@ public void ConfigureScope(Action configureScope) public Task ConfigureScopeAsync(Func configureScope) => SentrySdk.ConfigureScopeAsync(configureScope); + /// + /// Forwards the call to . + /// + [DebuggerStepThrough] + public void SetTag(string key, string value) + => SentrySdk.SetTag(key, value); + + /// + /// Forwards the call to . + /// + [DebuggerStepThrough] + public void UnsetTag(string key) + => SentrySdk.UnsetTag(key); + /// /// Forwards the call to . /// diff --git a/src/Sentry/ISentryScopeManager.cs b/src/Sentry/ISentryScopeManager.cs index 3fd221c229..25b58069db 100644 --- a/src/Sentry/ISentryScopeManager.cs +++ b/src/Sentry/ISentryScopeManager.cs @@ -22,6 +22,16 @@ public interface ISentryScopeManager /// A task that completes when the callback is done or a completed task if the SDK is disabled. public Task ConfigureScopeAsync(Func configureScope); + /// + /// Sets a tag on the current scope. + /// + public void SetTag(string key, string value); + + /// + /// Removes a tag from the current scope. + /// + public void UnsetTag(string key); + /// /// Binds the client to the current scope. /// diff --git a/src/Sentry/Internal/Hub.cs b/src/Sentry/Internal/Hub.cs index fea369de0f..9176ca3ea4 100644 --- a/src/Sentry/Internal/Hub.cs +++ b/src/Sentry/Internal/Hub.cs @@ -111,6 +111,10 @@ public async Task ConfigureScopeAsync(Func configureScope) } } + public void SetTag(string key, string value) => ScopeManager.SetTag(key, value); + + public void UnsetTag(string key) => ScopeManager.UnsetTag(key); + public IDisposable PushScope() => ScopeManager.PushScope(); public IDisposable PushScope(TState state) => ScopeManager.PushScope(state); diff --git a/src/Sentry/Internal/SentryScopeManager.cs b/src/Sentry/Internal/SentryScopeManager.cs index 676c105eb5..61e68ac1f4 100644 --- a/src/Sentry/Internal/SentryScopeManager.cs +++ b/src/Sentry/Internal/SentryScopeManager.cs @@ -44,6 +44,18 @@ public Task ConfigureScopeAsync(Func? configureScope) return configureScope?.Invoke(scope) ?? Task.CompletedTask; } + public void SetTag(string key, string value) + { + var (scope, _) = GetCurrent(); + scope.SetTag(key, value); + } + + public void UnsetTag(string key) + { + var (scope, _) = GetCurrent(); + scope.UnsetTag(key); + } + public IDisposable PushScope() => PushScope(null); public IDisposable PushScope(TState? state) diff --git a/src/Sentry/SentrySdk.cs b/src/Sentry/SentrySdk.cs index 6627007cda..decd76f6d1 100644 --- a/src/Sentry/SentrySdk.cs +++ b/src/Sentry/SentrySdk.cs @@ -389,6 +389,20 @@ public static void ConfigureScope(Action configureScope) public static Task ConfigureScopeAsync(Func configureScope) => CurrentHub.ConfigureScopeAsync(configureScope); + /// + /// Sets a tag on the current scope. + /// + [DebuggerStepThrough] + public static void SetTag(string key, string value) + => CurrentHub.SetTag(key, value); + + /// + /// Removes a tag from the current scope. + /// + [DebuggerStepThrough] + public static void UnsetTag(string key) + => CurrentHub.UnsetTag(key); + /// [DebuggerStepThrough] [EditorBrowsable(EditorBrowsableState.Never)] diff --git a/test/Sentry.Tests/ApiApprovalTests.Run.DotNet8_0.verified.txt b/test/Sentry.Tests/ApiApprovalTests.Run.DotNet8_0.verified.txt index 4f3690e5d4..b9ad168f30 100644 --- a/test/Sentry.Tests/ApiApprovalTests.Run.DotNet8_0.verified.txt +++ b/test/Sentry.Tests/ApiApprovalTests.Run.DotNet8_0.verified.txt @@ -259,6 +259,8 @@ namespace Sentry System.Threading.Tasks.Task ConfigureScopeAsync(System.Func configureScope); System.IDisposable PushScope(); System.IDisposable PushScope(TState state); + void SetTag(string key, string value); + void UnsetTag(string key); } public interface ISentryScopeStateProcessor { @@ -863,12 +865,14 @@ namespace Sentry public static System.IDisposable PushScope() { } public static System.IDisposable PushScope(TState state) { } public static void ResumeSession() { } + public static void SetTag(string key, string value) { } public static void StartSession() { } public static Sentry.ITransactionTracer StartTransaction(Sentry.ITransactionContext context) { } public static Sentry.ITransactionTracer StartTransaction(Sentry.ITransactionContext context, System.Collections.Generic.IReadOnlyDictionary customSamplingContext) { } public static Sentry.ITransactionTracer StartTransaction(string name, string operation) { } public static Sentry.ITransactionTracer StartTransaction(string name, string operation, Sentry.SentryTraceHeader traceHeader) { } public static Sentry.ITransactionTracer StartTransaction(string name, string operation, string? description) { } + public static void UnsetTag(string key) { } } public class SentrySession : Sentry.ISentrySession { @@ -1367,8 +1371,10 @@ namespace Sentry.Extensibility public System.IDisposable PushScope() { } public System.IDisposable PushScope(TState state) { } public void ResumeSession() { } + public void SetTag(string key, string value) { } public void StartSession() { } public Sentry.ITransactionTracer StartTransaction(Sentry.ITransactionContext context, System.Collections.Generic.IReadOnlyDictionary customSamplingContext) { } + public void UnsetTag(string key) { } } public class FormRequestPayloadExtractor : Sentry.Extensibility.BaseRequestPayloadExtractor { @@ -1413,8 +1419,10 @@ namespace Sentry.Extensibility public System.IDisposable PushScope() { } public System.IDisposable PushScope(TState state) { } public void ResumeSession() { } + public void SetTag(string key, string value) { } public void StartSession() { } public Sentry.ITransactionTracer StartTransaction(Sentry.ITransactionContext context, System.Collections.Generic.IReadOnlyDictionary customSamplingContext) { } + public void UnsetTag(string key) { } } public interface IBackgroundWorker { diff --git a/test/Sentry.Tests/ApiApprovalTests.Run.DotNet9_0.verified.txt b/test/Sentry.Tests/ApiApprovalTests.Run.DotNet9_0.verified.txt index 4f3690e5d4..b9ad168f30 100644 --- a/test/Sentry.Tests/ApiApprovalTests.Run.DotNet9_0.verified.txt +++ b/test/Sentry.Tests/ApiApprovalTests.Run.DotNet9_0.verified.txt @@ -259,6 +259,8 @@ namespace Sentry System.Threading.Tasks.Task ConfigureScopeAsync(System.Func configureScope); System.IDisposable PushScope(); System.IDisposable PushScope(TState state); + void SetTag(string key, string value); + void UnsetTag(string key); } public interface ISentryScopeStateProcessor { @@ -863,12 +865,14 @@ namespace Sentry public static System.IDisposable PushScope() { } public static System.IDisposable PushScope(TState state) { } public static void ResumeSession() { } + public static void SetTag(string key, string value) { } public static void StartSession() { } public static Sentry.ITransactionTracer StartTransaction(Sentry.ITransactionContext context) { } public static Sentry.ITransactionTracer StartTransaction(Sentry.ITransactionContext context, System.Collections.Generic.IReadOnlyDictionary customSamplingContext) { } public static Sentry.ITransactionTracer StartTransaction(string name, string operation) { } public static Sentry.ITransactionTracer StartTransaction(string name, string operation, Sentry.SentryTraceHeader traceHeader) { } public static Sentry.ITransactionTracer StartTransaction(string name, string operation, string? description) { } + public static void UnsetTag(string key) { } } public class SentrySession : Sentry.ISentrySession { @@ -1367,8 +1371,10 @@ namespace Sentry.Extensibility public System.IDisposable PushScope() { } public System.IDisposable PushScope(TState state) { } public void ResumeSession() { } + public void SetTag(string key, string value) { } public void StartSession() { } public Sentry.ITransactionTracer StartTransaction(Sentry.ITransactionContext context, System.Collections.Generic.IReadOnlyDictionary customSamplingContext) { } + public void UnsetTag(string key) { } } public class FormRequestPayloadExtractor : Sentry.Extensibility.BaseRequestPayloadExtractor { @@ -1413,8 +1419,10 @@ namespace Sentry.Extensibility public System.IDisposable PushScope() { } public System.IDisposable PushScope(TState state) { } public void ResumeSession() { } + public void SetTag(string key, string value) { } public void StartSession() { } public Sentry.ITransactionTracer StartTransaction(Sentry.ITransactionContext context, System.Collections.Generic.IReadOnlyDictionary customSamplingContext) { } + public void UnsetTag(string key) { } } public interface IBackgroundWorker { diff --git a/test/Sentry.Tests/ApiApprovalTests.Run.Net4_8.verified.txt b/test/Sentry.Tests/ApiApprovalTests.Run.Net4_8.verified.txt index 4faee7024c..97dac16884 100644 --- a/test/Sentry.Tests/ApiApprovalTests.Run.Net4_8.verified.txt +++ b/test/Sentry.Tests/ApiApprovalTests.Run.Net4_8.verified.txt @@ -247,6 +247,8 @@ namespace Sentry System.Threading.Tasks.Task ConfigureScopeAsync(System.Func configureScope); System.IDisposable PushScope(); System.IDisposable PushScope(TState state); + void SetTag(string key, string value); + void UnsetTag(string key); } public interface ISentryScopeStateProcessor { @@ -844,12 +846,14 @@ namespace Sentry public static System.IDisposable PushScope() { } public static System.IDisposable PushScope(TState state) { } public static void ResumeSession() { } + public static void SetTag(string key, string value) { } public static void StartSession() { } public static Sentry.ITransactionTracer StartTransaction(Sentry.ITransactionContext context) { } public static Sentry.ITransactionTracer StartTransaction(Sentry.ITransactionContext context, System.Collections.Generic.IReadOnlyDictionary customSamplingContext) { } public static Sentry.ITransactionTracer StartTransaction(string name, string operation) { } public static Sentry.ITransactionTracer StartTransaction(string name, string operation, Sentry.SentryTraceHeader traceHeader) { } public static Sentry.ITransactionTracer StartTransaction(string name, string operation, string? description) { } + public static void UnsetTag(string key) { } } public class SentrySession : Sentry.ISentrySession { @@ -1348,8 +1352,10 @@ namespace Sentry.Extensibility public System.IDisposable PushScope() { } public System.IDisposable PushScope(TState state) { } public void ResumeSession() { } + public void SetTag(string key, string value) { } public void StartSession() { } public Sentry.ITransactionTracer StartTransaction(Sentry.ITransactionContext context, System.Collections.Generic.IReadOnlyDictionary customSamplingContext) { } + public void UnsetTag(string key) { } } public class FormRequestPayloadExtractor : Sentry.Extensibility.BaseRequestPayloadExtractor { @@ -1394,8 +1400,10 @@ namespace Sentry.Extensibility public System.IDisposable PushScope() { } public System.IDisposable PushScope(TState state) { } public void ResumeSession() { } + public void SetTag(string key, string value) { } public void StartSession() { } public Sentry.ITransactionTracer StartTransaction(Sentry.ITransactionContext context, System.Collections.Generic.IReadOnlyDictionary customSamplingContext) { } + public void UnsetTag(string key) { } } public interface IBackgroundWorker { diff --git a/test/Sentry.Tests/SentrySdkTests.cs b/test/Sentry.Tests/SentrySdkTests.cs index fd6abadb00..ca2723df61 100644 --- a/test/Sentry.Tests/SentrySdkTests.cs +++ b/test/Sentry.Tests/SentrySdkTests.cs @@ -467,6 +467,57 @@ async Task ModifyScope() } } + [Fact] + public void SetTag_SetsTagOnCurrentScope() + { + using var _ = SentrySdk.Init(o => + { + o.Dsn = ValidDsn; + o.AutoSessionTracking = false; + o.BackgroundWorker = Substitute.For(); + o.InitNativeSdks = false; + }); + + const string key = "key"; + const string value = "value"; + + SentrySdk.SetTag(key, value); + + string actual = null; + SentrySdk.ConfigureScope(s => actual = s.Tags[key]); + + Assert.Equal(value, actual); + } + + [Fact] + public void SetTag_NotInit_NoOp() => SentrySdk.SetTag("key", "value"); + + [Fact] + public void UnsetTag_UnsetsTagOnCurrentScope() + { + using var _ = SentrySdk.Init(o => + { + o.Dsn = ValidDsn; + o.AutoSessionTracking = false; + o.BackgroundWorker = Substitute.For(); + o.InitNativeSdks = false; + }); + + const string key = "key"; + const string value = "value"; + + SentrySdk.SetTag(key, value); + SentrySdk.UnsetTag(key); + + bool? containsKey = null; + SentrySdk.ConfigureScope(s => containsKey = s.Tags.ContainsKey(key)); + + Assert.True(containsKey is false); + } + + [Fact] + public void UnsetTag_NotInit_NoOp() => SentrySdk.UnsetTag("key"); + [Fact] public void CaptureEvent_WithConfiguredScope_ScopeAppliesToEvent() {