GoToSocial/vendor/gopkg.in/mcuadros/go-syslog.v2/internal/syslogparser/syslogparser.go

214 lines
3.6 KiB
Go

package syslogparser
import (
"fmt"
"strconv"
"strings"
"time"
)
const (
PRI_PART_START = '<'
PRI_PART_END = '>'
NO_VERSION = -1
)
var (
ErrEOL = &ParserError{"End of log line"}
ErrNoSpace = &ParserError{"No space found"}
ErrPriorityNoStart = &ParserError{"No start char found for priority"}
ErrPriorityEmpty = &ParserError{"Priority field empty"}
ErrPriorityNoEnd = &ParserError{"No end char found for priority"}
ErrPriorityTooShort = &ParserError{"Priority field too short"}
ErrPriorityTooLong = &ParserError{"Priority field too long"}
ErrPriorityNonDigit = &ParserError{"Non digit found in priority"}
ErrVersionNotFound = &ParserError{"Can not find version"}
ErrTimestampUnknownFormat = &ParserError{"Timestamp format unknown"}
ErrHostnameTooShort = &ParserError{"Hostname field too short"}
)
type LogParser interface {
Parse() error
Dump() LogParts
Location(*time.Location)
}
type ParserError struct {
ErrorString string
}
type Priority struct {
P int
F Facility
S Severity
}
type Facility struct {
Value int
}
type Severity struct {
Value int
}
type LogParts map[string]interface{}
// https://tools.ietf.org/html/rfc3164#section-4.1
func ParsePriority(buff []byte, cursor *int, l int) (Priority, error) {
pri := newPriority(0)
if l <= 0 {
return pri, ErrPriorityEmpty
}
if buff[*cursor] != PRI_PART_START {
return pri, ErrPriorityNoStart
}
i := 1
priDigit := 0
for i < l {
if i >= 5 {
return pri, ErrPriorityTooLong
}
c := buff[i]
if c == PRI_PART_END {
if i == 1 {
return pri, ErrPriorityTooShort
}
*cursor = i + 1
return newPriority(priDigit), nil
}
if IsDigit(c) {
v, e := strconv.Atoi(string(c))
if e != nil {
return pri, e
}
priDigit = (priDigit * 10) + v
} else {
return pri, ErrPriorityNonDigit
}
i++
}
return pri, ErrPriorityNoEnd
}
// https://tools.ietf.org/html/rfc5424#section-6.2.2
func ParseVersion(buff []byte, cursor *int, l int) (int, error) {
if *cursor >= l {
return NO_VERSION, ErrVersionNotFound
}
c := buff[*cursor]
*cursor++
// XXX : not a version, not an error though as RFC 3164 does not support it
if !IsDigit(c) {
return NO_VERSION, nil
}
v, e := strconv.Atoi(string(c))
if e != nil {
*cursor--
return NO_VERSION, e
}
return v, nil
}
func IsDigit(c byte) bool {
return c >= '0' && c <= '9'
}
func 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{
P: p,
F: Facility{Value: p / 8},
S: Severity{Value: p % 8},
}
}
func FindNextSpace(buff []byte, from int, l int) (int, error) {
var to int
for to = from; to < l; to++ {
if buff[to] == ' ' {
to++
return to, nil
}
}
return 0, ErrNoSpace
}
func Parse2Digits(buff []byte, cursor *int, l int, min int, max int, e error) (int, error) {
digitLen := 2
if *cursor+digitLen > l {
return 0, ErrEOL
}
sub := string(buff[*cursor : *cursor+digitLen])
*cursor += digitLen
i, err := strconv.Atoi(sub)
if err != nil {
return 0, e
}
if i >= min && i <= max {
return i, nil
}
return 0, e
}
func ParseHostname(buff []byte, cursor *int, l int) (string, error) {
from := *cursor
if from >= l {
return "", ErrHostnameTooShort
}
var to int
for to = from; to < l; to++ {
if buff[to] == ' ' {
break
}
}
hostname := buff[from:to]
*cursor = to
return string(hostname), nil
}
func ShowCursorPos(buff []byte, cursor int) {
fmt.Println(string(buff))
padding := strings.Repeat("-", cursor)
fmt.Println(padding + "↑\n")
}
func (err *ParserError) Error() string {
return err.ErrorString
}