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

fix: tests reaching the web #1161

Merged
merged 8 commits into from
Aug 25, 2021
Merged
Show file tree
Hide file tree
Changes from 3 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
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@

## Unreleased

### Features

- EF Core performance monitoring ([#1112](https://github.com/getsentry/sentry-dotnet/pull/1112))
bruno-garcia marked this conversation as resolved.
Show resolved Hide resolved
- Improved SDK diagnostic logs ([#1161](https://github.com/getsentry/sentry-dotnet/pull/1161))

### Fixes

- Fix end session from Hub adapter not being passed to SentrySDK ([#1158](https://github.com/getsentry/sentry-dotnet/pull/1158))
Expand Down
3 changes: 1 addition & 2 deletions src/Sentry.AspNet/SentryAspNetOptionsExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,7 @@ public static void AddAspNet(this SentryOptions options, RequestSize maxRequestB
var payloadExtractor = new RequestBodyExtractionDispatcher(
new IRequestPayloadExtractor[] {new FormRequestPayloadExtractor(), new DefaultRequestPayloadExtractor()},
options,
() => maxRequestBodySize
);
() => maxRequestBodySize);

var eventProcessor = new SystemWebRequestEventProcessor(payloadExtractor, options);

Expand Down
3 changes: 1 addition & 2 deletions src/Sentry/Breadcrumb.cs
Original file line number Diff line number Diff line change
Expand Up @@ -120,8 +120,7 @@ public void WriteTo(Utf8JsonWriter writer)

writer.WriteString(
"timestamp",
Timestamp.ToString("yyyy-MM-ddTHH\\:mm\\:ss.fffZ", DateTimeFormatInfo.InvariantInfo)
);
Timestamp.ToString("yyyy-MM-ddTHH\\:mm\\:ss.fffZ", DateTimeFormatInfo.InvariantInfo));

writer.WriteStringIfNotWhiteSpace("message", Message);
writer.WriteStringIfNotWhiteSpace("type", Type);
Expand Down
3 changes: 1 addition & 2 deletions src/Sentry/Dsn.cs
Original file line number Diff line number Diff line change
Expand Up @@ -109,8 +109,7 @@ public static Dsn Parse(string dsn)
path,
secretKey,
publicKey,
apiBaseUri
);
apiBaseUri);
}

public static Dsn? TryParse(string? dsn)
Expand Down
25 changes: 7 additions & 18 deletions src/Sentry/GlobalSessionManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -214,25 +214,18 @@ private void PersistSession(SessionUpdate update, DateTimeOffset? pauseTimestamp

_options.DiagnosticLogger?.LogDebug(
"Created persistence directory for session file '{0}'.",
_persistenceDirectoryPath
);
_persistenceDirectoryPath);

var filePath = Path.Combine(_persistenceDirectoryPath, PersistedSessionFileName);

var persistedSessionUpdate = new PersistedSessionUpdate(update, pauseTimestamp);
persistedSessionUpdate.WriteToFile(filePath);

_options.DiagnosticLogger?.LogInfo(
"Persisted session to a file '{0}'.",
filePath
);
_options.DiagnosticLogger?.LogDebug("Persisted session to a file '{0}'.", filePath);
}
catch (Exception ex)
{
_options.DiagnosticLogger?.LogError(
"Failed to persist session on the file system.",
ex
);
_options.DiagnosticLogger?.LogError("Failed to persist session on the file system.", ex);
}
}

Expand Down Expand Up @@ -376,8 +369,7 @@ private void DeletePersistedSession()

_options.DiagnosticLogger?.LogInfo(
"Started new session (SID: {0}; DID: {1}).",
session.Id, session.DistinctId
);
session.Id, session.DistinctId);

var update = session.CreateUpdate(true, _clock.GetUtcNow());

Expand All @@ -390,8 +382,7 @@ private SessionUpdate EndSession(Session session, DateTimeOffset timestamp, Sess
{
_options.DiagnosticLogger?.LogInfo(
"Ended session (SID: {0}; DID: {1}) with status '{2}'.",
session.Id, session.DistinctId, status
);
session.Id, session.DistinctId, status);

var update = session.CreateUpdate(false, timestamp, status);

Expand Down Expand Up @@ -472,8 +463,7 @@ public IReadOnlyList<SessionUpdate> ResumeSession()

_options.DiagnosticLogger?.LogDebug(
"Paused session has been paused for {0}, which is shorter than the configured timeout.",
pauseDuration
);
pauseDuration);

return Array.Empty<SessionUpdate>();
}
Expand All @@ -499,8 +489,7 @@ public IReadOnlyList<SessionUpdate> ResumeSession()
}

_options.DiagnosticLogger?.LogDebug(
"Failed to report an error on a session because there is none active."
);
"Failed to report an error on a session because there is none active.");

return null;
}
Expand Down
3 changes: 1 addition & 2 deletions src/Sentry/Internal/BackgroundWorker.cs
Original file line number Diff line number Diff line change
Expand Up @@ -201,8 +201,7 @@ public async Task FlushAsync(TimeSpan timeout)
var timeoutWithShutdown = CancellationTokenSource.CreateLinkedTokenSource(
timeoutSource.Token,
_shutdownSource.Token,
flushSuccessSource.Token
);
flushSuccessSource.Token);

var counter = 0;
var depth = int.MaxValue;
Expand Down
20 changes: 15 additions & 5 deletions src/Sentry/Internal/DuplicateEventDetectionEventProcessor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,21 +25,24 @@ internal class DuplicateEventDetectionEventProcessor : ISentryEventProcessor
}

if (@event.Exception == null
|| !IsDuplicate(@event.Exception))
|| !IsDuplicate(@event.Exception, @event.EventId, true))
{
return @event;
}

_options.DiagnosticLogger?.LogDebug("Duplicate Exception detected. Event {0} will be discarded.", @event.EventId);
return null;
}

private bool IsDuplicate(Exception ex)
private bool IsDuplicate(Exception ex, SentryId eventId, bool debugLog)
{
if (_options.DeduplicateMode.HasFlag(DeduplicateMode.SameExceptionInstance))
{
if (_capturedObjects.TryGetValue(ex, out _))
{
if (debugLog)
{
_options.DiagnosticLogger?.LogDebug("Duplicate Exception: 'SameExceptionInstance'. Event {0} will be discarded.", eventId);
}
return true;
}

Expand All @@ -49,14 +52,21 @@ private bool IsDuplicate(Exception ex)
if (_options.DeduplicateMode.HasFlag(DeduplicateMode.AggregateException)
&& ex is AggregateException aex)
{
return aex.InnerExceptions.Any(IsDuplicate);
var result = aex.InnerExceptions.Any(e => IsDuplicate(e, eventId, false));
if (result)
{
_options.DiagnosticLogger?.LogDebug("Duplicate Exception: 'AggregateException'. Event {0} will be discarded.", eventId);
}

return result;
}

if (_options.DeduplicateMode.HasFlag(DeduplicateMode.InnerException)
&& ex.InnerException != null)
{
if (IsDuplicate(ex.InnerException))
if (IsDuplicate(ex.InnerException, eventId, false))
{
_options.DiagnosticLogger?.LogDebug("Duplicate Exception: 'SameExceptionInstance'. Event {0} will be discarded.", eventId);
return true;
}
}
Expand Down
47 changes: 20 additions & 27 deletions src/Sentry/Internal/Http/CachingTransport.cs
Original file line number Diff line number Diff line change
Expand Up @@ -67,10 +67,11 @@ private async Task CachedTransportBackgroundTask()
{
foreach (var filePath in Directory.EnumerateFiles(_processingDirectoryPath))
{
File.Move(
filePath,
Path.Combine(_isolatedCacheDirectoryPath, Path.GetFileName(filePath))
);
var destinationPath = Path.Combine(_isolatedCacheDirectoryPath, Path.GetFileName(filePath));
_options.DiagnosticLogger?.LogDebug("Moving unprocessed file back to cache: {0} to {1}.",
filePath, destinationPath);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit-pick but I believe the below one will be slightly more clear to the end-user.

Suggested change
filePath, destinationPath);
_options.DiagnosticLogger?.LogDebug("Moving unprocessed file back from cache {0} to {1}.",
filePath, destinationPath);

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You suggestion adds another call to the logger?
Code here is moving for "processing" back to the "cache" folder. So I think the original message makes more sense


File.Move(filePath, destinationPath);
}
}

Expand All @@ -79,6 +80,7 @@ private async Task CachedTransportBackgroundTask()
try
{
await _workerSignal.WaitAsync(_workerCts.Token).ConfigureAwait(false);
_options.DiagnosticLogger?.LogDebug("Worker signal triggered: flushing cached envelopes.");
await ProcessCacheAsync(_workerCts.Token).ConfigureAwait(false);
}
catch (OperationCanceledException)
Expand All @@ -87,10 +89,7 @@ private async Task CachedTransportBackgroundTask()
}
catch (Exception ex)
{
_options.DiagnosticLogger?.LogError(
"Exception in background worker of CachingTransport.",
ex
);
_options.DiagnosticLogger?.LogError("Exception in background worker of CachingTransport.", ex);

// Wait a bit before retrying
await Task.Delay(500, _workerCts.Token).ConfigureAwait(false);
Expand Down Expand Up @@ -143,18 +142,13 @@ private IEnumerable<string> GetCacheFilePaths()
}
catch (DirectoryNotFoundException)
{
_options.DiagnosticLogger?.LogWarning(
"Cache directory is empty."
);

_options.DiagnosticLogger?.LogWarning("Cache directory is empty.");
return Array.Empty<string>();
}
}

private async Task ProcessCacheAsync(CancellationToken cancellationToken = default)
{
_options.DiagnosticLogger?.LogDebug("Flushing cached envelopes.");

while (await TryPrepareNextCacheFileAsync(cancellationToken).ConfigureAwait(false) is { } envelopeFilePath)
{
_options.DiagnosticLogger?.LogDebug(
Expand All @@ -173,23 +167,20 @@ private async Task ProcessCacheAsync(CancellationToken cancellationToken = defau

_options.DiagnosticLogger?.LogDebug(
"Sending cached envelope: {0}",
envelope.TryGetEventId()
);
envelope.TryGetEventId());

await _innerTransport.SendEnvelopeAsync(envelope, cancellationToken).ConfigureAwait(false);

_options.DiagnosticLogger?.LogDebug(
"Successfully sent cached envelope: {0}",
envelope.TryGetEventId()
);
envelope.TryGetEventId());
}
catch (Exception ex) when (IsRetryable(ex))
{
_options.DiagnosticLogger?.LogError(
"Failed to send cached envelope: {0}, retrying after a delay.",
ex,
envelopeFilePath
);
envelopeFilePath);

// Let the worker catch, log, wait a bit and retry.
throw;
Expand All @@ -215,9 +206,9 @@ private async Task ProcessCacheAsync(CancellationToken cancellationToken = defau
// from disk could raise an IOException related to Disk I/O.
// For that reason, we're not retrying IOException, to avoid any disk related exception from retrying.
private static bool IsRetryable(Exception exception) =>
exception is OperationCanceledException // Timed-out or Shutdown triggered
|| exception is HttpRequestException // Myriad of HTTP related errors
|| exception is SocketException; // Network related
exception is OperationCanceledException
bruno-garcia marked this conversation as resolved.
Show resolved Hide resolved
or HttpRequestException
or SocketException; // Network related

// Gets the next cache file and moves it to "processing"
private async Task<string?> TryPrepareNextCacheFileAsync(
Expand All @@ -242,7 +233,7 @@ exception is OperationCanceledException // Timed-out or Shutdown triggered
// already exists in the output directory.
// That should never happen under normal workflows because the filenames
// have high variance.
#if NETCOREAPP3_0 || NET5_0
#if NETCOREAPP3_0_OR_GREATER
File.Move(filePath, targetFilePath, true);
#else
File.Copy(filePath, targetFilePath, true);
Expand All @@ -266,8 +257,7 @@ private async Task StoreToCacheAsync(
$"{Guid.NewGuid().GetHashCode() % 1_0000}_" + // random 1-4 digits for extra variance
$"{envelope.TryGetEventId()}_" + // event ID (may be empty)
$"{envelope.GetHashCode()}" + // envelope hash code
$".{EnvelopeFileExt}"
);
$".{EnvelopeFileExt}");

_options.DiagnosticLogger?.LogDebug("Storing file {0}.", envelopeFilePath);

Expand Down Expand Up @@ -310,8 +300,11 @@ public async Task StopWorkerAsync()
await _worker.ConfigureAwait(false);
}

public async Task FlushAsync(CancellationToken cancellationToken = default) =>
public async Task FlushAsync(CancellationToken cancellationToken = default)
{
_options.DiagnosticLogger?.LogDebug("External FlushAsync invocation: flushing cached envelopes.");
await ProcessCacheAsync(cancellationToken).ConfigureAwait(false);
}

public async ValueTask DisposeAsync()
{
Expand Down
28 changes: 21 additions & 7 deletions src/Sentry/Internal/Http/HttpTransport.cs
Original file line number Diff line number Diff line change
Expand Up @@ -169,11 +169,17 @@ public async Task SendEnvelopeAsync(Envelope envelope, CancellationToken cancell
return;
}

_options.DiagnosticLogger?.LogDebug(
"Envelope {0} successfully received by Sentry.",
processedEnvelope.TryGetEventId()
);

if (_options.DiagnosticLogger?.IsEnabled(SentryLevel.Debug) is true)
{
_options.DiagnosticLogger?.LogDebug("Envelope '{0}' sent successfully. Payload:\n{1}",
envelope.TryGetEventId(),
await envelope.SerializeToStringAsync(cancellationToken).ConfigureAwait(false));
}
else
{
_options.DiagnosticLogger?.LogInfo("Envelope '{0}' successfully received by Sentry.",
processedEnvelope.TryGetEventId());
}
}

private async Task HandleFailureAsync(
Expand All @@ -182,7 +188,7 @@ private async Task HandleFailureAsync(
CancellationToken cancellationToken)
{
// Spare the overhead if level is not enabled
if (_options.DiagnosticLogger?.IsEnabled(SentryLevel.Error) == true)
if (_options.DiagnosticLogger?.IsEnabled(SentryLevel.Error) is true)
{
if (string.Equals(response.Content.Headers.ContentType?.MediaType, "application/json",
StringComparison.OrdinalIgnoreCase))
Expand Down Expand Up @@ -220,11 +226,19 @@ private async Task HandleFailureAsync(
responseString
);
}

// If debug level, dump the whole envelope to the logger
if (_options.DiagnosticLogger?.IsEnabled(SentryLevel.Debug) is true)
{
_options.DiagnosticLogger?.LogDebug("Failed envelope '{0}' has payload:\n{1}",
bruno-garcia marked this conversation as resolved.
Show resolved Hide resolved
processedEnvelope.TryGetEventId(),
await processedEnvelope.SerializeToStringAsync(cancellationToken).ConfigureAwait(false));
}
}

// SDK is in debug mode, and envelope was too large. To help troubleshoot:
const string persistLargeEnvelopePathEnvVar = "SENTRY_KEEP_LARGE_ENVELOPE_PATH";
if (_options.DiagnosticLogger?.IsEnabled(SentryLevel.Debug) == true
if (_options.DiagnosticLogger?.IsEnabled(SentryLevel.Debug) is true
&& response.StatusCode == HttpStatusCode.RequestEntityTooLarge
&& _getEnvironmentVariable(persistLargeEnvelopePathEnvVar) is { } destinationDirectory)
{
Expand Down
3 changes: 1 addition & 2 deletions src/Sentry/Internal/Http/RateLimit.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,7 @@ public static RateLimit Parse(string rateLimitEncoded)

return new RateLimit(
categories,
retryAfter
);
retryAfter);
}

public static IEnumerable<RateLimit> ParseMany(string rateLimitsEncoded) =>
Expand Down
3 changes: 1 addition & 2 deletions src/Sentry/Internal/Hub.cs
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,7 @@ internal Hub(
ScopeManager = scopeManager ?? new SentryScopeManager(
options.ScopeStackContainer ?? new AsyncLocalScopeStackContainer(),
options,
_ownedClient
);
_ownedClient);

_rootScope = options.IsGlobalModeEnabled
? DisabledHub.Instance
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,18 @@
using System.Threading.Tasks;
using Sentry.Protocol.Envelopes;

namespace Sentry.Tests.Helpers
namespace Sentry.Internal
{
internal static class SerializableExtensions
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I may be wrong, but moving a static code from the Test project to the Main project isn't going to include an unused static extension to the Main SDK instead of limiting it to the Test projects.

{
public static async Task<string> SerializeToStringAsync(
this ISerializable serializable,
CancellationToken cancellationToken = default)
{
#if !NET461 && !NETCOREAPP2_1
#if !NET461 && !NETSTANDARD2_0
await
#endif
using var stream = new MemoryStream();
using var stream = new MemoryStream();
Comment on lines +15 to +18
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
#if !NET461 && !NETSTANDARD2_0
await
#endif
using var stream = new MemoryStream();
using var stream = new MemoryStream();
#if !NET461 && !NETSTANDARD2_0
await using var stream = new MemoryStream();
#else
using var stream = new MemoryStream();
#endif

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is it beneficial to add a #else block with the same statement over only adding await?

If we agreed on this new style lets do a single pass and replace it everything. The approach I used is already the standard and there are many usages

await serializable.SerializeAsync(stream, cancellationToken).ConfigureAwait(false);
return Encoding.UTF8.GetString(stream.ToArray());
}
Expand Down
Loading