'Console application not logging error with NLOG in AppDomain.CurrentDomain.UnhandledException

The below code isn't logging to my database. Is it because the console application closes too soon and if so how can I prevent this?

   private static ILogger _logger;

      static void UnhandledExceptionTrapper(object sender, UnhandledExceptionEventArgs e)
        {
            _logger.Error((Exception)e.ExceptionObject);
        }
    
        private static void Main(string[] args)
        {
            var container = new Container();
            _logger = container.GetInstance<ILogger>();
    AppDomain.CurrentDomain.UnhandledException += UnhandledExceptionTrapper;
            throw new Exception("test");


Solution 1:[1]

You can call LogManager.Shutdown() before leaving your UnhandledExceptionTrapper method. This calls internally LogManager.Flush() which

Flush any pending log messages (in case of asynchronous targets) with the default timeout of 15 seconds.

see NSLog Flush documentation.

Additional Troubleshooting

In the debug console, error messages are displayed if, for example, SQL errors occur on the connection, but by default you do not see if you have an invalid configuration.

Since NLog offers extensive configuration options, it is easy to make a small configuration errors that results in no logging. A typical example would be an incorrect ConnectionString or DBProvider.

As an example, suppose you have configured an incorrect DBProvider.

You can e.g call

LogManager.GetCurrentClassLogger().Debug("test logging");
LogManager.Flush();

and it would just silently fail.

However, if you insert

LogManager.ThrowExceptions = true;

before the test log call, you will see appropriate messages in the debug console. For example, if you have an invalid DBProvider like Npgsql.NpgsqlConnection1, Npgsql (note the invalid 1 in it), you would see

Unhandled exception. NLog.NLogRuntimeException: Exception occurred in NLog
 ---> NLog.NLogRuntimeException: Target Database Target[nlog-db] failed to initialize.
 ---> System.TypeLoadException: Could not load type 'Npgsql.NpgsqlConnection1' from assembly 'Npgsql'.

which would give a clear indication of what went wrong.

More Diagnostics

To see even more diagnostics, you can add the following lines:

InternalLogger.LogToConsole = true;
InternalLogger.LogLevel = LogLevel.Trace;

Among other things, you can see when the connection is opened, what data is written and information about flushing, a small sample excerpt:

2022-03-11 08:47:56.8428 Trace DatabaseTarget(Name=nlog-db): Open connection.
2022-03-11 08:47:57.0275 Trace DatabaseTarget(Name=nlog-db): Executing Text: insert into "public"."log_table"(time_stamp,level,logger,message,stacktrace) values(CAST(@time_stamp AS timestamp),@level,@logger,@message,@stacktrace);
2022-03-11 08:47:57.0380 Trace   DatabaseTarget: Parameter: '@time_stamp' = '2022-03-11T08:47:56.837' (String)
2022-03-11 08:47:57.0380 Trace   DatabaseTarget: Parameter: '@level' = 'Debug' (String)
2022-03-11 08:47:57.0380 Trace   DatabaseTarget: Parameter: '@logger' = 'ConsoleDBLog.Program' (String)
2022-03-11 08:47:57.0395 Trace   DatabaseTarget: Parameter: '@message' = 'test logging' (String)
2022-03-11 08:47:57.0415 Trace   DatabaseTarget: Parameter: '@stacktrace' = '' (String)
2022-03-11 08:47:57.1099 Trace DatabaseTarget(Name=nlog-db): Finished execution, result = 1
2022-03-11 08:47:57.1111 Trace DatabaseTarget(Name=nlog-db): Close connection (KeepConnection = false).
2022-03-11 08:47:57.1167 Debug LogFactory Flush with timeout=15 secs

Self-Contained Example

using System;
using NLog;
using NLog.Targets;

namespace ConsoleDBLog;

internal static class Program
{
    private static ILogger? _logger;

    static void UnhandledExceptionTrapper(object sender, UnhandledExceptionEventArgs e)
    {
        _logger?.Error((Exception?) e.ExceptionObject, "unhandled exception");
        LogManager.Shutdown();
    }

    private static void Main()
    {
        SetupDB_NLOG();
        _logger = LogManager.GetCurrentClassLogger();
        AppDomain.CurrentDomain.UnhandledException += UnhandledExceptionTrapper;
        throw new Exception("test");
    }

    static void SetupDB_NLOG()
    {
        DatabaseTarget target = new DatabaseTarget();
        target.Name = "nlog-db";

        target.DBProvider = "Npgsql.NpgsqlConnection, Npgsql";
        target.ConnectionString = "Server=127.0.0.1;Port=5432;User Id=stephan;Password=;Database=stephan;";
        target.CommandText =
            "insert into \"public\".\"log_table\"(time_stamp,level,logger,message,stacktrace) values(CAST(@time_stamp AS timestamp),@level,@logger,@message,@stacktrace);";

        var param = new DatabaseParameterInfo
        {
            Name = "@time_stamp",
            Layout = "${date:format=yyyy-MM-ddTHH\\:mm\\:ss.fff}"
        };
        target.Parameters.Add(param);

        param = new DatabaseParameterInfo
        {
            Name = "@level",
            Layout = "${level}"
        };
        target.Parameters.Add(param);

        param = new DatabaseParameterInfo
        {
            Name = "@logger",
            Layout = "${logger}"
        };
        target.Parameters.Add(param);

        param = new DatabaseParameterInfo
        {
            Name = "@message",
            Layout = "${message}"
        };
        target.Parameters.Add(param);

        param = new DatabaseParameterInfo
        {
            Name = "@stacktrace",
            Layout = "${exception:format=stacktrace}"
        };
        target.Parameters.Add(param);

        NLog.Config.SimpleConfigurator.ConfigureForTargetLogging(target, LogLevel.Debug);

        //Uncomment the following lines to see things that would silently fail and
        //to get more diagnostic debug output about what is actually running.
        
        // InternalLogger.LogToConsole = true;
        // InternalLogger.LogLevel = LogLevel.Trace;
        // LogManager.ThrowExceptions = true;

        // LogManager.GetCurrentClassLogger().Debug("test logging");
        // LogManager.Flush();
    }
}

Which gives the followoing entries in the DB:

nlog db entries example

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Stephan Schlecht