Exemplo n.º 1
0
        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);
        }