// Copyright (c) 2025-2026 GeWuYou
// SPDX-License-Identifier: Apache-2.0
using System;
using System.Collections.Generic;
using GFramework.Core.Abstractions.Logging;
using GFramework.Core.Logging;
namespace GFramework.Godot.Logging;
///
/// Godot platform logger implementation.
///
///
/// This logger preserves the existing entry point while delegating output to
/// so Godot rendering remains compatible with the Core appender pipeline.
///
public sealed class GodotLogger : AbstractLogger
{
private static readonly IReadOnlyDictionary EmptyProperties =
new Dictionary(StringComparer.Ordinal);
private readonly GodotLogAppender _appender;
///
/// Initializes a logger that preserves the historical fixed-format template.
///
/// The logger name.
/// The minimum enabled log level.
public GodotLogger(string? name = null, LogLevel minLevel = LogLevel.Info)
: this(
name ?? RootLoggerName,
CreateFixedOptionsProvider(minLevel),
() => minLevel)
{
}
///
/// Initializes a logger with Godot-specific formatting options.
///
/// The logger name.
/// The logger options.
public GodotLogger(string? name, GodotLoggerOptions options)
: this(
name ?? RootLoggerName,
CreateOptionsProvider(options),
CreateMinLevelProvider(options))
{
}
///
/// Initializes the core logger with dynamic options and level providers.
///
/// The resolved logger name used in rendered output.
///
/// The provider that supplies the latest rendering options for each write.
///
/// The provider that supplies the latest effective minimum level.
///
/// The Godot factory uses this constructor so cached logger instances can observe hot-reloaded settings without
/// being recreated. The default public constructor supplies a fixed provider to avoid allocation on the log
/// path.
///
internal GodotLogger(
string name,
Func optionsProvider,
Func minLevelProvider)
: base(name, minLevelProvider ?? throw new ArgumentNullException(nameof(minLevelProvider)))
{
_appender = new GodotLogAppender(
optionsProvider ?? throw new ArgumentNullException(nameof(optionsProvider)));
}
///
/// Writes a log entry to Godot.
///
/// The log level.
/// The rendered message body.
/// The optional exception.
protected override void Write(LogLevel level, string message, Exception? exception)
{
WriteEntry(level, message, exception, properties: null);
}
///
/// Uses Godot-aware structured rendering instead of the base string concatenation fallback.
///
/// The log level.
/// The message body before Godot template rendering.
/// Structured properties appended through the configured Godot template.
public override void Log(LogLevel level, string message, params (string Key, object? Value)[] properties)
{
if (!IsEnabled(level))
{
return;
}
WriteEntry(level, message, exception: null, properties);
}
///
/// Uses Godot-aware structured rendering instead of the base string concatenation fallback.
///
/// The log level.
/// The message body before Godot template rendering.
/// The optional exception written after the rendered message.
/// Structured properties appended through the configured Godot template.
public override void Log(
LogLevel level,
string message,
Exception? exception,
params (string Key, object? Value)[] properties)
{
if (!IsEnabled(level))
{
return;
}
WriteEntry(level, message, exception, properties);
}
private void WriteEntry(
LogLevel level,
string message,
Exception? exception,
(string Key, object? Value)[]? properties)
{
var entry = new LogEntry(
DateTime.UtcNow,
level,
Name(),
message,
exception,
ToPropertiesDictionary(properties));
_appender.Append(entry);
}
private static IReadOnlyDictionary ToPropertiesDictionary(
(string Key, object? Value)[]? properties)
{
if (properties == null || properties.Length == 0)
{
return EmptyProperties;
}
var result = new Dictionary(StringComparer.Ordinal);
foreach (var property in properties)
{
if (string.IsNullOrWhiteSpace(property.Key))
{
continue;
}
result[property.Key.Trim()] = property.Value;
}
return result.Count == 0 ? EmptyProperties : result;
}
private static Func CreateFixedOptionsProvider(LogLevel minLevel)
{
var options = GodotLoggerOptions.ForMinimumLevel(minLevel);
return () => options;
}
private static Func CreateOptionsProvider(GodotLoggerOptions options)
{
ArgumentNullException.ThrowIfNull(options);
return () => options;
}
private static Func CreateMinLevelProvider(GodotLoggerOptions options)
{
ArgumentNullException.ThrowIfNull(options);
return () => options.GetEffectiveMinLevel();
}
}