Как устроить "debug-mode" в программе?

57 views
Skip to first unread message

Inno Bragovich

unread,
Mar 25, 2018, 4:37:13 AM3/25/18
to Golang Russian
Привет!

Коллеги, вопрос про то, как правильно организовать режим использования программы, когда мы хотим чтобы она выводила или не выводила какую-либо отладочную информацию?

Пока программа была маленькая, я делал так, но у меня теперь весь код покрыт этой хренью:

import "runtime/debug"

var mode string = "debug" // или не-debug

func main(){

//......

result, err := myFunc()
if mode == "debug"  {
    fmt.Println("result is:", result)
    if err !=nil {
        fmt.Println(err, string(debug.Stack()))
    }
}

//......

}




CynicRus

unread,
Mar 25, 2018, 4:43:50 AM3/25/18
to Golang Russian
Я бы вынес это в отдельную функцию debug, куда бы выбросил printf() с %v и имя функции. А внутри бы уже проверял if debugMode{
 тут код выхлопа в консоль и т.д.}. А по коду бы уже разбрасывал вызовы этого debug(obj, funcname,etc,etc). Оверхед на вызов лишней функции небольшой конечно есть - но избавляет от портянок дебаг кода-)

воскресенье, 25 марта 2018 г., 11:37:13 UTC+3 пользователь Inno Bragovich написал:

Inno Bragovich

unread,
Mar 25, 2018, 4:57:40 AM3/25/18
to Golang Russian
Про то что нужно этот if вынести - согласен, это-то понятно. Вот вопрос, скорее, в том, как минимизировать вредное влияние того, что каждая третья строчка будет с debug.
Если пользователь хочет иметь такую опцию - включать дебаг моде, то вопрос в том нет ли каких-то более умных путей - например, если дебаг-моде выключено, то чтобы программа не только не выполняла функцию дебага, но и вообще не проверяла каждый раз, где эта функция встречается - есть дебаг моде или нету, а проверила один раз - при старте программы. Вот как-то так бы хотелось ))

Виталий Осипов

unread,
Mar 25, 2018, 6:26:21 AM3/25/18
to Golang Russian
Используй пакет log, создай Logger который будет делать вывод в io.Writer и в зависимости от режима назначай его или /dev/null или что хочешь - как вариант

воскресенье, 25 марта 2018 г., 13:37:13 UTC+5 пользователь Inno Bragovich написал:

Inno Bragovich

unread,
Mar 25, 2018, 6:39:41 AM3/25/18
to Golang Russian
Не очень понял идею
Как это избавит от кучи проверок в коде на предмет того - какой сейчас режим?

CynicRus

unread,
Mar 25, 2018, 7:04:03 AM3/25/18
to Golang Russian
А никак не избавит, учитывая что нет препроцессора. Так что, судя по докам от проверок не избавится, но можно записать лаконично.

воскресенье, 25 марта 2018 г., 13:39:41 UTC+3 пользователь Inno Bragovich написал:

Daniel Podolsky

unread,
Mar 25, 2018, 8:29:55 AM3/25/18
to gola...@googlegroups.com
Я бы взял логгер с поддержкой уровней логирования. Structlog или logrus

--
Вы получили это сообщение, поскольку подписаны на группу "Golang Russian".
Чтобы отменить подписку на эту группу и больше не получать от нее сообщения, отправьте письмо на электронный адрес golang-ru+...@googlegroups.com.
Чтобы настроить другие параметры, перейдите по ссылке https://groups.google.com/d/optout.

Inno Bragovich

unread,
Mar 25, 2018, 11:48:51 AM3/25/18
to Golang Russian

On Sunday, March 25, 2018 at 2:04:03 PM UTC+3, CynicRus wrote:
А никак не избавит, учитывая что нет препроцессора. Так что, судя по докам от проверок не избавится, но можно записать лаконично.

Ну, тогда, наверное, так и сделаю.. Хотя вопрос может стать ребром, если такая нзадача будет в хайлоад проекте. 

On Sunday, March 25, 2018 at 3:29:55 PM UTC+3, Daniel Podolsky wrote:
Я бы взял логгер с поддержкой уровней логирования. Structlog или logrus
 
Спасибо, погляжу

Виталий Осипов

unread,
Mar 25, 2018, 11:07:06 PM3/25/18
to Golang Russian
не надо никаких проверок, просто в зависимости от режима сообщение выводится в /dv/nill или например в stdout

воскресенье, 25 марта 2018 г., 15:39:41 UTC+5 пользователь Inno Bragovich написал:

Alex Lurye

unread,
Mar 26, 2018, 2:35:53 AM3/26/18
to gola...@googlegroups.com
Проверки, нужны, конечно. Eсли для того, чтобы сформировать строчку для лога, надо какие-то нетривиальные вычисления провести, то лучше их вообще не делать, если заранее ясно, что в лог она не попадёт. В Google мы используем другой модуль для логгирования: https://godoc.org/github.com/golang/glog. Чем он хорош - можно делать проверку уровня логгирования при помощи встроенных средств:

if glog.V(2) {
	glog.Info("Starting transaction...")
}

glog.V(2).Infoln("Processed", nItems, "elements")

Соответственно, программа принимает специальные флаги командной строки, чтобы регулировать детализацию логов:

-v=0
	Enable V-leveled logging at the specified level.
-vmodule=""
	The syntax of the argument is a comma-separated list of pattern=N,
	where pattern is a literal file name (minus the ".go" suffix) or
	"glob" pattern and N is a V level. For instance,
		-vmodule=gopher*=3
	sets the V level to 3 in all Go files whose names begin "gopher".

Inno Bragovich

unread,
Mar 26, 2018, 4:40:28 AM3/26/18
to Golang Russian

On Monday, March 26, 2018 at 6:07:06 AM UTC+3, Виталий Осипов wrote:
не надо никаких проверок, просто в зависимости от режима сообщение выводится в /dv/nill или например в stdout


Значит, я вас не понимаю. Как он будет осуществлять вывод туда или туда, если он не будет проверять - какой сейчас режим? 





On Monday, March 26, 2018 at 9:35:53 AM UTC+3, Alex Lurye wrote:
Проверки, нужны, конечно. Eсли для того, чтобы сформировать строчку для лога, надо какие-то нетривиальные вычисления провести, то лучше их вообще не делать, если заранее ясно, что в лог она не попадёт. В Google мы используем другой модуль для логгирования: https://godoc.org/github.com/golang/glog. Чем он хорош - можно делать проверку уровня логгирования при помощи встроенных средств:

if glog.V(2) {
	glog.Info("Starting transaction...")
}

glog.V(2).Infoln("Processed", nItems, "elements")

Соответственно, программа принимает специальные флаги командной строки, чтобы регулировать детализацию логов:

-v=0
	Enable V-leveled logging at the specified level.
-vmodule=""
	The syntax of the argument is a comma-separated list of pattern=N,
	where pattern is a literal file name (minus the ".go" suffix) or
	"glob" pattern and N is a V level. For instance,
		-vmodule=gopher*=3
	sets the V level to 3 in all Go files whose names begin "gopher".
Хм, интересная штучка, спасибо, погляжу.
Всё равно остаётся вопрос - в хайловд проектах  - вот этот ваш код 
if glog.V(2) {
       
...
}

он не будет тормозить, если эти ифы расставить через каждые три строчки?

Daniel Podolsky

unread,
Mar 26, 2018, 4:43:02 AM3/26/18
to gola...@googlegroups.com
> он не будет тормозить, если эти ифы расставить через каждые три строчки?

тормозить он будет на логах, а не на ифах

Alex Lurye

unread,
Mar 26, 2018, 4:46:18 AM3/26/18
to gola...@googlegroups.com
Да, glog.V(2) это очень дешёвая проверка, её можно смело в продакшне оставлять. А вот если в оном продакшне что-то заглючит, вот тогда можно быстренько включить и посмотреть, что там происходит.

On Mon, Mar 26, 2018 at 10:43 AM Daniel Podolsky <onok...@gmail.com> wrote:
> он не будет тормозить, если эти ифы расставить через каждые три строчки?

тормозить он будет на логах, а не на ифах

--
Вы получили это сообщение, поскольку подписаны на группу Golang Russian.

Чтобы отменить подписку на эту группу и больше не получать от нее сообщения, отправьте письмо на электронный адрес golang-ru+...@googlegroups.com.
Настройки подписки и доставки писем: https://groups.google.com/d/optout.

Daniel Podolsky

unread,
Mar 26, 2018, 5:03:16 AM3/26/18
to gola...@googlegroups.com
> Да, glog.V(2) это очень дешёвая проверка, её можно смело в продакшне
> оставлять. А вот если в оном продакшне что-то заглючит, вот тогда можно
> быстренько включить и посмотреть, что там происходит.

ужас в том, что воспроизвести ошибку очень трудно, и, соответственно,
иметь динамический уровень логов для прода довольно бессмысленно.

писать надо все, просто не писать лишнего

Alex Lurye

unread,
Mar 26, 2018, 5:15:01 AM3/26/18
to gola...@googlegroups.com
Какие уровни для чего использовать - это отдельный вопрос. Error/Warning/Info - тут всё просто, они должны быть дешёвыми, включенными всегда и использоваться по назначению.

А V-логи, о которых речь (см. вопрос топикстартера с дампом стека) - это самые подробные, типа "вошёл в функцию", "вышел из функции", "началась новая итерация цикла по юзерам", "стектрейс, если функцию позвали с пустым аргументом" и т.д. Вот их точно надо делать отключаемыми, причём можно по уровням сделать - каждый следующий более подробный, чем предыдущий. Вплоть до полного дампа всех принятых и переданных данных.

Daniel Podolsky

unread,
Mar 26, 2018, 6:59:24 AM3/26/18
to gola...@googlegroups.com
в общем, две вещи очень печальны с обработкой ошибок в go

1. у ошибок отсутствует типизация. если тебе приехала ошибка - в общем
случае ты можешь анализировать ее текст. а больше у нее и нет ничего.

2. у ошибок отсутствует stack trace. в результате, если ты хочешь
понять где именно возникла ошибка и откуда приехал вызов, который ее
вызвал - ты вынужден каждый раз, получив ошибку, ее логировать.

но ужа как есть



2018-03-26 12:14 GMT+03:00 'Alex Lurye' via Golang Russian
<gola...@googlegroups.com>:

Alex Lurye

unread,
Mar 26, 2018, 7:08:19 AM3/26/18
to gola...@googlegroups.com
1. Типизацию зачастую можно устроить. error - это всего лишь интерфейс, и его можно реализовать типами данных с дополнительной структурированной информацией. Структурированная информация потеряется, если ошибку возвращать не как она есть, а через fmt.Errorf("backend failed: %v", err) - надо этого избегать по возможности, но если исходная ошибка достаточно подробная, то такие аннотации через fmt.Errorf не особо и нужны. Впрочем я бы не отказался от каких-то более удобных инструментов возврата структурированной информации вместе с ошибками. grpc/status - отличная штука для кодов. Её бы ещё дополнить для других типов. Может разработчики чем-нибудь разродятся.

2. Ни разу не было нужно :) Обычно из ошибки и так всё понятно.

Daniel Podolsky

unread,
Mar 26, 2018, 7:53:20 AM3/26/18
to gola...@googlegroups.com
> 1. Типизацию зачастую можно устроить.

устраивать можно что угодно. но, если тебе возвращают error - уверен
ты можешь быть только в том, что ты сможешь получить строку. все.


> 2. Ни разу не было нужно :) Обычно из ошибки и так всё понятно.

ну вот у меня есть некий метод, который делает некую математику. и
метод этот может вернуть error "входные данные не годятся". выяснить,
в какой именно части программы были порождены те самые неверные данные
без стектрейса невозможно.

Виталий Осипов

unread,
Mar 26, 2018, 11:50:35 PM3/26/18
to Golang Russian
при запуске программы происходит установка режима, т.е. если debug == true, io.Write устанавливаешь в какой-нибудь файл, иначе в /dev/null
и не надо делать проверку, или пишешь в выходной файл, или в никуда.
можешь сделать несколько logger для различных режимов и в коде будет что-то типа такого:

...
logger.Debug.Print(err)
...
logger.Info.Print("...")
...

понедельник, 26 марта 2018 г., 13:40:28 UTC+5 пользователь Inno Bragovich написал:

Alex Lurye

unread,
Mar 27, 2018, 3:00:51 AM3/27/18
to gola...@googlegroups.com
Проблема с таким подходом будет, если подготовка данных для логгирования - дорогая операция.

log.Debug.Print(collectAllDebuggingInformation())

Программа выполнит collectAllDebuggingInformation вне зависимости от того, куда пойдет лог.

Евгений Д

unread,
Mar 29, 2018, 4:33:52 AM3/29/18
to Golang Russian
поддержу
достаточно уже оверхеда на форматирование, чтобы так не делать

вторник, 27 марта 2018 г., 14:00:51 UTC+7 пользователь Alex Lurye написал:
Reply all
Reply to author
Forward
0 new messages