Проблема записи журнала в базе данных с Nlog с несколькими потоками - PullRequest
0 голосов
/ 18 апреля 2019

Я заметил странную проблему с Nlog при записи журналов в базу данных через несколько потоков.

Проблема в том, что журнал для одной базы данных иногда добавляется в другую базу данных, т. Е. Иногда смешивается. Является ли метод обновления DbTarget из Nlog не безопасным для потоков или я что-то здесь не так делаю? Пожалуйста, поделитесь своими идеями / опытом, чтобы справиться с этим.

Я использую одно и то же приложение для трех разных клиентов, у каждого из которых три разные базы данных на одном сервере. У меня есть приложение C # Windows, где есть 3 потока, которые созданы для 3 разных баз данных. Каждый поток имеет дело со своей собственной базой данных так, как показано здесь:

public void StartService()
{
    string conn1 = connectionstring for database1;
    string conn2 = connectionstring for database2;

     Thread ThreadDB1 = new Thread(new ThreadStart(() => Process(conn1)));
    Thread ThreadDB2 = new Thread(new ThreadStart(() => Process(conn2)));

    ThreadDB1.Start();
    ThreadDB2.Start();
}


public void Process(string domainCon)
{
    this.isActive = true;

    while (this.isActive)
    {
        try
        {
            StartOperation(domainCon);
        }
        catch (Exception ex)
        {
            //since the Nlog has not been initialised for an individual threads that switch per database, 
            //If any error occurs here, log that in event log instead
            EventLog.WriteEntry("Application", ex.ToString(), EventLogEntryType.Error);
        }
    }
}


public static void StartOperation(string domainCon)
{
    NLogLoggerService loggerService;
    NLogLogger Logger;

    /* Set the "NEW" instance of logger here so that each instance
     * will be per thread (i.e per database) */

    Logger = new NLogLogger("MyAppName_Processor");
    loggerService = new NLogLoggerService(Logger);
    //create the DbTarget that is passed by current thread
    Logger.UpdateNLogDatabase(domainCon);

    // the process to select the records from Db happens here
    ProcessDbRecords(domainCon);

}


ProcessDbRecords(domainCon)
{
    //Records are selected, analyzed processed and saved again in database for each thread

    //the following log is written in database but log of one database is written in another database sometimes

    loggerService.LoggerInfo("The Database has been called for connString: " + domainCon);
}


//in LogService class, i have a method that updates the database target with connectionstring provided in runtime:

public static void UpdateNLogDatabase(string connString)
{
    var config = LogManager.Configuration;
    var dbTarget = (Targets.DatabaseTarget)config.FindTargetByName("Database");
    dbTarget.ConnectionString = connString;
    LogManager.ReconfigExistingLoggers();
}

1 Ответ

0 голосов
/ 20 апреля 2019

Идея (с большинством каркасов ведения журналов) заключается не в постоянном изменении конфигурации ведения журналов, а в вводе требуемой контекстной информации:

NLog имеет несколько опций для предоставления контекстной информации:

https://github.com/NLog/NLog/wiki/LogEvent-Context-Information

Вы можете сделать это следующим образом с MappedDiagnosticsLogicalContext:

public void Process(string domainCon)
{
    this.isActive = true;

    NLog.MappedDiagnosticsLogicalContext.Set("ThreadConnectionString", domainCon);

    while (this.isActive)
    {
        try
        {
            StartOperation(domainCon);
        }
        catch (Exception ex)
        {
            //since the Nlog has not been initialised for an individual threads that switch per database, 
            //If any error occurs here, log that in event log instead
            EventLog.WriteEntry("Application", ex.ToString(), EventLogEntryType.Error);
        }
    }
}

И затем иметь это в NLog.config:

<target type="database" connectionString="${mdlc:item=ThreadConnectionString}">
</target>

См.также: https://github.com/NLog/NLog/wiki/MDLC-Layout-Renderer

...