GFramework/docs/zh-CN/godot/logging.md
GeWuYou fb14d7122c docs(style): 更新文档中的命名空间导入格式
- 将所有小写的命名空间导入更正为首字母大写格式
- 统一 GFramework 框架的命名空间引用规范
- 修复 core、ecs、godot 等模块的命名空间导入错误
- 标准化文档示例代码中的 using 语句格式
- 确保所有文档中的命名空间引用保持一致性
- 更新 global using 语句以匹配正确的命名空间格式
2026-03-10 07:18:49 +08:00

15 KiB
Raw Permalink Blame History

title, description
title description
Godot 日志系统 Godot 日志系统提供了 GFramework 日志功能与 Godot 引擎控制台的完整集成。

Godot 日志系统

概述

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

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

主要特性

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

核心概念

GodotLogger

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

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 日志记录器实例:

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

GodotLoggerFactoryProvider

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

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

基本用法

配置 Godot 日志系统

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

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("游戏架构初始化完成");
    }
}

创建和使用日志记录器

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("玩家状态异常");
    }
}

记录不同级别的日志

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("游戏崩溃");

记录异常信息

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

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

高级用法

在 System 中使用日志

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 中使用日志

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("玩家生命值归零");
        }
    }
}

条件日志记录

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

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

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

分类日志记录

// 为不同模块创建独立的日志记录器
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 决策树遍历");

自定义日志级别

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

在 Godot 模块中使用日志

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 方法 颜色 说明
Trace GD.PrintRich 灰色 最详细的跟踪信息
Debug GD.PrintRich 青色 调试信息
Info GD.Print 白色 一般信息
Warning GD.PushWarning 黄色 警告信息
Error GD.PrintErr 红色 错误信息
Fatal GD.PushError 红色 致命错误

异常信息格式

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

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

最佳实践

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

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

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

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

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

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

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

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

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

性能考虑

  1. 日志缓存

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

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

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

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

常见问题

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

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

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

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

解答 检查以下几点:

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

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

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

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

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

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

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

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

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

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

解答

  1. 提高最小日志级别
  2. 使用条件日志
  3. 避免在高频调用的方法中记录日志
// 提高日志级别
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 接口:

// 参数化日志
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));
}

相关文档