NLog: how to obtain level of a specific target programatically - c#

I currently use NLog and allow admin users to set the level at runtime using a variable as follows:
<logger name="*" minLevel="${var:myFileLevel}" writeTo="file" />
I would like to know the level of this logger at runtime (so I cannot obtain it from the configuration file due to the variable)
I am able to easily obtain the Target as follows:
Target target= LogManager.Configuration.FindTargetByName("file");
but unfortunately there are no relevant methods on the target object to obtain the level. Is it possible to obtain the logging level at runtime?

The enabled logging level is configured at the logging rule.
So you could do this:
Add a rulename, so you could find the rule easier:
<logger name="*" minLevel="${var:myFileLevel}" writeTo="file" ruleName="myrule" />
Find the rule and check the Levels property. See LoggingConfiguration.FindRuleByName Method
var rule = LogManager.Configuration.FindRuleByName("myrule");
var levels = rule.Levels; // enabled levels
For this case, another option is to read the myFileLevel variable value. For that, you need to render it, you could use LogEventInfo.CreateNullEvent() for that.
var myFileLevelLayout = LoggingConfiguration.Variables["myFileLevel"]; // Type SimpleLayout
string value = myFileLevelLayout.Render(LogEventInfo.CreateNullEvent())
See
LoggingConfiguration.Variables Property

Related

Why is NLog not logging scope data to Application Insights custom Dimensions

I am currently logging to Application Insights using NLog configured in an nlog.config file. I don't have IncludeScopes set anywhere (it is true by default).
I am trying to log custom properties using scope. It works when logging to a file or the console but not when logging to the Application Insights customDimensions.
This is how I am logging my scope:
using (_logger.BeginScope(new Dictionary<string, object> { ["ActivityId"] = Guid.NewGuid()})
{
_logger.LogInformation("Logging from with scope");
}
and this is the nlog.config file:
<target name="applicationInsights" xsi:type="ApplicationInsightsTarget" >
<instrumentationKey>8d9f67d5-fe36-45cf-935f-2f87bb240b12</instrumentationKey>
<!-- Only required if not using ApplicationInsights.config -->
<contextproperty name="threadId" layout="${threadid}" />
<contextproperty name="processName" layout="${processname}" />
<!-- Can be repeated with more context -->
</target>
Unfortunately I don't see the ActivityId when I look in the customDimensions in Application Insights.
I am running my Console app in Azure so have registered a worker service (which processes messages) like this:
services.AddHostedService<PositionMessageProcessor>()
What do I need to do get the logging scope to log my ActivityId in Application Insights?
Update
I have managed to it logging the ActivityId by adding it as a specific contextProperty. I don't really want to have to update the config file everytime I call BeginScope(...) with different properties.
Is there a generic way to get it to work for all scope properties?
Is there a generic way to get it to work for all scope properties?
I assume you mean that it sends all the scoped properties to application insights without specifying which keys.
Currently this isn't supported by the target, see source.
In NLog 4, the scope properties are pushed to the NestedDiagnosticsLogicalContext.
You could do that by creating your own target:
Copy the target from source
Loop over NestedDiagnosticsLogicalContext.GetAllObjects() in BuildPropertyBag inside ApplicationInsightsTarget.
Register your target, see NLog-Register your custom component
You can have scope-context included as blob-data by using a <contextproperty> with a JsonLayout:
<target type="ApplicationInsightsTarget" name="aiTarget">
<contextProperty name="scopeproperties">
<layout type="JsonLayout" includeMdlc="true" />
</contextProperty/>
</target>

C#: Regarding Nlog settings and usage

i am curious to use nlog. i did not use it previously. so i like to use it now in my project. i have a question.
<rules>
<logger name="SomeNamespace.Component.*" minlevel="Trace" writeTo="logfile" final="true" />
<logger name="*" minlevel="Info" writeTo="logfile" />
</rules>
what does mean here SomeNamespace.Component.* ? show me the usage of this type of rule with sample code
what is minlevel="Info" ? what does mean minlevel here ?
what other option can be set for minlevel ?
thanks
what does mean here SomeNamespace.Component.* ?
It means that this rule will match any loggers which have name starting with SomeNamespace.Component.. Usually name of logger equals name of class where you are creating logger (but you also can provide custom logger name):
private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
So rule configuration above will match following logger names:
SomeNamespace.Component.MyClass
SomeNamespace.Component.Cool.Other.Namespace.YourClass
// etc
I.e. any logger created in class from SomeNamespace.Component. namespace will match rule. No matter how many classes with loggers you have there.
what is minlevel="Info" ?
It's a minimal level of log messages which will be logged by logger.
Logger.Debug("This will not be logged");
Logger.Info("This will be logged");
NLog supports following log levels (you can use any of them to control which messages will be logged):
Trace
Debug
Info
Warn
Error
Fatal
Check description of each level at NLog wiki. Usually you should use min level Info which will log any error messages and some high-level details of what system is doing. For debugging purpose you can turn-on Trace or Debug level, but your log file can become huge very quickly. Performance also will hurt when you are writing lot of log messages.

Check NLog minlevel before logging?

I want to enable developers to log objects as JSON with NLog. To do this I need to implement some logic before sending to nLog OR before sending to target.
I can build my own Target(TargetWithLayout) but I canĀ“t find a way to check the log level from the config for this specific target/logger? Another drawback is that I need to make a new TargetWithLayout class for each target that we will use (EventLog, File, WebService and so on).
Another solution would be to do it in my LogHandler that uses NLog. The only way to know if I should translate the object is probably to read all the loggers from the config file, if any of them is set to log objects then I serialize. I am however not sure if I can check this information from the LogHandler (without doing it manually)?
You can use the NLog-Logger object to query active logging-rules:
if (myLogger.IsTraceEnabled)
myLogger.Trace("Hello World");
You can use the NLog json layout to write json in you log files, no need to check and do the serialization yourself:
<target name="jsonFile" xsi:type="File" fileName="${logFileNamePrefix}.json">
<layout xsi:type="JsonLayout">
<attribute name="time" layout="${longdate}" />
<attribute name="level" layout="${level:upperCase=true}"/>
<attribute name="message" layout="${message}" />
</layout>
</target>
The log messages formatting is handled by NLog instead of doing it yourself.
release notes nlog
To add some theory ;)
Another drawback is that I need to make a new TargetWithLayout class for each target that we will use (EventLog, File, WebService and so on).
That's the reasons there are Layouts in NLog. Those are the layouts that could be used in the target, but those are independent of the target.
(don't get confused with Layout Renderers, those ${..} things.)
There are multiple layouts (plain text, CSV, JSON) (see list) , and you could easily add your own layout, analogous to adding a custom Target / Layout renderer, see the wiki

Adding method name in NLog

I am using NLog and following the recommended pattern of having a log declare on each class, for the purpose of being able to track which class/method has written to the log. I do find this very useful to have a bit of a top level 'stack trace' with each log write.
My code used to look like this:
class SomeClass {
private static readonly Logger logger = LogManager.GetCurrentClassLogger();
void DoStuff() {
logger.Debug("stuff"); }
}
I recently had the requirement my single project to write to 3 separate log files, and to do this, I added multiple loggers and targets as specified here: https://stackoverflow.com/a/21711838/191206
However, now in my log files, I have lost the class level name. It now just writes the log name that I specified in the NLog.config. I've considered simply adding the method name myself with a call to
System.Reflection.MethodBase.GetCurrentMethod(); // use Name property
or using something else in Reflection like this
However, I'm wondering if NLog has something built into this that I'm missing? The Debug() method I only see the ability to write a string, with parameters & optionally formatted..
Is this built into NLog?
There is a built in layout renderer called ${callsite} that you can use to include the call site information (class name, method name and source information) in your log entries:
<targets>
<target
name="task1File"
xsi:type="File"
layout="${callsite} - ${message}"
fileName="${basedir}../Data/debugLog1.txt"
archiveAboveSize ="5000000"
maxArchiveFiles="2"/>
<target
name="task2File"
xsi:type="File"
layout="${callsite} - ${message}"
fileName="${basedir}../Data/debugLog2.txt"
archiveAboveSize ="5000000"
maxArchiveFiles="2"/>
</targets>

dynamic log4net appender name?

Let's say i have 3 smtp appenders in same log4net file whose names are:
<appender name = "emailDevelopment".. />
<appender name = "emailBeta".. />
<appender name = "emailProduction".. />
Let's say i have 3 different servers(Dev, Beta, Production). Depending upon the server, i want to fire the log. In case of Development server, it would fire log from "emailDevelopment". I have a system variable in each server named "ApplicationEnvironment" whose value is Development, Beta, Production based on the server names. Now is there anyway i can setup root in log4net so that it fires email depending upon the server name.
<root>
<priority value="ALL" />
<appender-ref ref="email<environment name from whose appender should be used>" />
</root>
This doesn't directly answer your question, but another approach is to simply have multiple log4net configuration files and call XmlConfigurator.Configure() on the right one. For example, you might have Logging.Development.Config, Logging.Beta.Config and so on.
Somewhere in code, you determine the "environment" and configure using the file you want.
I've even gone so far as to have multiple config files and pull different parts of them out into a single XML representing the "true" config, and then calling the Configure() method on that. For example, Logging.Appenders.Config which has all the appenders, and takes all of them and combines it with one of your environment-specific config files above; the environment-specific ones simply reference what they need, and the rest are effectively inactive/unreferenced for that environment.
Even after having written the only XSD file for log4net configuration I'm still not aware of an easy way to achieve this.
You might be able to do something like:
log4net.GlobalContext.Properties["host"] = new ClassThatToStringsHost();
class ClassThatToStringsHost
{ public override string ToString() { return "whatever"; } }
Now you can reference this value from the Log format with: "%property{host}"
To perform the filtering you will need to use a filter configuration in the adapter(s):
<appender name="file" type="log4net.Appender.RollingFileAppender">
<filter type="log4net.Filter.PropertyFilter">
<Key value="host" />
<StringToMatch value="whatever" />
</filter>
<!-- Anything not accepted by the above should be excluded -->
<filter type="log4net.Filter.DenyAllFilter" />
</appender>
There may even be a built-in property you could leverage and this should work. See also this post: http://geekswithblogs.net/rgupta/archive/2009/03/03/dynamic-log-filenames-with-log4net.aspx
For me, myself, and I... I would approach it another way all together. I would derive my own SMTP appender from the default and in the ActivateOptions() method I'd configure the values according to the environment. This would allow you to use one SMTP appender with consistent rules and yet provide three public properties for each of the email addresses you want to send from. It's not hard, give it a try!

Categories

Resources