Комментарии 176
Теперь вы знаете простой и быстрый способ подключения приложения к системе мониторинга ошибок.
Серьезно. А документацию прочитать никак нельзя было, да?
Вы же понимаете, я надеюсь, что предлагаемый вами путь никак не помогает исправить ошибку, он просто позволяет узнать о ней немного раньше?
(и это не говоря про развитие систем структурированного логирования, где NLog, прямо скажем, не очень играет)
Например, на работе мы занимаемся поддержкой TSD (терминалы сбора данных), у нас нет доступа к этим устройствам, а ошибки исправлять надо, мониторинг ошибок тут сильно помогает. До мониторинга приходилось просить пользователей присылать логи, это было ужасно.
Система мониторинга предоставляет для каждой ошибки стек и другую полезную техническую информацию
Странно, а я думал, эту полезную техническую информацию предоставляет система логирования.
Например, на работе мы занимаемся поддержкой TSD (терминалы сбора данных), у нас нет доступа к этим устройствам, а ошибки исправлять надо, мониторинг ошибок тут сильно помогает.
Тут помогает не мониторинг ошибок, а хранение логов не на устройстве.
Если вам нужно найти ошибку, то в большинстве случаев удобнее иметь сгруппированный список из 5 ошибок, чем лог из 1000 записей. Тот же Zidium умеет хранить логи, но я этим функционалом почти не пользуюсь (очень редко), потому что удобнее иметь список ошибок, он маленький.
А чем "ошибка" отличается от "лога", простите?
А еще опыт показывает, что удобнее иметь структурированный лог, в котором есть вся информация по обрабатываемой операции, потому что самой ошибки регулярно мало для анализа.
В системе мониторинга Zidium лог — это просто строки текста, которые можно читать, фильтровать. Ошибка — это событие, которое имеет уникальный тип и стек.
Вот поэтому и надо пользоваться структурированными логами, а не "просто строками текста". Тогда, внезапно, выясняется, что, увидев ошибку, произошедшую в рамках конкретного HTTP-запроса, можно (а) посмотреть все остальные логи для этого запроса (иногда даже по распределенной системе) и (б) посмотреть, как себя вели остальные запросы на тот же адрес. В один клик.
Вот это — правда позволяет обрабатывать ошибки быстрее и эффективнее.
Структурированный лог, конечно, круто, но не для всех проектов нужна такая навороченность.
А ничего навороченного, он в использовании не сложнее, чем любой другой.
Уже есть огромное количество сделанных сайтов и приложений, которые используют NLog или аналоги, и которые никто переделывать не будет. А подключить в конфиге адаптер для облачного мониторинга — это вполне реально, и выгода получается сразу же.
Да, но это тривиальный пункт из документации на любой логгер.
Nlog конечно не умеет включать/выключать логирование всей информации обрабатываемой операции (и идентификацию такой операции во время исполнения) — так до сих пор я думал что это всегда решается ситуационными соглашениями по конфигурации логгирования в пользовательском коде. Можете ли дать ссылки на системы о которых вы говорите?
П.С. Сам таскаю такие соглашения из проекта в проект которые помогают включить логгинг «всего» (input, output, verbose ef, verbose custom) для конкретной операции, конкретного пользователя) при поиске глюков. Но могу сказать что такую систему соглашений в библиотеку очень трудно «запаковать» (всё пытаюсь), так как она все время хочет стать платформой: 1) появляется новый уровень абстракции — вы должны все операции запускать внутри особого хендлера — новый уровень абстракции всегда болезнено 2) кода не становится знаково меньше — определение такого хендлера в новом проекте НЕ сводится к десятку строк в одном файле 3) идет поперек доминирующей архитектурной идеи «делегируем DI в стандартный IoC контейнер» (стандартный ioc контейнер не умеет собиреть DI per-session/per-user) — «архитекторы» не принимают, что контейнер используется только для «взять конфигурацию» — а дальше строим объекты ручками (внедряя логгеры соответственно конфигурации и данным сессии — типа имени пользователя).
XML — это конечно хорошо, но туда надо еще и данные положить. А вот с тут-то с ними у NLog и беда: интерфейс логгеров заточен на прием текстовых данных и все.
Все богатые возможности по логированию всего и вся направлены на получение информации из окружения (статических полей), а вот чтобы записать строчку в лог указав ей при этом "Код ошибки — 0012" — приходится исхитряться.
Но не понял вы хотите «записать в строчку» — а это уже не «структурированный», поскольку абстракиция строка не имеет структуры. Вот если код обавить как property он его аккуратно как xml в отдельный тэг и положит (xml/nlog форматтер):
<nlog:properties>
<nlog:data name="CorrelationToken" value="a5d5b1f5-526b-420d-aa03-727033f2ec37" />
<nlog:data name="OperationName" value="RibbonDashboard.javascript" />
<nlog:data name="ServiceName" value="/Error/LogBrowserMessage" />
</nlog:properties>
другой вопрос если вы и liar хотите форматтер в дб или любой другой ремоут, а он не пишется. понимаю, но не сочувствую (не правильно это).
… я могу в NLog из кода написать _logger.Info("Operation {Id} completed in {Elapsed} ms", operationId, elapsed.TotalMs
и получить запись в логе со свойствами Id: ..., Elapsed: ...
и возможностью выбрать все записи такого типа?
var logEventInfo = new LogEventInfo()
{
Message = message,
Level = LogLevel.Info,
TimeStamp = DateTime.Now
};
logEventInfo.Properties[«Id»] = ...;
logEventInfo.Properties[«Elapsed»] = ...;
logger.Log(logEventInfo);
не совсем так как вы хотите, но записи в логе с <nlog:data name=«Elapsed» value="..." /> вы получите.
Про «выбрать запросы» я в недоумении, это задача другого уровня, в моей голове есть шаблон «лог надо скормить системе мониторинга и у нее и запрашивать».
Я сразу приведу код
Значит, я был неправ, и NLog тоже научился структурированные данные. Когда я с ним работал, этого либо не было, либо, что более вероятно, это не было на поверхности.
Про «выбрать запросы» я в недоумении, это задача другого уровня, в моей голове есть шаблон «лог надо скормить системе мониторинга и у нее и запрашивать».
В вашем примере нет одного важного пункта: а как, собственно, выглядит сообщение в коде и в логе (причем как структурированном, типа Эластика или Seq, так и, что важнее, в обычном, типа файлового, при настройках по умолчанию).
Я лучше поясню почему использую xml для verbose. Потому что не использую эластик для трейсинга (т.е. для отлавливания ошибок). Для трейсинга моя «платформа» работает лучше: клиент жалуется, включил ему и только ему трейсинг — или же редкая операция падает — включил трейсинг только ее (все без переустановки). При чем можно задать собирать сообщения с начала операции, в буфер, а сбросить буфер в файл реагируя на событие (вылет с ошибкой, нужный результат, слишком долгое исполнение). Порции данных получаются и полными и только то что необходимо. В xml же проще видны многострочные данные (сериализированный output, stacktrace).
А для мониторинга, сбора статистики я просто имею дюжину разных файлов которые можно грузить в эластик, но форматирую их прямо в строке, это жестко, но это файлы с жестко заданой целью (только длитиельность исполнения, только броузеры/разрешения и т.д.). Если цель лога жесткая то и формат жесткий: нет причин вводить лишние абстракции.
Полностью сообщения я не приевел, но отрывок (хмл) привел.
Я спрашивал про текстовое сообщение, человекочитаемое. В моем примере это "Operation {Id} completed in {Elapsed} ms"
в коде и Operation CQ/15/83 completed in 182 ms
в логе.
Для трейсинга моя «платформа» работает лучше
Лучше по сравнению с чем?
Порции данных получаются и полными и только то что необходимо.
Я вот обычно заранее не знаю, что необходимо.
А для мониторинга, сбора статистики я просто имею дюжину разных файлов которые можно грузить в эластик, но форматирую их прямо в строке, это жестко, но это файлы с жестко заданой целью
Хорошо, когда у вас есть конечное число жестко заданных целей. А мне надоело делать отдельный логгер под отдельную задачу.
Я вот обычно заранее не знаю, что необходимо.
Лучше по сравнению с чем?Лучше по сравнению с подходом «кидать в лог всё по максимуму».
я могу включать то что мне нужно (а именно инпут, оутпут, кастом вербос, вербосе EF с фильтром по операции/категории, пользователю, результату; есть еще опции, но список их безусловно конечен, т.е. то что предусмотрено ) без переустановки приложения.
Лучше потому что у меня точно контроллируемый IO и performance. Я не готов ими жертвовать ради того «что когда-нибудь мне может что-то понадобится». И я не готов держать в голове и осложнять себе трейсинг размышлениями кто создает мне эту нагрузку «апликация» или «трейсинг».
и трейсинг с логгингом я различаю безусловно.
Лучше по сравнению с подходом «кидать в лог всё по максимуму».
я могу включать то что мне нужно [...] без переустановки приложения.
Эм, "я" (точнее, правильно написанная система) тоже так может. Непонятно, чему вы противопоставляете свой подход.
Лучше потому что у меня точно контроллируемый IO и performance.
У полностью включенного логирования они тоже точно контролируемые.
Я не готов им жертвовать ради того «что когда-нибудь мне может что-то понадобится».
Ну зато вы, видимо, готовы разбирать инцидент при его повторе, а не первом случае.
И я не готов держать в голове и осложнять себе трейсинг размышлениями кто создает мне эту нагрузку «апликация» или «трейсинг».
Ну так перформанс-тесты вроде как для этого существуют.
Ну зато вы, видимо, готовы разбирать инцидент при его повторе, а не первом случае.
Во-первых если это вылет с ошибкой — я все же стактрейс имею, во вторых если это вылет с ошибкой и не периодическая операция — я имею почти все (по дефолту для таких операций идет сбор информации в буфер, и сброс на вылет с ошибкой). В третьих:
разбор инцидента вот просто «всегда» включает в себя повторение, и вопрос «можно ли повторить» — это первое что требуется установить. Когда пытаюсь повторить и включаю полный трейсинг. Так что такой задачи «со 100% успеха пойми в чем дело» просто нет. Не достижимы 100%
Во-первых если это вылет с ошибкой — я все стактрейс, во вторых если это вылет с ошибкой и не периодическая операция — я имею почти все (по дефолту для таких операций идет сбор информации в буфер, и сброс на вылет с ошибкой).
Ну то есть ваш подход, на самом деле, ничем не отличается от "логируем все", только у вас есть ненулевой шанс потерять ваш буфер.
В третьих:
разбор инцидента «всегда» включает в себя повторение, вопрос «можно ли повторить» — это первое что требуется установить.
Угу, и вот для этого очень полезны логи.
Обратите внимание, вы так и не ответили на вопрос про текстовое представление сообщения в NLog.
Обратите внимание, вы так и не ответили на вопрос про текстовое представление сообщения в NLog.
я ответил: мне кажется можно. mayorovp
говорит что кажется нельзя. нужно смотреть документацию.
вы извините, я просто под рукой кода не имею, мне кажется я где-то в layout задавал форматирование propoerty
мне кажется я где-то в layout задавал форматирование propoerty
Это не то. Мне нужно, чтобы формат текстового сообщения задавался программистом в момент вывода (я выше привел пример, как это выглядит).
Layout в конфиге просто не может знать про все property, их в приложении сотни.
но то что вы привели выше
_logger.Info("Operation {Id} completed in {Elapsed} ms", operationId, elapsed.TotalMs
как по мне так точно можно сделать имея API вида.
var logEventInfo = new LogEventInfo()
{
Message = message,
Level = LogLevel.Info,
TimeStamp = DateTime.Now
};
logEventInfo.Properties[«Id»] = ...;
logEventInfo.Properties[«Elapsed»] = ...;
logger.Log(logEventInfo);
и кастом форматера который выводит все properties.
может есть и из коробки.
я не очень понял что такое момент вывода
В коде я пишу:
_logger.Info("Operation {Id} completed in {Elapsed} ms", operationId, elapsed.TotalMs)
В другом месте я пишу:
_logger.Verbose("User {Name} expired on {ExpirationDate}", username, DateTime.Now)
В конфиге я пишу:
<add key="serilog:write-to:File.path" value="log.txt" />
<add key="serilog:write-to:File.outputTemplate" value="[{Level:u3}] {Message:lj}{NewLine}{Exception}">
В файле получаю:
INF Operation ON/15/86 completed in 123 ms
VRB User baad expired on 2012-01-01 13:45
Но если я настрою конфиг так, что он будет писать в Seq или Elastic, я получу там и сообщение, и тип сообщения (Operation {Id} completed in {Elapsed} ms
и User {Name} expired on {ExpirationDate}
), что позволит мне выбрать все сообщения такого типа (например, для графика по времени), и все переданные данные, что позволит мне, например, вытащить все сообщения, связанные с конкретной операцией.
стандартный ioc контейнер не умеет собиреть DI per-session/per-user
Возможно, это повод перейти на другой контейнер...
я не сильно искал, потому что предпологаю, что если контейнер будет такой гибкий, что может все, даже по значению полученому в httprequest собрать мне акшн со всем полным verbose трейсингом по всей трубе, то ожидаемо что кода будет не меньше (чем при ручной сборки зависимостей), а сложности добавит еще как (болезненная новая абстракция).
Ninject умеет как per-session, так и per-user, или вовсе per-что-угодно из коробки (но вам все равно придется вручную уведомлять Ninject об окончании времени жизни ваших скоупов).
Скажите а вы сами что думаете о пользе приносимой внешними «третьесторонними» контейнерами? Я никак не могу увидеть их преимуществ перед «кастомными контейнерами» и собиранием зависимостей руками (контейнер как шаблон, а не как библиотека). Ну напр перед таким кодом:
class ProdContainer:IContainer {
public IMyService ResolveMyService(){ ... return MyService(...); } // inject for test env
}
class TestContainer:IContainer{
public IMyService ResolveMyService(){ ... return MyService(...); } // inject for test env
}
Я не могу принять аргументы про «велосипед», тут нет никакого велосипеда. Про меньше кода — не на столько меньше.
У автоконтейнеров может быть динамическое связывание и видимо на них можно строить системы плагинов — но если такой задачи нет?
Контейнер помогает не забыть какую-нибудь ерунду когда кода много.
Извиняюсь за длинноты и неуместную агитацию.
var dbContextHandler = container.ResolveDbContextHandler<TOutput>();
var output = dbContextHandler(
(dbContext)=>{
dbContext.MyEntities.Select...
// ...
return myOutput;
}
);
тут dbContext будет жить ровно столько сколько нужно.
Я наоборот скажу я не могу представить ничего более удобного.
А dbContextHandler
надо руками написать, да?
А dbContextHandler надо руками написать, да?
И да и нет. Можно руками один раз на проект, а можно generic
dbContextHandler<TDbContext>
из своей библиотеки (я знаю новая абстракция это тяжело для других, должна быть мотивация, и тут вопрос на сколько вам важно избавиться от забытых dispose).Можно руками один раз на проект, а можно generic
Ну то есть все равно надо написать (либо в проекте, либо в библиотеке). А вы говорите — кода не больше.
должна быть мотивация, и тут вопрос на сколько вам важно избавиться от забытых dispose
У меня проблему забытых Dispose
решает контейнер, так что мне это просто не нужно.
т.е. строит dbContext с логгерами которые зависят от httprequest.
http://docs.autofac.org/en/latest/lifetime/instance-scope.html#instance-per-request
У меня работает в ванильном asp.net, OWIN и WebAPI.
Но не все ваши аргументы были удачны, или хорошо сформулированы, я бы это хотел донести. Надеюсь дискуссия была взаимовыгодной.
Я в autofac не могу
Тогда не понятно, зачем вы спрашивали о преимуществах.
Тогда не понятно, зачем вы спрашивали о преимуществах.
не знал — потому и спрашивал у знающих «можно ли такое или нет» есть премущества или нет? затем уже вопросы были ко мне (я благодорен за них, на самом деле я хоть и горжусь своим метапрограммированием, но не понимаю как его представить хотя бы даже в песочнице хабра).
Не очень понятно, зачем вообще нужен такой контейнер, как вы показываете. Как его ожидаемый сценарий использования?
Я не уверен, что "любой дженерик контейнер" делает то же самое. Просто покажите конкретный пример использования, если не сложно.
<blockquote>var dbContextHandler = container.ResolveDbContextHandler<TOutput> </blockquote>
вот вариант использования в качестве service locator. разница с использованием generic IServiceCollection мне была бы не заметна
Это сервис-локатор. Я, скажем, использую сервис-локаторы только в случае невозможности написать иначе, и предпочитаю dependency injection. Как мне поможет ваш контейнер?
Я сказал именно то, что сказал. Я использую контейнер для DI, а не для SL. Как мне поможет ваш контейнер?
Вот два контейнера
interface IContainer{
T Resolve<T>();
}
interface MyProjectContainer{
MyService ResolveMyService();
}
как то что вы используете первый а я второй делает вас православным пользователем DI а меня еретиком? Я тоже люблю DI.
Я (практически) не использую первый. Он используется фреймворками, на которых я пишу (и для которых придется написать дополнительный код, чтобы подключить ваш контейнер), а я работаю напрямую с созданными объектами, получая зависимости "магическим образом".
В ASP я так же как и вы получую контроллер из контейнера платформы со всем чем в конструкторе. Затем в начале акшна получу создам свой кастом контроллер (передав динамическую информацию сессии или юзера) я даже могу получить factory этого моего контроллера из контейнера платформы и в этот factory скину динамические параметры (разницы нет). и начну получать свои компоненты из своего кастом контроллера. и на уровне business service ровно такой же DI продолжится: вот в реализации MyService в конструкторе все зависимости будут получены ровно согласно принцпипам DI, соберет не контейнер платформы, а мой. Понимаете? Контейнер не обязан быть всеобщим и дженерик. Контейнер обязан собирать зависимости и все.
В ASP я так же как и вы получую контроллер из контейнера платформы со всем чем в конструкторе.
… это работает через ваш самописный котейнер?
и начну получать свои компоненты из своего кастом контроллера
… кажется, нет. Теперь у вас в системе два контейнера. И зачем мне это?
Теперь у вас в системе два контейнера. И зачем мне это?
Вы цепляетесь к словам а не к архитектуре. Назовите мой контейнер «динамическим сборщиком зависимостей» в отличии от «статитического сборщика платформы» и вопрос исчезнет.
А зачем мне два сборщика? Суть-то не меняется, как вы их не назовите.
У меня есть контроллер в WebAPI, он получает в конструктор правильно инициализированные зависимости, они при создании получают в конструктор правильно инициализированные зависимости, и так далее. И ничто из этого даже не знает, какой контейнер использовался (и использовался ли).
а что в том что асп контроллер, знает о каком-то фактори в который скинет динамическую информуцию и далее получит business service из него? Что тут сломано?
Здесь внесена лишняя зависимость, которая не приносит практической пользы (и усложняет миграцию в случае необходимости).
2) как это усложнит миграцию? кастомный сборник зависимостей — это ваш/мой код и все что он делает — собирает зависимости (нет ничего внешнего).
будующей миграцией оправдывают любой овердейзайн. как отличить отмазку от аргумента?
… а чем вы оправдываете лишнюю зависимость, напомните?
как это усложнит миграцию?
Надо будет переписать весь код, зависящий от вашего кастомного контейнера. В том числе — в вашем случае — код внутри контроллера.
лишнюю абстракцию — мне так более понтяно — я оправдываю тем что она мне обеспечивает результат: построение бизнес сервисов с динамически заданым уровнем логгирования (конкретную сессию, конкретный данный акшн, на конкретный результат и т.д. надеюсь не забыли, действительно надеюсь — потому что именно гибкостью и хотел удивить). и я хочу думать как раз без лишних зависимостей обеспечивает: никаких autofac и ninject (вот уж где явные лишнии зависимости). и вы так представляете что кастом контейнер это какой-то сложный код. да это примитивное: проверь условие создай-положи в конструктор.
лишнюю абстракцию — мне так более понтяно
Нет, именно зависимость. То, от чего зависит ваш контроллер.
я оправдываю тем что она мне обеспечивает результат: построение бизнес сервисов с динамически заданым уровнем логгирования [...] именно гибкостью и хотел удивить
Это все можно сделать на стандартном Autofac + Serilog. Так что вашу зависимость это никак не оправдывает.
и вы так представляете что кастом контейнер это какой-то сложный код. да это примитивное проверь условие создай-положи в конструктор.
Тем обиднее его писать каждый раз.
Потому что контроллер про него не знает.
Потому что контроллер про него не знает.
если про него знает references ассембли это зависимость. будем считать что мы тут приниципиально расходимся. но кажется друг друга поняли.
А что если контроллер сам по себе генерируем и в нем не строчки кастомного кода? Строится по мете? Т.е. вызов создания моего кастомного контроллера в коде проекта есть только один раз, в одном фале (так же как и у вас autofac конфигурирутеся один раз)?
Вот, кстати, хороший вопрос в контексте поста: как в вашем кастомном контейнере сделать так, что если какой-то компонент требует в качестве зависимости ILogger
, этот ILogger
параметризуется полным именем типа. Иными словами, мы для каждого компонента автоматически имеем логгер, в котором прописан правильный контекст.
lair, ну все зависимости которые собирает контейнер можно собрать «руками», просто все.
И написать для этого много повторяющегося кода. Зачем, если можно отдать это контейнеру?
вы лучше скажите что должен показать вами выбранный пример?
Пример пользы, которую приносит готовое решение, по сравнению с самописным.
И написать для этого много повторяющегося кода. Зачем, если можно отдать это контейнеру?
почему он должен быть повторяющимся. повторяющий код заключается в методы и перестает быть повторяющимся.
Пример пользы, которую приносит готовое решение, по сравнению с самописным
это работает в обе стороны: мне не ясна польза от регистрации своих зависимостей в контейнере платформы.
кроме того для меня польза очевидна. я могу без всякого труда собирать зависимости на динамических параметрах. у меня есть такой запрос
почему он должен быть повторяющимся. повторяющий код заключается в методы и перестает быть повторяющимся.
Вот у вас есть сто конструкторов, каждый из которых принимает ILogger
. Остальные параметры у них отличаются. Приблизительно вот так:
new MyService(logger.ForContext<MyService>(), ..., ..., ...)
new OtherService(logger.ForContext<OtherService>(), ..., ...)
Как вы вынесете это в метод?
мне не ясна польза от регистрации своих зависимостей в контейнере платформы.
Я никакой не вижу. Иногда это, вероятно, требование.
Как вы вынесете это в метод?
повторю, я не пишу монолиты где вызовы ста сервисов. ставьте минус.
а создание зависимостей для своего скромного набора сервисов из каких пяти-десяти штук, я действительно пропишу вот так как вы показали. но я думаю и на сотню решусь: больше контроля компилятору в таком аду это наверное еще более важно.
а свой создание зависимостей для своего скромного набора сервисов из каких пяти штук, я действительно пропишу вот так как вы показали.
Вот вам и еще пример, где в вашем подходе надо писать больше кода.
больше контроля компилятору.
Как компилятор спасет вас от следующей ошибки:
new MyService(logger.ForContext<MyService>(), ..., ..., ...)
new OtherService(logger.ForContext<MyService>(), ..., ...)
Вместо вашего у меня будет что-то вроде
var logger = BuildLogger(configuraiton, httpRequest)
IMyService GetMyService(){
new MyService(logger , ..., ..., ...);
}
// ...
IOtherService GetOtherService(){
new OtherService(logger , ..., ...)
}
вместо httpRequest можно любую динамическую информацию. понимаете, когда нужно трейсить мне нужно всё, а когда нужно — решается динамически. именно из этой потребности вытекают все мои решения. т.е. мне не надо сильно различать разные уровни логгирования для разных бизнес сервисов для трейсируемой сессии.
другой вопрос что если очень надо (например добавить категорию «имя бизнес сервиса») все это можно чисто программерскими методами решить (я тоже могу в рефлекшн и execution trees) как и генерик контейнер…
Я говорил можно избежать повторяещегося кода.
Вы его не избежали.
Вместо вашего у меня будет что-то вроде
Это не решает поставленную задачу: вброшенный логгер ничего не знает о сервисе, в который он вброшен.
все это можно чисто программерскими методами решить (я тоже могу в рефлекшн и execution trees)
Ну то есть написать еще кода, причем я пока даже не понимаю, какого, чтобы сохранить ваше ручное создание каждого сервиса.
И все это вместо того, чтобы взять готовое решение, которое все это умеет. Я решал ровно эту задачу на прошлой неделе, это потребовало доставить один пакет и вписать одну строчку кода.
Ну то есть написать еще кода, причем я пока даже не понимаю, какого, чтобы сохранить ваше ручное создание каждого сервиса.
нет, не для создания сервиса — для этого кода не надо, а для возможности принципиально недопустить ту возможную ошибку которую вы указали. я надеюсь что вы не будете допускать эмоционально окрашенных упрощений.
одну строчку кода.
понимаете, это не совсем так. логгеры это не весь трейдофф за сто моих срочек кода против вашей одной. вот как выглядит мой контроллер:
public class PrivilegesController : ConfigurableController
{
#region Meta
static ControllerMeta<Privilege, string> meta = new ControllerMeta<Privilege, string>(
// ...
);
#endregion
CrudRoutineControllerConsumer<Privilege, string> consumer;
public PrivilegesController(IConfigurationRoot configurationRoot) :base(configurationRoot)
{
consumer = new CrudRoutineControllerConsumer<Privilege, string>(this, meta, (action, userContext) => userContext.HasPrivilege(Privilege.ConfigureSystem));
}
#region Details / Index
public async Task<IActionResult> Details()
{
return await consumer.Details();
}
public async Task<IActionResult> Index()
{
return await consumer.Index();
}
#endregion
#region Edit
public async Task<IActionResult> Edit()
{
return await consumer.Edit();
}
[HttpPost, ActionName(nameof(Edit)), ValidateAntiForgeryToken]
public async Task<IActionResult> EditFormData()
{
return await consumer.EditConfirmed();
}
#endregion
}
В зависимости от мета (EF Core модели) он создаст полностью рабочий контроллер по стандартным лекалам с поддержкой редактирования one-many и many-many, возможностью перехвата ошибки дб и семантического его разбора, с возможностью проверки ошибки синхрона через rowversio, и еще многих разных штук. Это тысячи строк кода которые входят в наш трейд офф.
Только не подумайте что я только из таких контроллеров апликацию создаю, просто новый уровень абстракции «dbcontexthandlerов» мне позволил сделать «T4 генерируремые контроллеры» через execution tree.
П.С.
Это пример не кастом контроллера (он тут скрыт и будет создан глубже из динамики и IConfigurationRoot) а того что можно сделать имея новые абстракции.
нет, не для создания сервиса — для этого кода не надо,
То есть конструкторы в вашем коде мне померещились?
а для возможности принципиально недопустить ту возможную ошибку которую вы указали
Вот я и говорю, я пока не очень понимаю, как вы это сделаете, не написав свой собственный дженерик контейнер.
Это тысячи строк кода которые входят в наш трейд офф.
Нет, не входят. Потому что это не функциональность DI. (При этом нет никаких проблем вынести эту фабрику в тот же Autofac).
того что можно сделать имея новые абстракции.
Угу, написать контроллер, который состоит исключительно из делегирования. Еще пара десяток строчек кода, без которых можно было бы обойтись.
Извините, хочтеся задать вопрос по реплике: "(не говоря про) развитие систем структурированного логирования". Далее вы пишите " удобнее иметь структурированный лог, в котором есть вся информация по обрабатываемой операции". Видимо это раскрывает что за системы имеются ввиду — и да такие системы интересны. Но имеются ли ввиду библиотеки или системы соглашений?
Давайте я просто дам ссылку, там много полезного сказано, и это будет быстрее, чем я это перепишу: https://nblumhardt.com/2016/06/structured-logging-concepts-in-net-series-1/
Конфигурационный файл Zidium.xml должен быть в папке, где находится exe-файл программы.
Ну зачеееем?! Почему нельзя написать конфиг внутри элемента target? Или хотя бы указать путь к конфигу атрибутом...
Сам спросил — сам отвечаю.
Файл Zidium.xml ищется в той же папке, где находится библиотека (!), но если эта папка называется "bin", а в родительской лежит файл web.config или global.asax — то берется именно родительская...
То есть с настройками по умолчанию файл Zidium.xml можно будет спокойно скачать с веб-сервера, со всеми <access accountName="Test" secretKey="..." />
. А еще клиентскую библиотеку нельзя будет засунуть в GAC (некоторые интеграторы любят ставить все сборки туда).
Вот зачем так делать? Скажите, чем вас AppDomain.CurrentDomain.BaseDirectory
не устраивал?
группирует логи. цепляется практически к любой системе логгирования. free (до определенного объема логов если не ошибаюсь, но все же). есть докер-образы. тэги. чем NLog превосходит ее?
Может ли она использовать для настройки logback.xml и т.п.?
ps отдельный вопрос по требованиям к системе. sentry прожорливая и это явный минус.
создание клиента занимает 3 пункта в java приложении.
— зависимости
— логгер в logback.xml
— иногда нужно запустить приложение с флагом, указывающим имплементацию для логгера.
это при неопытности и инструкции перед глазами 10 минут.
честно не помню нужно ли создавать Bean в спринге для raven-клиента.
или NLog не потребует пересборки проекта и перезапуска? зацепится на горячее?
Вы правда не видите разницы между private static readonly ILogger log = Logmanager.GetCurrentClassLogger();
и передачей IRavenClient
через конструктор?
Представьте, что кусок программ писался без логов долгое время и накопил цепочку из 10 классов которые создают друг друга. Вы решили добавить логи в последний.
В случае NLog вы берете и добавляете их. В случае Sentry/RavenClient — надо будет протащить IRavenClient
через 10 конструкторов.
иногда нужно запустить приложение с флагом, указывающим имплементацию для логгера
Ну и как вы собрались менять имплементацию логгера флагом — если у разных логгеров разные интерфейсы?
Только не надо про Commons Logging — этот "общий знаменатель" довольно беден, и при работе через него Sentry окажется ничем не лучше чем log4j или NLog.
я прикручивал Sentry к проекту, которому было лет 10. названные мною шаги — все, что я сделал чтобы получить информацию в dashboard.
никакой передачи в конструктор не было точно.
В случае Sentry/RavenClient — надо будет протащить IRavenClient через 10 конструкторов.
простите, вы в sentry отправляете вызывая напрямую ravenClient? оО зачем?! он же свободно перехватывает все, что поступит в него на appender в логгере, который уже есть в системе. в самих классах вообще ничего менять не требуется.
но если уж очень надо — утилитный класс со статик-полем… синглтон… варианты проектирования единого ресурса — тема любого собеседования.
Ну и как вы собрались менять имплементацию логгера флагом — если у разных логгеров разные интерфейсы?
до своих записей я увы прямо сейчас добраться не могу — закрыт evernote на работе.
но если кратко — log4j-api отдельный проект и как-то ведь живет.
указание логгера используется именно для ситуаций, когда транзитивно заходят log4j и logback, например.
это не более, чем указание Sentry "перехватывай то, что идет к logback, а не log4j". как он это будет делать — скрыто под капотом и программиста не касается.
простите, вы в sentry отправляете вызывая напрямую ravenClient?
Нет, это вы предлагаете так делать. Вижу, вы используете log4j. В таком случае возвращаю вам ваш же вопрос:
Sentry?
группирует логи. цепляется практически к любой системе логгирования. free (до определенного объема логов если не ошибаюсь, но все же). есть докер-образы. тэги. чем log4j превосходит ее?
я спросил чем NLog превосходит Sentry, а не log4j
ravenClient напрямую из кода нигде не используется.
Достаточно только пунктов installation и usage.
и уже после их выполнения все будет работать.
никаких танцев с бубном и никаких новых файлов конфига как в случае с zidium.xml.
PS Перечитайте на всякий случай список хабов в которых расположен этот пост.
есть некое приложение. оно еще мааааленькое. логов там мало. потом кто-то понимает, что если сделать его большим — оно принесет чемодан денег и начинают его выращивать.
Все это время приложение пишет логи… там много мусора. сообщения о том, что пользователь успешно авторизовался… сообщения о том, что пользователь просмотрел 101 страницу с их полным списком… все это пишется log4j-подобным логгером в файл.
приходит админ и видит этот бардак… распиливает логи по уровням — в один все, в другой debug, в третий error. дышать становится легче…
приложение растет… разлетается в инстансы облака… работает на 3-4 машинах и собирать эти файлы уже проблема…
одна и та же ошибка, на которую нет времени потому что она не критична, повторяется каждые 2 минуты и полностью забивает лог-файлы. найти в error-логе одну новую ошибку среди 200 уже известных проблема.
а потом наконец-то до кого-то доходит, что можно прикрутить Sentry.
делается это оооочень трудным путем.
— добавляется maven-зависимость
— добавляется запись о новом аппендере в файл-конфиг уже существующего и работающего логгера приложения
теперь как только что-то попадает на отправку в логи — оно идет и в файл и в Sentry.
настраиваем уровень логов для Sentry-appender (он использует ravenClient внутри себя — во всяком случае для варианта http-нотификаций.)
заходим в Sentry и видим что-то типа
(оригинал взят отсюда)
все ошибки разложены по полочкам. в любую можно зайти и посмотреть stacktrace.
отличие от log4j в том, что там просто простыня из всего подрят без какой-то группировки и типизирования. на 300 ошибок может быть 297 одного типа и 3 критических. поверьте — найти эти 3 будет нереально. в Sentry это будет всего ДВЕ строки с количеством инцидентов.
это разные инструменты. один пишет все, второй шлет нотификации на инциденты и группирует события.
это за тем же, зачем включают создания dump-файла на падении jvm. чтобы было.
при наличии и Sentry и log4j однозначно все выбирают удобную админку вместо простыни. log4j лишь страховка например на случай отсутствия сети или на момент перезапуска Sentry.
Чтож, теперь я готов ответить вам на ваш вопрос, хотя и нахожу этот ответ странным.
NLog лишь страховка например на случай отсутствия сети или на момент перезапуска Sentry.
изначальный вопрос был именно чем Nlog лучше уже имеющегося популярного решения Sentry.
сравнивать log4j и Sentry столь же корректно, как автомобиль и прицеп — функции схожие, но не тождественные. если есть возможность — лучше иметь и то и другое.
вы же не станете держать в одном проекте logback и log4j про запас? это уже практически одинаковые инструменты.
изначальный вопрос был именно чем Nlog лучше уже имеющегося популярного решения Sentry.
Хорошо, но вы же в таком случае сможете объяснить чем log4j лучше уже имеющегося популярного решения Sentry?
какая библиотека вам понравится — по алфавиту и полочкам или сваленная в кучу из самосвала посреди двора?
выше я уже отвечал на этот вопрос.
В таком случае я могу вам ответить на ваш изначальный вопрос, хоть уже и не понимаю этого ответа:
какая библиотека вам понравится — по алфавиту и полочкам или сваленная в кучу из самосвала посреди двора?
PS читая этот пост я не поленился погуглить что такое Zidium. Отвечая на ваши комментарии я не поленился погуглить что такое Sentry. Также потенциально я мог и вовсе не знать что такое log4j и "спринг" — но знал, потому что интересуюсь соседними экосистемами.
Как так вышло, что вы до сих пор не зашли в гугл и не погуглили что такое NLog?
Я уже не говорю о том, что прежде чем писать комментарии, было бы неплохо почитать пост. Там в первом же примере кода есть строчка using System;
, которая как бы намекает, что вы ошиблись хабом и задаете тут не просто глупые — а идиотские вопросы, причем уже зная на них ответы.
Или вы просто тролль, и намеренно скрываете свое знание очевидных вещей?
был такой форум… звался javatalks.
когда-то довольно активный… знаете на чем они погорели и почему от них ушла большая часть аудитории? модераторы начали вместо ответов говорить "посмотри в гугле".
зачем мне идти в поисковик, читать документацию, если меня интересует краткий ответ на мой вопрос и статья посвящена этой системе?
все что есть в этой статье в гугле есть — так может быть она и не нужна по вашей логике? если вы не можете ответить на мой вопрос — подождите пока до вопроса дойдут компетентные люди.
я погуглил что такое zidium когда стало ясно, что статья более с рекламным уклоном, нежели с желанием просвятить сообщество относительно новой альтернативной методики логгирования. честно говоря наколенная поделка на первый взгляд и интерес к нему угас как таковой. хранить логи на стороне неприемлемо от слова "совсем".
мог и вовсе не знать что такое log4j
нуууу… в этом случае и говорить не о чем. не знание азов лечится только книгами или практикой хотя бы на уровне джуна-стажера. это как человек, не знающий что-такое git.
ps не смотря на то, что вы по 2 раза задаете одни и те же вопросы — я же не считаю, что передо мной идиотские вопросы, задаваемые троллем? хотя исправляемый текст в цитатах якобы на меня наводит на такую мысль.
я ответил, что вы сравниваете два разных продукта. "зачем нам шурупы, когда есть гвозди?". действительно, а зачем? (это риторический вопрос — ответа не требуется) аналогия с машиной и прицепом очевидно слишком сложна.
в первом же примере кода есть строчка using System;
действительно редкость для кода на C++
нуууу… в этом случае и говорить не о чем. не знание азов лечится только книгами или практикой хотя бы на уровне джуна-стажера. это как человек, не знающий что-такое git.
Вот только это азы экосистемы Java, а я на C# пишу. Вам не приходило в голову что не каждый программист в мире пишет на Java?
я ответил, что вы сравниваете два разных продукта
Вообще-то, вы первым решили сравнить два разных продукта.
хотя исправляемый текст в цитатах якобы на меня наводит на такую мысль.
Проекты NLog и log4j из одной семьи и решают одни и те же задачи. От замены одного названия на другое аргумент не перестает быть корректным (или не начинает). Является ли он при этом цитатой — не так важно.
По вашему исходному вопросу. Попробую объяснить последний раз. Вот вы пишите:
простите, вы в sentry отправляете вызывая напрямую ravenClient? оО зачем?! он же свободно перехватывает все, что поступит в него на appender в логгере, который уже есть в системе. в самих классах вообще ничего менять не требуется.
То есть вы предполагете что в проекте уже есть какой-то логгер, к которому можно прицепить аппендер.
Но откуда в проекте возьмутся логгеры и аппендеры если в нем нет NLog или хотя бы log2net? NLog — это и есть та самая библиотека, которая дает программисту логгеры и аппендеры (которые называются таргетами).
Именно такую схему работы реализовывает расширение NLog для системы мониторинга Zidium.
де-факто статья не об NLog, а о его связке с zidium. здесь показано как связаться с этой тулой. но нет ни настроек уровней, ни записей в консоль/файл. по самой настройке NLog здесь нет практически НИЧЕГО.
Вообще-то, вы первым решили сравнить два разных продукта.
ложь. я попросил разницу между тем, что в статье и Sentry.
Вам не приходило в голову что не каждый программист в мире пишет на Java?
я пишу на десятке языков. C# в арсенале есть. не вижу особых проблем в понимании похожих синтаксисов.
каких-то глубоких знаний java здесь не требуется.
но для простоты я мог бы оперировать псевдоязыком — надеюсь вы не стали бы просить тогда писать строго на C#?
То есть вы предполагете что в проекте уже есть какой-то логгер, к которому можно прицепить аппендер.
если его нет, но это по всей видимости десктопный калькулятор, которому логи ни к чему. в наше время нет приложений, который зарабатывают деньги и при этом не пишут логов. так что да — я предполагаю, что система логгирования уже есть. а если ее нет — она должна быть добавлена обязательно первым же делом.
NLog — это и есть та самая библиотека, которая дает программисту логгеры и аппендеры
за это пояснение спасибо. теперь понял. а то по тону статьи выглядит так, будто это часть zidium. что-то вроде его плагинов.
полностью согласен, что я не правильно понял назначение этого инструмента и провел ложные аналогии.
а то по тону статьи выглядит так, будто это часть zidium. что-то вроде его плагинов.
полностью согласен
Вот из этого фрагмента можно было бы понять что чьим плагином тут является:
PS
нуууу… в этом случае и говорить не о чем. не знание азов лечится только книгами или практикой хотя бы на уровне джуна-стажера. это как человек, не знающий что-такое git. :-)
Вот уж чего я не ожидал от кого-то кто знает C# — так это незнания того что такое NLog.
я его использую в unity3d. там своя система логгирования.
Вот из этого фрагмента можно было бы понять что чьим плагином тут является:
разумеется. а еще можно было назвать статью как-то более четко. например, "использование zidium для логгирования ошибок". я думал это статья, а не кроссворд со скрытыми загадками.
а так сейчас поиск "логгирование с NLog" будет выводить сюда.
меня выше спрашивали про log4j. он есть. носит название log4net.
принципиальной разницы с версией java практически нет. а уж влияния на внешние инструменты тем более.
ps я так и не увидел ответа чем же Sentry хуже/лучше связки NLog + Zidium
вместо этого собеседник старается заглушить вопрос своим "а нафига вам Sentry если есть log4j?".
может быть вы ответите — а нафига нам NLog если есть log4j? аргументы "он раскладывает все по полочкам" приняты mayorovp не были.
ps я так и не увидел ответа чем же Sentry хуже/лучше связки NLog + Zidium
Пожалуйста, укажите где именно в вашем первом комментарии, где вы задавали свой вопрос, находится слово "Zidium".
из-за того, что в статье 90 процентов почему-то посвящено не настройке NLog, а Zidium, я получил ложное представление об этом инструменте. посчитал, что NLog это часть Zidium и ассоциировал их как единое целое.
на самом деле вопрос носил смысл "чем предложенное лучше/хуже Sentry". именно в таком виде. извиняюсь, что возникло недопонимание. когда говорят колесо, а тычат пальцем в телегу — не видя ранее ни того, ни другого не запутаться трудно.
плюсов я откровенно не увидел. из минусов — нет локальной версии, странные ограничения на объемы нотификаций.
я так и не увидел ответа чем же Sentry хуже/лучше связки NLog + Zidium
1) Sentry нельзя использовать прозрачно через NLog.
2) Если у вас связка log4net+Sentry, то чисто для мониторинга ошибок Sentry лучше, потому что в статистике ошибок показывает сколько пользователей затронула данная ошибка, zidium показывает только статистику ошибок.
3) Если вам хочется иметь комплексный мониторинг, то zidium поможет показать всю картину в одном окне. Zidium кроме мониторинга ошибок умеет выполнять проверки и проверять метрики. Например, мои windows-службы шлют в zidium проверку «биение-сердца». В итоге я получаю уведомления, когда случаются фатальные ошибки, или приложение упало совсем (или сервер)
Sentry нельзя использовать прозрачно через NLog.
Ну на полчаса же задача! Берется вот этот файл — https://github.com/themotleyfool/SentryAppender/blob/master/src/app/SharpRaven.Log4Net/SentryAppender.cs — и часть строк копируется в заготовку таргета из примеров NLog...
Sentry нельзя использовать прозрачно через NLog.
на самом деле можно и даже проще.
Если вам хочется иметь комплексный мониторинг, то zidium поможет показать всю картину в одном окне.
zabbix? зато платить не потребуется. и метрики и статус сервера. и локальное по без выноса непонятно к кому.
upd вижу, что у него беда с dotnet. не подойдет. но возможно все-таки есть смысл не стягивать на одну систему все?
Если меня спросят всё ли в порядке сейчас с приложением, я смогу легко ответить.
ради этого платить абонентку?
может и удобно, но есть нишевые инструменты. если zidium начинает втаскивать в себя все, что можно — возникает логичный вопрос — для этого раздувается команда или падает качество компонентов?
плохо организованная сборка метрик может откусить не малую часть ресурсов. видел проект, в котором при отключении профилирования расходы падали процентов на 15. это действительно задача на порядки сложнее, чем насобирать 100500 http запросов.
одно окно это удобно, но и 2 как-то проблем не вызовут.
если вы не можете найти ресурсов на 1 сервер — значит и приложение не такое уж огромное или просто нерентабельное.
зачем сопровождать? какой-нибудь типовой jenkins годами можно не трогать — все работает. никто не заставляет вас добавлять туда модные фичи если они не нужны.
если приложение маленькое и логов мало — разграничить по уровню можно для начала.
Sentry в бесплатном тарифе имеет ограничение One user, не получится использовать в команде.
а зачем вам несколко пользователей если приложение на 1 виртуальный сервер? логи можно и под одной учеткой смотреть. не привязывать к AD и все.
В статье это не очень подробно раскрыто, но Зидиум это не просто продвинутый лог, это мониторинг ошибок, с жизненным циклом каждой ошибки. Есть даже баг-трекер встроенный.
И всякие плюшки вроде метрик, но это уже не через NLog.
Sentry ставится на отдельный сервер. в том числе и докером. арендовать хостинг не обязательно.
встроенный баг-треккер вроде в чистой Sentry отсутствует, но вот назначить ошибку на человека и пометить ее исправленной как в типовой таске — это есть.
самой лучшей плюшкой на мой взгляд является отправка на почту нотификации по подписке, что добавилось что-то новое. не надо сидеть и обновлять страницу.
И уведомления у них тоже есть, на почту и по смс.
до 2000 ошибок в день против 10к на бесплатном тарифе в облаке и безлимите в локальной инсталяции. если все плохо — эти 2000 ошибок набегут очень быстро и всю вторую половину дня придется куковать. если все хорошо — лимит за сутки не перенесется.
установка на локальные сервера строго платная и обсуждается отдельно. для бизнеса хранение логов «где-то там» далеко не всегда подойдет.
инструмент несомненно интересный, но я даже личные приложения предпочитаю держать в максимально замкнутой системе.
2) в Sentry нет проверок
3) в Sentry нет метрик
4) в Sentry нет логов
В zidium мониторинг ошибок, проверки, метрики и логи есть «всё в одном»
или у вас хранятся все логи. а NLog просто указывает на место?
каков принцип приема данных?
при повышении требований вы отказываетесь принимать данные или выполняется доступ к приложению с его перенастройкой?
кстати, аналогичная настройка и у Sentry -там тоже можно принимать хоть все info.
выше я еще спрашивал про прожорливость админки. подскажите, пожалуйста, как система почувствует себя на микрокомпьютере с минимумом ресурсов (кто-то типа 1 core 2GHz + 256 ram)
пожалуйста, как система почувствует себя на микрокомпьютере с минимумом ресурсов (кто-то типа 1 core 2GHz + 256 ram)
Сам Зидиум в облаке, поэтому ваших ресурсов не тратит.
А адаптер логирования, думаю, потребляет минимально )
1) чтобы использовать Sentry нужно вносить изменения в код
PM > Install-Package NLog.Zidium
А это по вашему что?
3) в Sentry нет метрик
Такой-то экспешен выпал 99 раз. Пардон, но наличие сообщений в Sentry уже само по себе метрика.
4) в Sentry нет логов
У вас будет стектрейс со всеми значениями переменных + у вас есть доступ к коду. Если вы по этим компонентам не можете реконструировать что произошло, то чем вам помогут логи?
1) чтобы использовать Sentry нужно вносить изменения в код
PM > Install-Package NLog.Zidium
А это по вашему что?
это удобный способ установить нужные библиотеки, вы их можете и руками положить в папку с приложением если хотите. Можно даже подключиться к чужому приложению, где у вас нет исходных кодов
3) в Sentry нет метрик
Такой-то экспешен выпал 99 раз. Пардон, но наличие сообщений в Sentry уже само по себе метрика.
Имелись ввиду произвольные метрики. Sentry уведомит вас, если на диске осталось мало места? В zidium можно отправлять любые метрики и настроить пороговые значения для их контроля.
Поставим вопрос по-другому, чем Install-Package NLog.Zidium
принципиально отличается от Install-Package NLog.Targets.Sentry
? Почему первое не считается внесением изменений в код — а второе считается?
кроме того, этот ключ вероятнее всего можно поменять «на лету».
да и максимум, что уронят — систему логгирования на стороннем сервере. само приложение от этого не пострадает. (если, конечно, его не держат там же, где и отладочный инструментарий)
ключ может быть и в переменных окружения. (не знаю есть ли реально такая возможность у NLog)
его назначение только одно — чтобы система логгирования "узнала" отправителя.
какого-либо доступа он не даст.
Расширение NLog для мониторинга ошибок