diff --git a/osu.Desktop/Program.cs b/osu.Desktop/Program.cs
index cc08e08653..19ceade644 100644
--- a/osu.Desktop/Program.cs
+++ b/osu.Desktop/Program.cs
@@ -4,7 +4,10 @@
using System;
using System.IO;
using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
using osu.Framework;
+using osu.Framework.Logging;
using osu.Framework.Platform;
using osu.Game.IPC;
@@ -20,6 +23,8 @@ namespace osu.Desktop
using (DesktopGameHost host = Host.GetSuitableHost(@"osu", true))
{
+ host.ExceptionThrown += handleException;
+
if (!host.IsPrimaryInstance)
{
var importer = new ArchiveImportIPCChannel(host);
@@ -45,5 +50,22 @@ namespace osu.Desktop
return 0;
}
}
+
+ private static int allowableExceptions = 1;
+
+ ///
+ /// Allow a maximum of one unhandled exception, per second of execution.
+ ///
+ ///
+ ///
+ private static bool handleException(Exception arg)
+ {
+ bool continueExecution = Interlocked.Decrement(ref allowableExceptions) >= 0;
+
+ Logger.Log($"Unhandled exception has been {(continueExecution ? "allowed" : "denied")} with {allowableExceptions} more allowable exceptions.");
+
+ Task.Delay(1000).ContinueWith(_ => Interlocked.Increment(ref allowableExceptions));
+ return continueExecution;
+ }
}
}
diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs
index a1e385921f..6576529326 100644
--- a/osu.Game/OsuGame.cs
+++ b/osu.Game/OsuGame.cs
@@ -36,6 +36,8 @@ using osu.Game.Skinning;
using OpenTK.Graphics;
using osu.Game.Overlays.Volume;
using osu.Game.Screens.Select;
+using osu.Game.Utils;
+using LogLevel = osu.Framework.Logging.LogLevel;
namespace osu.Game
{
@@ -65,6 +67,8 @@ namespace osu.Game
private ScreenshotManager screenshotManager;
+ protected RavenLogger RavenLogger;
+
public virtual Storage GetStorageForStableInstall() => null;
private Intro intro
@@ -108,6 +112,8 @@ namespace osu.Game
this.args = args;
forwardLoggedErrorsToNotifications();
+
+ RavenLogger = new RavenLogger(this);
}
public void ToggleSettings() => settings.ToggleVisibility();
@@ -152,6 +158,8 @@ namespace osu.Game
dependencies.CacheAs(this);
+ dependencies.Cache(RavenLogger);
+
dependencies.CacheAs(ruleset);
dependencies.CacheAs>(ruleset);
@@ -273,6 +281,12 @@ namespace osu.Game
menu.Push(new PlayerLoader(new ReplayPlayer(s.Replay)));
}
+ protected override void Dispose(bool isDisposing)
+ {
+ base.Dispose(isDisposing);
+ RavenLogger.Dispose();
+ }
+
protected override void LoadComplete()
{
// this needs to be cached before base.LoadComplete as it is used by MenuCursorContainer.
@@ -449,7 +463,7 @@ namespace osu.Game
Schedule(() => notifications.Post(new SimpleNotification
{
Icon = entry.Level == LogLevel.Important ? FontAwesome.fa_exclamation_circle : FontAwesome.fa_bomb,
- Text = entry.Message,
+ Text = entry.Message + (entry.Exception != null ? "\n\nThis error has been automatically reported to the devs." : string.Empty),
}));
}
else if (recentLogCount == short_term_display_limit)
@@ -601,6 +615,7 @@ namespace osu.Game
private void screenAdded(Screen newScreen)
{
currentScreen = (OsuScreen)newScreen;
+ Logger.Log($"Screen changed → {currentScreen}");
newScreen.ModePushed += screenAdded;
newScreen.Exited += screenRemoved;
@@ -609,6 +624,7 @@ namespace osu.Game
private void screenRemoved(Screen newScreen)
{
currentScreen = (OsuScreen)newScreen;
+ Logger.Log($"Screen changed ← {currentScreen}");
if (newScreen == null)
Exit();
diff --git a/osu.Game/Utils/RavenLogger.cs b/osu.Game/Utils/RavenLogger.cs
new file mode 100644
index 0000000000..aadb70add8
--- /dev/null
+++ b/osu.Game/Utils/RavenLogger.cs
@@ -0,0 +1,73 @@
+// Copyright (c) 2007-2018 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using System;
+using System.Collections.Generic;
+using System.Threading.Tasks;
+using osu.Framework.Logging;
+using SharpRaven;
+using SharpRaven.Data;
+
+namespace osu.Game.Utils
+{
+ ///
+ /// Report errors to sentry.
+ ///
+ public class RavenLogger : IDisposable
+ {
+ private readonly RavenClient raven = new RavenClient("https://5e342cd55f294edebdc9ad604d28bbd3@sentry.io/1255255");
+
+ private readonly List tasks = new List();
+
+ public RavenLogger(OsuGame game)
+ {
+ raven.Release = game.Version;
+
+ Logger.NewEntry += entry =>
+ {
+ if (entry.Level < LogLevel.Verbose) return;
+
+ if (entry.Exception != null)
+ queuePendingTask(raven.CaptureAsync(new SentryEvent(entry.Exception)));
+ else
+ raven.AddTrail(new Breadcrumb(entry.Target.ToString(), BreadcrumbType.Navigation) { Message = entry.Message });
+ };
+ }
+
+ private void queuePendingTask(Task task)
+ {
+ lock (tasks) tasks.Add(task);
+ task.ContinueWith(_ =>
+ {
+ lock (tasks)
+ tasks.Remove(task);
+ });
+ }
+
+ #region Disposal
+
+ ~RavenLogger()
+ {
+ Dispose(false);
+ }
+
+ public void Dispose()
+ {
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+
+ private bool isDisposed;
+
+ protected virtual void Dispose(bool isDisposing)
+ {
+ if (isDisposed)
+ return;
+
+ isDisposed = true;
+ lock (tasks) Task.WaitAll(tasks.ToArray(), 5000);
+ }
+
+ #endregion
+ }
+}
diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj
index eef586fd4c..da17500128 100644
--- a/osu.Game/osu.Game.csproj
+++ b/osu.Game/osu.Game.csproj
@@ -21,6 +21,7 @@
+
\ No newline at end of file