yehey's 공부 노트 \n ο(=•ω<=)ρ⌒☆

[golang] logger 추가하기, custom logger 생성하기 본문

개발/Golang

[golang] logger 추가하기, custom logger 생성하기

yehey 2023. 11. 2. 16:00

Today I Learned

: golang 에서 log 패키지를 이용해서 logger를 만들고 custom logger 도 생성하기

배경

golang으로 프로젝트 진행하면서 로깅을 추가해야했음, 기왕 추가하는거 내가 원하는 정보만 담아서 (디버깅하기 좋은 정보) 로그를 남기고 싶었음

Contents

기본 logger

type Logger struct{
    mu sync.Mutex
    prefix string
    flag int
    out io.Writer
    buf []byte
}

log 패키지에서 기본으로 제공하는 Logger 타입

log.Println("logging") //2020/12/30 10:27:11 Logging 으로 출력됨

log.SetFlag(0)
log.Println("logging") // 날짜 시간 없이 logging만 출력

기본으로 logger 생성 없이 log 사용 가능

logger 포맷 변경

go log 패키지에서 제공하는 flag 옵션들을 이용해서 로그 포맷을 변경할 수 있음

const (
   Ldate         = 1 << iota     // the date in the local time zone: 2009/01/23
   Ltime                         // the time in the local time zone: 01:23:23
   Lmicroseconds                 // microsecond resolution: 01:23:23.123123.  assumes Ltime.
   Llongfile                     // full file name and line number: /a/b/c/d.go:23
   Lshortfile                    // final file name element and line number: d.go:23. overrides Llongfile
   LUTC                          // if Ldate or Ltime is set, use UTC rather than the local time zone
   Lmsgprefix                    // move the "prefix" from the beginning of the line to before the message
   LstdFlags     = Ldate | Ltime // initial values for the standard logger
)

log.SetFlags(log.Ldate|log.Ltime|log.Llongfile)
log.setPrefix("INFO: ")
log.Println("Logging")

//INFO: 2020/12/30 15:41:20 /Users/ykoh/GolandProjects/tutorials-go/go-logging/go_logging_test.go:23: Logging

표준 입출력 말고 로그 파일에 쓰기

표준 입출력에서 확인하려 하면 개발 단에서는 편하고 좋지만, 실제 운영에서는 실행 로그들을 쭉쭉 올려가며 찾아야하는 점이 번거롭고 과거 로그들은 삭제되기도 함
그래서 운영 시 로그는 파일로 작성하는 것이 표준, 보통 5~10개 정도의 파일로 운영하고 (ex. log1, log2, log3,...) 만약 주어진 파일 개수가 끝나면 과거 로그 파일부터 덮어 쓰는 방식 (파일 다 쓰는 동안 지났던 로그는 필요 없다고 판단하는 듯)

logFile,err:=os.OpenFile("logfile.txt",os.O_CREATE|os.O_WRONLY|os.O_APPEND,0666)
if err!=nil{
    panic(err)
}
defer logFile.Close()

log.SetOutPut(logFile)

log.Print("test")

logfile로 출력을 변경한 결과 위와 같이 로그가 파일로 저장됨

로그 파일에도 쓰고 표준 입출력에도 찍기

사실 개발을 할 때는 표준 입출력도 찍으면서 로그가 파일에 잘 저장되고 있는지가 궁금하기 때문에..
둘 다 할 수 있는 방법을 찾아보게 됐음

func Test_Multiple_Outputs(t *testing.T) {
   logFile, err := os.OpenFile("logfile.txt", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
   if err != nil {
      panic(err)
   }
   defer logFile.Close()

   multiWriter := io.MultiWriter(logFile, os.Stdout)
   log.SetOutput(multiWriter)

   printMsgLog("test msg")
   log.Println("End of Program")
}

func printMsgLog(msg string) {
   log.Print(msg)
}

크게 어려운건 없었고 MultiWriterSetOutput 으로 설정 할 수 있음

custom logger

func New(out io.Writer, prefix string, flag int) *Logger{
    return &Logger{out:out, prefix:prefix, flag:flag}
}

log.New() 함수를 이용해서 custom logger를 생성할 수 있다.

  1. 로그 output 지정
  2. os.Stdout, os.Stderr, 파일 포인터 등을 지정해서 output으로 사용할 수 있다.
  3. 로그 prefix
  4. 상태나 카테고리를 표시할 수 있다.
  5. 로그 포멧을 설정할 수 있다.
  6. 위에서 사용했던 flag를 조합해서 원하는 로그를 만들 수 있음
type Logger struct {
   Trace *log.Logger
   Warn  *log.Logger
   Info  *log.Logger
   Error *log.Logger
}

var myLogger Logger

func logInit(traceHandle io.Writer, infoHandle io.Writer, warningHandle io.Writer, errorHandle io.Writer) {
   myLogger.Trace = log.New(traceHandle, "[TRACE] ", log.Ldate|log.Ltime|log.Lshortfile)
   myLogger.Info = log.New(infoHandle, "[INFO] ", log.Ldate|log.Ltime|log.Lshortfile)
   myLogger.Warn = log.New(warningHandle, "[WARNING] ", log.Ldate|log.Ltime|log.Lshortfile)
   myLogger.Error = log.New(errorHandle, "[ERROR] ", log.Ldate|log.Ltime|log.Lshortfile)
}

func Test(t *testing.T) {
   logInit(ioutil.Discard, os.Stdout, os.Stdout, os.Stderr)
   myLogger.Info.Println("Starting the application...")
   myLogger.Trace.Println("Something noteworthy happened")
   myLogger.Warn.Println("There is something you should know about")
   myLogger.Error.Println("Something went wrong")

   //[INFO] 2020/12/30 15:43:40 go_logging_test.go:46: Starting the application...
   //[WARNING] 2020/12/30 15:43:40 go_logging_test.go:48: There is something you should know about
   //[ERROR] 2020/12/30 15:43:40 go_logging_test.go:49: Something went wrong
}

Example / Practice

package tools

import (
    "backup/configs"
    "io"
    "log"
    "os"
)

type logger struct {
    Error *log.Logger
    Info  *log.Logger
}

var MyLogger logger

func LongInit(infoHandler io.Writer, errorHandler io.Writer) {
    if _, err := os.Stat(configs.Config.LogFile + "debug.log"); os.IsNotExist(err) {
        os.Create(configs.Config.LogFile + "debug.log")
    }
    logfile, err := os.OpenFile(configs.Config.LogFile+"debug.log", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
    if err != nil {
        panic(err)
    }

    // defer logfile.Close()
    writer := io.MultiWriter(infoHandler, logfile)

    MyLogger.Info = log.New(writer, "[INFO] ", log.Ldate|log.Ltime)
    MyLogger.Error = log.New(writer, "[ERROR] ", log.Ldate|log.Ltime|log.Lshortfile)
}

Warning, Trace 등은 진행하고 있는 프로젝트에서 굳이 나눌 필요가 없다고 생각해서 생성하지 않았음
INFO 의 경우 원하는 메시지만 들어가면 된다고 생각해서 날짜와 시간만 출력,
ERROR의 경우에는 디버깅 시에 어떤 에러가 어느 파일에서 일어났는지 까지 알고 싶어서 file정보를 출력하도록 추가

Learned

golang 프로젝트에 logger, 더 나아가서 custom logger 까지 적용하는 방법을 배웠다.
프로젝트 진행 및 운영에 있어서 log의 중요성을 알았고 왜 적용해야 하는지, 현재 프로젝트에는 어떻게 적용해야 하는지를 생각하고 학습하니 잘 와닿았다.


What I Will Study

운영, 개발, staging 별로 log의 level을 다르게 적용하고 파일을 분리하는 등의 시도를 하고 싶다.

참조

https://pkg.go.dev/log#pkg-examples

 

log package - log - Go Packages

Discover Packages Standard library log Jump to ... Documentation Documentation ¶ Package log implements a simple logging package. It defines a type, Logger, with methods for formatting output. It also has a predefined 'standard' Logger accessible through

pkg.go.dev

https://blog.advenoh.pe.kr/go/Go에서의-로그깅-Logging-in-Go/

 

Go에서의 로그깅 (Logging in Go)

1. 들어가며 Go 표준 패키지 중에 log에서 로깅에 필요한 기본 메서드를 제공한다. 표준 출력 stdout, stderr외에 파일로 로그를 저장하는 방법, 그리고 로그 포맷 변경해서 출력하는 방법 등에 대해서

blog.advenoh.pe.kr

 

'개발 > Golang' 카테고리의 다른 글

[golang] 기본 문법 및 함수  (1) 2023.11.19
[golang] http response unmarshal (with type struct)  (0) 2023.10.31
Comments