-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmain.go
120 lines (95 loc) · 2.64 KB
/
main.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
// Tiny log merging tool. Suitable for merging multiple log files of different size. Reads streams, so should not use a lot of RAM.
// Logs are merged based on parsed timestamp, so it must be the same in all input files.
// You should provide '-ts' timestamp format that corresponds the one in your logs. Default is: 'Jan 2 15:04:05'.
// Please refer to time package documentation for more info. https://golang.org/pkg/time/#pkg-constants
package main
import (
"bufio"
"flag"
"fmt"
"io/ioutil"
"log"
"os"
"path/filepath"
"time"
)
func main() {
start := time.Now()
var (
output string
inputDir string
timeFormat string
)
flag.StringVar(&output, "o", "output.log", "Path where result must be written")
flag.StringVar(&inputDir, "i", "input", "Path to the folder with logs to merge")
flag.StringVar(&timeFormat, "tf", "Jan 2 15:04:05", "Time format of log entries")
flag.Parse()
outFile, err := os.Create(output)
if err != nil {
log.Fatal("Failed create merged file:", err)
}
defer outFile.Close()
if err := merge(inputDir, bufio.NewWriter(outFile), timeFormat); err != nil {
log.Fatal(err)
}
fmt.Println("Job done in:", time.Since(start))
}
func merge(inputDir string, out *bufio.Writer, timeFormat string) error {
files, err := ioutil.ReadDir(inputDir)
if err != nil {
return fmt.Errorf("failed to read input dir content: %s", err)
}
nFiles := len(files)
var (
buf = make([]string, nFiles)
readers = make([]*bufio.Scanner, nFiles)
)
var file *os.File
for i, fi := range files {
filePath := filepath.Join(inputDir, fi.Name())
file, err = os.Open(filePath)
if err != nil {
return fmt.Errorf("failed opening file: %q, %s", filePath, err)
}
defer file.Close()
readers[i] = bufio.NewScanner(file)
}
var readersDone int
// Load first events from all files.
for i, reader := range readers {
if ok := reader.Scan(); !ok {
if reader.Err() != nil {
return fmt.Errorf("failed reading file: %s", err)
}
readersDone++
continue
}
buf[i] = reader.Text()
}
sorter := newEventSorter(nFiles, timeFormat)
for {
if len(readers) == readersDone {
break
}
i, err := sorter.firstEventIndex(buf)
if err != nil {
return err
}
if _, err := out.WriteString(buf[i] + "\n"); err != nil {
return fmt.Errorf("failed writing sting to bufio: %s", err)
}
buf[i] = ""
if err := out.Flush(); err != nil {
return fmt.Errorf("failed to write data to output file: %s", err)
}
if ok := readers[i].Scan(); !ok {
if readers[i].Err() != nil {
return fmt.Errorf("failed reading file: %s", err)
}
readersDone++
continue
}
buf[i] = readers[i].Text()
}
return nil
}