Skip to content

Commit 58670f6

Browse files
authored
Add SentryScopeStateProcessor #603
2 parents 1e769e1 + 2776908 commit 58670f6

File tree

8 files changed

+114
-37
lines changed

8 files changed

+114
-37
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
# Changelog
22

3+
## vNext
4+
5+
* Add SentryScopeStateProcessor #603
6+
37
## 3.0.0-alpha.5
48

59
* Replaced `BaseScope` with `IScope`. (#590) @Tyrrrz

samples/Sentry.Samples.Console.Customized/Program.cs

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,9 @@ await SentrySdk.ConfigureScopeAsync(async scope =>
107107
{
108108
client.DefaultRequestHeaders.TryAddWithoutValidation("CustomHeader", new[] { "my value" });
109109
};
110+
111+
// Control/override how to apply the State object into the scope
112+
o.SentryScopeStateProcessor = new MyCustomerScopeStateProcessor();
110113
}))
111114
{
112115
// Ignored by its type due to the setting above
@@ -222,7 +225,7 @@ public AdminPartMiddleware(ISentryClient adminClient, dynamic middleware)
222225

223226
public void Invoke(dynamic request)
224227
{
225-
using (SentrySdk.PushScope())
228+
using (SentrySdk.PushScope(new SpecialContextObject()))
226229
{
227230
SentrySdk.AddBreadcrumb(request.Path, "request-path");
228231

@@ -265,4 +268,27 @@ protected override void ProcessException(ArgumentException exception, SentryEven
265268
sentryEvent.SetTag("parameter-name", exception.ParamName);
266269
}
267270
}
271+
272+
private class MyCustomerScopeStateProcessor : ISentryScopeStateProcessor
273+
{
274+
private readonly ISentryScopeStateProcessor _fallback = new DefaultSentryScopeStateProcessor();
275+
276+
public void Apply(IScope scope, object state)
277+
{
278+
if (state is SpecialContextObject specialState)
279+
{
280+
scope.SetTag("SpecialContextObject", specialState.A + specialState.B);
281+
}
282+
else
283+
{
284+
_fallback.Apply(scope, state);
285+
}
286+
}
287+
}
288+
289+
private class SpecialContextObject
290+
{
291+
public string A { get; } = "hello";
292+
public string B { get; } = "world";
293+
}
268294
}

src/Sentry/Internal/SentryScopeManager.cs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,8 +58,14 @@ public IDisposable PushScope<TState>(TState state)
5858

5959
if (scope.Key.Locked)
6060
{
61-
// TODO: keep state on current scope?
62-
_options.DiagnosticLogger?.LogDebug("Locked scope. No new scope pushed.");
61+
_options?.DiagnosticLogger?.LogDebug("Locked scope. No new scope pushed.");
62+
63+
// Apply to current scope
64+
if (state != null)
65+
{
66+
scope.Key.Apply(state);
67+
}
68+
6369
return DisabledHub.Instance;
6470
}
6571

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
5+
namespace Sentry.Protocol
6+
{
7+
/// <summary>
8+
/// Defines the logic for applying state onto a scope.
9+
/// </summary>
10+
public class DefaultSentryScopeStateProcessor : ISentryScopeStateProcessor
11+
{
12+
/// <summary>
13+
/// Applies state onto a scope.
14+
/// </summary>
15+
public void Apply(IScope scope, object state)
16+
{
17+
switch (state)
18+
{
19+
case string scopeString:
20+
// TODO: find unique key to support multiple single-string scopes
21+
scope.SetTag("scope", scopeString);
22+
break;
23+
case IEnumerable<KeyValuePair<string, string>> keyValStringString:
24+
scope.SetTags(keyValStringString
25+
.Where(kv => !string.IsNullOrEmpty(kv.Value)));
26+
break;
27+
case IEnumerable<KeyValuePair<string, object>> keyValStringObject:
28+
{
29+
scope.SetTags(keyValStringObject
30+
.Select(k => new KeyValuePair<string, string>(
31+
k.Key,
32+
k.Value?.ToString()!))
33+
.Where(kv => !string.IsNullOrEmpty(kv.Value)));
34+
35+
break;
36+
}
37+
#if HAS_VALUE_TUPLE
38+
case ValueTuple<string, string> tupleStringString:
39+
if (!string.IsNullOrEmpty(tupleStringString.Item2))
40+
{
41+
scope.SetTag(tupleStringString.Item1, tupleStringString.Item2);
42+
}
43+
break;
44+
#endif
45+
default:
46+
scope.SetExtra("state", state);
47+
break;
48+
}
49+
}
50+
}
51+
}

src/Sentry/Protocol/IScopeOptions.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,11 @@ namespace Sentry.Protocol
77
/// </summary>
88
public interface IScopeOptions
99
{
10+
/// <summary>
11+
/// Configured scope processor.
12+
/// </summary>
13+
ISentryScopeStateProcessor SentryScopeStateProcessor { get; set; }
14+
1015
/// <summary>
1116
/// Gets or sets the maximum breadcrumbs.
1217
/// </summary>
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
namespace Sentry.Protocol
2+
{
3+
/// <summary>
4+
/// Defines the logic for applying state onto a scope.
5+
/// </summary>
6+
public interface ISentryScopeStateProcessor
7+
{
8+
/// <summary>
9+
/// Applies state onto a scope.
10+
/// </summary>
11+
void Apply(IScope scope, object state);
12+
}
13+
}

src/Sentry/ScopeExtensions.cs

Lines changed: 3 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22
using System.Collections.Concurrent;
33
using System.Collections.Generic;
44
using System.ComponentModel;
5-
using System.Linq;
65
using Sentry.Extensibility;
6+
using System.Linq;
77
using Sentry.Internal;
88
using Sentry.Protocol;
99
using Constants = Sentry.Protocol.Constants;
@@ -354,39 +354,8 @@ public static void Apply(this IScope from, IScope to)
354354
/// <param name="state">The state object to apply.</param>
355355
public static void Apply(this IScope scope, object state)
356356
{
357-
switch (state)
358-
{
359-
case string scopeString:
360-
// TODO: find unique key to support multiple single-string scopes
361-
scope.SetTag("scope", scopeString);
362-
break;
363-
case IEnumerable<KeyValuePair<string, string>> keyValStringString:
364-
scope.SetTags(keyValStringString
365-
.Where(kv => !string.IsNullOrEmpty(kv.Value)));
366-
break;
367-
case IEnumerable<KeyValuePair<string, object>> keyValStringObject:
368-
{
369-
scope.SetTags(keyValStringObject
370-
.Select(k => new KeyValuePair<string, string>(
371-
k.Key,
372-
k.Value?.ToString()!))
373-
.Where(kv => !string.IsNullOrEmpty(kv.Value)));
374-
375-
break;
376-
}
377-
#if HAS_VALUE_TUPLE
378-
case ValueTuple<string, string> tupleStringString:
379-
if (!string.IsNullOrEmpty(tupleStringString.Item2))
380-
{
381-
scope.SetTag(tupleStringString.Item1, tupleStringString.Item2);
382-
}
383-
384-
break;
385-
#endif
386-
default:
387-
scope.SetExtra("state", state);
388-
break;
389-
}
357+
var processor = scope.ScopeOptions?.SentryScopeStateProcessor ?? new DefaultSentryScopeStateProcessor();
358+
processor.Apply(scope, state);
390359
}
391360

392361
/// <summary>

src/Sentry/SentryOptions.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,9 @@ public class SentryOptions : IScopeOptions
5959

6060
internal ISentryHttpClientFactory? SentryHttpClientFactory { get; set; }
6161

62+
/// <inheritdoc />
63+
public ISentryScopeStateProcessor SentryScopeStateProcessor { get; set; } = new DefaultSentryScopeStateProcessor();
64+
6265
/// <summary>
6366
/// A list of namespaces (or prefixes) considered not part of application code
6467
/// </summary>

0 commit comments

Comments
 (0)