We know NLog 4.6.7 added support for using NLog Layout like ${gdc:globalLevel}
to dynamically change level attributes at runtime. And the Better solution is to upgrade your NLog if it is possible.
Update: New solution
I tried this code on version 4.5 and it works fine. It seems you don't need to upgrade your NLog version.
In this case, all the configurations set programmatically. You can send your desired level in the header as loglevel
. If you send loglevel
in the header, it will be used. Otherwise, logLevel will be Error
. See here, please.
Notice: Just use using NLog;
. You don't need using Microsoft.Extensions.Logging;
[Route("api/[controller]/[action]")]
[ApiController]
public class HomeController : ControllerBase
{
private readonly Logger _log = LogManager.GetCurrentClassLogger();
[HttpGet]
public async Task<IEnumerable<string>> Get()
{
var requestLogLevel = Request.Headers.SingleOrDefault(x => x.Key == "loglevel");
LogLevel logLevel = LogLevel.Error;
switch (requestLogLevel.Value.ToString().ToLower())
{
case "trace":
logLevel = LogLevel.Trace;
break;
case "debug":
logLevel = LogLevel.Debug;
break;
case "info":
logLevel = LogLevel.Info;
break;
case "warn":
case "warning":
logLevel = LogLevel.Warn;
break;
case "error":
logLevel = LogLevel.Error;
break;
case "fatal":
logLevel = LogLevel.Fatal;
break;
}
var config = new NLog.Config.LoggingConfiguration();
var defaultMode = new NLog.Targets.FileTarget("defaultlog") { FileName = "log.txt" };
config.AddRule(logLevel, LogLevel.Fatal, defaultMode);
NLog.LogManager.Configuration = config;
_log.Trace("Some logs");
return new string[] { "value1", "value2" };
}
}
Solution 1) Upgrade NLog to 4.6.7 or later:
var config = new NLog.Config.LoggingConfiguration();
// Targets where to log to: File and Console
var logfile = new NLog.Targets.FileTarget("logfile") { FileName = "file.txt" };
var logconsole = new NLog.Targets.ConsoleTarget("logconsole");
// Rules for mapping loggers to targets
config.AddRule(LogLevel.Info, LogLevel.Fatal, logconsole);
config.AddRule(LogLevel.Debug, LogLevel.Fatal, logfile);
// Apply config
NLog.LogManager.Configuration = config;
Solution 2) Change the configuration file programmatically:
Because your version of NLog
doesn't support change configuration automatically, we are going to change it programmatically:
[Route("api/[controller]/[action]")]
[ApiController]
public class HomeController : ControllerBase
{
private readonly Logger _log = LogManager.GetCurrentClassLogger();
// Special Authorization needed
public bool ChangeToDebugMode()
{
try
{
XmlDocument doc = new XmlDocument();
doc.Load(AppDomain.CurrentDomain.BaseDirectory + "nlog.config");
XmlNode root = doc.DocumentElement;
XmlNode myNode = root["include"].Attributes["file"];
myNode.Value = "debugmode.config";
doc.Save(AppDomain.CurrentDomain.BaseDirectory + "nlog.config");
}
catch (Exception)
{
return false;
}
return true;
}
// Special Authorization needed
public bool RestToDefault()
{
try
{
XmlDocument doc = new XmlDocument();
doc.Load(AppDomain.CurrentDomain.BaseDirectory + "nlog.config");
XmlNode root = doc.DocumentElement;
XmlNode myNode = root["include"].Attributes["file"];
myNode.Value = "defaultmode.config";
doc.Save(AppDomain.CurrentDomain.BaseDirectory + "nlog.config");
}
catch (Exception)
{
return false;
}
return true;
}
[HttpGet]
public async Task<IEnumerable<string>> Get()
{
_log.Trace("Some logs");
return new string[] { "value1", "value2" };
}
}
In this case, you need some change in your config file.
You need to add autoReload=true
to configuration. Now, when any configuration change, NLog automatically reload the configuration and you don't need to restart the application. You need to take a look at autoReload
and include
here
nlog.config
<?xml version="1.0" encoding="utf-8"?>
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" autoReload="true">
<include file="defaultmode.config" />
</nlog>
defaultmode.config
<?xml version="1.0" encoding="utf-8" ?>
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<targets>
<target name="logfile" xsi:type="File" fileName="file.txt" />
</targets>
<rules>
<logger name="*" minlevel="Debug" writeTo="logfile" />
</rules>
<!-- ... -->
</nlog>
debugmode.config
<?xml version="1.0" encoding="utf-8" ?>
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<targets>
<target name="logfile" xsi:type="File" fileName="file.txt" />
</targets>
<rules>
<logger name="*" minlevel="Trace" writeTo="logfile" />
</rules>
<!-- ... -->
</nlog>
I made two other config files. debugmode.config
and defaultmode.config
. By default in nlog.config
file, deafultmode.config
is included. When ChangeToDebugMode
is called, it changes to debugmode.config
and when RestToDefault
is called, it changes to defaultmode.config
. I used include
and separate configuration into two files just for simlicity.
Solution 3) Based on your question:
In this case, I used the code that you provided in your question. If you send the log level in your request header, it will be considered. If you don't send, it will use the default value that you set in the configuration. Thus, you don't need to change your application on the client-side. It works fine. Just send your desired log level when you are debugging.
[Route("api/[controller]/[action]")]
[ApiController]
public class HomeController : ControllerBase
{
private readonly Logger _log = LogManager.GetCurrentClassLogger();
[HttpGet]
public async Task<IEnumerable<string>> Get()
{
var requestLogLevel = Request.Headers.SingleOrDefault(x => x.Key == "loglevel");
LogLevel logLevel = LogLevel.Error;
switch (requestLogLevel.Value.ToString().ToLower())
{
case "trace":
logLevel = LogLevel.Trace;
break;
case "debug":
logLevel = LogLevel.Debug;
break;
case "info":
logLevel = LogLevel.Info;
break;
case "warn":
case "warning":
logLevel = LogLevel.Warn;
break;
case "error":
logLevel = LogLevel.Error;
break;
case "fatal":
logLevel = LogLevel.Fatal;
break;
}
SetMinLogLevel(logLevel);
_log.Trace("Some logs.");
return new string[] { "value1", "value2" };
}
public static void SetMinLogLevel(LogLevel NewLogLevel)
{
foreach (var rule in LogManager.Configuration.LoggingRules)
{
rule.EnableLoggingForLevel(NewLogLevel);
}
//Call to update existing Loggers created with GetLogger() or
//GetCurrentClassLogger()
LogManager.ReconfigExistingLoggers();
}
}
The problem is, this situation needs to send the log level every time.
In these screenshots, you see how to send log level in debugging mode.

