forked from 0xERR0R/blocky
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathfile_writer.go
131 lines (105 loc) · 3.37 KB
/
file_writer.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
121
122
123
124
125
126
127
128
129
130
131
package querylog
import (
"encoding/csv"
"fmt"
"io"
"os"
"path/filepath"
"regexp"
"strings"
"time"
"github.com/0xERR0R/blocky/log"
"github.com/0xERR0R/blocky/util"
"github.com/sirupsen/logrus"
)
const (
loggerPrefixFileWriter = "fileQueryLogWriter"
filePermission = 0o666
)
var validFilePattern = regexp.MustCompile("[^a-zA-Z0-9-_]+")
type FileWriter struct {
target string
perClient bool
logRetentionDays uint64
}
func NewCSVWriter(target string, perClient bool, logRetentionDays uint64) (*FileWriter, error) {
if _, err := os.Stat(target); target != "" && err != nil && os.IsNotExist(err) {
return nil, fmt.Errorf("query log directory '%s' does not exist or is not writable", target)
}
return &FileWriter{
target: target,
perClient: perClient,
logRetentionDays: logRetentionDays,
}, nil
}
func (d *FileWriter) Write(entry *LogEntry) {
var clientPrefix string
dateString := entry.Start.Format("2006-01-02")
if d.perClient {
clientPrefix = strings.Join(entry.ClientNames, "-")
} else {
clientPrefix = "ALL"
}
fileName := fmt.Sprintf("%s_%s.log", dateString, escape(clientPrefix))
writePath := filepath.Join(d.target, fileName)
file, err := os.OpenFile(writePath, os.O_APPEND|os.O_CREATE|os.O_RDWR, filePermission)
util.LogOnErrorWithEntry(log.PrefixedLog(loggerPrefixFileWriter).WithField("file_name", writePath),
"can't create/open file", err)
if err == nil {
defer file.Close()
writer := createCsvWriter(file)
err := writer.Write(createQueryLogRow(entry))
util.LogOnErrorWithEntry(log.PrefixedLog(loggerPrefixFileWriter).WithField("file_name", writePath),
"can't write to file", err)
writer.Flush()
}
}
// CleanUp deletes old log files
func (d *FileWriter) CleanUp() {
const hoursPerDay = 24
logger := log.PrefixedLog(loggerPrefixFileWriter)
logger.Trace("starting clean up")
files, err := os.ReadDir(d.target)
util.LogOnErrorWithEntry(logger.WithField("target", d.target), "can't list log directory: ", err)
// search for log files, which names starts with date
for _, f := range files {
if strings.HasSuffix(f.Name(), ".log") && len(f.Name()) > 10 {
t, err := time.ParseInLocation("2006-01-02", f.Name()[:10], time.Local)
if err == nil {
differenceDays := uint64(time.Since(t).Hours() / hoursPerDay)
if d.logRetentionDays > 0 && differenceDays > d.logRetentionDays {
logger.WithFields(logrus.Fields{
"file": f.Name(),
"ageInDays": differenceDays,
"logRetentionDays": d.logRetentionDays,
}).Info("existing log file is older than retention time and will be deleted")
err := os.Remove(filepath.Join(d.target, f.Name()))
util.LogOnErrorWithEntry(logger.WithField("file", f.Name()), "can't remove file: ", err)
}
}
}
}
}
func createQueryLogRow(logEntry *LogEntry) []string {
return []string{
logEntry.Start.Format("2006-01-02 15:04:05"),
logEntry.ClientIP,
strings.Join(logEntry.ClientNames, "; "),
fmt.Sprintf("%d", logEntry.DurationMs),
logEntry.ResponseReason,
logEntry.QuestionName,
logEntry.Answer,
logEntry.ResponseCode,
logEntry.ResponseType,
logEntry.QuestionType,
util.HostnameString(),
}
}
func createCsvWriter(file io.Writer) *csv.Writer {
writer := csv.NewWriter(file)
writer.Comma = '\t'
return writer
}
func escape(file string) string {
return validFilePattern.ReplaceAllString(file, "_")
}