Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix thread safe issue about console output #189

Merged
merged 1 commit into from
Dec 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 1 addition & 15 deletions src/ZLogger/AsyncStreamLineMessageWriter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,8 @@ public sealed class AsyncStreamLineMessageWriter : IAsyncLogProcessor, IAsyncDis
readonly Channel<IZLoggerEntry> channel;
readonly Task writeLoop;
readonly ZLoggerOptions options;
readonly Func<LogLevel, bool>? levelFilter;

public AsyncStreamLineMessageWriter(Stream stream, ZLoggerOptions options)
: this(stream, options, null)
{
}

// handling `Func<LogLevel, bool>? levelFilter` correctly is very context dependent.
// so only allows internal provider.
internal AsyncStreamLineMessageWriter(Stream stream, ZLoggerOptions options, Func<LogLevel, bool>? levelFilter = null)
public AsyncStreamLineMessageWriter(Stream stream, ZLoggerOptions options)
{
this.newLine = Encoding.UTF8.GetBytes(Environment.NewLine);
if (newLine.Length == 1)
Expand All @@ -46,7 +38,6 @@ internal AsyncStreamLineMessageWriter(Stream stream, ZLoggerOptions options, Fun

this.options = options;
this.stream = stream;
this.levelFilter = levelFilter;

channel = this.options.FullMode switch
{
Expand Down Expand Up @@ -121,7 +112,6 @@ async Task WriteLoop()
var writer = new StreamBufferWriter(stream);
var formatter = options.CreateFormatter();
var withLineBreak = formatter.WithLineBreak;
var requireFilterCheck = levelFilter != null;
var reader = channel.Reader;

while (await reader.WaitToReadAsync().ConfigureAwait(false))
Expand All @@ -130,10 +120,6 @@ async Task WriteLoop()
{
while (reader.TryRead(out var value))
{
if (requireFilterCheck && levelFilter!.Invoke(value.LogInfo.LogLevel) == false)
{
continue;
}
try
{
value.FormatUtf8(writer, formatter);
Expand Down
19 changes: 12 additions & 7 deletions src/ZLogger/Providers/ZLoggerConsoleLoggerProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,9 @@ public ZLoggerConsoleLoggerProvider(ZLoggerConsoleOptions options)
{
var logToStandardErrorThreshold = options.LogToStandardErrorThreshold;
processor = new DualAsyncLogProcessor(
new AsyncStreamLineMessageWriter(Console.OpenStandardOutput(), this.options, level => level < logToStandardErrorThreshold),
new AsyncStreamLineMessageWriter(Console.OpenStandardError(), this.options, level => level >= logToStandardErrorThreshold));
new AsyncStreamLineMessageWriter(Console.OpenStandardOutput(), this.options),
new AsyncStreamLineMessageWriter(Console.OpenStandardError(), this.options),
level => level < logToStandardErrorThreshold);
}
}

Expand All @@ -52,14 +53,18 @@ public void SetScopeProvider(IExternalScopeProvider scopeProvider)
this.scopeProvider = scopeProvider;
}

sealed class DualAsyncLogProcessor(AsyncStreamLineMessageWriter processor1, AsyncStreamLineMessageWriter processor2) : IAsyncLogProcessor
sealed class DualAsyncLogProcessor(AsyncStreamLineMessageWriter processor1, AsyncStreamLineMessageWriter processor2, Func<LogLevel, bool> levelFilter) : IAsyncLogProcessor
{
public void Post(IZLoggerEntry log)
{
// Post two entry is dangerous for log-state reference count cache.
// However filtered reader-loop does not call Return so ok to post multiple.
processor1.Post(log);
processor2.Post(log);
if (levelFilter(log.LogInfo.LogLevel))
{
processor1.Post(log);
}
else
{
processor2.Post(log);
}
}

public async ValueTask DisposeAsync()
Expand Down