forked from andybalholm/redwood
/
log.go
120 lines (100 loc) · 2.62 KB
/
log.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
package redwood
import (
"bytes"
"encoding/csv"
"fmt"
"log"
"mime"
"net/http"
"os"
"strings"
"time"
)
// recording pages filtered to access log
var accessLogChan = make(chan []string)
var tlsLogChan = make(chan []string)
type CSVLog struct {
file *os.File
csv *csv.Writer
}
func NewCSVLog(filename string) *CSVLog {
l := new(CSVLog)
if filename != "" {
logfile, err := os.OpenFile(filename, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0644)
if err != nil {
log.Printf("Could not open log file (%s): %s\n Sending access log messages to standard output instead.", filename, err)
} else {
l.file = logfile
}
}
if l.file == nil {
l.file = os.Stdout
}
l.csv = csv.NewWriter(l.file)
return l
}
func (l *CSVLog) Log(data []string) {
l.csv.Write(data)
l.csv.Flush()
}
func (l *CSVLog) Close() {
if l.file == os.Stdout {
return
}
l.file.Close()
}
func logAccess(req *http.Request, resp *http.Response, contentLength int, pruned bool, user string, tally map[rule]int, scores map[string]int, rule ACLActionRule, title string, ignored []string, userAgent string) {
modified := ""
if pruned {
modified = "pruned"
}
status := 0
if resp != nil {
status = resp.StatusCode
}
if rule.Action == "" {
rule.Action = "allow"
}
var contentType string
if resp != nil {
contentType = resp.Header.Get("Content-Type")
}
if ct2, _, err := mime.ParseMediaType(contentType); err == nil {
contentType = ct2
}
accessLogChan <- toStrings(time.Now().Format("2006-01-02 15:04:05"), user, rule.Action, req.URL, req.Method, status, contentType, contentLength, modified, listTally(stringTally(tally)), listTally(scores), rule.Conditions(), title, strings.Join(ignored, ","), userAgent)
}
func logTLS(user, serverAddr, serverName string, err error) {
errStr := ""
if err != nil {
errStr = err.Error()
}
tlsLogChan <- toStrings(time.Now().Format("2006-01-02 15:04:05"), user, serverName, serverAddr, errStr)
}
// toStrings converts its arguments into a slice of strings.
func toStrings(a ...interface{}) []string {
result := make([]string, len(a))
for i, x := range a {
result[i] = fmt.Sprint(x)
}
return result
}
// stringTally returns a copy of tally with strings instead of rules as keys.
func stringTally(tally map[rule]int) map[string]int {
st := make(map[string]int)
for r, n := range tally {
st[r.String()] = n
}
return st
}
// listTally sorts the tally and formats it as a comma-separated string.
func listTally(tally map[string]int) string {
b := new(bytes.Buffer)
for i, rule := range sortedKeys(tally) {
if i > 0 {
b.WriteString(", ")
}
fmt.Fprint(b, rule, " ", tally[rule])
}
return b.String()
}