332 lines
7.1 KiB
Go
332 lines
7.1 KiB
Go
|
// 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
|
||
|
}
|