Skip to content

Commit b6c74b7

Browse files
committed
feat(logger): 添加临时日志记录器支持初始化前日志缓冲
- 新增 InternalTempLogger 类用于在正式日志系统初始化前缓存日志 - 新增 LoggerMemorySink 内存日志接收器实现日志事件缓冲 - LogHelper 支持将临时缓存的日志自动转移到正式日志系统 - 添加 Debug 配置的 XML 文档生成支持
1 parent b49100e commit b6c74b7

4 files changed

Lines changed: 232 additions & 9 deletions

File tree

GameFrameX.Foundation.Logger/GameFrameX.Foundation.Logger.csproj

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@
88
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
99
<OutputPath>..\bin\app</OutputPath>
1010
</PropertyGroup>
11+
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
12+
<DocumentationFile>bin\Debug\GameFrameX.Foundation.Logger.xml</DocumentationFile>
13+
</PropertyGroup>
1114
<ItemGroup>
1215
<None Include="..\logo.png">
1316
<Pack>True</Pack>
@@ -22,14 +25,14 @@
2225
</ItemGroup>
2326

2427
<ItemGroup>
25-
<PackageReference Include="Serilog.AspNetCore" Version="10.0.0" />
26-
<PackageReference Include="Serilog.Sinks.Grafana.Loki" Version="8.3.2" />
27-
<PackageReference Include="Serilog.Sinks.MongoDB" Version="7.2.0" />
28+
<PackageReference Include="Serilog.AspNetCore" Version="10.0.0"/>
29+
<PackageReference Include="Serilog.Sinks.Grafana.Loki" Version="8.3.2"/>
30+
<PackageReference Include="Serilog.Sinks.MongoDB" Version="7.2.0"/>
2831
</ItemGroup>
2932

3033
<ItemGroup>
31-
<ProjectReference Include="..\GameFrameX.Foundation.Extensions\GameFrameX.Foundation.Extensions.csproj" />
32-
<ProjectReference Include="..\GameFrameX.Foundation.Json\GameFrameX.Foundation.Json.csproj" />
34+
<ProjectReference Include="..\GameFrameX.Foundation.Extensions\GameFrameX.Foundation.Extensions.csproj"/>
35+
<ProjectReference Include="..\GameFrameX.Foundation.Json\GameFrameX.Foundation.Json.csproj"/>
3336
</ItemGroup>
3437

3538
</Project>
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
// ==========================================================================================
2+
// GameFrameX 组织及其衍生项目的版权、商标、专利及其他相关权利
3+
// GameFrameX organization and its derivative projects' copyrights, trademarks, patents, and related rights
4+
// 均受中华人民共和国及相关国际法律法规保护。
5+
// are protected by the laws of the People's Republic of China and relevant international regulations.
6+
//
7+
// 使用本项目须严格遵守相应法律法规及开源许可证之规定。
8+
// Usage of this project must strictly comply with applicable laws, regulations, and open-source licenses.
9+
//
10+
// 本项目采用 MIT 许可证与 Apache License 2.0 双许可证分发,
11+
// This project is dual-licensed under the MIT License and Apache License 2.0,
12+
// 完整许可证文本请参见源代码根目录下的 LICENSE 文件。
13+
// please refer to the LICENSE file in the root directory of the source code for the full license text.
14+
//
15+
// 禁止利用本项目实施任何危害国家安全、破坏社会秩序、
16+
// It is prohibited to use this project to engage in any activities that endanger national security, disrupt social order,
17+
// 侵犯他人合法权益等法律法规所禁止的行为!
18+
// or infringe upon the legitimate rights and interests of others, as prohibited by laws and regulations!
19+
// 因基于本项目二次开发所产生的一切法律纠纷与责任,
20+
// Any legal disputes and liabilities arising from secondary development based on this project
21+
// 本项目组织与贡献者概不承担。
22+
// shall be borne solely by the developer; the project organization and contributors assume no responsibility.
23+
//
24+
// GitHub 仓库:https://github.com/GameFrameX
25+
// GitHub Repository: https://github.com/GameFrameX
26+
// Gitee 仓库:https://gitee.com/GameFrameX
27+
// Gitee Repository: https://gitee.com/GameFrameX
28+
// CNB 仓库:https://cnb.cool/GameFrameX
29+
// CNB Repository: https://cnb.cool/GameFrameX
30+
// 官方文档:https://gameframex.doc.alianblank.com/
31+
// Official Documentation: https://gameframex.doc.alianblank.com/
32+
// ==========================================================================================
33+
34+
using System;
35+
using Serilog;
36+
using Serilog.Events;
37+
using Serilog.Core;
38+
39+
namespace GameFrameX.Foundation.Logger.Internal;
40+
41+
internal sealed class InternalTempLogger : IDisposable
42+
{
43+
private readonly ILogger _logger;
44+
private readonly LoggerMemorySink _buffer;
45+
private bool _isFlushed;
46+
private bool _isDisposed;
47+
48+
public ILogger Logger
49+
{
50+
get { return _logger; }
51+
}
52+
53+
public LoggerMemorySink Buffer
54+
{
55+
get { return _buffer; }
56+
}
57+
58+
public InternalTempLogger()
59+
{
60+
_buffer = new LoggerMemorySink(OnLogEvent);
61+
_logger = new LoggerConfiguration()
62+
.MinimumLevel.Verbose()
63+
.WriteTo.Sink(_buffer)
64+
.CreateLogger();
65+
}
66+
67+
private void OnLogEvent(LogEvent evt)
68+
{
69+
Console.WriteLine(evt.RenderMessage());
70+
}
71+
72+
public void FlushTo(ILogger targetLogger)
73+
{
74+
if (_isFlushed || _isDisposed)
75+
{
76+
return;
77+
}
78+
79+
foreach (var evt in _buffer.GetEvents())
80+
{
81+
targetLogger.Write(evt);
82+
}
83+
84+
_isFlushed = true;
85+
}
86+
87+
public void Dispose()
88+
{
89+
if (_isDisposed)
90+
{
91+
return;
92+
}
93+
94+
if (_logger is IDisposable disposable)
95+
{
96+
disposable.Dispose();
97+
}
98+
99+
_isDisposed = true;
100+
}
101+
}
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
// ==========================================================================================
2+
// GameFrameX 组织及其衍生项目的版权、商标、专利及其他相关权利
3+
// GameFrameX organization and its derivative projects' copyrights, trademarks, patents, and related rights
4+
// 均受中华人民共和国及相关国际法律法规保护。
5+
// are protected by the laws of the People's Republic of China and relevant international regulations.
6+
//
7+
// 使用本项目须严格遵守相应法律法规及开源许可证之规定。
8+
// Usage of this project must strictly comply with applicable laws, regulations, and open-source licenses.
9+
//
10+
// 本项目采用 MIT 许可证与 Apache License 2.0 双许可证分发,
11+
// This project is dual-licensed under the MIT License and Apache License 2.0,
12+
// 完整许可证文本请参见源代码根目录下的 LICENSE 文件。
13+
// please refer to the LICENSE file in the root directory of the source code for the full license text.
14+
//
15+
// 禁止利用本项目实施任何危害国家安全、破坏社会秩序、
16+
// It is prohibited to use this project to engage in any activities that endanger national security, disrupt social order,
17+
// 侵犯他人合法权益等法律法规所禁止的行为!
18+
// or infringe upon the legitimate rights and interests of others, as prohibited by laws and regulations!
19+
// 因基于本项目二次开发所产生的一切法律纠纷与责任,
20+
// Any legal disputes and liabilities arising from secondary development based on this project
21+
// 本项目组织与贡献者概不承担。
22+
// shall be borne solely by the developer; the project organization and contributors assume no responsibility.
23+
//
24+
// GitHub 仓库:https://github.com/GameFrameX
25+
// GitHub Repository: https://github.com/GameFrameX
26+
// Gitee 仓库:https://gitee.com/GameFrameX
27+
// Gitee Repository: https://gitee.com/GameFrameX
28+
// CNB 仓库:https://cnb.cool/GameFrameX
29+
// CNB Repository: https://cnb.cool/GameFrameX
30+
// 官方文档:https://gameframex.doc.alianblank.com/
31+
// Official Documentation: https://gameframex.doc.alianblank.com/
32+
// ==========================================================================================
33+
34+
using System.Collections.Concurrent;
35+
using Serilog;
36+
using Serilog.Core;
37+
using Serilog.Events;
38+
39+
namespace GameFrameX.Foundation.Logger.Internal;
40+
41+
/// <summary>
42+
/// 内存日志事件接收器,用于临时缓冲日志事件。
43+
/// </summary>
44+
/// <remarks>
45+
/// <para>该类在日志系统正式初始化前使用,用于:</para>
46+
/// <list type="number">
47+
/// <item><description>缓存日志事件到内存队列</description></item>
48+
/// <item><description>通过回调函数实时输出日志到控制台</description></item>
49+
/// <item><description>支持将缓存的日志事件批量转移到正式日志系统</description></item>
50+
/// </list>
51+
/// <para>当正式日志系统初始化完成后,通过 <see cref="GetEvents"/> 方法获取所有缓存的事件并转移到新日志器。</para>
52+
/// </remarks>
53+
public sealed class LoggerMemorySink : ILogEventSink
54+
{
55+
private readonly ConcurrentQueue<LogEvent> _events = new();
56+
private readonly Action<LogEvent> _onEmit;
57+
58+
/// <summary>
59+
/// 初始化 <see cref="LoggerMemorySink"/> 类的新实例。
60+
/// </summary>
61+
/// <param name="onEmit">可选的回调函数,在每个日志事件被接收时触发。</param>
62+
/// <remarks>
63+
/// 如果提供了 <paramref name="onEmit"/> 回调,每次调用 <see cref="Emit"/> 时都会触发该回调,
64+
/// 可用于实时输出日志到控制台。
65+
/// </remarks>
66+
public LoggerMemorySink(Action<LogEvent> onEmit = null)
67+
{
68+
_onEmit = onEmit;
69+
}
70+
71+
/// <summary>
72+
/// 接收一个日志事件。
73+
/// </summary>
74+
/// <param name="logEvent">要接收的日志事件。</param>
75+
/// <remarks>
76+
/// 将日志事件加入内部队列,并可选地触发回调函数。
77+
/// </remarks>
78+
public void Emit(LogEvent logEvent)
79+
{
80+
_events.Enqueue(logEvent);
81+
_onEmit?.Invoke(logEvent);
82+
}
83+
84+
/// <summary>
85+
/// 获取并移除所有缓存的日志事件。
86+
/// </summary>
87+
/// <returns>包含所有缓存日志事件的枚举器。</returns>
88+
/// <remarks>
89+
/// 调用此方法后,内部队列会被清空。
90+
/// 该方法主要用于将临时缓存的日志事件转移到正式日志系统。
91+
/// </remarks>
92+
public IEnumerable<LogEvent> GetEvents()
93+
{
94+
while (_events.TryDequeue(out var evt))
95+
{
96+
yield return evt;
97+
}
98+
}
99+
}

GameFrameX.Foundation.Logger/LogHelper.cs

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
using System.Diagnostics;
3535
using System.Text;
3636
using Serilog;
37+
using GameFrameX.Foundation.Logger.Internal;
3738

3839
namespace GameFrameX.Foundation.Logger;
3940

@@ -54,7 +55,7 @@ public static partial class LogHelper
5455
/// </remarks>
5556
public static void FlushAndSave()
5657
{
57-
Serilog.Log.CloseAndFlush();
58+
Log.CloseAndFlush();
5859
}
5960

6061
/// <summary>
@@ -65,14 +66,19 @@ public static void FlushAndSave()
6566
/// </remarks>
6667
public static async void CloseAndFlushAsync()
6768
{
68-
await Serilog.Log.CloseAndFlushAsync();
69+
await Log.CloseAndFlushAsync();
6970
}
7071

7172
/// <summary>
7273
/// 内部日志记录器实例
7374
/// </summary>
7475
private static ILogger _logger;
7576

77+
/// <summary>
78+
/// 临时日志记录器实例,用于在正式日志系统初始化前提供日志输出
79+
/// </summary>
80+
private static InternalTempLogger _tempLogger;
81+
7682
/// <summary>
7783
/// 用于保护 _logger 字段的锁对象
7884
/// </summary>
@@ -88,19 +94,33 @@ public static void SetLogger(ILogger logger)
8894
ArgumentNullException.ThrowIfNull(logger);
8995
lock (_loggerLock)
9096
{
97+
_tempLogger?.FlushTo(logger);
98+
_tempLogger?.Dispose();
99+
_tempLogger = null;
100+
91101
_logger = logger;
92102
}
93103
}
94104

95105
/// <summary>
96106
/// 获取当前使用的日志记录器
97107
/// </summary>
98-
/// <returns>返回当前设置的日志记录器,如果未设置则返回Serilog的默认Logger</returns>
108+
/// <returns>返回当前设置的日志记录器,如果未设置则返回临时日志记录器</returns>
99109
private static ILogger GetLogger()
100110
{
101111
lock (_loggerLock)
102112
{
103-
return _logger ?? Log.Logger;
113+
if (_logger != null)
114+
{
115+
return _logger;
116+
}
117+
118+
if (_tempLogger == null)
119+
{
120+
_tempLogger = new InternalTempLogger();
121+
}
122+
123+
return _tempLogger.Logger;
104124
}
105125
}
106126

0 commit comments

Comments
 (0)