diff --git a/GlitchLog/src/DebugLogger.bf b/GlitchLog/src/DebugLogger.bf index 3c36a86..402217c 100644 --- a/GlitchLog/src/DebugLogger.bf +++ b/GlitchLog/src/DebugLogger.bf @@ -5,34 +5,6 @@ using internal GlitchLog; namespace GlitchLog { - public abstract class Logger - { - protected LogLevel _logLevel; - - public LogLevel Level - { - get => _logLevel; - set => _logLevel = value; - } - - public abstract String Name {get; set;} - - public abstract void Trace(StringView format, params Object[] args); - public abstract void Info(StringView format, params Object[] args); - public abstract void Warning(StringView format, params Object[] args); - public abstract void Error(StringView format, params Object[] args); - public abstract void Critical(StringView format, params Object[] args); - - public abstract void Assert(bool condition, String error = Compiler.CallerExpression[0], String filePath = Compiler.CallerFilePath, int line = Compiler.CallerLineNum); - -#if !DEBUG - [SkipCall] -#endif - public abstract void AssertDebug(bool condition, String error = Compiler.CallerExpression[0], String filePath = Compiler.CallerFilePath, int line = Compiler.CallerLineNum); - - public abstract void Log(LogLevel level, StringView format, params Object[] args); - } - public class DebugLogger : Logger { // {l} = log level (first parameter) diff --git a/GlitchLog/src/Logger.bf b/GlitchLog/src/Logger.bf new file mode 100644 index 0000000..bba8f77 --- /dev/null +++ b/GlitchLog/src/Logger.bf @@ -0,0 +1,31 @@ +using System; + +namespace GlitchLog; + +public abstract class Logger +{ + protected LogLevel _logLevel; + + public LogLevel Level + { + get => _logLevel; + set => _logLevel = value; + } + + public abstract String Name {get; set;} + + public abstract void Trace(StringView format, params Object[] args); + public abstract void Info(StringView format, params Object[] args); + public abstract void Warning(StringView format, params Object[] args); + public abstract void Error(StringView format, params Object[] args); + public abstract void Critical(StringView format, params Object[] args); + + public abstract void Assert(bool condition, String error = Compiler.CallerExpression[0], String filePath = Compiler.CallerFilePath, int line = Compiler.CallerLineNum); + +#if !DEBUG + [SkipCall] +#endif + public abstract void AssertDebug(bool condition, String error = Compiler.CallerExpression[0], String filePath = Compiler.CallerFilePath, int line = Compiler.CallerLineNum); + + public abstract void Log(LogLevel level, StringView format, params Object[] args); +} diff --git a/GlitchyEditor/src/EditWindows/LogWindow.bf b/GlitchyEditor/src/EditWindows/LogWindow.bf index 85f86be..b312c93 100644 --- a/GlitchyEditor/src/EditWindows/LogWindow.bf +++ b/GlitchyEditor/src/EditWindows/LogWindow.bf @@ -6,6 +6,7 @@ using GlitchyEngine.World; using GlitchLog; using GlitchyEngine.Scripting; using GlitchyEngine.Renderer; +using GlitchyEngine; namespace GlitchyEditor.EditWindows; @@ -38,8 +39,7 @@ enum MessageType class MessageSource { public UUID? Entity = null; - public StringView? ScriptName = null; - public int? Line = null; + public MessageOrigin MessageOrigin = null ~ delete _; /// If true, the message is only meant for engine developers... so only me :( public bool IsEngineMessage = false; @@ -258,53 +258,66 @@ class LogWindow : EditorWindow ImGui.TableNextColumn(); - // Timestamp - ImGui.TextWrapped($"[{message.Timestamp:HH:mm:ss.fff}]"); - - if (message.Source.IsEngineMessage) + if (ImGui.BeginChild("Message", .Zero, .None, .None)) { - ImGui.SameLine(); - ImGui.TextUnformatted("Engine"); - } + // Timestamp + ImGui.TextWrapped($"[{message.Timestamp:HH:mm:ss.fff}]"); - // Show entity - if (message.Source?.Entity != null) - { - ImGui.SameLine(); - - Result entity = Editor.Instance.CurrentScene.GetEntityByID(message.Source.Entity.Value); - - if (entity case .Ok(let e)) + if (message.Source.IsEngineMessage) { - ImGui.Text($"Entity: \"{e.Name}\" (ID: {message.Source.Entity})"); + ImGui.SameLine(); + ImGui.TextUnformatted("Engine"); + } - if (ImGui.IsItemClicked()) - Editor.Instance.EntityHierarchyWindow.HighlightEntity(entity); + // Show entity + if (message.Source?.Entity != null) + { + ImGui.SameLine(); + + Result entity = Editor.Instance.CurrentScene.GetEntityByID(message.Source.Entity.Value); + + if (entity case .Ok(let e)) + { + ImGui.Text($"Entity: \"{e.Name}\" (ID: {message.Source.Entity})"); + + if (ImGui.IsItemClicked()) + Editor.Instance.EntityHierarchyWindow.HighlightEntity(entity); + } + else + { + ImGui.Text($"Entity: (ID: {message.Source.Entity})"); + } + } + + if (message.Source.Exception != null) + { + if (ImGui.CollapsingHeader(message.Message.Ptr)) + { + // Show the native to managed entry point only if we show engine messages + + if (_showEngineMessages) + ImGui.TextUnformatted(message.Source.Exception.StackTrace); + else + ImGui.TextUnformatted(message.Source.Exception.CleanStackTrace); + + ImGui.NewLine(); + } } else { - ImGui.Text($"Entity: (ID: {message.Source.Entity})"); + ImGui.TextUnformatted(message.Message); } } - if (message.Source.Exception != null) + ImGui.EndChild(); + + if (ImGui.IsItemHovered() && ImGui.IsMouseDoubleClicked(.Left)) { - if (ImGui.CollapsingHeader(message.Message.Ptr)) + if (message.Source.MessageOrigin != null) { - // Show the native to managed entry point only if we show engine messages - - if (_showEngineMessages) - ImGui.TextUnformatted(message.Source.Exception.StackTrace); - else - ImGui.TextUnformatted(message.Source.Exception.CleanStackTrace); - - ImGui.NewLine(); + VisualStudioUtility.OpenScript(message.Source.MessageOrigin.FileName); } } - else - { - ImGui.TextUnformatted(message.Message); - } // Dont show the counter if we only have one message. if (count > 1) diff --git a/GlitchyEditor/src/EditorLogger.bf b/GlitchyEditor/src/EditorLogger.bf index 130fdeb..c19c091 100644 --- a/GlitchyEditor/src/EditorLogger.bf +++ b/GlitchyEditor/src/EditorLogger.bf @@ -120,6 +120,10 @@ public class EditorLogger : Logger { Editor.Instance.LogWindow.LogException(timestamp, ex); } + else if (args.Count > 0 && (var messageOrigin = args[^1] as MessageOrigin)) + { + Editor.Instance.LogWindow.Log(timestamp, level, message, new .() {IsEngineMessage = IsEngineLogger, MessageOrigin = messageOrigin}); + } else { Editor.Instance.LogWindow.Log(timestamp, level, message, new .() {IsEngineMessage = IsEngineLogger}); diff --git a/GlitchyEngine/src/Scripting/ScriptGlue.bf b/GlitchyEngine/src/Scripting/ScriptGlue.bf index 6e7ad35..31d5932 100644 --- a/GlitchyEngine/src/Scripting/ScriptGlue.bf +++ b/GlitchyEngine/src/Scripting/ScriptGlue.bf @@ -15,6 +15,24 @@ namespace GlitchyEngine.Scripting; using internal GlitchyEngine.Scripting; +class MessageOrigin +{ + private String _fileName; + private int _lineNumber; + + public StringView FileName => _fileName; + public int LineNumber => _lineNumber; + + [AllowAppend] + public this(StringView fileName, int lineNumber) + { + String file = append String(fileName); + + _fileName = file; + _lineNumber = lineNumber; + } +} + static class ScriptGlue { private static Dictionary s_AddComponentMethods = new .() ~ delete _; @@ -185,11 +203,24 @@ static class ScriptGlue #region Log [RegisterCall("ScriptGlue::Log_LogMessage")] - static void Log_LogMessage(int32 logLevel, MonoString* message) + static void Log_LogMessage(int32 logLevel, MonoString* message, MonoString* fileName, int lineNumber) { char8* utfMessage = Mono.mono_string_to_utf8(message); - Log.ClientLogger.Log((LogLevel)logLevel, StringView(utfMessage)); + if (fileName != null) + { + char8* utfFileName = Mono.mono_string_to_utf8(fileName); + + MessageOrigin messageOrigin = new MessageOrigin(StringView(utfFileName), lineNumber); + + Log.ClientLogger.Log((LogLevel)logLevel, StringView(utfMessage), messageOrigin); + + Mono.mono_free(utfFileName); + } + else + { + Log.ClientLogger.Log((LogLevel)logLevel, StringView(utfMessage)); + } Mono.mono_free(utfMessage); } diff --git a/ScriptCore/Log.cs b/ScriptCore/Log.cs index dc3180f..6d75a1d 100644 --- a/ScriptCore/Log.cs +++ b/ScriptCore/Log.cs @@ -1,4 +1,6 @@ using System; +using System.Diagnostics; +using System.Runtime.CompilerServices; namespace GlitchyEngine; @@ -20,95 +22,115 @@ public class Log Critical, Off } - + /// /// Logs a trace message. /// /// The message to log. - public static void Trace(string message) + /// File path of the caller. + /// Line number of the caller. + public static void Trace(string message, [CallerFilePath]string callerFilePath = "", [CallerLineNumber]int callerLineNumber = 0) { - ScriptGlue.Log_LogMessage(LogLevel.Trace, message); + ScriptGlue.Log_LogMessage(LogLevel.Trace, message, callerFilePath, callerLineNumber); } /// /// Logs an info message. /// /// The message to log. - public static void Info(string message) + /// File path of the caller. + /// Line number of the caller. + public static void Info(string message, [CallerFilePath]string callerFilePath = "", [CallerLineNumber]int callerLineNumber = 0) { - ScriptGlue.Log_LogMessage(LogLevel.Info, message); + ScriptGlue.Log_LogMessage(LogLevel.Info, message, callerFilePath, callerLineNumber); } /// /// Logs a warning message. /// /// The message to log. - public static void Warning(string message) + /// File path of the caller. + /// Line number of the caller. + public static void Warning(string message, [CallerFilePath]string callerFilePath = "", [CallerLineNumber]int callerLineNumber = 0) { - ScriptGlue.Log_LogMessage(LogLevel.Warning, message); + ScriptGlue.Log_LogMessage(LogLevel.Warning, message, callerFilePath, callerLineNumber); } /// /// Logs an error message. /// /// The message to log. - public static void Error(string message) + /// File path of the caller. + /// Line number of the caller. + public static void Error(string message, [CallerFilePath]string callerFilePath = "", [CallerLineNumber]int callerLineNumber = 0) { - ScriptGlue.Log_LogMessage(LogLevel.Error, message); + ScriptGlue.Log_LogMessage(LogLevel.Error, message, callerFilePath, callerLineNumber); } /// /// Logs a critical error message. /// /// The message to log. - public static void Critical(string message) + /// File path of the caller. + /// Line number of the caller. + public static void Critical(string message, [CallerFilePath]string callerFilePath = "", [CallerLineNumber]int callerLineNumber = 0) { - ScriptGlue.Log_LogMessage(LogLevel.Critical, message); + ScriptGlue.Log_LogMessage(LogLevel.Critical, message, callerFilePath, callerLineNumber); } /// /// Serializes the given object (using ) and logs it as a trace message. /// /// The object to serialize. - public static void Trace(object obj) + /// File path of the caller. + /// Line number of the caller. + public static void Trace(object obj, [CallerFilePath]string callerFilePath = "", [CallerLineNumber]int callerLineNumber = 0) { - ScriptGlue.Log_LogMessage(LogLevel.Trace, obj.ToString()); + ScriptGlue.Log_LogMessage(LogLevel.Trace, obj.ToString(), callerFilePath, callerLineNumber); } /// /// Serializes the given object (using ) and logs it as an info message. /// /// The object to serialize. - public static void Info(object obj) + /// File path of the caller. + /// Line number of the caller. + public static void Info(object obj, [CallerFilePath]string callerFilePath = "", [CallerLineNumber]int callerLineNumber = 0) { - ScriptGlue.Log_LogMessage(LogLevel.Info, obj.ToString()); + ScriptGlue.Log_LogMessage(LogLevel.Info, obj.ToString(), callerFilePath, callerLineNumber); } /// /// Serializes the given object (using ) and logs it as a warning message. /// /// The object to serialize. - public static void Warning(object obj) + /// File path of the caller. + /// Line number of the caller. + public static void Warning(object obj, [CallerFilePath]string callerFilePath = "", [CallerLineNumber]int callerLineNumber = 0) { - ScriptGlue.Log_LogMessage(LogLevel.Warning, obj.ToString()); + ScriptGlue.Log_LogMessage(LogLevel.Warning, obj.ToString(), callerFilePath, callerLineNumber); } /// /// Serializes the given object (using ) and logs it as an error message. /// /// The object to serialize. - public static void Error(object obj) + /// File path of the caller. + /// Line number of the caller. + public static void Error(object obj, [CallerFilePath]string callerFilePath = "", [CallerLineNumber]int callerLineNumber = 0) { - ScriptGlue.Log_LogMessage(LogLevel.Error, obj.ToString()); + ScriptGlue.Log_LogMessage(LogLevel.Error, obj.ToString(), callerFilePath, callerLineNumber); } /// /// Serializes the given object (using ) and logs it as a critical error message. /// /// The object to serialize. - public static void Critical(object obj) + /// File path of the caller. + /// Line number of the caller. + public static void Critical(object obj, [CallerFilePath]string callerFilePath = "", [CallerLineNumber]int callerLineNumber = 0) { - ScriptGlue.Log_LogMessage(LogLevel.Critical, obj.ToString()); + ScriptGlue.Log_LogMessage(LogLevel.Critical, obj.ToString(), callerFilePath, callerLineNumber); } /// diff --git a/ScriptCore/ScriptGlue.cs b/ScriptCore/ScriptGlue.cs index edbf095..57e2d01 100644 --- a/ScriptCore/ScriptGlue.cs +++ b/ScriptCore/ScriptGlue.cs @@ -14,7 +14,7 @@ internal static class ScriptGlue #region Log [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern void Log_LogMessage(Log.LogLevel logLevel, string message); + internal static extern void Log_LogMessage(Log.LogLevel logLevel, string message, string filePath, int line); [MethodImpl(MethodImplOptions.InternalCall)]