Мне кажется термин «ошибка» слишком широко трактуется, следует разделять error/bug.
Разработчики допускают ошибки(bugs) при написании кода и в программе затаивается дефект. И даже если дефект не нашли и о нем никто не знает, он все равно есть! Сидит и ждет своего часа. И когда пользователь натыкается на ошибочный код, происходит сбой(error).
Мы сообщаем пользователю о чем? О сбое. Сбой может быть восстановимым или фатальным.
При восстановимом сбое, пользователь например получит сообщение «Устройство не отвечает на запрос», пользователь сам решает, что он будет делать при этом сбое.
Стоит ли это писать в лог? Ведь это нормальное поведение программы, этот сбой обрабатывается, пользователь видит сообщение на экране.
Я считаю, в лог тут эту ошибку нужно писать через Debug, и в продакшн их быть не должно, при отладке, да они должны быть. Пользуясь структурированным логгировнием, я бы записал «type=ProcessFlowError processFlow=DeviceConnect и сообщение».
Как это записать в лог — решение разработчика. Кто-то может запишет как уровень Info, а кто-то Warn, а кто-то Error. Это уже детали реализации.
О фатальном сбое нам сообщает вылет/неожиданное завершение работы программы. Мы увидим или сообщение в консоли, или в логе «Неожиданное завершение программы: <тут стек>»
Как вы разделяете, о какой ошибке нужно уведомлять разработчика, а о какой пользователя, или того и другого?
Получается одна ошибка — для пользователя должна сообщить, что не так, а пользователь решит как исправить. Вторая, мне кажется, должна зваться «необработанная ошибка», т.к. эта ситуация, которую программист не обрабатывал (тут должен быть стек скорее всего).
Что тогда третий тип ошибок?
Вы логируете ошибку, если отправляете ее в ответе на запрос? Мне кажется нет, тут как раз пользователь решает, как ее обработать, а не код программы.
Что тогда ошибка, которую не показывают пользователю, но пишут в лог?
Если ошибка записана в лог, а после этого не произведено действий по ее исправлению — значит это и есть обработка ошибки. Ведь если мы более ничего с ней не делаем, значит нам этого достаточно, а сам факт такой обработки ошибки означает, как мне кажется, что действия по ее исправлению должны ложиться не на программу (на другую систему или на пользователя).
Как думаете? Интересно этот момент обсудить.
С уровнями тут кстати все в порядке. Т.к. интерфейс это абстракция, то автор и не говорит о деталях реализации.
Я это понял также как разработчик и logr — он сделал Info и Error, и немного ушел от идей Дейва, объяснил он это так:
Info логи это то что вы хотите показать пользователю, и что не является ошибкой. А ошибки, то ошибки. Если ваш код получил ошибку из подфункуции записывает эту ошибку в лог и не возвращает ее вызвавшему — используйте error логи.
Автор разработчик одного из наипопулярнейших пакетов для работы с ошибками github.com/pkg/errors
Судить о его понимании можно по его работам на github.
Это так, вы верно подметили. Я думаю этот подход имеет место быть. Но когда знаешь, что проект будет сделан единожды или он простой и небольшой, то смысла во всех этих абстракциях нет, оно лишь отвлекает от дела.
Но на крупном проекте, я думаю, это окупит накладные расходы на этот подход.
Но не к логированию, т.к. Go 2 еще не вышел — этот вопрос еще окончательно не решен.
Вы бы согласились обменять синтаксический сахар различных логгеров на такой простой интерфейс ради слабой связанности?
Понятно что программ из одного файла этот вопрос мало относится, речь идет о средних и крупных многокомпонентный системах.
Когда вся программа накрылась, код возврата говорит лишь о том, что случилась ошибка, причину ошибки логично поместить в лог с уровнем fatal.
Автор об этом случае пишет так:
Если возникает ошибка, которая не может быть обработана, она должна появиться в main.main там, где программа завершается. Незначительные неудобства, связанные с необходимостью вставки префикса FATAL перед окончательным сообщением журнала или записи непосредственно в os.Stderr с помощью fmt.Fprintf, не является достаточным основанием для расширения пакета матодом log.Fatal.
Лично мне больше нравится подход logr и go-kit-log, там тоже есть интерфейс логгера сильно ограниченный. В logr есть вывод с уровнем важности, V(verboseLevel int), чем больше verboseLevel тем ниже важность сообщения и больше сообщений у вас в логе.
В go-kit-log никто не запрещает вам указывать название уровней для записи журнала, но вы это передаете как параметр вызова.
Интерфейс тут такой:
type Logger interface {
Log(keyvals ...interface{}) error
}
Ваша реализация сама разберется как это записать в хранилище.
И кстати тут автор не уточняет какова должна быть реализации, а вот некоторые разработчики пошли по его идеям. Автор github.com/go-log/log сделал интерфейс, как пишет Дейв.
Конечно автор идет на пролом к идеальному, но по факту в реализации разработчики понимают эту идею, как намеренное сокращение интерфейса в угоду слабой связанности.
Посмотрите на реализацию логера в Go Kit, описанную в данной презентации speakerdeck.com/chrishines/go-kit-log-package возможно это внесет больше ясности.
Заметьте автор — Дейв Чини, достаточно опытный разработчик, много его пакетов используется большинством разработчиков, например, github.com/pkg/errors. Думаю, он имеет причины к такому подходу.
Товарищи, не забывайте — это перевод. Прошу вас минусы аргументировать, мне это важно, чтобы пронять как люди относятся к данной идее Дейва Чини (автор известного модуля github.com/pkg/errors).
А если отбросить евангелистическую подачу материала, то о solid можно и не говорить вовсе. Лучше бы на примере показали как этому следовать в go. Я вот форк https://github.com/r3code/clean-go сделал от clean-go чтобы посмотреть инверсию зависимости, тут как раз есть части solid.
Отлично когда человек смог решить проблему в поставленных рамках. Большинство комментариев тут выглядят как комментарии на форумах по программированию, когда человек спрашивает "как такое сделать на таком то языке", а в ответ получает "лучше возьми вот этот язык он круче и лучше и быстро сделай".
testbase.ru/235
Мы сообщаем пользователю о чем? О сбое. Сбой может быть восстановимым или фатальным.
При восстановимом сбое, пользователь например получит сообщение «Устройство не отвечает на запрос», пользователь сам решает, что он будет делать при этом сбое.
Стоит ли это писать в лог? Ведь это нормальное поведение программы, этот сбой обрабатывается, пользователь видит сообщение на экране.
Я считаю, в лог тут эту ошибку нужно писать через Debug, и в продакшн их быть не должно, при отладке, да они должны быть. Пользуясь структурированным логгировнием, я бы записал «type=ProcessFlowError processFlow=DeviceConnect и сообщение».
Как это записать в лог — решение разработчика. Кто-то может запишет как уровень Info, а кто-то Warn, а кто-то Error. Это уже детали реализации.
О фатальном сбое нам сообщает вылет/неожиданное завершение работы программы. Мы увидим или сообщение в консоли, или в логе «Неожиданное завершение программы: <тут стек>»
Получается одна ошибка — для пользователя должна сообщить, что не так, а пользователь решит как исправить. Вторая, мне кажется, должна зваться «необработанная ошибка», т.к. эта ситуация, которую программист не обрабатывал (тут должен быть стек скорее всего).
Что тогда третий тип ошибок?
Что тогда ошибка, которую не показывают пользователю, но пишут в лог?
Как думаете? Интересно этот момент обсудить.
Я это понял также как разработчик и logr — он сделал Info и Error, и немного ушел от идей Дейва, объяснил он это так:
Судить о его понимании можно по его работам на github.
Но на крупном проекте, я думаю, это окупит накладные расходы на этот подход.
См. пример speakerdeck.com/chrishines/go-kit-log-package?slide=18
Пример вывода в разных форматах speakerdeck.com/chrishines/go-kit-log-package?slide=20
Вы бы согласились обменять синтаксический сахар различных логгеров на такой простой интерфейс ради слабой связанности?
Понятно что программ из одного файла этот вопрос мало относится, речь идет о средних и крупных многокомпонентный системах.
Автор об этом случае пишет так:
Лично мне больше нравится подход logr и go-kit-log, там тоже есть интерфейс логгера сильно ограниченный. В logr есть вывод с уровнем важности, V(verboseLevel int), чем больше verboseLevel тем ниже важность сообщения и больше сообщений у вас в логе.
В go-kit-log никто не запрещает вам указывать название уровней для записи журнала, но вы это передаете как параметр вызова.
Интерфейс тут такой:
Ваша реализация сама разберется как это записать в хранилище.
Конечно автор идет на пролом к идеальному, но по факту в реализации разработчики понимают эту идею, как намеренное сокращение интерфейса в угоду слабой связанности.
Посмотрите на реализацию логера в Go Kit, описанную в данной презентации speakerdeck.com/chrishines/go-kit-log-package возможно это внесет больше ясности.
Какое то перечисление технологий и все. Нудно. А выставка рекламы в начале просто как бельмо.
А если отбросить евангелистическую подачу материала, то о solid можно и не говорить вовсе. Лучше бы на примере показали как этому следовать в go. Я вот форк https://github.com/r3code/clean-go сделал от clean-go чтобы посмотреть инверсию зависимости, тут как раз есть части solid.
Отлично когда человек смог решить проблему в поставленных рамках. Большинство комментариев тут выглядят как комментарии на форумах по программированию, когда человек спрашивает "как такое сделать на таком то языке", а в ответ получает "лучше возьми вот этот язык он круче и лучше и быстро сделай".