📜 ⬆️ ⬇️

NLog: rules and filters

NLog: rules and filters


In Confirmit, we use the NLog library for logging in our .NET applications. Although there is documentation for this library, it was difficult for me to understand how it all works. In this article I will try to explain how the rules and filters are applied in NLog. Let's start.


How to configure NLog


And we begin with a brief reminder of what we can do with the NLog configuration. In the simplest case, this configuration is an XML file (for example, 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"> <targets> <target name="target1" xsi:type="ColoredConsole" layout="Access Log|${level:uppercase=true}|${logger}|${message}"> <highlight-row condition="true" foregroundColor="red"/> </target> <target name="target2" xsi:type="ColoredConsole" layout="Common Log|${level:uppercase=true}|${logger}|${message}"> <highlight-row condition="true" foregroundColor="green"/> </target> <target name="target3" xsi:type="ColoredConsole" layout="Yellow Log|${level:uppercase=true}|${logger}|${message}"> <highlight-row condition="true" foregroundColor="yellow"/> </target> </targets> <rules> <logger name="*" minlevel="Warn" writeTo="target1,target2,target3" /> </rules> </nlog> 

You can download this file with a single line of code:


 LogManager.Configuration = new XmlLoggingConfiguration("NLog.config"); 

What can we do with it? We can set several message receivers (target) on the rule:


 <rules> <logger name="*" minlevel="Warn" writeTo="target1,target2,target3" /> </rules> 

We can determine for which levels of logging this rule is enabled:


 <rules> <logger name="*" minlevel="Warn" writeTo="target1" /> <logger name="*" levels="Debug,Warn,Info" writeTo="target2" /> </rules> 

We can set filters for each rule:


 <rules> <logger name="*" minlevel="Info" writeTo="target1"> <filters defaultAction='Log'> <when condition="contains('${message}','Common')" action="Ignore" /> </filters> </logger> </rules> 

And finally, we can define nested rules:


 <rules> <logger name="*" minlevel="Info" writeTo="target1"> <logger name="*" minlevel="Warn" writeTo="target2" /> </logger> </rules> 

It's time to find out how it all works.


Creating a logger configuration


When you request an instance of the logger,


 var commonLogger = LogManager.GetLogger("Common"); 

NLog either takes an existing cache or creates a new one (see here ). In the latter case, a configuration is also created for the logger with the given name. Let's look at the process of its creation.


In short, the logger configuration is a separate chain of receivers and corresponding filters for each logging level ( Trace , Debug , Info , Warn , Error , Fatal ) (see here ). Now I will show you how these chains are built.


The main method responsible for creating data chains is the GetTargetsByLevelForLogger class LogFactory . Here is how it works. All rules specified in the NLog configuration are iterated. First of all, it checks whether the name of the rule matches the name of the logger. Rule names can contain wildcards, such as those we use for file system objects:



Thus the rule name ' * ' matches any logger name, and ' Common* ' matches all loggers whose names begin with ' Common '.


If the name of the rule does not match the name of the logger, then this rule is discarded with all the rules enclosed in it. Otherwise, the GetTargetsByLevelForLogger method gets all logging levels for which this rule is enabled. For each such level, NLog adds all message receivers specified in the rule to the corresponding receiver chains along with filters for this rule.


There is another important feature of the design of chains of receivers. If the current rule is marked as final and its name matches the name of the logger, then NLog terminates on it the construction of chains for all levels of logging enabled for this rule. This means that neither nested rules nor subsequent rules will add anything to these chains of receivers. Their creation is fully completed and they will not change. It follows that it does not make sense to write something like this:


 <rules> <logger name="*" minlevel="Info" writeTo="target1" final="true"> <logger name="*" minlevel="Warn" writeTo="target2" /> </logger> </rules> 

No messages will fall into target2 . But it is possible to write something like this:


 <rules> <logger name="*" minlevel="Warn" writeTo="target1" final="true"> <logger name="*" minlevel="Info" writeTo="target2" /> </logger> </rules> 

Since the external rule is not enabled for the Info level, the chain of receivers for this level will not end on the external rule. Therefore, all messages with the Info level will fall into target2 .


After all the receivers from this rule are added to the corresponding chains, the method recursively processes all the nested rules of the current rule using the same algorithm. This happens regardless of the logging levels enabled for the parent rule.


In total, the configuration for the logger is ready. It contains receiver chains with filters for each possible logging level:


Chain of receivers


It's time to see how this configuration is used.


Using Logger Configuration


Let's start with simple things. The Logger class has the IsEnabled method and the related properties IsXXXEnabled ( IsDebugEnabled , IsInfoEnabled , ...). How do they work? In fact, they simply check if the receiver chains for a given level of logging contain at least one link (see here ). This means that filters never affect the values ​​of these properties.


Next, let me explain what happens when you try to secure a message. As you might guess, the logger takes a chain of receivers for the logging level of this message. Then he begins to process the links of this chain one by one. For each link, the logger decides whether to write the message to the receiver specified in the link, and then continue processing the chain. These decisions are made using filters. Let me show you how filters work in NLog.


Here is how the filters are set in the configuration:


 <rules> <logger name="*" minlevel="Info" writeTo="target1"> <filters defaultAction='Log'> <when condition="contains('${message}','Common')" action="Ignore" /> </filters> </logger> </rules> 

Usually the filter contains some boolean condition. Here you can decide that the filter returns true or false for each message. But it is not. The result of their work is the value of the FilterResult type. If the filter condition returns true , then the result of the filter becomes the value specified in the action attribute (in our example, Ignore ). If the condition returns false , then the result of the filter will be Neutral . This means that the filter does not want to decide what to do with the message.


You can see how the receiver chain is handled here . For each receiver, the result of the operation of the corresponding filters in the GetFilterResult method is GetFilterResult . It is equal to the result of the operation of the first filter that returned not a Neutral . This means that if a filter returns a value other than Neutral , all subsequent filters are not executed.


But what happens if all filters return Neutral ? In this case, the default value will be used. This value is set using the defaultAction attribute defaultAction the filters element for the rule. What do you think, what is the default value for defaultAction ? You are right if you think this is Neutral . That is, the entire filter chain can return Neutral as a result. In this case, NLog behaves the same way as receiving Log . The message will be written to the receiver (see here ).


As you might have guessed, if the filter returns Ignore or IgnoreFinal , the message will not be written to the receiver. If the result of the filter is Log or LogFinal , the message will be recorded. But what is the difference between Ignore and IgnoreFinal and between Log or LogFinal ? It's simple. In the case of IgnoreFinal and LogFinal NLog stops processing the receiver chain and does not write anything to the receivers contained in subsequent links.


Conclusion


Code analysis NLog helped me figure out how the rules and filters work. I hope that this article will be useful to you. Good luck!


')

Source: https://habr.com/ru/post/450728/


All Articles