using System; using System.Collections.Concurrent; using System.Diagnostics; using System.Runtime.InteropServices; using NetVips.Internal; namespace NetVips; /// /// Wrapper for message logging functions. /// public static class Log { private static GLib.LogFuncNative _nativeHandler; /// /// Specifies the prototype of log handler functions. /// /// The log domain of the message. /// The log level of the message (including the fatal and recursion flags). /// The message to process. public delegate void LogDelegate(string logDomain, Enums.LogLevelFlags logLevel, string message); private static void NativeCallback(string logDomain, Enums.LogLevelFlags flags, nint messagePtr, nint userData) { if (userData == IntPtr.Zero) { return; } var message = messagePtr.ToUtf8String(); var gch = (GCHandle)userData; if (gch.Target is LogDelegate func) { func(logDomain, flags, message); } } private static readonly ConcurrentDictionary Handlers = new(); /// /// Sets the log handler for a domain and a set of log levels. /// /// The log domain, or for the default "" application domain. /// The log levels to apply the log handler for. /// The log handler function. /// The id of the handler. public static uint SetLogHandler(string logDomain, Enums.LogLevelFlags flags, LogDelegate logFunc) { _nativeHandler ??= NativeCallback; var gch = GCHandle.Alloc(logFunc); var result = GLib.GLogSetHandler(logDomain, flags, _nativeHandler, (nint)gch); Handlers.AddOrUpdate(result, gch, (_, _) => gch); return result; } /// /// Removes the log handler. /// /// The log domain. /// The id of the handler, which was returned in . public static void RemoveLogHandler(string logDomain, uint handlerId) { if (Handlers != null && Handlers.ContainsKey(handlerId) && Handlers.TryRemove(handlerId, out var handler)) { handler.Free(); } GLib.GLogRemoveHandler(logDomain, handlerId); } /// /// Sets the message levels which are always fatal, in any log domain. /// When a message with any of these levels is logged the program terminates. /// /// The mask containing bits set for each level of error which is to be fatal. /// The old fatal mask. public static Enums.LogLevelFlags SetAlwaysFatal(Enums.LogLevelFlags fatalMask) { return GLib.GLogSetAlwaysFatal(fatalMask); } /// /// Sets the log levels which are fatal in the given domain. /// /// The log domain. /// The new fatal mask. /// The old fatal mask for the log domain. public static Enums.LogLevelFlags SetAlwaysFatal(string logDomain, Enums.LogLevelFlags fatalMask) { return GLib.GLogSetFatalMask(logDomain, fatalMask); } /// /// Common logging method. /// /// /// Sample usage: /// /// // Print the messages for the NULL domain /// var logFunc = new LogFunc(Log.PrintLogFunction); /// Log.SetLogHandler(null, Enums.LogLevelFlags.All, logFunc); /// /// /// The log domain of the message. /// The log level of the message (including the fatal and recursion flags). /// The message to process. public static void PrintLogFunction(string domain, Enums.LogLevelFlags level, string message) { Console.WriteLine("Domain: '{0}' Level: {1}", domain, level); Console.WriteLine("Message: {0}", message); } /// /// Common logging method. /// /// /// Sample usage: /// /// // Print messages and stack trace for vips critical messages /// var logFunc = new LogFunc(Log.PrintTraceLogFunction); /// Log.SetLogHandler("VIPS", Enums.LogLevelFlags.Critical, logFunc); /// /// /// The log domain of the message. /// The log level of the message (including the fatal and recursion flags). /// The message to process. public static void PrintTraceLogFunction(string domain, Enums.LogLevelFlags level, string message) { PrintLogFunction(domain, level, message); Console.WriteLine("Trace follows:\n{0}", new StackTrace()); } }