How to include logging scope into the log file using NLog - c#

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.

Related

How do you pass a directory into NLog.config?

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

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.

Is it possible to only display SourceContext in Serilog if ForContext<class> has been defined

I am new to Serilog and I am struggling with displaying the class name only when required without having to add {SourceContext:l}to each log message. I am aware I can just write the code as:
Log.ForContext<Class1>().Verbose("message {SourceContext:l}");
I have the logger being setup as below:
var log = new LoggerConfiguration()
.MinimumLevel.Verbose()
.WriteTo
.ColoredConsole(outputTemplate: "{Timestamp:G} [{Level}] {SourceContext} {Message}{NewLine:l}{Exception:l}")
.CreateLogger();
I am hoping to only have the class name displayed when the class has been specified as seen here.
Log.ForContext<Class1>().Verbose("message");
And not displayed if the log is created like.
Log.Verbose("message");
However on the above example it will print a log out in the format of
'{SourceContext} message' instead of just 'message' which is not what I want.
This may be a bug in ColoredConsoleSink - generally when rendering output tokens a missing value will be left blank.
Raised: https://github.com/serilog/serilog/issues/649
You might try switching to the Literate Console sink (https://github.com/serilog/serilog-sinks-literate) which I don't believe has this bug.

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