using System; using System.Threading; using GFramework.Core.Abstractions.Logging; namespace GFramework.Godot.Logging; /// /// Static Godot logging entry point with auto-discovered configuration and deferred logger creation. /// public static class GodotLog { #if NET9_0_OR_GREATER private static readonly System.Threading.Lock ConfigureLock = new(); #else private static readonly object ConfigureLock = new(); #endif private static Action? _configure; private static readonly Lazy LazyConfigurationSource = new( CreateConfigurationSource, LazyThreadSafetyMode.ExecutionAndPublication); private static readonly Lazy LazyProvider = new( static () => new GodotLoggerFactoryProvider(() => LazyConfigurationSource.Value.CurrentSettings), LazyThreadSafetyMode.ExecutionAndPublication); /// /// Applies imperative option overrides before the global Godot logger provider is materialized. /// /// The options mutator. public static void Configure(Action configure) { ArgumentNullException.ThrowIfNull(configure); lock (ConfigureLock) { if (LazyProvider.IsValueCreated) { throw new InvalidOperationException( "GodotLog.Configure must be called before any GodotLog logger is materialized."); } _configure = configure; } } /// /// Gets the lazily-configured Godot logger provider. /// public static ILoggerFactoryProvider Provider { get { lock (ConfigureLock) { return LazyProvider.Value; } } } /// /// Gets the discovered configuration file path, if any. /// public static string? ConfigurationPath => LazyConfigurationSource.Value.ConfigurationPath; /// /// Creates a logger for the specified category without materializing the provider until first use. /// public static ILogger CreateLogger(string category) { ArgumentException.ThrowIfNullOrWhiteSpace(category); return new DeferredLogger(category, static () => Provider); } /// /// Creates a logger for the specified type without materializing the provider until first use. /// public static ILogger CreateLogger() { return CreateLogger(GetCategoryName(typeof(T))); } /// /// Installs the Godot provider as the current global resolver provider. /// public static void UseAsDefaultProvider() { LoggerFactoryResolver.Provider = Provider; } private static GodotLogConfigurationSource CreateConfigurationSource() { lock (ConfigureLock) { return new GodotLogConfigurationSource(_configure); } } private static string GetCategoryName(Type type) { return type.FullName ?? type.Name; } }