Skip to content

Commit

Permalink
Merge pull request #16 from dotnet-campus/t/walterlv/filter
Browse files Browse the repository at this point in the history
添加更完善的日志过滤
  • Loading branch information
lindexi authored Aug 15, 2024
2 parents dce0c0d + 2ec3991 commit 2a32261
Show file tree
Hide file tree
Showing 6 changed files with 396 additions and 51 deletions.
44 changes: 3 additions & 41 deletions src/dotnetCampus.Logger/Writers/ConsoleLogger.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ public class ConsoleLogger : ILogger
private int _isCursorMovementEnabled = 3;

private readonly RepeatLoggerDetector _repeat;
private TagFilterManager? _tagFilterManager;

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

var message = formatter(state, exception);
if (!IsTagEnabled(message))
if (_tagFilterManager?.IsTagEnabled(message) is false)
{
return;
}
Expand Down Expand Up @@ -94,11 +95,6 @@ private static void ConsoleMultilineMessage(string message, Func<string, string?
}
}

/// <summary>
/// 当前已设置的过滤标签。
/// </summary>
private static ImmutableHashSetString ConsoleFilterTags { get; set; } = [];

/// <summary>
/// 高于或等于此级别的日志才会被记录。
/// </summary>
Expand All @@ -114,44 +110,10 @@ public ConsoleLogger UseLevel(LogLevel level)
/// <param name="args">命令行参数。</param>
public ConsoleLogger FilterConsoleTagsFromCommandLineArgs(string[] args)
{
for (var i = 0; i < args.Length; i++)
{
if (args[i] == "--log-console-tags" && i + 1 < args.Length)
{
ConsoleFilterTags = args[i + 1].Split([',', ';', ' ']).ToImmutableHashSet();
break;
}
}
_tagFilterManager = TagFilterManager.FromCommandLineArgs(args);
return this;
}

/// <summary>
/// 判断某个日志是否满足当前标签过滤条件。
/// </summary>
/// <param name="text">要判断的日志原文。</param>
/// <returns>是否满足过滤条件。</returns>
private static bool IsTagEnabled(string text)
{
if (ConsoleFilterTags.Count is 0)
{
return true;
}

var start = text.IndexOf('[');
if (start == -1)
{
return true;
}
var end = text.IndexOf(']', start);
if (end == -1)
{
return true;
}

var tag = text.AsSpan().Slice(start + 1, end - start - 1);
return ConsoleFilterTags.Contains(tag.ToString());
}

private void ClearAndMoveToLastLine(int repeatCount)
{
if (_isCursorMovementEnabled > 0 && repeatCount > 2)
Expand Down
162 changes: 162 additions & 0 deletions src/dotnetCampus.Logger/Writers/Helpers/TagFilterManager.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
using System;
using System.Collections.Generic;
using System.Linq;

namespace dotnetCampus.Logging.Writers.Helpers;

internal class TagFilterManager
{
public const string LogTagParameterName = "--log-console-tags";

/// <summary>
/// 当前已设置的任一标签。(无前缀)
/// </summary>
public required ImmutableHashSetString AnyFilterTags { get; init; }

/// <summary>
/// 当前已设置的包含标签。(前缀为 +)
/// </summary>
public required ImmutableHashSetString IncludingFilterTags { get; init; }

/// <summary>
/// 当前已设置的排除标签。(前缀为 -)
/// </summary>
public required ImmutableHashSetString ExcludingFilterTags { get; init; }

/// <summary>
/// 判断某个日志是否满足当前标签过滤条件。
/// </summary>
/// <param name="text">要判断的日志原文。</param>
/// <returns>是否满足过滤条件。</returns>
/// <remarks>
/// 匹配原则:
/// <list type="number">
/// <item>先看任一标签进行初筛:只要有一个标签匹配,即选出;但如果没有指定任一标签,则全部选出。</item>
/// <item>在前一个初筛的基础上,再看排除标签:只要有一个标签匹配,即排除。</item>
/// <item>在前两个筛选的基础上,再看包含标签:必须全部标签匹配,才选出,其他全部排除。</item>
/// </list>
/// </remarks>
internal bool IsTagEnabled(string text)
{
if (AnyFilterTags.Count is 0 && ExcludingFilterTags.Count is 0 && IncludingFilterTags.Count is 0)
{
return true;
}

var 任一满足 = AnyFilterTags.Count is 0;
var 包含满足 = IncludingFilterTags.Count is 0;

var currentTagStartIndex = -1;
var isInTag = false;
List<string> includingTags = IncludingFilterTags.ToList();
for (var i = 0; i < text.Length; i++)
{
if (text[i] == '[')
{
// 进入标签。
currentTagStartIndex = i;
isInTag = true;
}
else if (text[i] == ']')
{
// 离开标签。
var currentTagEndIndex = i;
isInTag = false;
if (currentTagStartIndex < 0)
{
return 任一满足;
}
var tag = text.AsSpan().Slice(currentTagStartIndex + 1, currentTagEndIndex - currentTagStartIndex - 1).ToString();
// 只要有一个排除标签匹配,就不输出。
if (ExcludingFilterTags.Contains(tag))
{
return false;
}
// 如果有任一标签,则匹配一个即可。
任一满足 = 任一满足 || AnyFilterTags.Contains(tag);
if (任一满足)
{
// 如果有包含标签,则匹配一个,直到全部匹配。
if (!包含满足 && IncludingFilterTags.Count > 0)
{
if (includingTags.Contains(tag))
{
includingTags.Remove(tag);
}
if (includingTags.Count is 0)
{
包含满足 = true;
}
}
}
}
else if (char.IsWhiteSpace(text[i]))
{
// 空白字符,不处理。
}
else if (!isInTag)
{
// 当前不在标签内,且非空白字符,直接跳出。
return 任一满足 && 包含满足;
}
}
return 任一满足 && 包含满足;
}

/// <summary>
/// 从命令行参数中提取过滤标签。
/// </summary>
/// <param name="args">命令行参数。</param>
public static TagFilterManager? FromCommandLineArgs(string[] args)
{
HashSet<string> anyFilterTags = [];
HashSet<string> includingFilterTags = [];
HashSet<string> excludingFilterTags = [];
for (var i = 0; i < args.Length; i++)
{
if (args[i] != LogTagParameterName || i + 1 >= args.Length)
{
continue;
}

var filterTags = args[i + 1].Split([',', ';', ' ']);
foreach (var tag in filterTags)
{
if (tag.StartsWith("-", StringComparison.Ordinal))
{
#if NET8_0_OR_GREATER
excludingFilterTags.Add(tag[1..]);
#else
excludingFilterTags.Add(tag.Substring(1));
#endif
}
else if (tag.StartsWith("+", StringComparison.Ordinal))
{
#if NET8_0_OR_GREATER
includingFilterTags.Add(tag[1..]);
#else
includingFilterTags.Add(tag.Substring(1));
#endif
}
else
{
anyFilterTags.Add(tag);
}
}

return new TagFilterManager
{
AnyFilterTags = anyFilterTags.ToImmutableHashSet(),
IncludingFilterTags = includingFilterTags.ToImmutableHashSet(),
ExcludingFilterTags = excludingFilterTags.ToImmutableHashSet(),
};
}

return new TagFilterManager
{
AnyFilterTags = [],
IncludingFilterTags = [],
ExcludingFilterTags = [],
};
}
}
4 changes: 4 additions & 0 deletions src/dotnetCampus.Logger/dotnetCampus.Logger.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,10 @@
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0" PrivateAssets="All" />
</ItemGroup>

<ItemGroup>
<InternalsVisibleTo Include="dotnetCampus.Logger.Tests" />
</ItemGroup>

<!-- 生成 NuGet 包。 -->
<Target Name="_IncludeAllDependencies" BeforeTargets="_GetPackageFiles">
<ItemGroup>
Expand Down
Loading

0 comments on commit 2a32261

Please sign in to comment.