-
-
Notifications
You must be signed in to change notification settings - Fork 4
/
logentry.go
86 lines (72 loc) · 2.66 KB
/
logentry.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
package core
import (
"fmt"
"strconv"
"strings"
"time"
"github.com/pkg/errors"
)
// LogEntry represents a single event for forwarder and rest server and client
type LogEntry struct {
ID string `json:"id"`
Host string `json:"host"`
Container string `json:"container"`
Pid int `json:"pid"`
Msg string `json:"msg"`
Ts time.Time `json:"ts"`
CreatedTs time.Time `json:"cts"`
}
// NewEntry makes the LogEntry from a log line.
// example: "Oct 19 15:29:43 host-1 docker/mongo[888]: 2015-10-19T19:29:43 blah blah blah"
func NewEntry(line string, tz *time.Location) (entry LogEntry, err error) {
if len(line) < 16 { // 16 is minimal size of "Jan _2 15:04:05" timestamp
return entry, fmt.Errorf("line is too short, line=[%s]", line)
}
entry = LogEntry{Container: "syslog", Pid: 0, CreatedTs: time.Now()}
entry.Ts, line, err = parseTime(line, tz)
if err != nil {
return entry, err
}
// get host
entry.Host = strings.Split(line, " ")[0]
line = line[len(entry.Host)+1:]
// get service/container[pid]
serviceContainerPid := strings.Split(line, " ")[0]
serviceContainerPidElems := strings.Split(serviceContainerPid, "/")
if strings.HasPrefix(serviceContainerPidElems[0], "docker") && len(serviceContainerPidElems) > 1 { // skip non-docker msgs
containerAndPid := serviceContainerPidElems[1]
pidElems := strings.Split(containerAndPid, "[")
entry.Container = pidElems[0]
if len(pidElems) > 1 {
pidStr := strings.TrimSuffix(pidElems[1], ":")
pidStr = strings.TrimSuffix(pidStr, "]")
if pid, err := strconv.Atoi(pidStr); err == nil {
entry.Pid = pid
}
}
}
entry.Msg = strings.TrimSpace(line[len(serviceContainerPid)+1:])
return entry, nil
}
// parseTime gets date-time part of the log line and extracts. Returns tx and trimmed line
// supports "2006 Jan _2 15:04:05" and RFC3339 layouts
func parseTime(line string, tz *time.Location) (ts time.Time, trimmedLine string, err error) {
if len(line) < 16 {
return ts, trimmedLine, errors.Errorf("line %q too short to extract time", line)
}
tsy := fmt.Sprintf("%d %s", time.Now().Year(), line[0:15]) // try ts like "Oct 19 15:29:43"
ts, err = time.ParseInLocation("2006 Jan _2 15:04:05", tsy, tz)
if err == nil {
trimmedLine = line[16:]
return ts, trimmedLine, nil
}
// try RFC3339
dt := strings.Split(line, " ")[0]
if ts, err = time.Parse(time.RFC3339, dt); err != nil {
return time.Time{}, line, errors.Wrapf(err, "can't extract time from %q", line)
}
return ts.In(tz), line[len(dt)+1:], nil
}
func (entry LogEntry) String() string {
return fmt.Sprintf("%s : %s/%s [%d] - %s", entry.Ts.In(time.Local), entry.Host, entry.Container, entry.Pid, entry.Msg)
}