NLog 更改目标文件名后写入不同的日志

NLog Log writing to different after changing filename of a target

我已经提到了这个 Update NLog target filename at runtime 和许多其他链接,但 none 似乎适用于我的情况。 我使用

初始化记录器
private Logger logger = LogManager.GetCurrentClassLogger();

所有日志消息都排队,然后使用计时器清空。

this.messageQueue = new ConcurrentQueue<NotificationMessagePacket>();
this.timer = new System.Timers.Timer(1000);
this.timer.Elapsed += (o, e) =>
{
    if (!isWriting)
    {
        lock (_lockObject)
        {
            isWriting = true;
            NotificationMessagePacket packet;
            while (this.messageQueue.TryDequeue(out packet) && packet != null)
            {
                try
                {
                    if (!string.IsNullOrWhiteSpace(packet.DetailMessage))
                        this.Infolog(packet);
                }
                catch (Exception ex)
                {
                    ObjectUtils.EventLogWriteError("NotificationMessagePacket emptying error : " + ex.ToString(),
                        EventLogEntryType.Warning);
                }
            }
        }
        isWriting = false;
    }
};
this.timer.Start();

我正在尝试使用以下代码重置我的目标文件名:

LoggingConfiguration configuration = LogManager.Configuration;

var wrapper = (AsyncTargetWrapper)configuration.FindTargetByName("log");

var target = (FileTarget)wrapper.WrappedTarget;

string path = Path.Combine(basePath, "Test", "Log", string.Concat(DateTime.Now.ToString("dd"), "_", DateTime.Now.ToString("MMMM"), "_", DateTime.Now.Year + @"\AppLogs.txt"));

if (!string.IsNullOrEmpty(message.ProcessId))
    path = Path.Combine(basePath, "Test", "Log", string.Concat(DateTime.Now.ToString("dd"), "_", DateTime.Now.ToString("MMMM"), "_", DateTime.Now.Year + @"\" + message.ProcessId + ".txt"));

target.FileName = path;
target.ConcurrentWrites = false;
LogManager.Configuration = configuration;
LogManager.ReconfigExistingLoggers();

但有时,日志消息不会写入适当的文件。

问题(可能)

我在你的配置中看到 message.ProcessId。看起来您正在更改每个日志事件的配置。那不是线程安全的!请参阅 NLog 的线程安全性如何? .

如何修复

好消息,也无需更改配置。您可以使用上下文 类 在配置中注入值。对于全局,有 GDC,但也有线程和消息级别的上下文。 (和其他 here)。

修复示例

您可以这样设置配置:

// Set GDC
GlobalDiagnosticsContext.Set("basepath","somepath");

// Create config
var config = new LoggingConfiguration();

FileTarget fileTarget1 = new FileTarget();
fileTarget1.FileName = @"${gdc:basepath}\Test\Log${Date:format:dd_MMMM_yyyy}\AppLogs.txt";

FileTarget fileTargetWithProcessId = new FileTarget();
fileTargetWithProcessId.FileName = @"${gdc:basepath}\Test\Log${Date:format:dd_MMMM_yyyy}${event-properties:ProcessId}.txt";

var hasProcessIdProperty = "${event-properties:item=ProcessId}!=''";
var rule1 = new LoggingRule()
{ 
    // log without processid to fileTarget1
    Targets = { fileTarget1 },
    Filters = { new ConditionBasedFilter()
    {
        Condition = hasProcessIdProperty,
        Action = FilterResult.Ignore,
        DefaultFilterResult = FilterResult.Log
    } }
};
var rule2 = new LoggingRule()
{
    // log only with processid to fileTarget1
    Targets = { fileTargetWithProcessId },
    Filters = { new ConditionBasedFilter()
    {
        // Log with property processid
        Condition = hasProcessIdProperty,
        Action = FilterResult.Log,
        DefaultFilterResult = FilterResult.Ignore
    } }
};

// Enable trace to fatal (so all levels)
rule1.SetLoggingLevels(LogLevel.Trace, LogLevel.Fatal);
rule2.SetLoggingLevels(LogLevel.Trace, LogLevel.Fatal);

config.LoggingRules.Add(rule1);
config.LoggingRules.Add(rule2);

LogManager.Configuration = config; // apply

然后这样记录:

var logger = LogManager.GetCurrentClassLogger();

logger.Info("I go to AppLogs.txt");

logger.WithProperty("ProcessId", message.ProcessId).Info("I go to processid logs");

您也可以使用一个目标并使用一个 "if else" (${when}),设置起来有点复杂。

疑难解答

如果您对此有疑问,请检查