Комментарии 62
Глобальный логгер — это объект, через который осуществляется логирование в Serilog
В том-то и дело, что нет. Логирование в Serilog осуществляется через ILogger
, который не глобальный. Их можно иметь несколько, и все будет прекрасно. Log.Logger
— это всего лишь точка доступа к одному из них. И про нее можно сказать банальное "просто не делайте так". Начиная с того, что прикладному разработчику вообще не обязательно давать доступ к серилогу, и заканчивая тем, что сделайте статический анализатор, который бросает ошибку при обращении к Log.Logger
.
У меня тут под боком гигантский проект, внутри которого смесь Framework и Standard, три разных слоя логирования, включая Serilog, и там Log.Logger
используется только косвенно, легаси-адаптерами в виде LogLib.
(При этом, на самом деле, bootstrap logger — это не такое давнее добавление к Serilog, и оно нужно далеко не всегда, если вы понимаете, что и зачем вы делаете.)
Отдельно немножко про тесты:
public async Task<HttpResponseMessage> Send() { var client = _webAppFactory.WithWebHostBuilder(b => b.UseSerilog( (context, config) => config .WriteTo.Logger(new TestLogger(_logPrefix, _output)) )).CreateClient(); return await client.SendAsync(new HttpRequestMessage(HttpMethod.Get, "/ping")); } }
В методе Send инициируется приложение с добавлением Serilog и логированием в тестовый логгер.
Неправильно инициализируется, оттуда и проблемы. Зачем же вы в тестах используете оверлоад, который работает с Log.Logger
, когда есть оверлоад, который примет ваш логгер снаружи, тем самым сняв все проблемы со множественными логгерами (и жизненным циклом)?
Собственно, вот это:
Разработчики Serilog предлагают смешанный подход к использованию Serilog в .NET Core приложениях: [...] с использованием глобального логгера через свойство Logger публичного статического класса Serilog.Log.
вы откуда взяли? (кроме примера для Program.cs
)
У Serilog еще во времена .NET Framework была мощная интеграция с DI (например, с Autofac), и использовать Log.Logger
не было необходимости даже тогда.
А с Microsoft.Extensions.Logging
работали, или структурированное логирование вообще в новинку?
вы откуда взяли? (кроме примера для Program.cs)
В readme на странице библиотеки для интеграции Serilog в .NET Core serilog-aspnetcore такой код присутствует почти в самом начале в разделе Instructions. И когда программист знакомится с Serilog и заходит на эту страницу, то он расценивает это как рекомендацию. Разработчики сразу со страницы readme библиотеки практически заявляют: вот инструкция — делай так:
- используй Log.Logger
- интегрируй Serilog в подсистему логирования .NET Core.
И программисты так и будут делать. Они будут копипастить этот код. И будут привыкать так делать из проекта в проект. Если будет опытный наставник вроде Вас — хорошо. Но далеко не всем так везёт.
В readme на странице библиотеки для интеграции Serilog в .NET Core serilog-aspnetcore такой код присутствует почти в самом начале в разделе Instructions.
Это и есть пример для Program.cs
. Пример обращения к Log.Logger
из другого места программы есть?
Вы, кстати, понимаете, почему вброшенный через DI ILogger
не равен Log.Logger
?
Пример обращения к Log.Logger из другого места программы есть?
Мне кажется, что есть пример обращения к Log.Logger в принципе — и этого уже достаточно.
Вы, кстати, понимаете, почему вброшенный через DIILogger
не равенLog.Logger
?
Да, Я упустил тот момент, что это не тот же самый объект. Но вброшенный объект — это обёртка над объектом из
Log.Logger
, которая всё равно передаёт соощение тому же глобальному логгеру, как Я понял.Но вброшенный объект — это обёртка над объектом из Log.Logger, которая всё равно передаёт соощение тому же глобальному логгеру, как Я понял.
Вы поняли неправильно. Log.Logger
— это всего лишь место, куда можно положить любой ILogger
. Вот реально любой. У меня там лежит логгер со специальным контекстом, чтобы можно было ловить все записи.
Log.Logger
другой объект, то он не будет никак связан с тем, который будет использоваться подсистемой логирвоания .NET Core. Я думал, что не надо будет об этом писать — это итак очевидно. Но сразу после интеграции, это именно так, как Я написал. Я это проверил, прежде чем отвечать на Ваш комментарий.
Но сразу после интеграции, это именно так, как Я написал.
… сразу после интеграции, которая сделана вами по тем настройкам, которые вы сочли нужными.
А в интеграции, которая сделана у меня, в Log.Logger
никогда не оказывается тот же объект, который вбрасывается в DI.
Мммм. Я пошел по вашей ссылке, там написано вот такое:
Host.CreateDefaultBuilder(args)
.UseSerilog() // <-- Add this line
.ConfigureWebHostDefaults...
А у вас в посте — вот такое:
Host.CreateDefaultBuilder(args)
.UseSerilog((context, services, configuration) => configuration
.ReadFrom.Configuration(context.Configuration)
.ReadFrom.Services(services)
.Enrich.FromLogContext()
.WriteTo.Console())
.ConfigureWebHostDefaults...
Есть некоторая разница.
Но не суть, на самом деле. Все равно, вы, как разработчик, решаете, какую конфигурацию использовать. Если вы не думаете над тем кодом, который вы используете, его поведение будет для вас непредсказуемым.
Я брал код из примера, на который есть ссылка с этой же страницы.
Ну то есть видите, примеров было несколько, вы выбрали тот, который сочли нужным. Я и говорю — ваш собственный выбор.
Если я ничего не путаю, тот оверлоад, который приведен в первом примере, никак не заменяет Log.Logger
(что логично, потому что логгер не меняется).
К сожадению, многие просто копипастят и не думают.
Ну в это случае, когда программа ведет себя не так, как они ожидают, виноваты они сами. Потому что, внезапно, программирование предполагает думать.
И про нее можно сказать банальное «просто не делайте так»
Мне кажется, такое обычно не работает. Я много раз видел, кода различные библиотеки (или даже стандартные классы) используют совсем не так, как это нужно делать просто потому, что «так проще», «и так работает», «я просто скопипастил из интернета».
Их можно иметь несколько, и все будет прекрасно
Да, сделать можно — согласен. Но в подсистеме логирования .NET Core так не делается. И Serilog внедряет туда только один ILogger объект.
Но в подсистеме логирования .NET Core так не делается.
Что такое "подсистема логирования .NET Core"?
В этом случае утверждение "в подсистеме логирования .NET Core так [иметь несколько логгеров] не делается" неверно. Вот как раз в этой "подсистеме" — а, точнее, говоря, в Microsoft.Extensions.Logging
— логгеров заведомо множество.
Это не одно и тоже.
Что конкретно "не одно и то же"?
Вы предлагаете создать несколько логгеров.
Нет, не предлагаю. Их создает Microsoft.Extensions.Logging
.
В подсистеме лоигрования регистрируются не сами логгеры, а их провайдеры.
Именно. Поэтому ваше утверждение и неверно.
прикладному разработчику вообще не обязательно давать доступ к серилогу
К Log.Logger всегда есть доступ — на то он и глобальный логгер. Если Я правильно понял, и речь идёт про обёртку над Serilog, то это напоминает работу с полуфабрикатом. Он такой неудобный/непонятный, что приходится делать обёртку, чтобы ограничить другим разработчикам доступ, и чтобы они там не натворили чего лишнего. Или написать анализатор кода с той же целью (как Вы предложили).
К Log.Logger всегда есть доступ — на то он и глобальный логгер.
Нет, только если у разработчика есть доступ к сборке Serilog
, что совершенно не обязательно.
Serilog.AspNet.dll
использует Serilog.dll
. Поэтому в комилирующемся проекте, если используется первая библиотека, то и вторая — тоже. В этом случае доступ к сборке Serilog
не ограничить. Если знаете способ — поделитесь.Я знаю простой способ: не референсить Serilog.AspNet.dll
(который вам нужен только в composition root) из проектов, реализующих прикладную логику. И все. Они даже знать не будут, что у вас в проекте серилог.
Стоп, а какие именно фичи серилога вам нужны?..
LogContext.PushProperty(..., ...);
Вы в этот момент обходите ту самую "подсистему логирования .NET Core", про которую вы только что писали, и в которой для этого есть полностью работающий механизм в виде log scopes. И после этого вы удивляетесь, что получаете смешанное поведение?
Поясню: когда вы так делаете, ваши изменения влияют только на логгеры серилога, и только на те из них, у которых сделано Enrich.FromLogContext
. При этом логируете вы, по вашему же заверению, через Microsoft.Extensions.Logging.ILogger
— за которым может быть любой логгер, не только серилог. Это типичное такое нарушение инкапсуляции, не надо так делать, вы можете получить неконсистентные логи.
Зачем же вы в тестах используете оверлоад, который работает с Log.Logger, когда есть оверлоад, который примет ваш логгер снаружи, тем самым сняв все проблемы со множественными логгерами (и жизненным циклом)?
- Неочевидно, что для тестов мне следует использовать какой-то специальный оверлоад. При использовании других логгеров в .NET Core не приходится использовать какие-то особые оверлоады. Там одинаково работает как в тестах, так и вне их.
- Такой оверлоад всё равно приводит к ситуации с двумя логгерами: один в Log.Logger, а другой в подсистеме логирования .NET Core (в контексте использования примера от разработчиков Serilog).
Неочевидно, что для тестов мне следует использовать какой-то специальный оверлоад.
Во-первых, неочевидно, что вам для тестов вообще следует использовать серилог. Во-вторых, неочевидно, что вам нужен этот оверлоад в проде. В-третьих, вы в тестах уже используете другую конфигурацию, нежели в проде, так что вам это, на самом деле, было очевидно. А в-четвертых, когда у вас случилась первая проблема с конкурентность, можно было поисследовать и разобраться.
Такой оверлоад всё равно приводит к ситуации с двумя логгерами: один в Log.Logger, а другой в подсистеме логирования .NET Core
Какая вам разница, что в Log.Logger
? Не используйте его.
Srilog
или не справился с его использованием. Я пытаюсь донести, что как разрабочик .NET Core Я не должен видеть и иметь доступ к тому, что мне не следует использовать, но Я могу это использовать и мало того, мне ещё показывают в самом видном месте (в readme.md) как это делать, наводя меня на ложный путь. Вы об этом сами уже несколько раз упомянули — «Не используйте его». Тогда зачем он там нужен, если его не следует использовать? Значит библиотека Serilog для .NET Core должна быть иного содержания — адаптированая для API логирования .Net Core. А вместо этого мы имеем спорным образом прилепленный Serilog. Ещё раз уточню: Я не спорю, что есть технические возможности сделать всё нормально и правильно. Но это должно быть естественно и прозрачно/понятно. А не так, что надо бить по рукам или писать анализаторы кода.Тогда зачем он там нужен, если его не следует использовать?
Для обратной совместимости.
Значит библиотека Serilog для .NET Core должна быть иного содержания
Я боюсь, у нас тут с вами фундаментальное разногласие. Я вот считаю, что библиотека Serilog ничего вам не должна.
адаптированая для API логирования .Net Core.
А зачем вам вообще серилог, если вы используете Microsoft.Extensions.Logging?
Но это должно быть естественно и прозрачно/понятно.
Мне все прозрачно и понятно.
Я боюсь, у нас тут с вами фундаментальное разногласие. Я вот считаю, что библиотека Serilog ничего вам не должна.
Это действительно так. Я никому ничего не навязываю. Ничего не требую. Я рассуждаю о дизайте Serilog с точки зрения программиста .NET Core. Разве программисту .NET Core нужно думать о какой-то совместимости? О какой? .NET Core 3.1 с 1.0 если только.
PS: Отличнй ответ на все тикеты в github Serilog )))
библиотека Serilog ничего вам не должна.
Я рассуждаю о дизайте Serilog с точки зрения программиста .NET Core. Разве программисту .NET Core нужно думать о какой-то совместимости? О какой? .NET Core 3.1 с 1.0 если только.
Я не знаю, кто такой "программист .NET Core" (хотя и считаю, что думать надо любому программисту). А я вот, как программист, который пишет и под Core, и под .NET Framework, регулярно думаю о переносимости моего кода, и очень благодарен разработчикам Serilog, которые сделали так, что я могу взять свой код от .NET Framework, перенести его под Core, и он будет работать один-в-один.
При старте нужно использовать вывод в консоль минуя любое логгирование, т.к. если сервис упал при старте, значит неисправна инфраструктрура и вы всё равно будете смотреть вывод в консоль упавшего контейнера
Конечно, и там можно поглядеть в журнале событий, почему приложение не стартует — но в файле всё-таки быстрее. Поэтому добавление такого try-catch, как в примере статьи, всё-таки не лишено смысла, и иногда нас сильно выручало.
Глобальный логгер это ж legacy для обратной совместимости
Если вообще, то — да. Но контекст статьи – это .NET Core. Какое legacy Вы имеете в виду?
Интеграция Serilog с .NET Core в виде библиотеки serilog-aspnetcore появилась для .NET Core 2.0. Легаси тут может быть, наверное, если его писали под .NET Core 1 или Standard того времени. Правда, Я так и не раскопал, как обстояли дела с интеграцией Serilog с этими платформами на тот момент.
Его нигде не нужно использовать.
Опять же. Если самоконтроль — хорошо. Но когда приходится работать с другими людьми, то надо бить по рукам или, как предложил lair, писать статический анализатор кода.
Интеграция Serilog с .NET Core в виде библиотеки serilog-aspnetcore появилась для .NET Core 2.0.
serilog-aspnetcore
— это не библиотека, это репозиторий. Из этого репозитория собирается NuGet-пакет Serilog.AspNetCore
, который тоже не библиотека. А "библиотека" — это лежащая в этом пакете сборка (assembly) Serilog.AspNetCore.dll
. И в этой библиотеке, внезапно, нет никакого глобального логгера. Вы просто не сможете его найти в исходниках.
А есть он в библиотеке Serilog
, которая (а) была разработана до официального появления .NET Core и (б) хочет сохранять совместимость с теми своими пользователями, которые пользуются ей за пределами ASP.NET Core. Это и есть легаси.
Ну и на самом деле, что тоже немаловажно, Serilog.AspNetCore
— это не для интеграции Serilog с .NET Core, как вы пишете. Это для интеграции с ASP.NET Core (и название об этом говорит, и первая строка описания). Нет никакого пакета для интеграции Serilog с .NET Core, не с чем там интегрироваться, просто берите базовый Serilog и используйте.
Так что пакет с именем AspNetCore может годиться и для просто Core.
Не в этом случае. У него в .NETStandard зависимости от Microsoft.AspNetCore.Http.Abstractions
. Для "просто Core" есть Serilog.Extensions.Hosting
.
serilog-aspnetcore — это не библиотека
Да, пожалуй, Я не совсем правильно выразился. Но думаю, что все (и Вы тоже) меня правильно поняли.
А есть он в библиотеке Serilog
Да, всё так. И nuget с этой библиотекой подгружается в проект, как зависимость nuget пакета Serilog.AspNetCore. Принося с собой и глобальный логгер, не смотря на то, что он там не нужен.
хочет сохранять совместимость с теми своими пользователями, которые пользуются ей за пределами ASP.NET Core. Это и есть легаси.
Теперь понятно о каком легаси шла речь. Это понятное желание. И, если смотреть на картину в общем, то понятно почему это всё доступно и в .Net Core. Но разработчик .Net Core не виноват, что разрабочику Serilog надо там какое-то легаси поддерживать, поэтому надо дополнительно разбираться где ему предлагают использовать что-то сомнительное и какие оверлоады методов интеграции Serilog надо вызывать, чтобы всё работало норм. Уверен, что если приложить больше желания, то дизайн Serilog для .NET Core мог быть сильно лучше, даже при условии сохранения совместимости для .NET Framework проектов.
Уверен, что если приложить больше желания, то дизайн Serilog для .NET Core мог быть сильно лучше,
Прекрасно. Приложите это желание. По моему опыту, Блумхардт прекрасно принимает мотивированные pull requests.
дизайн Serilog для .NET Core
Нет никакого "Serilog для .NET Core". Его просто нет. Есть Serilog, который сам по себе, есть адаптер серилога для Microsoft.Extensions.Logging, есть расширения этого адаптера для ASP.NET Core.
Прекрасно. Приложите это желание.
Я уже писал в статье, что там нужны слишком концептуальные изменения. Одним только пул-реквестом это не решить. Не уверен, что на такие изменения автор пойдёт.
Нет никакого «Serilog для .NET Core»
Согласен. Но Я и не писал, что он есть. Я писал про дизайн Serilog с точки зрения разработчика .NET Core.
Я уже писал в статье, что там нужны слишком концептуальные изменения. Одним только пул-реквестом это не решить. Не уверен, что на такие изменения автор пойдёт.
Значит, ваше утверждение про "приложить больше желания" неверно.
Я писал про дизайн Serilog с точки зрения разработчика .NET Core.
А зачем вы смотрите на пакет, который под .NET Standard и Framework, с точки зрения Core?
А зачем вы смотрите на пакет, который под .NET Standard и Framework, с точки зрения Core?
Затем, что иначе этот функционал недосутпен. А пуш пропертей — это, на мой взгляд, категорически необходимая вещь. Иначе какой толк от Serilog?
Затем, что иначе этот функционал недосутпен.
Какая конкретно функциональность недоступна, если вы будете использовать Serilog напрямую как .NET Standard-пакет?
А пуш пропертей — это, на мой взгляд, категорически необходимая вещь.
"Пуш пропертей", как я уже писал, прекрасно доступен без серилога.
А если вам нужна какая-то функциональность, которая недоступна в Microsoft.Extensions.Logging, то перестаньте требовать, чтобы библиотека, которая вам ее предоставляет, играла по правилам Microsoft.Extensions.Logging. Это взаимопротиворечивые требования.
В чём преимущества использования Serilog по сравнению с NLog?
Когда я последний раз на него смотрел (давно, честно признаюсь), NLog не умел структурное логирование.
Страсти по Serilog + .NET Core: Глобальный логгер