195 lines
7.4 KiB
C#
195 lines
7.4 KiB
C#
#nullable enable
|
|
using System.IO;
|
|
using System.Text;
|
|
using System.Text.Json;
|
|
using ImGuiNET;
|
|
using Sentry;
|
|
using T3.Core.Animation;
|
|
using T3.Core.SystemUi;
|
|
using T3.Core.Utils;
|
|
using T3.Editor.Compilation;
|
|
using T3.Editor.Gui.AutoBackup;
|
|
using T3.Editor.Gui.UiHelpers;
|
|
using T3.Editor.SystemUi;
|
|
using T3.Editor.UiModel;
|
|
using T3.Editor.UiModel.Commands;
|
|
using T3.Editor.UiModel.Modification;
|
|
using T3.Editor.UiModel.ProjectHandling;
|
|
|
|
namespace T3.Editor;
|
|
|
|
internal static class CrashReporting
|
|
{
|
|
public static void InitializeCrashReporting()
|
|
{
|
|
SentrySdk.Init(o =>
|
|
{
|
|
// Tells which project in Sentry to send events to:
|
|
o.Dsn = "https://52e37e10dc9cebcc2328cc63fab57211@o4505681078059008.ingest.sentry.io/4505681082384384";
|
|
o.Debug = false;
|
|
o.TracesSampleRate = 0.0;
|
|
o.IsGlobalModeEnabled = true;
|
|
o.SendClientReports = false;
|
|
o.AutoSessionTracking = false;
|
|
o.SendDefaultPii = false;
|
|
o.Release = Program.Version.ToBasicVersionString();
|
|
o.SetBeforeSend(CrashHandler);
|
|
});
|
|
|
|
//SentrySdk.ConfigureScope(scope => { scope.SetTag("IsStandAlone", Program.IsStandAlone ? "Yes" : "No"); });
|
|
|
|
// ReSharper disable once RedundantAssignment
|
|
var configuration = "Release";
|
|
#if DEBUG
|
|
configuration = "Debug";
|
|
#endif
|
|
|
|
SentrySdk.ConfigureScope(scope => { scope.SetTag("Configuration", configuration); });
|
|
}
|
|
|
|
private static SentryEvent? CrashHandler(SentryEvent sentryEvent, SentryHint hint)
|
|
{
|
|
// Aggregate exception normally don't cause crashes
|
|
if (sentryEvent.Exception is AggregateException)
|
|
return null;
|
|
|
|
var components = ProjectView.Focused;
|
|
|
|
sentryEvent.SetTag("Nickname", UserSettings.Config.UserName);
|
|
sentryEvent.Contexts["tooll3"] = new
|
|
{
|
|
UndoStack = UndoRedoStack.GetUndoStackAsString(),
|
|
Selection = components == null ? string.Empty : string.Join("\n", components.NodeSelection),
|
|
Nickname = "",
|
|
RuntimeSeconds = Playback.RunTimeInSecs,
|
|
RuntimeFrames = ImGui.GetFrameCount(),
|
|
UndoActions = UndoRedoStack.UndoStack.Count,
|
|
};
|
|
|
|
string? json = null;
|
|
try
|
|
{
|
|
var primaryComposition = ProjectView.Focused?.CompositionInstance;
|
|
if (primaryComposition != null)
|
|
{
|
|
var compositionUi = primaryComposition.Symbol.GetSymbolUi();
|
|
GraphOperations.TryCopyNodesAsJson(primaryComposition,
|
|
compositionUi.ChildUis.Values,
|
|
compositionUi.Sections.Values.ToList(),
|
|
out json);
|
|
}
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
sentryEvent.SetExtra("CurrentOpExportFailed", e.Message);
|
|
}
|
|
|
|
WriteCrashReportFile(sentryEvent);
|
|
|
|
// We only show crash report dialog in release mode
|
|
|
|
#if RELEASE
|
|
var inReleaseMode = true;
|
|
#else
|
|
var inReleaseMode = false;
|
|
#endif
|
|
|
|
if (inReleaseMode)
|
|
{
|
|
var lastBackupTime = AutoBackup.GetTimeOfLastBackup().GetReadableRelativeTime();
|
|
|
|
var message = $"""
|
|
TiXL crashed. We're really sorry.
|
|
|
|
The last backup was saved {lastBackupTime}.
|
|
Backups live inside each user project at <project>/{AutoBackup.BackupSubFolder}/.
|
|
|
|
Please refer to Help > Using Backups on what to do next.
|
|
""";
|
|
|
|
if (json != null)
|
|
{
|
|
message += """
|
|
|
|
When this window closes, the current operator will be copied to your clipboard.
|
|
""";
|
|
}
|
|
|
|
message += "\n\n" + (sentryEvent.Exception?.ToString() ?? Environment.StackTrace);
|
|
|
|
const string confirmation = "Send crash report (it really helps!)";
|
|
var result = BlockingWindow.Instance.ShowMessageBox(message,
|
|
@"☠🙈 Damn!",
|
|
confirmation,
|
|
"No thanks");
|
|
|
|
if (json != null)
|
|
{
|
|
EditorUi.Instance.SetClipboardText(json);
|
|
}
|
|
|
|
var sendingEnabled = result == confirmation;
|
|
|
|
if (!string.IsNullOrWhiteSpace(LogPath))
|
|
{
|
|
CoreUi.Instance.OpenWithDefaultApplication(LogPath);
|
|
}
|
|
|
|
return sendingEnabled ? sentryEvent : null;
|
|
}
|
|
else
|
|
{
|
|
WriteReportToLog(sentryEvent, false);
|
|
CoreUi.Instance.SetUnhandledExceptionMode(true);
|
|
return null;
|
|
}
|
|
}
|
|
|
|
private static void WriteReportToLog(SentryEvent sentryEvent, bool sendingEnabled)
|
|
{
|
|
// Write formatted stacktrace for readability
|
|
if (sentryEvent.Exception != null)
|
|
{
|
|
Log.Warning($"{sentryEvent.Exception.Message}\n{sentryEvent.Exception}");
|
|
}
|
|
|
|
// Dump report as json
|
|
using var stream = new MemoryStream();
|
|
using var writer = new Utf8JsonWriter(stream, new JsonWriterOptions { Indented = true });
|
|
|
|
sentryEvent.WriteTo(writer, null);
|
|
writer.Flush();
|
|
|
|
var eventJson = Encoding.UTF8.GetString(stream.ToArray());
|
|
Log.Warning($"Encountered crash reported:{sendingEnabled}\n" + eventJson);
|
|
|
|
// Force writing
|
|
FileWriter.Flush();
|
|
}
|
|
|
|
/** Additional to logging the crash we also write a copy to a dedicated crash file. */
|
|
private static void WriteCrashReportFile(SentryEvent? sentryEvent)
|
|
{
|
|
if (sentryEvent?.Exception == null || FileWriter.Instance == null)
|
|
return;
|
|
|
|
var exceptionTitle = sentryEvent.Exception.GetType().Name;
|
|
|
|
var filepath = Path.Combine(FileWriter.Instance.LogDirectory, $"crash {DateTime.Now:yyyy-MM-dd HH-mm-ss} - {exceptionTitle}.txt");
|
|
|
|
using var streamFileWriter = new StreamWriter(filepath);
|
|
streamFileWriter.WriteLine($"{sentryEvent.Exception.Message}\n{sentryEvent.Exception}");
|
|
|
|
using var memoryStream = new MemoryStream();
|
|
using var jsonWriter = new Utf8JsonWriter(memoryStream, new JsonWriterOptions { Indented = true });
|
|
|
|
sentryEvent.WriteTo(jsonWriter, null);
|
|
jsonWriter.Flush();
|
|
|
|
streamFileWriter.WriteLine(Encoding.UTF8.GetString(memoryStream.ToArray()));
|
|
streamFileWriter.Flush();
|
|
}
|
|
|
|
// ReSharper disable once UnusedMember.Global
|
|
public static string? LogPath { get; set; }
|
|
} |