I'm learning go. I wrote a simple logparser for SLF4J in Python some time ago and tried to port it to go as an exercise. The algorithm is identical, but the go-solution isn't quite as fast (the Python solution is about 1.5 times faster for large enough logfiles). Is there any way to get more speed out of it? It do not care, which one is the fastest, but I wanted to know if I could do better with go. Any inspiration is welcome.
A typical logline looks like
[INFO 05:00:07] CoolServiceImpl.executeReminderCC(601) | ==================Finish transaction with id (REMINDER, 1394424006830)
Others include Stacktraces which span multiple lines.
My code:
package main
import (
"bufio"
"flag"
"fmt"
"io"
"log"
"os"
"regexp"
"strings"
)
func isStampRelevant(level, timestamp string) bool {
return level == "ERROR" && timestamp > "10:00:00" && timestamp < "10:10:00"
}
func isMessageRelevant(lineBuffer string) bool {
return strings.Contains(lineBuffer, "4020829010703")
}
func readFile(filename string) {
lineStart := regexp.MustCompile("(TRACE|DEBUG|INFO|WARN|ERROR|FATAL).*?(\\d{2}:\\d{2}:\\d{2})")
lineBuffer := ""
fileHandle, err := os.Open(filename)
if err != nil {
log.Fatal(err)
}
buffer := bufio.NewReader(fileHandle)
for {
line, isPrefix, err := buffer.ReadLine()
if err == io.EOF {
break
}
if err != nil {
log.Fatal(err)
}
if isPrefix {
log.Fatal("Error: Unexpected long line reading", fileHandle.Name())
}
currentLine := string(line)
matches := lineStart.FindStringSubmatch(currentLine)
if len(matches) == 3 {
level := matches[1]
timestamp := matches[2]
if len(lineBuffer) > 0 {
if isMessageRelevant(lineBuffer) {
fmt.Println(lineBuffer)
}
}
if isStampRelevant(level, timestamp) {
lineBuffer = currentLine
} else {
lineBuffer = ""
}
} else if len(lineBuffer) > 0 {
lineBuffer += currentLine + "\n"
}
}
if len(lineBuffer) > 0 {
if isMessageRelevant(lineBuffer) {
fmt.Println(lineBuffer)
}
}
}
func printUsage() {
fmt.Fprintf(os.Stderr, "usage: %s [inputfile]\n", os.Args[0])
flag.PrintDefaults()
os.Exit(2)
}
func main() {
if len(os.Args) < 2 {
printUsage()
}
filename := os.Args[1]
readFile(filename)
}