N
N
NOONE2019-07-30 18:20:19
go
NOONE, 2019-07-30 18:20:19

How to properly declare Logger in golang?

Hello, the question is of the following nature, there is a software, it needs to log various scenarios of its behavior, for this I chose logrus, I
wrote the logging format in a separate file, called it Logger.go, it looks like this

package controllers

import (
  "fmt"
  "io"
  "os"
  "strings"

  "github.com/sirupsen/logrus"
)

type myFormatter struct {
  logrus.TextFormatter
}

func (f *myFormatter) Format(entry *logrus.Entry) ([]byte, error) {
  // this whole mess of dealing with ansi color codes is required if you want the colored output otherwise you will lose colors in the logrus levels
  var levelColor int
  switch entry.Level {
  case logrus.DebugLevel, logrus.TraceLevel:
    levelColor = 31 // gray
  case logrus.WarnLevel:
    levelColor = 33 // yellow
  case logrus.ErrorLevel, logrus.FatalLevel, logrus.PanicLevel:
    levelColor = 31 // red
  default:
    levelColor = 36 // blue
  }
  return []byte(fmt.Sprintf("[%s] - \x1b[%dm%s\x1b[0m - %s\n", entry.Time.Format(f.TimestampFormat), levelColor, strings.ToUpper(entry.Level.String()), entry.Message)), nil
}
func Logger() *logrus.Logger {
  f, _ := os.OpenFile("logrus.txt", os.O_CREATE|os.O_WRONLY, 0777)
  logger := &logrus.Logger{
    Out:   io.MultiWriter(os.Stderr, f),
    Level: logrus.InfoLevel,
    Formatter: &myFormatter{logrus.TextFormatter{
      FullTimestamp:          true,
      TimestampFormat:        "2006-01-02 15:04:05",
      ForceColors:            true,
      DisableLevelTruncation: true,
    },
    },
  }
  return logger
}

in the Main.go file, therefore, it was declared in this way
Logger := controllers.Logger()
. Now, calling it in the Main file , I get the curve format, because the output works fine for Linux, I do it all on Windows, that is, in some form it all works . Now I want transfer this logger to another function that is generally in a different package, but nothing works for me, how can I do this? Or which way to go? how to make a centralized logger, where I can change everything in 1 place, and not climb through all the files and change something depending on the logging level in the config
Logger.Println("TEST")
[2019-07-30 17:28:47] - ←[36mINFO←[0m - TEST

Answer the question

In order to leave comments, you need to log in

1 answer(s)
V
Vladislav, 2019-07-30
@ghostiam

Here are 2 ways, but I prefer passing the logger through a struct with a constructor.
Only instead of a real logger (*logrus.Logger), the structure and function takes the logger interface ( logrus.FieldLogger ), in this case, it can be easily replaced (or disabled) for tests.

func main {
  log := logrus.New()

  // Передача логера через параметр в функции
  pkg1MyFunc(log, "data1", "data2")

  // Структура с конструктором
  s := NewMyStruct(log, "data")
  s.Do("params")
}

// Передача логера через параметр в функции
func pkg1MyFunc(log logrus.FieldLogger, data1, data2 string) {
  log.Info(data1, data2)
}

// Структура с конструктором
type MyStruct struct {
  log  logrus.FieldLogger
  data string
}

func NewMyStruct(log logrus.FieldLogger, data string) *MyStruct {
  return &MyStruct{log: log, data: data}
}

func (s *MyStruct) Do(params string) {
  s.log.Info(s.data, params)
}

Most importantly, do not use a global variable with a logger, then it will not be possible to replace it or test the code without it. Anyway, global variables are evil.
In addition to the logger, you can pass interfaces to the database in the same way:
type MyStruct struct {
  log   logrus.FieldLogger
  store storage.Store
  data  string
}

func NewMyStruct(log logrus.FieldLogger, store storage.Store, data string) *MyStruct {
  return &MyStruct{log: log, data: data, store: store}
}

func (s *MyStruct) Do(params string) {
  s.log.Info(s.data, params)
  s.store.AddParams(params)
}

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question