public SentryEvent Process(SentryEvent @event) { @event.Contexts["launch parameters"] = game.LaunchParameters; @event.SetTag("graphicsPlatform", $"{GraphicsDevice.Platform}"); @event.SetTag("processArch", $"{RuntimeInformation.ProcessArchitecture}"); @event.Release = GetVersion(); return(@event); }
private static void ConfigureServices(IServiceCollection services) { services.AddHttpClient <ISymbolClient, SymbolClient>() .AddPolicyHandler((s, r) => HttpPolicyExtensions.HandleTransientHttpError() .WaitAndRetryAsync(new[] { TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(2), TimeSpan.FromSeconds(5), #if RELEASE // TODO: Until a proper re-entrancy is built in the clients, add a last hope retry TimeSpan.FromSeconds(15) #endif }, onRetry: async(result, span, retryAttempt, context) => { var sentry = s.GetService <ISentryClient>(); var evt = new SentryEvent(result.Exception) { Level = SentryLevel.Warning, LogEntry = new LogEntry { Formatted = $"Waiting {span} following attempt {retryAttempt} failed HTTP request.", Message = "Waiting {span} following attempt {retryAttempt} failed HTTP request.", } }; evt.SetTag("Tag", "Polly"); if (result.Result is { } request) { const string traceIdKey = "TraceIdentifier"; if (request.Headers.TryGetValues(traceIdKey, out var traceIds)) { evt.SetTag(traceIdKey, traceIds.FirstOrDefault() ?? "unknown"); } evt.SetTag("StatusCode", request.StatusCode.ToString()); var responseBody = await request.Content.ReadAsStringAsync(); if (!string.IsNullOrWhiteSpace(responseBody)) { evt.SetExtra("body", responseBody); } } sentry.CaptureEvent(evt); } )); services.AddSingleton <Client>(); services.AddSingleton <ObjectFileParser>(); services.AddSingleton <ClientMetrics>(); services.AddSingleton <FatBinaryReader>(); services.AddSingleton <ClientMetrics>(); services.AddOptions <SymbolClientOptions>() .Configure <IConfiguration>((o, f) => f.Bind("SymbolClient", o)) .Validate(o => o.BaseAddress is {}, "BaseAddress is required.");
public SentryEvent Process(SentryEvent @event) { if (serviceContext.TryGetInstance <ExplorerContext>() is ExplorerContext context) { @event.SetTag("ColumnType", context.ColumnInfo.Type.ToString()); @event.SetExtra("ExplorationContext", context); } @event.SetTag("GitSha", ThisAssembly.Git.Sha); @event.SetTag("GitBranch", ThisAssembly.Git.Branch); return(@event); }
private static SentryEvent SentryBeforeSend(SentryEvent args) { #if DEBUG return(null); #else if (args.Exception?.TargetSite.Module.Assembly == Engine.ClassicAssembly) { return(null); } args.User = new User { Id = AssistantOptions.UserId }; args.SetTag("SessionId", AssistantOptions.SessionId); args.SetExtra("PlayerName", Engine.Player?.Name ?? "Unknown"); args.SetExtra("PlayerSerial", Engine.Player?.Serial ?? 0); args.SetExtra("Shard", Engine.CurrentShard?.Name ?? "Unknown"); args.SetExtra("ShardFeatures", Engine.Features.ToString()); args.SetExtra("CharacterListFlags", Engine.CharacterListFlags.ToString()); args.SetExtra("Connected", Engine.Connected); args.SetExtra("ClientVersion", Engine.ClientVersion == null ? "Unknown" : Engine.ClientVersion.ToString()); args.SetExtra("KeyboardLayout", InputLanguageManager.Current?.CurrentInputLanguage?.Name ?? "Unknown"); args.SetExtra("ClassicUO Version", Engine.ClassicAssembly?.GetName().Version.ToString() ?? "Unknown"); return(args); #endif }
private SentryEvent PrepareEvent(SentryEvent @event) { var scope = ScopeManagement.GetCurrent(); // TODO: Consider multiple events being sent with the same scope: // Wherever this code will end up, it should evaluate only once if (scope.States != null) { foreach (var state in scope.States) { if (state is string scopeString) { @event.SetTag("scope", scopeString); } else if (state is IEnumerable <KeyValuePair <string, string> > keyValStringString) { @event.SetTags(keyValStringString); } else if (state is IEnumerable <KeyValuePair <string, object> > keyValStringObject) { @event.SetTags(keyValStringObject.Select(k => new KeyValuePair <string, string>(k.Key, k.Value.ToString()))); } else { // TODO: possible callback invocation here @event.SetExtra("State of unknown type", state.GetType().ToString()); } } } @event = _options.BeforeSend?.Invoke(@event); return(@event); }
protected override void ProcessException( SpecialException exception, SentryEvent sentryEvent) { sentryEvent.AddBreadcrumb("Processor running on special exception."); sentryEvent.SetTag("IsSpecial", exception.IsSpecial.ToString()); }
/// <summary> /// Applies the default tags to an event without resetting existing tags. /// </summary> /// <param name="options">The options to read the default tags from.</param> /// <param name="event">The event to apply the tags to.</param> public static void ApplyDefaultTags(this SentryLoggingOptions options, SentryEvent @event) { foreach (var defaultTag in options.DefaultTags .Where(t => [email protected](t.Key, out _))) { @event.SetTag(defaultTag.Key, defaultTag.Value); } }
private static SentryEvent BeforeSend(SentryEvent arg) { var assemblypath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); var send = true; arg.SetTag("admin", App.IsAdministrator().ToString()); arg.SetTag("beta", (!string.IsNullOrWhiteSpace(LauncherContext.Instance.BetaVersion)).ToString()); try { try { if (!Directory.Exists($@"{assemblypath}\Logs\Hyddwn Launcher\Exceptions")) { Directory.CreateDirectory($@"{assemblypath}\Logs\Hyddwn Launcher\Exceptions"); } File.WriteAllText( $@"{assemblypath}\Logs\Hyddwn Launcher\Exceptions\Unhandled_Exception-{DateTime.Now:yyyy-MM-dd_hh-mm.fff}.log", string.Format(Resources.HyddwnLauncherVersion, 1) + string.Format(Resources.UnhandledExceptionFileSegmentException, arg.Exception)); } catch { Clipboard.SetText(arg.Exception.ToString()); MessageBox.Show( string.Format( Resources.UnhandledExceptionFileError, arg.Exception)); return(arg); } Log.Exception(arg.Exception, Resources.UnhandledExceptionFatalError); var exceptionReporter = new ExceptionReporter(arg.Exception); exceptionReporter.ShowDialog(); send = exceptionReporter.Result; } catch { return(arg); } return(send ? arg : null); }
public SentryEvent?Process(SentryEvent @event) { _metrics.SentryEventProcessed(); @event.SetTag("server-endpoint", _options.BaseAddress); @event.Contexts["SymbolServiceOptions"] = _options; @event.SetTag("cores", _cores); // In dev, ignore statsd errors if (_environment.IsDevelopment()) { if (@event.Exception is SocketException ex && ex.ToString().Contains("StatsD")) { return(null); } } return(@event); }
private static SentryEvent SentryEvent(SentryEvent e) { try { if (!string.IsNullOrEmpty(Settings.UserName) && Settings.UserId != 0) { e.User = new Sentry.Protocol.User() { Id = Settings.UserId.ToString(), Username = Settings.UserName }; } e.SetTag("Locale", Settings.Locale); e.SetTag("URI", uri); e.SetTag("MD5", md5Luancher); } catch { } return(e); }
public void Log <TState>( LogLevel logLevel, EventId eventId, TState state, Exception exception, Func <TState, Exception, string> formatter) { if (!IsEnabled(logLevel)) { return; } var message = formatter?.Invoke(state, exception); if (SendEvent(logLevel, eventId, exception)) { var @event = new SentryEvent(exception) { Logger = CategoryName, Message = message, Level = logLevel.ToSentryLevel() }; var tuple = eventId.ToTupleOrNull(); if (tuple.HasValue) { @event.SetTag(tuple.Value.name, tuple.Value.value); } _hub.CaptureEvent(@event); } // Even if it was sent as event, add breadcrumb so next event includes it if (_options.MinimumBreadcrumbLevel != LogLevel.None && logLevel >= _options.MinimumBreadcrumbLevel) { var data = eventId.ToDictionaryOrNull(); if (exception != null && message != null) { // Exception.Message won't be used as Breadcrumb message // Avoid losing it by adding as data: data = data ?? new Dictionary <string, string>(); data.Add("exception_message", exception.Message); } _hub.AddBreadcrumb( _clock, message ?? exception?.Message, CategoryName, type: null, data, logLevel.ToBreadcrumbLevel()); } }
public void Process(Exception exception, SentryEvent sentryEvent) { if (exception is UnityLogException ule) { // TODO: At this point the original (Mono+.NET stack trace factories already ran) // Ideally this strategy would fit into the SDK hooks, even though this parse gives not only // a stacktrace but also the exception message and type so currently can't be hooked into StackTraceFactory sentryEvent.SentryExceptions = new[] { ule.ToSentryException() }; sentryEvent.SetTag("source", "log"); } }
public static void FatalError(string message, Exception exception) { SentryEvent sentryEvent = new SentryEvent(exception) { Message = message, Level = Sentry.Protocol.SentryLevel.Fatal }; sentryEvent.SetTag("HRESULT", exception.HResult.ToString()); SentrySdk.CaptureEvent(sentryEvent); }
public void ApplyDefaultTags_TagInEvent_DoesNotOverrideTag() { const string key = "key"; const string expected = "event tag value"; var target = new SentryEvent(); target.SetTag(key, expected); _sut.DefaultTags[key] = "default value"; _sut.ApplyDefaultTags(target); Assert.Equal(expected, target.Tags[key]); }
private static SentryEvent SentryBeforeSend(SentryEvent args) { args.User = new User { Id = AssistantOptions.UserId }; args.SetTag("SessionId", AssistantOptions.SessionId); args.SetExtra("PlayerName", Engine.Player?.Name ?? "Unknown"); args.SetExtra("PlayerSerial", Engine.Player?.Serial ?? 0); args.SetExtra("Shard", Engine.CurrentShard?.Name ?? "Unknown"); args.SetExtra("Connected", Engine.Connected); return(args); }
public static void Error(string message, Exception exception, IEnumerable <KeyValuePair <string, string> > extraTags) { SentryEvent sentryEvent = new SentryEvent(exception) { Message = message, Level = Sentry.Protocol.SentryLevel.Error, }; if (extraTags != null) { sentryEvent.SetTags(extraTags); } sentryEvent.SetTag("HRESULT", exception.HResult.ToString()); SentrySdk.CaptureEvent(sentryEvent); }
private static SentryEvent SentryBeforeSend(SentryEvent args) { args.User = new User { Id = AssistantOptions.UserId }; args.SetTag("SessionId", AssistantOptions.SessionId); args.SetExtra("PlayerName", Engine.Player?.Name ?? "Unknown"); args.SetExtra("PlayerSerial", Engine.Player?.Serial ?? 0); args.SetExtra("Shard", Engine.CurrentShard?.Name ?? "Unknown"); args.SetExtra("ShardFeatures", Engine.Features.ToString()); args.SetExtra("Connected", Engine.Connected); args.SetExtra("ClientVersion", Engine.ClientVersion == null ? "Unknown" : Engine.ClientVersion.ToString()); args.SetExtra("KeyboardLayout", InputLanguageManager.Current?.CurrentInputLanguage?.Name ?? "Unknown"); return(args); }
// SentryException.Extra is not supported by Sentry yet. // Move the extras to the Event Extra while marking // by index the Exception which owns it. private static void MoveExceptionExtrasToEvent( SentryEvent sentryEvent, IReadOnlyList <SentryException> sentryExceptions) { for (var i = 0; i < sentryExceptions.Count; i++) { var sentryException = sentryExceptions[i]; if (sentryException.Data.Count <= 0) { continue; } foreach (var keyValue in sentryException.Data) { if (keyValue.Key.StartsWith("sentry:", StringComparison.OrdinalIgnoreCase) && keyValue.Value != null) { if (keyValue.Key.StartsWith(ExceptionDataTagKey, StringComparison.OrdinalIgnoreCase) && keyValue.Value is string tagValue && ExceptionDataTagKey.Length < keyValue.Key.Length) { // Set the key after the ExceptionDataTagKey string. sentryEvent.SetTag(keyValue.Key.Substring(ExceptionDataTagKey.Length), tagValue); } else if (keyValue.Key.StartsWith(ExceptionDataContextKey, StringComparison.OrdinalIgnoreCase) && ExceptionDataContextKey.Length < keyValue.Key.Length) { // Set the key after the ExceptionDataTagKey string. _ = sentryEvent.Contexts[keyValue.Key.Substring(ExceptionDataContextKey.Length)] = keyValue.Value; } else { sentryEvent.SetExtra($"Exception[{i}][{keyValue.Key}]", sentryException.Data[keyValue.Key]); } }
public override void Handle(ExceptionHandlerContext context) { ExceptionDispatchInfo info = ExceptionDispatchInfo.Capture(context.Exception); // Add any contextual data you want to the event: var @event = new SentryEvent(info.SourceException) { Request = new Request { Method = context.Request.Method.ToString(), Url = context.Request.RequestUri.AbsoluteUri, QueryString = context.Request.Headers.ToString() } }; @event.SetTag("http-version", context.Request.Version.ToString()); SentrySdk.CaptureEvent(@event); // Set the result: context.Result = new InternalServerErrorResult(context.Request); // Or re-throw if you want it to bubble up the middleware chain (Sentry removes duplicate captures) //info.Throw(); }
public void SerializeObject_AllPropertiesSetToNonDefault_SerializesValidObject() { var ex = new Exception("exception message"); var timestamp = DateTimeOffset.MaxValue; var id = Guid.Parse("4b780f4c-ec03-42a7-8ef8-a41c9d5621f8"); var sut = new SentryEvent(ex, timestamp, id) { User = new User { Id = "user-id" }, Request = new Request { Method = "POST" }, Contexts = new Contexts { ["context_key"] = "context_value", [".NET Framework"] = new Dictionary <string, string> { [".NET Framework"] = "\"v2.0.50727\", \"v3.0\", \"v3.5\"", [".NET Framework Client"] = "\"v4.8\", \"v4.0.0.0\"", [".NET Framework Full"] = "\"v4.8\"" } }, Sdk = new SdkVersion { Name = "SDK-test", Version = "1.1.1" }, Environment = "environment", Level = SentryLevel.Fatal, Logger = "logger", Message = new SentryMessage { Message = "message", Formatted = "structured_message" }, Modules = { { "module_key", "module_value" } }, Release = "release", SentryExceptions = new[] { new SentryException { Value = "exception_value" } }, SentryThreads = new[] { new SentryThread { Crashed = true } }, ServerName = "server_name", TransactionName = "transaction", }; sut.Sdk.AddPackage(new Package("name", "version")); sut.AddBreadcrumb(new Breadcrumb(timestamp, "crumb")); sut.AddBreadcrumb(new Breadcrumb( timestamp, "message", "type", new Dictionary <string, string> { { "data-key", "data-value" } }, "category", BreadcrumbLevel.Warning)); sut.SetExtra("extra_key", "extra_value"); sut.Fingerprint = new[] { "fingerprint" }; sut.SetTag("tag_key", "tag_value"); var actualString = sut.ToJsonString(); var actual = SentryEvent.FromJson(Json.Parse(actualString)); actual.Should().BeEquivalentTo(sut, o => { // Due to timestamp precision o.Excluding(e => e.Breadcrumbs); o.Excluding(e => e.Exception); return(o); }); // Expected item[0].Timestamp to be <9999-12-31 23:59:59.9999999>, but found <9999-12-31 23:59:59.999>. actual.Breadcrumbs.Should().BeEquivalentTo(sut.Breadcrumbs, o => o.Excluding(b => b.Timestamp)); var counter = 0; foreach (var sutBreadcrumb in sut.Breadcrumbs) { sutBreadcrumb.Timestamp.Should().BeCloseTo(actual.Breadcrumbs.ElementAt(counter++).Timestamp); } }
public void Log <TState>( LogLevel logLevel, EventId eventId, TState state, Exception exception, Func <TState, Exception, string> formatter) { if (!IsEnabled(logLevel)) { return; } var message = formatter?.Invoke(state, exception); if (_options.MinimumEventLevel != LogLevel.None && logLevel >= _options.MinimumEventLevel) { var @event = new SentryEvent(exception) { Logger = CategoryName, }; if (message != null) { // TODO: this will override the current message // which could have been set from reading Exception.Message if (@event.Message != null) { @event.SetExtra("original_message", @event.Message); } @event.Message = message; } var tuple = eventId.ToTupleOrNull(); if (tuple.HasValue) { @event.SetTag(tuple.Value.name, tuple.Value.value); } _sentryClient.CaptureEvent(@event); } // Even if it was sent as event, add breadcrumb so next event includes it if (_options.MinimumBreadcrumbLevel != LogLevel.None && logLevel >= _options.MinimumBreadcrumbLevel) { var data = eventId.ToDictionaryOrNull(); if (exception != null && message != null) { // Exception.Message won't be used as Breadcrumb message // Avoid losing it by adding as data: data = data ?? new Dictionary <string, string>(); data.Add("exception_message", exception.Message); } _sentryClient.AddBreadcrumb( _clock, message ?? exception?.Message, "default", CategoryName, data, logLevel.ToBreadcrumbLevel()); } }
public void SerializeObject_AllPropertiesSetToNonDefault_SerializesValidObject() { var ex = new Exception("exception message"); var timestamp = DateTimeOffset.MaxValue; var id = Guid.Parse("4b780f4c-ec03-42a7-8ef8-a41c9d5621f8"); var sut = new SentryEvent(ex, timestamp, id) { User = new User { Id = "user-id" }, Request = new Request { Method = "POST" }, Contexts = new Contexts { ["context_key"] = "context_value", [".NET Framework"] = new Dictionary <string, string> { [".NET Framework"] = "\"v2.0.50727\", \"v3.0\", \"v3.5\"", [".NET Framework Client"] = "\"v4.8\", \"v4.0.0.0\"", [".NET Framework Full"] = "\"v4.8\"" } }, Sdk = new SdkVersion { Name = "SDK-test", Version = "1.1.1" }, Environment = "environment", Level = SentryLevel.Fatal, Logger = "logger", Message = new SentryMessage { Message = "message", Formatted = "structured_message" }, Modules = { { "module_key", "module_value" } }, Release = "release", SentryExceptions = new[] { new SentryException { Value = "exception_value" } }, SentryThreads = new[] { new SentryThread { Crashed = true } }, ServerName = "server_name", TransactionName = "transaction", }; sut.Sdk.AddPackage(new Package("name", "version")); sut.AddBreadcrumb(new Breadcrumb(timestamp, "crumb")); sut.AddBreadcrumb(new Breadcrumb( timestamp, "message", "type", new Dictionary <string, string> { { "data-key", "data-value" } }, "category", BreadcrumbLevel.Warning)); sut.SetExtra("extra_key", "extra_value"); sut.Fingerprint = new[] { "fingerprint" }; sut.SetTag("tag_key", "tag_value"); var actualString = sut.ToJsonString(); var actual = SentryEvent.FromJson(Json.Parse(actualString)); // Assert actual.Should().BeEquivalentTo(sut, o => { // Exceptions are not deserialized o.Excluding(x => x.Exception); // Timestamps lose some precision when writing to JSON o.Using <DateTimeOffset>(ctx => ctx.Subject.Should().BeCloseTo(ctx.Expectation, TimeSpan.FromMilliseconds(1)) ).WhenTypeIs <DateTimeOffset>(); return(o); }); }
public SentryEvent Process(SentryEvent @event) { @event.SetTag("server-endpoint", _options.BaseAddress); @event.Contexts["SymbolServiceOptions"] = _options; return(@event); }
protected override void ProcessException(ArgumentException exception, SentryEvent sentryEvent) { // Handle specific types of exceptions and add more data to the event sentryEvent.SetTag("parameter-name", exception.ParamName); }
public void Log <TState>( LogLevel logLevel, EventId eventId, TState state, Exception exception, Func <TState, Exception, string> formatter) { if (!IsEnabled(logLevel)) { return; } var message = formatter?.Invoke(state, exception); if (ShouldCaptureEvent(logLevel, eventId, exception)) { var @event = new SentryEvent(exception) { Logger = CategoryName, Message = message, Level = logLevel.ToSentryLevel() }; if (state is IEnumerable <KeyValuePair <string, object> > pairs) { foreach (var property in pairs) { if (property.Key == "{OriginalFormat}" && property.Value is string template) { // Original format found, use Sentry logEntry interface @event.Message = null; @event.LogEntry = new LogEntry { Formatted = message, Message = template }; continue; } if (property.Value is string tagValue) { @event.SetTag(property.Key, tagValue); } } } var tuple = eventId.ToTupleOrNull(); if (tuple.HasValue) { @event.SetTag(tuple.Value.name, tuple.Value.value); } _hub.CaptureEvent(@event); } // Even if it was sent as event, add breadcrumb so next event includes it if (ShouldAddBreadcrumb(logLevel, eventId, exception)) { var data = eventId.ToDictionaryOrNull(); if (exception != null && message != null) { // Exception.Message won't be used as Breadcrumb message // Avoid losing it by adding as data: data = data ?? new Dictionary <string, string>(); data.Add("exception_message", exception.Message); } _hub.AddBreadcrumb( _clock, message ?? exception?.Message, CategoryName, type: null, data, logLevel.ToBreadcrumbLevel()); } }
public async Task Roundtrip_WithEvent_Success() { // Arrange var ex = new Exception("exception message"); var timestamp = DateTimeOffset.MaxValue; var id = Guid.Parse("4b780f4c-ec03-42a7-8ef8-a41c9d5621f8"); var @event = new SentryEvent(ex, timestamp, id) { User = new User { Id = "user-id" }, Request = new Request { Method = "POST" }, Contexts = new Contexts { ["context_key"] = "context_value" }, Sdk = new SdkVersion { Name = "SDK-test", Version = "1.0.0" }, Environment = "environment", Level = SentryLevel.Fatal, Logger = "logger", Message = new SentryMessage { Message = "message", Formatted = "structured_message" }, Modules = { { "module_key", "module_value" } }, Release = "release", SentryExceptions = new[] { new SentryException { Value = "exception_value" } }, SentryThreads = new[] { new SentryThread { Crashed = true } }, ServerName = "server_name", TransactionName = "transaction", }; @event.SetExtra("extra_key", "extra_value"); @event.Fingerprint = new[] { "fingerprint" }; @event.SetTag("tag_key", "tag_value"); using var envelope = Envelope.FromEvent(@event); #if !NET461 && !NETCOREAPP2_1 await #endif using var stream = new MemoryStream(); // Act await envelope.SerializeAsync(stream); stream.Seek(0, SeekOrigin.Begin); using var envelopeRoundtrip = await Envelope.DeserializeAsync(stream); // Assert // Can't compare the entire object graph because output envelope contains evaluated length, // which original envelope doesn't have. envelopeRoundtrip.Header.Should().BeEquivalentTo(envelope.Header); envelopeRoundtrip.Items.Should().ContainSingle(); var payloadContent = (envelopeRoundtrip.Items[0].Payload as JsonSerializable)?.Source; payloadContent.Should().BeEquivalentTo(@event, o => o.Excluding(x => x.Exception)); }