Go를 활용한 Log Parsing 파이프라인 만들기

임태빈·2022년 1월 3일
0

go

목록 보기
4/13

안녕하세요.

갈비만두를 좋아하는 개발자 임태빈입니다.

이번 포스팅에서는 Go의 Channel과 goRoutine을 활용해서

로그 파싱 파이프라인을 만들어보려고 합니다.

코드는 매우 간단하며 전체 코드 공유 후, 함수 설명을 간단하게 해보도록 하겠습니다:)

1. 전체 코드

package main

import (
	"fmt"
	"log"
	"os"
	"regexp"
	"strings"
)

func PrintData(data string) <-chan string {
	oc := make(chan string)
	go func() {
		oc <- data
		close(oc)
	}()
	return oc
}

func ExtractRegexpData(ic <-chan string) <-chan string {
	oc := make(chan string)

	go func() {
		for data := range ic {
			re1, err := regexp.Compile("\\w+/\\w+/\\w+ \\w+:\\w+:\\w+ - ")
			if err != nil {
				panic(err)
			}
			regexpRemoveText := re1.FindString(data)
			//fmt.Println(regexpRemoveText)
			//fmt.Println(data)
			extraText := strings.Replace(data, regexpRemoveText, "", 1)
			//fmt.Println(extraText)
			oc <- extraText
		}
		close(oc)
	}()
	return oc
}

func WriteFile(ic <-chan string) <-chan string {
	oc := make(chan string)
	var f *os.File
	var err error
	f, err = os.OpenFile("data.txt", os.O_RDWR|os.O_CREATE, 0755)
	f.Seek(0, 2)
	if err != nil {
		log.Fatal(err)
	}
	defer f.Close()
	for data := range ic {
		data_byte := []byte(data + "\n")
		_, err = f.Write(data_byte)
		if err != nil {
			panic(err)
		}
	}
	return oc
}

func PrintChanData(ic <-chan string) {
	for data := range ic {
		fmt.Println(data)
	}
}

func main() {
	str1 := "2022/01/03 09:53:27 - 123123"
	PrintChanData(ExtractRegexpData(PrintData(str1)))
	WriteFile(ExtractRegexpData(PrintData(str1)))
}

메인 코드에서는 파이프라인 코드를 두개 돌렸습니다.
하나는 값을 console창에서만 출력하는 것이고 다른 하나는 파일에 데이터를 Write해주는 것입니다.
자세한 내용은 함수 설명에서 해보도록 하겠습니다.

2.함수 설명

input으로 받은 데이터 출력하기

func PrintData(data string) <-chan string {
	oc := make(chan string)
	go func() {
		oc <- data
		close(oc)
	}()
	return oc
}

PrintData 함수에서는 입력 받은 데이터를 출력하고 채널에 넣어서 다른 goRoutine에 넘겨주는 함수입니다.

로그 데이터에서 메시지 추출하기

func ExtractRegexpData(ic <-chan string) <-chan string {
	oc := make(chan string)

	go func() {
		for data := range ic {
			re1, err := regexp.Compile("\\w+/\\w+/\\w+ \\w+:\\w+:\\w+ - ")
			if err != nil {
				panic(err)
			}
			regexpRemoveText := re1.FindString(data)
			extraText := strings.Replace(data, regexpRemoveText, "", 1)
			oc <- extraText
		}
		close(oc)
	}()
	return oc
}

ExtractRegexpData함수는 PrintData로 부터 받은 채널에서 값을 꺼네 정규표현식을 활용해 메시지만 추출을 진행합니다. 추출한 데이터를 다시 채널에 넣어서 다른 goRoutine 함수에 보내줍니다.

추출한 데이터 출력하기

func PrintChanData(ic <-chan string) {
	for data := range ic {
		fmt.Println(data)
	}
}

PrintChanData함수는 추출한 데이터를 출력해주는 함수입니다.

추출한 데이터 파일로 저장하기

func WriteFile(ic <-chan string) <-chan string {
	oc := make(chan string)
	var f *os.File
	var err error
	f, err = os.OpenFile("output.txt", os.O_RDWR|os.O_CREATE, 0755)
	f.Seek(0, 2)
	if err != nil {
		log.Fatal(err)
	}
	defer f.Close()
	for data := range ic {
		data_byte := []byte(data + "\n")
		_, err = f.Write(data_byte)
		if err != nil {
			panic(err)
		}
	}
	return oc
}

WriteFile함수는 추출한 데이터를 파일에 저장하는 함수입니다. 여기서 파일을 생성해주는 부분은 os.OpenFile입니다. 만약 os.O_CREATE가 없다면 파일 생성이 되지 않아 에러가 발생합니다. 파일이 기존에 있다면 에러가 발생하지 않습니다. 이 부분만 주의하신다면 문제 없이 코드를 사용하실 수 있을 거라 생각합니다.

3. 결론

이번 포스팅에 준비한 것은 이걸로 끝입니다:) goRoutine이 아직 익숙치가 않아서 자꾸 실수를 할 때도 있었지만 코드를 구현해보면서 고가 정말 재밌다고 생각이 들었습니다 ㅎㅎ
궁금하신 부분이 있다면 댓글로 남겨주시면 감사드리겠습니다!!

profile
golang과 서버 개발을 하고 있는 개발자입니다.

0개의 댓글