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());
}
}