github.com/hhrutter/nomad@v0.6.0-rc2.0.20170723054333-80c4b03f0705/client/driver/logging/syslog_parser.go (about) 1 // +build darwin dragonfly freebsd linux netbsd openbsd solaris windows 2 3 package logging 4 5 import ( 6 "fmt" 7 "log" 8 "strconv" 9 10 syslog "github.com/RackSec/srslog" 11 ) 12 13 // Errors related to parsing priority 14 var ( 15 ErrPriorityNoStart = fmt.Errorf("No start char found for priority") 16 ErrPriorityEmpty = fmt.Errorf("Priority field empty") 17 ErrPriorityNoEnd = fmt.Errorf("No end char found for priority") 18 ErrPriorityTooShort = fmt.Errorf("Priority field too short") 19 ErrPriorityTooLong = fmt.Errorf("Priority field too long") 20 ErrPriorityNonDigit = fmt.Errorf("Non digit found in priority") 21 ) 22 23 // Priority header and ending characters 24 const ( 25 PRI_PART_START = '<' 26 PRI_PART_END = '>' 27 ) 28 29 // SyslogMessage represents a log line received 30 type SyslogMessage struct { 31 Message []byte 32 Severity syslog.Priority 33 } 34 35 // Priority holds all the priority bits in a syslog log line 36 type Priority struct { 37 Pri int 38 Facility syslog.Priority 39 Severity syslog.Priority 40 } 41 42 // DockerLogParser parses a line of log message that the docker daemon ships 43 type DockerLogParser struct { 44 logger *log.Logger 45 } 46 47 // NewDockerLogParser creates a new DockerLogParser 48 func NewDockerLogParser(logger *log.Logger) *DockerLogParser { 49 return &DockerLogParser{logger: logger} 50 } 51 52 // Parse parses a syslog log line 53 func (d *DockerLogParser) Parse(line []byte) *SyslogMessage { 54 pri, _, _ := d.parsePriority(line) 55 msgIdx := d.logContentIndex(line) 56 57 // Create a copy of the line so that subsequent Scans do not override the 58 // message 59 lineCopy := make([]byte, len(line[msgIdx:])) 60 copy(lineCopy, line[msgIdx:]) 61 62 return &SyslogMessage{ 63 Severity: pri.Severity, 64 Message: lineCopy, 65 } 66 } 67 68 // logContentIndex finds out the index of the start index of the content in a 69 // syslog line 70 func (d *DockerLogParser) logContentIndex(line []byte) int { 71 cursor := 0 72 numSpace := 0 73 numColons := 0 74 // first look for at least 2 colons. This matches into the date that has no more spaces in it 75 // DefaultFormatter log line look: '<30>2016-07-06T15:13:11Z00:00 hostname docker/9648c64f5037[16200]' 76 // UnixFormatter log line look: '<30>Jul 6 15:13:11 docker/9648c64f5037[16200]' 77 for i := 0; i < len(line); i++ { 78 if line[i] == ':' { 79 numColons += 1 80 if numColons == 2 { 81 cursor = i 82 break 83 } 84 } 85 } 86 // then look for the next space 87 for i := cursor; i < len(line); i++ { 88 if line[i] == ' ' { 89 numSpace += 1 90 if numSpace == 1 { 91 cursor = i 92 break 93 } 94 } 95 } 96 // then the colon is what seperates it, followed by a space 97 for i := cursor; i < len(line); i++ { 98 if line[i] == ':' && i+1 < len(line) && line[i+1] == ' ' { 99 cursor = i + 1 100 break 101 } 102 } 103 // return the cursor to the next character 104 return cursor + 1 105 } 106 107 // parsePriority parses the priority in a syslog message 108 func (d *DockerLogParser) parsePriority(line []byte) (Priority, int, error) { 109 cursor := 0 110 pri := d.newPriority(0) 111 if len(line) <= 0 { 112 return pri, cursor, ErrPriorityEmpty 113 } 114 if line[cursor] != PRI_PART_START { 115 return pri, cursor, ErrPriorityNoStart 116 } 117 i := 1 118 priDigit := 0 119 for i < len(line) { 120 if i >= 5 { 121 return pri, cursor, ErrPriorityTooLong 122 } 123 c := line[i] 124 if c == PRI_PART_END { 125 if i == 1 { 126 return pri, cursor, ErrPriorityTooShort 127 } 128 cursor = i + 1 129 return d.newPriority(priDigit), cursor, nil 130 } 131 if d.isDigit(c) { 132 v, e := strconv.Atoi(string(c)) 133 if e != nil { 134 return pri, cursor, e 135 } 136 priDigit = (priDigit * 10) + v 137 } else { 138 return pri, cursor, ErrPriorityNonDigit 139 } 140 i++ 141 } 142 return pri, cursor, ErrPriorityNoEnd 143 } 144 145 // isDigit checks if a byte is a numeric char 146 func (d *DockerLogParser) isDigit(c byte) bool { 147 return c >= '0' && c <= '9' 148 } 149 150 // newPriority creates a new default priority 151 func (d *DockerLogParser) newPriority(p int) Priority { 152 // The Priority value is calculated by first multiplying the Facility 153 // number by 8 and then adding the numerical value of the Severity. 154 return Priority{ 155 Pri: p, 156 Facility: syslog.Priority(p / 8), 157 Severity: syslog.Priority(p % 8), 158 } 159 }