-
Notifications
You must be signed in to change notification settings - Fork 0
/
log.go
119 lines (94 loc) · 2.46 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
package client
import (
"context"
"fmt"
"net/url"
"regexp"
"strings"
"time"
"unicode"
)
// ListLogs retrieves the names of available log files.
func (c *Client) ListLogs(ctx context.Context) ([]string, *Response, error) {
req := c.newRequest(ctx).SetResult([]string(nil))
resp, err := req.Get("api/logs/")
if err := convertError(err, resp); err != nil {
return nil, wrapResponse(resp), err
}
return *resp.Result().(*[]string), wrapResponse(resp), nil
}
type LogEntry struct {
Time time.Time
Level string
Module string
Message string
}
func (e *LogEntry) appendLine(line string) {
e.Message += "\n" + line
}
// Regular expression matching a log message. Example:
// [2023-02-28 00:28:37,604] [INFO] [paperless.consumer] Consuming xyz.pdf"
var logEntryRe = regexp.MustCompile(`^` +
`\[(?P<time>\d\d\d\d-\d\d-\d\d\s+\d\d:\d\d:\d\d(?:[.,]\d{1,6})?)\]\s+` +
`\[(?P<level>[A-Z]{1,20})\]\s+` +
`\[(?P<module>[^\]]{1,64})\]\s?` +
`(?P<message>.*)` +
`$`)
type logParser struct {
loc *time.Location
}
func (p *logParser) parseTime(value string) time.Time {
for _, layout := range []string{
"2006-01-02 15:04:05.000",
"2006-01-02 15:04:05",
} {
if ts, err := time.ParseInLocation(layout, value, p.loc); err == nil {
return ts
}
}
return time.Time{}
}
func (p *logParser) detectStart(line string) *LogEntry {
groups := logEntryRe.FindStringSubmatch(line)
if len(groups) < 4 {
return nil
}
return &LogEntry{
Time: p.parseTime(groups[1]),
Level: groups[2],
Module: groups[3],
Message: groups[4],
}
}
func (p *logParser) parse(lines []string) []LogEntry {
var result []LogEntry
var current *LogEntry
for _, line := range lines {
line = strings.TrimRightFunc(line, unicode.IsSpace)
if entry := p.detectStart(line); entry != nil {
if current != nil {
result = append(result, *current)
}
current = entry
} else if current != nil {
current.appendLine(line)
}
}
if current != nil {
result = append(result, *current)
}
return result
}
// GetLog retrieves all entries of the named log file.
func (c *Client) GetLog(ctx context.Context, name string) ([]LogEntry, *Response, error) {
req := c.newRequest(ctx).SetResult([]string(nil))
resp, err := req.Get(fmt.Sprintf("api/logs/%s/", url.PathEscape(name)))
if err := convertError(err, resp); err != nil {
return nil, wrapResponse(resp), err
}
lines := *resp.Result().(*[]string)
p := logParser{
loc: c.loc,
}
return p.parse(lines), wrapResponse(resp), nil
}