#region usings using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.IO.Compression; using System.Windows.Forms; using OpenTK.Graphics.OpenGL4; using SM.OGL; #endregion namespace SM.Base { /// /// Specifies the target. /// [Flags] public enum LogTarget { /// /// No target, will not draw. /// None = 0, /// /// Takes the . /// Default = 1, /// /// Writes the log to the console. /// Console = 2, /// /// Writes the log to the debugger at . /// Debugger = 4, /// /// Writes the log to the specific file. /// File = 8, /// /// Writes the log to every target. /// All = Console | Debugger | File } /// /// Preset log types. /// public enum LogType { /// /// Informations. Console Color: Green /// Info, /// /// Warnings. Console Color: Yellow /// Warning, /// /// Error. Console Color: Red /// Error } /// /// Contains the system for logging. /// public class Log { private static StreamWriter _logStream; private static bool _init; /// /// Presets for the log targets. /// public static Dictionary Preset = new() { {LogTarget.Console, "[%type%] %msg%"}, {LogTarget.Debugger, "[%type%] %msg%"}, {LogTarget.File, "<%date%, %time%> [%type%] %msg%"} }; private static readonly Dictionary Colors = new() { {LogType.Info, ConsoleColor.Green}, {LogType.Warning, ConsoleColor.Yellow}, {LogType.Error, ConsoleColor.Red} }; /// /// Specified the default target. /// public static LogTarget DefaultTarget = LogTarget.All; /// /// Sets the log file. At wish compresses the old file to a zip file. /// /// The path to the log file. /// Path for the compression, if desired. public static void SetLogFile(string path = "sm.log", string compressionFolder = "") { _logStream?.Close(); if (!_init) Init(); if (File.Exists(path)) { if (compressionFolder != "") { if (!Directory.Exists(compressionFolder)) Directory.CreateDirectory(compressionFolder); var creation = File.GetLastWriteTime(path); try { using var archive = ZipFile.Open( $"{compressionFolder}{Path.DirectorySeparatorChar}{Path.GetFileName(path)}_{creation.Year.ToString() + creation.Month + creation.Day}_{creation.Hour.ToString() + creation.Minute + creation.Second + creation.Millisecond}.zip", ZipArchiveMode.Create); archive.CreateEntryFromFile(path, Path.GetFileName(path)); } catch { // ignore } } File.Delete(path); } _logStream = new StreamWriter(path) {AutoFlush = true}; Write(LogType.Info, $"Activate new log file. ['{path}']"); } internal static void Init() { if (_init) return; AppDomain.CurrentDomain.UnhandledException += ExceptionHandler; AppDomain.CurrentDomain.DomainUnload += (sender, args) => { _logStream.WriteLine("Unload application"); _logStream.Close(); }; GLCustomActions.AtKHRDebug = GLDebugAction; GLCustomActions.AtError = err => Write(LogType.Error, err); GLCustomActions.AtWarning = warning => Write(LogType.Warning, warning); GLCustomActions.AtInfo = info => Write(LogType.Info, info); _init = true; } private static void GLDebugAction(DebugSource source, DebugType type, DebugSeverity severity, string msg) { if (type.HasFlag(DebugType.DebugTypeError)) throw new Exception("[GLError] " + msg); Write("GL"+ (type != DebugType.DontCare ? type.ToString().Substring(9) : "DontCare"), ConsoleColor.Gray, msg); } [DebuggerStepThrough] private static void ExceptionHandler(object sender, UnhandledExceptionEventArgs e) { Write(e.IsTerminating ? "Terminating Error" : LogType.Error.ToString(), e.IsTerminating ? ConsoleColor.DarkRed : ConsoleColor.Red, e.ExceptionObject); if (e.IsTerminating) { MessageBox.Show($"Critical error occured.\n\n{e.ExceptionObject}", $"Terminating Error: {e.ExceptionObject.GetType().Name}"); _logStream?.Close(); } } /// /// Writes multiple lines of the same type to the log. /// public static void Write(LogType type, params T[] values) { Write(type.ToString(), Colors[type], values); } /// /// Writes multiple lines of the same type to the log. /// public static void Write(string type, ConsoleColor color, params T[] values) { for (var i = 0; i < values.Length; i++) Write(type, color, values[i], DefaultTarget); } /// /// Writes one line to the log. /// public static void Write(LogType type, T value, LogTarget target = LogTarget.Default) { Write(type.ToString(), Colors[type], value, target); } /// /// Writes one line to the log. /// public static void Write(string type, ConsoleColor color, T value, LogTarget target = LogTarget.Default) { if (target == LogTarget.Default) target = DefaultTarget; if (target.HasFlag(LogTarget.Console)) ColorfulWriteLine(color, ProcessPreset(LogTarget.Console, type, value.ToString())); if (target.HasFlag(LogTarget.Debugger)) Debug.WriteLine(ProcessPreset(LogTarget.Debugger, type, value.ToString())); if (target.HasFlag(LogTarget.File)) _logStream?.WriteLine(ProcessPreset(LogTarget.File, type, value.ToString())); } /// /// Writes a text with a different color. /// /// /// public static void ColorfulWriteLine(ConsoleColor color, string value) { var before = Console.ForegroundColor; Console.ForegroundColor = color; Console.WriteLine(value); Console.ForegroundColor = before; } private static string ProcessPreset(LogTarget target, string type, string msg) { var preset = Preset[target]; var now = DateTime.Now; return preset.Replace("%date%", now.ToShortDateString()) .Replace("%time%", now.ToShortTimeString()) .Replace("%type%", type) .Replace("%msg%", msg); } } }