Структурные логи в json формате.
Библиотека является набором пресетов для Serilog, которые позволяют добавлять поддержку логирования в несколько строк.
using Byndyusoft.Logging.Configuration;
using Serilog;
...
Host.CreateDefaultBuilder(args)
.UseSerilog((context, configuration) => configuration
.UseDefaultSettings(context.Configuration, "Sample project")
)
В результате такого подключения будут добавлено логирование в стандартный вывод логов в формате json. Каждый вывод в лог производится с новой строки и занимает 1 строку.
Например, для тестового вывода будет, будет получен следущей лог:
var values = new[] {"value1", "value2"};
logger.LogInformation("запрошены {@Values}", (object)values);
Вывод (отформатированный):
{
"Timestamp": "2021-04-09T13:12:58.2633978Z",
"Message": "запрошены [\"value1\", \"value2\"]",
"MessageTemplateHash": "05114bc8",
"Level": "Information",
"Properties": {
"Values": [
"value1",
"value2"
]
}
}
Где:
Timestamp
— время в utc в которое создана запись логаMessage
— залогированное сообщение с подставленными параметрамиMessageTemplateHash
— хэш шаблона сообщения, который не зависит от параметровLevel
— уровень логаProperties.*
— все остальные свойства сообщенияValues
— параметры сообщения
Кроме того Properties.*
обогащается полями добавленными в контекст лога. Т.е. если вызывать logger.LogInformation("запрошены {@Values}", (object)values)
из метода контроллера, то в Properties
добавятся следующие поля, которые добавляет инфраструктура asp.net
"SourceContext": "Byndyusoft.Logging.Sample.Controllers.ValuesController",
"ActionId": "21eb782a-3717-4616-9927-7d2a5a23c8b8",
"ActionName": "Byndyusoft.Logging.Sample.Controllers.ValuesController.Get (Byndyusoft.Logging.Sample)",
"RequestId": "0HM7RB4OJU2MI:00000001",
"RequestPath": "/api/values",
"SpanId": "|b01373be-4fffa4bff3ac61ea.",
"TraceId": "b01373be-4fffa4bff3ac61ea",
"ParentId": "",
"ConnectionId": "0HM7RB4OJU2MI",
В случе логирования исключения, добавляет информация об исключении (ToString()) и хэш стектрейса для поиска аналогичных исключений. Хэш считается без учёта номеров строк.
logger.LogError(ex, "Должен совпасть хэш ошибки")
{
"Timestamp":"2021-04-09T10:10:16.6873034Z",
"Message":"Должен совпасть хэш ошибки",
"MessageTemplateHash":"3e4763a9",
"Level":"Error",
"Exception":"System.Exception: Что-то пошло не так\r\n ---> System.NotImplementedException: Скоро сделаем\r\n at Byndyusoft.Logging.Sample.Controllers.ValuesController.ThrowError() in C:\\work\\reps\\Byndyusoft.Logging\\src\\Byndyusoft.Logging.Sample\\Controllers\\ValuesController.cs:line 58\r\n at Byndyusoft.Logging.Sample.Controllers.ValuesController.ThrowErrorWithInnerError() in C:\\work\\reps\\Byndyusoft.Logging\\src\\Byndyusoft.Logging.Sample\\Controllers\\ValuesController.cs:line 65\r\n --- End of inner exception stack trace ---\r\n at Byndyusoft.Logging.Sample.Controllers.ValuesController.ThrowErrorWithInnerError() in C:\\work\\reps\\Byndyusoft.Logging\\src\\Byndyusoft.Logging.Sample\\Controllers\\ValuesController.cs:line 69\r\n at Byndyusoft.Logging.Sample.Controllers.ValuesController.GetError() in C:\\work\\reps\\Byndyusoft.Logging\\src\\Byndyusoft.Logging.Sample\\Controllers\\ValuesController.cs:line 44",
"ExceptionHash":"533f548e",
...
}
Сначала нужно подключить Byndyusoft.Logging.OpenTracing
Можно заменить значения TraceId
и SpanId
на полученные от OpenTracing. ParentId
совсем удаляет.
.UseSerilog((context, configuration) => configuration
.UseOpenTracingTraces()
Можно сделать так, чтобы всё что пишется в логи, оказалось в трасах.
.UseSerilog((context, configuration) => configuration
.WriteToOpenTracing()
Сначала нужно подключить Byndyusoft.Logging.OpenTelemetry
Можно заменить значения TraceId
и SpanId
на полученные от OpenTelemetry. ParentId
совсем удаляет.
.UseSerilog((context, configuration) => configuration
.UseOpenTelemetryTraces()
Можно сделать так, чтобы всё что пишется в логи, оказалось в трасах.
.UseSerilog((context, configuration) => configuration
.WriteToOpenTelemetry()
.UseSerilog((context, configuration) => configuration
.UseFileWriterSettings()
В рабочей паке будут добавлены 2 файла logs/verbose.log
и error.log
Это просто Serilog. Поэтому в ваших настройках должен быть параметр Serilog:MinimumLevel:Default
с желаемым уровнем логирования. В переменных окружения :
нужно заменить на __
: Serilog__MinimumLevel__Default
.
Помните, что это всё ещё обычный Serilog. Вы можете изменить настройки так, как вам нравится. Скажем UseDefaultSettings
представляет из себя следующей вызов:
return loggerConfiguration
.Enrich.FromLogContext()
.UseConsoleWriterSettings(restrictedToMinimumLevel)
.OverrideDefaultLoggers()
.ReadFrom.Configuration(configuration)
Можно использовать только те его части, которые подходят вашему проекту.
Хочу положить в события трассировок только заданные мной параметры, а не весь лог (структурные события)
Если использовать стандартный способ переноса логов в события трассы, то в них будут попадать само сообщение лога, его свойства и остальные дополнительные параметры, такие как уровень логирования и контекст логирования. В трассах выглядит следующим образом:
Если требуется, чтобы в событиях трассы попадали только параметры без дублирования их в сообщении, то можно использовать структурные события. Они позволяют записать события в виде название события и его параметров. Пример:
Чтобы заработали структурные события, нужно подключить StructuredActivityEventBuilder
:
.UseSerilog((context, configuration) => configuration
.WriteToOpenTelemetry(activityEventBuilder: StructuredActivityEventBuilder.Instance)
После этого можно создавать структурные события с помощью логирования по конвенции или вспомогательного метода.
Для этого в логах должно быть свойство TraceEventName, которое будет использоваться как название события в трассе:
_logger.LogInformation("{TraceEventName} Parameters: Company.Name = {Company_Name}; Id = {Id}", "MethodInput", "Byndyusoft", 10);
В логи сообщение попадёт как обычно, а событие трассы будет содержать только два параметра: Id и Company.Name. Имя события будет MethodInput. События будут выглядеть, как на картинке выше.
Чтобы явно указать на использование структурных событий, можно воспользоваться вспомогательным методом LogStructuredActivityEvent
, который находится в пакете Byndyusoft.Logging.OpenTelemetry.Abstractions
.
Чтобы создать событие трассировки, аналогичное картинке выше, нужно написать следующее:
var eventItems = new[]
{
new StructuredActivityEventItem("Id", 10),
new StructuredActivityEventItem("Company.Name", "Byndyusoft")
};
_logger.LogStructuredActivityEvent("MethodInput", eventItems);
Пример выше полностью аналогичен коду логирования по конвенции:
_logger.LogInformation("Structured Event {TraceEventName} Properties: Id = {Id}; Company.Name = {Company_Name}; ", "MethodInput", 10, "Byndyusoft");
По умолчанию структурные события попадают в логи с уровнем Information.
Для обогащения информацией о параметрах окружения с наименованием BUILD_*, именем сервиса и версией сервиса можно воспользоваться библиотекой Byndyusoft.Telemetry.Logging.Serilog. Документация - тут.