github.com/dkerwin/nomad@v0.3.3-0.20160525181927-74554135514b/client/driver/logging/syslog_parser_unix.go (about)

     1  // +build darwin dragonfly freebsd linux netbsd openbsd solaris
     2  
     3  package logging
     4  
     5  import (
     6  	"fmt"
     7  	"log"
     8  	"log/syslog"
     9  	"strconv"
    10  )
    11  
    12  // Errors related to parsing priority
    13  var (
    14  	ErrPriorityNoStart  = fmt.Errorf("No start char found for priority")
    15  	ErrPriorityEmpty    = fmt.Errorf("Priority field empty")
    16  	ErrPriorityNoEnd    = fmt.Errorf("No end char found for priority")
    17  	ErrPriorityTooShort = fmt.Errorf("Priority field too short")
    18  	ErrPriorityTooLong  = fmt.Errorf("Priority field too long")
    19  	ErrPriorityNonDigit = fmt.Errorf("Non digit found in priority")
    20  )
    21  
    22  // Priority header and ending characters
    23  const (
    24  	PRI_PART_START = '<'
    25  	PRI_PART_END   = '>'
    26  )
    27  
    28  // SyslogMessage represents a log line received
    29  type SyslogMessage struct {
    30  	Message  []byte
    31  	Severity syslog.Priority
    32  }
    33  
    34  // Priority holds all the priority bits in a syslog log line
    35  type Priority struct {
    36  	Pri      int
    37  	Facility syslog.Priority
    38  	Severity syslog.Priority
    39  }
    40  
    41  // DockerLogParser parses a line of log message that the docker daemon ships
    42  type DockerLogParser struct {
    43  	logger *log.Logger
    44  }
    45  
    46  // NewDockerLogParser creates a new DockerLogParser
    47  func NewDockerLogParser(logger *log.Logger) *DockerLogParser {
    48  	return &DockerLogParser{logger: logger}
    49  }
    50  
    51  // Parse parses a syslog log line
    52  func (d *DockerLogParser) Parse(line []byte) *SyslogMessage {
    53  	pri, _, _ := d.parsePriority(line)
    54  	msgIdx := d.logContentIndex(line)
    55  	return &SyslogMessage{
    56  		Severity: pri.Severity,
    57  		Message:  line[msgIdx:],
    58  	}
    59  }
    60  
    61  // logContentIndex finds out the index of the start index of the content in a
    62  // syslog line
    63  func (d *DockerLogParser) logContentIndex(line []byte) int {
    64  	cursor := 0
    65  	numSpace := 0
    66  	for i := 0; i < len(line); i++ {
    67  		if line[i] == ' ' {
    68  			numSpace += 1
    69  			if numSpace == 1 {
    70  				cursor = i
    71  				break
    72  			}
    73  		}
    74  	}
    75  	for i := cursor; i < len(line); i++ {
    76  		if line[i] == ':' {
    77  			cursor = i
    78  			break
    79  		}
    80  	}
    81  	return cursor + 1
    82  }
    83  
    84  // parsePriority parses the priority in a syslog message
    85  func (d *DockerLogParser) parsePriority(line []byte) (Priority, int, error) {
    86  	cursor := 0
    87  	pri := d.newPriority(0)
    88  	if len(line) <= 0 {
    89  		return pri, cursor, ErrPriorityEmpty
    90  	}
    91  	if line[cursor] != PRI_PART_START {
    92  		return pri, cursor, ErrPriorityNoStart
    93  	}
    94  	i := 1
    95  	priDigit := 0
    96  	for i < len(line) {
    97  		if i >= 5 {
    98  			return pri, cursor, ErrPriorityTooLong
    99  		}
   100  		c := line[i]
   101  		if c == PRI_PART_END {
   102  			if i == 1 {
   103  				return pri, cursor, ErrPriorityTooShort
   104  			}
   105  			cursor = i + 1
   106  			return d.newPriority(priDigit), cursor, nil
   107  		}
   108  		if d.isDigit(c) {
   109  			v, e := strconv.Atoi(string(c))
   110  			if e != nil {
   111  				return pri, cursor, e
   112  			}
   113  			priDigit = (priDigit * 10) + v
   114  		} else {
   115  			return pri, cursor, ErrPriorityNonDigit
   116  		}
   117  		i++
   118  	}
   119  	return pri, cursor, ErrPriorityNoEnd
   120  }
   121  
   122  // isDigit checks if a byte is a numeric char
   123  func (d *DockerLogParser) isDigit(c byte) bool {
   124  	return c >= '0' && c <= '9'
   125  }
   126  
   127  // newPriority creates a new default priority
   128  func (d *DockerLogParser) newPriority(p int) Priority {
   129  	// The Priority value is calculated by first multiplying the Facility
   130  	// number by 8 and then adding the numerical value of the Severity.
   131  	return Priority{
   132  		Pri:      p,
   133  		Facility: syslog.Priority(p / 8),
   134  		Severity: syslog.Priority(p % 8),
   135  	}
   136  }