mirror of
https://github.com/ppy/osu.git
synced 2025-02-15 11:43:21 +08:00
Merge pull request #18205 from peppy/sentry-setup
Bring sentry usage up-to-date
This commit is contained in:
commit
a6674d8c1a
@ -23,7 +23,6 @@ using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Scoring;
|
||||
using osu.Game.Screens.Menu;
|
||||
using osu.Game.Skinning;
|
||||
using osu.Game.Utils;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Navigation
|
||||
{
|
||||
@ -33,7 +32,6 @@ namespace osu.Game.Tests.Visual.Navigation
|
||||
private IReadOnlyList<Type> requiredGameDependencies => new[]
|
||||
{
|
||||
typeof(OsuGame),
|
||||
typeof(SentryLogger),
|
||||
typeof(OsuLogo),
|
||||
typeof(IdleTracker),
|
||||
typeof(OnScreenDisplay),
|
||||
|
@ -258,7 +258,7 @@ namespace osu.Game
|
||||
{
|
||||
dependencies.CacheAs(this);
|
||||
|
||||
dependencies.Cache(SentryLogger);
|
||||
SentryLogger.AttachUser(API.LocalUser);
|
||||
|
||||
dependencies.Cache(osuLogo = new OsuLogo { Alpha = 0 });
|
||||
|
||||
|
@ -1,11 +1,17 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
#nullable enable
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Logging;
|
||||
using osu.Game.Online.API.Requests.Responses;
|
||||
using Sentry;
|
||||
using Sentry.Protocol;
|
||||
|
||||
namespace osu.Game.Utils
|
||||
{
|
||||
@ -14,26 +20,43 @@ namespace osu.Game.Utils
|
||||
/// </summary>
|
||||
public class SentryLogger : IDisposable
|
||||
{
|
||||
private SentryClient sentry;
|
||||
private Scope sentryScope;
|
||||
private Exception lastException;
|
||||
private IBindable<APIUser>? localUser;
|
||||
|
||||
private readonly IDisposable? sentrySession;
|
||||
|
||||
public SentryLogger(OsuGame game)
|
||||
{
|
||||
if (!game.IsDeployedBuild) return;
|
||||
|
||||
var options = new SentryOptions
|
||||
sentrySession = SentrySdk.Init(options =>
|
||||
{
|
||||
Dsn = "https://ad9f78529cef40ac874afb95a9aca04e@sentry.ppy.sh/2",
|
||||
Release = game.Version
|
||||
};
|
||||
// Not setting the dsn will completely disable sentry.
|
||||
if (game.IsDeployedBuild)
|
||||
options.Dsn = "https://ad9f78529cef40ac874afb95a9aca04e@sentry.ppy.sh/2";
|
||||
|
||||
sentry = new SentryClient(options);
|
||||
sentryScope = new Scope(options);
|
||||
options.AutoSessionTracking = true;
|
||||
options.IsEnvironmentUser = false;
|
||||
options.Release = game.Version;
|
||||
});
|
||||
|
||||
Logger.NewEntry += processLogEntry;
|
||||
}
|
||||
|
||||
~SentryLogger() => Dispose(false);
|
||||
|
||||
public void AttachUser(IBindable<APIUser> user)
|
||||
{
|
||||
Debug.Assert(localUser == null);
|
||||
|
||||
localUser = user.GetBoundCopy();
|
||||
localUser.BindValueChanged(u =>
|
||||
{
|
||||
SentrySdk.ConfigureScope(scope => scope.User = new User
|
||||
{
|
||||
Username = u.NewValue.Username,
|
||||
Id = u.NewValue.Id.ToString(),
|
||||
});
|
||||
}, true);
|
||||
}
|
||||
|
||||
private void processLogEntry(LogEntry entry)
|
||||
{
|
||||
if (entry.Level < LogLevel.Verbose) return;
|
||||
@ -44,14 +67,77 @@ namespace osu.Game.Utils
|
||||
{
|
||||
if (!shouldSubmitException(exception)) return;
|
||||
|
||||
// since we let unhandled exceptions go ignored at times, we want to ensure they don't get submitted on subsequent reports.
|
||||
if (lastException != null && lastException.Message == exception.Message && exception.StackTrace.StartsWith(lastException.StackTrace, StringComparison.Ordinal)) return;
|
||||
// framework does some weird exception redirection which means sentry does not see unhandled exceptions using its automatic methods.
|
||||
// but all unhandled exceptions still arrive via this pathway. we just need to mark them as unhandled for tagging purposes.
|
||||
// easiest solution is to check the message matches what the framework logs this as.
|
||||
// see https://github.com/ppy/osu-framework/blob/f932f8df053f0011d755c95ad9a2ed61b94d136b/osu.Framework/Platform/GameHost.cs#L336
|
||||
bool wasUnhandled = entry.Message == @"An unhandled error has occurred.";
|
||||
bool wasUnobserved = entry.Message == @"An unobserved error has occurred.";
|
||||
|
||||
lastException = exception;
|
||||
sentry.CaptureEvent(new SentryEvent(exception) { Message = entry.Message }, sentryScope);
|
||||
if (wasUnobserved)
|
||||
{
|
||||
// see https://github.com/getsentry/sentry-dotnet/blob/c6a660b1affc894441c63df2695a995701671744/src/Sentry/Integrations/TaskUnobservedTaskExceptionIntegration.cs#L39
|
||||
exception.Data[Mechanism.MechanismKey] = @"UnobservedTaskException";
|
||||
}
|
||||
|
||||
if (wasUnhandled)
|
||||
{
|
||||
// see https://github.com/getsentry/sentry-dotnet/blob/main/src/Sentry/Integrations/AppDomainUnhandledExceptionIntegration.cs#L38-L39
|
||||
exception.Data[Mechanism.MechanismKey] = @"AppDomain.UnhandledException";
|
||||
}
|
||||
|
||||
exception.Data[Mechanism.HandledKey] = !wasUnhandled;
|
||||
|
||||
SentrySdk.CaptureEvent(new SentryEvent(exception)
|
||||
{
|
||||
Message = entry.Message,
|
||||
Level = getSentryLevel(entry.Level),
|
||||
});
|
||||
}
|
||||
else
|
||||
sentryScope.AddBreadcrumb(DateTimeOffset.Now, entry.Message, entry.Target.ToString(), "navigation");
|
||||
SentrySdk.AddBreadcrumb(entry.Message, entry.Target.ToString(), "navigation", level: getBreadcrumbLevel(entry.Level));
|
||||
}
|
||||
|
||||
private BreadcrumbLevel getBreadcrumbLevel(LogLevel entryLevel)
|
||||
{
|
||||
switch (entryLevel)
|
||||
{
|
||||
case LogLevel.Debug:
|
||||
return BreadcrumbLevel.Debug;
|
||||
|
||||
case LogLevel.Verbose:
|
||||
return BreadcrumbLevel.Info;
|
||||
|
||||
case LogLevel.Important:
|
||||
return BreadcrumbLevel.Warning;
|
||||
|
||||
case LogLevel.Error:
|
||||
return BreadcrumbLevel.Error;
|
||||
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException(nameof(entryLevel), entryLevel, null);
|
||||
}
|
||||
}
|
||||
|
||||
private SentryLevel getSentryLevel(LogLevel entryLevel)
|
||||
{
|
||||
switch (entryLevel)
|
||||
{
|
||||
case LogLevel.Debug:
|
||||
return SentryLevel.Debug;
|
||||
|
||||
case LogLevel.Verbose:
|
||||
return SentryLevel.Info;
|
||||
|
||||
case LogLevel.Important:
|
||||
return SentryLevel.Warning;
|
||||
|
||||
case LogLevel.Error:
|
||||
return SentryLevel.Error;
|
||||
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException(nameof(entryLevel), entryLevel, null);
|
||||
}
|
||||
}
|
||||
|
||||
private bool shouldSubmitException(Exception exception)
|
||||
@ -93,8 +179,7 @@ namespace osu.Game.Utils
|
||||
protected virtual void Dispose(bool isDisposing)
|
||||
{
|
||||
Logger.NewEntry -= processLogEntry;
|
||||
sentry = null;
|
||||
sentryScope = null;
|
||||
sentrySession?.Dispose();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
Loading…
Reference in New Issue
Block a user