Правильный подход к глобальной регистрации в Golang


120

Каков шаблон для входа в приложение в Go? Если у меня есть, скажем, 5 горутин, из которых мне нужно выйти, я должен ...

  • Создать сингл log.Loggerи распространить его?
  • Передать указатель на это log.Logger?
  • Должна ли каждая горутина или функция создавать регистратор?
  • Должен ли я создавать регистратор как глобальную переменную?

Ответы:


59
  • Создать единый log.Logger и передать его?

Это возможно. Log.Logger может использоваться одновременно из нескольких goroutines.

  • Передать указатель на этот журнал.

log.New возвращает a, *Loggerкоторый обычно указывает на то, что вы должны передать объект как указатель. Передача его в качестве значения создаст копию структуры (т.е. копию Logger), а затем несколько горутин могут одновременно писать в один и тот же io.Writer . Это может быть серьезной проблемой, в зависимости от реализации писателя.

  • Должна ли каждая горутина или функция создавать регистратор?

Я бы не стал создавать отдельный логгер для каждой функции или горутины. Горутины (и функции) используются для очень легких задач, которые не оправдывают обслуживание отдельного регистратора. Вероятно, будет хорошей идеей создать регистратор для каждого более крупного компонента вашего проекта. Например, если ваш проект использует службу SMTP для отправки писем, создание отдельного регистратора для почтовой службы кажется хорошей идеей, так что вы можете фильтровать и отключать вывод отдельно.

  • Должен ли я создавать регистратор как глобальную переменную?

Это зависит от вашего пакета. В предыдущем примере почтовой службы, вероятно, было бы хорошей идеей иметь по одному регистратору для каждого экземпляра вашей службы, чтобы пользователи могли регистрировать сбои при использовании почтовой службы gmail иначе, чем сбои, которые произошли при использовании локального MTA (например, sendmail ).


37

Для простых случаев существует глобальный регистратор, определенный в пакете журнала log.Logger. Этот глобальный регистратор можно настроить через log.SetFlags.

После этого можно просто вызвать функции верхнего уровня пакета журнала, такие как log.Printfи log.Fatalf, которые используют этот глобальный экземпляр.


Думал, что вы можете установить флаги, вы не можете использовать собственный регистратор.
0xcaff

@caffinatedmonkey на самом деле, вы можете использовать собственные регистраторы, если они реализуют io.Writerинтерфейс, и вы изменяете вывод регистратора по умолчанию через SetOutput().
congusbongus

16

Это простой регистратор

package customlogger

import (
    "log"
    "os"
    "sync"
)

type logger struct {
    filename string
    *log.Logger
}

var logger *logger
var once sync.Once

// start loggeando
func GetInstance() *logger {
    once.Do(func() {
        logger = createLogger("mylogger.log")
    })
    return logger
}

func createLogger(fname string) *logger {
    file, _ := os.OpenFile(fname, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0777)

    return &logger{
        filename: fname,
        Logger:   log.New(file, "My app Name ", log.Lshortfile),
    }
}

Вы можете использовать это таким образом

package main

import (
    "customlogger"
    "fmt"
    "net/http"
)

func main() {
    logger := customlogger.GetInstance()
    logger.Println("Starting")

    http.HandleFunc("/", sroot)
    http.ListenAndServe(":8080", nil)
}

func sroot(w http.ResponseWriter, r *http.Request) {
    logger := customlogger.GetInstance()

    fmt.Fprintf(w, "welcome")
    logger.Println("Starting")
}

10

Я знаю, что этот вопрос немного устарел, но если, как и я, ваши проекты состоят из нескольких файлов меньшего размера, я голосую за ваш 4-й вариант - я создал его, logger.goкоторый является частью пакета main. Этот файл go создает регистратор, назначает его файлу и предоставляет его остальной части main. Обратите внимание: я не придумал изящного способа закрыть журнал ошибок ...

package main

import (
    "fmt"
    "log"
    "os"
)

var errorlog *os.File
var logger *log.Logger

func init() {
    errorlog, err := os.OpenFile(logfile,  os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
    if err != nil {
        fmt.Printf("error opening file: %v", err)
        os.Exit(1)
    }

    logger = log.New(errorlog, "applog: ", log.Lshortfile|log.LstdFlags)
}

8
Для плавного закрытия вы могли бы, вероятно, defer errorlog.Close()в конце выполнения или, чтобы лучше гарантировать его закрытие, настроить обработчики сигналов с помощью пакета сигналов Go golang.org/pkg/os/signal
Anfernee

4

Это старый вопрос, но я хотел бы предложить использовать http://github.com/romana/rlog (который мы разработали). Он настраивается с помощью переменных среды, объект регистратора создается и инициализируется при импорте rlog. Следовательно, нет необходимости передавать регистратор.

rlog имеет несколько функций:

  • Полностью настраиваемые метки даты и времени
  • Одновременный вывод в stderr или stdout, а также в файл.
  • Стандартные уровни журнала (отладка, информация и т. Д.), А также свободно настраиваемое многоуровневое ведение журнала.
  • По запросу ведение журнала информации о вызывающем абоненте (файл, номер строки, функция).
  • Возможность устанавливать разные уровни журнала для разных исходных файлов.

Он очень маленький, не имеет внешних зависимостей, кроме стандартной библиотеки Golang и активно развивается. Примеры представлены в репо.


3
Благодарим вас за раскрытие вашей связи с продуктом, который вы рекомендуете! Это ценится.
Роберт Колумбия

2

Я обнаружил, что пакет журнала по умолчанию ( https://golang.org/pkg/log/ ) несколько ограничивает. Например, нет поддержки журналов информации и отладки.
После некоторых поисков остановился на https://github.com/golang/glog . Это похоже на порт https://github.com/google/glog и дает приличную гибкость при ведении журнала. Например, при локальном запуске приложения вам может потребоваться журнал уровня DEBUG, но вы можете захотеть запустить только на уровне INFO / ERROR в производстве. Список полных функций / руководство находится здесь https://google-glog.googlecode.com/svn/trunk/doc/glog.html (это для модуля c ++, но по большей части переводится на порт golang)


0

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

klog является форком glog и преодолевает следующие недостатки

  • glog представляет множество ошибок и проблем в контейнерных средах, все из которых плохо документированы.
  • glog не предоставляет простой способ тестирования журналов, что снижает стабильность программного обеспечения, использующего его
  • glog основан на C ++, а klog - это чистая реализация golang

Пример реализации

package main

import (
    "flag"

    "k8s.io/klog"


)

type myError struct {
    str string
}

func (e myError) Error() string {
    return e.str
}

func main() {
    klog.InitFlags(nil)
    flag.Set("v", "1")
    flag.Parse()

    klog.Info("hello", "val1", 1, "val2", map[string]int{"k": 1})
    klog.V(3).Info("nice to meet you")
    klog.Error(nil, "uh oh", "trouble", true, "reasons", []float64{0.1, 0.11, 3.14})
    klog.Error(myError{"an error occurred"}, "goodbye", "code", -1)
    klog.Flush()
}
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.