Custom logging with NLog on database - c#

I have some problem with NLog. According to here that said
If you want custom layout properties (NLog calls them layout renderers) you can use the EventProperties Layout Renderer.
I wrote some config :
<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="on" internalLogFile="c:\temp\nlog-internal.log">
<targets>
<!-- database target -->
<target name="databaseauthentication"
xsi:type="Database"
connectionString="Data Source = [....]; Initial Catalog = [....]; User Id = [....]; Password = [....]"
commandText="exec dbo.InsertAuthentication
#company,
#firstname,
#lastname,
#ip,
#pcname,
#additionalInfo">
<parameter name="#company" layout="${event-properties:item=companyValue}" />
<parameter name="#firstname" layout="${event-properties:item=firstnameValue}" />
<parameter name="#lastname" layout="${event-properties:item=lastnameValue}" />
<parameter name="#ip" layout="${event-properties:item=ipValue}" />
<parameter name="#pcname" layout="${event-properties:item=pcnameValue}" />
<parameter name="#additionalInfo" layout="${event-properties:item=additionalInfoValue}" />
</target>
</targets>
<rules>
<logger levels="Info" name="asyncdatabaseauthenticationLogger" writeTo="asyncdatabaseauthentication"/>
<logger levels="Info" name="databaseauthenticationLogger" writeTo="databaseauthentication"/>
</rules>
</nlog>
And used that like :
public static void SendLogin()
{
var eventInfo = new LogEventInfo(LogLevel.Info, databaseAuthenticateLogger.Name, "Message");
eventInfo.Properties["firstnameValue"] = "My Fist Name;
eventInfo.Properties["lastnameValue"] = "My Last Name";
eventInfo.Properties["companyValue"] = "My Company";
eventInfo.Properties["ipValue"] = "IP";
eventInfo.Properties["pcnameValue"] = "PC Name";
eventInfo.Properties["additionalInfoValue"] = "Login";
databaseAuthenticateLogger.Log(eventInfo);
}
But this code don't work. Can anyone tell me where is my mistake?

The Above code is true, But We need to add this part in c# code :
static Logger databaseAuthenticateLogger = LogManager.GetLogger("databaseauthenticationLogger");
static Logger asyncdatabaseAuthenticateLogger = LogManager.GetLogger("asyncdatabaseauthenticationLogger");
Everythings works good.

Related

Config nLog .NET Core 3.1, missing debug messages

I have looked at numerous configurations for nLog in .net core, all is working, except that I cannot get a debug message to log. If I write an information message or error message those work just fine. So this seems like a logging level problem, but try as I might I can't seem to get the level low enough to write out debug messages.
I have also looked at this: Missing trace\debug logs in ASP.NET Core 3?
Nlog.Web.AspNetCore 4.11
.Net Core 3.1
Here is my configuration:
nothing logging specific in startup.cs
program.sc
public static void Main(string[] args)
{
var logger = NLog.Web.NLogBuilder.ConfigureNLog("nlog.config").GetCurrentClassLogger();
try
{
logger.Debug("init main");
CreateHostBuilder(args).Build().Run();
}
catch (Exception exception)
{
//NLog: catch setup errors
logger.Error(exception, "Stopped program because of exception");
throw;
}
finally
{
// Ensure to flush and stop internal timers/threads before application-exit (Avoid segmentation fault on Linux)
NLog.LogManager.Shutdown();
}
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
})
.ConfigureLogging(logging =>
{
logging.ClearProviders();
logging.SetMinimumLevel(Microsoft.Extensions.Logging.LogLevel.Trace);
})
.UseNLog();
appsettings.json
"Logging": {
"LogLevel": {
"Default": "Trace",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
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"
throwConfigExceptions="true"
internalLogLevel="info"
internalLogFile="c:\temp\Nlog.log">
<extensions>
<add assembly="NLog.Web.AspNetCore"/>
</extensions>
<targets>
<target name="database" xsi:type="Database" connectionString="${appsettings:name=ConnectionStrings.applog}" >
<commandText>
INSERT INTO [NLog] (
[ApplicationName],
[AppUserIdentity],
[LogDate] ,
[LogLevel] ,
[LogSource],
[LogAssembly] ,
[LogMessage] ,
[MachineName] ,
[DomainUser],
[CallSite],
[LogThread] ,
[LogException] ,
[ClientIp]
)
VALUES (
#ApplicationName,
#AppUserIdentity,
#LogDate ,
#LogLevel ,
#LogSource,
#LogAssembly,
#LogMessage ,
#MachineName ,
#DomainUser ,
#CallSite ,
#LogThread ,
#LogException,
#ClientIp
);
</commandText>
<parameter name="#ApplicationName" layout="${appsettings:name=AppName:default=Missing-Config}" />
<parameter name="#AppUserIdentity" layout="${aspnet-user-identity}" />
<parameter name="#LogDate" layout="${date:format=yyyy-MM-dd HH\:mm\:ss.fff}" />
<parameter name="#LogLevel" layout="${level}" />
<parameter name="#LogSource" layout="${logger} " />
<parameter name="#LogAssembly" layout="${assembly-version}" />
<parameter name="#LogMessage" layout="${message}" />
<parameter name="#MachineName" layout="${machinename}" />
<parameter name="#DomainUser" layout="${windows-identity:domain=true}" />
<parameter name="#CallSite" layout="${callsite}" />
<parameter name="#LogThread" layout="${threadid}" />
<parameter name="#LogException" layout="${exception}" />
<parameter name="#ClientIp" layout="${aspnet-request-ip}" />
</target>
</targets>
<rules>
<logger name="*" minlevel="Trace" maxlevel="Error" final="true" writeTo="database" />
</rules>
</nlog>
injected into a service. This call will not log, but if it were _logger.LogInformation("xx"), it will log. This ILogger reference is Microsoft.Extensions.Logging.ILogger
public TaxCalculationService(IUnderwritingRepository repository, ILogger<TaxCalculationService> logger)
{
_repository = repository;
_logger = logger;
}
public TaxCalculationResponseDto CalculateTaxes(TaxCalculationRequestDto request)
{
_logger.LogDebug("calculateTaxes request: " + JsonConvert.SerializeObject(request));
Please try to upgrade to NLog.Web.AspNetCore v4.12.0, and then change UseNLog() to use RemoveLoggerFactoryFilter = true like this:
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
})
.ConfigureLogging(logging =>
{
logging.ClearProviders();
logging.SetMinimumLevel(Microsoft.Extensions.Logging.LogLevel.Trace);
})
.UseNLog(new NLogAspNetCoreOptions() { RemoveLoggerFactoryFilter = true });
See also: https://github.com/NLog/NLog.Extensions.Logging/pull/482
You might want to update the NLog-LoggingRules to ignore noise from Microsoft-Loggers:
<rules>
<!-- Block output from noisy loggers -->
<logger name="Microsoft.*" maxlevel="Info" final="true" />
<logger name="System.Net.Http.*" maxlevel="Info" final="true" />
<!-- Write the good stuff to the databse -->
<logger name="*" minlevel="Debug" writeTo="database" />
</rules>

add record in nlog to field with dataType = date

I use nlog dll to write to database - oracle with entity frameWork in the line :
logger.Log(logLevel, "try");
I get in the logs of nlog the following error:
The literal does not match the template string
the code is:
SetPropGDC(LogEntity);
NLog.LogLevel logLevel = SetLogLevel(Level.Debug);
logger.Log(logLevel, "try");
ClearGDC();
private void SetPropGDC(LogEntity LogEntity)
{
GlobalDiagnosticsContext.Set(processId, LogEntity.PROCESS_ID.ToString());
GlobalDiagnosticsContext.Set("TIME_STAMP", DateTime.Now);
GlobalDiagnosticsContext.Set(customerId, LogEntity.CUSTOMER_ID.ToString());
}
<targets>
<target name="TRACEDatabase" type="DataBase" keepConnection="false"
dbProvider="Oracle.ManagedDataAccess.Client" connectionString="${gdc:connectionString}"
commandText="insert into TLOG_SITE_GENERAL_TRACE( PROCESS_ID,TIME_STAMP,CUSTOMER_ID)
values(:PROCESS_ID,:TIME_STAMP,:CUSTOMER_ID)">
<parameter name="PROCESS_ID" layout="${gdc:PROCESS_ID}" />
<parameter name="TIME_STAMP" layout="${gdc:TIME_STAMP}" />
<parameter name="CUSTOMER_ID" layout="${gdc:CUSTOMER_ID}" />
</target>
I tryed in the Web.config to change the line:
<parameter name="TIME_STAMP" layout="${gdc:TIME_STAMP}" />
to:
<parameter name="TIME_STAMP" layout="${longDate}" />
and I got the same error
Can anyone help?
NLog DatabaseTarget parameters converts to string by default. You can change the datatype by specifying dbType so it matches the database-column:
<target name="TRACEDatabase" type="DataBase">
<parameter name="PROCESS_ID" layout="${event-properties:PROCESS_ID}" />
<parameter name="TIME_STAMP" layout="${date}" dbType="DateTime" />
<parameter name="CUSTOMER_ID" layout="${event-properties:CUSTOMER_ID}" />
</target>
Btw. it is a bad idea to use global variables for transfering context specific details.
Instead you should make use of NLog LogEventInfo Properties:
var logLevel = SetLogLevel(Level.Debug);
var theEvent = new NLog.LogEventInfo(logLevel, null, "try");
theEvent.Properties["PROCESS_ID"] = LogEntity.PROCESS_ID.ToString();
theEvent.Properties["CUSTOMER_ID"] = LogEntity.CUSTOMER_ID.ToString();
log.Log(theEvent);
See also: https://github.com/NLog/NLog/wiki/EventProperties-Layout-Renderer

Value does not fall within the expected range using nlog with entity frameWork

I use nlog dll(version 3.1.0.0) to write to database - oracle with entity frameWork in the line :
logger.Log(logLevel, "try");
I get in the logs of nlog the following error:
Error Error when writing to database System.ArgumentException: Value
does not fall within the expected range. at
Oracle.ManagedDataAccess.Client.OracleCommand.set_CommandType(CommandType
value) at
NLog.Targets.DatabaseTarget.WriteEventToDatabase(LogEventInfo
logEvent) at NLog.Targets.DatabaseTarget.Write(LogEventInfo
logEvent)
the code is:
SetPropGDC(LogEntity);
NLog.LogLevel logLevel = SetLogLevel(Level.Debug);
logger.Log(logLevel, "try");
ClearGDC();
private static LogLevel SetLogLevel(Level level)
{
switch (level)
{
case Level.Debug: return LogLevel.Debug;
case Level.Error: return LogLevel.Error;
case Level.Fatal: return LogLevel.Fatal;
case Level.Info: return LogLevel.Info;
default: return LogLevel.Error;
}
}
private void SetPropGDC(LogEntity LogEntity)
{
GlobalDiagnosticsContext.Set("connectionString", _unitOfWork.getConnectionString());
GlobalDiagnosticsContext.Set(processId, LogEntity.PROCESS_ID.ToString());
GlobalDiagnosticsContext.Set("TIME_STAMP", LogEntity.TIME_STAMP.ToString());
GlobalDiagnosticsContext.Set(customerId, LogEntity.CUSTOMER_ID.ToString());
GlobalDiagnosticsContext.Set("REQUEST", LogEntity.REQUEST.ToString());
GlobalDiagnosticsContext.Set("RESPONSE", LogEntity.RESPONSE.ToString());
GlobalDiagnosticsContext.Set("EXCEPTION", LogEntity.EXCEPTION.ToString());
GlobalDiagnosticsContext.Set("STACK_TRACE", LogEntity.STACK_TRACE.ToString());
GlobalDiagnosticsContext.Set("DETAILS", LogEntity.DETAILS.ToString());
}
<targets>
<target name="TRACEDatabase" type="DataBase" keepConnection="false"
dbProvider="Oracle.ManagedDataAccess.Client" connectionString="${gdc:connectionString}"
commandText="insert into TLOG_SITE_GENERAL_TRACE( PROCESS_ID,TIME_STAMP,CUSTOMER_ID,LOG_LEVEL,REQUEST,RESPONSE,EXCEPTION,STACK_TRACE,MESSAGE)
values(:PROCESS_ID,:TIME_STAMP,:CUSTOMER_ID,:LOG_LEVEL,:REQUEST,:RESPONSE,:EXCEPTION,:STACK_TRACE,:MESSAGE)">
<parameter name="PROCESS_ID" layout="${gdc:PROCESS_ID}" />
<parameter name="TIME_STAMP" layout="${gdc:TIME_STAMP}" />
<parameter name="CUSTOMER_ID" layout="${gdc:CUSTOMER_ID}" />
<parameter name="LOG_LEVEL" layout="${level:uppercase=true}" />
<parameter name="REQUEST" layout="${gdc:REQUEST}" />
<parameter name="RESPONSE" layout="${gdc:RESPONSE}" />
<parameter name="EXCEPTION" layout="${gdc:EXCEPTION}" />
<parameter name="STACK_TRACE" layout="${gdc:STACK_TRACE}" />
<parameter name="MESSAGE" layout="${message}" />
</target>
Can anyone help?
I think you're looking for type support for database parameters, that's introduced in NLog 4.6.
PS: Please note that event properties (on LogEventInfo) are a more robust than the GlobalDiagnosticsContext in your example

How to change the NLog layout at run time?

I created the 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"
throwExceptions="false"
throwConfigExceptions="true"> <!--make NLog complain, when something is wrong with the configuration-->
<targets>
<target name="logfile" xsi:type="File"
fileName="${basedir}/Logs/Log_${shortdate}.txt"
layout="[${longdate}] [${uppercase:${level}}] ${message} ${exception:format=tostring}"
/>
<target name="logconsole" xsi:type="ColoredConsole"
layout="[${longdate}] ${uppercase:${level}}: ${message} ${exception:format=tostring}"
/>
</targets>
<rules>
<logger name="*" minlevel="Info" writeTo="logconsole" />
<logger name="*" minlevel="Debug" writeTo="logfile" />
</rules>
</nlog>
When I call logger.Info("Hello World"); I get an output both in the console and the file:
[2018-03-17 15:34:24.2843] INFO: Hello World
Which is fine.
Now in some cases I do not want to display the date-time part, and I want to display it only in the console.
How would I do that with NLog?
A pseudo solution I would want is:
logger.Info("Hello World", writeTo: "logconsole", layout: "[${uppercase:${level}}] ${message}");
I tried adding a new taget
<target name="logconsole_simple" xsi:type="ColoredConsole"
layout="${uppercase:${level}}: ${message} ${exception:format=tostring}"/>
and new rule:
<logger name="logconsole_simple" level="Info" writeTo="logconsole_simple" />
And call it like:
logger.Log(LogEventInfo.Create(LogLevel.Info, "logconsole_simple", "simple message!"));
But it still displays the date-time part and logs it to the file also (which I don't want).
How to fix this?
Add a target and rule for your special case and reference your loggers by name.
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"
throwExceptions="false"
throwConfigExceptions="true"><!--make NLog complain, when something is wrong with the configuration-->
<targets>
<target name="logfile" xsi:type="File"
fileName="${basedir}/Logs/Log_${shortdate}.txt"
layout="[${longdate}] [${uppercase:${level}}] ${message} ${exception:format=tostring}"
/>
<target name="logconsole_default" xsi:type="Console"
layout="[${longdate}] ${uppercase:${level}}: ${message} ${exception:format=tostring}"
/>
<target name="logconsole_other" xsi:type="Console"
layout="${uppercase:${level}}: ${message} ${exception:format=tostring}"
/>
</targets>
<rules>
<logger name="default" minlevel="Debug" writeTo="logfile" />
<logger name="default" minlevel="Info" writeTo="logconsole_default" />
<logger name="other" minlevel="Info" writeTo="logconsole_other" />
</rules>
</nlog>
Usage:
internal class Program
{
private static readonly Logger DefaultLogger = LogManager.GetLogger("default");
private static readonly Logger OtherLogger = LogManager.GetLogger("other");
private static void Main(string[] args)
{
for (var i = 0; i < 2; i++)
{
if (i % 2 == 0)
{
DefaultLogger.Info("normal log message");
}
else
{
OtherLogger.Info("special case");
}
}
}
}
EDIT: If you really want to do it in code by modifying your existing target and rule, you can (this will work with the config from your original post):
using System.Linq;
using NLog;
using NLog.Layouts;
using NLog.Targets;
internal class Program
{
private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
private static void Main(string[] args)
{
for (var i = 0; i < 2; i++)
{
if (i % 2 == 0)
{
Logger.Info("normal log message");
}
else
{
// reconfigure
var consoleTarget = LogManager.Configuration.FindTargetByName<ColoredConsoleTarget>("logconsole");
var oldLayout = consoleTarget.Layout;
consoleTarget.Layout = new SimpleLayout { Text = "${uppercase:${level}}: ${message} ${exception:format=tostring}" };
var fileRule = LogManager.Configuration.LoggingRules.Single(r => r.Targets.Any(t => t.Name == "logfile"));
LogManager.Configuration.LoggingRules.Remove(fileRule);
LogManager.Configuration.Reload();
Logger.Info("special case");
// replace originals
consoleTarget.Layout = oldLayout;
LogManager.Configuration.LoggingRules.Add(fileRule);
LogManager.Configuration.Reload();
}
}
}
}

Logging with NLog and runtime parameters to Database

I am trying to log some info to database with NLog, but I can retrieve some DB parameters only at runtime. Tried few solutions, but unsuccessfully.
There is my 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">
<variable name="sqlDate" value="[${date:format=yyyy-MM-dd HH\:mm\:ss\.fff}]"/>
<targets>
<target name="dataBase"
xsi:type="Database"
dbProvider="System.Data.SqlClient"
connectionString="${gdc:item=myConnectionstring}"
dbDatabase="${gdc:item=myDataBase}">
<commandText>
INSERT INTO [TABLE_NAME]([COL1], [COL2] )
VALUES(#val1, #val2)
</commandText>
<parameter name="#val1" layout="${event-context:item=val1}"/>
<parameter name="#val2" layout="${event-context:item=val2}"/>
</target>
</targets>
<rules>
<logger name="_dataBase" levels="Info" writeTo="dataBase" final="true" />
</rules>
</nlog>
And there is my .NET C# code:
public static class Log
{
private static NLog.Logger _logger = NLog.LogManager.GetCurrentClassLogger();
private static Config _config = LoadConfig();
public static void Info(string val1, string val2)
{
DatabaseTarget databaseTarget = (NLog.Targets.DatabaseTarget)NLog.LogManager.Configuration.FindTargetByName("dataBase");
databaseTarget.ConnectionString = _config.ConnectString;
databaseTarget.DBDatabase = _config.DBName;
NLog.LogManager.ReconfigExistingLoggers();
LogEventInfo info = new LogEventInfo(LogLevel.Info, "_dataBase", "Test_message");
info.Properties["val1"] = val1;
info.Properties["val2"] = val2;
_logger.Log(info);
}
}
And it just do nothing: not crashing, not writing any data to DB. Here is my internal NLog log: http://pastebin.com/0tLi9zJ1
How can I fix this?
P.S. Sorry for my last try to ask this question, it looks like I forgot to put to it some necessary information.
So, the working code is:
<?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">
<variable name="sqlDate" value="${date:format=yyyy-MM-dd HH\:mm\:ss\.fff}"/>
<targets>
<target name="dataBase"
xsi:type="Database"
dbProvider="System.Data.SqlClient"
connectionString="${gdc:item=myConnectionString}"
dbDatabase="${gdc:item=myDataBase}">
<commandText>
INSERT INTO [TABLE_NAME]([COL1], [COL2] )
VALUES(#val1, #val2)
</commandText>
<parameter name="#val1" layout="${event-context:item=val1}"/>
<parameter name="#val2" layout="${event-context:item=val2}"/>
</target>
</targets>
<rules>
<logger name="_dataBase" levels="Info" writeTo="dataBase" final="true" />
</rules>
</nlog>
public static class Log
{
private static Config _config = LoadConfig();
public static void Info(string val1, string val2)
{
NLog.Logger logger = NLog.LogManager.GetLogger("_dataBase");
GlobalDiagnosticsContext.Set("myConnectionString", _config.ConnectString);
GlobalDiagnosticsContext.Set("myDataBase", _config.DBName);
LogEventInfo info = new LogEventInfo(LogLevel.Info, "_dataBase", "Test_message");
info.Properties["val1"] = val1;
info.Properties["val2"] = val2;
logger.Log(info);
}
}

Categories