Skip to content

Commit 2a32261

Browse files
authored
Merge pull request #16 from dotnet-campus/t/walterlv/filter
添加更完善的日志过滤
2 parents dce0c0d + 2ec3991 commit 2a32261

File tree

6 files changed

+396
-51
lines changed

6 files changed

+396
-51
lines changed

src/dotnetCampus.Logger/Writers/ConsoleLogger.cs

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

1818
private readonly RepeatLoggerDetector _repeat;
19+
private TagFilterManager? _tagFilterManager;
1920

2021
/// <summary>
2122
/// 高于或等于此级别的日志才会被记录。
@@ -35,7 +36,7 @@ public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Except
3536
}
3637

3738
var message = formatter(state, exception);
38-
if (!IsTagEnabled(message))
39+
if (_tagFilterManager?.IsTagEnabled(message) is false)
3940
{
4041
return;
4142
}
@@ -94,11 +95,6 @@ private static void ConsoleMultilineMessage(string message, Func<string, string?
9495
}
9596
}
9697

97-
/// <summary>
98-
/// 当前已设置的过滤标签。
99-
/// </summary>
100-
private static ImmutableHashSetString ConsoleFilterTags { get; set; } = [];
101-
10298
/// <summary>
10399
/// 高于或等于此级别的日志才会被记录。
104100
/// </summary>
@@ -114,44 +110,10 @@ public ConsoleLogger UseLevel(LogLevel level)
114110
/// <param name="args">命令行参数。</param>
115111
public ConsoleLogger FilterConsoleTagsFromCommandLineArgs(string[] args)
116112
{
117-
for (var i = 0; i < args.Length; i++)
118-
{
119-
if (args[i] == "--log-console-tags" && i + 1 < args.Length)
120-
{
121-
ConsoleFilterTags = args[i + 1].Split([',', ';', ' ']).ToImmutableHashSet();
122-
break;
123-
}
124-
}
113+
_tagFilterManager = TagFilterManager.FromCommandLineArgs(args);
125114
return this;
126115
}
127116

128-
/// <summary>
129-
/// 判断某个日志是否满足当前标签过滤条件。
130-
/// </summary>
131-
/// <param name="text">要判断的日志原文。</param>
132-
/// <returns>是否满足过滤条件。</returns>
133-
private static bool IsTagEnabled(string text)
134-
{
135-
if (ConsoleFilterTags.Count is 0)
136-
{
137-
return true;
138-
}
139-
140-
var start = text.IndexOf('[');
141-
if (start == -1)
142-
{
143-
return true;
144-
}
145-
var end = text.IndexOf(']', start);
146-
if (end == -1)
147-
{
148-
return true;
149-
}
150-
151-
var tag = text.AsSpan().Slice(start + 1, end - start - 1);
152-
return ConsoleFilterTags.Contains(tag.ToString());
153-
}
154-
155117
private void ClearAndMoveToLastLine(int repeatCount)
156118
{
157119
if (_isCursorMovementEnabled > 0 && repeatCount > 2)
Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
5+
namespace dotnetCampus.Logging.Writers.Helpers;
6+
7+
internal class TagFilterManager
8+
{
9+
public const string LogTagParameterName = "--log-console-tags";
10+
11+
/// <summary>
12+
/// 当前已设置的任一标签。(无前缀)
13+
/// </summary>
14+
public required ImmutableHashSetString AnyFilterTags { get; init; }
15+
16+
/// <summary>
17+
/// 当前已设置的包含标签。(前缀为 +)
18+
/// </summary>
19+
public required ImmutableHashSetString IncludingFilterTags { get; init; }
20+
21+
/// <summary>
22+
/// 当前已设置的排除标签。(前缀为 -)
23+
/// </summary>
24+
public required ImmutableHashSetString ExcludingFilterTags { get; init; }
25+
26+
/// <summary>
27+
/// 判断某个日志是否满足当前标签过滤条件。
28+
/// </summary>
29+
/// <param name="text">要判断的日志原文。</param>
30+
/// <returns>是否满足过滤条件。</returns>
31+
/// <remarks>
32+
/// 匹配原则:
33+
/// <list type="number">
34+
/// <item>先看任一标签进行初筛:只要有一个标签匹配,即选出;但如果没有指定任一标签,则全部选出。</item>
35+
/// <item>在前一个初筛的基础上,再看排除标签:只要有一个标签匹配,即排除。</item>
36+
/// <item>在前两个筛选的基础上,再看包含标签:必须全部标签匹配,才选出,其他全部排除。</item>
37+
/// </list>
38+
/// </remarks>
39+
internal bool IsTagEnabled(string text)
40+
{
41+
if (AnyFilterTags.Count is 0 && ExcludingFilterTags.Count is 0 && IncludingFilterTags.Count is 0)
42+
{
43+
return true;
44+
}
45+
46+
var 任一满足 = AnyFilterTags.Count is 0;
47+
var 包含满足 = IncludingFilterTags.Count is 0;
48+
49+
var currentTagStartIndex = -1;
50+
var isInTag = false;
51+
List<string> includingTags = IncludingFilterTags.ToList();
52+
for (var i = 0; i < text.Length; i++)
53+
{
54+
if (text[i] == '[')
55+
{
56+
// 进入标签。
57+
currentTagStartIndex = i;
58+
isInTag = true;
59+
}
60+
else if (text[i] == ']')
61+
{
62+
// 离开标签。
63+
var currentTagEndIndex = i;
64+
isInTag = false;
65+
if (currentTagStartIndex < 0)
66+
{
67+
return 任一满足;
68+
}
69+
var tag = text.AsSpan().Slice(currentTagStartIndex + 1, currentTagEndIndex - currentTagStartIndex - 1).ToString();
70+
// 只要有一个排除标签匹配,就不输出。
71+
if (ExcludingFilterTags.Contains(tag))
72+
{
73+
return false;
74+
}
75+
// 如果有任一标签,则匹配一个即可。
76+
任一满足 = 任一满足 || AnyFilterTags.Contains(tag);
77+
if (任一满足)
78+
{
79+
// 如果有包含标签,则匹配一个,直到全部匹配。
80+
if (!包含满足 && IncludingFilterTags.Count > 0)
81+
{
82+
if (includingTags.Contains(tag))
83+
{
84+
includingTags.Remove(tag);
85+
}
86+
if (includingTags.Count is 0)
87+
{
88+
包含满足 = true;
89+
}
90+
}
91+
}
92+
}
93+
else if (char.IsWhiteSpace(text[i]))
94+
{
95+
// 空白字符,不处理。
96+
}
97+
else if (!isInTag)
98+
{
99+
// 当前不在标签内,且非空白字符,直接跳出。
100+
return 任一满足 && 包含满足;
101+
}
102+
}
103+
return 任一满足 && 包含满足;
104+
}
105+
106+
/// <summary>
107+
/// 从命令行参数中提取过滤标签。
108+
/// </summary>
109+
/// <param name="args">命令行参数。</param>
110+
public static TagFilterManager? FromCommandLineArgs(string[] args)
111+
{
112+
HashSet<string> anyFilterTags = [];
113+
HashSet<string> includingFilterTags = [];
114+
HashSet<string> excludingFilterTags = [];
115+
for (var i = 0; i < args.Length; i++)
116+
{
117+
if (args[i] != LogTagParameterName || i + 1 >= args.Length)
118+
{
119+
continue;
120+
}
121+
122+
var filterTags = args[i + 1].Split([',', ';', ' ']);
123+
foreach (var tag in filterTags)
124+
{
125+
if (tag.StartsWith("-", StringComparison.Ordinal))
126+
{
127+
#if NET8_0_OR_GREATER
128+
excludingFilterTags.Add(tag[1..]);
129+
#else
130+
excludingFilterTags.Add(tag.Substring(1));
131+
#endif
132+
}
133+
else if (tag.StartsWith("+", StringComparison.Ordinal))
134+
{
135+
#if NET8_0_OR_GREATER
136+
includingFilterTags.Add(tag[1..]);
137+
#else
138+
includingFilterTags.Add(tag.Substring(1));
139+
#endif
140+
}
141+
else
142+
{
143+
anyFilterTags.Add(tag);
144+
}
145+
}
146+
147+
return new TagFilterManager
148+
{
149+
AnyFilterTags = anyFilterTags.ToImmutableHashSet(),
150+
IncludingFilterTags = includingFilterTags.ToImmutableHashSet(),
151+
ExcludingFilterTags = excludingFilterTags.ToImmutableHashSet(),
152+
};
153+
}
154+
155+
return new TagFilterManager
156+
{
157+
AnyFilterTags = [],
158+
IncludingFilterTags = [],
159+
ExcludingFilterTags = [],
160+
};
161+
}
162+
}

src/dotnetCampus.Logger/dotnetCampus.Logger.csproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,10 @@
4444
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0" PrivateAssets="All" />
4545
</ItemGroup>
4646

47+
<ItemGroup>
48+
<InternalsVisibleTo Include="dotnetCampus.Logger.Tests" />
49+
</ItemGroup>
50+
4751
<!-- 生成 NuGet 包。 -->
4852
<Target Name="_IncludeAllDependencies" BeforeTargets="_GetPackageFiles">
4953
<ItemGroup>

0 commit comments

Comments
 (0)