NLog dynamically change filename using NLog.config

2019-05-26 10:23发布

问题:

How to dynamically change the FileName using a variable from C#? My idea is to create a log file like Log_<UserId_From_DB>_${date:format=yyyy-MM-dd}.log. Any ideas?

回答1:

Another option is to use the Global Diagnostic Context - $(GDC):

Set the value in C#

GlobalDiagnosticsContext.Set("UserId_From_DB","42");

In the config (nlog.config):

<target type="file" filename="Log_${gdc:item=UserId_From_DB}_${date:format=yyyy-MM-dd}.log" ..>

Please avoid modifying NLog Variables at runtime (See previous answer below). They should be seen as readonly, because they are not threadsafe. NLog Variables will also be affected if LoggingConfiguration is reloaded.

Previous answer with NLog Variables:

Set the value in C#

LogManager.Configuration.Variables["UserId_From_DB"] = "42";

In the config (nlog.config):

<target type="file" filename="Log_${var:UserId_From_DB}_${date:format=yyyy-MM-dd}.log" ..>

If the value is set again, the filename will automatically changed.



回答2:

While the posted answer works, it suffers from concurrency issues. That variable is a global variable and you may end up with conflicts.

There is a better solution available. There is a way to pass event properties to NLog.

Link to the relevant NLog documentation.

Let's assume you want to log an error message:

Logger myLog = LogManager.GetLogger(name);
LogLevel level = LogLevel.Error;
string message = "This is an error message!";

You turn this information into a LogEventInfo object:

LogEventInfo logEvent = new LogEventInfo(level , myLog.Name, message);

You can then add properties to this event (the string indexes are free to choose):

logEvent.Properties["MySpecialValue"] = "SPECIAL";

And then you write to the log:

myLog.Log(logEvent);

The interesting thing here is that in your NLog configuration, you can use this custom property in any field that the Nlog documentation refers to as a "Layout" value.

You use ${event-properties:item=MySpecialValue} in the layout to access the property. For example:

<target xsi:type="File" 
        name="file" 
        fileName="${basedir}/logs/${event-properties:item=MySpecialValue}/my_${event-properties:item=MySpecialValue}_file.log"
        layout="${event-properties:item=MySpecialValue} ${message}" />

Following the posted example, you will get a folder named SPECIAL, inside of which is a log file named my_SPECIAL_file.log in which you find the message SPECIAL This is an error message!. Just to prove the point that you can use this custom value in many different ways and shapes.


I commonly use this to make entity-specific logging (where the filename of the log equals the entity's ID value), which is essentially the same as you want to do here.

As a quick tip, I tend to wrap the NLog Logger in a class of my own:

public class UserLogger
{
    private readonly Logger _log;
    private readonly User _user;

    public UserLogger(User u)
    {
        _user = u;
        _log = LogManager.GetCurrentClassLogger();
    }

    public void Error(string message)
    {
        LogEventInfo logEvent = 
                 new LogEventInfo(LogLevel.Error, _log.Name, message);

        logEvent.Properties["UserId"] = _user.Id;

        _log.Log(logEvent);
    }
}

This is just a simple example to get you started. The cool feature I'm using here is that I've defined the log's filename (in the Nlog.Config target) using the UserId value and thus can ensure that each user gets logged to their own unique log file.

This way, you can enforce that the user ID is known when you want to log to the "user log" target. As an additional bonus, it also neatly decouples NLog dependencies from your calling code.



回答3:

Assuming you have a log file called mylogfile.log in your nlog.config file

FileTarget target = LogManager.Configuration.FindTargetByName("mylogfile.log") as FileTarget; 
String customlog = "Log_" +  GetUserId(UserId_From_DB) + "_" + DateTime.Now.ToString("yyyy-MM-dd") + ".log"; 
target.FileName = customlog;