NLog not saving large string In Database with Buffer and asynchronously options - c#

I am working on a project which need to log SOAP messages request. Some requests have very large string more then 14 million XML characters. Issue is when these kind of messages are send to database, these messages are not saved neither any kind of exception occur in application. I try to switch on N-Log exception property but nothing happened. You can find my configuration settings below
<targets>
<target name="main" xsi:type="AutoFlushWrapper" asyncFlush="true">
<target name="database_buffer"
xsi:type="BufferingWrapper"
bufferSize="50"
flushTimeout="120000">
<target xsi:type="FallbackGroup"
name="String"
returnToFirstOnSuccess="true">
<target xsi:type="Database"
name="database_inner"
connectionString="${event-context:item=dbConnectionString}"
commandText="INSERT INTO [Log] ([Level],[Message],[Application],[MethodInfo],[Exception]) VALUES(#Level,#Message,#ApplicationName,#MethodInfo,#Exception)">
<parameter name="#Level" layout="${level:uppercase=true}" />
<parameter name="#Message" layout="${event-properties:item=Message}" />
<parameter name="#ApplicationName" layout="${event-properties:item=SourceName}" />
<parameter name="#MethodInfo" layout="${event-properties:item=MethodInfo}" />
<parameter name="#Exception" layout="${event-properties:item=Exception}" />
</target>
</target>
</target>
</target>
</targets>
There is no internal log file created or application crash in debug mode. On the same end small strings and normal length messages are successfully going into database. In database I have Nvarchar Max datatype for column.
I can't find option here to upload file so I am attaching this link to download exact message which I am trying to save in database. Here is it.
This file text successfully insert in database when I remove Buffer and asynchronously options or I insert with Ado.net components.
So please guide me the where I am wrong and what is the issue.

Related

nlog failing on production server

I have setup my asp.Net Core 5 application with Nlog for logging errors on LIVE environment. It has been working smoothly till recently when I realized that the hosting company reconfigured permissions to folders on the server, denying write permissions to some folders.
I started noticing that I was no longer able to upload files to some of the folders I had under www folder in the application.
When I brought their attention to it, they enabled write access to that folder and since then, I can upload files but I also noticed that at about the same time, my error logging stopped working. Locally, my error logging still works but not working on the production server anymore.
Now I wish to find out whether the error logging can be affected by the review of write permissions and how I might be able to troubleshoot the issue further and probably make some reasonable suggestions to the technical support team to help resolve the issue faster.
My Nlog configuration file is as follows
<?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"
autoReload="true"
internalLogLevel="Info"
internalLogFile="c:\temp\internal-nlog.txt">
<!-- enable asp.net core layout renderers -->
<extensions>
<add assembly="NLog.Web.AspNetCore" />
<add assembly="NLog.MailKit" />
</extensions>
<!-- the targets to write to -->
<targets>
<!-- write logs to file -->
<target xsi:type="File" name="alllogs" fileName="./logs/all-logs/${shortdate}.log"
layout="${longdate}|${event-properties:item=EventId_Id}|${uppercase:${level}}|${logger}|${message} ${exception:format=tostring}" />
<!-- another file log, only own logs. Uses some ASP.NET core renderers -->
<target xsi:type="File" name="errorlogs" fileName="./logs/error-logs/${shortdate}.log"
layout="${longdate}|${event-properties:item=EventId_Id}|${uppercase:${level}}|${logger}|${message} ${exception:format=tostring}|url: ${aspnet-request-url}|action: ${aspnet-mvc-action}" />
<!-- Database Logging target -->
<target name="db" xsi:type="Database"
dbHost="hosteserver"
dbDatabase="databasename"
dbUserName="username"
dbPassword="password">
<commandText>
insert into dbo.ErrorLogs (
Logtime, Level, Message, Exception,
Logger
) values (
#TimeLogged, #Level, #Message, #Exception,
#Logger
);
</commandText>
<parameter name="#TimeLogged" layout="${longdate}" />
<parameter name="#Level" layout="${level}" />
<parameter name="#Message" layout="${message}" />
<parameter name="#Exception" layout="${exception:tostring}" />
<parameter name="#Logger" layout="${logger}" />
</target>
<!-- my mailkit target for email notifications on error -->
<target xsi:type="Mail"
name="ErrorMailLogTarget"
subject="Error Occurred on The Application Name"
to="myname#myemail.com"
from="email#mydomain.com"
body="${longdate}|${event-properties:item=EventId_Id}|${uppercase:${level}}|${logger}|${message} ${exception:format=tostring}|url: ${aspnet-request-url}|action: ${aspnet-mvc-action}"
smtpUserName="email#mydomain.com"
smtpServer="mydomain.com"
smtpPort="587"
timeout="20000"
smtpPassword="email_password"
smtpAuthentication="basic"
skipCertificateValidation="true"
priority="Urgent"
/>
</targets>
<!-- rules to map from logger name to target -->
<rules>
<!--All logs, including from Microsoft-->
<logger name="*" minlevel="Trace" writeTo="alllogs" />
<!--Skip non-critical Microsoft logs and so log only own logs-->
<logger name="Microsoft.*" maxlevel="Info" final="true" />
<!-- BlackHole without writeTo -->
<logger name="*" minlevel="Error" writeTo="errorlogs" />
<!-- Error Mail notification log -->
<logger name="*" minlevel="Error" writeTo="ErrorMailLogTarget" />
<!-- Database Logging Provision -->
<logger name="*" minlevel="Error" writeTo="db" />
</rules>
</nlog>
I will appreciate any guide to resolve this.
Thank you

How to log single SQL table using NLog in C#?

I am trying to log the single table using NLog bypassing parameter values into each column using NLog. But somehow I couldn't able to log in to the SQL table.
I tried by passing values by passing code like this and added the targets and rules in web.config.
Code in the class
private static Logger _logger;
public CustomToken()
{
_logger = LogManager.GetLogger("apiUsageLogger");
}
_logger.Info("{clientname}", "test");
_logger.Info($"clientusername", "test");
_logger.Info($"route", "test");
_logger.Info($"parameters", "test");
_logger.Info($"isuserauthenticated", 1);
In the web.config
<target name="apiUsageLog" xsi:type="Database" connectionStringName="connStringName">
<commandtext>
INSERT INTO Table
(ClientName, ClientUserName, Route, Parameters, IsUserAuthenticated, Machine)
VALUES
(#clientname, #clientusername, #route, #parameters, #isuserauthenticated, #machine)
</commandtext>
<parameter name="#clientname" layout="${clientname}" />
<parameter name="#clientusername" layout="${clientusername}" />
<parameter name="#route" layout="${route}" />
<parameter name="#parameters" layout="${parameters}" />
<parameter name="#isuserauthenticated" layout="${isuserauthenticated}" />
<parameter name="#machine" layout="${machinename}" />
</target>
</targets>
<rules>
<<logger name="apiUsageLogger" minlevel="Info" writeTo="apiUsageLog" />
</rules>
Somehow data is not getting populated into the table.
Is there a way I can able to achieve in populating the data into appropriate columns? NLog is the right way of doing it?
Not sure if it's clear, but with database target one log message will be one record in the database. I would recommend to read the tutorial. Also in this case the database target options are good to check.
Your config isn't working as ${clientusername} doesn't exist in NLog.
I will show 3 examples, hope that make things clear
Example 1: simple logs to database target
Logger call:
logger.Info("my info message");
config:
<target name="apiUsageLog" xsi:type="Database" connectionStringName="connStringName">
<commandtext>
INSERT INTO Table
(message, machinename)
VALUES
(#message, #machinenameParam)
</commandtext>
<parameter name="#messageParam" layout="${message}" /> <!-- this will be "my info message"-->
<parameter name="#machinenameParam" layout="${machinename}" /> <!-- defined in NLog, see https://nlog-project.org/config/?tab=layout-renderers-->
</target>
</targets>
This will create a log record in the database with my info message and the machine name.
Example 2: with custom properties:
I will use structured logging here. See structured logging
Logger call:
logger.Info("my info message with {Property1}", "value1");
config:
<target name="apiUsageLog" xsi:type="Database" connectionStringName="connStringName">
<commandtext>
INSERT INTO Table
(message, machinename, property1)
VALUES
(#message, #machinenameParam, #propertyParam1)
</commandtext>
<parameter name="#messageParam" layout="${message}" /> <!-- this will be "my info message"-->
<parameter name="#machinenameParam" layout="${machinename}" /> <!-- defined in NLog, see https://nlog-project.org/config/?tab=layout-renderers-->
<parameter name="#propertyParam1" layout="${event-properties:Property1}" /> <!-- this will be "value1" -->
</target>
</targets>
This will create a log record in the database with my info message with "Value1" , the machine name and the custom property with "value1".
Example 3: custom properties, not all in the message
This combines structured logging and WithProperty. You need at least NLog 4.6.3 for this.
Logger call:
logger.WithProperty("Property2", "value2")
.Info("my info message {Property1}", "value1");
config:
<target name="apiUsageLog" xsi:type="Database" connectionStringName="connStringName">
<commandtext>
INSERT INTO Table
(message, machinename, property1, property2)
VALUES
(#message, #machinenameParam, #propertyParam2)
</commandtext>
<parameter name="#messageParam" layout="${message}" /> <!-- this will be: my info message with "value1"-->
<parameter name="#machinenameParam" layout="${machinename}" /> <!-- defined in NLog, see https://nlog-project.org/config/?tab=layout-renderers-->
<parameter name="#propertyParam1" layout="${event-properties:Property1}" /> <!-- this will be "value1" -->
<parameter name="#propertyParam2" layout="${event-properties:Property2}" /> <!-- this will be "value2" -->
</target>
</targets>
This will create a log record in the database with my info message with "Value1" , the machine name and the custom properties "value1" and "value2"
Note, now "value1" is in the message and "value2" isn't.

NLog Custom Target for JSNLog

I am currently developing an application which makes use of NLog for .NET logging and JSNLog to send logs from Javascript to NLog for logging. These logs are stored in a SQL database.
The SQL Database has several columns for NLog such as Message, StackTrace, InnerException and AdditionalInfo. However when I log something from JSNLog like so
window.onerror = function (errorMsg, url, lineNumber, column, errorObj) {
JL("databaseLogger").fatalException({
"msg": "Uncaught Exception",
"errorMsg": errorMsg, "url": url,
"line number": lineNumber, "column": column
}, errorObj);
return false;
}
It is simply interpreted by NLog as a simple string log and not an error, so it just adds this giant JSON string to the AdditionalInfo column, and leaves the Message, StackTrace and InnerException columns blank. This makes reading the logs much harder.
I'd like to be able to parse this JSON sent by the JSNLog call and send it to the appropriate NLog variable, i.e:
JL("databaseLogger").fatalException({
"Message": "Uncaught Exception",
"InnerException": errorMsg,
}, errorObj);
would result in the Message and InnerException columns containing the data sent in the JSON. The parsing is not an issue as I know how to do that, but I am not sure how to intercept the call to NLog so I can parse the JSON before sending it onto NLog correctly.
I've looked into writing a custom NLog target which doesn't seem to hard, but I am not sure how to then pass my parsed JSON to Nlog correctly?
EDIT #1: Updated NLog.config for #Julian
Here is my new NLog.config but the Log from the new Write function does not get passed to the database target.
<targets>
<target xsi:type="ParseJsonWrapper"
name="JSONParser">
<target name="database"
xsi:type="Database"
connectionStringName="ErrorLog"
commandText="exec dbo.InsertLog
#level,
#callSite,
#type,
#message,
#stackTrace,
#innerException,
#additionalInfo">
<parameter name="#level" layout="${level}" />
<parameter name="#callSite" layout="${callsite}" />
<parameter name="#type" layout="${exception:format=type}" />
<parameter name="#message" layout="${exception:format=message}" />
<parameter name="#stackTrace" layout="${exception:format=stackTrace}" />
<parameter name="#innerException"
layout="${exception:format=:innerFormat=ShortType,Message,Method:MaxInnerExceptionLevel=1:InnerExceptionSeparator=}" />
<parameter name="#additionalInfo" layout="${message}" />
</target>
</target>
</targets>
<rules>
<logger levels="Trace,Info,Debug,Error,Warn,Fatal" name="JSLogger" writeTo="JSONParser"/>
</rules>
In this case, it's a good solution to create a TargetWrapper so you could combine it with other targets.
The config will looks like this:
<target xsi:type="ParseJsonWrapper"
name="myWrapper">
<target xsi:type="File" ...target properties... />
</target>
The code needed for this: inherit from WrapperTargetBase and override Write. e.g.
using System;
using NLog.Common;
/// <summary>
/// Limits the number of messages written per timespan to the wrapped target.
/// </summary>
[Target("ParseJsonWrapper", IsWrapper = true)]
public class ParseJsonWrapper : WrapperTargetBase
{
protected override void Write(AsyncLogEventInfo logEvent)
{
var json = logEvent.LogEvent.Message;
// parse json
// update log event
logEvent.LogEvent.Exception = new Exception("something");
// write to wrapped target
WrappedTarget.WriteAsyncLogEvent(logEvent)
}
}
Don't forget to register your wrapper (see here) and update your config to write to the wrapper instead of the wrapped target.

Conditionally execute NLog target statement

I have one Enum class given below
public enum ScanStatus
{
Success,
Failure,
Exception
}
From .cs file I have passing the value of Exception to Nlog.config file using code
GlobalDiagnosticsContext.Set("ExceptionStatus", Convert.ToString((int)ScanStatus.Exception));
In nlog.config, i have configured to log the message in database using below code.
<target name="database" xsi:type="Database" keepConnection="true"
dbProvider="System.Data.SqlClient"
connectionStringName="PaymentScan"
commandText="INSERT INTO [dbo].[LoggingTrace] (PaymentId, ExceptionStatus, TraceDetails) VALUES (#ItemId, #ExceptionStatus, #Message)">
<parameter name="#message" layout="${message}"/>
<parameter name="#ItemId" layout="${gdc:ItemId}"/>
<!-- custom field! -->
<parameter name="#ExceptionStatus" layout="${gdc:ExceptionStatus}"/>
<!-- custom field! -->
</target>
I want this target statement should execute only when any exception occurs in the code. To handle this, I have written the rules given below
<rules>
<logger name="*" minlevel="Info" writeTo="database">
<filters>
<when condition="equals('${gdc:ExceptionStatus}', '2')" action="Log" />
</filters>
</logger></rules>
I am facing issue that When exception does not occurs, the target is getting executed, means equals statement written in rules in not working.
Could any body let me know the solution for this.

How do I configure NLog to write to a database?

I'm trying to get NLog to write to a database, however with my current code it throws an exception when I attempt to debug, the exception is: The type initializer for 'NotifyIcon.Program' threw an exception.
my NLog configuration file code is below, as this seems to be causing the issue as it's the only code I've changed.
<?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" autoReload="true">
<!--
See http://nlog-project.org/wiki/Configuration_file
for information on customizing logging rules and outputs.
-->
<targets>
<!-- add your targets here -->
<target name="database" xsi:type="Database" />
<target xsi:type="Database"
name="String"
dbUserName="Layout"
dbProvider="sqlserver"
useTransactions="false"
connectionStringName="String"
connectionString="Data Source=AC-02\SQLEXPRESS;Initial Catalog=master;Integrated Security=True"
keepConnection="true"
dbDatabase="Layout"
dbPassword="Layout"
dbHost="Layout"
installConnectionString="Layout"
commandText="INSERT INTO Logs (Machine_Name, Username, Logon_Time, Screensaver_On, Screensaver_Off, Logoff_Time, Program_Start) Values #MachineName, #Username, #LogonTime, #Screensaver_On, #Screensaver_Off, #LogoffTime, #ProgramStart "/>
</targets>
<rules>
<logger name="*" minlevel="Trace" writeTo="database" />
</rules>
</nlog>
any and all help would be greatly appreciated =]
You seem to be missing the parameters that are to be inserted.
See the examples at http://justinpdavis.blogspot.com/2010/04/logging-to-database-with-nlog.html
The nLog web page doesn't make it very clear that these are required, but if you squint your eyes and read https://github.com/nlog/NLog/wiki/Database-target you should find that they are required.
A simple example,
Config:
<target type="Database" name="database" connectionstring="Server=localhost;Database=NLog;Trusted_Connection=True;">
<commandText>
INSERT INTO NLogEntries ([Origin], [Message], [LogLevel],[CreatedOn],[OrderId]) VALUES (#Origin,#Message,#LogLevel,#Date, #OrderId);
</commandText>
<parameter name="#Date" layout="${date}" dbType="DbType.Date"/>
<parameter name="#Origin" layout="${callsite}"/>
<parameter name="#LogLevel" layout="${level}"/>
<parameter name="#message" layout="${message}"/>
<parameter name="#OrderId" layout="${event-properties:MyOrderId}" dbType="DbType.Int32"/> <!-- custom field! Note also the DB Type. Using Logger.WithProperty -->
</target>
Note, NLog 4.6 has also support for DbType - See https://nlog-project.org/2019/03/20/nlog-4-6-is-live.html
U also wrote 2 targets. And also a lot of attributes that u don't need to set. Should just be:
<target name="DbLog" xsi:type="Database" connectionString="YourConStr"
commandText="insert into [blablablabal] (Col1) values (#val1)">
<parameter name="#val1" layout="${level}" /></target>
Something like this. Easy no? :)
It looks your insert string is not in the right format? You are missing () around the parameters list.
commandText="INSERT INTO Logs (Machine_Name, Username, Logon_Time, Screensaver_On, Screensaver_Off, Logoff_Time, Program_Start) Values (#MachineName, #Username, #LogonTime, #Screensaver_On, #Screensaver_Off, #LogoffTime, #ProgramStart) "

Categories