Правильний підхід до глобальної реєстрації в Голангу


119

Який шаблон для входу в програму Перейти? Якщо у мене, скажімо, 5 городин, з яких мені потрібно увійти, чи варто ...

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

Відповіді:


59
  • Створіть єдиний журнал. Запустіть і передайте його навколо?

Це можливо. 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що є частиною основного пакету. Цей файл go створює реєстратор, призначає його файлу та надає його решті основного. Зауважте, я не придумав витонченого способу закрити журнал помилок ...

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()в кінці виконання або, щоб краще забезпечити його закриття, налаштуйте обробники сигналів, використовуючи пакет сигналу 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 і дає гідну гнучкість в журналі. Наприклад, при запуску програми локально, ви можете зажадати журнал рівня ДЕБУГ, але, можливо, потрібно запускати лише на рівні INFO / ПОМИЛКА у виробництві. Список повних функцій / довідника є тут https://google-glog.googlecode.com/svn/trunk/doc/glog.html (його використовується для модуля c ++, але здебільшого перекладається на порт golang)


0

Один з модулів реєстрації даних, який ви можете врахувати, - це klog . Він підтримує 'V' ведення журналів, що надає можливість вести журнал на певному рівні

klog - це вилка glog і долає наступні недоліки

  • glog представляє багато "gotchas" і представляє виклики в контейнерних середовищах, які все не добре зафіксовані.
  • glog не забезпечує простий спосіб тестування журналів, що погіршує стабільність використання програмного забезпечення
  • glog заснований на C ++, а klog - це чиста голанг-реалізація

Реалізація зразка

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.