fix(review-followup): 修复日志配置空项校验与文档示例

- 修复 ConfigurableLoggerFactory 对 null Appender 配置项的显式校验与 XML 契约
- 补充日志工厂针对 appenders 空项输入的回归测试
- 更新 Godot setting 文档中的 async 示例签名以匹配 ApplyAsync 用法
- 修正 AbstractArchitecture 中 ObserveDestroyCoreAsync 方法里调用await DestroyAsync() 未配置ConfigureAwait(false)的问题
This commit is contained in:
GeWuYou 2026-04-18 21:09:08 +08:00
parent 2c2df5de29
commit 5046c9752b
4 changed files with 30 additions and 6 deletions

View File

@ -36,6 +36,24 @@ public sealed class ConfigurableLoggerFactoryTests
});
}
/// <summary>
/// 验证当配置输入把 appenders 集合中的某个元素反序列化为 <see langword="null" /> 时,工厂会抛出可诊断异常。
/// </summary>
[Test]
public void CreateFactory_ShouldThrowInvalidOperationException_WhenAppenderEntryIsNull()
{
var config = LoggingConfigurationLoader.LoadFromJsonString(
"""
{
"appenders": [ null ]
}
""");
var exception = Assert.Throws<InvalidOperationException>(() => LoggingConfigurationLoader.CreateFactory(config));
Assert.That(exception!.Message, Is.EqualTo("Appender configuration cannot be null."));
}
/// <summary>
/// 验证在未命中命名空间覆盖时,调用方传入的默认最小级别会作为最终 logger 级别的下限参与计算。
/// </summary>

View File

@ -16,6 +16,8 @@ internal sealed class ConfigurableLoggerFactory : ILoggerFactory, IDisposable
/// 初始化一个基于日志配置创建输出管线的工厂实例。
/// </summary>
/// <param name="config">日志配置。</param>
/// <exception cref="ArgumentNullException"><paramref name="config"/> 为 <see langword="null" />。</exception>
/// <exception cref="InvalidOperationException">配置中的某个 Appender 项为 <see langword="null" />。</exception>
public ConfigurableLoggerFactory(LoggingConfiguration config)
{
_config = config ?? throw new ArgumentNullException(nameof(config));
@ -23,7 +25,13 @@ internal sealed class ConfigurableLoggerFactory : ILoggerFactory, IDisposable
// 反序列化输入可能显式把集合写成 null这里统一归一化为可安全枚举的空集合。
_config.Appenders ??= [];
_config.LoggerLevels ??= new Dictionary<string, LogLevel>(StringComparer.Ordinal);
_appenders = _config.Appenders.Select(LoggingConfigurationLoader.CreateAppender).ToArray();
// 外部配置可能把集合项反序列化为 null这里先给出可诊断异常避免后续工厂链路出现不清晰的空引用失败。
_appenders = _config.Appenders
.Select(static appenderConfig => appenderConfig ??
throw new InvalidOperationException("Appender configuration cannot be null."))
.Select(LoggingConfigurationLoader.CreateAppender)
.ToArray();
}
/// <summary>

View File

@ -2,8 +2,6 @@
using GFramework.Core.Abstractions.Environment;
using GFramework.Core.Architectures;
using GFramework.Core.Constants;
using GFramework.Godot.Extensions;
using Godot;
namespace GFramework.Godot.Architectures;
@ -165,7 +163,7 @@ public abstract class AbstractArchitecture(
{
try
{
await DestroyAsync();
await DestroyAsync().ConfigureAwait(false);
}
catch (Exception ex)
{

View File

@ -378,7 +378,7 @@ public class AudioManager : Node
);
}
private void SetMasterVolume(float linearVolume)
private async void SetMasterVolume(float linearVolume)
{
var settings = new AudioSettings { MasterVolume = linearVolume };
var audioSettings = new GodotAudioSettings(settings, new AudioBusMap());
@ -419,7 +419,7 @@ public class CustomAudioManager : Node
);
}
private void SetMasterVolume(float linearVolume)
private async void SetMasterVolume(float linearVolume)
{
var audioSettingsData = new AudioSettings { MasterVolume = linearVolume };
var audioSettings = new GodotAudioSettings(audioSettingsData, _customBusMap);