I'm using NLog for logging into database. It seems to me its misplacing value in columns. For instance, it writes StackTrace in Message column and Exception information in StackTrace column
Configuration:
<nlog xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" throwConfigExceptions="true" throwExceptions="true">
<targets>
<target name="database" type="Database" connectionString="Data Source=Server1;initial catalog=MyDb;Integrated Security=True;">
<commandText>insert into dbo.AppException ([Level], Logger, Message, Exception, StackTrace) values (#Level, #Logger, #Message, #Exception, #StackTrace);</commandText>
<parameter name="#Level" layout="${level}" />
<parameter name="#Logger" layout="${logger}" />
<parameter name="#Message" layout="${message}" />
<parameter name="#Exception" layout="${exception}" />
<parameter name="#StackTrace" layout="${stacktrace}" />
<dbProvider>System.Data.SqlClient</dbProvider>
</target>
</targets>
<rules>
<logger name="*" minlevel="Error" writeTo="database" />
</rules>
</nlog>
My test code:
throw new IOException("This is my message");
Logging code:
logger.Error(ex);
Below is a sample row in database
In my opinion, the value in "Exception" field should be written in "Message" column and value of "StackTrace" should be written into "Exception" column and finally value of "Message" should be written in "StackTrace".
Is there anything wrong in my configuration or my expectation is wrong?
I'm guessing you are logging the exception like this:
catch (Exception ex)
{
_logger.Error(ex); // ${message} will become ex.ToString(), since no message provided.
}
If you changed to this instead:
catch (Exception ex)
{
_logger.Error(ex, "Exception caught while testing");
}
And updated NLog.config to this:
<parameter name="#Exception" layout="${exception:format=tostring,data}" />
Then you will probably get what you want.
After reading answer posted by #Rolf, I found my nlog.config setting is not correct. The format setting in nlog is important
NLog Document
I changed my nlog to below and it worked as expected
<parameter name="#Message" layout="${exception:format=message}" />
<parameter name="#Exception" layout="${exception:format=type}" />
<parameter name="#StackTrace" layout="${exception:format=stacktrace}" />
Related
What I want to do is to save all my logs to a table in a separate logging database, using NLog, and I can't seem to make it work.
This is my nlog config file:
<?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="Trace"
internalLogFile="c:\temp\internal-nlog.txt">
<extensions>
<add assembly="NLog.Web.AspNetCore"/>
</extensions>
<!-- the targets to write to -->
<targets>
<target name="dbLogger" xsi:type="Database"
connectionStringName="LoggingConnection" commandType="StoredProcedure"
commandText="[dbo].[NLog_AddEntry_p]">
<parameter name="#machineName" layout="${machinename}" />
<parameter name="#logged" layout="${date}" />
<parameter name="#level" layout="${level}" />
<parameter name="#message" layout="${message}" />
<parameter name="#logger" layout="${logger}" />
<parameter name="#properties" layout="${all-event-properties:separator=|}" />
<parameter name="#callsite" layout="${callsite}" />
<parameter name="#exception" layout="${exception:tostring}" />
</target>
</targets>
<rules>
<logger name="*" minlevel="Trace" writeTo="dbLogger"/>
</rules>
</nlog>
I managed to make it log in a .txt file with a similar config, but it does not write to the DB. The "LoggingConnection" connection string is identical to the one I use for normal CRUD operations in the website; the difference is that is uses another DB.
So, after some digging checking the internal log file I've come up with this solution.
System.Data.SqlClient was added to the project and it need to be. Also I've added Nlog.Config Nuget package to the project as well.
I've removed the "extension" tag and I've done some tweaks to the nlog tag
<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="Trace" internalLogFile="c:\temp\nlog-internal.log">
I've replaced the "connectionStringName" attribute with "connectionString" and added the connection string from the appSettings.json.
I have a dotnet core API that's up on version 2.2 with NLog.Web.AspNetCore 4.7.0 (NLog 4.5.11) which was previously working back in version 4.5.4. With the update, it now doesn't appear to be logging to the database. Nothing in terms of the logging infrastructure has changed within my code.
I should mention, I also have in the log this lovely line, so I know it's somewhat working. But I use to get A LOT more information, and of course, the database would have stuff written to it as well.
2018-12-26 15:06:59.9503||DEBUG|SomeApp.API.Program|init main |url: |action:
I tried looking around in the github issues and on here, but I haven't found anything of much use, any help is greatly appreciated!
Initial Setup in Program.cs
var logger = NLog.Web.NLogBuilder.ConfigureNLog("nlog.config").GetCurrentClassLogger();
try
{
logger.Debug("init main");
}
catch (Exception ex)
{
//NLog: catch setup errors
logger.Error(ex, "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();
}
Implementation
public static IWebHost BuildWebHost(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>()
.UseIISIntegration()
.ConfigureLogging(logging =>
{
logging.ClearProviders();
logging.SetMinimumLevel(Microsoft.Extensions.Logging.LogLevel.Trace);
})
.UseNLog() // NLog: setup NLog for Dependency injection
.Build();
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"
internalLogLevel="Trace"
internalLogFile="C:\temp\internal-nlog.txt">
<extensions>
<add assembly="NLog.Web.AspNetCore"/>
</extensions>
<targets async="true">
<target type="File" name="file" fileName="${basedir}\logs\logfile.txt"
maxArchiveFiles="5" archiveAboveSize="20971520" archiveEvery="Day"
layout="${longdate}|${event-properties:item=EventId_Id}|${uppercase:${level}}|${logger}|${message} ${exception:format=tostring}|url: ${aspnet-request-url}|action: ${aspnet-mvc-action}" />
<target xsi:type="Null" name="blackhole" />
<target name="database" xsi:type="Database">
<connectionString>${gdc:item=connectionString}</connectionString>
<commandText>
insert into dbo.Log (
EventId, Logged, Level, Message,
Username,
ServerName, Port, Url, Https,
ServerAddress, RemoteAddress,
Logger, CallSite, Exception
) values (
#EventId, #Logged, #Level, #Message,
#Username,
#ServerName, #Port, #Url, #Https,
#ServerAddress, #RemoteAddress,
#Logger, #Callsite, #Exception
);
</commandText>
<parameter name="#EventId" layout="${event-properties:item=EventId_Id}" />
<parameter name="#logged" layout="${date}" />
<parameter name="#level" layout="${level}" />
<parameter name="#message" layout="${message}" />
<parameter name="#username" layout="${aspnet-user-identity}" />
<parameter name="#serverName" layout="${aspnet-request:serverVariable=SERVER_NAME}" />
<parameter name="#port" layout="${aspnet-request:serverVariable=SERVER_PORT}" />
<parameter name="#url" layout="${aspnet-request:serverVariable=HTTP_URL}" />
<parameter name="#https" layout="${when:inner=1:when='${aspnet-request:serverVariable=HTTPS}' == 'on'}${when:inner=0:when='${aspnet-request:serverVariable=HTTPS}' != 'on'}" />
<parameter name="#serverAddress" layout="${aspnet-request:serverVariable=LOCAL_ADDR}" />
<parameter name="#remoteAddress" layout="${aspnet-request:serverVariable=REMOTE_ADDR}:${aspnet-request:serverVariable=REMOTE_PORT}" />
<parameter name="#logger" layout="${logger}" />
<parameter name="#callSite" layout="${callsite}" />
<parameter name="#exception" layout="${exception:tostring}" />
</target>
</targets>
<rules>
<!--Limit to only configured logs for all logs, change name="*"-->
<logger name="SomeApp.*" minlevel="Info" writeTo="database" />
<logger name="*" minlevel="Trace" writeTo="file" />
<!--Skip Microsoft logs and so log only own logs-->
<logger name="Microsoft.*" minlevel="Trace" writeTo="blackhole" final="true" />
</rules>
</nlog>
Sample logging code
_logger.LogInformation(1001, "Login success: {0}", userForLoginDto.Username);
appsettings.json & appsettings.Development.json Logging Settings
"Logging": {
"LogLevel": {
"Default": "Trace",
"Microsoft": "Information"
}
The NLog internal log doesn't seem to have anything of significance, I can provide it if requested though.
Apologies for any weird formatting issues, the nlog.config file did not want to come willingly.
The NLog.config file does not set the connection string.
<?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="Warn"
internalLogFile="c:\temp\internal-nlog.txt">
<!-- Load the ASP.NET Core plugin -->
<extensions>
<add assembly="NLog.Web.AspNetCore" />
</extensions>
<variable name="SirNLogDb" value="data source=SQL_MULALLEY;initial catalog=LogFiles;User ID=xxx;Password=yyy;">
</variable>
<!-- providerName="System.Data.SqlClient"-->
<!-- the targets to write to -->
<targets>
<target name="db"
xsi:type="Database"
dbProvider="System.Data.SqlClient"
connectionString="${var:SirNLogDb}"
commandType="StoredProcedure"
commandText="[dbo].[NLog_AddEntry_p]">
<parameter name="#machineName" layout="${machinename}" />
<parameter name="#siteName" layout="${iis-site-name}" />
<parameter name="#logged" layout="${date}" />
<parameter name="#level" layout="${level}" />
<parameter name="#username" layout="${aspnet-user-identity}" />
<parameter name="#message" layout="${message}" />
<parameter name="#logger" layout="${logger}" />
<parameter name="#properties" layout="${all-event-properties:separator=|}" />
<parameter name="#serverName" layout="${aspnet-request:serverVariable=SERVER_NAME}" />
<parameter name="#port" layout="${aspnet-request:serverVariable=SERVER_PORT}" />
<parameter name="#url" layout="${aspnet-request:serverVariable=HTTP_URL}" />
<parameter name="#https" layout="${when:inner=1:when='${aspnet-request:serverVariable=HTTPS}' == 'on'}${when:inner=0:when='${aspnet-request:serverVariable=HTTPS}' != 'on'}" />
<parameter name="#serverAddress" layout="${aspnet-request:serverVariable=LOCAL_ADDR}" />
<parameter name="#remoteAddress" layout="${aspnet-request:serverVariable=REMOTE_ADDR}:${aspnet-request:serverVariable=REMOTE_PORT}" />
<parameter name="#callSite" layout="${callsite}" />
<parameter name="#exception" layout="${exception:tostring}" />
</target>
</targets>
<!-- rules to map from logger name to target -->
<rules>
<!--All logs, including from Microsoft-->
<logger name="*" minlevel="Trace" writeTo="database" />
</rules>
</nlog>
I put a breakpoint and the connection string is null;
My Startup method is as follows;
public void ConfigureServices(IServiceCollection services)
{
services.AddMvcCore()
.AddMvcOptions(o => o.OutputFormatters.Add(
new JsonOutputFormatter(new JsonSerializerSettings(), ArrayPool<char>.Shared)));
var connectionStringMSurveyV2 = Configuration.GetConnectionString("MSurveyV2Db");
services.AddScoped<MSurveyV2Db>(_ => new MSurveyV2Db(connectionStringMSurveyV2));
var connectionStringSir = Configuration.GetConnectionString("SirDb");
services.AddScoped<SirDb>(_ => new SirDb(connectionStringSir));
services.AddScoped<IPropertiesRepo, PropertiesRepo>();
services.AddScoped<ISirUoW, SirUoW>();
services.AddScoped<Services.IMailService, Services.MailService>();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
loggerFactory.AddConsole();
loggerFactory.AddDebug();
loggerFactory.AddNLog();
//add NLog.Web
app.AddNLogWeb();
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler();
}
app.UseMvc();
AutoMapper.Mapper.Initialize(cfg =>
{
cfg.CreateMap<Exception, OperationStatus>();
cfg.CreateMap<ViewSelectedContracts, ContractDto>();
});
var logger = LogManager.GetCurrentClassLogger();
logger.Info("Logged in");
}
}
EDIT - I change the logger rule to <logger name="*" minlevel="Trace" writeTo="db" /> but still it didn't output anything. However I looked for the c:\temp\internal-nlog.txt and it had not been created. So it appears the nlog.config file is being ignored. But it is in my project next to the Startup.cs file.
EDIT2: - the null configuration can be solved by setting "Copy to output directory" to "copy always".
From the comments underneath I have now got this working.
Updated answer
Since NLog.Web.AspNetCore 4.8 (NLog.Extensions.Logging 1.4 for .NET Core console programs) you could directly read from your appSettings.json
${configsetting:name=MyConnectionString}
see docs
Original answer
Unfortunately reading connectionstrings/settings from appSettings.json / app.config is not yet supported in NLog for .NET core.
Two options:
Set the connectionstring programmatically, by using variables.
In your nlog.config:
<target ... connectionString="${var:myConnectionstring}" ... />
and in code: (e.g. in Configure)
LogManager.Configuration.Variables["myConnectionstring"] = "...."; //read config here
Or, set the connectionstring in nlog.config.
In your nlog.config:
<variable name="myConnectionstring" value="...." />
and using in your target in nlog.config:
<target ... connectionString="${var:myConnectionstring}" ... />
Another option is to create and register a custom NLog layout-renderer (startup.cs):
https://github.com/NLog/NLog/wiki/How-to-write-a-custom-layout-renderer
Which outputs the ConnectionString after having read it from your favorite configuration-location. Then you don't have the connectionstring in your nlog.config, but just refer to your custom layout-renderer.
Maybe cheer for this pending issue:
https://github.com/NLog/NLog.Web/issues/107
I'm trying to use NLog to log to an Oracle database, already created the table but when I try to log something I get the exception:
ORA-00928: missing SELECT keyword
My NLog.config file is:
<?xml version="1.0" ?>
<nlog autoReload="true" throwExceptions="true" internalLogFile="${basedir}/App_Data/nlog.txt" internalLogLevel="Debug"
internalLogToConsole="true">
<targets>
<!--Useful for debugging-->
<target name="filelog" type="File" fileName="C:/App_Data/Site.log"
layout="${date}: ${message}" />
<target name="databaselog" type="Database">
<dbProvider>Oracle.DataAccess.Client</dbProvider>
<!-- database connection parameters -->
<!-- alternatively you could provide a single 'connectionstring' parameter -->
<connectionString>DATA SOURCE=database;PERSIST SECURITY INFO=True;USER ID=user;Password=password;Validate Connection=true</connectionString>
<commandText>
insert into RS_LOGTABLE ([log_user],[log_level],[log_date],[log_message]) values(#log_user,#log_level,#log_date,#log_message);
</commandText>
<parameter name="#log_user" layout="${message}"/>
<parameter name="#log_level" layout="${message}"/>
<parameter name="#log_date" layout="${date}"/>
<parameter name="#log_message" layout="${message}"/>
</target>
</targets>
<rules>
<logger name="*" minlevel="Trace" writeTo="filelog" />
<logger name="*" minlevel="Trace" writeTo="databaselog" />
</rules>
</nlog>
And the exception occurs when I do this:
logger = LogManager.GetCurrentClassLogger();
logger.Debug("Teste logger");
I already tried to do the insert without the brackets and get another exception:
**ORA-00936: missing expression**
I guess it's bit late for you, but I hope my solution will be helpful for others.
I changed characters # for colons at commandText and at parameter tags I removed it completely. I don't use brackets and it started to work.
It should be correct this way:
<commandText>
insert into RS_LOGTABLE (log_user,log_level,log_date,log_message) values(:log_user,:log_level,:log_date,:log_message);
</commandText>
<parameter name="log_user" layout="${message}"/>
<parameter name="log_level" layout="${message}"/>
<parameter name="log_date" layout="${date}"/>
<parameter name="log_message" layout="${message}"/>
I tested it with dbProvider Oracle.DataAccess.Client.
Warning for others:
I had a variable named with reserved word and it throws another exception. More details are here PHP ORA-01745: invalid host/bind variable name Warning
I am learning NLog and have moved on to Database logging. I have tried multiple examples on the web and can't get any of them to actually insert a record. I can't see why the log attempt failed due to NLog gobbling up any exceptions it gets. My logger and the config is found without issue. It executes the line as if it worked but the table remains empty. I did step for step on Nlog's example but it doesn't work either! :(
Nlog Database Example
UPDATE: I ended up deleting and starting over on the config and it now works. Not sure what fixed the issue from the new config file but i also realized the asp renders were causing errors so i removed them.
<target name="db"
xsi:type="Database"
dbProvider="System.Data.SqlClient"
connectionString="Data Source=Server;Initial Catalog=Database;Persist Security Info=True;Integrated Security=SSPI;"
commandType="StoredProcedure"
commandText="[dbo].[NLog_AddEntry_p]">
<parameter name="#machineName" layout="${machinename}" />
<parameter name="#siteName" layout="${iis-site-name}" />
<parameter name="#logged" layout="${date}" />
<parameter name="#level" layout="${level}" />
<parameter name="#username" layout="${aspnet-user-identity}" />
<parameter name="#message" layout="${message}" />
<parameter name="#logger" layout="${logger}" />
<parameter name="#properties" layout="${all-event-properties:separator=|}" />
<parameter name="#serverName" layout="${aspnet-request:serverVariable=SERVER_NAME}" />
<parameter name="#port" layout="${aspnet-request:serverVariable=SERVER_PORT}" />
<parameter name="#url" layout="${aspnet-request:serverVariable=HTTP_URL}" />
<parameter name="#https" layout="${when:inner=1:when='${aspnet-request:serverVariable=HTTPS}' == 'on'}${when:inner=0:when='${aspnet-request:serverVariable=HTTPS}' != 'on'}" />
<parameter name="#serverAddress" layout="${aspnet-request:serverVariable=LOCAL_ADDR}" />
<parameter name="#remoteAddress" layout="${aspnet-request:serverVariable=REMOTE_ADDR}:${aspnet-request:serverVariable=REMOTE_PORT}" />
<parameter name="#callSite" layout="${callsite}" />
<parameter name="#exception" layout="${exception:tostring}" />
</target>
try
{
int zero = 0;
int result = 100 / zero;
}
catch (Exception ex)
{
Logger filelogger = LogManager.GetLogger("filelogger");
Logger dblogger = LogManager.GetLogger("dblogger");
//filelogger.Error(ex, "Whoops");
dblogger.Error(ex, "Whoops");
}
The issue was with asp layout renders and missing a package for them. Also i had set the internalLogging flag to an invalid value. These corrections fixed the problem.