Table of contents
Preface
A year ago, I wrote an introduction to NLog NLog log framework use inquiry-1 , this article briefly introduces the basic use of NLog and how to use Log4View2 tool to cooperate with the unified collection of log view. This article will document some common uses of NLog.
Custom parameters
Sometimes we need to customize some parameters according to our business characteristics. For example, there is a unique Id. At this time, we can customize the parameters to extract the Id instead of putting it into the log content, which is convenient to retrieve.
stay EventProperties Layout Renderer In the document, support the dynamic rendering of custom EventPropertie to Layout.
Nlog already has some custom parameters, such as ${counter}, ${longdate}, ${message:format=message}.
The custom parameter uses the format ${event properties: item = string: culture = string: format = string}.
The official example is as follows. There are four types of properties defined in the code
... Logger logger = LogManager.GetCurrentClassLogger(); LogEventInfo theEvent = new LogEventInfo(LogLevel.Debug, "", "Pass my custom value"); theEvent.Properties["MyValue"] = "My custom string"; theEvent.Properties["MyDateTimeValue"] = new DateTime(2015, 08, 30, 11, 26, 50); theEvent.Properties["MyDateTimeValueWithCulture"] = new DateTime(2015, 08, 30, 11, 26, 50); theEvent.Properties["MyDateTimeValueWithCultureAndFormat"] = new DateTime(2015, 08, 30, 11, 26, 50); logger.Log(theEvent); ...
You can get it in the configuration file through ${event properties: item = string: culture = string: format = string}
${event-properties:item=MyValue} -- renders "My custom string" ${event-properties:MyDateTimeValue:format=yyyy-M-dd}"; -- renders "2015-8-30" ${event-properties:MyDateTimeValueWithCulture:culture=en-US} -- renders "8/30/2015 11:26:50 AM" ${event-properties:MyDateTimeValueWithCultureAndFormat:format=yyyy-M-dd HH:mm:ss:culture=en-US} -- renders "2015-8-30 11:26:50"
As can be seen from the above, if we need to customize parameters, we need to create a LogEventInfo object and record the LogEventInfo object through the Log() method.
Calling Info() and other methods internally creates the LogEventInfo object, and finally calls the Log() method.
Now we can test our own code.
Modify the configuration of the above Json output. Because the Memo parameter contains Chinese, you need to set encode to false to prevent it from being encoded as Unicode. The following are Nlog related source codes
protected override void RenderInnerAndTransform(LogEventInfo logEvent, StringBuilder builder, int orgLength) { Inner.RenderAppendBuilder(logEvent, builder); if (JsonEncode && builder.Length > orgLength) { if (RequiresJsonEncode(builder, orgLength)) { var str = builder.ToString(orgLength, builder.Length - orgLength); builder.Length = orgLength; Targets.DefaultJsonSerializer.AppendStringEscape(builder, str, EscapeUnicode); } } } private bool RequiresJsonEncode(StringBuilder target, int startPos = 0) { for (int i = startPos; i < target.Length; ++i) { if (Targets.DefaultJsonSerializer.RequiresJsonEscape(target[i], EscapeUnicode)) { return true; } } return false; }
When encode is set to false, the output will not have double quotes. Looked up the next source code, it is. Specifically why this design is not very understanding, some students can explain it.
public class JsonAttribute { ... public bool Encode { get => LayoutWrapper.JsonEncode; set => LayoutWrapper.JsonEncode = value; } ... } private bool RenderAppendJsonPropertyValue(JsonAttribute attrib, LogEventInfo logEvent, StringBuilder sb, bool beginJsonMessage) { BeginJsonProperty(sb, attrib.Name, beginJsonMessage); if (attrib.Encode) { // "\"{0}\":{1}\"{2}\"" sb.Append('"'); } ... if (attrib.Encode) { sb.Append('"'); } return true; }
Next, add the following configuration to target configuration of nlog.Config
<layout xsi:type="JsonLayout" > <attribute name="counter" layout="${counter}" /> <attribute name="time" layout="${longdate}" /> <attribute name="level" layout="${level:upperCase=true}"/> <attribute name="message" layout="${message:format=message}" encode="false" /> <attribute name="Id" layout="${event-properties:item=Id}" /> <attribute name="No" layout="${event-properties:item=No}" /> <attribute name="Memo" layout="${event-properties:item=Memo}" encode="false" /> </layout>
Properites corresponding to code
LogEventInfo theEvent = new LogEventInfo(LogLevel.Debug, "", "Custom dynamic parameters"); theEvent.Properties["Id"] = Guid.NewGuid(); theEvent.Properties["No"] = "1"; theEvent.Properties["Memo"] = "Remarks"; logger.Log(theEvent);
The output is as follows
It should be noted that if we customize parameters and write them to a file in json format, the key of Properties in the code must match the case of the key in ${event Properties: item = key} of the configuration file.
Log output mode
file
When we need to store logs in files, we usually need to distinguish the log directory according to the service name, module name, and so on. We can also output the logs through custom parameters.
Suppose we need to catalog the logs according to the service, and the log file name contains the content prefix we specified. A unique number needs to be recorded in each log. Log configuration is as follows
... <targets> <target xsi:type="File" name="InfoFile" fileName="${basedir}/logs/${event-properties:item=ServiceName}/${logger:shortName=true}/${shortdate}/${event-properties:item=LogPrefix}_log.txt" encoding="utf-8"> <layout xsi:type="JsonLayout" > <attribute name="counter" layout="${counter}" /> <attribute name="time" layout="${longdate}" /> <attribute name="level" layout="${level:upperCase=true}"/> <attribute name="UniqueNo" layout="${event-properties:item=UniqueNo}" /> <attribute name="Message" layout="${message:format=message}" encode="false" /> </layout> </target> </targets> <rules> <logger name="*" maxlevel="Info" writeTo="InfoFile" /> </rules>
First, look at the recorded logs. The circles are all the values generated by our customization.
- ${basedir} is the running directory of the program
- ${logger:shortName=true} is the program name specified in the code. In the code, the log name can be specified through NLog.LogManager.GetLogger("test") or NLog.LogManager.GetCurrentClassLogger() points to the full name (including namespace) of the current class.
- ${short date} is a short date format. Nlog also has a built-in ${date} to get the complete time. As we said earlier, you can customize the format through ${event properties: item = string: culture = string: format = string}. Here, you can also customize the date format through ${date:format=String}, for example, the output of ${date:format=yyyyMMdd} is the date format like 20191201.
network transmission
In the previous chapter, we mentioned that logs are sent to Log4View2 and other tools through the network for unified summary.
<targets async="true"> <target xsi:type="Network" address="udp://127.0.0.1:878" name="network" newLine="false" maxMessageSize="65000" encoding="gbk" layout="${log4jxmlevent:includeCallSite=true:includeNLogData=true}"/> </targets> <rules> <logger name="*" minlevel="Info" writeTo="network" /> ... </rules>
Nlog supports tcp or udp protocol for network transmission.
- Specify network transport via xsi:type="Network"
- Specify the protocol and address through address = "protocol: / / ip: Port".
- It can be serialized into XML transport through layout = ${log4jxmlevent: includecallsite = true: includenlogdata = true}. Of course, we can also transport the customized format or Json format. As long as the corresponding format resolution is used at the target end.
The following is an example of Xml serialization transfer to Log4View2. When the custom fields in our code are sequenced into Xml and sent to the opposite end. We can right-click the column on the Log4View2 interface and select Show Column Chooser to select which fields to display, which is very convenient.
It also supports filtering columns, which is convenient for us to find.
data base
In general, the logs do not need to be viewed in real time, usually when troubleshooting problems, so sometimes we can hope to summarize the logs and do log analysis. Nlog supports inserting data into the database. For example, I store the logs into the Oracle database and use oracle.manageddataccess. We can install the library package install package oracle.manageddataccess through nuget
<target xsi:type="Database" name="DB45" dbProvider="Oracle.ManagedDataAccess.Client.OracleConnection, Oracle.ManagedDataAccess" keepConnection="true" optimizeBufferReuse="true" connectionString="Data Source=10.60.45.239/devdb;user id=fgmain10001;password=test1" commandText="insert into NETWORKLAYERLOG (TIME, LOGLEVEL, APPID, LOGGER,IDENTITY, REQUESTID, MESSAGE,EXCEPTION) values (to_timestamp(:TIME,'YYYY-MM-DD HH24:MI:SS.FF'), :LOGLEVEL, :APPID, :LOGGER,:IDENTITY, :REQUESTID, :MESSAGE,:EXCEPTION)"> <parameter name=":TIME" layout="${longdate}" /> <parameter name=":LOGLEVEL" layout="${level}" /> <parameter name=":APPID" layout="${event-properties:item=AppId}" /> <parameter name=":LOGGER" layout="${logger}" /> <parameter name=":IDENTITYID" layout="${event-properties:item=IdentityId}" /> <parameter name=":REQUESTID" layout="${event-properties:item=RequestId}" /> <parameter name=":MESSAGE" layout="${message}" /> <parameter name=":EXCEPTION" layout="${exception:format=toString,Data}" /> </target>
- dbProvider: configure dbProvider first.
- keepConnection: indicates whether a connection needs to be maintained.
- optimizeBufferReuse: indicates whether the connection pool is used.
- connectionString: the statement to be entered.
- connectionString: connection string.
- commandText:sql statement, which supports parameterization.
- < parameter name = "columnname" layout = "value" / >: parameter, name is the parameter name, and layout is the parameter value.
In Log4View2, you can point the data source to the table where the log is located. Select the database sink.
After filling in the relevant configuration, select the table.
When selecting a table, you need to select a Key, but not all keys are optional.
From the Log4View2 source code, when the Key needs to be the end of the id, the type is numeric or time type or the column is set with auto increment.
this.IsKey = ((this.Name.ToLowerInvariant().EndsWith("id") && DbMessageKey.IsValidKeyType(this.Type)) || column.AutoIncrement); public static bool IsValidKeyType(Type type) { return type == typeof(int) || type == typeof(uint) || type == typeof(long) || type == typeof(ulong) || type == typeof(decimal) || type == typeof(DateTime); }
This key is used for time filtering. In log configuration, you can also set to get the specified log level, or you can specify a column as the time filter condition.
Look at the source code of Log4View2. When DBReceiver initializes, the Key we selected will be passed in and assigned to dbMessageKey
public DbReceiver(IReceiverFactory factory, ReceiverConfig recRow) : base(factory, recRow) { ... this._columns = new DbColumns(dbReceiverConfig.DbColumns); this._dbMessageKey = this._columns.Key; ... }
This value is used when initializing query sql
private DbCommand CreateReadQuery() { ... DbCommand dbCommand = this._database.CreateCommand(); dbCommand.CommandTimeout = this._commandTimeout; string parameterName = this._database.GetParameterName(0); string arg = this._dbMessageKey.IsUnique ? ">" : ">="; string text = this._database.QuoteName(this._dbMessageKey.Name); string tableName = this._database.QuoteName(this._tableName); DbParameter dbParameter = dbCommand.CreateParameter(); dbParameter.ParameterName = parameterName; dbParameter.DbType = this._dbMessageKey.DbType; dbParameter.Value = this._dbMessageKey.ParameterValue; dbCommand.Parameters.Add(dbParameter); string text2 = string.Format("WHERE ({0} {2} {1})", text, parameterName, arg); ... return dbCommand; }
Scientific use
There is a 30 day trial period for the first installation and use of Log4View2 tool. After the trial period, some functions will be limited. In the next chapter, I will show you how to use Log4View2 by using the decompilation tools (po) to learn (jie).
Reference documents