---
title: 日志（Logging）
description: 说明 GFramework.Core.Logging 的日志接口、组合方式与常见使用入口。
---

# 日志（Logging）

`GFramework.Core.Logging` 是 Core runtime 的默认日志实现。只加载抽象层时，`LoggerFactoryResolver` 会退回
silent provider；加载 `GFramework.Core` 或在 `ArchitectureConfiguration` 里显式提供 provider 后，日志才会
真正输出。

## 最小用法

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

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

logger.Info("Application started");
logger.Warn("Config file missing");
```

默认 `ArchitectureConfiguration` 会把 provider 配成 `ConsoleLoggerFactoryProvider`，最小级别是 `Info`。如果你
直接走标准 `Architecture` 启动路径，这条配置会自动生效。

## 在 Architecture 中调整日志级别

```csharp
using GFramework.Core.Architectures;
using GFramework.Core.Abstractions.Logging;
using GFramework.Core.Abstractions.Properties;
using GFramework.Core.Logging;

var configuration = new ArchitectureConfiguration
{
    LoggerProperties = new LoggerProperties
    {
        LoggerFactoryProvider = new ConsoleLoggerFactoryProvider
        {
            MinLevel = LogLevel.Debug
        }
    }
};
```

如果你只是想减少噪音或临时打开 `Debug`，通常只调 `MinLevel` 就够了。

## 结构化日志与上下文

默认 Core logger 实现支持 `IStructuredLogger` 和 `LogContext`。当你需要把 `requestId`、`sceneName` 之类的
上下文随异步流透传时，优先用上下文属性，而不是把所有信息拼进字符串。

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

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

using (LogContext.Push("RequestId", requestId))
{
    if (logger is IStructuredLogger structured)
    {
        structured.Log(
            LogLevel.Info,
            "Player matched",
            ("PlayerId", playerId),
            ("RoomId", roomId));
    }
}
```

## 当前仓库内置的常用实现

- `ConsoleLoggerFactoryProvider`
- `ConsoleLoggerFactory`
- `CompositeLogger`
- `LoggingConfigurationLoader`

如果你需要文件输出、rolling file、async appender 或 JSON formatter，可以先用
`LoggingConfigurationLoader` 读取 `LoggingConfiguration`，再把自定义 `ILoggerFactoryProvider` 挂到
`ArchitectureConfiguration.LoggerProperties.LoggerFactoryProvider` 或 `LoggerFactoryResolver.Provider`。

宿主包也可以提供自己的 appender。Godot 项目如果需要把 Core 日志管线输出到 Godot 控制台，可以引用
`GFramework.Godot.Logging.GodotLogAppender`，再用 `CompositeLogger` 或自定义 factory 把它和文件、JSON、异步输出等
Core 组件组合在同一条调用面下。

## 组合多个输出目标

需要同时写入宿主控制台和文件时，保留业务侧 `ILogger` 调用面不变，替换 provider 即可。下面的 provider 会为每个
logger 创建一个 `CompositeLogger`，并把同步的 Godot 控制台输出和异步文件输出组合在一起：

```csharp
using GFramework.Core.Abstractions.Logging;
using GFramework.Core.Logging;
using GFramework.Core.Logging.Appenders;
using GFramework.Core.Logging.Formatters;
using GFramework.Godot.Logging;

public sealed class GodotCompositeLoggerFactoryProvider : ILoggerFactoryProvider
{
    private readonly GodotLogAppender _godotAppender = new();
    private readonly AsyncLogAppender _fileAppender;

    public GodotCompositeLoggerFactoryProvider(string filePath)
    {
        _fileAppender = new AsyncLogAppender(new FileAppender(filePath, new DefaultLogFormatter()));
    }

    public LogLevel MinLevel { get; set; } = LogLevel.Info;

    public ILogger CreateLogger(string name)
    {
        return new CompositeLogger(
            name,
            MinLevel,
            _godotAppender,
            _fileAppender);
    }
}
```

把 provider 挂到架构配置时，传入已经解析好的普通文件系统路径：

```csharp
using GFramework.Core.Abstractions.Logging;
using GFramework.Core.Abstractions.Properties;
using GFramework.Core.Architectures;

var logPath = "path/to/game.log";
var configuration = new ArchitectureConfiguration
{
    LoggerProperties = new LoggerProperties
    {
        LoggerFactoryProvider = new GodotCompositeLoggerFactoryProvider(logPath)
        {
            MinLevel = LogLevel.Debug
        }
    }
};
```

`GodotLogAppender` 只负责 Godot 控制台落点；文件生命周期、异步缓冲、formatter 与过滤规则仍然来自 Core logging 组件。
Godot 项目的 `user://` 路径解析方式见 [Godot 日志集成](../godot/logging.md#_3-组合-godot-控制台和文件输出)。

## 什么时候该换 provider

下面这些场景通常不该只靠改 `MinLevel`：

- 需要文件输出、rolling file 或 async appender
- 需要按 namespace / level 做过滤
- 需要 JSON 格式日志
- 需要组合多个 appender
- 需要把输出落到 Godot、Unity 或其他宿主控制台

这时更合理的做法是保留 `ILogger` 调用面不变，只替换 provider / factory / formatter / appender 组合。
