Skip to content
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
556ba4c
Added SentryOptions.SetBeforeSend
jamescrosswell May 2, 2023
a713108
Added tests for new CaptureHint overloads taking a Hint parameter
jamescrosswell May 2, 2023
36699e8
Failed requests add a Hint for the HttpResponseMessage
jamescrosswell May 2, 2023
baed55b
Added BeforeBreadcrumb Hint support (for breadcrumbs on the scope only)
jamescrosswell May 3, 2023
836ac09
- Fixed ScopeExtensionTests
jamescrosswell May 4, 2023
7b4efa1
Added stub of Android platform code to enable builds to complete
jamescrosswell May 4, 2023
52e5a33
Sentry.Samples.Console.Customized now demonstrates using hints with b…
jamescrosswell May 4, 2023
baee2ca
Added missing XML docs on Hint constructors
jamescrosswell May 4, 2023
d3dae05
Updated MiddlewareLoggerIntegration tests to account for modified imp…
jamescrosswell May 4, 2023
fb6e9cf
Updated verified tests for CaptureTransaction_BeforeSendTransactionTh…
jamescrosswell May 4, 2023
d211548
Tail chasing Verify test errors
jamescrosswell May 4, 2023
6c9be76
Merge branch 'main' into feat/hint-before-send
mattjohnsonpint May 6, 2023
c1c2777
Update CHANGELOG.md
mattjohnsonpint May 6, 2023
eded3b6
Fix iOS compilation issue
mattjohnsonpint May 6, 2023
6d78761
Moved hint data from base Hint class to Items property, for clarity
jamescrosswell May 7, 2023
9142abf
Added XML docs for Hint.Items property
jamescrosswell May 7, 2023
8868e47
Updated Customized console sample to use new Hint
jamescrosswell May 7, 2023
0f701eb
- Added Hints to BeforeSendTransaction
jamescrosswell May 8, 2023
25b61f4
Attachments from the Scope get included in Hints before adding Bookma…
jamescrosswell May 9, 2023
aa6d91e
Added hint support for Transaction/Event processors
jamescrosswell May 9, 2023
aa3750f
Merge remote-tracking branch 'getsentry/main' into feat/hint-before-send
jamescrosswell May 9, 2023
860af91
- Renamed Contextual processors to ProcessorWithHint (more specific)
jamescrosswell May 9, 2023
f27a650
Merge remote-tracking branch 'origin/main' into feat/hint-before-send
jamescrosswell May 10, 2023
afb18bd
Merge branch 'main' into feat/hint-before-send
mattjohnsonpint May 15, 2023
d02f33a
Add overloads without hints
mattjohnsonpint May 15, 2023
e382114
Cleanup Hint. Just expose Attachments, not AddAttachments.
mattjohnsonpint May 15, 2023
c3fdad4
Update API snapshots
mattjohnsonpint May 15, 2023
13c70d6
Ensure hint modifications to attachments are sent
mattjohnsonpint May 15, 2023
af95623
Update CHANGELOG.md
mattjohnsonpint May 15, 2023
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
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# Changelog

## Unreleased

### Features

- Add `Hint` support ([#2351](https://github.com/getsentry/sentry-dotnet/pull/2351))

## 3.31.0

### Features
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.ContainsKey(replaceBreadcrumb))
{
return new Breadcrumb(hint.GetValue<string>(replaceBreadcrumb), 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
5 changes: 5 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 Down
28 changes: 18 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 Down
143 changes: 143 additions & 0 deletions src/Sentry/Hint.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
using System;
using System.Collections.Generic;
using System.Net.Mail;
using Sentry.Internal.Extensions;

namespace Sentry;

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

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

/// <summary>
/// Creates a new hint with a single key/value pair.
/// </summary>
/// <param name="key"></param>
/// <param name="value"></param>
public Hint(string key, object? value)
: this()
{
_internalStorage[key] = value;
}

/// <summary>
/// Gets or sets additional values to be provided with the hint
/// </summary>
/// <param name="key">The key</param>
/// <returns>The value with the specified key or null if none exist.</returns>
public object? this[string key]
{
get => _internalStorage.GetValueOrDefault(key);
set => _internalStorage[key] = value;
}

internal void AddAttachmentsInternal(IEnumerable<Attachment> attachments)
{
if (attachments is not null)
{
_attachments.AddRange(attachments);
}
}

/// <summary>
/// Adds one or more attachments to the Hint.
/// </summary>
/// <param name="attachments"></param>
public void AddAttachments(params Attachment[] attachments) => AddAttachmentsInternal(attachments);

/// <summary>
/// Adds multiple attachments to the Hint.
/// </summary>
/// <param name="attachments"></param>
public void AddAttachments(IEnumerable<Attachment> attachments) => AddAttachmentsInternal(attachments);

/// <summary>
/// Attachments added to the Hint.
/// </summary>
public ICollection<Attachment> Attachments => _attachments;

/// <summary>
/// Clears any values stored in <see cref="this[string]"/>
/// </summary>
public void Clear() => _internalStorage.Clear();

/// <summary>
/// Checks if the specified key exists
/// </summary>
/// <param name="key">The key</param>
/// <returns>True if the key exists. False otherwise.</returns>
public bool ContainsKey(string key) => _internalStorage.ContainsKey(key);

/// <inheritdoc />
public void CopyTo(Array array, int index) => ((ICollection)_internalStorage).CopyTo(array, index);

/// <inheritdoc />
public int Count => _internalStorage.Count;

IEnumerator IEnumerable.GetEnumerator() => _internalStorage.GetEnumerator();

/// <inheritdoc />
public IEnumerator<KeyValuePair<string, object?>> GetEnumerator()
=> ((IEnumerable<KeyValuePair<string, object?>>)_internalStorage).GetEnumerator();

/// <summary>
/// Gets the value with the specified key as type <typeparamref name="T"/>
/// </summary>
/// <typeparam name="T">They expected value type</typeparam>
/// <param name="key">The key</param>
/// <returns>A value of type <typeparamref name="T"/> if one exists with the specified key or null otherwise.</returns>
public T? GetValue<T>(string key) where T : class? => (this[key] is T typedHintValue) ? typedHintValue : null;

/// <inheritdoc />
public bool IsSynchronized => ((ICollection)_internalStorage).IsSynchronized;

/// <summary>
/// Remves the value with the specified key
/// </summary>
/// <param name="key"></param>
public void Remove(string key) => _internalStorage.Remove(key);

/// <summary>
/// Gets or sets a Screenshot for the Hint
/// </summary>
public Attachment? Screenshot { get; set; }

/// <inheritdoc />
public object SyncRoot => ((ICollection)_internalStorage).SyncRoot;

/// <summary>
/// Gets or sets a ViewHierarchy for the Hint
/// </summary>
public Attachment? ViewHierarchy { get; set; }

/// <summary>
/// Creates a new Hint with one or more attachments.
/// </summary>
/// <param name="attachment"></param>
/// <returns></returns>
public static Hint WithAttachments(params Attachment[] attachment) => Hint.WithAttachments(attachment.ToList());

/// <summary>
/// Creates a new Hint with attachments.
/// </summary>
/// <param name="attachments"></param>
/// <returns></returns>
public static Hint WithAttachments(ICollection<Attachment> attachments)
{
var hint = new Hint();
hint.AddAttachments(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";
}
43 changes: 35 additions & 8 deletions src/Sentry/HubExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -111,14 +111,41 @@ public static void AddBreadcrumb(
return;
}

var breadcrumb = new Breadcrumb(
(clock ?? SystemClock.Clock).GetUtcNow(),
message,
type,
data != null ? new Dictionary<string, string>(data) : null,
category,
level
);

hub.AddBreadcrumb(
breadcrumb
);
}

/// <summary>
/// Adds a breadcrumb to the current scope.
/// </summary>
/// <param name="hub">The Hub which holds the scope stack.</param>
/// <param name="breadcrumb">The breadcrumb to add</param>
/// <param name="hint">An hint provided with the breadcrumb in the BeforeBreadcrumb callback</param>
public static void AddBreadcrumb(
this IHub hub,
Breadcrumb breadcrumb,
Hint? hint = null
)
{
// Not to throw on code that ignores nullability warnings.
if (hub.IsNull())
{
return;
}

hub.ConfigureScope(
s => s.AddBreadcrumb(
(clock ?? SystemClock.Clock).GetUtcNow(),
message,
category,
type,
data != null ? new Dictionary<string, string>(data) : null,
level));
s => s.AddBreadcrumb(breadcrumb, hint ?? new Hint())
);
}

/// <summary>
Expand Down Expand Up @@ -159,7 +186,7 @@ internal static SentryId CaptureExceptionInternal(this IHub hub, Exception ex) =
hub.CaptureEventInternal(new SentryEvent(ex));

internal static SentryId CaptureEventInternal(this IHub hub, SentryEvent evt) =>
hub is IHubEx hubEx ? hubEx.CaptureEventInternal(evt) : hub.CaptureEvent(evt);
hub is IHubEx hubEx ? hubEx.CaptureEventInternal(evt, null, null) : hub.CaptureEvent(evt);

/// <summary>
/// Captures the exception with a configurable scope callback.
Expand Down
Loading