채널을 이용한 비동기 로깅

1. 채널을 이용한 비동기 로깅

[Go활용]편에 있는 로깅하는 방법 아티클에서는 GO의 표준라이브러리인 "log" 패키지를 사용하는 방법을 설명하였다. 이 아티클에서는 채널을 활용하여 비동기 goroutine에서 로깅하는 방법을 간단히 소개한다. 이는 로깅 전용 goroutine을 만들고 여기에 채널을 통해 메서지를 보내면, 이를 비동기적으로 계속 로깅하도록 하는 방식이다.

아래 예제를 살펴보면, logSetup() 함수에서 먼저 로그 채널(logChannel)을 만들고 새로운 goroutine을 실행하는데, 이 goroutine 안에서 로그채널이 닫힐 때까지 계속해서 메시지를 수신하게 된다. 만약 메서지가 도착하면 로그 파일에 메시지를 Append 모드로 계속 추가하고, 로그 채널이 닫혀지면 이 로깅 goroutine은 종료하게 된다.

package main

import (
	"os"
	"strconv"
	"time"
)

/* 비동기 로깅 */
var logChannel chan string
func logSetup(logFile string) {

	// 로그 파일이 없으면, 생성한다
	if _, err := os.Stat(logFile); os.IsNotExist(err) {
		f, _ := os.Create(logFile)
		f.Close()
	}

	// 로그 채널을 만든다
	logChannel = make(chan string, 100)

	// 채널을 통한 비동기 로깅
	go func() {
		// 채널이 닫힐 때까지 메시지 받으면 로깅
		for msg := range logChannel {
			f, _ := os.OpenFile(logFile, os.O_APPEND, os.ModeAppend)
			f.WriteString(time.Now().String() + " " + msg + "\n")
			f.Close()
		}
	}()
}

/* 테스트 코드 */
func main() {
	logSetup("./logfile.txt")

	go func() {
		for i := 1; i < 20; i++ {
			n := strconv.Itoa(i)
			println(n)
			logChannel <- n
		}
	}()

	go func() {
		for i := 100; i < 120; i++ {
			logChannel <- strconv.Itoa(i)
		}
	}()

	time.Sleep(1 * time.Second)
	close(logChannel)
}

main()함수의 테스트 코드를 보면, 두 개의 goroutine에서 각각 20개의 루프를 돌며, 숫자를 로그채널(logChannel)에 보내고 있다. 이 프로그램을 실행하면 - 머신에 따라 다르겠지만 - 랜덤하게 로깅됨을 볼 수 있다.