NLog Database Target Not Finding Table - c#

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>

Related

Want to log Database connection failure issue in other file using NLog

I am working on .Net Core 2.2 and MySQL.
General log of application are logged into Database using NLog but I want to log only DB errors (If occurs while logging) to the file.
How I can capture DB connection failure to the file with custom fields like UserID.?
Here's the code of 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"
autoReload="true"
throwExceptions="false"
internalLogLevel="Warn"
internalLogFile="D:\logs\New\internal-nlog.txt">
<targets>
<target name="database" xsi:type="Database"
dbProvider="MySql.Data.MySqlClient.MySqlConnection, MySql.Data"
connectionString="${var:myConnectionstring}">
<commandText>
insert into log (
Logged, Message,
CallSite, Exception, userID
) values (
#Logged, #Message,
#Callsite, #Exception, #userID
);
</commandText>
<parameter name="#logged" layout="${date}" />
<parameter name="#message" layout="${message}" />
<parameter name="#callSite" layout="${event-properties:Callsite}" />
<parameter name="#exception" layout="${exception:tostring}" />
<parameter name="#userID" layout="${event-properties:userID}" />
</target>
</targets>
<rules>
<logger name="*" level="Error" writeTo="database" />
</rules>
</nlog>
In .Net core I had written this:
Logger logger = LogManager.GetCurrentClassLogger();
LogManager.Configuration.Variables["myConnectionstring"]=Setting.connString;
LogManager.ReconfigExistingLoggers();
logger.SetProperty("userID", "UR0001");
logger.SetProperty("Callsite", ex.Error.TargetSite.Name);
logger.LogException(NLog.LogLevel.Error, ex.Error.Message,ex.Error);

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 ${identity} and ${windows-identity} saved as "notauth"

I'm working on a C# class library project that is using NLog. NLog is logging to a database. I've set up a console app test project to call the library (the console app has no logging). I have a column in the log table to store the user name of the logged in user running the application.
According to NLog's documentation, that value is stored in either the ${identity} or ${windows-identity} layout renderers. I've tried using both, but when the log writes to the database, the value of the UserName column is notauth::. How can I fix this? My nlog.config file is below.
<?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"
xsi:schemaLocation="http://www.nlog-project.org/schemas/NLog.xsd NLog.xsd"
autoReload="true"
throwExceptions="false"
internalLogLevel="Off"
internalLogFile="c:\temp\nlog-internal.log"
throwConfigExceptions="true">
<targets>
<target name="db"
xsi:type="Database"
connectionString="Server=ServerName;Database=DatabaseName;Integrated Security=True;">
<commandText>
INSERT INTO Logs.TableName
(
[Level]
, UserName
, CallSite
, [Message]
, Exception
, StackTrace
, Logged
)
VALUES
(
#level
, #userName
, #callSite
, #message
, #exception
, #stackTrace
, #logged
)
</commandText>
<parameter name="#level" layout="${level}" />
<parameter name="#userName" layout="${identity}" />
<parameter name="#callSite" layout="${callsite}" />
<parameter name="#message" layout="${message}" />
<parameter name="#exception" layout="${exception:tostring}" />
<parameter name="#stackTrace" layout="${stacktrace}" />
<parameter name="#logged" layout="${date}" />
</target>
</targets>
<rules>
<logger name="*" minlevel="Debug" writeTo="db" />
</rules>
</nlog>
I think there is something strange in you project. I made a small repo and have the following results:
${identity} shows indeed notauth::, that's correct as I don't authenticate to something - we use this for IIS with forms or windows authentication.
${windows-identity} shows COFFEE-LAKE\Julian - which is correct. "COFFEE-LAKE" is my machine name and Julian is also correct :) note - ${windows-identity} will never render notauth:: - I checked NLog's source
See repo here: https://github.com/304NotModified/nlog-console-notauth
When running, there is a "test1.log" in your "bin/Debug" folder.
note: it has the following 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"
xsi:schemaLocation="http://www.nlog-project.org/schemas/NLog.xsd NLog.xsd"
autoReload="true"
throwExceptions="false"
internalLogLevel="Off" internalLogFile="c:\temp\nlog-internal.log">
<targets>
<target xsi:type="File" name="f"
fileName="${basedir}/test1.log"
layout="${longdate}|${level:uppercase=true}|${logger}|${message}|identity: '${identity}' | windows-identity: '${windows-identity}'" />
</targets>
<rules>
<logger name="*" minlevel="Debug" writeTo="f" />
</rules>
</nlog>

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

Categories