From f8ad4805b4d881b6311b494f20a2383de044993b Mon Sep 17 00:00:00 2001 From: Bruno Garcia Date: Sat, 9 Dec 2023 12:43:54 -0500 Subject: [PATCH 1/8] spotlight --- .../Program.cs | 1 + .../Program.cs | 7 +- .../DiagnosticLoggerExtensions.cs | 62 +++++++++++++++++ src/Sentry/Http/HttpTransportBase.cs | 68 ++++++++++--------- src/Sentry/Http/SpotlightHttpTransport.cs | 63 +++++++++++++++++ .../Http/DefaultSentryHttpClientFactory.cs | 2 +- src/Sentry/Internal/Http/HttpTransport.cs | 2 +- src/Sentry/Internal/Http/LazyHttpTransport.cs | 6 +- src/Sentry/Internal/Hub.cs | 2 - src/Sentry/Internal/SdkComposer.cs | 25 ++++++- src/Sentry/SentryClient.cs | 5 +- src/Sentry/SentryOptions.cs | 23 +++++++ 12 files changed, 222 insertions(+), 44 deletions(-) create mode 100644 src/Sentry/Http/SpotlightHttpTransport.cs diff --git a/samples/Sentry.Samples.AspNetCore.Basic/Program.cs b/samples/Sentry.Samples.AspNetCore.Basic/Program.cs index d982123b56..f778f44b89 100644 --- a/samples/Sentry.Samples.AspNetCore.Basic/Program.cs +++ b/samples/Sentry.Samples.AspNetCore.Basic/Program.cs @@ -14,6 +14,7 @@ public static IWebHost BuildWebHost(string[] args) => .UseSentry(o => { + o.EnableSpotlight = true; // A DSN is required. You can set it here, or in configuration, or in an environment variable. o.Dsn = "https://eb18e953812b41c3aeb042e666fd3b5c@o447951.ingest.sentry.io/5428537"; diff --git a/samples/Sentry.Samples.Console.Customized/Program.cs b/samples/Sentry.Samples.Console.Customized/Program.cs index b1af6291fd..87aa852ccb 100644 --- a/samples/Sentry.Samples.Console.Customized/Program.cs +++ b/samples/Sentry.Samples.Console.Customized/Program.cs @@ -22,7 +22,9 @@ await SentrySdk.ConfigureScopeAsync(async scope => // A Sentry Data Source Name (DSN) is required. // See https://docs.sentry.io/product/sentry-basics/dsn-explainer/ // You can set it in the SENTRY_DSN environment variable, or you can set it in code here. - // o.Dsn = "... Your DSN ..."; + o.Dsn = "https://eb18e953812b41c3aeb042e666fd3b5c@o447951.ingest.sentry.io/5428537"; + + o.EnableSpotlight = true; // Send stack trace for events that were not created from an exception // e.g: CaptureMessage, log.LogDebug, log.LogInformation ... @@ -107,6 +109,8 @@ await SentrySdk.ConfigureScopeAsync(async scope => // Ignored by its type due to the setting above SentrySdk.CaptureException(new XsltCompileException()); + return; +#pragma warning disable CS0162 // Unreachable code detected SentrySdk.AddBreadcrumb( "A 'bad breadcrumb' that will be rejected because of 'BeforeBreadcrumb callback above.'"); @@ -207,6 +211,7 @@ await SentrySdk.ConfigureScopeAsync(async scope => SentrySdk.CaptureException( new Exception("Error outside of the admin section: Goes to the default DSN")); +#pragma warning restore CS0162 // Unreachable code detected } // On Dispose: SDK closed, events queued are flushed/sent to Sentry } diff --git a/src/Sentry/Extensibility/DiagnosticLoggerExtensions.cs b/src/Sentry/Extensibility/DiagnosticLoggerExtensions.cs index 579364db9f..a0862dc122 100644 --- a/src/Sentry/Extensibility/DiagnosticLoggerExtensions.cs +++ b/src/Sentry/Extensibility/DiagnosticLoggerExtensions.cs @@ -58,6 +58,17 @@ internal static void LogDebug( TArg2 arg2) => options.DiagnosticLogger?.LogIfEnabled(SentryLevel.Debug, null, message, arg, arg2); + /// + /// Log a debug message. + /// + internal static void LogDebug( + this SentryOptions options, + string message, + TArg arg, + TArg2 arg2, + TArg3 arg3) + => options.DiagnosticLogger?.LogIfEnabled(SentryLevel.Debug, null, message, arg, arg2, arg3); + /// /// Log a debug message. /// @@ -206,6 +217,17 @@ internal static void LogWarning( TArg2 arg2) => options.DiagnosticLogger?.LogIfEnabled(SentryLevel.Warning, null, message, arg, arg2); + /// + /// Log a warning message. + /// + internal static void LogWarning( + this SentryOptions options, + string message, + TArg arg, + TArg2 arg2, + TArg3 arg3) + => options.DiagnosticLogger?.LogIfEnabled(SentryLevel.Warning, null, message, arg, arg2, arg3); + /// /// Log a error message. /// @@ -300,6 +322,29 @@ internal static void LogError(this SentryOptions opti TArg4 arg4) => options.DiagnosticLogger?.LogIfEnabled(SentryLevel.Error, exception, message, arg, arg2, arg3, arg4); + /// + /// Log a error message. + /// + internal static void LogError(this SentryOptions options, + string message, + TArg arg, + TArg2 arg2, + TArg3 arg3, + TArg4 arg4, + TArg5 arg5) + => options.DiagnosticLogger?.LogIfEnabled(SentryLevel.Error, null, message, arg, arg2, arg3, arg4, arg5); + + /// + /// Log a error message. + /// + internal static void LogError(this SentryOptions options, + string message, + TArg arg, + TArg2 arg2, + TArg3 arg3, + TArg4 arg4) + => options.DiagnosticLogger?.LogIfEnabled(SentryLevel.Error, null, message, arg, arg2, arg3, arg4); + /// /// Log an error message. /// @@ -504,6 +549,23 @@ internal static void LogIfEnabled( } } + internal static void LogIfEnabled( + this IDiagnosticLogger logger, + SentryLevel level, + Exception? exception, + string message, + TArg arg, + TArg2 arg2, + TArg3 arg3, + TArg4 arg4, + TArg5 arg5) + { + if (logger.IsEnabled(level)) + { + logger.Log(level, message, exception, arg, arg2, arg3, arg4, arg5); + } + } + internal static void LogIfEnabled( this SentryOptions options, SentryLevel level, diff --git a/src/Sentry/Http/HttpTransportBase.cs b/src/Sentry/Http/HttpTransportBase.cs index 2eb646b8bd..20b41c8af6 100644 --- a/src/Sentry/Http/HttpTransportBase.cs +++ b/src/Sentry/Http/HttpTransportBase.cs @@ -66,20 +66,22 @@ protected internal Envelope ProcessEnvelope(Envelope envelope) if (clientReport != null) { envelopeItems.Add(EnvelopeItem.FromClientReport(clientReport)); - _options.LogDebug("Attached client report to envelope {0}.", eventId); + _options.LogDebug("{0}: Attached client report to envelope {1}.", GetType().Name, eventId); } if (envelopeItems.Count == 0) { if (_options.SendClientReports) { - _options.LogInfo("Envelope {0} was discarded because all contained items are rate-limited " + + _options.LogInfo("{0}: Envelope '{1}' was discarded because all contained items are rate-limited " + "and there are no client reports to send.", + GetType().Name, eventId); } else { - _options.LogInfo("Envelope {0} was discarded because all contained items are rate-limited.", + _options.LogInfo("{0}: Envelope '{1}' was discarded because all contained items are rate-limited.", + GetType().Name, eventId); } } @@ -99,7 +101,8 @@ private void ProcessEnvelopeItem(DateTimeOffset now, EnvelopeItem item, ListThe envelope. /// An HTTP request message, with the proper headers and body set. /// Throws if the DSN is not set in the options. - protected internal HttpRequestMessage CreateRequest(Envelope envelope) + protected internal virtual HttpRequestMessage CreateRequest(Envelope envelope) { if (string.IsNullOrWhiteSpace(_options.Dsn)) { @@ -302,22 +308,22 @@ private void LogEnvelopeSent(Envelope envelope, string? payload = null) { if (eventId == null) { - _options.LogInfo("Envelope successfully sent.", eventId); + _options.LogInfo("{0}: Envelope successfully sent.", GetType().Name); } else { - _options.LogInfo("Envelope '{0}' successfully sent.", eventId); + _options.LogInfo("{0}: Envelope '{1}' successfully sent.", GetType().Name, eventId); } } else { if (eventId == null) { - _options.LogDebug("Envelope successfully sent. Content: {1}", eventId, payload); + _options.LogDebug("{0}: Envelope successfully sent. Content: {1}", GetType().Name, payload); } else { - _options.LogDebug("Envelope '{0}' successfully sent. Content: {1}", eventId, payload); + _options.LogDebug("{0}: Envelope '{1}' successfully sent. Content: {2}", GetType().Name, eventId, payload); } } } @@ -347,17 +353,17 @@ private void HandleFailure(HttpResponseMessage response, Envelope envelope) if (_options.DiagnosticLogger?.IsEnabled(SentryLevel.Debug) is true) { var payload = envelope.SerializeToString(_options.DiagnosticLogger, _clock); - _options.LogDebug("Failed envelope '{0}' has payload:\n{1}\n", eventId, payload); + _options.LogDebug("{0}: Failed envelope '{1}' has payload:\n{2}\n", GetType().Name, eventId, payload); // SDK is in debug mode, and envelope was too large. To help troubleshoot: const string persistLargeEnvelopePathEnvVar = "SENTRY_KEEP_LARGE_ENVELOPE_PATH"; if (response.StatusCode == HttpStatusCode.RequestEntityTooLarge && _getEnvironmentVariable(persistLargeEnvelopePathEnvVar) is { } destinationDirectory) { - _options.DiagnosticLogger? - .LogDebug("Environment variable '{0}' set. Writing envelope to {1}", - persistLargeEnvelopePathEnvVar, - destinationDirectory); + _options.LogDebug("{0}: Environment variable '{1}' set. Writing envelope to {2}", + GetType().Name, + persistLargeEnvelopePathEnvVar, + destinationDirectory); var destination = Path.Combine(destinationDirectory, "envelope_too_large", (eventId ?? SentryId.Create()).ToString()); @@ -370,8 +376,8 @@ private void HandleFailure(HttpResponseMessage response, Envelope envelope) { envelope.Serialize(envelopeFile, _options.DiagnosticLogger); envelopeFile.Flush(); - _options.LogInfo("Envelope's {0} bytes written to: {1}", - envelopeFile.Length, destination); + _options.LogInfo("{0}: Envelope's {1} bytes written to: {2}", + GetType().Name, envelopeFile.Length, destination); } } } @@ -403,17 +409,17 @@ private async Task HandleFailureAsync(HttpResponseMessage response, Envelope env { var payload = await envelope .SerializeToStringAsync(_options.DiagnosticLogger, _clock, cancellationToken).ConfigureAwait(false); - _options.LogDebug("Failed envelope '{0}' has payload:\n{1}\n", eventId, payload); + _options.LogDebug("{0}: Failed envelope '{1}' has payload:\n{2}\n", GetType().Name, eventId, payload); // SDK is in debug mode, and envelope was too large. To help troubleshoot: const string persistLargeEnvelopePathEnvVar = "SENTRY_KEEP_LARGE_ENVELOPE_PATH"; if (response.StatusCode == HttpStatusCode.RequestEntityTooLarge && _getEnvironmentVariable(persistLargeEnvelopePathEnvVar) is { } destinationDirectory) { - _options.DiagnosticLogger? - .LogDebug("Environment variable '{0}' set. Writing envelope to {1}", - persistLargeEnvelopePathEnvVar, - destinationDirectory); + _options.LogDebug("{0}: Environment variable '{1}' set. Writing envelope to {2}", + GetType().Name, + persistLargeEnvelopePathEnvVar, + destinationDirectory); var destination = Path.Combine(destinationDirectory, "envelope_too_large", (eventId ?? SentryId.Create()).ToString()); @@ -431,8 +437,8 @@ await envelope .SerializeAsync(envelopeFile, _options.DiagnosticLogger, cancellationToken) .ConfigureAwait(false); await envelopeFile.FlushAsync(cancellationToken).ConfigureAwait(false); - _options.LogInfo("Envelope's {0} bytes written to: {1}", - envelopeFile.Length, destination); + _options.LogInfo("{0}: Envelope's {1} bytes written to: {2}", + GetType().Name, envelopeFile.Length, destination); } } } @@ -460,9 +466,8 @@ private void IncrementDiscardsForHttpFailure(HttpStatusCode responseStatusCode, private void LogFailure(string responseString, HttpStatusCode responseStatusCode, SentryId? eventId) { - _options.Log(SentryLevel.Error, - "Sentry rejected the envelope {0}. Status code: {1}. Error detail: {2}.", - null, + _options.LogError("{0}: Sentry rejected the envelope '{1}'. Status code: {2}. Error detail: {3}.", + GetType().Name, eventId, responseStatusCode, responseString); @@ -478,9 +483,8 @@ private void LogFailure(JsonElement responseJson, HttpStatusCode responseStatusC responseJson.GetPropertyOrNull("causes")?.EnumerateArray().Select(j => j.GetString()).ToArray() ?? Array.Empty(); - _options.Log(SentryLevel.Error, - "Sentry rejected the envelope {0}. Status code: {1}. Error detail: {2}. Error causes: {3}.", - null, + _options.LogError("{0}: Sentry rejected the envelope '{1}'. Status code: {2}. Error detail: {3}. Error causes: {4}.", + GetType().Name, eventId, responseStatusCode, errorMessage, diff --git a/src/Sentry/Http/SpotlightHttpTransport.cs b/src/Sentry/Http/SpotlightHttpTransport.cs new file mode 100644 index 0000000000..ad06ddf811 --- /dev/null +++ b/src/Sentry/Http/SpotlightHttpTransport.cs @@ -0,0 +1,63 @@ +using Sentry.Extensibility; +using Sentry.Infrastructure; +using Sentry.Internal.Http; +using Sentry.Protocol.Envelopes; + +namespace Sentry.Http; + +internal class SpotlightHttpTransport : HttpTransport +{ + private readonly ITransport _inner; + private readonly SentryOptions _options; + private readonly HttpClient _httpClient; + private readonly ISystemClock _clock; + + public SpotlightHttpTransport(ITransport inner, SentryOptions options, HttpClient httpClient, ISystemClock clock) + : base(options, httpClient) + { + _options = options; + _httpClient = httpClient; + _inner = inner; + _clock = clock; + } + + protected internal override HttpRequestMessage CreateRequest(Envelope envelope) + { + if (!Uri.TryCreate(_options.SpotlightUrl, UriKind.Absolute, out var spotlightUrl)) + { + throw new InvalidOperationException("Invalid option for SpotlightUrl: " + _options.SpotlightUrl); + } + + return new HttpRequestMessage + { + RequestUri = spotlightUrl, + Method = HttpMethod.Post, + Content = new EnvelopeHttpContent(envelope, _options.DiagnosticLogger, _clock) + { Headers = { ContentType = MediaTypeHeaderValue.Parse("application/x-sentry-envelope") } } + }; + } + + public override async Task SendEnvelopeAsync(Envelope envelope, CancellationToken cancellationToken = default) + { + var sentryTask = _inner.SendEnvelopeAsync(envelope, cancellationToken); + + try + { + // Send to spotlight + using var processedEnvelope = ProcessEnvelope(envelope); + if (processedEnvelope.Items.Count > 0) + { + using var request = CreateRequest(processedEnvelope); + using var response = await _httpClient.SendAsync(request, cancellationToken).ConfigureAwait(false); + await HandleResponseAsync(response, processedEnvelope, cancellationToken).ConfigureAwait(false); + } + } + catch (Exception e) + { + _options.LogError(e, "Failed sending envelope to Spotlight."); + } + + // await the Sentry request before returning + await sentryTask.ConfigureAwait(false); + } +} diff --git a/src/Sentry/Internal/Http/DefaultSentryHttpClientFactory.cs b/src/Sentry/Internal/Http/DefaultSentryHttpClientFactory.cs index ab1f9a795d..e6746283a6 100644 --- a/src/Sentry/Internal/Http/DefaultSentryHttpClientFactory.cs +++ b/src/Sentry/Internal/Http/DefaultSentryHttpClientFactory.cs @@ -20,7 +20,7 @@ public HttpClient Create(SentryOptions options) throw new ArgumentNullException(nameof(options)); } - HttpMessageHandler handler = options.CreateHttpMessageHandler?.Invoke() ?? new HttpClientHandler(); + var handler = options.CreateHttpMessageHandler?.Invoke() ?? new HttpClientHandler(); if (handler is HttpClientHandler httpClientHandler) { if (options.HttpProxy is not null) diff --git a/src/Sentry/Internal/Http/HttpTransport.cs b/src/Sentry/Internal/Http/HttpTransport.cs index 495d732dfb..7f8b7ce91d 100644 --- a/src/Sentry/Internal/Http/HttpTransport.cs +++ b/src/Sentry/Internal/Http/HttpTransport.cs @@ -33,7 +33,7 @@ internal HttpTransport(SentryOptions options, HttpClient httpClient, /// such that they can be shared with higher-level SDKs (such as Unity) that may implement their own method /// for performing HTTP transport. /// - public async Task SendEnvelopeAsync(Envelope envelope, CancellationToken cancellationToken = default) + public virtual async Task SendEnvelopeAsync(Envelope envelope, CancellationToken cancellationToken = default) { using var processedEnvelope = ProcessEnvelope(envelope); if (processedEnvelope.Items.Count > 0) diff --git a/src/Sentry/Internal/Http/LazyHttpTransport.cs b/src/Sentry/Internal/Http/LazyHttpTransport.cs index cff2f85e85..8a81042694 100644 --- a/src/Sentry/Internal/Http/LazyHttpTransport.cs +++ b/src/Sentry/Internal/Http/LazyHttpTransport.cs @@ -9,11 +9,7 @@ internal class LazyHttpTransport : ITransport public LazyHttpTransport(SentryOptions options) { - _httpTransport = new Lazy(() => - { - var factory = (options.SentryHttpClientFactory ?? new DefaultSentryHttpClientFactory()).Create(options); - return new HttpTransport(options, factory); - }); + _httpTransport = new Lazy(() => new HttpTransport(options, options.GetHttpClient())); } public Task SendEnvelopeAsync(Envelope envelope, CancellationToken cancellationToken = default) diff --git a/src/Sentry/Internal/Hub.cs b/src/Sentry/Internal/Hub.cs index c8c2e1da86..1ce56c8429 100644 --- a/src/Sentry/Internal/Hub.cs +++ b/src/Sentry/Internal/Hub.cs @@ -1,7 +1,5 @@ using Sentry.Extensibility; using Sentry.Infrastructure; -using Sentry.Integrations; -using Sentry.Internal.ScopeStack; namespace Sentry.Internal; diff --git a/src/Sentry/Internal/SdkComposer.cs b/src/Sentry/Internal/SdkComposer.cs index f813a61805..9b6c7c1921 100644 --- a/src/Sentry/Internal/SdkComposer.cs +++ b/src/Sentry/Internal/SdkComposer.cs @@ -1,4 +1,6 @@ using Sentry.Extensibility; +using Sentry.Http; +using Sentry.Infrastructure; using Sentry.Internal.Http; namespace Sentry.Internal; @@ -27,6 +29,27 @@ private ITransport CreateTransport() transport = CachingTransport.Create(transport, _options); } + // Wrap the transport with the Spotlight one that double sends the envelope: Sentry + Spotlight + if (_options.EnableSpotlight) + { + var environment = _options.SettingLocator.GetEnvironment(true); + if (string.Equals(environment, Constants.ProductionEnvironmentSetting, StringComparison.OrdinalIgnoreCase)) + { + _options.LogWarning(""" + [Spotlight] It seems you're not in dev mode because environment is set to 'production'. + Do you really want to have Spotlight enabled? + You can set a different environment via SENTRY_ENVIRONMENT env var or programatically during Init. + Docs on Environment: https://docs.sentry.io/platforms/dotnet/configuration/environments/ + """); + } + else + { + _options.LogInfo("Connecting to Spotlight at {0}", _options.SpotlightUrl); + } + + transport = new SpotlightHttpTransport(transport, _options, _options.GetHttpClient(), SystemClock.Clock); + } + // Always persist the transport on the options, so other places can pick it up where necessary. _options.Transport = transport; @@ -35,7 +58,7 @@ private ITransport CreateTransport() private LazyHttpTransport CreateHttpTransport() { - if (_options.SentryHttpClientFactory is { }) + if (_options.SentryHttpClientFactory is not null) { _options.LogDebug( "Using ISentryHttpClientFactory set through options: {0}.", diff --git a/src/Sentry/SentryClient.cs b/src/Sentry/SentryClient.cs index 3f39a34f40..520e1954a0 100644 --- a/src/Sentry/SentryClient.cs +++ b/src/Sentry/SentryClient.cs @@ -49,11 +49,14 @@ internal SentryClient( options.SetupLogging(); // Only relevant if this client wasn't created as a result of calling Init - if (AotHelper.IsNativeAot) { + if (AotHelper.IsNativeAot) +#pragma warning disable CS0162 // Unreachable code detected + { #pragma warning disable 0162 // Unreachable code on old .NET frameworks options.LogDebug("This looks like a NativeAOT application build."); #pragma warning restore 0162 } else { +#pragma warning restore CS0162 // Unreachable code detected options.LogDebug("This looks like a standard JIT/AOT application build."); } diff --git a/src/Sentry/SentryOptions.cs b/src/Sentry/SentryOptions.cs index 19810f2939..3833303a22 100644 --- a/src/Sentry/SentryOptions.cs +++ b/src/Sentry/SentryOptions.cs @@ -214,6 +214,12 @@ internal IEnumerable Integrations internal ISentryHttpClientFactory? SentryHttpClientFactory { get; set; } + internal HttpClient GetHttpClient() + { + var factory = SentryHttpClientFactory ?? new DefaultSentryHttpClientFactory(); + return factory.Create(this); + } + /// /// Scope state processor. /// @@ -1064,6 +1070,23 @@ public bool JsonPreserveReferences [EditorBrowsable(EditorBrowsableState.Never)] public Func? AssemblyReader { get; set; } + /// + /// The Spotlight URL. Defaults to http://localhost:8969 + /// + /// + /// + public string SpotlightUrl { get; set; } = "http://localhost:8969/stream"; + + /// + /// Whether to enable spotlight for local development. + /// + /// + /// Only set this option to `true` while developing, not in production! + /// + /// + /// + public bool EnableSpotlight { get; set; } + internal SettingLocator SettingLocator { get; set; } /// From e252b1fdf21e51f9fb706f4749b2fd3f74e074f4 Mon Sep 17 00:00:00 2001 From: Bruno Garcia Date: Sat, 9 Dec 2023 12:55:14 -0500 Subject: [PATCH 2/8] changelog --- CHANGELOG.md | 6 ++++++ src/Sentry/SentryOptions.cs | 4 ++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6522855e4b..7c20446038 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,12 @@ ## Unreleased +### Feature + +- Support for [Spotlight](https://spotlightjs.com/). Debug tool for local development. ([#2961](https://github.com/getsentry/sentry-dotnet/pull/2961)) + - Enable it with option `EnableSpotlight` + - Optionally configure the URL to connect via `SpotlightUrl`. Defaults to `http://localhost:8969/stream`. + ### Fixes - Stop Sentry for MacCatalyst from creating `default.profraw` in the app bundle using xcodebuild archive to build sentry-cocoa ([#2960](https://github.com/getsentry/sentry-dotnet/pull/2960)) diff --git a/src/Sentry/SentryOptions.cs b/src/Sentry/SentryOptions.cs index 3833303a22..198c1a569e 100644 --- a/src/Sentry/SentryOptions.cs +++ b/src/Sentry/SentryOptions.cs @@ -1071,14 +1071,14 @@ public bool JsonPreserveReferences public Func? AssemblyReader { get; set; } /// - /// The Spotlight URL. Defaults to http://localhost:8969 + /// The Spotlight URL. Defaults to http://localhost:8969/stream /// /// /// public string SpotlightUrl { get; set; } = "http://localhost:8969/stream"; /// - /// Whether to enable spotlight for local development. + /// Whether to enable Spotlight for local development. /// /// /// Only set this option to `true` while developing, not in production! From 38331735be2c53cb97b800a625d571702139903a Mon Sep 17 00:00:00 2001 From: Bruno Garcia Date: Sun, 10 Dec 2023 22:21:46 -0500 Subject: [PATCH 3/8] chore binding boilerplate --- src/Sentry/BindableSentryOptions.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Sentry/BindableSentryOptions.cs b/src/Sentry/BindableSentryOptions.cs index 92360933ae..0964b5ebad 100644 --- a/src/Sentry/BindableSentryOptions.cs +++ b/src/Sentry/BindableSentryOptions.cs @@ -47,6 +47,8 @@ internal partial class BindableSentryOptions public bool? AutoSessionTracking { get; set; } public bool? UseAsyncFileIO { get; set; } public bool? JsonPreserveReferences { get; set; } + public bool? EnableSpotlight { get; set; } + public string? SpotlightUrl { get; set; } public void ApplyTo(SentryOptions options) { @@ -90,6 +92,8 @@ public void ApplyTo(SentryOptions options) options.AutoSessionTracking = AutoSessionTracking ?? options.AutoSessionTracking; options.UseAsyncFileIO = UseAsyncFileIO ?? options.UseAsyncFileIO; options.JsonPreserveReferences = JsonPreserveReferences ?? options.JsonPreserveReferences; + options.EnableSpotlight = EnableSpotlight ?? options.EnableSpotlight; + options.SpotlightUrl = SpotlightUrl ?? options.SpotlightUrl; #if ANDROID Android.ApplyTo(options.Android); From f33e05fb18e36003819155ff87b753eec2347c7d Mon Sep 17 00:00:00 2001 From: Bruno Garcia Date: Sun, 10 Dec 2023 22:50:51 -0500 Subject: [PATCH 4/8] binding and tests --- ...piApprovalTests.Run.DotNet6_0.verified.txt | 4 +- ...piApprovalTests.Run.DotNet7_0.verified.txt | 4 +- ...piApprovalTests.Run.DotNet8_0.verified.txt | 4 +- .../ApiApprovalTests.Run.Net4_8.verified.txt | 4 +- .../Internals/Http/HttpTransportTests.cs | 51 ++++++++++--------- 5 files changed, 40 insertions(+), 27 deletions(-) diff --git a/test/Sentry.Tests/ApiApprovalTests.Run.DotNet6_0.verified.txt b/test/Sentry.Tests/ApiApprovalTests.Run.DotNet6_0.verified.txt index 60d2cee9db..df13a013dd 100644 --- a/test/Sentry.Tests/ApiApprovalTests.Run.DotNet6_0.verified.txt +++ b/test/Sentry.Tests/ApiApprovalTests.Run.DotNet6_0.verified.txt @@ -618,6 +618,7 @@ namespace Sentry public string? Distribution { get; set; } public string? Dsn { get; set; } public bool EnableScopeSync { get; set; } + public bool EnableSpotlight { get; set; } public bool? EnableTracing { get; set; } public string? Environment { get; set; } public System.Collections.Generic.IList FailedRequestStatusCodes { get; set; } @@ -644,6 +645,7 @@ namespace Sentry public Sentry.ISentryScopeStateProcessor SentryScopeStateProcessor { get; set; } public string? ServerName { get; set; } public System.TimeSpan ShutdownTimeout { get; set; } + public string SpotlightUrl { get; set; } public Sentry.StackTraceMode StackTraceMode { get; set; } public System.Collections.Generic.ICollection TagFilters { get; set; } public System.Collections.Generic.IList TracePropagationTargets { get; set; } @@ -1336,7 +1338,7 @@ namespace Sentry.Http public abstract class HttpTransportBase { protected HttpTransportBase(Sentry.SentryOptions options, System.Func? getEnvironmentVariable = null, Sentry.Infrastructure.ISystemClock? clock = null) { } - protected System.Net.Http.HttpRequestMessage CreateRequest(Sentry.Protocol.Envelopes.Envelope envelope) { } + protected virtual System.Net.Http.HttpRequestMessage CreateRequest(Sentry.Protocol.Envelopes.Envelope envelope) { } protected void HandleResponse(System.Net.Http.HttpResponseMessage response, Sentry.Protocol.Envelopes.Envelope envelope) { } protected System.Threading.Tasks.Task HandleResponseAsync(System.Net.Http.HttpResponseMessage response, Sentry.Protocol.Envelopes.Envelope envelope, System.Threading.CancellationToken cancellationToken) { } protected Sentry.Protocol.Envelopes.Envelope ProcessEnvelope(Sentry.Protocol.Envelopes.Envelope envelope) { } diff --git a/test/Sentry.Tests/ApiApprovalTests.Run.DotNet7_0.verified.txt b/test/Sentry.Tests/ApiApprovalTests.Run.DotNet7_0.verified.txt index 60d2cee9db..df13a013dd 100644 --- a/test/Sentry.Tests/ApiApprovalTests.Run.DotNet7_0.verified.txt +++ b/test/Sentry.Tests/ApiApprovalTests.Run.DotNet7_0.verified.txt @@ -618,6 +618,7 @@ namespace Sentry public string? Distribution { get; set; } public string? Dsn { get; set; } public bool EnableScopeSync { get; set; } + public bool EnableSpotlight { get; set; } public bool? EnableTracing { get; set; } public string? Environment { get; set; } public System.Collections.Generic.IList FailedRequestStatusCodes { get; set; } @@ -644,6 +645,7 @@ namespace Sentry public Sentry.ISentryScopeStateProcessor SentryScopeStateProcessor { get; set; } public string? ServerName { get; set; } public System.TimeSpan ShutdownTimeout { get; set; } + public string SpotlightUrl { get; set; } public Sentry.StackTraceMode StackTraceMode { get; set; } public System.Collections.Generic.ICollection TagFilters { get; set; } public System.Collections.Generic.IList TracePropagationTargets { get; set; } @@ -1336,7 +1338,7 @@ namespace Sentry.Http public abstract class HttpTransportBase { protected HttpTransportBase(Sentry.SentryOptions options, System.Func? getEnvironmentVariable = null, Sentry.Infrastructure.ISystemClock? clock = null) { } - protected System.Net.Http.HttpRequestMessage CreateRequest(Sentry.Protocol.Envelopes.Envelope envelope) { } + protected virtual System.Net.Http.HttpRequestMessage CreateRequest(Sentry.Protocol.Envelopes.Envelope envelope) { } protected void HandleResponse(System.Net.Http.HttpResponseMessage response, Sentry.Protocol.Envelopes.Envelope envelope) { } protected System.Threading.Tasks.Task HandleResponseAsync(System.Net.Http.HttpResponseMessage response, Sentry.Protocol.Envelopes.Envelope envelope, System.Threading.CancellationToken cancellationToken) { } protected Sentry.Protocol.Envelopes.Envelope ProcessEnvelope(Sentry.Protocol.Envelopes.Envelope envelope) { } diff --git a/test/Sentry.Tests/ApiApprovalTests.Run.DotNet8_0.verified.txt b/test/Sentry.Tests/ApiApprovalTests.Run.DotNet8_0.verified.txt index 4ed6442010..c12dbe0dbe 100644 --- a/test/Sentry.Tests/ApiApprovalTests.Run.DotNet8_0.verified.txt +++ b/test/Sentry.Tests/ApiApprovalTests.Run.DotNet8_0.verified.txt @@ -619,6 +619,7 @@ namespace Sentry public string? Distribution { get; set; } public string? Dsn { get; set; } public bool EnableScopeSync { get; set; } + public bool EnableSpotlight { get; set; } public bool? EnableTracing { get; set; } public string? Environment { get; set; } public System.Collections.Generic.IList FailedRequestStatusCodes { get; set; } @@ -645,6 +646,7 @@ namespace Sentry public Sentry.ISentryScopeStateProcessor SentryScopeStateProcessor { get; set; } public string? ServerName { get; set; } public System.TimeSpan ShutdownTimeout { get; set; } + public string SpotlightUrl { get; set; } public Sentry.StackTraceMode StackTraceMode { get; set; } public System.Collections.Generic.ICollection TagFilters { get; set; } public System.Collections.Generic.IList TracePropagationTargets { get; set; } @@ -1337,7 +1339,7 @@ namespace Sentry.Http public abstract class HttpTransportBase { protected HttpTransportBase(Sentry.SentryOptions options, System.Func? getEnvironmentVariable = null, Sentry.Infrastructure.ISystemClock? clock = null) { } - protected System.Net.Http.HttpRequestMessage CreateRequest(Sentry.Protocol.Envelopes.Envelope envelope) { } + protected virtual System.Net.Http.HttpRequestMessage CreateRequest(Sentry.Protocol.Envelopes.Envelope envelope) { } protected void HandleResponse(System.Net.Http.HttpResponseMessage response, Sentry.Protocol.Envelopes.Envelope envelope) { } protected System.Threading.Tasks.Task HandleResponseAsync(System.Net.Http.HttpResponseMessage response, Sentry.Protocol.Envelopes.Envelope envelope, System.Threading.CancellationToken cancellationToken) { } protected Sentry.Protocol.Envelopes.Envelope ProcessEnvelope(Sentry.Protocol.Envelopes.Envelope envelope) { } diff --git a/test/Sentry.Tests/ApiApprovalTests.Run.Net4_8.verified.txt b/test/Sentry.Tests/ApiApprovalTests.Run.Net4_8.verified.txt index c2ad004e64..298e3ff6ee 100644 --- a/test/Sentry.Tests/ApiApprovalTests.Run.Net4_8.verified.txt +++ b/test/Sentry.Tests/ApiApprovalTests.Run.Net4_8.verified.txt @@ -616,6 +616,7 @@ namespace Sentry public string? Distribution { get; set; } public string? Dsn { get; set; } public bool EnableScopeSync { get; set; } + public bool EnableSpotlight { get; set; } public bool? EnableTracing { get; set; } public string? Environment { get; set; } public System.Collections.Generic.IList FailedRequestStatusCodes { get; set; } @@ -642,6 +643,7 @@ namespace Sentry public Sentry.ISentryScopeStateProcessor SentryScopeStateProcessor { get; set; } public string? ServerName { get; set; } public System.TimeSpan ShutdownTimeout { get; set; } + public string SpotlightUrl { get; set; } public Sentry.StackTraceMode StackTraceMode { get; set; } public System.Collections.Generic.ICollection TagFilters { get; set; } public System.Collections.Generic.IList TracePropagationTargets { get; set; } @@ -1333,7 +1335,7 @@ namespace Sentry.Http public abstract class HttpTransportBase { protected HttpTransportBase(Sentry.SentryOptions options, System.Func? getEnvironmentVariable = null, Sentry.Infrastructure.ISystemClock? clock = null) { } - protected System.Net.Http.HttpRequestMessage CreateRequest(Sentry.Protocol.Envelopes.Envelope envelope) { } + protected virtual System.Net.Http.HttpRequestMessage CreateRequest(Sentry.Protocol.Envelopes.Envelope envelope) { } protected void HandleResponse(System.Net.Http.HttpResponseMessage response, Sentry.Protocol.Envelopes.Envelope envelope) { } protected System.Threading.Tasks.Task HandleResponseAsync(System.Net.Http.HttpResponseMessage response, Sentry.Protocol.Envelopes.Envelope envelope, System.Threading.CancellationToken cancellationToken) { } protected Sentry.Protocol.Envelopes.Envelope ProcessEnvelope(Sentry.Protocol.Envelopes.Envelope envelope) { } diff --git a/test/Sentry.Tests/Internals/Http/HttpTransportTests.cs b/test/Sentry.Tests/Internals/Http/HttpTransportTests.cs index e00b8a3f55..7e2eb80825 100644 --- a/test/Sentry.Tests/Internals/Http/HttpTransportTests.cs +++ b/test/Sentry.Tests/Internals/Http/HttpTransportTests.cs @@ -82,12 +82,13 @@ public async Task SendEnvelopeAsync_ResponseNotOkWithJsonMessage_LogsError() // Assert logger.Entries.Any(e => e.Level == SentryLevel.Error && - e.Message == "Sentry rejected the envelope {0}. Status code: {1}. Error detail: {2}. Error causes: {3}." && + e.Message == "{0}: Sentry rejected the envelope '{1}'. Status code: {2}. Error detail: {3}. Error causes: {4}." && e.Exception == null && - e.Args[0].ToString() == envelope.TryGetEventId().ToString() && - e.Args[1].ToString() == expectedCode.ToString() && - e.Args[2].ToString() == expectedMessage && - e.Args[3].ToString() == expectedCausesFormatted + e.Args[0].ToString() == "HttpTransport" && + e.Args[1].ToString() == envelope.TryGetEventId().ToString() && + e.Args[2].ToString() == expectedCode.ToString() && + e.Args[3].ToString() == expectedMessage && + e.Args[4].ToString() == expectedCausesFormatted ).Should().BeTrue(); } @@ -125,28 +126,29 @@ public async Task SendEnvelopeAsync_ResponseRequestEntityTooLargeWithPathDefined // Assert logger.Entries.Any(e => e.Level == SentryLevel.Debug && - e.Message == "Environment variable '{0}' set. Writing envelope to {1}" && + e.Message == "{0}: Environment variable '{1}' set. Writing envelope to {2}" && e.Exception == null && - e.Args[0].ToString() == expectedEnvVar && - e.Args[1].ToString() == path) + e.Args[0].ToString() == "HttpTransport" && + e.Args[1].ToString() == expectedEnvVar && + e.Args[2].ToString() == path) .Should() .BeTrue(); var fileStoredLogEntry = logger.Entries.FirstOrDefault(e => e.Level == SentryLevel.Info && - e.Message == "Envelope's {0} bytes written to: {1}"); + e.Message == "{0}: Envelope's {1} bytes written to: {2}"); Assert.NotNull(fileStoredLogEntry); - var expectedFile = new FileInfo(fileStoredLogEntry.Args[1].ToString()!); + var expectedFile = new FileInfo(fileStoredLogEntry.Args[2].ToString()!); Assert.True(expectedFile.Exists); try { Assert.Null(fileStoredLogEntry.Exception); // // Path is based on the provided path: - Assert.Contains(path, fileStoredLogEntry.Args[1] as string); + Assert.Contains(path, fileStoredLogEntry.Args[2] as string); // // Path contains the envelope id in its name: - Assert.Contains(envelope.TryGetEventId().ToString(), fileStoredLogEntry.Args[1] as string); - Assert.Equal(expectedFile.Length, (long)fileStoredLogEntry.Args[0]); + Assert.Contains(envelope.TryGetEventId().ToString(), fileStoredLogEntry.Args[2] as string); + Assert.Equal(expectedFile.Length, (long)fileStoredLogEntry.Args[1]); } finally { @@ -224,11 +226,12 @@ public async Task SendEnvelopeAsync_ResponseNotOkWithStringMessage_LogsError() // Assert _ = logger.Entries.Any(e => e.Level == SentryLevel.Error && - e.Message == "Sentry rejected the envelope {0}. Status code: {1}. Error detail: {2}." && + e.Message == "{0}: Sentry rejected the envelope '{1}'. Status code: {2}. Error detail: {3}." && e.Exception == null && - e.Args[0].ToString() == envelope.TryGetEventId().ToString() && - e.Args[1].ToString() == expectedCode.ToString() && - e.Args[2].ToString() == expectedMessage + e.Args[0].ToString() == "HttpTransport" && + e.Args[1].ToString() == envelope.TryGetEventId().ToString() && + e.Args[2].ToString() == expectedCode.ToString() && + e.Args[3].ToString() == expectedMessage ).Should().BeTrue(); } @@ -262,12 +265,14 @@ public async Task SendEnvelopeAsync_ResponseNotOkNoMessage_LogsError() // Assert logger.Entries.Any(e => e.Level == SentryLevel.Error && - e.Message == "Sentry rejected the envelope {0}. Status code: {1}. Error detail: {2}. Error causes: {3}." && + e.Message == "{0}: Sentry rejected the envelope '{1}'. Status code: {2}. Error detail: {3}. Error causes: {4}." && e.Exception == null && - e.Args[0].ToString() == envelope.TryGetEventId().ToString() && - e.Args[1].ToString() == expectedCode.ToString() && - e.Args[2].ToString() == HttpTransportBase.DefaultErrorMessage && - e.Args[3].ToString() == string.Empty + e.Args[0].ToString() == "HttpTransport" && + e.Args[1].ToString() == envelope.TryGetEventId().ToString() && + e.Args[2].ToString() == expectedCode.ToString() && + e.Args[3].ToString() == HttpTransportBase.DefaultErrorMessage && + e.Args[4].ToString() == string.Empty + ).Should().BeTrue(); } @@ -588,7 +593,7 @@ public async Task SendEnvelopeAsync_AttachmentTooLarge_DropsItem() // (the envelope should have only one item) logger.Entries.Should().Contain(e => - string.Format(e.Message, e.Args) == "Attachment 'test2.txt' dropped because it's too large (5 bytes)."); + string.Format(e.Message, e.Args) == "HttpTransport: Attachment 'test2.txt' dropped because it's too large (5 bytes)."); actualEnvelopeSerialized.Should().NotContain("test2.txt"); } From 492e626333c9464d445900ba8d6311e09861f08c Mon Sep 17 00:00:00 2001 From: Bruno Garcia Date: Tue, 12 Dec 2023 21:07:13 -0500 Subject: [PATCH 5/8] test --- .../Internals/Http/SpotlightTransportTests.cs | 53 +++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 test/Sentry.Tests/Internals/Http/SpotlightTransportTests.cs diff --git a/test/Sentry.Tests/Internals/Http/SpotlightTransportTests.cs b/test/Sentry.Tests/Internals/Http/SpotlightTransportTests.cs new file mode 100644 index 0000000000..722caef016 --- /dev/null +++ b/test/Sentry.Tests/Internals/Http/SpotlightTransportTests.cs @@ -0,0 +1,53 @@ +using Sentry.Http; +using Sentry.Tests.Helpers; + +namespace Sentry.Tests.Internals.Http; + +public class SpotlightTransportTests +{ + // Makes sure it'll call both inner transport and spotlight, even if spotlight's request fails. + // Inner transport error actually bubbles up instead of Spotlights' + [Fact] + public async Task SendEnvelopeAsync_SpotlightRequestFailed_InnerTransportFailureBubblesUp() + { + // Arrange + var httpHandler = Substitute.For(); + var expectedSpotlightTransportException = new Exception("Spotlight request fails"); + httpHandler.WhenForAnyArgs(h => h.VerifiableSendAsync(null, CancellationToken.None)) + .Throw(expectedSpotlightTransportException); + + var innerTransport = Substitute.For(); + var logger = new InMemoryDiagnosticLogger(); + + var sut = new SpotlightHttpTransport( + innerTransport, + new SentryOptions + { + Dsn = ValidDsn, + Debug = true, + DiagnosticLogger = logger + }, + new HttpClient(httpHandler), + Substitute.For()); + + var envelope = Envelope.FromEvent(new SentryEvent()); + var expectedInnerTransportException = new Exception("expected inner transport exception"); + var tcs = new TaskCompletionSource(); + tcs.SetException(expectedInnerTransportException); + innerTransport.SendEnvelopeAsync(envelope).Returns(tcs.Task); + + // Act + var actualException = await Assert.ThrowsAsync(() => sut.SendEnvelopeAsync(envelope)); + + // Assert + // Inner transport Exception bubbles out + Assert.Same(expectedInnerTransportException, actualException); + + // Spotlight request failure logged out to diagnostic logger + logger.Entries.Any(e => + e.Level == SentryLevel.Error && + e.Message == "Failed sending envelope to Spotlight." && + ReferenceEquals(expectedSpotlightTransportException, e.Exception) + ).Should().BeTrue(); + } +} From ec8df9664d35a2c04fa4aee56db816f5f55c70a3 Mon Sep 17 00:00:00 2001 From: Bruno Garcia Date: Tue, 12 Dec 2023 21:20:27 -0500 Subject: [PATCH 6/8] cache type name --- src/Sentry/Http/HttpTransportBase.cs | 41 +++++++++++++++------------- 1 file changed, 22 insertions(+), 19 deletions(-) diff --git a/src/Sentry/Http/HttpTransportBase.cs b/src/Sentry/Http/HttpTransportBase.cs index 20b41c8af6..9b8e491805 100644 --- a/src/Sentry/Http/HttpTransportBase.cs +++ b/src/Sentry/Http/HttpTransportBase.cs @@ -24,6 +24,8 @@ public abstract class HttpTransportBase // Using string instead of SentryId here so that we can use Interlocked.Exchange(...). private string? _lastDiscardedSessionInitId; + private string _typeName; + /// /// Constructor for this class. /// @@ -37,6 +39,7 @@ protected HttpTransportBase(SentryOptions options, _options = options; _clock = clock ?? SystemClock.Clock; _getEnvironmentVariable = getEnvironmentVariable ?? options.SettingLocator.GetEnvironmentVariable; + _typeName = GetType().Name; } // Keep track of rate limits and their expiry dates. @@ -66,7 +69,7 @@ protected internal Envelope ProcessEnvelope(Envelope envelope) if (clientReport != null) { envelopeItems.Add(EnvelopeItem.FromClientReport(clientReport)); - _options.LogDebug("{0}: Attached client report to envelope {1}.", GetType().Name, eventId); + _options.LogDebug("{0}: Attached client report to envelope {1}.", _typeName, eventId); } if (envelopeItems.Count == 0) @@ -75,13 +78,13 @@ protected internal Envelope ProcessEnvelope(Envelope envelope) { _options.LogInfo("{0}: Envelope '{1}' was discarded because all contained items are rate-limited " + "and there are no client reports to send.", - GetType().Name, + _typeName, eventId); } else { _options.LogInfo("{0}: Envelope '{1}' was discarded because all contained items are rate-limited.", - GetType().Name, + _typeName, eventId); } } @@ -102,7 +105,7 @@ private void ProcessEnvelopeItem(DateTimeOffset now, EnvelopeItem item, List(); _options.LogError("{0}: Sentry rejected the envelope '{1}'. Status code: {2}. Error detail: {3}. Error causes: {4}.", - GetType().Name, + _typeName, eventId, responseStatusCode, errorMessage, From e072400d62da5dbaeca3b6fb04f823fac5aea017 Mon Sep 17 00:00:00 2001 From: Bruno Garcia Date: Tue, 12 Dec 2023 22:01:26 -0500 Subject: [PATCH 7/8] remove opt-in from samples --- samples/Sentry.Samples.AspNetCore.Basic/Program.cs | 1 - samples/Sentry.Samples.Console.Customized/Program.cs | 7 +------ 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/samples/Sentry.Samples.AspNetCore.Basic/Program.cs b/samples/Sentry.Samples.AspNetCore.Basic/Program.cs index f778f44b89..d982123b56 100644 --- a/samples/Sentry.Samples.AspNetCore.Basic/Program.cs +++ b/samples/Sentry.Samples.AspNetCore.Basic/Program.cs @@ -14,7 +14,6 @@ public static IWebHost BuildWebHost(string[] args) => .UseSentry(o => { - o.EnableSpotlight = true; // A DSN is required. You can set it here, or in configuration, or in an environment variable. o.Dsn = "https://eb18e953812b41c3aeb042e666fd3b5c@o447951.ingest.sentry.io/5428537"; diff --git a/samples/Sentry.Samples.Console.Customized/Program.cs b/samples/Sentry.Samples.Console.Customized/Program.cs index 87aa852ccb..b1af6291fd 100644 --- a/samples/Sentry.Samples.Console.Customized/Program.cs +++ b/samples/Sentry.Samples.Console.Customized/Program.cs @@ -22,9 +22,7 @@ await SentrySdk.ConfigureScopeAsync(async scope => // A Sentry Data Source Name (DSN) is required. // See https://docs.sentry.io/product/sentry-basics/dsn-explainer/ // You can set it in the SENTRY_DSN environment variable, or you can set it in code here. - o.Dsn = "https://eb18e953812b41c3aeb042e666fd3b5c@o447951.ingest.sentry.io/5428537"; - - o.EnableSpotlight = true; + // o.Dsn = "... Your DSN ..."; // Send stack trace for events that were not created from an exception // e.g: CaptureMessage, log.LogDebug, log.LogInformation ... @@ -109,8 +107,6 @@ await SentrySdk.ConfigureScopeAsync(async scope => // Ignored by its type due to the setting above SentrySdk.CaptureException(new XsltCompileException()); - return; -#pragma warning disable CS0162 // Unreachable code detected SentrySdk.AddBreadcrumb( "A 'bad breadcrumb' that will be rejected because of 'BeforeBreadcrumb callback above.'"); @@ -211,7 +207,6 @@ await SentrySdk.ConfigureScopeAsync(async scope => SentrySdk.CaptureException( new Exception("Error outside of the admin section: Goes to the default DSN")); -#pragma warning restore CS0162 // Unreachable code detected } // On Dispose: SDK closed, events queued are flushed/sent to Sentry } From f5efe1c5428b039eeef9b2652a4d956268bfc8f1 Mon Sep 17 00:00:00 2001 From: Bruno Garcia Date: Wed, 13 Dec 2023 09:45:42 -0500 Subject: [PATCH 8/8] parse url once, during init --- src/Sentry/Http/SpotlightHttpTransport.cs | 11 ++++------- src/Sentry/Internal/SdkComposer.cs | 7 +++++-- .../Internals/Http/SpotlightTransportTests.cs | 1 + 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/src/Sentry/Http/SpotlightHttpTransport.cs b/src/Sentry/Http/SpotlightHttpTransport.cs index ad06ddf811..097847531f 100644 --- a/src/Sentry/Http/SpotlightHttpTransport.cs +++ b/src/Sentry/Http/SpotlightHttpTransport.cs @@ -10,27 +10,24 @@ internal class SpotlightHttpTransport : HttpTransport private readonly ITransport _inner; private readonly SentryOptions _options; private readonly HttpClient _httpClient; + private readonly Uri _spotlightUrl; private readonly ISystemClock _clock; - public SpotlightHttpTransport(ITransport inner, SentryOptions options, HttpClient httpClient, ISystemClock clock) + public SpotlightHttpTransport(ITransport inner, SentryOptions options, HttpClient httpClient, Uri spotlightUrl, ISystemClock clock) : base(options, httpClient) { _options = options; _httpClient = httpClient; + _spotlightUrl = spotlightUrl; _inner = inner; _clock = clock; } protected internal override HttpRequestMessage CreateRequest(Envelope envelope) { - if (!Uri.TryCreate(_options.SpotlightUrl, UriKind.Absolute, out var spotlightUrl)) - { - throw new InvalidOperationException("Invalid option for SpotlightUrl: " + _options.SpotlightUrl); - } - return new HttpRequestMessage { - RequestUri = spotlightUrl, + RequestUri = _spotlightUrl, Method = HttpMethod.Post, Content = new EnvelopeHttpContent(envelope, _options.DiagnosticLogger, _clock) { Headers = { ContentType = MediaTypeHeaderValue.Parse("application/x-sentry-envelope") } } diff --git a/src/Sentry/Internal/SdkComposer.cs b/src/Sentry/Internal/SdkComposer.cs index 9b6c7c1921..66d158d3f5 100644 --- a/src/Sentry/Internal/SdkComposer.cs +++ b/src/Sentry/Internal/SdkComposer.cs @@ -46,8 +46,11 @@ You can set a different environment via SENTRY_ENVIRONMENT env var or programati { _options.LogInfo("Connecting to Spotlight at {0}", _options.SpotlightUrl); } - - transport = new SpotlightHttpTransport(transport, _options, _options.GetHttpClient(), SystemClock.Clock); + if (!Uri.TryCreate(_options.SpotlightUrl, UriKind.Absolute, out var spotlightUrl)) + { + throw new InvalidOperationException("Invalid option for SpotlightUrl: " + _options.SpotlightUrl); + } + transport = new SpotlightHttpTransport(transport, _options, _options.GetHttpClient(), spotlightUrl, SystemClock.Clock); } // Always persist the transport on the options, so other places can pick it up where necessary. diff --git a/test/Sentry.Tests/Internals/Http/SpotlightTransportTests.cs b/test/Sentry.Tests/Internals/Http/SpotlightTransportTests.cs index 722caef016..0669280b4b 100644 --- a/test/Sentry.Tests/Internals/Http/SpotlightTransportTests.cs +++ b/test/Sentry.Tests/Internals/Http/SpotlightTransportTests.cs @@ -28,6 +28,7 @@ public async Task SendEnvelopeAsync_SpotlightRequestFailed_InnerTransportFailure DiagnosticLogger = logger }, new HttpClient(httpHandler), + new Uri("http://localhost:8969/stream"), Substitute.For()); var envelope = Envelope.FromEvent(new SentryEvent());