Как заставить программу Golang печатать номер строки только что вызванной ошибки?

96

Я пытался выдать ошибки в моей программе Golang, log.Fatalно log.Fatalне печатал строку, в которой log.Fatalбыл запущен. Нет ли возможности получить доступ к номеру строки, которая называется log.Fatal? т.е. есть ли способ получить номер строки при выдаче ошибки?

Я пытался погуглить, но не знал, как это сделать. Лучшее, что я мог получить, - это распечатать трассировку стека , что, я думаю, хорошо, но может быть слишком много. Я также не хочу писать debug.PrintStack()каждый раз, когда мне нужен номер строки, я просто удивлен, что нет встроенной функции для этого типа log.FatalStackTrace()или чего-то, что не является костюмом.

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

"А, хорошо, значит, он выдает ошибку и выполняет X ..."

Чем меньше людей узнают о моем коде, тем лучше :)

Пиноккио
источник
В тот момент, когда вы печатаете номера строк, это означает, что мне придется погрузиться в ваш код, поэтому вопрос «Чем меньше люди узнают о моем коде, тем лучше» здесь спорный. Вам следует иметь четкие и краткие ошибки.
Wessie

Ответы:

124

Вы можете установить флаги либо в пользовательском Регистраторе, либо по умолчанию, чтобы включить LlongfileилиLshortfile

// to change the flags on the default logger
log.SetFlags(log.LstdFlags | log.Lshortfile)
JimB
источник
Итак, чтобы это сработало, мне нужно только установить это в верхней части одного из файлов пакета, и он будет доступен для всех моих файлов для этого пакета?
Пиноккио
4
Да, если вы используете собственный журнал, вы можете использовать его как var mylog = log.New(os.Stderr, "app: ", log.LstdFlags | log.Lshortfile).
OneOfOne
мне действительно нужно создавать переменную? Я не могу просто сделать log.SetFlags (log.LstdFlags | log.Lshortfile) в верхней части моего файла go? Получаю ошибку: expected declaration, found 'INDENT' logпри попытке сделать log.SetFlags(log.LstdFlags | log.Lshortfile). Меня просто раздражает необходимость создавать для него переменную, почему не может быть log.Fatal("string", log.Flag). Но создание нового журнала переменных сработало. Стандартно ли создавать переменные журнала и прочее?
Пиноккио
3
@Pinocchio: эта ошибка связана с тем, что Go недействителен, у вас не может быть простого вызова функции на верхнем уровне. Поместите его в init () или в другую точку входа.
JimB
5
вы должны вставить этоfunc init() {}
OneOfOne
94

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

func HandleError(err error) (b bool) {
    if err != nil {
        // notice that we're using 1, so it will actually log where
        // the error happened, 0 = this function, we don't want that.
        _, fn, line, _ := runtime.Caller(1)
        log.Printf("[error] %s:%d %v", fn, line, err)
        b = true
    }
    return
}

//this logs the function name as well.
func FancyHandleError(err error) (b bool) {
    if err != nil {
        // notice that we're using 1, so it will actually log the where
        // the error happened, 0 = this function, we don't want that.
        pc, fn, line, _ := runtime.Caller(1)

        log.Printf("[error] in %s[%s:%d] %v", runtime.FuncForPC(pc).Name(), fn, line, err)
        b = true
    }
    return
}

func main() {
    if FancyHandleError(fmt.Errorf("it's the end of the world")) {
        log.Print("stuff")
    }
}

playground

OneOfOne
источник
11
Хотя уже данный ответ аккуратно устраняет проблему, ваше решение предупредило меня о существовании чего-то удивительного - пакета времени выполнения! Замечательные
Гвинет Ллевелин,
fnПеременной присваивается от runtime.Caller()фактически имя файла, а не функция отсчета. Я считаю fn функцией, а не именем файла .
sshow
1
Потрясающие! Спасибо. Это отличный пример runtimeиспользования пакета. Очень полезно для отладки через журналы.
18 августа,
2

Если вам нужна именно трассировка стека, взгляните на https://github.com/ztrue/tracerr

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

Вот пример кода:

package main

import (
    "io/ioutil"
    "github.com/ztrue/tracerr"
)

func main() {
    if err := read(); err != nil {
        tracerr.PrintSourceColor(err)
    }
}

func read() error {
    return readNonExistent()
}

func readNonExistent() error {
    _, err := ioutil.ReadFile("/tmp/non_existent_file")
    // Add stack trace to existing error, no matter if it's nil.
    return tracerr.Wrap(err)
}

И вот результат: трассировка стека ошибок golang

Йохан
источник