Skip to content
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

Improve WithScope and add WithScopeAsync #2303

Merged
merged 10 commits into from
Apr 17, 2023
Merged
Show file tree
Hide file tree
Changes from 6 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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
### Features

- Initial work to support profiling in a future release. ([#2206](https://github.com/getsentry/sentry-dotnet/pull/2206))
- Improve `WithScope` and add `WithScopeAsync` ([#2303](https://github.com/getsentry/sentry-dotnet/pull/2303))

### Fixes

Expand Down
2 changes: 0 additions & 2 deletions src/Sentry/Extensibility/HubAdapter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,6 @@ public IDisposable PushScope<TState>(TState state)
/// <summary>
/// Forwards the call to <see cref="SentrySdk"/>.
/// </summary>
[Obsolete("This method is deprecated in favor of overloads of CaptureEvent, CaptureMessage and CaptureException " +
"that provide a callback to a configurable scope.")]
[DebuggerStepThrough]
public void WithScope(Action<Scope> scopeCallback)
=> SentrySdk.WithScope(scopeCallback);
Expand Down
4 changes: 3 additions & 1 deletion src/Sentry/ISentryScopeManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,9 @@ public interface ISentryScopeManager
/// Runs the callback with a new scope which gets dropped at the end.
/// </summary>
/// <remarks>
/// Pushes a new scope, runs the callback, pops the scope.
/// Pushes a new scope, runs the callback, then pops the scope. Use this when you have significant work to
/// perform within an isolated scope. If you just need to configure scope for a single event, use the overloads
/// of CaptureEvent, CaptureMessage and CaptureException that provide a callback to a configurable scope.
/// </remarks>
/// <see href="https://docs.sentry.io/platforms/dotnet/enriching-events/scopes/#local-scopes"/>
/// <param name="scopeCallback">The callback to run with the one time scope.</param>
Expand Down
5 changes: 5 additions & 0 deletions src/Sentry/Internal/IInternalScopeManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,9 @@ internal interface IInternalScopeManager : ISentryScopeManager, IDisposable
{
KeyValuePair<Scope, ISentryClient> GetCurrent();
IScopeStackContainer ScopeStackContainer { get; }

// TODO: Move The following to ISentryScopeManager in a future major version.
T? WithScope<T>(Func<Scope, T?> scopeCallback);
Task WithScopeAsync(Func<Scope, Task> scopeCallback);
Task<T?> WithScopeAsync<T>(Func<Scope, Task<T?>> scopeCallback);
}
27 changes: 27 additions & 0 deletions src/Sentry/Internal/SentryScopeManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,33 @@ public void WithScope(Action<Scope> scopeCallback)
}
}

public T? WithScope<T>(Func<Scope, T?> scopeCallback)
{
using (PushScope())
{
var scope = GetCurrent();
return scopeCallback.Invoke(scope.Key);
}
}

public async Task WithScopeAsync(Func<Scope, Task> scopeCallback)
{
using (PushScope())
{
var scope = GetCurrent();
await scopeCallback.Invoke(scope.Key).ConfigureAwait(false);
mattjohnsonpint marked this conversation as resolved.
Show resolved Hide resolved
}
}

public async Task<T?> WithScopeAsync<T>(Func<Scope, Task<T?>> scopeCallback)
{
using (PushScope())
{
var scope = GetCurrent();
return await scopeCallback.Invoke(scope.Key).ConfigureAwait(false);
}
}

public void BindClient(ISentryClient? client)
{
_options.LogDebug("Binding a new client to the current scope.");
Expand Down
75 changes: 75 additions & 0 deletions src/Sentry/SentryScopeManagerExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
using Sentry.Internal;

namespace Sentry;

/// <summary>
/// Extension methods for <see cref="ISentryScopeManager"/>.
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
public static class SentryScopeManagerExtensions
{
/// <summary>
/// Runs the callback with a new scope which gets dropped at the end.
/// </summary>
/// <remarks>
/// Pushes a new scope, runs the callback, then pops the scope. Use this when you have significant work to
/// perform within an isolated scope. If you just need to configure scope for a single event, use the overloads
/// of CaptureEvent, CaptureMessage and CaptureException that provide a callback to a configurable scope.
/// </remarks>
/// <see href="https://docs.sentry.io/platforms/dotnet/enriching-events/scopes/#local-scopes"/>
/// <param name="scopeManager">The scope manager (usually the hub).</param>
/// <param name="scopeCallback">The callback to run with the one time scope.</param>
/// <returns>The result from the callback.</returns>
[DebuggerStepThrough]
public static T? WithScope<T>(this ISentryScopeManager scopeManager, Func<Scope, T?> scopeCallback) =>
scopeManager switch
{
Hub hub => hub.ScopeManager.WithScope(scopeCallback),
IInternalScopeManager manager => manager.WithScope(scopeCallback),
_ => default
};

/// <summary>
/// Runs the asynchronous callback with a new scope which gets dropped at the end.
/// </summary>
/// <remarks>
/// Asynchronous version of <see cref="ISentryScopeManager.WithScope"/>.
/// Pushes a new scope, runs the callback, then pops the scope. Use this when you have significant work to
/// perform within an isolated scope. If you just need to configure scope for a single event, use the overloads
/// of CaptureEvent, CaptureMessage and CaptureException that provide a callback to a configurable scope.
/// </remarks>
/// <see href="https://docs.sentry.io/platforms/dotnet/enriching-events/scopes/#local-scopes"/>
/// <param name="scopeManager">The scope manager (usually the hub).</param>
/// <param name="scopeCallback">The callback to run with the one time scope.</param>
/// <returns>An async task to await the callback.</returns>
[DebuggerStepThrough]
public static Task WithScopeAsync(this ISentryScopeManager scopeManager, Func<Scope, Task> scopeCallback) =>
scopeManager switch
{
Hub hub => hub.ScopeManager.WithScopeAsync(scopeCallback),
IInternalScopeManager manager => manager.WithScopeAsync(scopeCallback),
_ => Task.CompletedTask
};

/// <summary>
/// Runs the asynchronous callback with a new scope which gets dropped at the end.
/// </summary>
/// <remarks>
/// Asynchronous version of <see cref="ISentryScopeManager.WithScope"/>.
/// Pushes a new scope, runs the callback, then pops the scope. Use this when you have significant work to
/// perform within an isolated scope. If you just need to configure scope for a single event, use the overloads
/// of CaptureEvent, CaptureMessage and CaptureException that provide a callback to a configurable scope.
/// </remarks>
/// <see href="https://docs.sentry.io/platforms/dotnet/enriching-events/scopes/#local-scopes"/>
/// <param name="scopeManager">The scope manager (usually the hub).</param>
/// <param name="scopeCallback">The callback to run with the one time scope.</param>
/// <returns>An async task to await the result of the callback.</returns>
[DebuggerStepThrough]
public static Task<T?> WithScopeAsync<T>(this ISentryScopeManager scopeManager, Func<Scope, Task<T?>> scopeCallback) =>
scopeManager switch
{
Hub hub => hub.ScopeManager.WithScopeAsync(scopeCallback),
IInternalScopeManager manager => manager.WithScopeAsync(scopeCallback),
_ => Task.FromResult(default(T))
};
}
53 changes: 50 additions & 3 deletions src/Sentry/SentrySdk.cs
Original file line number Diff line number Diff line change
Expand Up @@ -275,16 +275,63 @@ public static void AddBreadcrumb(
/// Runs the callback with a new scope which gets dropped at the end.
/// </summary>
/// <remarks>
/// Pushes a new scope, runs the callback, pops the scope.
/// Pushes a new scope, runs the callback, then pops the scope. Use this when you have significant work to
/// perform within an isolated scope. If you just need to configure scope for a single event, use the overloads
/// of CaptureEvent, CaptureMessage and CaptureException that provide a callback to a configurable scope.
/// </remarks>
/// <see href="https://docs.sentry.io/platforms/dotnet/enriching-events/scopes/#local-scopes"/>
/// <param name="scopeCallback">The callback to run with the one time scope.</param>
[Obsolete("This method is deprecated in favor of overloads of CaptureEvent, CaptureMessage and CaptureException " +
"that provide a callback to a configurable scope.")]
[DebuggerStepThrough]
public static void WithScope(Action<Scope> scopeCallback)
=> CurrentHub.WithScope(scopeCallback);

/// <summary>
/// Runs the callback with a new scope which gets dropped at the end.
mattjohnsonpint marked this conversation as resolved.
Show resolved Hide resolved
/// </summary>
/// <remarks>
/// Pushes a new scope, runs the callback, then pops the scope. Use this when you have significant work to
/// perform within an isolated scope. If you just need to configure scope for a single event, use the overloads
/// of CaptureEvent, CaptureMessage and CaptureException that provide a callback to a configurable scope.
/// </remarks>
/// <see href="https://docs.sentry.io/platforms/dotnet/enriching-events/scopes/#local-scopes"/>
/// <param name="scopeCallback">The callback to run with the one time scope.</param>
/// <returns>The result from the callback.</returns>
[DebuggerStepThrough]
public static T? WithScope<T>(Func<Scope, T?> scopeCallback)
=> CurrentHub.WithScope(scopeCallback);

/// <summary>
/// Runs the asynchronous callback with a new scope which gets dropped at the end.
mattjohnsonpint marked this conversation as resolved.
Show resolved Hide resolved
/// </summary>
/// <remarks>
/// Asynchronous version of <see cref="ISentryScopeManager.WithScope"/>.
/// Pushes a new scope, runs the callback, then pops the scope. Use this when you have significant work to
/// perform within an isolated scope. If you just need to configure scope for a single event, use the overloads
/// of CaptureEvent, CaptureMessage and CaptureException that provide a callback to a configurable scope.
/// </remarks>
/// <see href="https://docs.sentry.io/platforms/dotnet/enriching-events/scopes/#local-scopes"/>
/// <param name="scopeCallback">The callback to run with the one time scope.</param>
/// <returns>An async task to await the callback.</returns>
[DebuggerStepThrough]
public static Task WithScopeAsync(Func<Scope, Task> scopeCallback)
=> CurrentHub.WithScopeAsync(scopeCallback);

/// <summary>
/// Runs the asynchronous callback with a new scope which gets dropped at the end.
mattjohnsonpint marked this conversation as resolved.
Show resolved Hide resolved
/// </summary>
/// <remarks>
/// Asynchronous version of <see cref="ISentryScopeManager.WithScope"/>.
/// Pushes a new scope, runs the callback, then pops the scope. Use this when you have significant work to
/// perform within an isolated scope. If you just need to configure scope for a single event, use the overloads
/// of CaptureEvent, CaptureMessage and CaptureException that provide a callback to a configurable scope.
/// </remarks>
/// <see href="https://docs.sentry.io/platforms/dotnet/enriching-events/scopes/#local-scopes"/>
/// <param name="scopeCallback">The callback to run with the one time scope.</param>
/// <returns>An async task to await the result of the callback.</returns>
[DebuggerStepThrough]
public static Task<T?> WithScopeAsync<T>(Func<Scope, Task<T?>> scopeCallback)
=> CurrentHub.WithScopeAsync(scopeCallback);

/// <summary>
/// Configures the scope through the callback.
/// </summary>
Expand Down
13 changes: 9 additions & 4 deletions test/Sentry.Tests/ApiApprovalTests.Run.Core3_1.verified.txt
Original file line number Diff line number Diff line change
Expand Up @@ -632,6 +632,12 @@ namespace Sentry
where TIntegration : Sentry.Integrations.ISdkIntegration { }
public static Sentry.SentryOptions UseStackTraceFactory(this Sentry.SentryOptions options, Sentry.Extensibility.ISentryStackTraceFactory sentryStackTraceFactory) { }
}
public static class SentryScopeManagerExtensions
{
public static T? WithScope<T>(this Sentry.ISentryScopeManager scopeManager, System.Func<Sentry.Scope, T?> scopeCallback) { }
public static System.Threading.Tasks.Task WithScopeAsync(this Sentry.ISentryScopeManager scopeManager, System.Func<Sentry.Scope, System.Threading.Tasks.Task> scopeCallback) { }
public static System.Threading.Tasks.Task<T?> WithScopeAsync<T>(this Sentry.ISentryScopeManager scopeManager, System.Func<Sentry.Scope, System.Threading.Tasks.Task<T?>> scopeCallback) { }
}
public static class SentrySdk
{
public static bool IsEnabled { get; }
Expand Down Expand Up @@ -678,9 +684,10 @@ namespace Sentry
public static Sentry.ITransaction StartTransaction(string name, string operation) { }
public static Sentry.ITransaction StartTransaction(string name, string operation, Sentry.SentryTraceHeader traceHeader) { }
public static Sentry.ITransaction StartTransaction(string name, string operation, string? description) { }
[System.Obsolete("This method is deprecated in favor of overloads of CaptureEvent, CaptureMessage a" +
"nd CaptureException that provide a callback to a configurable scope.")]
public static void WithScope(System.Action<Sentry.Scope> scopeCallback) { }
public static T? WithScope<T>(System.Func<Sentry.Scope, T?> scopeCallback) { }
public static System.Threading.Tasks.Task WithScopeAsync(System.Func<Sentry.Scope, System.Threading.Tasks.Task> scopeCallback) { }
public static System.Threading.Tasks.Task<T?> WithScopeAsync<T>(System.Func<Sentry.Scope, System.Threading.Tasks.Task<T?>> scopeCallback) { }
}
public sealed class SentryStackFrame : Sentry.IJsonSerializable
{
Expand Down Expand Up @@ -1165,8 +1172,6 @@ namespace Sentry.Extensibility
public void ResumeSession() { }
public void StartSession() { }
public Sentry.ITransaction StartTransaction(Sentry.ITransactionContext context, System.Collections.Generic.IReadOnlyDictionary<string, object?> customSamplingContext) { }
[System.Obsolete("This method is deprecated in favor of overloads of CaptureEvent, CaptureMessage a" +
"nd CaptureException that provide a callback to a configurable scope.")]
public void WithScope(System.Action<Sentry.Scope> scopeCallback) { }
}
public interface IBackgroundWorker
Expand Down
13 changes: 9 additions & 4 deletions test/Sentry.Tests/ApiApprovalTests.Run.DotNet6_0.verified.txt
Original file line number Diff line number Diff line change
Expand Up @@ -632,6 +632,12 @@ namespace Sentry
where TIntegration : Sentry.Integrations.ISdkIntegration { }
public static Sentry.SentryOptions UseStackTraceFactory(this Sentry.SentryOptions options, Sentry.Extensibility.ISentryStackTraceFactory sentryStackTraceFactory) { }
}
public static class SentryScopeManagerExtensions
{
public static T? WithScope<T>(this Sentry.ISentryScopeManager scopeManager, System.Func<Sentry.Scope, T?> scopeCallback) { }
public static System.Threading.Tasks.Task WithScopeAsync(this Sentry.ISentryScopeManager scopeManager, System.Func<Sentry.Scope, System.Threading.Tasks.Task> scopeCallback) { }
public static System.Threading.Tasks.Task<T?> WithScopeAsync<T>(this Sentry.ISentryScopeManager scopeManager, System.Func<Sentry.Scope, System.Threading.Tasks.Task<T?>> scopeCallback) { }
}
public static class SentrySdk
{
public static bool IsEnabled { get; }
Expand Down Expand Up @@ -678,9 +684,10 @@ namespace Sentry
public static Sentry.ITransaction StartTransaction(string name, string operation) { }
public static Sentry.ITransaction StartTransaction(string name, string operation, Sentry.SentryTraceHeader traceHeader) { }
public static Sentry.ITransaction StartTransaction(string name, string operation, string? description) { }
[System.Obsolete("This method is deprecated in favor of overloads of CaptureEvent, CaptureMessage a" +
"nd CaptureException that provide a callback to a configurable scope.")]
public static void WithScope(System.Action<Sentry.Scope> scopeCallback) { }
public static T? WithScope<T>(System.Func<Sentry.Scope, T?> scopeCallback) { }
public static System.Threading.Tasks.Task WithScopeAsync(System.Func<Sentry.Scope, System.Threading.Tasks.Task> scopeCallback) { }
public static System.Threading.Tasks.Task<T?> WithScopeAsync<T>(System.Func<Sentry.Scope, System.Threading.Tasks.Task<T?>> scopeCallback) { }
}
public sealed class SentryStackFrame : Sentry.IJsonSerializable
{
Expand Down Expand Up @@ -1165,8 +1172,6 @@ namespace Sentry.Extensibility
public void ResumeSession() { }
public void StartSession() { }
public Sentry.ITransaction StartTransaction(Sentry.ITransactionContext context, System.Collections.Generic.IReadOnlyDictionary<string, object?> customSamplingContext) { }
[System.Obsolete("This method is deprecated in favor of overloads of CaptureEvent, CaptureMessage a" +
"nd CaptureException that provide a callback to a configurable scope.")]
public void WithScope(System.Action<Sentry.Scope> scopeCallback) { }
}
public interface IBackgroundWorker
Expand Down
13 changes: 9 additions & 4 deletions test/Sentry.Tests/ApiApprovalTests.Run.DotNet7_0.verified.txt
Original file line number Diff line number Diff line change
Expand Up @@ -632,6 +632,12 @@ namespace Sentry
where TIntegration : Sentry.Integrations.ISdkIntegration { }
public static Sentry.SentryOptions UseStackTraceFactory(this Sentry.SentryOptions options, Sentry.Extensibility.ISentryStackTraceFactory sentryStackTraceFactory) { }
}
public static class SentryScopeManagerExtensions
{
public static T? WithScope<T>(this Sentry.ISentryScopeManager scopeManager, System.Func<Sentry.Scope, T?> scopeCallback) { }
public static System.Threading.Tasks.Task WithScopeAsync(this Sentry.ISentryScopeManager scopeManager, System.Func<Sentry.Scope, System.Threading.Tasks.Task> scopeCallback) { }
public static System.Threading.Tasks.Task<T?> WithScopeAsync<T>(this Sentry.ISentryScopeManager scopeManager, System.Func<Sentry.Scope, System.Threading.Tasks.Task<T?>> scopeCallback) { }
}
public static class SentrySdk
{
public static bool IsEnabled { get; }
Expand Down Expand Up @@ -678,9 +684,10 @@ namespace Sentry
public static Sentry.ITransaction StartTransaction(string name, string operation) { }
public static Sentry.ITransaction StartTransaction(string name, string operation, Sentry.SentryTraceHeader traceHeader) { }
public static Sentry.ITransaction StartTransaction(string name, string operation, string? description) { }
[System.Obsolete("This method is deprecated in favor of overloads of CaptureEvent, CaptureMessage a" +
"nd CaptureException that provide a callback to a configurable scope.")]
public static void WithScope(System.Action<Sentry.Scope> scopeCallback) { }
public static T? WithScope<T>(System.Func<Sentry.Scope, T?> scopeCallback) { }
public static System.Threading.Tasks.Task WithScopeAsync(System.Func<Sentry.Scope, System.Threading.Tasks.Task> scopeCallback) { }
public static System.Threading.Tasks.Task<T?> WithScopeAsync<T>(System.Func<Sentry.Scope, System.Threading.Tasks.Task<T?>> scopeCallback) { }
}
public sealed class SentryStackFrame : Sentry.IJsonSerializable
{
Expand Down Expand Up @@ -1165,8 +1172,6 @@ namespace Sentry.Extensibility
public void ResumeSession() { }
public void StartSession() { }
public Sentry.ITransaction StartTransaction(Sentry.ITransactionContext context, System.Collections.Generic.IReadOnlyDictionary<string, object?> customSamplingContext) { }
[System.Obsolete("This method is deprecated in favor of overloads of CaptureEvent, CaptureMessage a" +
"nd CaptureException that provide a callback to a configurable scope.")]
public void WithScope(System.Action<Sentry.Scope> scopeCallback) { }
}
public interface IBackgroundWorker
Expand Down
Loading