How to log single SQL table using NLog in C#? - 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.

Related

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) "

How do I log a custom field in NLog to database?

I currently use NLog on a lot of projects. On some, I log to a database.
Here is what I would like to do:
CREATE TABLE [dbo].[NLogEntries](
[Id] [bigint] IDENTITY(1,1) NOT NULL,
[Origin] [nvarchar](100) NOT NULL,
[LogLevel] [nvarchar](20) NOT NULL,
[Message] [nvarchar](3600) NOT NULL,
[CreatedOn] [datetime] NOT NULL,
[OrderId] [int] NULL --Custom field!
)
And NLog.config with this target:
<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}"/>
<parameter name="#Origin" layout="${callsite}"/>
<parameter name="#LogLevel" layout="${level}"/>
<parameter name="#message" layout="${message}"/>
<parameter name="#OrderId" layout="${orderId}"/> <!-- custom field! -->
</target>
And then log something like this:
var logger = LogManager.GetCurrentClassLogger();
var orderId = 123;
logger.Debug("What is going on here", orderId);
Is there a good way to do this and keep using NLog? Or do I have to roll my own logger and skip NLog when these are the requirements?
UPDATE 11 Feb 2022: newer versions of NLOG have other solutions- see Julian’s answer.
Rather than using GDC, which is for global static data and fails on concurrent logging, it is better to use EventProperties-Layout-Renderer that allows to pass custom  properties specific for the event
LogEventInfo theEvent = new LogEventInfo(logLevel, "", message);
theEvent.Properties["OrderId"] =orderId;`
log.Log(theEvent);
... and in your NLog.config file:
${event-context:item=OrderId} -- obsolete
${event-properties:item=OrderId} -- renders OrderId
Here is one approach using the GlobalContext.
Configuration:
<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}"/>
<parameter name="#Origin" layout="${callsite}"/>
<parameter name="#LogLevel" layout="${level}"/>
<parameter name="#message" layout="${message}"/>
<parameter name="#OrderId" layout="${gdc:OrderId}"/> <!-- custom field! -->
</target>
Call site:
var logger = LogManager.GetCurrentClassLogger();
GlobalDiagnosticsContext.Set("OrderId",123);
logger.Debug("What is going on here"); //If you use the logging configuration above, 123 will be logged to the OrderId column in your database
With a little more effort, you could wrap the NLog logger using one of the techniques illustrated here.
Or, you could create your own "context" object and write a custom LayoutRenderer to pull the values from it and write them to the log. Custom LayourRenderers are easy to write. You can see one example in my first answer to this question. There, I show how to write your own LayoutRenderer that appends the current value of System.Diagnostics.Trace.CorrelationManager.ActivityId to the log message.
NLog 4.5 introduces structured logging, so you can do this:
logger.Debug("What is going on here. OrderId={MyOrderId}", orderId);
With NLog 4.6.3 it possible to create temporary Logger, that is imbued with the desired property using WithProperty:
Call
int orderId = 123;
logger.WithProperty("MyOrderId", orderId).Info("This is my message!");
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 -->
</target>
Note, NLog 4.6 has also support for DbType - See https://nlog-project.org/2019/03/20/nlog-4-6-is-live.html
If that's all one needs, as of NLog version 4.3.3 there's an easier way to declare and access custom variables. Beware: none of these solutions are thread-safe.
Add the following to the NLog.config
<nlog ...
<!-- optional, add some variables -->
...
<variable name="myvarone" value="myvalue"/>
<variable name="myvartwo" value=2/>
...
</nlog>
Variables can be changed/accessed in the code by:
LogManager.Configuration.Variables["myvarone"] = "New Value"
LogManager.Configuration.Variables["myvartwo"] = 2
The values can be referenced in NLog.config:
"${var:myvarone}" -- renders "New Value"
"${var:myvartwo}" -- renders 2
As I mentioned above var and LogEventInfo objects are global. So if multiple instances are defined, changing a value would change the value for all instances. I'm very interested if anyone knows a way to declare per instance custom variables for NLog.
You can use the MDC code:
var logger = LogManager.GetCurrentClassLogger();
MDC.Set("OrderId", 123);
MDC.Set("user", HttpContext.Current.User.Identity.Name);
// ... and so on
also check this http://weblogs.asp.net/drnetjes/archive/2005/02/16/374780.aspx

NLog Database Target Not Finding Table

I am using NLog 1.0 and have the following configuration file:
<?xml version="1.0"?>
<nlog autoReload="true" throwExceptions="true">
<targets>
<!--<target name="console" xsi:type="ColoredConsole"
layout="${date:format=HH\:mm\:ss}|${level}|${stacktrace}|${message}" />
<target name="file" xsi:type="File" fileName="${basedir}/Logs/Site.log"
layout="${date}: ${message}" />
<target name="eventlog" xsi:type="EventLog" source="My App" log="Application"
layout="${date}: ${message} ${stacktrace}" />-->
<target name="database" type="Database" connectionStringName="xxxxxxx">
<dbprovider>mssql</dbprovider>
<commandText>
INSERT INTO Logs (Message, Level, CreatedOn) VALUES (#message, #level, #createdOn);
</commandText>
<parameter name="#message" layout="${message}" />
<parameter name="#level" layout="${level}" />
<parameter name="#createdOn" layout="${date}" />
</target>
</targets>
<rules>
<logger name="*" minlevel="Debug" appendTo="database" />
<!--<logger name="*" minlevel="Info" writeTo="file" />
<logger name="*" minlevel="Fatal" writeTo="eventlog" />-->
</rules>
This is almost a copy paste from the codeplex example supplied with nlog. In my global.ascx I have it log "Application Started!" just to test logging is working however this is where the problems begin (by the way the file target works well it's just the DB target):
System.Data.SqlClient.SqlException: Invalid object name 'Logs'.
Seems pretty straight forward to me, must be a typo. I check SQL Profiler and get the following:
exec sp_executesql N'
INSERT INTO Logs (Message, Level, CreatedOn) VALUES (#message, #level, #createdOn);
',N'#message nvarchar(20),#level nvarchar(4),#createdOn nvarchar(19)',#message=N'Application Started!',#level=N'Info',#createdOn=N'06/25/2010 23:59:30'
My table create code is below:
CREATE TABLE Logs (
Id INT IDENTITY(1, 1) NOT NULL,
[Level] VARCHAR(5) NOT NULL,
[Message] VARCHAR(4095) NOT NULL,
CreatedOn DATETIME NOT NULL,
CONSTRAINT PK_Logs PRIMARY KEY (Id)
)
Any ideas, surely I am missing something simple?
I managed to get this working and have a feeling my connection string is the problem. When the commandtext is changed to:
USE xxxx INSERT INTO dbo.Logs (Message, Level, CreatedOn) VALUES (#message, #level, #createdOn);
All works, I will try some connection strings if you know any that work with NLog let me know please!
Check the namespace of the database table. You can prefix the table name with "dbo" (for Database Owner) as follows:
CREATE TABLE dbo.Logs (
[Id] INT IDENTITY(1, 1) NOT NULL,
[Level] VARCHAR(5) NOT NULL,
[Message] VARCHAR(4095) NOT NULL,
[CreatedOn] DATETIME NOT NULL,
CONSTRAINT PK_Logs PRIMARY KEY (Id)
)
Your table might have a namespace of someName.logs when you created it. Then you can specify the full name in the XML file as follows:
INSERT INTO dbo.Logs (Message, Level, CreatedOn) VALUES (#message, #level, #createdOn);
To resolve the problem I added the "dbDatabase" parameter to the target like so:
<target name="database" type="Database" connectionStringName="MediaLibrary" dbDatabase="MediaLibrary">
<commandText>
INSERT INTO dbo.Logs (Message, Level, CreatedOn) VALUES (#message, #level, #createdOn);
</commandText>
<parameter name="#message" layout="${message}" />
<parameter name="#level" layout="${level}" />
<parameter name="#createdOn" layout="${date}" />
</target>

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

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.

Categories