Skip to content

Godot 日志系统

概述

Godot 日志系统是 GFramework.Godot 中连接框架日志功能与 Godot 引擎控制台的核心组件。它提供了与 Godot 控制台的深度集成,支持彩色输出、多级别日志记录,以及与 GFramework 日志系统的无缝对接。

通过 Godot 日志系统,你可以在 Godot 项目中使用统一的日志接口,日志会自动输出到 Godot 编辑器控制台,并根据日志级别使用不同的颜色和输出方式。

主要特性

  • 与 Godot 控制台深度集成
  • 支持彩色日志输出
  • 多级别日志记录(Trace、Debug、Info、Warning、Error、Fatal)
  • 日志缓存机制
  • 时间戳和格式化支持
  • 异常信息记录

核心概念

GodotLogger

GodotLogger 是 Godot 平台的日志记录器实现,继承自 AbstractLogger

csharp
public sealed class GodotLogger : AbstractLogger
{
    public GodotLogger(string? name = null, LogLevel minLevel = LogLevel.Info);
    protected override void Write(LogLevel level, string message, Exception? exception);
}

GodotLoggerFactory

GodotLoggerFactory 用于创建 Godot 日志记录器实例:

csharp
public class GodotLoggerFactory : ILoggerFactory
{
    public ILogger GetLogger(string name, LogLevel minLevel = LogLevel.Info);
}

GodotLoggerFactoryProvider

GodotLoggerFactoryProvider 提供日志工厂实例,并支持日志缓存:

csharp
public sealed class GodotLoggerFactoryProvider : ILoggerFactoryProvider
{
    public LogLevel MinLevel { get; set; }
    public ILogger CreateLogger(string name);
}

基本用法

配置 Godot 日志系统

在架构初始化时配置日志提供程序:

csharp
using GFramework.Godot.Architecture;
using GFramework.Godot.Logging;
using GFramework.Core.Logging;
using GFramework.Core.Abstractions.Logging;

public class GameArchitecture : AbstractArchitecture
{
    public static GameArchitecture Interface { get; private set; }

    public GameArchitecture()
    {
        Interface = this;

        // 配置 Godot 日志系统
        LoggerFactoryResolver.Provider = new GodotLoggerFactoryProvider
        {
            MinLevel = LogLevel.Debug  // 设置最小日志级别
        };
    }

    protected override void InstallModules()
    {
        var logger = LoggerFactoryResolver.Provider.CreateLogger("GameArchitecture");
        logger.Info("游戏架构初始化开始");

        RegisterModel(new PlayerModel());
        RegisterSystem(new GameplaySystem());

        logger.Info("游戏架构初始化完成");
    }
}

创建和使用日志记录器

csharp
using Godot;
using GFramework.Core.Logging;
using GFramework.Core.Abstractions.Logging;

public partial class Player : CharacterBody2D
{
    private ILogger _logger;

    public override void _Ready()
    {
        // 创建日志记录器
        _logger = LoggerFactoryResolver.Provider.CreateLogger("Player");

        _logger.Info("玩家初始化");
        _logger.Debug("玩家位置: {0}", Position);
    }

    public override void _Process(double delta)
    {
        if (_logger.IsDebugEnabled())
        {
            _logger.Debug("玩家速度: {0}", Velocity);
        }
    }

    private void TakeDamage(float damage)
    {
        _logger.Warn("玩家受到伤害: {0}", damage);
    }

    private void OnError()
    {
        _logger.Error("玩家状态异常");
    }
}

记录不同级别的日志

csharp
var logger = LoggerFactoryResolver.Provider.CreateLogger("GameSystem");

// Trace - 最详细的跟踪信息(灰色)
logger.Trace("执行函数: UpdatePlayerPosition");

// Debug - 调试信息(青色)
logger.Debug("当前帧率: {0}", Engine.GetFramesPerSecond());

// Info - 一般信息(白色)
logger.Info("游戏开始");

// Warning - 警告信息(黄色)
logger.Warn("资源加载缓慢: {0}ms", loadTime);

// Error - 错误信息(红色)
logger.Error("无法加载配置文件");

// Fatal - 致命错误(红色,使用 PushError)
logger.Fatal("游戏崩溃");

记录异常信息

csharp
var logger = LoggerFactoryResolver.Provider.CreateLogger("SaveSystem");

try
{
    SaveGame();
}
catch (Exception ex)
{
    // 记录异常信息
    logger.Error("保存游戏失败", ex);
}

高级用法

在 System 中使用日志

csharp
using GFramework.Core.System;
using GFramework.Core.Logging;
using GFramework.Core.Abstractions.Logging;

public class CombatSystem : AbstractSystem
{
    private ILogger _logger;

    protected override void OnInit()
    {
        _logger = LoggerFactoryResolver.Provider.CreateLogger("CombatSystem");
        _logger.Info("战斗系统初始化完成");
    }

    public void ProcessCombat(Entity attacker, Entity target, float damage)
    {
        _logger.Debug("战斗处理: {0} 攻击 {1}, 伤害: {2}",
            attacker.Name, target.Name, damage);

        if (damage > 100)
        {
            _logger.Warn("高伤害攻击: {0}", damage);
        }
    }

    protected override void OnDestroy()
    {
        _logger.Info("战斗系统已销毁");
    }
}

在 Model 中使用日志

csharp
using GFramework.Core.Model;
using GFramework.Core.Logging;
using GFramework.Core.Abstractions.Logging;

public class PlayerModel : AbstractModel
{
    private ILogger _logger;
    private int _health;

    protected override void OnInit()
    {
        _logger = LoggerFactoryResolver.Provider.CreateLogger("PlayerModel");
        _logger.Info("玩家模型初始化");

        _health = 100;
    }

    public void SetHealth(int value)
    {
        var oldHealth = _health;
        _health = value;

        _logger.Debug("玩家生命值变化: {0} -> {1}", oldHealth, _health);

        if (_health <= 0)
        {
            _logger.Warn("玩家生命值归零");
        }
    }
}

条件日志记录

csharp
var logger = LoggerFactoryResolver.Provider.CreateLogger("PerformanceMonitor");

// 检查日志级别是否启用,避免不必要的字符串格式化
if (logger.IsDebugEnabled())
{
    var stats = CalculateComplexStats();  // 耗时操作
    logger.Debug("性能统计: {0}", stats);
}

// 简化写法
if (logger.IsTraceEnabled())
{
    logger.Trace("详细的执行流程信息");
}

分类日志记录

csharp
// 为不同模块创建独立的日志记录器
var networkLogger = LoggerFactoryResolver.Provider.CreateLogger("Network");
var databaseLogger = LoggerFactoryResolver.Provider.CreateLogger("Database");
var aiLogger = LoggerFactoryResolver.Provider.CreateLogger("AI");

networkLogger.Info("连接到服务器");
databaseLogger.Debug("查询用户数据");
aiLogger.Trace("AI 决策树遍历");

自定义日志级别

csharp
// 在开发环境使用 Debug 级别
#if DEBUG
LoggerFactoryResolver.Provider = new GodotLoggerFactoryProvider
{
    MinLevel = LogLevel.Debug
};
#else
// 在生产环境使用 Info 级别
LoggerFactoryResolver.Provider = new GodotLoggerFactoryProvider
{
    MinLevel = LogLevel.Info
};
#endif

在 Godot 模块中使用日志

csharp
using GFramework.Godot.Architecture;
using GFramework.Core.Logging;
using GFramework.Core.Abstractions.Logging;
using Godot;

public class SceneModule : AbstractGodotModule
{
    private ILogger _logger;
    private Node _sceneRoot;

    public override Node Node => _sceneRoot;

    public SceneModule()
    {
        _sceneRoot = new Node { Name = "SceneRoot" };
        _logger = LoggerFactoryResolver.Provider.CreateLogger("SceneModule");
    }

    public override void Install(IArchitecture architecture)
    {
        _logger.Info("场景模块安装开始");

        // 安装场景系统
        var sceneSystem = new SceneSystem();
        architecture.RegisterSystem<ISceneSystem>(sceneSystem);

        _logger.Info("场景模块安装完成");
    }

    public override void OnPhase(ArchitecturePhase phase, IArchitecture architecture)
    {
        _logger.Debug("场景模块阶段: {0}", phase);

        if (phase == ArchitecturePhase.Ready)
        {
            _logger.Info("场景模块已就绪");
        }
    }

    public override void OnDetach()
    {
        _logger.Info("场景模块已分离");
        _sceneRoot?.QueueFree();
    }
}

日志输出格式

输出格式说明

Godot 日志系统使用以下格式输出日志:

[时间戳] 日志级别 [日志器名称] 日志消息

示例输出

[2025-01-09 10:30:45.123] INFO    [GameArchitecture] 游戏架构初始化开始
[2025-01-09 10:30:45.456] DEBUG   [Player] 玩家位置: (100, 200)
[2025-01-09 10:30:46.789] WARNING [CombatSystem] 高伤害攻击: 150
[2025-01-09 10:30:47.012] ERROR   [SaveSystem] 保存游戏失败

日志级别与 Godot 输出方法

日志级别Godot 方法颜色说明
TraceGD.PrintRich灰色最详细的跟踪信息
DebugGD.PrintRich青色调试信息
InfoGD.Print白色一般信息
WarningGD.PushWarning黄色警告信息
ErrorGD.PrintErr红色错误信息
FatalGD.PushError红色致命错误

异常信息格式

当记录异常时,异常信息会附加到日志消息后:

[2025-01-09 10:30:47.012] ERROR   [SaveSystem] 保存游戏失败
System.IO.IOException: 文件访问被拒绝
   at SaveSystem.SaveGame() in SaveSystem.cs:line 42

最佳实践

  1. 在架构初始化时配置日志系统

    csharp
    public GameArchitecture()
    {
        LoggerFactoryResolver.Provider = new GodotLoggerFactoryProvider
        {
            MinLevel = LogLevel.Debug
        };
    }
  2. 为每个类创建独立的日志记录器

    csharp
    private ILogger _logger;
    
    public override void _Ready()
    {
        _logger = LoggerFactoryResolver.Provider.CreateLogger(GetType().Name);
    }
  3. 使用合适的日志级别

    • Trace:详细的执行流程,仅在深度调试时使用
    • Debug:调试信息,开发阶段使用
    • Info:重要的业务流程和状态变化
    • Warning:潜在问题但不影响功能
    • Error:错误但程序可以继续运行
    • Fatal:严重错误,程序无法继续
  4. 检查日志级别避免性能损失

    csharp
    if (_logger.IsDebugEnabled())
    {
        var expensiveData = CalculateExpensiveData();
        _logger.Debug("数据: {0}", expensiveData);
    }
  5. 提供有意义的上下文信息

    csharp
    // ✗ 不好
    logger.Error("错误");
    
    // ✓ 好
    logger.Error("加载场景失败: SceneKey={0}, Path={1}", sceneKey, scenePath);
  6. 记录异常时提供上下文

    csharp
    try
    {
        LoadScene(sceneKey);
    }
    catch (Exception ex)
    {
        logger.Error($"加载场景失败: {sceneKey}", ex);
    }
  7. 使用分类日志记录器

    csharp
    var networkLogger = LoggerFactoryResolver.Provider.CreateLogger("Network");
    var aiLogger = LoggerFactoryResolver.Provider.CreateLogger("AI");
  8. 在生命周期方法中记录关键事件

    csharp
    protected override void OnInit()
    {
        _logger.Info("系统初始化完成");
    }
    
    protected override void OnDestroy()
    {
        _logger.Info("系统已销毁");
    }

性能考虑

  1. 日志缓存

    • GodotLoggerFactoryProvider 使用 CachedLoggerFactory 缓存日志记录器实例
    • 相同名称和级别的日志记录器会被复用
  2. 级别检查

    • 日志方法会自动检查日志级别
    • 低于最小级别的日志不会被处理
  3. 字符串格式化

    • 使用参数化日志避免不必要的字符串拼接
    csharp
    // ✗ 不好 - 总是执行字符串拼接
    logger.Debug("位置: " + position.ToString());
    
    // ✓ 好 - 只在 Debug 启用时格式化
    logger.Debug("位置: {0}", position);
  4. 条件日志

    • 对于耗时的数据计算,先检查日志级别
    csharp
    if (logger.IsDebugEnabled())
    {
        var stats = CalculateComplexStats();
        logger.Debug("统计: {0}", stats);
    }

常见问题

问题:如何配置 Godot 日志系统?

解答: 在架构构造函数中配置日志提供程序:

csharp
public GameArchitecture()
{
    LoggerFactoryResolver.Provider = new GodotLoggerFactoryProvider
    {
        MinLevel = LogLevel.Debug
    };
}

问题:日志没有输出到 Godot 控制台?

解答: 检查以下几点:

  1. 确认已配置 GodotLoggerFactoryProvider
  2. 检查日志级别是否低于最小级别
  3. 确认使用了正确的日志记录器
csharp
// 确认配置
LoggerFactoryResolver.Provider = new GodotLoggerFactoryProvider
{
    MinLevel = LogLevel.Trace  // 设置为最低级别测试
};

// 创建日志记录器
var logger = LoggerFactoryResolver.Provider.CreateLogger("Test");
logger.Info("测试日志");  // 应该能看到输出

问题:如何在不同环境使用不同的日志级别?

解答: 使用条件编译或环境检测:

csharp
public GameArchitecture()
{
    var minLevel = OS.IsDebugBuild() ? LogLevel.Debug : LogLevel.Info;

    LoggerFactoryResolver.Provider = new GodotLoggerFactoryProvider
    {
        MinLevel = minLevel
    };
}

问题:如何禁用某个模块的日志?

解答: 为该模块创建一个高级别的日志记录器:

csharp
// 只记录 Error 及以上级别
var logger = new GodotLogger("VerboseModule", LogLevel.Error);

问题:日志输出影响性能怎么办?

解答

  1. 提高最小日志级别
  2. 使用条件日志
  3. 避免在高频调用的方法中记录日志
csharp
// 提高日志级别
LoggerFactoryResolver.Provider = new GodotLoggerFactoryProvider
{
    MinLevel = LogLevel.Warning  // 只记录警告及以上
};

// 使用条件日志
if (_logger.IsDebugEnabled())
{
    _logger.Debug("高频数据: {0}", data);
}

// 避免在 _Process 中频繁记录
public override void _Process(double delta)
{
    // ✗ 不好 - 每帧都记录
    // _logger.Debug("帧更新");

    // ✓ 好 - 只在特定条件下记录
    if (someErrorCondition)
    {
        _logger.Error("检测到错误");
    }
}

问题:如何记录结构化日志?

解答: 使用参数化日志或 IStructuredLogger 接口:

csharp
// 参数化日志
logger.Info("玩家登录: UserId={0}, UserName={1}, Level={2}",
    userId, userName, level);

// 使用结构化日志(如果实现了 IStructuredLogger)
if (logger is IStructuredLogger structuredLogger)
{
    structuredLogger.Log(LogLevel.Info, "玩家登录",
        ("UserId", userId),
        ("UserName", userName),
        ("Level", level));
}

相关文档

基于 Apache 2.0 许可证发布