Skip to content

Commit

Permalink
Add Hint support (#2351)
Browse files Browse the repository at this point in the history
  • Loading branch information
jamescrosswell authored May 16, 2023
1 parent 4e947d8 commit 370acc1
Show file tree
Hide file tree
Showing 60 changed files with 2,393 additions and 201 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@

### Features

- Add `Hint` support ([#2351](https://github.com/getsentry/sentry-dotnet/pull/2351))
- Currently, this allows you to manipulate attachments in the various "before" event delegates.
- Hints can also be used in event and transaction processors by implementing `ISentryEventProcessorWithHint` or `ISentryTransactionProcessorWithHint`, instead of `ISentryEventProcessor` or `ISentryTransactionProcessor`.
- 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.

- Allow setting the active span on the scope ([#2364](https://github.com/getsentry/sentry-dotnet/pull/2364))
- Note: Obsoletes the `Scope.GetSpan` method in favor of a `Scope.Span` property (which now has a setter as well).

Expand Down
33 changes: 23 additions & 10 deletions samples/Sentry.Samples.Console.Customized/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,28 +42,36 @@ await SentrySdk.ConfigureScopeAsync(async scope =>
// o.SampleRate = 0.5f; // Randomly drop (don't send to Sentry) half of events

// Modifications to event before it goes out. Could replace the event altogether
o.BeforeSend = @event =>
{
// Drop an event altogether:
if (@event.Tags.ContainsKey("SomeTag"))
o.SetBeforeSend((@event, _) =>
{
return null;
}
// Drop an event altogether:
if (@event.Tags.ContainsKey("SomeTag"))
{
return null;
}

return @event;
};
return @event;
}
);

// Allows inspecting and modifying, returning a new or simply rejecting (returning null)
o.BeforeBreadcrumb = crumb =>
o.SetBeforeBreadcrumb((crumb, hint) =>
{
// Don't add breadcrumbs with message containing:
if (crumb.Message?.Contains("bad breadcrumb") == true)
{
return null;
}

// Replace breadcrumbs entirely incase of a drastic hint
const string replaceBreadcrumb = "don't trust this breadcrumb";
if (hint.Items.TryGetValue(replaceBreadcrumb, out var replacementMessage))
{
return new Breadcrumb((string)replacementMessage, null, null, null, BreadcrumbLevel.Critical);
}

return crumb;
};
});

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

SentrySdk.AddBreadcrumb(
new Breadcrumb("A breadcrumb that will be replaced by the 'BeforeBreadcrumb callback because of the hint", null),
new Hint("don't trust this breadcrumb", "trust this instead")
);

// Data added to the root scope (no PushScope called up to this point)
// The modifications done here will affect all events sent and will propagate to child scopes.
await SentrySdk.ConfigureScopeAsync(async scope =>
Expand Down
21 changes: 11 additions & 10 deletions samples/Sentry.Samples.Console.Profiling/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,19 +43,20 @@ await SentrySdk.ConfigureScopeAsync(async scope =>
// o.SampleRate = 0.5f; // Randomly drop (don't send to Sentry) half of events

// Modifications to event before it goes out. Could replace the event altogether
o.BeforeSend = @event =>
{
// Drop an event altogether:
if (@event.Tags.ContainsKey("SomeTag"))
o.SetBeforeSend((@event, _) =>
{
return null;
}
// Drop an event altogether:
if (@event.Tags.ContainsKey("SomeTag"))
{
return null;
}

return @event;
};
return @event;
}
);

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

return crumb;
};
});

// Ignore exception by its type:
o.AddExceptionFilterForType<XsltCompileException>();
Expand Down
12 changes: 12 additions & 0 deletions src/Sentry/Extensibility/DisabledHub.cs
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,11 @@ public void BindClient(ISentryClient client)
/// </summary>
public SentryId CaptureEvent(SentryEvent evt, Scope? scope = null) => SentryId.Empty;

/// <summary>
/// No-Op.
/// </summary>
public SentryId CaptureEvent(SentryEvent evt, Hint? hint, Scope? scope = null) => SentryId.Empty;

/// <summary>
/// No-Op.
/// </summary>
Expand All @@ -126,6 +131,13 @@ public void CaptureTransaction(Transaction transaction)
{
}

/// <summary>
/// No-Op.
/// </summary>
public void CaptureTransaction(Transaction transaction, Hint? hint)
{
}

/// <summary>
/// No-Op.
/// </summary>
Expand Down
36 changes: 26 additions & 10 deletions src/Sentry/Extensibility/HubAdapter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -163,33 +163,34 @@ public void AddBreadcrumb(
data,
level);

/// <summary>
/// Forwards the call to <see cref="SentrySdk"/>
/// </summary>
SentryId IHubEx.CaptureEventInternal(SentryEvent evt, Hint? hint, Scope? scope)
=> SentrySdk.CaptureEventInternal(evt, hint, scope);

/// <summary>
/// Forwards the call to <see cref="SentrySdk"/>.
/// </summary>
[DebuggerStepThrough]
public SentryId CaptureEvent(SentryEvent evt)
=> SentrySdk.CaptureEvent(evt);

/// <summary>
/// Forwards the call to <see cref="SentrySdk"/>
/// </summary>
SentryId IHubEx.CaptureEventInternal(SentryEvent evt, Scope? scope)
=> SentrySdk.CaptureEventInternal(evt, scope);

/// <summary>
/// Forwards the call to <see cref="SentrySdk"/>.
/// </summary>
[DebuggerStepThrough]
public SentryId CaptureException(Exception exception)
=> SentrySdk.CaptureException(exception);
[EditorBrowsable(EditorBrowsableState.Never)]
public SentryId CaptureEvent(SentryEvent evt, Scope? scope)
=> SentrySdk.CaptureEvent(evt, scope);

/// <summary>
/// Forwards the call to <see cref="SentrySdk"/>.
/// </summary>
[DebuggerStepThrough]
[EditorBrowsable(EditorBrowsableState.Never)]
public SentryId CaptureEvent(SentryEvent evt, Scope? scope)
=> SentrySdk.CaptureEvent(evt, scope);
public SentryId CaptureEvent(SentryEvent evt, Hint? hint, Scope? scope)
=> SentrySdk.CaptureEvent(evt, hint, scope);

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

/// <summary>
/// Forwards the call to <see cref="SentrySdk"/>.
/// </summary>
[DebuggerStepThrough]
public SentryId CaptureException(Exception exception)
=> SentrySdk.CaptureException(exception);

/// <summary>
/// Forwards the call to <see cref="SentrySdk"/>.
/// </summary>
Expand All @@ -207,6 +215,14 @@ public SentryId CaptureEvent(SentryEvent evt, Action<Scope> configureScope)
public void CaptureTransaction(Transaction transaction)
=> SentrySdk.CaptureTransaction(transaction);

/// <summary>
/// Forwards the call to <see cref="SentrySdk"/>.
/// </summary>
[DebuggerStepThrough]
[EditorBrowsable(EditorBrowsableState.Never)]
public void CaptureTransaction(Transaction transaction, Hint? hint)
=> SentrySdk.CaptureTransaction(transaction, hint);

/// <summary>
/// Forwards the call to <see cref="SentrySdk"/>.
/// </summary>
Expand Down
12 changes: 11 additions & 1 deletion src/Sentry/Extensibility/ISentryEventProcessor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,14 @@ public interface ISentryEventProcessor
/// Meaning the event should no longer be processed nor send.
/// </remarks>
SentryEvent? Process(SentryEvent @event);
}
}

internal static class ISentryEventProcessorExtensions
{
internal static SentryEvent? DoProcessEvent(this ISentryEventProcessor processor, SentryEvent @event, Hint hint)
{
return (processor is ISentryEventProcessorWithHint contextualProcessor)
? contextualProcessor.Process(@event, hint)
: processor.Process(@event);
}
}
21 changes: 21 additions & 0 deletions src/Sentry/Extensibility/ISentryEventProcessorWithHint.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
namespace Sentry.Extensibility;

/// <summary>
/// Process a SentryEvent during the prepare phase.
/// </summary>
public interface ISentryEventProcessorWithHint: ISentryEventProcessor
{
/// <summary>
/// Process the <see cref="SentryEvent"/>
/// </summary>
/// <param name="event">The event to process</param>
/// <param name="hint">A <see cref="Hint"/> with context that may be useful prior to sending the event</param>
/// <return>The processed event or <c>null</c> if the event was dropped.</return>
/// <remarks>
/// The event returned can be the same instance received or a new one.
/// Returning null will stop the processing pipeline so that the event will neither be processed by
/// additional event processors or sent to Sentry.
/// </remarks>
SentryEvent? Process(SentryEvent @event, Hint hint);
}

12 changes: 11 additions & 1 deletion src/Sentry/Extensibility/ISentryTransactionProcessor.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
namespace Sentry.Extensibility;
namespace Sentry.Extensibility;

/// <summary>
/// Process a <see cref="Transaction"/> during the prepare phase.
Expand All @@ -16,3 +16,13 @@ public interface ISentryTransactionProcessor
/// </remarks>
Transaction? Process(Transaction transaction);
}

internal static class ISentryTransactionProcessorExtensions
{
internal static Transaction? DoProcessTransaction(this ISentryTransactionProcessor processor, Transaction transaction, Hint hint)
{
return (processor is ISentryTransactionProcessorWithHint contextualProcessor)
? contextualProcessor.Process(transaction, hint)
: processor.Process(transaction);
}
}
19 changes: 19 additions & 0 deletions src/Sentry/Extensibility/ISentryTransactionProcessorWithHint.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
namespace Sentry.Extensibility;

/// <summary>
/// Process a <see cref="Transaction"/> during the prepare phase.
/// </summary>
public interface ISentryTransactionProcessorWithHint: ISentryTransactionProcessor
{
/// <summary>
/// Process the <see cref="Transaction"/>
/// </summary>
/// <param name="transaction">The Transaction to process</param>
/// <param name="hint">A <see cref="Hint"/> with context that may be useful prior to sending the transaction</param>
/// <remarks>
/// The transaction returned can be the same instance received or a new one.
/// Returning null will stop the processing pipeline.
/// Meaning the transaction should no longer be processed nor send.
/// </remarks>
Transaction? Process(Transaction transaction, Hint hint);
}
73 changes: 73 additions & 0 deletions src/Sentry/Hint.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
namespace Sentry;

/// <summary>
/// A hint that can be provided when capturing a <see cref="SentryEvent"/> or when adding a <see cref="Breadcrumb"/>.
/// Hints can be used to filter or modify events, transactions, or breadcrumbs before they are sent to Sentry.
/// </summary>
public class Hint
{
private readonly List<Attachment> _attachments = new();
private readonly Dictionary<string, object?> _items = new();

/// <summary>
/// Creates a new instance of <see cref="Hint"/>.
/// </summary>
public Hint()
{
}

/// <summary>
/// Creates a new hint containing a single item.
/// </summary>
/// <param name="key">The key of the hint item.</param>
/// <param name="value">The value of the hint item.</param>
public Hint(string key, object? value)
: this()
{
_items[key] = value;
}

/// <summary>
/// The Java SDK has some logic so that certain Hint types do not copy attachments from the Scope.
/// This provides a location that allows us to do the same in the .NET SDK in the future.
/// </summary>
/// <param name="scope">The <see cref="Scope"/> that the attachments should be copied from</param>
internal void AddAttachmentsFromScope(Scope scope) => _attachments.AddRange(scope.Attachments);

/// <summary>
/// Attachments added to the Hint.
/// </summary>
/// <remarks>
/// This collection represents all of the attachments that will be sent to Sentry with the corresponding event.
/// You can add or remove attachments from this collection as needed.
/// </remarks>
public ICollection<Attachment> Attachments => _attachments;

/// <summary>
/// A dictionary of arbitrary items provided with the Hint.
/// </summary>
/// <remarks>
/// These are not sent to Sentry, but rather they are available during processing, such as when using
/// BeforeSend and others.
/// </remarks>
public IDictionary<string, object?> Items => _items;

/// <summary>
/// Creates a new Hint with one or more attachments.
/// </summary>
/// <param name="attachments">The attachment(s) to add.</param>
/// <returns>A Hint having the attachment(s).</returns>
public static Hint WithAttachments(params Attachment[] attachments) => WithAttachments(attachments.AsEnumerable());

/// <summary>
/// Creates a new Hint with attachments.
/// </summary>
/// <param name="attachments">The attachments to add.</param>
/// <returns>A Hint having the attachments.</returns>
public static Hint WithAttachments(IEnumerable<Attachment> attachments)
{
var hint = new Hint();
hint._attachments.AddRange(attachments);
return hint;
}
}
12 changes: 12 additions & 0 deletions src/Sentry/HintTypes.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
namespace Sentry;

/// <summary>
/// Constants used to name Hints generated by the Sentry SDK
/// </summary>
public static class HintTypes
{
/// <summary>
/// Used for HttpResponseMessage hints
/// </summary>
public const string HttpResponseMessage = "http-response-message";
}
Loading

0 comments on commit 370acc1

Please sign in to comment.