В этой теме - Должен ли NLog flu sh все сообщения в очереди в AsyncTargetWrapper при вызове Flu sh ()? - я читал, что «LogManager
устанавливает для конфигурации значение null на выгрузка домена или выход из процесса »(см. раздел« Правка »в первом ответе). В моем понимании это должно привести к тому, что все ожидающие записи журнала будут записаны в зарегистрированные цели. Однако после тестирования с FileTarget
, обернутым с AsyncTargetWrapper
, это не выполняется. Я создал минимальное репро на GitHub - https://github.com/PaloMraz/NLogMultiProcessTargetsSample, который работает следующим образом:
LogLib
- это .netstandard2.0
библиотека, ссылающаяся на NLog
4.6.8 пакет NuGet и разоблачение класс CompositeLogger
, который программно настраивает цели NLog
:
public class CompositeLogger
{
private readonly ILogger _logger;
public CompositeLogger(string logFilePath)
{
var fileTarget = new FileTarget("file")
{
FileName = logFilePath,
AutoFlush = true
};
var asyncTargetWrapper = new AsyncTargetWrapper("async", fileTarget)
{
OverflowAction = AsyncTargetWrapperOverflowAction.Discard
};
var config = new LoggingConfiguration();
config.AddTarget(asyncTargetWrapper);
config.AddRuleForAllLevels(asyncTargetWrapper);
LogManager.Configuration = config;
this._logger = LogManager.GetLogger("Default");
}
public void Log(string message) => this._logger.Trace(message);
}
LogConsoleRunner
- это консольное приложение. NET Framework 4.8, которое использует LogLib.CompositeLogger
для записи указанного количества сообщений журнала в файл (указанный в качестве аргумента командной строки) с небольшой задержкой между операциями записи:
public static class Program
{
public const int LogWritesCount = 10;
public static readonly TimeSpan DelayBetweenLogWrites = TimeSpan.FromMilliseconds(25);
static async Task Main(string[] args)
{
string logFilePath = args.FirstOrDefault();
if (string.IsNullOrWhiteSpace(logFilePath))
{
throw new InvalidOperationException("Must specify logging file path as an argument.");
}
logFilePath = Path.GetFullPath(logFilePath);
Process currentProcess = Process.GetCurrentProcess();
var logger = new CompositeLogger(logFilePath);
for(int i = 0; i < LogWritesCount; i++)
{
logger.Log($"Message from {currentProcess.ProcessName}#{currentProcess.Id} at {DateTimeOffset.Now:O}");
await Task.Delay(DelayBetweenLogWrites);
}
}
}
Наконец, LogTest
- это XUnit
тестовая сборка с одним тестом, запускающим десять LogConsoleRunner
записей, записывающих в одну и ту же файл журнала:
[Fact]
public async Task LaunchMultipleRunners()
{
string logFilePath = Path.GetTempFileName();
using var ensureLogFileDisposed = new Nito.Disposables.AnonymousDisposable(() => File.Delete(logFilePath));
string logConsoleRunnerAppExePath = Path.GetFullPath(
Path.Combine(
Path.GetDirectoryName(this.GetType().Assembly.Location),
@"..\..\..\..\LogConsoleRunner\bin\Debug\LogConsoleRunner.exe"));
var startInfo = new ProcessStartInfo(logConsoleRunnerAppExePath)
{
Arguments = logFilePath,
UseShellExecute = false
};
const int LaunchProcessCount = 10;
Process[] processes = Enumerable
.Range(0, LaunchProcessCount)
.Select(i => Process.Start(startInfo))
.ToArray();
while (!processes.All(p => p.HasExited))
{
await Task.Delay(LogConsoleRunner.Program.DelayBetweenLogWrites);
}
string[] lines = File.ReadAllLines(logFilePath);
Assert.Equal(LaunchProcessCount * LogConsoleRunner.Program.LogWritesCount, lines.Length);
}
Ошибка Assert.Equal
в последней строке всегда завершается ошибкой, поскольку в целевом файле всегда записывается меньше строк, чем ожидаемое число, равное 100. На моем компьютере оно меняется при каждом запуске между 96 - 99, но никогда не содержит все 100 строк.
Мой вопрос: как мне настроить NLog
, чтобы после завершения всех процессов все ожидающие записи журнала записывались в целевой файл журнала? * 1 034 *