It is necessary to achieve prompt (instantaneous) notification of responsible people about errors in all instances of the application. And for different instances there should be different ways of log delivery: for a local launch, the programmer should only notify him; with PROD - project leads, immediately mobilizing them; from the test server - responsible for the corresponding circuit.
Using NLog will allow you to customize the log delivery method not in the application code (in the C # code it will be _logger.Info (message) or _logger.Error (exception) ), but in the xml-configuration file NLog.config . At the level of this file for different levels (and other necessary conditions) it is possible to set different delivery methods. There are four main ways:
There are other ways, including you can create your own way by attaching it to the application plugin.
For prompt delivery of errors (exception) in a web application, they are best delivered by email . All other (informational) logs can be saved either to the database or to a file . And in the case of the launch of the console application - all in the console . Thus, when executing the same C # code (C # libraries), the logs must be delivered in different ways - which is achieved by highlighting the delivery conditions in a separate file, NLog.config , which is stored in the project of the application being launched.
The same logging entry can be delivered in several ways at the same time - for example, errors other than sending by email should be saved in the main log repository.
To have different log delivery settings for different contours (application instances), you need to use config transformations . It is necessary to do so, and with their help it is possible to:
For this, C # sources do not need to be edited - it is enough to have different NLog.config files in different projects launched and config transformations on these files.
All this is possible thanks to the removal of logging settings in the config-file (which gives us NLog), and setting up the configuration transformations to it.
I defined the target 's:
<target name="databaseLog" dbProvider="mssql" xsi:type="Database" connectionString="Data Source=${sqlserver}; Initial Catalog=Logs;Persist Security Info=True;User ID=log_writer;Password=gfhjkm;Application Name=${src} Logger;" commandText="exec AddLog @MachineName=@machinename, @Source=@source, @SubSource=@subsource, @Level=@level, @ThreadName=@threadname, @ThreadId=@threadid, @ProcessName=@pn, @ProcessFullName=@pfn, @Msg=@message" > <parameter name="@machinename" layout="${machinename}" /> <parameter name="@source" layout="${src}" /> <parameter name="@subsource" layout="${logger}" /> <parameter name="@level" layout="${level}" /> <parameter name="@threadname" layout="${threadname}" /> <parameter name="@threadid" layout="${threadid}" /> <parameter name="@pn" layout="${processname:fullName=false}" /> <parameter name="@pfn" layout="${processname:fullName=true}" /> <parameter name="@message" layout="${message}. ${exception:format=ToString}" /> </target>
<target name="mailtargetError" xsi:type="Mail" html="false" addNewLines="true" encoding="UTF-8" subject="${src} error notification (server ${machinename}, iis ${iis-site-name})" header="Runtime error in project ${src} at server ${machinename}, iis site ${iis-site-name}. ${newline} ${newline} " body="${date:format=dd.MM.yyyy HH\:mm\:ss} Thread=${threadname}:${threadid} ${level:uppercase=true} in ${logger}: ${newline} ${newline} ${message}. ${exception:format=ToString} ${newline} ${newline} Process [${processname:fullName=true}] ${newline} (${processname:fullName=false})" to="${mails_error_reciever}" from="${mails_error_sender}" smtpAuthentication="None" smtpServer="${mails_error_smtpserver}" smtpPort="25" />
<target xsi:type="Console" name="Console" layout="Thread ${threadname}:${threadid} ${level:uppercase=true} ${logger}: ${message}. ${exception:format=ToString}" error="true" />
Delivery rules ( rules ) in NLog.config for a web application look like this:
<rules> <logger name="*" minlevel="Trace" writeTo="databaseLog" /> <logger name="*" minlevel="Error" writeTo="mailtargetError"/> </rules>
Such a record means that errors will be sent via email and written to the database, and all logs below the level will only be written to the database.
The delivery rule for the console looks like this:
<rules> <logger name="*" minlevel="Trace" writeTo="Console" /> </rules>
It means outputting all the logs to the console.
So that you can redefine the parameters in the config transformations separately, you can extract the nlog-variables:
<variable name="fileLogDir" value="${basedir}/log"/> <variable name="fileLayout" value="${date:format=dd.MM.yyyy HH\:mm\:ss} Thread=${threadname}:${threadid} ${level:uppercase=true} in ${logger}: ${message}. ${exception:format=ToString}"/> <variable name="sqlserver" value="sqlserver_logs"/> <variable name="mails_error_smtpserver" value="mail.company.com"/> <variable name="mails_error_reciever" value="konstantin.chernyaev@company.com"/> <variable name="mails_error_sender" value="project@company.com"/>
NLog allows you to output, besides the information line itself or exception, quite a lot of data (see the documentation ). If you need to save special data, this is possible with the help of event-properties :
LogEventInfo e = new LogEventInfo(LogLevel.Info, _logger.Name, "message"); e.Properties["userId"] = user.Id; _logger.Log(e);
Then in the NLog.config you can use the variable $ {event-properties: item = domainId} :
<target name="databaseLogCustom" dbProvider="mssql" xsi:type="Database" commandText="exec AddLog @UserId = @userid, ..." ... > <parameter name="@userid" layout="${event-properties:item=domainId}" /> ...
1) Config transformation 's are possible only in solution configuration . Therefore, it is needed for each circuit (instance of application) has its own, including for each developer - its own. An example of a personal solution configuration :
After installation, any file (!) In Solution Explorer will have the items " Add Config Transforms ", " Preview Config Transforms ":
Example - App.config , NLog.config :
<UsingTask TaskName="TransformXml" AssemblyFile="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\Web\Microsoft.Web.Publishing.Tasks.dll" /> <Target Name="App_config_AfterCompile" AfterTargets="AfterCompile" Condition="Exists('App.$(Configuration).config')"> <!--Generate transformed app config in the intermediate directory--> <TransformXml Source="App.config" Destination="$(IntermediateOutputPath)$(TargetFileName).config" Transform="App.$(Configuration).config" /> <!--Force build process to use the transformed configuration file from now on.--> <ItemGroup> <AppConfigWithTargetPath Remove="App.config" /> <AppConfigWithTargetPath Include="$(IntermediateOutputPath)$(TargetFileName).config"> <TargetPath>$(TargetFileName).config</TargetPath> </AppConfigWithTargetPath> </ItemGroup> </Target> <!--Override After Publish to support ClickOnce AfterPublish. Target replaces the untransformed config file copied to the deployment directory with the transformed one.--> <Target Name="App_config_AfterPublish" AfterTargets="AfterPublish" Condition="Exists('App.$(Configuration).config')"> <PropertyGroup> <DeployedConfig>$(_DeploymentApplicationDir)$(TargetName)$(TargetExt).config$(_DeploymentFileMappingExtension)</DeployedConfig> </PropertyGroup> <!--Publish copies the untransformed App.config to deployment directory so overwrite it--> <Copy Condition="Exists('$(DeployedConfig)')" SourceFiles="$(IntermediateOutputPath)$(TargetFileName).config" DestinationFiles="$(DeployedConfig)" /> </Target> <Target Name="NLog_config_AfterBuild" AfterTargets="AfterBuild" Condition="Exists('NLog.$(Configuration).config')"> <TransformXml Source="NLog.config" Destination="$(OutputPath)NLog.config" Transform="NLog.$(Configuration).config" /> </Target>
(TeamCity picks up these transformations because they are part of the build process)
With this writing of tags, the source file ( Web.config , NLog.config ) does not change , only the resulting file is corrected, which is put in the assembly folder - this means that it will not change constantly and commit each time if the developers have personal configuration solutions there will be various transformations of this file.
5 Next, in the created NLog.username.config file corresponding to the personal solution configuration , you need to replace the address of the error recipient:
<variable name="mails_error_reciever" value="konstantin.chernyaev@company.com" xdt:Locator="Match(name)" xdt:Transform="SetAttributes"/>
And for a production contour (for example):
<variable name="mails_error_reciever" value="developers@company.com" xdt:Locator="Match(name)" xdt:Transform="SetAttributes"/>
An exhaustive help on writing transformations is here: https://msdn.microsoft.com/en-us/library/dd465326(v=vs.110).aspx
Briefly:
To replace a whole tag, you need to mark it with the xdt attribute : Transform = "Replace" :
<rules xdt:Transform="Replace"> <logger name="*" minlevel="Trace" writeTo="databaseLog" /> <logger name="*" minlevel="Error" writeTo="mailtargetError"/> </rules>
To remove a whole tag, you need to mark it with the xdt attribute : Transform = "Remove" :
<authorization> <deny xdt:Transform="Remove" /> </authorization>
To insert a tag, you need to mark it with the xdt attribute : Transform = "Insert" :
<nlog> <targets> <target ... xdt:Transform="Insert" >
To add tag attributes, you need to mark it with the xdt attribute : Transform = "SetAttributes (list of attributes via PPT)" , adding the attributes themselves:
<compilation debug="true" xdt:Transform="SetAttributes(debug)" />
To remove a tag attribute, you need to mark it with the xdt attribute : Transform = "RemoveAttributes (list of attributes via PPT)" :
<compilation xdt:Transform="RemoveAttributes(debug)" />
To replace attribute values, you need to tag the tag with xdt attributes : Locator = "Match (name)" xdt: Transform = "SetAttributes" :
<connectionStrings> <add name="CS" connectionString="Data Source=dbserver;Initial Catalog=DB;Persist Security Info=True;User ID=usr;Password=psw" xdt:Transform="SetAttributes" xdt:Locator="Match(name)" />
When the developer starts a web project from Visual Studio, the error messages will be sent only to the launching developer (if he does everything described above), when launched from the console, only to him to the console, when launched on the PROD, as indicated in NLog.Prod .config .
Global.asax.cs :
static readonly Logger _logger = LogManager.GetLogger("Global.asax"); protected void Application_Error(object sender, EventArgs e) { HttpApplication app = (HttpApplication)sender; Exception ex = Server.GetLastError(); _logger.Fatal(ex, $"Application_Error: {app.Context.Request.RawUrl}"); }
WebAPI controller:
// : static readonly Logger _logger = LogManager.GetLogger("( )"); // : try { // code } catch (SomeSoftException ex) { return BadRequest(ex.Message); } catch (Exception ex) { _logger.Error(ex); // email return base.InternalServerError(ex); }
Source: https://habr.com/ru/post/358584/
All Articles