How do you pass a directory into NLog.config? - c#

There is a global variable that the user can set to define where NLog will log files. However, I don't know how to pass that variable to NLog.config.
I'd like to be able to use it like ${basedir}, but instead have it be ${userdir}. I think this is doable without having to pass the variable using event-properties every time I log, but I don't know how. I'd like to define it once when I write
static private NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();
Does anyone know if this is doable?

There are several ways to pass (context) information to NLog. I think it this case the "GDC" (GlobalDiagnosticsContext) is the best way:
Set in your code:
GlobalDiagnosticsContext.Set("userdir", myDirectory);
Usage in nlog.config:
<target name="file" xsi:type="File"
fileName="${gdc:item=userdir}/${shortdate}.log" ... />
See GDC docs

Related

How to include logging scope into the log file using NLog

I'm using NLog.Extensions.Logging.
When registering a logger factory using the method AddNLog(), it is possible to enable logging scope using NLogProviderOptions.IncludeScopes.
But how to make NLog write logging scope to a file?
I haven't found anything similar in the list of available layouts
An example:
Log like this:
// logger is here of type Microsoft.Extensions.Logging.ILogger
using (logger.BeginScope(new[] { new KeyValuePair<string, object>("userid", request.UserId) }))
{
logger.LogDebug("My log message");
}
Render like this: ${mdlc:userid}.
For example in the file target:
<target name="file" xsi:type="File"
layout="${longdate} ${logger} ${message}${exception:format=ToString}, user: ${mdlc:userid}"
fileName="${basedir}/${shortdate}.log" />
Note: NLogProviderOptions.IncludeScopes is enabled by default.
NLog directly
The syntax is a bit clumsy, but that is because Microsoft's abstraction is a bit limited. See also this issue: .NET - Logging structured data without it appearing in the text message
If you refer NLog directly, you could also do:
using (NLog.MappedDiagnosticsLogicalContext.SetScoped("userid", request.UserId))
{
// logger here of type NLog.Logger
logger.Info("My log message");
}
Also this is rendered with ${mdlc:userid}
More examples and different scopes for NLog explained here
Docs
PS: I have updated available layouts, so you could find it easier :)
New (in V5) is to use ${scopenested}.
See https://github.com/NLog/NLog/wiki/ScopeNested-Layout-Renderer.

NLog Concurrent Requests into different files

As the title suggests, I am trying to log separate requests to my .NET Web API controllers to separate files.
With the introduction of {var}, here is what I am trying inside my c# code.
private static Logger logger = LogManager.GetLogger("test");
[HttpPost, Route("xyz")]
public IHttpActionResult Post(Obj value)
{
var guid = Guid.NewGuid();
LogManager.Configuration.Variables["test"] = guid.ToString();
logger.Info(value);
//do my execution here.
}
My Nlog.Config looks like this:
<variable name="test" value=""/>
<targets async="true">
<target xsi:type="File"
name="concurrents"
fileName="${basedir}/nlogs/${logger}/${var:test}.log"
layout="${uppercase:${level}} | ${callsite} | ${date:format=HH\:mm\:ss.fff} | "${message}""
createDirs="true" />
</target>
</targets>
<rules>
<logger name="test" minlevel="Info" writeTo="concurrents"></logger>
</rules>
As you all might already know,
When concurrent requests come in, I am trying to log each and every requests by creating a creating a new Guid and assinging it to the {var} and creating the file in the name of the var.
The logs somehow gets mixed up and one half of the request is in one file and the other half in another.
To be quite honest, I think I am missing some fundamental thing that I should know about but not sure where.
It would be great if someone could point to the right direction and help me understand how can I go about logging current request in different files.
Cheers
Variables are at global level and that won't work with a multi-threaded environment (http request)
A good solution is to set the guid on HttpContext level:
HttpContext.Current.Items["myvariable"] = guid.ToString();
and use
${aspnet-item:variable=myvariable}
You need NLog.Web.AspNetCore package for ASP.NET Core - for ASP.NET non-core you need NLog.Web

NLog: How to determine if a named logger was not found in config

Let's say I have this partial configuration, with NLog:
<rules>
<logger name="ExistsInConfig" writeTo="Console"/>
</rules>
..and then I write this code:
var configuredLogger = LogManager.GetLogger("ExistsInConfig");
configuredLogger.Log(LogLevel.Info, "hello, cruel world!");
var missingLogger = LogManager.GetLogger("NotInConfig");
missingLogger.Log(LogLevel.Info, "goodbye, cruel world!");
In the console output I will see only the first logging statement, because the second named logger was not found in the config file.
How can I programatically detect that the second logger was not found, and therefore will produce no output?
If you have the instance of Logger, you could ask it to it:
bool hasConfigRuleForInfo = missingLogger.IsEnabled(LogLevel.Info)
If not, then you need some tricks, some possibilities:
or create your own LogManager class remember which remembers which loggers are used
or read with reflection the private propertiy LogManager.factory.loggerCache (not supported of course ;))
add a wildcard( *) rule to your config (API or XML) and write to MemoryTarget or a Custom Target. This could effect your performance. PS. with ${logger} you get the logger name. You will also need the final option on other rules.
I think this is the best way:
if (!NLog.LogManager.Configuration.ConfiguredNamedTargets.Any(t => t.Name.Equals("NameToValidate")))
{
//config not found
}
With #Julian 's answer, you could have the Config you are looking for but not the level you are comparing within. You could even have the Config without any level activated in your NLog config.
In those cases you would get an incorrect check response.

Log some information no matter what using log4net

Situation is that level of log4net is configured as 'Error' but there is some information i need to write under a 'no matter what' condition
for example
"loggin has started"
if only 'Error' or 'Fatal' is enabled i cant log this in Error or Fatal since its just information
so is there any way i can do that other than change the level of the logger to info, write the log and then change back the level because that will act just like a workaround not a solution
and without using Headers since they only come at the beginning
EDIT: In an Appender
StringMatchFilter stringFilter = new StringMatchFilter();
stringFilter.AcceptOnMatch = true;
stringFilter.StringToMatch = "successfully";
stringFilter.ActivateOptions();
appender.AddFilter(stringFilter);
DenyAllFilter deny = new DenyAllFilter();
deny.ActivateOptions();
appender.AddFilter(deny);
adding to an appender and setting level 'All' to the root and managing levels in appenders but still i am unable to write any message containing 'successfully'
but please note when i set appender level to info the filter begins to work
I assume this is all because you don't like the idea of using .FatalFormat when it is not really an error.
I'm not sure how you would do this programatically, but if you were using a config file you add a section like
<logger name="ALWAYS">
<level value="DEBUG" />
appender-ref ref="RollingFileAppender" />
</logger>
which means you can log your messages like
log4net.LogManager.GetLogger("Always").InfoFormat( ... );
or you could create a static
static readonly log4net.ILog alwaysLogger = log4net.LogManager.GetLogger("Always");
and log via.
alwaysLogger.InfoFormat(....);
My suggestion would be to create a filter that looks first at a string match and then at the level. That way you could have a key string that you pass in the message (say "AppInfo" or something that would be unique and not found in an error). Then, your filter would pick that up and log it even when you logged it at the INFO level but the filter would ignore all other messages that weren't ERROR or FATAL. I wrote an article on CodeProject that will show you how to do complex filtering like this:
http://www.codeproject.com/KB/dotnet/Log4net_Tutorial.aspx
The one key here is that you would probably need to not specify the filtering in the root for this particular appender, since root supersedes the appender if I remember correctly.

How flexible is NLog? I want custom layout properties

I'm using NLog to send emails when an exception occurs in my app. Here's a portion of my target :
<target xsi:type="Mail"
name="email"
subject="${level}:" .. >
I receive emails with subjects like "Error:" or "Fatal:". This works fine but I want to add the Exception.Message to the subject of the email
Is it possible to setup custom properties in NLog. I can't find out how to do this, so just to make it clear what I want here is an example of the kind of thing I'm trying to do :
m_oLogger.Fatal( oException.BuildMessage(), new {MyMessage=oException.Message});
*Note that BuildMessage() is just an extension method to convert the full exception details (including inner exceptions) to a readable string
And in my target :
<target xsi:type="Mail"
name="email"
subject="${level}: ${Custom.MyMessage}" .. >
Then I would get emails with the subjects like :
Fatal: Syntax error in parameters or
arguments. The server response was:
Account does not exist
Is this kind of flexibility possible with NLog? If not, do you know of another .NET logging platforms that offers this kind of functionality?
In general instead of creating custom layoutRenderer suggested by wageoghe it's possible to use EventContext-Layout-Renderer that allows to pass any number of custom  properties 
LogEventInfo theEvent = new LogEventInfo(LogLevel.Debug, "", "Pass my custom value");
theEvent.Properties["MyValue"] = "My custom string";`
log.Log(theEvent);
and in your NLog.config file:
${event-context:item=MyValue} -- renders "My custom string
For the particular https://github.com/nlog/NLog/wiki/Exception-Layout-Renderer the format should be
${exception:format=message}
It is very easy to add a custom LayoutRenderer to NLog. See my first answer to this question for an example of a LayoutRenderer that allows you to add the System.Diagnostics.Trace.CorrelationManager.ActivityId value to your logging output.
To do what you want, you probably don't need a custom LayoutRenderer. If you want to send an email whose subject is the log leve PLUS the Message property of the exception, you should be able to configure something like this:
<target xsi:type="Mail"
name="email"
subject="${level}: ${exception.Message}" ..>
That should create the subject of the email by concatenating the level and the value of the Exception.Message property. You will have to call the logging overload that takes an Exception as a parameter.
Does this help?
This is likely impractical: I'm fairly sure the NLog framework doesn't have reflection as a part of its logging format [to do so would require NLog to have some sort of concept of where your referenced assemblies are and what their type is.]
Can you simply do all your message parsing/formatting in the C# code, and and pass it as a part of the already existing variables? They list a lot of these in the Mail-target NLog documenation.

Categories