Skip to content

Commit 370acc1

Browse files
Add Hint support (#2351)
1 parent 4e947d8 commit 370acc1

File tree

60 files changed

+2393
-201
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

60 files changed

+2393
-201
lines changed

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,11 @@
44

55
### Features
66

7+
- Add `Hint` support ([#2351](https://github.com/getsentry/sentry-dotnet/pull/2351))
8+
- Currently, this allows you to manipulate attachments in the various "before" event delegates.
9+
- Hints can also be used in event and transaction processors by implementing `ISentryEventProcessorWithHint` or `ISentryTransactionProcessorWithHint`, instead of `ISentryEventProcessor` or `ISentryTransactionProcessor`.
10+
- Note: Obsoletes the `BeforeSend`, `BeforeSendTransaction`, and `BeforeBreadcrumb` properties on the `SentryOptions` class. They have been replaced with `SetBeforeSend`, `SetBeforeSendTransaction`, and `SetBeforeBreadcrumb` respectively. Each one provides overloads both with and without a `Hint` object.
11+
712
- Allow setting the active span on the scope ([#2364](https://github.com/getsentry/sentry-dotnet/pull/2364))
813
- Note: Obsoletes the `Scope.GetSpan` method in favor of a `Scope.Span` property (which now has a setter as well).
914

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

Lines changed: 23 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -42,28 +42,36 @@ await SentrySdk.ConfigureScopeAsync(async scope =>
4242
// o.SampleRate = 0.5f; // Randomly drop (don't send to Sentry) half of events
4343

4444
// Modifications to event before it goes out. Could replace the event altogether
45-
o.BeforeSend = @event =>
46-
{
47-
// Drop an event altogether:
48-
if (@event.Tags.ContainsKey("SomeTag"))
45+
o.SetBeforeSend((@event, _) =>
4946
{
50-
return null;
51-
}
47+
// Drop an event altogether:
48+
if (@event.Tags.ContainsKey("SomeTag"))
49+
{
50+
return null;
51+
}
5252

53-
return @event;
54-
};
53+
return @event;
54+
}
55+
);
5556

5657
// Allows inspecting and modifying, returning a new or simply rejecting (returning null)
57-
o.BeforeBreadcrumb = crumb =>
58+
o.SetBeforeBreadcrumb((crumb, hint) =>
5859
{
5960
// Don't add breadcrumbs with message containing:
6061
if (crumb.Message?.Contains("bad breadcrumb") == true)
6162
{
6263
return null;
6364
}
6465

66+
// Replace breadcrumbs entirely incase of a drastic hint
67+
const string replaceBreadcrumb = "don't trust this breadcrumb";
68+
if (hint.Items.TryGetValue(replaceBreadcrumb, out var replacementMessage))
69+
{
70+
return new Breadcrumb((string)replacementMessage, null, null, null, BreadcrumbLevel.Critical);
71+
}
72+
6573
return crumb;
66-
};
74+
});
6775

6876
// Ignore exception by its type:
6977
o.AddExceptionFilterForType<XsltCompileException>();
@@ -102,6 +110,11 @@ await SentrySdk.ConfigureScopeAsync(async scope =>
102110
SentrySdk.AddBreadcrumb(
103111
"A 'bad breadcrumb' that will be rejected because of 'BeforeBreadcrumb callback above.'");
104112

113+
SentrySdk.AddBreadcrumb(
114+
new Breadcrumb("A breadcrumb that will be replaced by the 'BeforeBreadcrumb callback because of the hint", null),
115+
new Hint("don't trust this breadcrumb", "trust this instead")
116+
);
117+
105118
// Data added to the root scope (no PushScope called up to this point)
106119
// The modifications done here will affect all events sent and will propagate to child scopes.
107120
await SentrySdk.ConfigureScopeAsync(async scope =>

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

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -43,19 +43,20 @@ await SentrySdk.ConfigureScopeAsync(async scope =>
4343
// o.SampleRate = 0.5f; // Randomly drop (don't send to Sentry) half of events
4444

4545
// Modifications to event before it goes out. Could replace the event altogether
46-
o.BeforeSend = @event =>
47-
{
48-
// Drop an event altogether:
49-
if (@event.Tags.ContainsKey("SomeTag"))
46+
o.SetBeforeSend((@event, _) =>
5047
{
51-
return null;
52-
}
48+
// Drop an event altogether:
49+
if (@event.Tags.ContainsKey("SomeTag"))
50+
{
51+
return null;
52+
}
5353

54-
return @event;
55-
};
54+
return @event;
55+
}
56+
);
5657

5758
// Allows inspecting and modifying, returning a new or simply rejecting (returning null)
58-
o.BeforeBreadcrumb = crumb =>
59+
o.SetBeforeBreadcrumb((crumb, _) =>
5960
{
6061
// Don't add breadcrumbs with message containing:
6162
if (crumb.Message?.Contains("bad breadcrumb") == true)
@@ -64,7 +65,7 @@ await SentrySdk.ConfigureScopeAsync(async scope =>
6465
}
6566

6667
return crumb;
67-
};
68+
});
6869

6970
// Ignore exception by its type:
7071
o.AddExceptionFilterForType<XsltCompileException>();

src/Sentry/Extensibility/DisabledHub.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,11 @@ public void BindClient(ISentryClient client)
114114
/// </summary>
115115
public SentryId CaptureEvent(SentryEvent evt, Scope? scope = null) => SentryId.Empty;
116116

117+
/// <summary>
118+
/// No-Op.
119+
/// </summary>
120+
public SentryId CaptureEvent(SentryEvent evt, Hint? hint, Scope? scope = null) => SentryId.Empty;
121+
117122
/// <summary>
118123
/// No-Op.
119124
/// </summary>
@@ -126,6 +131,13 @@ public void CaptureTransaction(Transaction transaction)
126131
{
127132
}
128133

134+
/// <summary>
135+
/// No-Op.
136+
/// </summary>
137+
public void CaptureTransaction(Transaction transaction, Hint? hint)
138+
{
139+
}
140+
129141
/// <summary>
130142
/// No-Op.
131143
/// </summary>

src/Sentry/Extensibility/HubAdapter.cs

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -163,33 +163,34 @@ public void AddBreadcrumb(
163163
data,
164164
level);
165165

166+
/// <summary>
167+
/// Forwards the call to <see cref="SentrySdk"/>
168+
/// </summary>
169+
SentryId IHubEx.CaptureEventInternal(SentryEvent evt, Hint? hint, Scope? scope)
170+
=> SentrySdk.CaptureEventInternal(evt, hint, scope);
171+
166172
/// <summary>
167173
/// Forwards the call to <see cref="SentrySdk"/>.
168174
/// </summary>
169175
[DebuggerStepThrough]
170176
public SentryId CaptureEvent(SentryEvent evt)
171177
=> SentrySdk.CaptureEvent(evt);
172178

173-
/// <summary>
174-
/// Forwards the call to <see cref="SentrySdk"/>
175-
/// </summary>
176-
SentryId IHubEx.CaptureEventInternal(SentryEvent evt, Scope? scope)
177-
=> SentrySdk.CaptureEventInternal(evt, scope);
178-
179179
/// <summary>
180180
/// Forwards the call to <see cref="SentrySdk"/>.
181181
/// </summary>
182182
[DebuggerStepThrough]
183-
public SentryId CaptureException(Exception exception)
184-
=> SentrySdk.CaptureException(exception);
183+
[EditorBrowsable(EditorBrowsableState.Never)]
184+
public SentryId CaptureEvent(SentryEvent evt, Scope? scope)
185+
=> SentrySdk.CaptureEvent(evt, scope);
185186

186187
/// <summary>
187188
/// Forwards the call to <see cref="SentrySdk"/>.
188189
/// </summary>
189190
[DebuggerStepThrough]
190191
[EditorBrowsable(EditorBrowsableState.Never)]
191-
public SentryId CaptureEvent(SentryEvent evt, Scope? scope)
192-
=> SentrySdk.CaptureEvent(evt, scope);
192+
public SentryId CaptureEvent(SentryEvent evt, Hint? hint, Scope? scope)
193+
=> SentrySdk.CaptureEvent(evt, hint, scope);
193194

194195
/// <summary>
195196
/// Forwards the call to <see cref="SentrySdk"/>.
@@ -199,6 +200,13 @@ public SentryId CaptureEvent(SentryEvent evt, Scope? scope)
199200
public SentryId CaptureEvent(SentryEvent evt, Action<Scope> configureScope)
200201
=> SentrySdk.CaptureEvent(evt, configureScope);
201202

203+
/// <summary>
204+
/// Forwards the call to <see cref="SentrySdk"/>.
205+
/// </summary>
206+
[DebuggerStepThrough]
207+
public SentryId CaptureException(Exception exception)
208+
=> SentrySdk.CaptureException(exception);
209+
202210
/// <summary>
203211
/// Forwards the call to <see cref="SentrySdk"/>.
204212
/// </summary>
@@ -207,6 +215,14 @@ public SentryId CaptureEvent(SentryEvent evt, Action<Scope> configureScope)
207215
public void CaptureTransaction(Transaction transaction)
208216
=> SentrySdk.CaptureTransaction(transaction);
209217

218+
/// <summary>
219+
/// Forwards the call to <see cref="SentrySdk"/>.
220+
/// </summary>
221+
[DebuggerStepThrough]
222+
[EditorBrowsable(EditorBrowsableState.Never)]
223+
public void CaptureTransaction(Transaction transaction, Hint? hint)
224+
=> SentrySdk.CaptureTransaction(transaction, hint);
225+
210226
/// <summary>
211227
/// Forwards the call to <see cref="SentrySdk"/>.
212228
/// </summary>

src/Sentry/Extensibility/ISentryEventProcessor.cs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,14 @@ public interface ISentryEventProcessor
1616
/// Meaning the event should no longer be processed nor send.
1717
/// </remarks>
1818
SentryEvent? Process(SentryEvent @event);
19-
}
19+
}
20+
21+
internal static class ISentryEventProcessorExtensions
22+
{
23+
internal static SentryEvent? DoProcessEvent(this ISentryEventProcessor processor, SentryEvent @event, Hint hint)
24+
{
25+
return (processor is ISentryEventProcessorWithHint contextualProcessor)
26+
? contextualProcessor.Process(@event, hint)
27+
: processor.Process(@event);
28+
}
29+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
namespace Sentry.Extensibility;
2+
3+
/// <summary>
4+
/// Process a SentryEvent during the prepare phase.
5+
/// </summary>
6+
public interface ISentryEventProcessorWithHint: ISentryEventProcessor
7+
{
8+
/// <summary>
9+
/// Process the <see cref="SentryEvent"/>
10+
/// </summary>
11+
/// <param name="event">The event to process</param>
12+
/// <param name="hint">A <see cref="Hint"/> with context that may be useful prior to sending the event</param>
13+
/// <return>The processed event or <c>null</c> if the event was dropped.</return>
14+
/// <remarks>
15+
/// The event returned can be the same instance received or a new one.
16+
/// Returning null will stop the processing pipeline so that the event will neither be processed by
17+
/// additional event processors or sent to Sentry.
18+
/// </remarks>
19+
SentryEvent? Process(SentryEvent @event, Hint hint);
20+
}
21+

src/Sentry/Extensibility/ISentryTransactionProcessor.cs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
namespace Sentry.Extensibility;
1+
namespace Sentry.Extensibility;
22

33
/// <summary>
44
/// Process a <see cref="Transaction"/> during the prepare phase.
@@ -16,3 +16,13 @@ public interface ISentryTransactionProcessor
1616
/// </remarks>
1717
Transaction? Process(Transaction transaction);
1818
}
19+
20+
internal static class ISentryTransactionProcessorExtensions
21+
{
22+
internal static Transaction? DoProcessTransaction(this ISentryTransactionProcessor processor, Transaction transaction, Hint hint)
23+
{
24+
return (processor is ISentryTransactionProcessorWithHint contextualProcessor)
25+
? contextualProcessor.Process(transaction, hint)
26+
: processor.Process(transaction);
27+
}
28+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
namespace Sentry.Extensibility;
2+
3+
/// <summary>
4+
/// Process a <see cref="Transaction"/> during the prepare phase.
5+
/// </summary>
6+
public interface ISentryTransactionProcessorWithHint: ISentryTransactionProcessor
7+
{
8+
/// <summary>
9+
/// Process the <see cref="Transaction"/>
10+
/// </summary>
11+
/// <param name="transaction">The Transaction to process</param>
12+
/// <param name="hint">A <see cref="Hint"/> with context that may be useful prior to sending the transaction</param>
13+
/// <remarks>
14+
/// The transaction returned can be the same instance received or a new one.
15+
/// Returning null will stop the processing pipeline.
16+
/// Meaning the transaction should no longer be processed nor send.
17+
/// </remarks>
18+
Transaction? Process(Transaction transaction, Hint hint);
19+
}

src/Sentry/Hint.cs

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
namespace Sentry;
2+
3+
/// <summary>
4+
/// A hint that can be provided when capturing a <see cref="SentryEvent"/> or when adding a <see cref="Breadcrumb"/>.
5+
/// Hints can be used to filter or modify events, transactions, or breadcrumbs before they are sent to Sentry.
6+
/// </summary>
7+
public class Hint
8+
{
9+
private readonly List<Attachment> _attachments = new();
10+
private readonly Dictionary<string, object?> _items = new();
11+
12+
/// <summary>
13+
/// Creates a new instance of <see cref="Hint"/>.
14+
/// </summary>
15+
public Hint()
16+
{
17+
}
18+
19+
/// <summary>
20+
/// Creates a new hint containing a single item.
21+
/// </summary>
22+
/// <param name="key">The key of the hint item.</param>
23+
/// <param name="value">The value of the hint item.</param>
24+
public Hint(string key, object? value)
25+
: this()
26+
{
27+
_items[key] = value;
28+
}
29+
30+
/// <summary>
31+
/// The Java SDK has some logic so that certain Hint types do not copy attachments from the Scope.
32+
/// This provides a location that allows us to do the same in the .NET SDK in the future.
33+
/// </summary>
34+
/// <param name="scope">The <see cref="Scope"/> that the attachments should be copied from</param>
35+
internal void AddAttachmentsFromScope(Scope scope) => _attachments.AddRange(scope.Attachments);
36+
37+
/// <summary>
38+
/// Attachments added to the Hint.
39+
/// </summary>
40+
/// <remarks>
41+
/// This collection represents all of the attachments that will be sent to Sentry with the corresponding event.
42+
/// You can add or remove attachments from this collection as needed.
43+
/// </remarks>
44+
public ICollection<Attachment> Attachments => _attachments;
45+
46+
/// <summary>
47+
/// A dictionary of arbitrary items provided with the Hint.
48+
/// </summary>
49+
/// <remarks>
50+
/// These are not sent to Sentry, but rather they are available during processing, such as when using
51+
/// BeforeSend and others.
52+
/// </remarks>
53+
public IDictionary<string, object?> Items => _items;
54+
55+
/// <summary>
56+
/// Creates a new Hint with one or more attachments.
57+
/// </summary>
58+
/// <param name="attachments">The attachment(s) to add.</param>
59+
/// <returns>A Hint having the attachment(s).</returns>
60+
public static Hint WithAttachments(params Attachment[] attachments) => WithAttachments(attachments.AsEnumerable());
61+
62+
/// <summary>
63+
/// Creates a new Hint with attachments.
64+
/// </summary>
65+
/// <param name="attachments">The attachments to add.</param>
66+
/// <returns>A Hint having the attachments.</returns>
67+
public static Hint WithAttachments(IEnumerable<Attachment> attachments)
68+
{
69+
var hint = new Hint();
70+
hint._attachments.AddRange(attachments);
71+
return hint;
72+
}
73+
}

0 commit comments

Comments
 (0)