Skip to content

Commit 019c6fb

Browse files
committed
使控制台日志记录线程安全,使用 Builder 模式创建控制台日志记录器
1 parent 58ce875 commit 019c6fb

File tree

7 files changed

+305
-58
lines changed

7 files changed

+305
-58
lines changed

samples/LoggerSample.MainApp/Program.cs

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System;
2+
using System.Threading;
23
using System.Threading.Tasks;
34
using dotnetCampus.Logging.Attributes;
45
using dotnetCampus.Logging.Configurations;
@@ -23,26 +24,26 @@ public static void Main(string[] args)
2324
{
2425
LogLevel = LogLevel.Debug,
2526
})
26-
.AddWriter(new ConsoleLogger
27-
{
28-
// Options = new ConsoleLoggerOptions
29-
// {
30-
// IncludeScopes = true,
31-
// },
32-
})
27+
.AddConsoleLogger(b => b
28+
.WithThreadSafe(LogWritingThreadMode.ProducerConsumer)
29+
.FilterConsoleTagsFromCommandLineArgs(args))
3330
.AddBridge(LoggerBridgeLinker.Default)
3431
.Build()
3532
.IntoGlobalStaticLog();
3633

3734
Run();
35+
Thread.Sleep(5000);
3836
}
3937

4038
private static void Run()
4139
{
42-
Parallel.For(0, 0x00010000, i =>
40+
Log.Debug("[TEST] 开始");
41+
Parallel.For(0, 0x00004000, i =>
4342
{
43+
Thread.Sleep(1);
4444
Log.Debug($"[TEST] {DateTime.Now:HH:mm:ss}");
4545
});
46+
Log.Debug("[TEST] 完成");
4647
}
4748
}
4849

src/dotnetCampus.Logger/LoggerBuilder.cs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,13 @@
1-
using System;
2-
using System.Collections.Generic;
1+
using System.Collections.Generic;
32
using dotnetCampus.Logging.Bridges;
43
using dotnetCampus.Logging.Configurations;
5-
using dotnetCampus.Logging.Writers;
64

75
namespace dotnetCampus.Logging;
86

97
/// <summary>
108
/// 辅助创建日志记录器的构建器。
119
/// </summary>
12-
public class LoggerBuilder
10+
public sealed class LoggerBuilder
1311
{
1412
private LogOptions? _options;
1513
private readonly List<ILogger> _writers = [];

src/dotnetCampus.Logger/Writers/ConsoleLogger.cs

Lines changed: 59 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -16,18 +16,30 @@ public class ConsoleLogger : ILogger
1616
private int _isCursorMovementEnabled = 3;
1717

1818
private readonly RepeatLoggerDetector _repeat;
19-
private TagFilterManager? _tagFilterManager;
19+
20+
internal ConsoleLogger(ICoreLogWriter coreWriter, TagFilterManager? tagManager)
21+
{
22+
_repeat = new RepeatLoggerDetector(ClearAndMoveToLastLine);
23+
CoreWriter = coreWriter;
24+
TagManager = tagManager;
25+
}
2026

2127
/// <summary>
2228
/// 高于或等于此级别的日志才会被记录。
2329
/// </summary>
24-
public LogLevel Level { get; set; }
30+
public LogLevel Level { get; init; }
2531

26-
public ConsoleLogger()
27-
{
28-
_repeat = new(ClearAndMoveToLastLine);
29-
}
32+
/// <summary>
33+
/// 最终日志写入器。
34+
/// </summary>
35+
private ICoreLogWriter CoreWriter { get; }
36+
37+
/// <summary>
38+
/// 管理控制台日志的标签过滤。
39+
/// </summary>
40+
private TagFilterManager? TagManager { get; }
3041

42+
/// <inheritdoc />
3143
public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func<TState, Exception?, string> formatter)
3244
{
3345
if (logLevel < Level)
@@ -36,7 +48,7 @@ public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Except
3648
}
3749

3850
var message = formatter(state, exception);
39-
if (_tagFilterManager?.IsTagEnabled(message) is false)
51+
if (TagManager?.IsTagEnabled(message) is false)
4052
{
4153
return;
4254
}
@@ -53,7 +65,14 @@ public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Except
5365
});
5466
}
5567

56-
private void LogCore(LogLevel logLevel, Exception? exception, string message, Func<string, string?> formatter)
68+
/// <summary>
69+
/// 记录日志。在必要的情况下会保证线程安全。
70+
/// </summary>
71+
/// <param name="logLevel"></param>
72+
/// <param name="exception"></param>
73+
/// <param name="message"></param>
74+
/// <param name="formatter"></param>
75+
private void LogCore(LogLevel logLevel, Exception? exception, string message, Func<string, string?> formatter) => CoreWriter.Do(() =>
5776
{
5877
if (_repeat.RepeatOrResetLastLog(logLevel, message, exception) is var count and > 1)
5978
{
@@ -77,9 +96,15 @@ private void LogCore(LogLevel logLevel, Exception? exception, string message, Fu
7796
{tag}{exception}
7897
""", formatter);
7998
}
80-
}
99+
});
81100

82-
private static void ConsoleMultilineMessage(string message, Func<string, string?> formatter, bool forceSingleLine = false)
101+
/// <summary>
102+
/// 记录多行日志。
103+
/// </summary>
104+
/// <param name="message"></param>
105+
/// <param name="formatter"></param>
106+
/// <param name="forceSingleLine"></param>
107+
private void ConsoleMultilineMessage(string message, Func<string, string?> formatter, bool forceSingleLine = false)
83108
{
84109
if (forceSingleLine || !message.Contains('\n'))
85110
{
@@ -96,46 +121,34 @@ private static void ConsoleMultilineMessage(string message, Func<string, string?
96121
}
97122

98123
/// <summary>
99-
/// 高于或等于此级别的日志才会被记录。
100-
/// </summary>
101-
public ConsoleLogger UseLevel(LogLevel level)
102-
{
103-
Level = level;
104-
return this;
105-
}
106-
107-
/// <summary>
108-
/// 从命令行参数中提取过滤标签。
124+
/// 清空当前行并移动光标到上一行。
109125
/// </summary>
110-
/// <param name="args">命令行参数。</param>
111-
public ConsoleLogger FilterConsoleTagsFromCommandLineArgs(string[] args)
112-
{
113-
_tagFilterManager = TagFilterManager.FromCommandLineArgs(args);
114-
return this;
115-
}
116-
126+
/// <param name="repeatCount">此移动光标,是因为日志已重复第几次。</param>
117127
private void ClearAndMoveToLastLine(int repeatCount)
118128
{
119-
if (_isCursorMovementEnabled > 0 && repeatCount > 2)
129+
if (_isCursorMovementEnabled <= 0 || repeatCount <= 2)
120130
{
121-
try
122-
{
123-
var desiredY = Console.CursorTop - 1;
124-
var y = Math.Clamp(desiredY, 0, Console.WindowHeight - 1);
125-
Console.SetCursorPosition(0, y);
126-
Console.Write(new string(' ', Console.WindowWidth));
127-
Console.SetCursorPosition(0, y);
128-
}
129-
catch (IOException)
130-
{
131-
// 日志记录时,如果无法移动光标,说明可能当前输出位置不在缓冲区内。
132-
// 如果多次尝试失败,则认为当前控制台缓冲区不支持光标移动,遂放弃。
133-
_isCursorMovementEnabled--;
134-
}
135-
catch (ArgumentException)
136-
{
137-
// 日志记录时,有可能已经移动到头了,就不要移动了。
138-
}
131+
// 如果光标控制不可用,或者还没有重复次数,则不尝试移动光标。
132+
return;
133+
}
134+
135+
try
136+
{
137+
var desiredY = Console.CursorTop - 1;
138+
var y = Math.Clamp(desiredY, 0, Console.WindowHeight - 1);
139+
Console.SetCursorPosition(0, y);
140+
Console.Write(new string(' ', Console.WindowWidth));
141+
Console.SetCursorPosition(0, y);
142+
}
143+
catch (IOException)
144+
{
145+
// 日志记录时,如果无法移动光标,说明可能当前输出位置不在缓冲区内。
146+
// 如果多次尝试失败,则认为当前控制台缓冲区不支持光标移动,遂放弃。
147+
_isCursorMovementEnabled--;
148+
}
149+
catch (ArgumentException)
150+
{
151+
// 日志记录时,有可能已经移动到头了,就不要移动了。
139152
}
140153
}
141154

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
using System;
2+
using dotnetCampus.Logging.Writers.Helpers;
3+
4+
namespace dotnetCampus.Logging.Writers;
5+
6+
/// <summary>
7+
/// 辅助创建控制台日志记录器的构建器。
8+
/// </summary>
9+
public sealed class ConsoleLoggerBuilder
10+
{
11+
private TagFilterManager? _tagFilterManager;
12+
private ICoreLogWriter _coreWriter = new NotThreadSafeLogWriter();
13+
14+
/// <summary>
15+
/// 高于或等于此级别的日志才会被记录。
16+
/// </summary>
17+
public LogLevel Level { get; set; }
18+
19+
/// <summary>
20+
/// 高于或等于此级别的日志才会被记录。
21+
/// </summary>
22+
public ConsoleLoggerBuilder WithLevel(LogLevel level)
23+
{
24+
Level = level;
25+
return this;
26+
}
27+
28+
/// <summary>
29+
/// 指定控制台日志的线程安全模式。
30+
/// </summary>
31+
/// <param name="threadMode">线程安全模式。</param>
32+
/// <returns>构造器模式。</returns>
33+
/// <exception cref="ArgumentOutOfRangeException">线程安全模式不支持。</exception>
34+
public ConsoleLoggerBuilder WithThreadSafe(LogWritingThreadMode threadMode)
35+
{
36+
_coreWriter = threadMode switch
37+
{
38+
LogWritingThreadMode.NotThreadSafe => new NotThreadSafeLogWriter(),
39+
LogWritingThreadMode.Lock => new LockLogWriter(),
40+
LogWritingThreadMode.ProducerConsumer => new ProducerConsumerLogWriter(),
41+
_ => throw new ArgumentOutOfRangeException(nameof(threadMode)),
42+
};
43+
return this;
44+
}
45+
46+
/// <summary>
47+
/// 从命令行参数中提取过滤标签,使得控制台日志支持过滤标签行为。
48+
/// </summary>
49+
/// <param name="args">命令行参数。</param>
50+
/// <returns>构造器模式。</returns>
51+
public ConsoleLoggerBuilder FilterConsoleTagsFromCommandLineArgs(string[] args)
52+
{
53+
_tagFilterManager = TagFilterManager.FromCommandLineArgs(args);
54+
return this;
55+
}
56+
57+
/// <summary>
58+
/// 创建控制台日志记录器。
59+
/// </summary>
60+
/// <returns>控制台日志记录器。</returns>
61+
internal ConsoleLogger Build() => new(_coreWriter, _tagFilterManager)
62+
{
63+
Level = Level,
64+
};
65+
}
66+
67+
/// <summary>
68+
/// 辅助创建控制台日志记录器。
69+
/// </summary>
70+
public static class ConsoleLoggerBuilderExtensions
71+
{
72+
/// <summary>
73+
/// 添加控制台日志记录器。
74+
/// </summary>
75+
/// <param name="builder">日志构建器。</param>
76+
/// <param name="configure">配置控制台日志记录器。</param>
77+
/// <returns>日志构建器。</returns>
78+
public static LoggerBuilder AddConsoleLogger(this LoggerBuilder builder, Action<ConsoleLoggerBuilder> configure)
79+
{
80+
var consoleLoggerBuilder = new ConsoleLoggerBuilder();
81+
configure(consoleLoggerBuilder);
82+
return builder.AddWriter(consoleLoggerBuilder.Build());
83+
}
84+
}

0 commit comments

Comments
 (0)