private async Task WorkerAsync() { var cancellation = _shutdownSource.Token; using var shutdownTimeout = new CancellationTokenSource(); var shutdownRequested = false; try { while (!shutdownTimeout.IsCancellationRequested) { // If the cancellation was signaled, // set the latest we can keep reading off of the queue (while there's still stuff to read) // No longer synchronized with queuedEnvelopeSemaphore (Enqueue will throw object disposed), // run until the end of the queue or shutdownTimeout if (!shutdownRequested) { try { await _queuedEnvelopeSemaphore.WaitAsync(cancellation).ConfigureAwait(false); } // Cancellation requested, scheduled shutdown but continue in case there are more items catch (OperationCanceledException) { if (_options.ShutdownTimeout == TimeSpan.Zero) { _options.LogDebug( "Exiting immediately due to 0 shutdown timeout. #{0} in queue.", _queue.Count); return; } else { _options.LogDebug( "Shutdown scheduled. Stopping by: {0}. #{1} in queue.", _options.ShutdownTimeout, _queue.Count); shutdownTimeout.CancelAfter(_options.ShutdownTimeout); } shutdownRequested = true; } } if (_queue.TryPeek(out var envelope)) // Work with the envelope while it's in the queue { try { // Dispose inside try/catch using var _ = envelope; var task = _transport.SendEnvelopeAsync(envelope, shutdownTimeout.Token); _options.LogDebug( "Envelope {0} handed off to transport. #{1} in queue.", envelope.TryGetEventId(), _queue.Count); await task.ConfigureAwait(false); } catch (OperationCanceledException) { _options.LogInfo( "Shutdown token triggered. Time to exit. #{0} in queue.", _queue.Count); return; } catch (Exception exception) { _options.LogError( "Error while processing envelope (event ID: '{0}'). #{1} in queue.", exception, envelope.TryGetEventId(), _queue.Count); } finally { _ = _queue.TryDequeue(out _); _ = Interlocked.Decrement(ref _currentItems); OnFlushObjectReceived?.Invoke(envelope, EventArgs.Empty); } } else { Debug.Assert(shutdownRequested); _options.LogInfo("Exiting the worker with an empty queue."); // Empty queue. Exit. return; } } } catch (Exception e) { _options.LogFatal("Exception in the background worker.", e); throw; } finally { _queuedEnvelopeSemaphore.Dispose(); } }
/// <summary> /// Creates an <see cref="T:System.Net.Http.HttpClient" /> configure to call Sentry for the specified <see cref="T:Sentry.Dsn" /> /// </summary> /// <param name="options">The HTTP options.</param> /// <returns></returns> public HttpClient Create(SentryOptions options) { if (options == null) { throw new ArgumentNullException(nameof(options)); } var httpClientHandler = options.CreateHttpClientHandler?.Invoke() ?? new HttpClientHandler(); if (options.HttpProxy != null) { httpClientHandler.Proxy = options.HttpProxy; options.LogInfo("Using Proxy: {0}", options.HttpProxy); } // If the platform supports automatic decompression if (httpClientHandler.SupportsAutomaticDecompression) { // if the SDK is configured to accept compressed data httpClientHandler.AutomaticDecompression = options.DecompressionMethods; } else { options.LogDebug("No response compression supported by HttpClientHandler."); } HttpMessageHandler handler = httpClientHandler; if (options.RequestBodyCompressionLevel != CompressionLevel.NoCompression) { if (options.RequestBodyCompressionBuffered) { handler = new GzipBufferedRequestBodyHandler(handler, options.RequestBodyCompressionLevel); options.LogDebug("Using 'GzipBufferedRequestBodyHandler' body compression strategy with level {0}.", options.RequestBodyCompressionLevel); } else { handler = new GzipRequestBodyHandler(handler, options.RequestBodyCompressionLevel); options.LogDebug("Using 'GzipRequestBodyHandler' body compression strategy with level {0}.", options.RequestBodyCompressionLevel); } } else { options.LogDebug("Using no request body compression strategy."); } // Adding retry after last for it to run first in the pipeline handler = new RetryAfterHandler(handler); var client = new HttpClient(handler); client.DefaultRequestHeaders.Add("Accept", "application/json"); if (options.ConfigureClient is { } configureClient) { options.LogDebug("Invoking user-defined HttpClient configuration action."); configureClient.Invoke(client); } return(client); }