gtsocial-umbx

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

syslogparser.go (3717B)


      1 package syslogparser
      2 
      3 import (
      4 	"fmt"
      5 	"strconv"
      6 	"strings"
      7 	"time"
      8 )
      9 
     10 const (
     11 	PRI_PART_START = '<'
     12 	PRI_PART_END   = '>'
     13 
     14 	NO_VERSION = -1
     15 )
     16 
     17 var (
     18 	ErrEOL     = &ParserError{"End of log line"}
     19 	ErrNoSpace = &ParserError{"No space found"}
     20 
     21 	ErrPriorityNoStart  = &ParserError{"No start char found for priority"}
     22 	ErrPriorityEmpty    = &ParserError{"Priority field empty"}
     23 	ErrPriorityNoEnd    = &ParserError{"No end char found for priority"}
     24 	ErrPriorityTooShort = &ParserError{"Priority field too short"}
     25 	ErrPriorityTooLong  = &ParserError{"Priority field too long"}
     26 	ErrPriorityNonDigit = &ParserError{"Non digit found in priority"}
     27 
     28 	ErrVersionNotFound = &ParserError{"Can not find version"}
     29 
     30 	ErrTimestampUnknownFormat = &ParserError{"Timestamp format unknown"}
     31 
     32 	ErrHostnameTooShort = &ParserError{"Hostname field too short"}
     33 )
     34 
     35 type LogParser interface {
     36 	Parse() error
     37 	Dump() LogParts
     38 	Location(*time.Location)
     39 }
     40 
     41 type ParserError struct {
     42 	ErrorString string
     43 }
     44 
     45 type Priority struct {
     46 	P int
     47 	F Facility
     48 	S Severity
     49 }
     50 
     51 type Facility struct {
     52 	Value int
     53 }
     54 
     55 type Severity struct {
     56 	Value int
     57 }
     58 
     59 type LogParts map[string]interface{}
     60 
     61 // https://tools.ietf.org/html/rfc3164#section-4.1
     62 func ParsePriority(buff []byte, cursor *int, l int) (Priority, error) {
     63 	pri := newPriority(0)
     64 
     65 	if l <= 0 {
     66 		return pri, ErrPriorityEmpty
     67 	}
     68 
     69 	if buff[*cursor] != PRI_PART_START {
     70 		return pri, ErrPriorityNoStart
     71 	}
     72 
     73 	i := 1
     74 	priDigit := 0
     75 
     76 	for i < l {
     77 		if i >= 5 {
     78 			return pri, ErrPriorityTooLong
     79 		}
     80 
     81 		c := buff[i]
     82 
     83 		if c == PRI_PART_END {
     84 			if i == 1 {
     85 				return pri, ErrPriorityTooShort
     86 			}
     87 
     88 			*cursor = i + 1
     89 			return newPriority(priDigit), nil
     90 		}
     91 
     92 		if IsDigit(c) {
     93 			v, e := strconv.Atoi(string(c))
     94 			if e != nil {
     95 				return pri, e
     96 			}
     97 
     98 			priDigit = (priDigit * 10) + v
     99 		} else {
    100 			return pri, ErrPriorityNonDigit
    101 		}
    102 
    103 		i++
    104 	}
    105 
    106 	return pri, ErrPriorityNoEnd
    107 }
    108 
    109 // https://tools.ietf.org/html/rfc5424#section-6.2.2
    110 func ParseVersion(buff []byte, cursor *int, l int) (int, error) {
    111 	if *cursor >= l {
    112 		return NO_VERSION, ErrVersionNotFound
    113 	}
    114 
    115 	c := buff[*cursor]
    116 	*cursor++
    117 
    118 	// XXX : not a version, not an error though as RFC 3164 does not support it
    119 	if !IsDigit(c) {
    120 		return NO_VERSION, nil
    121 	}
    122 
    123 	v, e := strconv.Atoi(string(c))
    124 	if e != nil {
    125 		*cursor--
    126 		return NO_VERSION, e
    127 	}
    128 
    129 	return v, nil
    130 }
    131 
    132 func IsDigit(c byte) bool {
    133 	return c >= '0' && c <= '9'
    134 }
    135 
    136 func newPriority(p int) Priority {
    137 	// The Priority value is calculated by first multiplying the Facility
    138 	// number by 8 and then adding the numerical value of the Severity.
    139 
    140 	return Priority{
    141 		P: p,
    142 		F: Facility{Value: p / 8},
    143 		S: Severity{Value: p % 8},
    144 	}
    145 }
    146 
    147 func FindNextSpace(buff []byte, from int, l int) (int, error) {
    148 	var to int
    149 
    150 	for to = from; to < l; to++ {
    151 		if buff[to] == ' ' {
    152 			to++
    153 			return to, nil
    154 		}
    155 	}
    156 
    157 	return 0, ErrNoSpace
    158 }
    159 
    160 func Parse2Digits(buff []byte, cursor *int, l int, min int, max int, e error) (int, error) {
    161 	digitLen := 2
    162 
    163 	if *cursor+digitLen > l {
    164 		return 0, ErrEOL
    165 	}
    166 
    167 	sub := string(buff[*cursor : *cursor+digitLen])
    168 
    169 	*cursor += digitLen
    170 
    171 	i, err := strconv.Atoi(sub)
    172 	if err != nil {
    173 		return 0, e
    174 	}
    175 
    176 	if i >= min && i <= max {
    177 		return i, nil
    178 	}
    179 
    180 	return 0, e
    181 }
    182 
    183 func ParseHostname(buff []byte, cursor *int, l int) (string, error) {
    184 	from := *cursor
    185 
    186 	if from >= l {
    187 		return "", ErrHostnameTooShort
    188 	}
    189 
    190 	var to int
    191 
    192 	for to = from; to < l; to++ {
    193 		if buff[to] == ' ' {
    194 			break
    195 		}
    196 	}
    197 
    198 	hostname := buff[from:to]
    199 
    200 	*cursor = to
    201 
    202 	return string(hostname), nil
    203 }
    204 
    205 func ShowCursorPos(buff []byte, cursor int) {
    206 	fmt.Println(string(buff))
    207 	padding := strings.Repeat("-", cursor)
    208 	fmt.Println(padding + "↑\n")
    209 }
    210 
    211 func (err *ParserError) Error() string {
    212 	return err.ErrorString
    213 }