using GlitchyEngine.Core; using System; using System.Diagnostics; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using static GlitchyEngine.Log; namespace GlitchyEngine; /// /// Provides methods to log messages to the Engine Console. /// public class Log { /// /// The severity of the log message. /// [EngineClass("GlitchLog.LogLevel")] internal enum LogLevel : byte { Trace = 0, Debug, Info, Warning, Error, Critical, Off } /// /// Logs a trace message. /// /// The message to log. /// File path of the caller. /// Line number of the caller. /// Name of the calling member. public static void Trace(string message, [CallerFilePath]string callerFilePath = "", [CallerLineNumber]int callerLineNumber = 0, [CallerMemberName]string callerMemberName = "") { ScriptGlue.Log_LogMessage(LogLevel.Trace, ScriptGlue.CurrentEntityId, message, callerFilePath, callerMemberName, callerLineNumber, 0); } /// /// Logs an info message. /// /// The message to log. /// File path of the caller. /// Line number of the caller. /// Name of the calling member. public static void Info(string message, [CallerFilePath]string callerFilePath = "", [CallerLineNumber]int callerLineNumber = 0, [CallerMemberName]string callerMemberName = "") { ScriptGlue.Log_LogMessage(LogLevel.Info, ScriptGlue.CurrentEntityId, message, callerFilePath, callerMemberName, callerLineNumber, 0); } /// /// Logs a warning message. /// /// The message to log. /// File path of the caller. /// Line number of the caller. /// Name of the calling member. public static void Warning(string message, [CallerFilePath]string callerFilePath = "", [CallerLineNumber]int callerLineNumber = 0, [CallerMemberName]string callerMemberName = "") { ScriptGlue.Log_LogMessage(LogLevel.Warning, ScriptGlue.CurrentEntityId, message, callerFilePath, callerMemberName, callerLineNumber, 0); } /// /// Logs an error message. /// /// The message to log. /// File path of the caller. /// Line number of the caller. /// Name of the calling member. public static void Error(string message, [CallerFilePath]string callerFilePath = "", [CallerLineNumber]int callerLineNumber = 0, [CallerMemberName]string callerMemberName = "") { ScriptGlue.Log_LogMessage(LogLevel.Error, ScriptGlue.CurrentEntityId, message, callerFilePath, callerMemberName, callerLineNumber, 0); } /// /// Logs a critical error message. /// /// The message to log. /// File path of the caller. /// Line number of the caller. /// Name of the calling member. public static void Critical(string message, [CallerFilePath]string callerFilePath = "", [CallerLineNumber]int callerLineNumber = 0, [CallerMemberName]string callerMemberName = "") { ScriptGlue.Log_LogMessage(LogLevel.Critical, ScriptGlue.CurrentEntityId, message, callerFilePath, callerMemberName, callerLineNumber, 0); } /// /// Serializes the given object (using ) and logs it as a trace message. /// /// The object to serialize. /// File path of the caller. /// Line number of the caller. /// Name of the calling member. public static void Trace(object obj, [CallerFilePath]string callerFilePath = "", [CallerLineNumber]int callerLineNumber = 0, [CallerMemberName]string callerMemberName = "") { ScriptGlue.Log_LogMessage(LogLevel.Trace, ScriptGlue.CurrentEntityId, obj.ToString(), callerFilePath, callerMemberName, callerLineNumber, 0); } /// /// Serializes the given object (using ) and logs it as an info message. /// /// The object to serialize. /// File path of the caller. /// Line number of the caller. /// Name of the calling member. public static void Info(object obj, [CallerFilePath]string callerFilePath = "", [CallerLineNumber]int callerLineNumber = 0, [CallerMemberName]string callerMemberName = "") { ScriptGlue.Log_LogMessage(LogLevel.Info, ScriptGlue.CurrentEntityId, obj.ToString(), callerFilePath, callerMemberName, callerLineNumber, 0); } /// /// Serializes the given object (using ) and logs it as a warning message. /// /// The object to serialize. /// File path of the caller. /// Line number of the caller. /// Name of the calling member. public static void Warning(object obj, [CallerFilePath]string callerFilePath = "", [CallerLineNumber]int callerLineNumber = 0, [CallerMemberName]string callerMemberName = "") { ScriptGlue.Log_LogMessage(LogLevel.Warning, ScriptGlue.CurrentEntityId, obj.ToString(), callerFilePath, callerMemberName, callerLineNumber, 0); } /// /// Serializes the given object (using ) and logs it as an error message. /// /// The object to serialize. /// File path of the caller. /// Line number of the caller. /// Name of the calling member. public static void Error(object obj, [CallerFilePath]string callerFilePath = "", [CallerLineNumber]int callerLineNumber = 0, [CallerMemberName]string callerMemberName = "") { ScriptGlue.Log_LogMessage(LogLevel.Error, ScriptGlue.CurrentEntityId, obj.ToString(), callerFilePath, callerMemberName, callerLineNumber, 0); } /// /// Serializes the given object (using ) and logs it as a critical error message. /// /// The object to serialize. /// File path of the caller. /// Line number of the caller. /// Name of the calling member. public static void Critical(object obj, [CallerFilePath]string callerFilePath = "", [CallerLineNumber]int callerLineNumber = 0, [CallerMemberName]string callerMemberName = "") { ScriptGlue.Log_LogMessage(LogLevel.Critical, ScriptGlue.CurrentEntityId, obj.ToString(), callerFilePath, callerMemberName, callerLineNumber, 0); } [DebuggerDisplay("{ToString(),raw}")] [StructLayout(LayoutKind.Sequential, Pack = 0)] [EngineClass("GlitchyEngine.Scripting.ScriptGlue.GlueStackFrameInfo")] internal struct StackFrameInfo { public Native.StringView FileName; public Native.StringView MethodSignature; public int LineNumber; public int ColumnNumber; } static string GetFullSignature(MethodBase method) { var typeName = method.DeclaringType?.FullName ?? ""; var parameters = string.Join(", ", method.GetParameters().Select(p => $"{p.ParameterType.Name}")); return $"{typeName}.{method.Name}({parameters})"; } /// /// Logs the give exception. /// /// The exception to log. public static void Exception(Exception exception) { StackTrace st = new StackTrace(exception, true); StackFrame[] frames = st.GetFrames(); StackFrameInfo[] stackFrameInfos = new StackFrameInfo[frames.Length]; // We only need StackFrameInfo for exception logging, so it's okay to manually handle the marshalling here. foreach (StackFrame stackFrame in frames) { MethodBase? method = stackFrame.GetMethod(); string methodSignature = method != null ? GetFullSignature(method) : ""; stackFrameInfos[frames.IndexOf(stackFrame)] = new StackFrameInfo { FileName = Native.StringView.FromManagedString(stackFrame.GetFileName()), MethodSignature = Native.StringView.FromManagedString(methodSignature), LineNumber = stackFrame.GetFileLineNumber(), ColumnNumber = stackFrame.GetFileColumnNumber() }; } unsafe { fixed (StackFrameInfo* stackFrames = stackFrameInfos) { ScriptGlue.Log_LogException(ScriptGlue.CurrentEntityId, exception.GetType().FullName, exception.Message, stackFrames, stackFrameInfos.Length); } } foreach (StackFrameInfo stackFrameInfo in stackFrameInfos) { Native.StringView.FreeNativeMemory(stackFrameInfo.FileName); } } }