Как мне записать настраиваемое поле в NLog в базу данных?

В настоящее время я использую NLog во многих проектах. В некоторых случаях я регистрируюсь в базе данных.

Вот что я хотел бы сделать:

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!
)

И NLog.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}"/>
  <parameter name="@Origin" layout="${callsite}"/>
  <parameter name="@LogLevel" layout="${level}"/>
  <parameter name="@message" layout="${message}"/>
  <parameter name="@OrderId" layout="${orderId}"/> <!-- custom field! -->
</target>

А затем запишите что-то вроде этого:

var logger = LogManager.GetCurrentClassLogger();
var orderId = 123;
logger.Debug("What is going on here", orderId);

Есть ли хороший способ сделать это и продолжать использовать NLog? Или мне нужно свернуть собственный регистратор и пропустить NLog, когда это необходимо?


person Kjensen    schedule 14.09.2012    source источник
comment
возможно, это может помочь: nlog-forum.1685105.n2.nabble.com/   -  person jbl    schedule 14.09.2012
comment
Что не так с string.Format ()?   -  person Ritch Melton    schedule 23.09.2012
comment
Богатый ›› Вы имеете в виду, что кроме запросов к лог-файлу позже будет сложно (потребуется разделение и т. Д.)? Не зря у нас есть реляционные базы данных с отдельными данными в отдельных полях.   -  person Kjensen    schedule 24.09.2012


Ответы (5)


Вместо использования GDC, который предназначен для глобальных статических данных и не работает при одновременном ведении журнала, лучше использовать EventProperties-Layout-Renderer, который позволяет передавать настраиваемые свойства, специфичные для события.

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
person Michael Freidgeim    schedule 20.07.2013
comment
Не могли бы вы добавить источник, утверждающий, что GDC и MDC теперь устарели? Сначала я попробовал ваше решение, но перешел к решению с отмеченным ответом, поскольку оно намного чище - мне не нужно определять новую переменную и вручную устанавливать уровень журнала и имя регистратора. Единственное, что я смог найти об их «устаревании», это то, что их имена были изменены с GDC / MDC на то, что используется в отмеченном ответе (GlobalDiagnosticsContext) - nlog-project.org/2009/10/19/ - person Brett; 17.10.2013
comment
Согласно github.com/nlog/nlog/wiki/Layout-Renderers MDC и NDC для совместимости с log4net. - person Michael Freidgeim; 18.10.2013
comment
С настраиваемым экземпляром LogEventInfo вам также необходимо вручную установить Timestamp / LoggerName, что не так удобно, как использование GDC. - person Dresel; 30.01.2015
comment
${event-context} устарел, вместо него следует использовать ${event-properties}. - person Sergey Kolodiy; 01.10.2015
comment
Это звучит более надежно, чем принятый ответ - person Mihir; 17.05.2016
comment
усложняет задачу. wageoghe ответ лучше. - person Doruk; 13.08.2018
comment
@Doruk, «Используйте контекст глобальной диагностики, если вы хотите сделать определенную информацию доступной для каждого регистратора в текущем процессе». из github.com/nlog/nlog/wiki/Gdc-Layout-Renderer - person Michael Freidgeim; 13.08.2018

Вот один из подходов с использованием GlobalContext.

Конфигурация:

<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>

Звоните на сайт:

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

Приложив немного больше усилий, вы можете обернуть регистратор NLog одним из способов, проиллюстрированных здесь.

Или вы можете создать свой собственный объект «контекста» и написать собственный LayoutRenderer, чтобы извлекать из него значения и записывать их в журнал. Пользовательские LayourRenderers легко написать. Вы можете увидеть один пример в моем первом ответе на этот вопрос. Здесь я покажу, как написать собственный LayoutRenderer, который добавляет текущее значение System.Diagnostics.Trace.CorrelationManager.ActivityId в сообщение журнала.

person wageoghe    schedule 14.09.2012
comment
GlobalDiagnosticsContext имеет несколько статических методов, но это не словарь, что-то меняется или? - person Nuri YILMAZ; 07.12.2012
comment
Вы правы, это GlobalContetxt, а не GlobalDiagnosticContext, и это не словарь, но он содержит словарь, который он предоставляет через свойство Properties. - person wageoghe; 07.12.2012
comment
Игнорируйте последний комментарий. Я обновил пример кода, чтобы он был правильным (или, по крайней мере, более правильным). - person wageoghe; 07.12.2012
comment
GlobalDiagnosticsContext.Set () принимает в качестве параметров 2 строковые переменные, поэтому GlobalDiagnosticsContext.Set (OrderId, 123); должно сработать - person Unix von Bash; 10.10.2014
comment
Здравствуйте, я уже написал этот код в Контроллере - MappedDiagnosticsContext.Set (UserName, LoggedUser.Name); и добавьте это в конфигурацию - ‹имя параметра = @ UserName layout = $ {gdc: UserName} /›, но таким образом нельзя записать имя пользователя в Db, есть ли у вас какие-то рекомендации? - person Avtandil Kavrelishvili; 17.11.2015
comment
Если вы помещаете свое значение в MappedDiagnosticsContext, вы должны использовать средство визуализации макета mdc. Вы используете gdc. - person wageoghe; 17.11.2015
comment
Разве это не вызывает проблем с одновременной записью? - Я имею в виду, что один поток устанавливает значение, а другой поток перезаписывает значение, которое затем будет записано первым потоком - person Mihir; 17.05.2016
comment
См. Ответ Майкла Фрейдгейма ниже, это решение не работает при одновременной записи. - person Fran Hoey; 29.09.2016
comment
Отлично ... Это большая помощь для меня. установка только в конструкторе и работа для всех методов - person Mansinh; 19.03.2018

NLog 4.5 представляет структурированное ведение журнала, поэтому вы можете это:

logger.Debug("What is going on here. OrderId={MyOrderId}", orderId);

С помощью NLog 4.6.3 можно создать временный регистратор, наделенный желаемым свойством с помощью WithProperty:

Вызов

int orderId = 123; 
logger.WithProperty("MyOrderId", orderId).Info("This is my message!"); 

Конфиг:

<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>

Обратите внимание, что NLog 4.6 также поддерживает DbType - см. https://nlog-project.org/2019/03/20/nlog-4-6-is-live.html

person Julian    schedule 22.06.2019
comment
Думаю, это лучшее решение для новых версий NLog. +1. - person Alexei - check Codidact; 26.09.2019
comment
Идеально! Отличный ответ! - person NiallMitch14; 02.10.2019
comment
Работает отлично! +1 - person Chandan Y S; 11.01.2020

Если это все, что вам нужно, начиная с версии 4.3.3 NLog существует более простой способ объявления пользовательских переменных и доступа к ним. Осторожно: ни одно из этих решений не является потокобезопасным.

Добавьте следующее в NLog.config

<nlog ...
    <!-- optional, add some variables -->  
    ...
    <variable name="myvarone" value="myvalue"/>
    <variable name="myvartwo" value=2/>
     ...
</nlog>

Переменные могут быть изменены / доступны в коде:

LogManager.Configuration.Variables["myvarone"] = "New Value"
LogManager.Configuration.Variables["myvartwo"] = 2

На значения можно ссылаться в NLog.config:

"${var:myvarone}"  -- renders "New Value"
"${var:myvartwo}"  -- renders 2

Как я упоминал выше, объекты var и LogEventInfo являются глобальными. Таким образом, если определено несколько экземпляров, изменение значения приведет к изменению значения для всех экземпляров. Мне очень интересно, знает ли кто-нибудь способ объявить индивидуальные переменные для каждого экземпляра для NLog.

person Balash    schedule 05.05.2016
comment
Потрясающая простота решения - быстро выполняет работу. - person Steve; 03.06.2016
comment
Ответ Майкла выше является потокобезопасным. он не использует Global LogEventInfo, он создает новый экземпляр. - person Fran Hoey; 29.09.2016

Вы можете использовать код MDC:

var logger = LogManager.GetCurrentClassLogger();

MDC.Set("OrderId", 123);
MDC.Set("user", HttpContext.Current.User.Identity.Name);
// ... and so on

также проверьте это http://weblogs.asp.net/drnetjes/archive/2005/02/16/374780.aspx

person Eldar    schedule 23.09.2012