// Licensed to Elasticsearch B.V. under one or more contributor // license agreements. See the NOTICE file distributed with // this work for additional information regarding copyright // ownership. Elasticsearch B.V. licenses this file to you under // the Apache License, Version 2.0 (the "License"); you may // not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, // software distributed under the License is distributed on an // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. package syslog import ( "math" "time" ) const severityMask = 7 const facilityShift = 3 var month = map[string]time.Month{ "Jan": time.January, "Feb": time.February, "Mar": time.March, "Apr": time.April, "May": time.May, "Jun": time.June, "Jul": time.July, "Aug": time.August, "Sep": time.September, "Oct": time.October, "Nov": time.November, "Dec": time.December, } var monthIndexed = []time.Month{ 0, time.January, time.February, time.March, time.April, time.May, time.June, time.July, time.August, time.September, time.October, time.November, time.December, } // event is a parsed syslog event, validation of the format is done at the parser level. type event struct { message string hostname string //x priority int program string //x pid int month time.Month day int hour int minute int second int nanosecond int year int loc *time.Location } // newEvent() return a new event. func newEvent() *event { return &event{ priority: -1, pid: -1, month: -1, day: -1, hour: -1, minute: -1, second: -1, year: time.Now().Year(), } } // SetTimeZone set the timezone offset from the string. func (s *event) SetTimeZone(b []byte) { // We assume that we are in utc and ignore any other bytes after. // This can be followed by others bytes +00, +00:00 or +0000. if b[0] == 'Z' || b[0] == 'z' { s.loc = time.UTC return } d := 1 if b[0] == '-' { d = -1 } // +00 +00:00 or +0000 // Use second value directly and don't use unecessary time.Duration. // Time.FixedZone accepts number of seconds. var h, m int switch len(b[1:]) { case 2: h = 3600 * bytesToInt(skipLeadZero(b[1:])) s.loc = time.FixedZone("", d*h) case 4: h = 3600 * bytesToInt(skipLeadZero(b[1:3])) m = 60 * bytesToInt(skipLeadZero(b[3:5])) s.loc = time.FixedZone("", d*(h+m)) case 5: h = 3600 * bytesToInt(skipLeadZero(b[1:3])) m = 60 * bytesToInt(skipLeadZero(b[4:6])) s.loc = time.FixedZone("", d*(h+m)) } } // SetMonthNumeric sets the month with a number. func (s *event) SetMonthNumeric(b []byte) { s.month = monthIndexed[bytesToInt(skipLeadZero(b))] } // SetMonth sets the month. func (s *event) SetMonth(b []byte) { var k string if len(b) > 3 { k = string(b[0:3]) } else { k = string(b) } v, ok := month[k] if ok { s.month = v } } // Month returns the month. func (s *event) Month() time.Month { return s.month } // SetDay sets the day as. func (s *event) SetDay(b []byte) { s.day = bytesToInt(skipLeadZero(b)) } // Day returns the day. func (s *event) Day() int { return s.day } // SetHour sets the hour. func (s *event) SetHour(b []byte) { s.hour = bytesToInt(skipLeadZero(b)) } // Hour returns the hour. func (s *event) Hour() int { return s.hour } // SetMinute sets the minute. func (s *event) SetMinute(b []byte) { s.minute = bytesToInt(skipLeadZero(b)) } // Minute return the minutes. func (s *event) Minute() int { return s.minute } // SetSecond sets the second. func (s *event) SetSecond(b []byte) { s.second = bytesToInt(skipLeadZero(b)) } // Second returns the second. func (s *event) Second() int { return s.second } // SetYear sets the current year. func (s *event) SetYear(b []byte) { s.year = bytesToInt(b) } // Year returns the current year, since syslog events don't include that. func (s *event) Year() int { return s.year } // SetMessage sets the message. func (s *event) SetMessage(b []byte) { s.message = string(b) } // Message returns the message. func (s *event) Message() string { return s.message } // SetPriority sets the priority. func (s *event) SetPriority(priority []byte) { s.priority = bytesToInt(priority) } // Priority returns the priority. func (s *event) Priority() int { return s.priority } // HasPriority returns if the priority was in original event. func (s *event) HasPriority() bool { return s.priority > 0 } // Severity returns the severity, will return -1 if priority is not set. func (s *event) Severity() int { if !s.HasPriority() { return -1 } return s.Priority() & severityMask } // Facility returns the facility, will return -1 if priority is not set. func (s *event) Facility() int { if !s.HasPriority() { return -1 } return s.Priority() >> facilityShift } // SetHostname sets the hostname. func (s *event) SetHostname(b []byte) { s.hostname = string(b) } // Hostname returns the hostname. func (s *event) Hostname() string { return string(s.hostname) } // SetProgram sets the programs as a byte slice. func (s *event) SetProgram(b []byte) { s.program = string(b) } // Program returns the program name. func (s *event) Program() string { return s.program } func (s *event) SetPid(b []byte) { s.pid = bytesToInt(b) } // Pid returns the pid. func (s *event) Pid() int { return s.pid } // HasPid returns true if a pid is set. func (s *event) HasPid() bool { return s.pid > 0 } // SetNanoSecond sets the nanosecond. func (s *event) SetNanosecond(b []byte) { // We assume that we receive a byte array representing a nanosecond, this might not be // always the case, so we have to pad it. if len(b) < 9 { s.nanosecond = bytesToInt(skipLeadZero(b)) * int(math.Pow10((9 - len(b)))) } else { s.nanosecond = bytesToInt(skipLeadZero(b)) } } // NanoSecond returns the nanosecond. func (s *event) Nanosecond() int { return s.nanosecond } // Timestamp return the timestamp in UTC. func (s *event) Timestamp(timezone *time.Location) time.Time { var t *time.Location if s.loc == nil { t = timezone } else { t = s.loc } return time.Date( s.Year(), s.Month(), s.Day(), s.Hour(), s.Minute(), s.Second(), s.Nanosecond(), t, ).UTC() } // IsValid returns true if the date and the message are present. func (s *event) IsValid() bool { return s.day != -1 && s.hour != -1 && s.minute != -1 && s.second != -1 && s.message != "" } // BytesToInt takes a variable length of bytes and assume ascii chars and convert it to int, this is // a simplified implementation of strconv.Atoi's fast path without error handling and remove the // need to convert the byte array to string, we also assume that any errors are taken care at // the parsing level. func bytesToInt(b []byte) int { var i int for _, x := range b { i = i*10 + int(x-'0') } return i } func skipLeadZero(b []byte) []byte { if len(b) > 1 && b[0] == '0' { return b[1:len(b)] } return b }