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

7.4 KiB
Raw Permalink Blame History

日志生成器

GFramework.SourceGenerators 自动生成日志代码,减少样板代码

概述

日志生成器是一个 Source Generator它会自动为标记了 [Log] 特性的类生成 ILogger 字段。这消除了手动编写日志字段的需要,让开发者专注于业务逻辑。

基本用法

标记类

using GFramework.SourceGenerators.Abstractions.Logging;

[Log]
public partial class MyService
{
    public void DoSomething()
    {
        // 自动生成的 Logger 字段可直接使用
        Logger.Info("执行操作");
    }
}

生成代码

上面的代码会被编译时转换为:

// <auto-generated/>
public partial class MyService
{
    private static readonly ILogger Logger =
        LoggerFactoryResolver.Provider.CreateLogger("YourNamespace.MyService");
}

注意:生成器只生成 ILogger 字段不生成日志方法。日志方法Info、Debug、Error 等)来自 ILogger 接口本身。

日志级别

生成的 Logger 字段支持 ILogger 接口的所有方法:

[Log]
public partial class MyClass
{
    public void Example()
    {
        // 调试信息
        Logger.Debug($"调试信息: {value}");

        // 普通信息
        Logger.Info("操作成功");

        // 警告
        Logger.Warning($"警告: {message}");

        // 错误
        Logger.Error($"错误: {ex.Message}");

        // 严重错误
        Logger.Critical("系统故障");
    }
}

自定义日志类别

[Log("Gameplay")]
public partial class GameplaySystem
{
    // 日志会标记为 Gameplay 类别
    public void Update()
    {
        Logger.Info("游戏逻辑更新");
    }
}

配置选项

自定义字段名称

[Log(FieldName = "_customLogger")]
public partial class MyClass
{
    public void DoSomething()
    {
        // 使用自定义字段名
        _customLogger.Info("使用自定义日志器");
    }
}

非静态字段

[Log(IsStatic = false)]
public partial class InstanceLogger
{
    // 生成实例字段而非静态字段
    public void LogMessage()
    {
        Logger.Info("实例日志");
    }
}

访问修饰符

[Log(AccessModifier = "protected")]
public partial class ProtectedLogger
{
    // 生成 protected 字段
}

配置选项说明

参数 类型 默认值 说明
Name string? null 日志分类名称(默认使用类名)
FieldName string "Logger" 生成的字段名称
IsStatic bool true 是否生成静态字段
AccessModifier string "private" 访问修饰符private/protected/public

与其他模块集成

与 Godot 集成

[Log]
[ContextAware]
public partial class GodotController : Node
{
    public override void _Ready()
    {
        Logger.Info("控制器已准备就绪");
    }
}

与架构集成

[Log]
public partial class MySystem : AbstractSystem
{
    protected override void OnInit()
    {
        Logger.Info("系统初始化");
    }
}

实际应用示例

游戏控制器

using GFramework.Core.Abstractions.Controller;
using GFramework.SourceGenerators.Abstractions.Logging;
using GFramework.SourceGenerators.Abstractions.Rule;

[Log]
[ContextAware]
public partial class PlayerController : IController
{
    public void HandleInput(string action)
    {
        Logger.Debug($"处理输入: {action}");

        switch (action)
        {
            case "jump":
                Logger.Info("玩家跳跃");
                Jump();
                break;
            case "attack":
                Logger.Info("玩家攻击");
                Attack();
                break;
            default:
                Logger.Warning($"未知操作: {action}");
                break;
        }
    }

    private void Jump()
    {
        try
        {
            // 跳跃逻辑
            Logger.Debug("跳跃执行成功");
        }
        catch (Exception ex)
        {
            Logger.Error($"跳跃失败: {ex.Message}");
        }
    }
}

数据处理服务

[Log("DataService")]
public partial class DataProcessor
{
    public void ProcessData(string data)
    {
        Logger.Info($"开始处理数据,长度: {data.Length}");

        if (string.IsNullOrEmpty(data))
        {
            Logger.Warning("数据为空,跳过处理");
            return;
        }

        try
        {
            // 处理逻辑
            Logger.Debug("数据处理中...");
            // ...
            Logger.Info("数据处理完成");
        }
        catch (Exception ex)
        {
            Logger.Error($"数据处理失败: {ex.Message}");
            throw;
        }
    }
}

最佳实践

1. 合理使用日志级别

[Log]
public partial class BestPracticeExample
{
    public void ProcessRequest()
    {
        // Debug: 详细的调试信息
        Logger.Debug("开始处理请求");

        // Info: 重要的业务流程信息
        Logger.Info("请求处理成功");

        // Warning: 可恢复的异常情况
        Logger.Warning("缓存未命中,使用默认值");

        // Error: 错误但不影响系统运行
        Logger.Error("处理失败,将重试");

        // Critical: 严重错误,可能导致系统崩溃
        Logger.Critical("数据库连接失败");
    }
}

2. 避免过度日志

[Log]
public partial class PerformanceExample
{
    private int _frameCount = 0;

    public void Update()
    {
        // 好的做法:定期记录
        if (_frameCount % 1000 == 0)
        {
            Logger.Debug($"已运行 {_frameCount} 帧");
        }
        _frameCount++;

        // 避免:每帧都记录
        // Logger.Debug($"帧 {_frameCount}"); // ❌ 太频繁
    }
}

3. 结构化日志信息

[Log]
public partial class StructuredLogging
{
    public void ProcessUser(int userId, string action)
    {
        // 好的做法:包含上下文信息
        Logger.Info($"用户操作 [UserId={userId}, Action={action}]");

        // 避免:信息不完整
        // Logger.Info("用户操作"); // ❌ 缺少上下文
    }
}

常见问题

Q: 为什么需要 partial 关键字?

A: 源代码生成器需要向现有类添加代码,partial 关键字允许一个类的定义分散在多个文件中。

Q: 可以在静态类中使用吗?

A: 可以,生成器会自动生成静态字段:

[Log]
public static partial class StaticHelper
{
    public static void DoSomething()
    {
        Logger.Info("静态方法日志");
    }
}

Q: 如何自定义日志工厂?

A: 通过配置 LoggerFactoryResolver.Provider 来自定义日志工厂实现。


相关文档