어쩌다가 회사에서 프로젝트로Go 언어를 이용한 서버를 만들게 되었다.
서버도 잘 모르고 Go 언어는 더 몰라서 처음에 많이 어려움을 느꼈다.
많은 샘플 코드를 보고 hello world 도 만들어 보고 하니 조금씩 익숙하게 느껴지기 시작했다.
어느개발자이건 어느 프로그램을 만들건 로그를 남길 일이 많은데
연습삼아 golang 으로 로그프로그램을 만들어 보았다.
물론 goalng에서는 자체적으로 log 를 지원한다.
하지만 난 파일 출력도 하면서 호출함수 명도 같이 넣어주고 싶어
"log" 를 래핑하여 나만의 로그를 만들게 되었다.
설계(?) 단계에서 내가 필요한 것은 아래와 같았다.
1. 로그 시간
2. 로그 호출 함수
3.멀티 파라미터 로그
4. 파일 출력 (당연하지만)
로그 시간 출력은 golang 의 "log" 를 이용하니 간단하게 해결하였다.
로그 호출 함수는 아래의 함수를 이용하여 얻을 수 있다.
주석에도 써있지만 skip 을 변경하여 자신이 원하는 이름을 가져올 수 있다.
skip변수는 현재 호출스택에서 몇번째 위의 이름을 가져올 지 정하는 변수이다.
멀티 파라미터도 다른 언어와 마찬가지로 golang 에서 간단하고 깔끔하게 지원한다.
사용법은 변수 ...형식 이다. 아래처럼.
ex:
그래서 아래와 같이 하였다.
파일출력은 두가지 방법이 있는데, 하나는 파일을 열어서 직접 쓰는 방법이 있고, 다른 방법은 log 에 파일 writer 를 설정하여 쓰는 방법이 있다. 나는 두번째 방법을 사용하였는데, 이는 코드 라인수도 줄이고 디버그 창에 동시에 출력하기 위해서다.
위 코드를 보면 log.SetOutput() 이 있는데, 해당 부분이 로그에 파일 writer 를 설정하는 것이다.
이리하여 간단한 로그 프로그램이 완성되었다.
로그 함수를 보면 아래와 같다.
파일 이름이 설정되지 않으면 현재 날자 + "_log.txt" 로 저장하게끔 설정하였다.
전체 코드는 아래와 같고, github 에서도 확인할 수 있다.
현재는 Log 함수에 string 형만 받도록 되어있는데, 추후에는 모든 형식이 가능하도록 변경할 예정이다.
서버도 잘 모르고 Go 언어는 더 몰라서 처음에 많이 어려움을 느꼈다.
많은 샘플 코드를 보고 hello world 도 만들어 보고 하니 조금씩 익숙하게 느껴지기 시작했다.
어느개발자이건 어느 프로그램을 만들건 로그를 남길 일이 많은데
연습삼아 golang 으로 로그프로그램을 만들어 보았다.
물론 goalng에서는 자체적으로 log 를 지원한다.
하지만 난 파일 출력도 하면서 호출함수 명도 같이 넣어주고 싶어
"log" 를 래핑하여 나만의 로그를 만들게 되었다.
설계(?) 단계에서 내가 필요한 것은 아래와 같았다.
1. 로그 시간
2. 로그 호출 함수
3.멀티 파라미터 로그
4. 파일 출력 (당연하지만)
로그 시간 출력은 golang 의 "log" 를 이용하니 간단하게 해결하였다.
로그 호출 함수는 아래의 함수를 이용하여 얻을 수 있다.
주석에도 써있지만 skip 을 변경하여 자신이 원하는 이름을 가져올 수 있다.
skip변수는 현재 호출스택에서 몇번째 위의 이름을 가져올 지 정하는 변수이다.
func getCallingFunctionName() string {
fpcs := make([]uintptr, 1)
// This is the value to skip the number of calling function names.
// Change this value as you wish.
skip := 3
runtime.Callers(skip, fpcs)
// get the info of the actual function that's in the pointer
return runtime.FuncForPC(fpcs[0] - 1).Name()
}
멀티 파라미터도 다른 언어와 마찬가지로 golang 에서 간단하고 깔끔하게 지원한다.
사용법은 변수 ...형식 이다. 아래처럼.
ex:
v ...string
그래서 아래와 같이 하였다.
func Log(v ...string)
파일출력은 두가지 방법이 있는데, 하나는 파일을 열어서 직접 쓰는 방법이 있고, 다른 방법은 log 에 파일 writer 를 설정하여 쓰는 방법이 있다. 나는 두번째 방법을 사용하였는데, 이는 코드 라인수도 줄이고 디버그 창에 동시에 출력하기 위해서다.
func openFile() {
fpLog, err = os.OpenFile(fileName, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
if err != nil {
panic(err)
}
isOpen = true
writer := io.MultiWriter(fpLog, os.Stdout)
log.SetOutput(writer)
}
위 코드를 보면 log.SetOutput() 이 있는데, 해당 부분이 로그에 파일 writer 를 설정하는 것이다.
이리하여 간단한 로그 프로그램이 완성되었다.
로그 함수를 보면 아래와 같다.
// Log will write log string both on console and in file
func Log(v ...string) {
if fileName == "" {
fileName = getNow() + "_log.txt"
}
if initialized == false {
initialized = initialize()
}
functionName := getCallingFunctionName()
logStr := functionName + "() --> " + strings.Join(v[:], ", ")
log.Println(logStr)
}
파일 이름이 설정되지 않으면 현재 날자 + "_log.txt" 로 저장하게끔 설정하였다.
전체 코드는 아래와 같고, github 에서도 확인할 수 있다.
package logger
import (
"io"
"log"
"os"
"runtime"
"strings"
"sync"
"time"
)
var fileName string
var mux sync.Mutex
var initialized bool
var printConsole bool
var fpLog *os.File
var err error
var isOpen bool
// SetFileName will set the log file name
// and re initialize the logger
func SetFileName(newFileName string) {
fileName = newFileName
initialized = false // in order to stop pollingData()
initialized = initialize()
}
func getNow() string {
return time.Now().Local().Format("2006-01-02")
}
// Log will write log string both on console and in file
func Log(v ...string) {
if fileName == "" {
fileName = getNow() + "_log.txt"
}
if initialized == false {
initialized = initialize()
}
functionName := getCallingFunctionName()
logStr := functionName + "() --> " + strings.Join(v[:], ", ")
log.Println(logStr)
}
// Debug will log parameters with Debug string in the beginning
func Debug(v ...string) {
v = append([]string{"***Debug***"}, v...)
Log(v...)
}
// Error will log parameters with Debug string in the beginning
func Error(v ...string) {
v = append([]string{"***Error ***"}, v...)
Log(v...)
}
func getCallingFunctionName() string {
fpcs := make([]uintptr, 1)
// This is the value to skip the number of calling function names.
// Change this value as you wish.
skip := 3
runtime.Callers(skip, fpcs)
// get the info of the actual function that's in the pointer
return runtime.FuncForPC(fpcs[0] - 1).Name()
}
func openFile() {
fpLog, err = os.OpenFile(fileName, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
if err != nil {
panic(err)
}
isOpen = true
writer := io.MultiWriter(fpLog, os.Stdout)
log.SetOutput(writer)
}
func closeFile() {
if fpLog != nil {
fpLog.Close()
isOpen = false
}
}
func initialize() bool {
if isOpen == true {
closeFile()
}
openFile()
return true
}
현재는 Log 함수에 string 형만 받도록 되어있는데, 추후에는 모든 형식이 가능하도록 변경할 예정이다.
댓글
댓글 쓰기