2016-09-04 21:03:36 +00:00
|
|
|
// +build darwin dragonfly freebsd linux netbsd openbsd solaris windows
|
2016-02-13 00:20:04 +00:00
|
|
|
|
2016-02-19 21:08:25 +00:00
|
|
|
package logging
|
2016-02-10 15:52:15 +00:00
|
|
|
|
|
|
|
import (
|
2016-02-10 20:09:07 +00:00
|
|
|
"fmt"
|
2016-02-10 15:52:15 +00:00
|
|
|
"log"
|
2016-02-10 20:09:07 +00:00
|
|
|
"strconv"
|
2016-09-04 21:03:36 +00:00
|
|
|
|
|
|
|
syslog "github.com/RackSec/srslog"
|
2016-02-10 15:52:15 +00:00
|
|
|
)
|
|
|
|
|
2016-02-10 23:23:25 +00:00
|
|
|
// Errors related to parsing priority
|
2016-02-10 20:09:07 +00:00
|
|
|
var (
|
|
|
|
ErrPriorityNoStart = fmt.Errorf("No start char found for priority")
|
|
|
|
ErrPriorityEmpty = fmt.Errorf("Priority field empty")
|
|
|
|
ErrPriorityNoEnd = fmt.Errorf("No end char found for priority")
|
|
|
|
ErrPriorityTooShort = fmt.Errorf("Priority field too short")
|
|
|
|
ErrPriorityTooLong = fmt.Errorf("Priority field too long")
|
|
|
|
ErrPriorityNonDigit = fmt.Errorf("Non digit found in priority")
|
|
|
|
)
|
|
|
|
|
2016-02-10 23:23:25 +00:00
|
|
|
// Priority header and ending characters
|
2016-02-10 20:09:07 +00:00
|
|
|
const (
|
|
|
|
PRI_PART_START = '<'
|
|
|
|
PRI_PART_END = '>'
|
|
|
|
)
|
|
|
|
|
2016-02-17 22:48:25 +00:00
|
|
|
// SyslogMessage represents a log line received
|
|
|
|
type SyslogMessage struct {
|
|
|
|
Message []byte
|
|
|
|
Severity syslog.Priority
|
|
|
|
}
|
|
|
|
|
2016-02-10 23:23:25 +00:00
|
|
|
// Priority holds all the priority bits in a syslog log line
|
2016-02-10 20:09:07 +00:00
|
|
|
type Priority struct {
|
2016-02-11 00:40:36 +00:00
|
|
|
Pri int
|
|
|
|
Facility syslog.Priority
|
|
|
|
Severity syslog.Priority
|
2016-02-10 20:09:07 +00:00
|
|
|
}
|
|
|
|
|
2016-02-10 23:23:25 +00:00
|
|
|
// DockerLogParser parses a line of log message that the docker daemon ships
|
2016-02-10 15:52:15 +00:00
|
|
|
type DockerLogParser struct {
|
2016-02-17 22:48:25 +00:00
|
|
|
logger *log.Logger
|
2016-02-10 15:52:15 +00:00
|
|
|
}
|
|
|
|
|
2016-02-10 23:23:25 +00:00
|
|
|
// NewDockerLogParser creates a new DockerLogParser
|
2016-02-17 22:48:25 +00:00
|
|
|
func NewDockerLogParser(logger *log.Logger) *DockerLogParser {
|
|
|
|
return &DockerLogParser{logger: logger}
|
2016-02-10 15:52:15 +00:00
|
|
|
}
|
|
|
|
|
2016-02-10 23:23:25 +00:00
|
|
|
// Parse parses a syslog log line
|
2016-02-17 22:48:25 +00:00
|
|
|
func (d *DockerLogParser) Parse(line []byte) *SyslogMessage {
|
|
|
|
pri, _, _ := d.parsePriority(line)
|
|
|
|
msgIdx := d.logContentIndex(line)
|
2016-06-20 20:14:43 +00:00
|
|
|
|
|
|
|
// Create a copy of the line so that subsequent Scans do not override the
|
|
|
|
// message
|
|
|
|
lineCopy := make([]byte, len(line[msgIdx:]))
|
|
|
|
copy(lineCopy, line[msgIdx:])
|
|
|
|
|
2016-02-17 22:48:25 +00:00
|
|
|
return &SyslogMessage{
|
|
|
|
Severity: pri.Severity,
|
2016-06-20 20:14:43 +00:00
|
|
|
Message: lineCopy,
|
2016-02-10 20:09:07 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-02-10 23:23:25 +00:00
|
|
|
// logContentIndex finds out the index of the start index of the content in a
|
|
|
|
// syslog line
|
2016-02-10 22:40:26 +00:00
|
|
|
func (d *DockerLogParser) logContentIndex(line []byte) int {
|
|
|
|
cursor := 0
|
|
|
|
numSpace := 0
|
2016-07-06 15:57:06 +00:00
|
|
|
numColons := 0
|
|
|
|
// first look for at least 2 colons. This matches into the date that has no more spaces in it
|
|
|
|
// DefaultFormatter log line look: '<30>2016-07-06T15:13:11Z00:00 hostname docker/9648c64f5037[16200]'
|
|
|
|
// UnixFormatter log line look: '<30>Jul 6 15:13:11 docker/9648c64f5037[16200]'
|
2016-02-10 22:40:26 +00:00
|
|
|
for i := 0; i < len(line); i++ {
|
2016-07-06 15:57:06 +00:00
|
|
|
if line[i] == ':' {
|
|
|
|
numColons += 1
|
|
|
|
if numColons == 2 {
|
|
|
|
cursor = i
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// then look for the next space
|
|
|
|
for i := cursor; i < len(line); i++ {
|
2016-02-10 22:40:26 +00:00
|
|
|
if line[i] == ' ' {
|
|
|
|
numSpace += 1
|
|
|
|
if numSpace == 1 {
|
|
|
|
cursor = i
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2017-09-26 22:26:33 +00:00
|
|
|
// then the colon is what separates it, followed by a space
|
2016-02-10 22:40:26 +00:00
|
|
|
for i := cursor; i < len(line); i++ {
|
2016-07-06 16:02:00 +00:00
|
|
|
if line[i] == ':' && i+1 < len(line) && line[i+1] == ' ' {
|
|
|
|
cursor = i + 1
|
2016-02-10 22:40:26 +00:00
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
2016-07-06 16:02:00 +00:00
|
|
|
// return the cursor to the next character
|
2016-02-10 22:40:26 +00:00
|
|
|
return cursor + 1
|
|
|
|
}
|
|
|
|
|
2016-02-10 23:23:25 +00:00
|
|
|
// parsePriority parses the priority in a syslog message
|
2016-02-10 20:09:07 +00:00
|
|
|
func (d *DockerLogParser) parsePriority(line []byte) (Priority, int, error) {
|
|
|
|
cursor := 0
|
|
|
|
pri := d.newPriority(0)
|
|
|
|
if len(line) <= 0 {
|
|
|
|
return pri, cursor, ErrPriorityEmpty
|
|
|
|
}
|
|
|
|
if line[cursor] != PRI_PART_START {
|
|
|
|
return pri, cursor, ErrPriorityNoStart
|
|
|
|
}
|
|
|
|
i := 1
|
|
|
|
priDigit := 0
|
|
|
|
for i < len(line) {
|
|
|
|
if i >= 5 {
|
|
|
|
return pri, cursor, ErrPriorityTooLong
|
|
|
|
}
|
|
|
|
c := line[i]
|
|
|
|
if c == PRI_PART_END {
|
|
|
|
if i == 1 {
|
|
|
|
return pri, cursor, ErrPriorityTooShort
|
|
|
|
}
|
|
|
|
cursor = i + 1
|
|
|
|
return d.newPriority(priDigit), cursor, nil
|
|
|
|
}
|
|
|
|
if d.isDigit(c) {
|
|
|
|
v, e := strconv.Atoi(string(c))
|
|
|
|
if e != nil {
|
|
|
|
return pri, cursor, e
|
|
|
|
}
|
|
|
|
priDigit = (priDigit * 10) + v
|
|
|
|
} else {
|
|
|
|
return pri, cursor, ErrPriorityNonDigit
|
|
|
|
}
|
|
|
|
i++
|
|
|
|
}
|
|
|
|
return pri, cursor, ErrPriorityNoEnd
|
|
|
|
}
|
|
|
|
|
2016-02-10 23:23:25 +00:00
|
|
|
// isDigit checks if a byte is a numeric char
|
2016-02-10 20:09:07 +00:00
|
|
|
func (d *DockerLogParser) isDigit(c byte) bool {
|
|
|
|
return c >= '0' && c <= '9'
|
|
|
|
}
|
|
|
|
|
2016-02-10 23:23:25 +00:00
|
|
|
// newPriority creates a new default priority
|
2016-02-10 20:09:07 +00:00
|
|
|
func (d *DockerLogParser) newPriority(p int) Priority {
|
|
|
|
// The Priority value is calculated by first multiplying the Facility
|
|
|
|
// number by 8 and then adding the numerical value of the Severity.
|
|
|
|
return Priority{
|
2016-02-11 00:40:36 +00:00
|
|
|
Pri: p,
|
|
|
|
Facility: syslog.Priority(p / 8),
|
|
|
|
Severity: syslog.Priority(p % 8),
|
2016-02-10 15:52:15 +00:00
|
|
|
}
|
|
|
|
}
|