├── .circleci └── config.yml ├── .gitignore ├── LICENSE ├── README.md ├── common ├── common.rl ├── facility.go ├── functions.go ├── functions_test.go └── severity.go ├── docs ├── nontransparent.dot ├── rfc3164.dot ├── rfc3164_content.dot ├── rfc3164_hostname.dot ├── rfc3164_msg.dot ├── rfc3164_pri.dot ├── rfc3164_tag.dot ├── rfc3164_timestamp.dot ├── rfc5424.dot ├── rfc5424_appname.dot ├── rfc5424_hostname.dot ├── rfc5424_msg.dot ├── rfc5424_msg_any.dot ├── rfc5424_msg_compliant.dot ├── rfc5424_msgid.dot ├── rfc5424_pri.dot ├── rfc5424_procid.dot ├── rfc5424_structureddata.dot ├── rfc5424_timestamp.dot └── rfc5424_version.dot ├── go.mod ├── go.sum ├── makefile ├── nontransparent ├── example_test.go ├── parser.go ├── parser.go.rl ├── parser_test.go ├── trailer_type.go └── trailer_type_test.go ├── octetcounting ├── example_test.go ├── parser.go ├── parser_test.go ├── performance_test.go ├── scanner.go ├── tokens.go └── tokens_test.go ├── options.go ├── rfc3164 ├── example_test.go ├── machine.go ├── machine.go.rl ├── machine_test.go ├── options.go ├── parser.go ├── syslog_message.go └── year.go ├── rfc5424 ├── builder.go ├── builder.go.rl ├── builder_test.go ├── example_test.go ├── machine.go ├── machine.go.rl ├── machine_test.go ├── options.go ├── parser.go ├── parser_test.go ├── performance_test.go └── syslog_message.go ├── syslog.go └── testing └── testing.go /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | jobs: 3 | test: 4 | docker: 5 | - image: circleci/golang:1.11 6 | environment: 7 | GOCACHE: /tmp/go-cache 8 | GOFLAGS: "-mod=readonly -p=4" 9 | working_directory: "/go/src/github.com/influxdata/go-syslog" 10 | steps: 11 | - checkout 12 | - restore_cache: 13 | name: Restoring caches 14 | keys: 15 | - go-syslog-gocache-{{ .Branch }}-{{ .Revision }} 16 | - go-syslog-modules-{{ checksum "go.sum" }} 17 | - run: make GO_ARGS="-timeout 20s -coverprofile cover.out -race -v" tests 18 | - save_cache: 19 | name: Caching GOCACHE 20 | key: go-syslog-gocache-{{ .Branch }}-{{ .Revision }} 21 | paths: 22 | - /tmp/go-cache 23 | when: always 24 | - save_cache: 25 | name: Caching modules 26 | key: go-syslog-modules-{{ checksum "go.sum" }} 27 | paths: 28 | - /go/pkg/mod 29 | when: always 30 | 31 | workflows: 32 | version: 2 33 | testing: 34 | jobs: 35 | - test -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | debug.test 2 | 3 | docs/*.png 4 | 5 | .vscode/ 6 | 7 | *.out 8 | *.html -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License 2 | 3 | Copyright (c) 2018, InfluxData Inc. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | > [!IMPORTANT] 2 | > ### `go-syslog` is maintained by the original author at [leodido/go-syslog](https://github.com/leodido/go-syslog) 3 | > This repository is maintained only as a placeholder; it is not actively developed and may be out of date. 4 | 5 | 6 | [![MIT License](http://img.shields.io/badge/license-MIT-blue.svg?style=for-the-badge)](LICENSE) 7 | 8 | **A parser for Syslog messages and transports**. 9 | 10 | > [Blazing fast](#Performances) Syslog parsers 11 | 12 | _By [@leodido](https://github.com/leodido)_. 13 | 14 | To wrap up, this package provides: 15 | 16 | - an [RFC5424-compliant parser and builder](/rfc5424) 17 | - an [RFC3164-compliant parser](/rfc3164) - ie., BSD-syslog messages 18 | - a parser that works on streams for syslog with [octet counting](https://tools.ietf.org/html/rfc5425#section-4.3) framing technique, see [octetcounting](/octetcounting) 19 | - a parser that works on streams for syslog with [non-transparent](https://tools.ietf.org/html/rfc6587#section-3.4.2) framing technique, see [nontransparent](/nontransparent) 20 | 21 | This library provides the pieces to parse Syslog messages transported following various RFCs. 22 | 23 | For example: 24 | 25 | - TLS with octet count ([RFC5425](https://tools.ietf.org/html/rfc5425)) 26 | - TCP with non-transparent framing or with octet count ([RFC 6587](https://tools.ietf.org/html/rfc6587)) 27 | - UDP carrying one message per packet ([RFC5426](https://tools.ietf.org/html/rfc5426)) 28 | 29 | ## Installation 30 | 31 | ``` 32 | go get github.com/influxdata/go-syslog/v3 33 | ``` 34 | 35 | ## Docs 36 | 37 | [![Documentation](https://img.shields.io/badge/godoc-reference-blue.svg?style=for-the-badge)](http://godoc.org/github.com/influxdata/go-syslog) 38 | 39 | The [docs](docs/) directory contains `.dot` files representing the finite-state machines (FSMs) implementing the syslog parsers and transports. 40 | 41 | ## Usage 42 | 43 | Suppose you want to parse a given sequence of bytes as a RFC5424 message. 44 | 45 | _Notice that the same interface applies for RFC3164. But you can always take a look at the [examples file](./rfc3164/example_test.go)._ 46 | 47 | ```go 48 | i := []byte(`<165>4 2018-10-11T22:14:15.003Z mymach.it e - 1 [ex@32473 iut="3"] An application event log entry...`) 49 | p := rfc5424.NewParser() 50 | m, e := p.Parse(i) 51 | ``` 52 | 53 | This results in `m` being equal to: 54 | 55 | ```go 56 | // (*rfc5424.SyslogMessage)({ 57 | // Base: (syslog.Base) { 58 | // Facility: (*uint8)(20), 59 | // Severity: (*uint8)(5), 60 | // Priority: (*uint8)(165), 61 | // Timestamp: (*time.Time)(2018-10-11 22:14:15.003 +0000 UTC), 62 | // Hostname: (*string)((len=9) "mymach.it"), 63 | // Appname: (*string)((len=1) "e"), 64 | // ProcID: (*string)(), 65 | // MsgID: (*string)((len=1) "1"), 66 | // Message: (*string)((len=33) "An application event log entry...") 67 | // }, 68 | // Version: (uint16) 4, 69 | // StructuredData: (*map[string]map[string]string)((len=1) { 70 | // (string) (len=8) "ex@32473": (map[string]string) (len=1) { 71 | // (string) (len=3) "iut": (string) (len=1) "3" 72 | // } 73 | // }) 74 | // }) 75 | ``` 76 | 77 | And `e` being equal to `nil` since the `i` byte slice contains a perfectly valid RFC5424 message. 78 | 79 | ### Best effort mode 80 | 81 | RFC5424 parser has the ability to perform partial matches (until it can). 82 | 83 | With this mode enabled, when the parsing process errors out it returns the message collected until that position, and the error that caused the parser to stop. 84 | 85 | Notice that in this modality the output is returned _iff_ it represents a minimally valid message - ie., a message containing almost a priority field in `[1,191]` within angular brackets, followed by a version in `]0,999]` (in the case of RFC5424). 86 | 87 | Let's look at an example. 88 | 89 | ```go 90 | i := []byte("<1>1 A - - - - - -") 91 | p := NewParser(WithBestEffort()) 92 | m, e := p.Parse(i) 93 | ``` 94 | 95 | This results in `m` being equal to the following `SyslogMessage` instance. 96 | 97 | ```go 98 | // (*rfc5424.SyslogMessage)({ 99 | // Base: (syslog.Base) { 100 | // Facility: (*uint8)(0), 101 | // Severity: (*uint8)(1), 102 | // Priority: (*uint8)(1), 103 | // Timestamp: (*time.Time)(), 104 | // Hostname: (*string)(), 105 | // Appname: (*string)(), 106 | // ProcID: (*string)(), 107 | // MsgID: (*string)(), 108 | // Message: (*string)() 109 | // }, 110 | // Version: (uint16) 1, 111 | // StructuredData: (*map[string]map[string]string)() 112 | // }) 113 | ``` 114 | 115 | And, at the same time, in `e` reporting the error that actually stopped the parser. 116 | 117 | ```go 118 | // expecting a RFC3339MICRO timestamp or a nil value [col 5] 119 | ``` 120 | 121 | Both `m` and `e` have a value since at the column the parser stopped it already was able to construct a minimally valid RFC5424 `SyslogMessage`. 122 | 123 | ### Builder 124 | 125 | This library also provides a builder to construct valid syslog messages. 126 | 127 | Notice that its API ignores input values that does not match the grammar. 128 | 129 | Let's have a look to an example. 130 | 131 | ```go 132 | msg := &rfc5424.SyslogMessage{} 133 | msg.SetTimestamp("not a RFC3339MICRO timestamp") 134 | msg.Valid() // Not yet a valid message (try msg.Valid()) 135 | msg.SetPriority(191) 136 | msg.SetVersion(1) 137 | msg.Valid() // Now it is minimally valid 138 | ``` 139 | 140 | Printing `msg` you will verify it contains a `nil` timestamp (since an invalid one has been given). 141 | 142 | ```go 143 | // (*rfc5424.SyslogMessage)({ 144 | // Base: (syslog.Base) { 145 | // Facility: (*uint8)(23), 146 | // Severity: (*uint8)(7), 147 | // Priority: (*uint8)(191), 148 | // Timestamp: (*time.Time)(), 149 | // Hostname: (*string)(), 150 | // Appname: (*string)(), 151 | // ProcID: (*string)(), 152 | // MsgID: (*string)(), 153 | // Message: (*string)() 154 | // }, 155 | // Version: (uint16) 1, 156 | // StructuredData: (*map[string]map[string]string)() 157 | // }) 158 | ``` 159 | 160 | Finally you can serialize the message into a string. 161 | 162 | ```go 163 | str, _ := msg.String() 164 | // <191>1 - - - - - - 165 | ``` 166 | 167 | ## Message transfer 168 | 169 | Excluding encapsulating one message for packet in packet protocols there are two ways to transfer syslog messages over streams. 170 | 171 | The older - ie., the **non-transparent** framing - and the newer one - ie., the **octet counting** framing - which is reliable and has not been seen to cause problems noted with the non-transparent one. 172 | 173 | This library provide stream parsers for both. 174 | 175 | ### Octet counting 176 | 177 | In short, [RFC5425](https://tools.ietf.org/html/rfc5425#section-4.3) and [RFC6587](https://tools.ietf.org/html/rfc6587), aside from the protocol considerations, describe a **transparent framing** technique for Syslog messages that uses the **octect counting** technique - ie., the message length of the incoming message. 178 | 179 | Each Syslog message is sent with a prefix representing the number of bytes it is made of. 180 | 181 | The [octecounting package](./octetcounting) parses messages stream following such rule. 182 | 183 | To quickly understand how to use it please have a look at the [example file](./octetcounting/example_test.go). 184 | 185 | ### Non transparent 186 | 187 | The [RFC6587](https://tools.ietf.org/html/rfc6587#section-3.4.2) also describes the **non-transparent framing** transport of syslog messages. 188 | 189 | In such case the messages are separated by a trailer, usually a line feed. 190 | 191 | The [nontransparent package](./nontransparent) parses message stream following such [technique](https://tools.ietf.org/html/rfc6587#section-3.4.2). 192 | 193 | To quickly understand how to use it please have a look at the [example file](./nontransparent/example_test.go). 194 | 195 | Things we do not support: 196 | 197 | - trailers other than `LF` or `NUL` 198 | - trailers which length is greater than 1 byte 199 | - trailer change on a frame-by-frame basis 200 | 201 | ## Performances 202 | 203 | To run the benchmark execute the following command. 204 | 205 | ```bash 206 | make bench 207 | ``` 208 | 209 | On my machine[1](#mymachine) these are the results obtained paring RFC5424 syslog messages with best effort mode on. 210 | 211 | ``` 212 | [no]_empty_input__________________________________ 4524100 274 ns/op 272 B/op 4 allocs/op 213 | [no]_multiple_syslog_messages_on_multiple_lines___ 3039513 361 ns/op 288 B/op 8 allocs/op 214 | [no]_impossible_timestamp_________________________ 1244562 951 ns/op 512 B/op 11 allocs/op 215 | [no]_malformed_structured_data____________________ 2389249 512 ns/op 512 B/op 9 allocs/op 216 | [no]_with_duplicated_structured_data_id___________ 1000000 1183 ns/op 712 B/op 17 allocs/op 217 | [ok]_minimal______________________________________ 6876235 178 ns/op 227 B/op 5 allocs/op 218 | [ok]_average_message______________________________ 730473 1653 ns/op 1520 B/op 24 allocs/op 219 | [ok]_complicated_message__________________________ 908776 1344 ns/op 1264 B/op 24 allocs/op 220 | [ok]_very_long_message____________________________ 392737 3114 ns/op 2448 B/op 25 allocs/op 221 | [ok]_all_max_length_and_complete__________________ 510740 2431 ns/op 1872 B/op 28 allocs/op 222 | [ok]_all_max_length_except_structured_data_and_mes 755124 1593 ns/op 867 B/op 13 allocs/op 223 | [ok]_minimal_with_message_containing_newline______ 6142984 199 ns/op 230 B/op 6 allocs/op 224 | [ok]_w/o_procid,_w/o_structured_data,_with_message 1670286 732 ns/op 348 B/op 10 allocs/op 225 | [ok]_minimal_with_UTF-8_message___________________ 3013480 407 ns/op 339 B/op 6 allocs/op 226 | [ok]_minimal_with_UTF-8_message_starting_with_BOM_ 2926410 423 ns/op 355 B/op 6 allocs/op 227 | [ok]_with_structured_data_id,_w/o_structured_data_ 1558971 814 ns/op 570 B/op 11 allocs/op 228 | [ok]_with_multiple_structured_data________________ 1000000 1243 ns/op 1205 B/op 16 allocs/op 229 | [ok]_with_escaped_backslash_within_structured_data 1000000 1025 ns/op 896 B/op 17 allocs/op 230 | [ok]_with_UTF-8_structured_data_param_value,_with_ 1000000 1241 ns/op 1034 B/op 19 allocs/op 231 | ``` 232 | 233 | As you can see it takes: 234 | 235 | * ~250ns to parse the smallest legal message 236 | 237 | * less than 2µs to parse an average legal message 238 | 239 | * ~3µs to parse a very long legal message 240 | 241 | Other RFC5424 implementations, like this [one](https://github.com/roguelazer/rust-syslog-rfc5424) in Rust, spend 8µs to parse an average legal message. 242 | 243 | _TBD: comparison against other Go parsers_. 244 | 245 | --- 246 | 247 | * [1]: Intel Core i7-8850H CPU @ 2.60GHz 248 | -------------------------------------------------------------------------------- /common/common.rl: -------------------------------------------------------------------------------- 1 | %%{ 2 | machine common; 3 | 4 | # whitespace 5 | sp = ' '; 6 | 7 | # closing square bracket 8 | csb = ']'; 9 | 10 | # double quote 11 | dq = '"'; 12 | 13 | # backslash 14 | bs = 0x5C; 15 | 16 | # ", ], \ 17 | toescape = (dq | csb | bs); 18 | 19 | # 1..9 20 | nonzerodigit = '1'..'9'; 21 | 22 | # 0..59 23 | sexagesimal = '0'..'5' . '0'..'9'; 24 | 25 | # 01..31 26 | datemday_2digit = ('0' . nonzerodigit | '1'..'2' . '0'..'9' | '3' . '0'..'1'); 27 | 28 | # 1 .. 9, 10..31 29 | datemday = (sp . nonzerodigit | '1'..'2' . '0'..'9' | '3' . '0'..'1'); 30 | 31 | # 01..12 32 | datemonth = ('0' . nonzerodigit | '1' . '0'..'2'); 33 | 34 | datemmm = ('Jan' | 'Feb' | 'Mar' | 'Apr' | 'May' | 'Jun' | 'Jul' | 'Aug' | 'Sep' | 'Oct' | 'Nov' | 'Dec'); 35 | 36 | datefullyear = digit{4}; 37 | 38 | fulldate = datefullyear '-' datemonth '-' datemday_2digit; 39 | 40 | # 01..23 41 | timehour = ('0'..'1' . '0'..'9' | '2' . '0'..'3'); 42 | 43 | timeminute = sexagesimal; 44 | 45 | timesecond = sexagesimal; 46 | 47 | timesecfrac = '.' digit{1,6}; 48 | 49 | timenumoffset = ('+' | '-') timehour ':' timeminute; 50 | 51 | timeoffset = 'Z' | timenumoffset; 52 | 53 | hhmmss = timehour ':' timeminute ':' timesecond; 54 | 55 | partialtime = hhmmss . timesecfrac?; 56 | 57 | fulltime = partialtime . timeoffset; 58 | 59 | # 1..191 60 | privalrange = (('1' ('9' ('0'..'1'){,1} | '0'..'8' ('0'..'9'){,1}){,1}) | ('2'..'9' ('0'..'9'){,1})); 61 | 62 | # 1..191 or 0 63 | prival = (privalrange | '0'); 64 | 65 | hostnamerange = graph{1,255}; 66 | 67 | appnamerange = graph{1,48}; 68 | 69 | procidrange = graph{1,128}; 70 | 71 | msgidrange = graph{1,32}; 72 | 73 | sdname = (graph - ('=' | sp | csb | dq)){1,32}; 74 | 75 | # rfc 3629 76 | utf8tail = 0x80..0xBF; 77 | 78 | utf81 = 0x00..0x7F; 79 | 80 | utf82 = 0xC2..0xDF utf8tail; 81 | 82 | utf83 = 0xE0 0xA0..0xBF utf8tail | 0xE1..0xEC utf8tail{2} | 0xED 0x80..0x9F utf8tail | 0xEE..0xEF utf8tail{2}; 83 | 84 | utf84 = 0xF0 0x90..0xBF utf8tail{2} | 0xF1..0xF3 utf8tail{3} | 0xF4 0x80..0x8F utf8tail{2}; 85 | 86 | utf8char = utf81 | utf82 | utf83 | utf84; 87 | 88 | utf8octets = utf8char*; 89 | 90 | bom = 0xEF 0xBB 0xBF; 91 | 92 | # utf8char except ", ], \ 93 | utf8charwodelims = utf8char - toescape; 94 | 95 | }%% -------------------------------------------------------------------------------- /common/facility.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | // Facility maps facility numeric codes to facility string messages. 4 | // As per RFC 5427. 5 | var Facility = map[uint8]string{ 6 | 0: "kernel messages", 7 | 1: "user-level messages", 8 | 2: "mail system messages", 9 | 3: "system daemons messages", 10 | 4: "authorization messages", 11 | 5: "messages generated internally by syslogd", 12 | 6: "line printer subsystem messages", 13 | 7: "network news subsystem messages", 14 | 8: "UUCP subsystem messages", 15 | 9: "clock daemon messages", 16 | 10: "security/authorization messages", 17 | 11: "ftp daemon messages", 18 | 12: "NTP subsystem messages", 19 | 13: "audit messages", 20 | 14: "console messages", 21 | 15: "clock daemon messages", 22 | 16: "local use 0 (local0)", 23 | 17: "local use 1 (local1)", 24 | 18: "local use 2 (local2)", 25 | 19: "local use 3 (local3)", 26 | 20: "local use 4 (local4)", 27 | 21: "local use 5 (local5)", 28 | 22: "local use 6 (local6)", 29 | 23: "local use 7 (local7)", 30 | } 31 | 32 | // FacilityKeywords maps facility numeric codes to facility keywords. 33 | // As per RFC 5427. 34 | var FacilityKeywords = map[uint8]string{ 35 | 0: "kern", 36 | 1: "user", 37 | 2: "mail", 38 | 3: "daemon", 39 | 4: "auth", 40 | 5: "syslog", 41 | 6: "lpr", 42 | 7: "news", 43 | 8: "uucp", 44 | 9: "cron", 45 | 10: "authpriv", 46 | 11: "ftp", 47 | 12: "ntp", 48 | 13: "security", 49 | 14: "console", 50 | 15: "cron2", 51 | 16: "local0", 52 | 17: "local1", 53 | 18: "local2", 54 | 19: "local3", 55 | 20: "local4", 56 | 21: "local5", 57 | 22: "local6", 58 | 23: "local7", 59 | } 60 | -------------------------------------------------------------------------------- /common/functions.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | // UnsafeUTF8DecimalCodePointsToInt converts a slice containing 4 | // a series of UTF-8 decimal code points into their integer rapresentation. 5 | // 6 | // It assumes input code points are in the range 48-57. 7 | // Returns a pointer since an empty slice is equal to nil and not to the zero value of the codomain (ie., `int`). 8 | func UnsafeUTF8DecimalCodePointsToInt(chars []uint8) int { 9 | out := 0 10 | ord := 1 11 | for i := len(chars) - 1; i >= 0; i-- { 12 | curchar := int(chars[i]) 13 | out += (curchar - '0') * ord 14 | ord *= 10 15 | } 16 | return out 17 | } 18 | 19 | // RemoveBytes removes byte at given positions from data byte slice, starting from the given offset. 20 | func RemoveBytes(data []byte, positions []int, offset int) []byte { 21 | // We need a copy here to not modify original data 22 | cp := append([]byte(nil), data...) 23 | for i, pos := range positions { 24 | at := pos - i - offset 25 | cp = append(cp[:at], cp[(at+1):]...) 26 | } 27 | return cp 28 | } 29 | 30 | // EscapeBytes adds a backslash to \, ], " characters. 31 | func EscapeBytes(value string) string { 32 | res := "" 33 | for i, c := range value { 34 | // todo(leodido): generalize byte codes (the function should ideally accept a byte slice containing byte codes to escape) 35 | if c == 92 || c == 93 || c == 34 { 36 | res += `\` 37 | } 38 | res += string(value[i]) 39 | } 40 | 41 | return res 42 | } 43 | 44 | // InBetween tells whether value is into [min, max] range. 45 | func InBetween(val, min, max int) bool { 46 | return val >= min && val <= max 47 | } 48 | 49 | // ValidPriority checks whether the given value is in the priority range [0, 191]. 50 | func ValidPriority(priority uint8) bool { 51 | return InBetween(int(priority), 0, 191) 52 | } 53 | 54 | // ValidVersion checks whether the given value is in the version range [1, 999]. 55 | func ValidVersion(version uint16) bool { 56 | return InBetween(int(version), 1, 999) 57 | } 58 | -------------------------------------------------------------------------------- /common/functions_test.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | ) 8 | 9 | func TestSimpleUTF8DecimalConversion(t *testing.T) { 10 | slice := []uint8{49, 48, 49} 11 | res := UnsafeUTF8DecimalCodePointsToInt(slice) 12 | assert.Equal(t, 101, res) 13 | } 14 | 15 | func TestNumberStartingWithZero(t *testing.T) { 16 | slice := []uint8{48, 48, 50} 17 | res := UnsafeUTF8DecimalCodePointsToInt(slice) 18 | assert.Equal(t, 2, res) 19 | } 20 | 21 | func TestCharsNotInRange(t *testing.T) { 22 | point := 10 23 | slice := []uint8{uint8(point)} // Line Feed (LF) 24 | res := UnsafeUTF8DecimalCodePointsToInt(slice) 25 | assert.Equal(t, res, -(48 - point)) 26 | } 27 | 28 | func TestAllDigits(t *testing.T) { 29 | slice := []uint8{49, 50, 51, 52, 53, 54, 55, 56, 57, 48} 30 | res := UnsafeUTF8DecimalCodePointsToInt(slice) 31 | assert.Equal(t, 1234567890, res) 32 | } 33 | -------------------------------------------------------------------------------- /common/severity.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | // SeverityMessages maps severity levels to severity string messages. 4 | var SeverityMessages = map[uint8]string{ 5 | 0: "system is unusable", 6 | 1: "action must be taken immediately", 7 | 2: "critical conditions", 8 | 3: "error conditions", 9 | 4: "warning conditions", 10 | 5: "normal but significant condition", 11 | 6: "informational messages", 12 | 7: "debug-level messages", 13 | } 14 | 15 | // SeverityLevels maps seveirty levels to severity string levels. 16 | var SeverityLevels = map[uint8]string{ 17 | 0: "emergency", 18 | 1: "alert", 19 | 2: "critical", 20 | 3: "error", 21 | 4: "warning", 22 | 5: "notice", 23 | 6: "informational", 24 | 7: "debug", 25 | } 26 | 27 | // SeverityLevelsShort maps severity levels to severity short string levels 28 | // as per https://github.com/torvalds/linux/blob/master/tools/include/linux/kern_levels.h and syslog(3). 29 | var SeverityLevelsShort = map[uint8]string{ 30 | 0: "emerg", 31 | 1: "alert", 32 | 2: "crit", 33 | 3: "err", 34 | 4: "warning", 35 | 5: "notice", 36 | 6: "info", 37 | 7: "debug", 38 | } -------------------------------------------------------------------------------- /docs/nontransparent.dot: -------------------------------------------------------------------------------- 1 | digraph nontransparent { 2 | rankdir=LR; 3 | node [ shape = point ]; 4 | ENTRY; 5 | en_1; 6 | node [ shape = circle, height = 0.2 ]; 7 | node [ fixedsize = true, height = 0.65, shape = doublecircle ]; 8 | 3; 9 | node [ shape = circle ]; 10 | 1 -> 2 [ label = "'<' / on_init" ]; 11 | 2 -> 2 [ label = "1..'\\t', '\\v'..255, '\\n'(!28:13), 0(!28:13)" ]; 12 | 2 -> 3 [ label = "'\\n'(28:13), 0(29:13) / on_trailer" ]; 13 | 3 -> 2 [ label = "1..'\\t', '\\v'..';', '='..255, '\\n'(!28:13), 0(!28:13)" ]; 14 | 3 -> 2 [ label = "'<' / on_init" ]; 15 | 3 -> 3 [ label = "'\\n'(28:13), 0(29:13) / on_trailer" ]; 16 | ENTRY -> 1 [ label = "IN" ]; 17 | en_1 -> 1 [ label = "main" ]; 18 | } 19 | -------------------------------------------------------------------------------- /docs/rfc3164_content.dot: -------------------------------------------------------------------------------- 1 | digraph rfc3164 { 2 | rankdir=LR; 3 | node [ shape = point ]; 4 | ENTRY; 5 | eof_4; 6 | node [ shape = circle, height = 0.2 ]; 7 | node [ fixedsize = true, height = 0.65, shape = doublecircle ]; 8 | 5; 9 | node [ shape = circle ]; 10 | 1 -> 2 [ label = "'['" ]; 11 | 2 -> 4 [ label = "'0'..'9', 'A'..'Z', 'a'..'z' / mark" ]; 12 | 2 -> 5 [ label = "']' / mark, set_content" ]; 13 | 2 -> 3 [ label = "DEF / mark" ]; 14 | 3 -> 5 [ label = "']' / set_content" ]; 15 | 3 -> 3 [ label = "DEF" ]; 16 | 4 -> 5 [ label = "']' / set_content" ]; 17 | 4 -> 3 [ label = "DEF" ]; 18 | 5 -> 5 [ label = "']' / set_content" ]; 19 | 5 -> 3 [ label = "DEF" ]; 20 | ENTRY -> 1 [ label = "IN" ]; 21 | 4 -> eof_4 [ label = "EOF / err_contentstart" ]; 22 | } 23 | -------------------------------------------------------------------------------- /docs/rfc3164_msg.dot: -------------------------------------------------------------------------------- 1 | digraph rfc3164 { 2 | rankdir=LR; 3 | node [ shape = point ]; 4 | ENTRY; 5 | eof_1; 6 | eof_6; 7 | eof_7; 8 | eof_8; 9 | eof_9; 10 | eof_10; 11 | eof_11; 12 | eof_12; 13 | eof_13; 14 | eof_14; 15 | eof_15; 16 | eof_16; 17 | eof_17; 18 | eof_18; 19 | eof_19; 20 | eof_20; 21 | eof_21; 22 | eof_22; 23 | eof_23; 24 | eof_24; 25 | eof_25; 26 | eof_26; 27 | eof_27; 28 | eof_28; 29 | eof_29; 30 | eof_30; 31 | eof_31; 32 | eof_32; 33 | eof_33; 34 | eof_34; 35 | eof_35; 36 | eof_36; 37 | eof_37; 38 | eof_38; 39 | eof_39; 40 | eof_40; 41 | eof_41; 42 | eof_42; 43 | eof_43; 44 | eof_44; 45 | eof_45; 46 | node [ shape = circle, height = 0.2 ]; 47 | err_1 [ label=""]; 48 | node [ fixedsize = true, height = 0.65, shape = doublecircle ]; 49 | 6; 50 | 7; 51 | 8; 52 | 9; 53 | 10; 54 | 11; 55 | 12; 56 | 13; 57 | 14; 58 | 15; 59 | 16; 60 | 17; 61 | 18; 62 | 19; 63 | 20; 64 | 21; 65 | 22; 66 | 23; 67 | 24; 68 | 25; 69 | 26; 70 | 27; 71 | 28; 72 | 29; 73 | 30; 74 | 31; 75 | 32; 76 | 33; 77 | 34; 78 | 35; 79 | 36; 80 | 37; 81 | 38; 82 | 39; 83 | 40; 84 | 41; 85 | 42; 86 | 43; 87 | 44; 88 | 45; 89 | node [ shape = circle ]; 90 | 1 -> err_1 [ label = "0..31, 127 / err_tag" ]; 91 | 1 -> 7 [ label = "'!'..'9', ';'..'Z', '\\'..'~' / mark" ]; 92 | 1 -> 6 [ label = "DEF / mark" ]; 93 | 2 -> 3 [ label = "']' / set_content" ]; 94 | 2 -> 2 [ label = "DEF" ]; 95 | 3 -> 4 [ label = "':'" ]; 96 | 3 -> 3 [ label = "']' / set_content" ]; 97 | 3 -> 2 [ label = "DEF" ]; 98 | 4 -> 5 [ label = "SP" ]; 99 | 4 -> 3 [ label = "']' / set_content" ]; 100 | 4 -> 2 [ label = "DEF" ]; 101 | 5 -> 2 [ label = "0..31, 127" ]; 102 | 5 -> 43 [ label = "']' / set_content, mark" ]; 103 | 5 -> 42 [ label = "DEF / mark" ]; 104 | 6 -> 6 [ label = "SP..'~', 128..255" ]; 105 | 7 -> 6 [ label = "SP, 128..255" ]; 106 | 7 -> 8 [ label = "'!'..'9', ';'..'Z', '\\'..'~'" ]; 107 | 7 -> 39 [ label = "':' / set_tag" ]; 108 | 7 -> 41 [ label = "'[' / set_tag" ]; 109 | 8 -> 6 [ label = "SP, 128..255" ]; 110 | 8 -> 9 [ label = "'!'..'9', ';'..'Z', '\\'..'~'" ]; 111 | 8 -> 39 [ label = "':' / set_tag" ]; 112 | 8 -> 41 [ label = "'[' / set_tag" ]; 113 | 9 -> 6 [ label = "SP, 128..255" ]; 114 | 9 -> 10 [ label = "'!'..'9', ';'..'Z', '\\'..'~'" ]; 115 | 9 -> 39 [ label = "':' / set_tag" ]; 116 | 9 -> 41 [ label = "'[' / set_tag" ]; 117 | 10 -> 6 [ label = "SP, 128..255" ]; 118 | 10 -> 11 [ label = "'!'..'9', ';'..'Z', '\\'..'~'" ]; 119 | 10 -> 39 [ label = "':' / set_tag" ]; 120 | 10 -> 41 [ label = "'[' / set_tag" ]; 121 | 11 -> 6 [ label = "SP, 128..255" ]; 122 | 11 -> 12 [ label = "'!'..'9', ';'..'Z', '\\'..'~'" ]; 123 | 11 -> 39 [ label = "':' / set_tag" ]; 124 | 11 -> 41 [ label = "'[' / set_tag" ]; 125 | 12 -> 6 [ label = "SP, 128..255" ]; 126 | 12 -> 13 [ label = "'!'..'9', ';'..'Z', '\\'..'~'" ]; 127 | 12 -> 39 [ label = "':' / set_tag" ]; 128 | 12 -> 41 [ label = "'[' / set_tag" ]; 129 | 13 -> 6 [ label = "SP, 128..255" ]; 130 | 13 -> 14 [ label = "'!'..'9', ';'..'Z', '\\'..'~'" ]; 131 | 13 -> 39 [ label = "':' / set_tag" ]; 132 | 13 -> 41 [ label = "'[' / set_tag" ]; 133 | 14 -> 6 [ label = "SP, 128..255" ]; 134 | 14 -> 15 [ label = "'!'..'9', ';'..'Z', '\\'..'~'" ]; 135 | 14 -> 39 [ label = "':' / set_tag" ]; 136 | 14 -> 41 [ label = "'[' / set_tag" ]; 137 | 15 -> 6 [ label = "SP, 128..255" ]; 138 | 15 -> 16 [ label = "'!'..'9', ';'..'Z', '\\'..'~'" ]; 139 | 15 -> 39 [ label = "':' / set_tag" ]; 140 | 15 -> 41 [ label = "'[' / set_tag" ]; 141 | 16 -> 6 [ label = "SP, 128..255" ]; 142 | 16 -> 17 [ label = "'!'..'9', ';'..'Z', '\\'..'~'" ]; 143 | 16 -> 39 [ label = "':' / set_tag" ]; 144 | 16 -> 41 [ label = "'[' / set_tag" ]; 145 | 17 -> 6 [ label = "SP, 128..255" ]; 146 | 17 -> 18 [ label = "'!'..'9', ';'..'Z', '\\'..'~'" ]; 147 | 17 -> 39 [ label = "':' / set_tag" ]; 148 | 17 -> 41 [ label = "'[' / set_tag" ]; 149 | 18 -> 6 [ label = "SP, 128..255" ]; 150 | 18 -> 19 [ label = "'!'..'9', ';'..'Z', '\\'..'~'" ]; 151 | 18 -> 39 [ label = "':' / set_tag" ]; 152 | 18 -> 41 [ label = "'[' / set_tag" ]; 153 | 19 -> 6 [ label = "SP, 128..255" ]; 154 | 19 -> 20 [ label = "'!'..'9', ';'..'Z', '\\'..'~'" ]; 155 | 19 -> 39 [ label = "':' / set_tag" ]; 156 | 19 -> 41 [ label = "'[' / set_tag" ]; 157 | 20 -> 6 [ label = "SP, 128..255" ]; 158 | 20 -> 21 [ label = "'!'..'9', ';'..'Z', '\\'..'~'" ]; 159 | 20 -> 39 [ label = "':' / set_tag" ]; 160 | 20 -> 41 [ label = "'[' / set_tag" ]; 161 | 21 -> 6 [ label = "SP, 128..255" ]; 162 | 21 -> 22 [ label = "'!'..'9', ';'..'Z', '\\'..'~'" ]; 163 | 21 -> 39 [ label = "':' / set_tag" ]; 164 | 21 -> 41 [ label = "'[' / set_tag" ]; 165 | 22 -> 6 [ label = "SP, 128..255" ]; 166 | 22 -> 23 [ label = "'!'..'9', ';'..'Z', '\\'..'~'" ]; 167 | 22 -> 39 [ label = "':' / set_tag" ]; 168 | 22 -> 41 [ label = "'[' / set_tag" ]; 169 | 23 -> 6 [ label = "SP, 128..255" ]; 170 | 23 -> 24 [ label = "'!'..'9', ';'..'Z', '\\'..'~'" ]; 171 | 23 -> 39 [ label = "':' / set_tag" ]; 172 | 23 -> 41 [ label = "'[' / set_tag" ]; 173 | 24 -> 6 [ label = "SP, 128..255" ]; 174 | 24 -> 25 [ label = "'!'..'9', ';'..'Z', '\\'..'~'" ]; 175 | 24 -> 39 [ label = "':' / set_tag" ]; 176 | 24 -> 41 [ label = "'[' / set_tag" ]; 177 | 25 -> 6 [ label = "SP, 128..255" ]; 178 | 25 -> 26 [ label = "'!'..'9', ';'..'Z', '\\'..'~'" ]; 179 | 25 -> 39 [ label = "':' / set_tag" ]; 180 | 25 -> 41 [ label = "'[' / set_tag" ]; 181 | 26 -> 6 [ label = "SP, 128..255" ]; 182 | 26 -> 27 [ label = "'!'..'9', ';'..'Z', '\\'..'~'" ]; 183 | 26 -> 39 [ label = "':' / set_tag" ]; 184 | 26 -> 41 [ label = "'[' / set_tag" ]; 185 | 27 -> 6 [ label = "SP, 128..255" ]; 186 | 27 -> 28 [ label = "'!'..'9', ';'..'Z', '\\'..'~'" ]; 187 | 27 -> 39 [ label = "':' / set_tag" ]; 188 | 27 -> 41 [ label = "'[' / set_tag" ]; 189 | 28 -> 6 [ label = "SP, 128..255" ]; 190 | 28 -> 29 [ label = "'!'..'9', ';'..'Z', '\\'..'~'" ]; 191 | 28 -> 39 [ label = "':' / set_tag" ]; 192 | 28 -> 41 [ label = "'[' / set_tag" ]; 193 | 29 -> 6 [ label = "SP, 128..255" ]; 194 | 29 -> 30 [ label = "'!'..'9', ';'..'Z', '\\'..'~'" ]; 195 | 29 -> 39 [ label = "':' / set_tag" ]; 196 | 29 -> 41 [ label = "'[' / set_tag" ]; 197 | 30 -> 6 [ label = "SP, 128..255" ]; 198 | 30 -> 31 [ label = "'!'..'9', ';'..'Z', '\\'..'~'" ]; 199 | 30 -> 39 [ label = "':' / set_tag" ]; 200 | 30 -> 41 [ label = "'[' / set_tag" ]; 201 | 31 -> 6 [ label = "SP, 128..255" ]; 202 | 31 -> 32 [ label = "'!'..'9', ';'..'Z', '\\'..'~'" ]; 203 | 31 -> 39 [ label = "':' / set_tag" ]; 204 | 31 -> 41 [ label = "'[' / set_tag" ]; 205 | 32 -> 6 [ label = "SP, 128..255" ]; 206 | 32 -> 33 [ label = "'!'..'9', ';'..'Z', '\\'..'~'" ]; 207 | 32 -> 39 [ label = "':' / set_tag" ]; 208 | 32 -> 41 [ label = "'[' / set_tag" ]; 209 | 33 -> 6 [ label = "SP, 128..255" ]; 210 | 33 -> 34 [ label = "'!'..'9', ';'..'Z', '\\'..'~'" ]; 211 | 33 -> 39 [ label = "':' / set_tag" ]; 212 | 33 -> 41 [ label = "'[' / set_tag" ]; 213 | 34 -> 6 [ label = "SP, 128..255" ]; 214 | 34 -> 35 [ label = "'!'..'9', ';'..'Z', '\\'..'~'" ]; 215 | 34 -> 39 [ label = "':' / set_tag" ]; 216 | 34 -> 41 [ label = "'[' / set_tag" ]; 217 | 35 -> 6 [ label = "SP, 128..255" ]; 218 | 35 -> 36 [ label = "'!'..'9', ';'..'Z', '\\'..'~'" ]; 219 | 35 -> 39 [ label = "':' / set_tag" ]; 220 | 35 -> 41 [ label = "'[' / set_tag" ]; 221 | 36 -> 6 [ label = "SP, 128..255" ]; 222 | 36 -> 37 [ label = "'!'..'9', ';'..'Z', '\\'..'~'" ]; 223 | 36 -> 39 [ label = "':' / set_tag" ]; 224 | 36 -> 41 [ label = "'[' / set_tag" ]; 225 | 37 -> 6 [ label = "SP, 128..255" ]; 226 | 37 -> 38 [ label = "'!'..'9', ';'..'Z', '\\'..'~'" ]; 227 | 37 -> 39 [ label = "':' / set_tag" ]; 228 | 37 -> 41 [ label = "'[' / set_tag" ]; 229 | 38 -> 6 [ label = "SP..'9', ';'..'Z', '\\'..'~', 128..255" ]; 230 | 38 -> 39 [ label = "':' / set_tag" ]; 231 | 38 -> 41 [ label = "'[' / set_tag" ]; 232 | 39 -> 40 [ label = "SP" ]; 233 | 39 -> 6 [ label = "'!'..'~', 128..255" ]; 234 | 40 -> 6 [ label = "SP..'~', 128..255 / mark" ]; 235 | 41 -> 2 [ label = "0..31, 127 / mark" ]; 236 | 41 -> 43 [ label = "']' / mark, set_content" ]; 237 | 41 -> 42 [ label = "DEF / mark" ]; 238 | 42 -> 2 [ label = "0..31, 127" ]; 239 | 42 -> 43 [ label = "']' / set_content" ]; 240 | 42 -> 42 [ label = "DEF" ]; 241 | 43 -> 2 [ label = "0..31, 127" ]; 242 | 43 -> 44 [ label = "':'" ]; 243 | 43 -> 43 [ label = "']' / set_content" ]; 244 | 43 -> 42 [ label = "DEF" ]; 245 | 44 -> 2 [ label = "0..31, 127" ]; 246 | 44 -> 45 [ label = "SP" ]; 247 | 44 -> 43 [ label = "']' / set_content" ]; 248 | 44 -> 42 [ label = "DEF" ]; 249 | 45 -> 2 [ label = "0..31, 127" ]; 250 | 45 -> 43 [ label = "']' / set_content, mark" ]; 251 | 45 -> 42 [ label = "DEF / mark" ]; 252 | ENTRY -> 1 [ label = "IN" ]; 253 | 1 -> eof_1 [ label = "EOF / err_tag" ]; 254 | 6 -> eof_6 [ label = "EOF / set_message" ]; 255 | 7 -> eof_7 [ label = "EOF / set_message" ]; 256 | 8 -> eof_8 [ label = "EOF / set_message" ]; 257 | 9 -> eof_9 [ label = "EOF / set_message" ]; 258 | 10 -> eof_10 [ label = "EOF / set_message" ]; 259 | 11 -> eof_11 [ label = "EOF / set_message" ]; 260 | 12 -> eof_12 [ label = "EOF / set_message" ]; 261 | 13 -> eof_13 [ label = "EOF / set_message" ]; 262 | 14 -> eof_14 [ label = "EOF / set_message" ]; 263 | 15 -> eof_15 [ label = "EOF / set_message" ]; 264 | 16 -> eof_16 [ label = "EOF / set_message" ]; 265 | 17 -> eof_17 [ label = "EOF / set_message" ]; 266 | 18 -> eof_18 [ label = "EOF / set_message" ]; 267 | 19 -> eof_19 [ label = "EOF / set_message" ]; 268 | 20 -> eof_20 [ label = "EOF / set_message" ]; 269 | 21 -> eof_21 [ label = "EOF / set_message" ]; 270 | 22 -> eof_22 [ label = "EOF / set_message" ]; 271 | 23 -> eof_23 [ label = "EOF / set_message" ]; 272 | 24 -> eof_24 [ label = "EOF / set_message" ]; 273 | 25 -> eof_25 [ label = "EOF / set_message" ]; 274 | 26 -> eof_26 [ label = "EOF / set_message" ]; 275 | 27 -> eof_27 [ label = "EOF / set_message" ]; 276 | 28 -> eof_28 [ label = "EOF / set_message" ]; 277 | 29 -> eof_29 [ label = "EOF / set_message" ]; 278 | 30 -> eof_30 [ label = "EOF / set_message" ]; 279 | 31 -> eof_31 [ label = "EOF / set_message" ]; 280 | 32 -> eof_32 [ label = "EOF / set_message" ]; 281 | 33 -> eof_33 [ label = "EOF / set_message" ]; 282 | 34 -> eof_34 [ label = "EOF / set_message" ]; 283 | 35 -> eof_35 [ label = "EOF / set_message" ]; 284 | 36 -> eof_36 [ label = "EOF / set_message" ]; 285 | 37 -> eof_37 [ label = "EOF / set_message" ]; 286 | 38 -> eof_38 [ label = "EOF / set_message" ]; 287 | 39 -> eof_39 [ label = "EOF / set_message" ]; 288 | 40 -> eof_40 [ label = "EOF / set_message" ]; 289 | 41 -> eof_41 [ label = "EOF / set_message" ]; 290 | 42 -> eof_42 [ label = "EOF / set_message" ]; 291 | 43 -> eof_43 [ label = "EOF / set_message" ]; 292 | 44 -> eof_44 [ label = "EOF / set_message" ]; 293 | 45 -> eof_45 [ label = "EOF / set_message" ]; 294 | } 295 | -------------------------------------------------------------------------------- /docs/rfc3164_pri.dot: -------------------------------------------------------------------------------- 1 | digraph rfc3164 { 2 | rankdir=LR; 3 | node [ shape = point ]; 4 | ENTRY; 5 | eof_1; 6 | eof_2; 7 | eof_3; 8 | eof_4; 9 | eof_5; 10 | eof_6; 11 | node [ shape = circle, height = 0.2 ]; 12 | err_1 [ label=""]; 13 | err_2 [ label=""]; 14 | err_3 [ label=""]; 15 | err_4 [ label=""]; 16 | err_5 [ label=""]; 17 | err_6 [ label=""]; 18 | node [ fixedsize = true, height = 0.65, shape = doublecircle ]; 19 | 7; 20 | node [ shape = circle ]; 21 | 1 -> 2 [ label = "'<'" ]; 22 | 1 -> err_1 [ label = "DEF / err_pri" ]; 23 | 2 -> 3 [ label = "'0' / mark" ]; 24 | 2 -> 4 [ label = "'1' / mark" ]; 25 | 2 -> 5 [ label = "'2'..'9' / mark" ]; 26 | 2 -> err_2 [ label = "DEF / err_prival, err_pri" ]; 27 | 3 -> 7 [ label = "'>' / set_prival" ]; 28 | 3 -> err_3 [ label = "DEF / set_prival, err_prival, err_pri" ]; 29 | 4 -> 5 [ label = "'0'..'8' / set_prival" ]; 30 | 4 -> 6 [ label = "'9' / set_prival" ]; 31 | 4 -> 7 [ label = "'>' / set_prival" ]; 32 | 4 -> err_4 [ label = "DEF / set_prival, err_prival, err_pri" ]; 33 | 5 -> 3 [ label = "'0'..'9' / set_prival" ]; 34 | 5 -> 7 [ label = "'>' / set_prival" ]; 35 | 5 -> err_5 [ label = "DEF / set_prival, err_prival, err_pri" ]; 36 | 6 -> 3 [ label = "'0'..'1' / set_prival" ]; 37 | 6 -> 7 [ label = "'>' / set_prival" ]; 38 | 6 -> err_6 [ label = "DEF / set_prival, err_prival, err_pri" ]; 39 | ENTRY -> 1 [ label = "IN" ]; 40 | 1 -> eof_1 [ label = "EOF / err_pri" ]; 41 | 2 -> eof_2 [ label = "EOF / err_prival, err_pri" ]; 42 | 3 -> eof_3 [ label = "EOF / err_prival, err_pri" ]; 43 | 4 -> eof_4 [ label = "EOF / err_prival, err_pri" ]; 44 | 5 -> eof_5 [ label = "EOF / err_prival, err_pri" ]; 45 | 6 -> eof_6 [ label = "EOF / err_prival, err_pri" ]; 46 | } 47 | -------------------------------------------------------------------------------- /docs/rfc3164_tag.dot: -------------------------------------------------------------------------------- 1 | digraph rfc3164 { 2 | rankdir=LR; 3 | node [ shape = point ]; 4 | ENTRY; 5 | eof_1; 6 | eof_2; 7 | eof_3; 8 | eof_4; 9 | eof_5; 10 | eof_6; 11 | eof_7; 12 | eof_8; 13 | eof_9; 14 | eof_10; 15 | eof_11; 16 | eof_12; 17 | eof_13; 18 | eof_14; 19 | eof_15; 20 | eof_16; 21 | eof_17; 22 | eof_18; 23 | eof_19; 24 | eof_20; 25 | eof_21; 26 | eof_22; 27 | eof_23; 28 | eof_24; 29 | eof_25; 30 | eof_26; 31 | eof_27; 32 | eof_28; 33 | eof_29; 34 | eof_30; 35 | eof_31; 36 | eof_32; 37 | eof_33; 38 | node [ shape = circle, height = 0.2 ]; 39 | err_1 [ label=""]; 40 | node [ fixedsize = true, height = 0.65, shape = doublecircle ]; 41 | 2; 42 | 3; 43 | 4; 44 | 5; 45 | 6; 46 | 7; 47 | 8; 48 | 9; 49 | 10; 50 | 11; 51 | 12; 52 | 13; 53 | 14; 54 | 15; 55 | 16; 56 | 17; 57 | 18; 58 | 19; 59 | 20; 60 | 21; 61 | 22; 62 | 23; 63 | 24; 64 | 25; 65 | 26; 66 | 27; 67 | 28; 68 | 29; 69 | 30; 70 | 31; 71 | 32; 72 | 33; 73 | node [ shape = circle ]; 74 | 1 -> 2 [ label = "'!'..'9', ';'..'Z', '\\'..'~' / mark" ]; 75 | 1 -> err_1 [ label = "DEF / err_tag" ]; 76 | 2 -> 3 [ label = "'!'..'9', ';'..'Z', '\\'..'~'" ]; 77 | 3 -> 4 [ label = "'!'..'9', ';'..'Z', '\\'..'~'" ]; 78 | 4 -> 5 [ label = "'!'..'9', ';'..'Z', '\\'..'~'" ]; 79 | 5 -> 6 [ label = "'!'..'9', ';'..'Z', '\\'..'~'" ]; 80 | 6 -> 7 [ label = "'!'..'9', ';'..'Z', '\\'..'~'" ]; 81 | 7 -> 8 [ label = "'!'..'9', ';'..'Z', '\\'..'~'" ]; 82 | 8 -> 9 [ label = "'!'..'9', ';'..'Z', '\\'..'~'" ]; 83 | 9 -> 10 [ label = "'!'..'9', ';'..'Z', '\\'..'~'" ]; 84 | 10 -> 11 [ label = "'!'..'9', ';'..'Z', '\\'..'~'" ]; 85 | 11 -> 12 [ label = "'!'..'9', ';'..'Z', '\\'..'~'" ]; 86 | 12 -> 13 [ label = "'!'..'9', ';'..'Z', '\\'..'~'" ]; 87 | 13 -> 14 [ label = "'!'..'9', ';'..'Z', '\\'..'~'" ]; 88 | 14 -> 15 [ label = "'!'..'9', ';'..'Z', '\\'..'~'" ]; 89 | 15 -> 16 [ label = "'!'..'9', ';'..'Z', '\\'..'~'" ]; 90 | 16 -> 17 [ label = "'!'..'9', ';'..'Z', '\\'..'~'" ]; 91 | 17 -> 18 [ label = "'!'..'9', ';'..'Z', '\\'..'~'" ]; 92 | 18 -> 19 [ label = "'!'..'9', ';'..'Z', '\\'..'~'" ]; 93 | 19 -> 20 [ label = "'!'..'9', ';'..'Z', '\\'..'~'" ]; 94 | 20 -> 21 [ label = "'!'..'9', ';'..'Z', '\\'..'~'" ]; 95 | 21 -> 22 [ label = "'!'..'9', ';'..'Z', '\\'..'~'" ]; 96 | 22 -> 23 [ label = "'!'..'9', ';'..'Z', '\\'..'~'" ]; 97 | 23 -> 24 [ label = "'!'..'9', ';'..'Z', '\\'..'~'" ]; 98 | 24 -> 25 [ label = "'!'..'9', ';'..'Z', '\\'..'~'" ]; 99 | 25 -> 26 [ label = "'!'..'9', ';'..'Z', '\\'..'~'" ]; 100 | 26 -> 27 [ label = "'!'..'9', ';'..'Z', '\\'..'~'" ]; 101 | 27 -> 28 [ label = "'!'..'9', ';'..'Z', '\\'..'~'" ]; 102 | 28 -> 29 [ label = "'!'..'9', ';'..'Z', '\\'..'~'" ]; 103 | 29 -> 30 [ label = "'!'..'9', ';'..'Z', '\\'..'~'" ]; 104 | 30 -> 31 [ label = "'!'..'9', ';'..'Z', '\\'..'~'" ]; 105 | 31 -> 32 [ label = "'!'..'9', ';'..'Z', '\\'..'~'" ]; 106 | 32 -> 33 [ label = "'!'..'9', ';'..'Z', '\\'..'~'" ]; 107 | ENTRY -> 1 [ label = "IN" ]; 108 | 1 -> eof_1 [ label = "EOF / err_tag" ]; 109 | 2 -> eof_2 [ label = "EOF / set_tag" ]; 110 | 3 -> eof_3 [ label = "EOF / set_tag" ]; 111 | 4 -> eof_4 [ label = "EOF / set_tag" ]; 112 | 5 -> eof_5 [ label = "EOF / set_tag" ]; 113 | 6 -> eof_6 [ label = "EOF / set_tag" ]; 114 | 7 -> eof_7 [ label = "EOF / set_tag" ]; 115 | 8 -> eof_8 [ label = "EOF / set_tag" ]; 116 | 9 -> eof_9 [ label = "EOF / set_tag" ]; 117 | 10 -> eof_10 [ label = "EOF / set_tag" ]; 118 | 11 -> eof_11 [ label = "EOF / set_tag" ]; 119 | 12 -> eof_12 [ label = "EOF / set_tag" ]; 120 | 13 -> eof_13 [ label = "EOF / set_tag" ]; 121 | 14 -> eof_14 [ label = "EOF / set_tag" ]; 122 | 15 -> eof_15 [ label = "EOF / set_tag" ]; 123 | 16 -> eof_16 [ label = "EOF / set_tag" ]; 124 | 17 -> eof_17 [ label = "EOF / set_tag" ]; 125 | 18 -> eof_18 [ label = "EOF / set_tag" ]; 126 | 19 -> eof_19 [ label = "EOF / set_tag" ]; 127 | 20 -> eof_20 [ label = "EOF / set_tag" ]; 128 | 21 -> eof_21 [ label = "EOF / set_tag" ]; 129 | 22 -> eof_22 [ label = "EOF / set_tag" ]; 130 | 23 -> eof_23 [ label = "EOF / set_tag" ]; 131 | 24 -> eof_24 [ label = "EOF / set_tag" ]; 132 | 25 -> eof_25 [ label = "EOF / set_tag" ]; 133 | 26 -> eof_26 [ label = "EOF / set_tag" ]; 134 | 27 -> eof_27 [ label = "EOF / set_tag" ]; 135 | 28 -> eof_28 [ label = "EOF / set_tag" ]; 136 | 29 -> eof_29 [ label = "EOF / set_tag" ]; 137 | 30 -> eof_30 [ label = "EOF / set_tag" ]; 138 | 31 -> eof_31 [ label = "EOF / set_tag" ]; 139 | 32 -> eof_32 [ label = "EOF / set_tag" ]; 140 | 33 -> eof_33 [ label = "EOF / set_tag" ]; 141 | } 142 | -------------------------------------------------------------------------------- /docs/rfc3164_timestamp.dot: -------------------------------------------------------------------------------- 1 | digraph rfc3164 { 2 | rankdir=LR; 3 | node [ shape = point ]; 4 | ENTRY; 5 | eof_1; 6 | eof_2; 7 | eof_3; 8 | eof_4; 9 | eof_5; 10 | eof_6; 11 | eof_7; 12 | eof_8; 13 | eof_9; 14 | eof_10; 15 | eof_11; 16 | eof_12; 17 | eof_13; 18 | eof_14; 19 | eof_15; 20 | eof_16; 21 | eof_17; 22 | eof_18; 23 | eof_19; 24 | eof_20; 25 | eof_21; 26 | eof_22; 27 | eof_23; 28 | eof_24; 29 | eof_25; 30 | eof_26; 31 | eof_27; 32 | eof_28; 33 | eof_29; 34 | eof_30; 35 | eof_31; 36 | eof_32; 37 | eof_33; 38 | eof_34; 39 | eof_35; 40 | node [ shape = circle, height = 0.2 ]; 41 | err_1 [ label=""]; 42 | err_2 [ label=""]; 43 | err_3 [ label=""]; 44 | err_4 [ label=""]; 45 | err_5 [ label=""]; 46 | err_6 [ label=""]; 47 | err_7 [ label=""]; 48 | err_8 [ label=""]; 49 | err_9 [ label=""]; 50 | err_10 [ label=""]; 51 | err_11 [ label=""]; 52 | err_12 [ label=""]; 53 | err_13 [ label=""]; 54 | err_14 [ label=""]; 55 | err_15 [ label=""]; 56 | err_16 [ label=""]; 57 | err_17 [ label=""]; 58 | err_18 [ label=""]; 59 | err_19 [ label=""]; 60 | err_20 [ label=""]; 61 | err_21 [ label=""]; 62 | err_22 [ label=""]; 63 | err_23 [ label=""]; 64 | err_24 [ label=""]; 65 | err_25 [ label=""]; 66 | err_26 [ label=""]; 67 | err_27 [ label=""]; 68 | err_28 [ label=""]; 69 | err_29 [ label=""]; 70 | err_30 [ label=""]; 71 | err_31 [ label=""]; 72 | err_32 [ label=""]; 73 | err_33 [ label=""]; 74 | err_34 [ label=""]; 75 | node [ fixedsize = true, height = 0.65, shape = doublecircle ]; 76 | 35; 77 | node [ shape = circle ]; 78 | 1 -> 2 [ label = "'A' / mark" ]; 79 | 1 -> 20 [ label = "'D' / mark" ]; 80 | 1 -> 22 [ label = "'F' / mark" ]; 81 | 1 -> 24 [ label = "'J' / mark" ]; 82 | 1 -> 27 [ label = "'M' / mark" ]; 83 | 1 -> 29 [ label = "'N' / mark" ]; 84 | 1 -> 31 [ label = "'O' / mark" ]; 85 | 1 -> 33 [ label = "'S' / mark" ]; 86 | 1 -> err_1 [ label = "DEF / err_timestamp" ]; 87 | 2 -> 3 [ label = "'p'" ]; 88 | 2 -> 19 [ label = "'u'" ]; 89 | 2 -> err_2 [ label = "DEF / err_timestamp" ]; 90 | 3 -> 4 [ label = "'r'" ]; 91 | 3 -> err_3 [ label = "DEF / err_timestamp" ]; 92 | 4 -> 5 [ label = "SP" ]; 93 | 4 -> err_4 [ label = "DEF / err_timestamp" ]; 94 | 5 -> 6 [ label = "SP" ]; 95 | 5 -> 17 [ label = "'1'..'2'" ]; 96 | 5 -> 18 [ label = "'3'" ]; 97 | 5 -> err_5 [ label = "DEF / err_timestamp" ]; 98 | 6 -> 7 [ label = "'1'..'9'" ]; 99 | 6 -> err_6 [ label = "DEF / err_timestamp" ]; 100 | 7 -> 8 [ label = "SP" ]; 101 | 7 -> err_7 [ label = "DEF / err_timestamp" ]; 102 | 8 -> 9 [ label = "'0'..'1'" ]; 103 | 8 -> 16 [ label = "'2'" ]; 104 | 8 -> err_8 [ label = "DEF / err_timestamp" ]; 105 | 9 -> 10 [ label = "'0'..'9'" ]; 106 | 9 -> err_9 [ label = "DEF / err_timestamp" ]; 107 | 10 -> 11 [ label = "':'" ]; 108 | 10 -> err_10 [ label = "DEF / err_timestamp" ]; 109 | 11 -> 12 [ label = "'0'..'5'" ]; 110 | 11 -> err_11 [ label = "DEF / err_timestamp" ]; 111 | 12 -> 13 [ label = "'0'..'9'" ]; 112 | 12 -> err_12 [ label = "DEF / err_timestamp" ]; 113 | 13 -> 14 [ label = "':'" ]; 114 | 13 -> err_13 [ label = "DEF / err_timestamp" ]; 115 | 14 -> 15 [ label = "'0'..'5'" ]; 116 | 14 -> err_14 [ label = "DEF / err_timestamp" ]; 117 | 15 -> 35 [ label = "'0'..'9'" ]; 118 | 15 -> err_15 [ label = "DEF / err_timestamp" ]; 119 | 16 -> 10 [ label = "'0'..'3'" ]; 120 | 16 -> err_16 [ label = "DEF / err_timestamp" ]; 121 | 17 -> 7 [ label = "'0'..'9'" ]; 122 | 17 -> err_17 [ label = "DEF / err_timestamp" ]; 123 | 18 -> 7 [ label = "'0'..'1'" ]; 124 | 18 -> err_18 [ label = "DEF / err_timestamp" ]; 125 | 19 -> 4 [ label = "'g'" ]; 126 | 19 -> err_19 [ label = "DEF / err_timestamp" ]; 127 | 20 -> 21 [ label = "'e'" ]; 128 | 20 -> err_20 [ label = "DEF / err_timestamp" ]; 129 | 21 -> 4 [ label = "'c'" ]; 130 | 21 -> err_21 [ label = "DEF / err_timestamp" ]; 131 | 22 -> 23 [ label = "'e'" ]; 132 | 22 -> err_22 [ label = "DEF / err_timestamp" ]; 133 | 23 -> 4 [ label = "'b'" ]; 134 | 23 -> err_23 [ label = "DEF / err_timestamp" ]; 135 | 24 -> 25 [ label = "'a'" ]; 136 | 24 -> 26 [ label = "'u'" ]; 137 | 24 -> err_24 [ label = "DEF / err_timestamp" ]; 138 | 25 -> 4 [ label = "'n'" ]; 139 | 25 -> err_25 [ label = "DEF / err_timestamp" ]; 140 | 26 -> 4 [ label = "'l', 'n'" ]; 141 | 26 -> err_26 [ label = "DEF / err_timestamp" ]; 142 | 27 -> 28 [ label = "'a'" ]; 143 | 27 -> err_27 [ label = "DEF / err_timestamp" ]; 144 | 28 -> 4 [ label = "'r', 'y'" ]; 145 | 28 -> err_28 [ label = "DEF / err_timestamp" ]; 146 | 29 -> 30 [ label = "'o'" ]; 147 | 29 -> err_29 [ label = "DEF / err_timestamp" ]; 148 | 30 -> 4 [ label = "'v'" ]; 149 | 30 -> err_30 [ label = "DEF / err_timestamp" ]; 150 | 31 -> 32 [ label = "'c'" ]; 151 | 31 -> err_31 [ label = "DEF / err_timestamp" ]; 152 | 32 -> 4 [ label = "'t'" ]; 153 | 32 -> err_32 [ label = "DEF / err_timestamp" ]; 154 | 33 -> 34 [ label = "'e'" ]; 155 | 33 -> err_33 [ label = "DEF / err_timestamp" ]; 156 | 34 -> 4 [ label = "'p'" ]; 157 | 34 -> err_34 [ label = "DEF / err_timestamp" ]; 158 | ENTRY -> 1 [ label = "IN" ]; 159 | 1 -> eof_1 [ label = "EOF / err_timestamp" ]; 160 | 2 -> eof_2 [ label = "EOF / err_timestamp" ]; 161 | 3 -> eof_3 [ label = "EOF / err_timestamp" ]; 162 | 4 -> eof_4 [ label = "EOF / err_timestamp" ]; 163 | 5 -> eof_5 [ label = "EOF / err_timestamp" ]; 164 | 6 -> eof_6 [ label = "EOF / err_timestamp" ]; 165 | 7 -> eof_7 [ label = "EOF / err_timestamp" ]; 166 | 8 -> eof_8 [ label = "EOF / err_timestamp" ]; 167 | 9 -> eof_9 [ label = "EOF / err_timestamp" ]; 168 | 10 -> eof_10 [ label = "EOF / err_timestamp" ]; 169 | 11 -> eof_11 [ label = "EOF / err_timestamp" ]; 170 | 12 -> eof_12 [ label = "EOF / err_timestamp" ]; 171 | 13 -> eof_13 [ label = "EOF / err_timestamp" ]; 172 | 14 -> eof_14 [ label = "EOF / err_timestamp" ]; 173 | 15 -> eof_15 [ label = "EOF / err_timestamp" ]; 174 | 16 -> eof_16 [ label = "EOF / err_timestamp" ]; 175 | 17 -> eof_17 [ label = "EOF / err_timestamp" ]; 176 | 18 -> eof_18 [ label = "EOF / err_timestamp" ]; 177 | 19 -> eof_19 [ label = "EOF / err_timestamp" ]; 178 | 20 -> eof_20 [ label = "EOF / err_timestamp" ]; 179 | 21 -> eof_21 [ label = "EOF / err_timestamp" ]; 180 | 22 -> eof_22 [ label = "EOF / err_timestamp" ]; 181 | 23 -> eof_23 [ label = "EOF / err_timestamp" ]; 182 | 24 -> eof_24 [ label = "EOF / err_timestamp" ]; 183 | 25 -> eof_25 [ label = "EOF / err_timestamp" ]; 184 | 26 -> eof_26 [ label = "EOF / err_timestamp" ]; 185 | 27 -> eof_27 [ label = "EOF / err_timestamp" ]; 186 | 28 -> eof_28 [ label = "EOF / err_timestamp" ]; 187 | 29 -> eof_29 [ label = "EOF / err_timestamp" ]; 188 | 30 -> eof_30 [ label = "EOF / err_timestamp" ]; 189 | 31 -> eof_31 [ label = "EOF / err_timestamp" ]; 190 | 32 -> eof_32 [ label = "EOF / err_timestamp" ]; 191 | 33 -> eof_33 [ label = "EOF / err_timestamp" ]; 192 | 34 -> eof_34 [ label = "EOF / err_timestamp" ]; 193 | 35 -> eof_35 [ label = "EOF / set_timestamp" ]; 194 | } 195 | -------------------------------------------------------------------------------- /docs/rfc5424_appname.dot: -------------------------------------------------------------------------------- 1 | digraph rfc5424 { 2 | rankdir=LR; 3 | node [ shape = point ]; 4 | ENTRY; 5 | eof_1; 6 | eof_2; 7 | eof_3; 8 | eof_4; 9 | eof_5; 10 | eof_6; 11 | eof_7; 12 | eof_8; 13 | eof_9; 14 | eof_10; 15 | eof_11; 16 | eof_12; 17 | eof_13; 18 | eof_14; 19 | eof_15; 20 | eof_16; 21 | eof_17; 22 | eof_18; 23 | eof_19; 24 | eof_20; 25 | eof_21; 26 | eof_22; 27 | eof_23; 28 | eof_24; 29 | eof_25; 30 | eof_26; 31 | eof_27; 32 | eof_28; 33 | eof_29; 34 | eof_30; 35 | eof_31; 36 | eof_32; 37 | eof_33; 38 | eof_34; 39 | eof_35; 40 | eof_36; 41 | eof_37; 42 | eof_38; 43 | eof_39; 44 | eof_40; 45 | eof_41; 46 | eof_42; 47 | eof_43; 48 | eof_44; 49 | eof_45; 50 | eof_46; 51 | eof_47; 52 | eof_48; 53 | eof_49; 54 | node [ shape = circle, height = 0.2 ]; 55 | err_1 [ label=""]; 56 | err_2 [ label=""]; 57 | err_3 [ label=""]; 58 | err_4 [ label=""]; 59 | err_5 [ label=""]; 60 | err_6 [ label=""]; 61 | err_7 [ label=""]; 62 | err_8 [ label=""]; 63 | err_9 [ label=""]; 64 | err_10 [ label=""]; 65 | err_11 [ label=""]; 66 | err_12 [ label=""]; 67 | err_13 [ label=""]; 68 | err_14 [ label=""]; 69 | err_15 [ label=""]; 70 | err_16 [ label=""]; 71 | err_17 [ label=""]; 72 | err_18 [ label=""]; 73 | err_19 [ label=""]; 74 | err_20 [ label=""]; 75 | err_21 [ label=""]; 76 | err_22 [ label=""]; 77 | err_23 [ label=""]; 78 | err_24 [ label=""]; 79 | err_25 [ label=""]; 80 | err_26 [ label=""]; 81 | err_27 [ label=""]; 82 | err_28 [ label=""]; 83 | err_29 [ label=""]; 84 | err_30 [ label=""]; 85 | err_31 [ label=""]; 86 | err_32 [ label=""]; 87 | err_33 [ label=""]; 88 | err_34 [ label=""]; 89 | err_35 [ label=""]; 90 | err_36 [ label=""]; 91 | err_37 [ label=""]; 92 | err_38 [ label=""]; 93 | err_39 [ label=""]; 94 | err_40 [ label=""]; 95 | err_41 [ label=""]; 96 | err_42 [ label=""]; 97 | err_43 [ label=""]; 98 | err_44 [ label=""]; 99 | err_45 [ label=""]; 100 | err_46 [ label=""]; 101 | err_47 [ label=""]; 102 | err_48 [ label=""]; 103 | err_49 [ label=""]; 104 | node [ fixedsize = true, height = 0.65, shape = doublecircle ]; 105 | 2; 106 | 3; 107 | 4; 108 | 5; 109 | 6; 110 | 7; 111 | 8; 112 | 9; 113 | 10; 114 | 11; 115 | 12; 116 | 13; 117 | 14; 118 | 15; 119 | 16; 120 | 17; 121 | 18; 122 | 19; 123 | 20; 124 | 21; 125 | 22; 126 | 23; 127 | 24; 128 | 25; 129 | 26; 130 | 27; 131 | 28; 132 | 29; 133 | 30; 134 | 31; 135 | 32; 136 | 33; 137 | 34; 138 | 35; 139 | 36; 140 | 37; 141 | 38; 142 | 39; 143 | 40; 144 | 41; 145 | 42; 146 | 43; 147 | 44; 148 | 45; 149 | 46; 150 | 47; 151 | 48; 152 | 49; 153 | node [ shape = circle ]; 154 | 1 -> 2 [ label = "'!'..'~' / mark" ]; 155 | 1 -> err_1 [ label = "DEF / err_appname" ]; 156 | 2 -> 3 [ label = "'!'..'~'" ]; 157 | 2 -> err_2 [ label = "DEF / err_appname" ]; 158 | 3 -> 4 [ label = "'!'..'~'" ]; 159 | 3 -> err_3 [ label = "DEF / err_appname" ]; 160 | 4 -> 5 [ label = "'!'..'~'" ]; 161 | 4 -> err_4 [ label = "DEF / err_appname" ]; 162 | 5 -> 6 [ label = "'!'..'~'" ]; 163 | 5 -> err_5 [ label = "DEF / err_appname" ]; 164 | 6 -> 7 [ label = "'!'..'~'" ]; 165 | 6 -> err_6 [ label = "DEF / err_appname" ]; 166 | 7 -> 8 [ label = "'!'..'~'" ]; 167 | 7 -> err_7 [ label = "DEF / err_appname" ]; 168 | 8 -> 9 [ label = "'!'..'~'" ]; 169 | 8 -> err_8 [ label = "DEF / err_appname" ]; 170 | 9 -> 10 [ label = "'!'..'~'" ]; 171 | 9 -> err_9 [ label = "DEF / err_appname" ]; 172 | 10 -> 11 [ label = "'!'..'~'" ]; 173 | 10 -> err_10 [ label = "DEF / err_appname" ]; 174 | 11 -> 12 [ label = "'!'..'~'" ]; 175 | 11 -> err_11 [ label = "DEF / err_appname" ]; 176 | 12 -> 13 [ label = "'!'..'~'" ]; 177 | 12 -> err_12 [ label = "DEF / err_appname" ]; 178 | 13 -> 14 [ label = "'!'..'~'" ]; 179 | 13 -> err_13 [ label = "DEF / err_appname" ]; 180 | 14 -> 15 [ label = "'!'..'~'" ]; 181 | 14 -> err_14 [ label = "DEF / err_appname" ]; 182 | 15 -> 16 [ label = "'!'..'~'" ]; 183 | 15 -> err_15 [ label = "DEF / err_appname" ]; 184 | 16 -> 17 [ label = "'!'..'~'" ]; 185 | 16 -> err_16 [ label = "DEF / err_appname" ]; 186 | 17 -> 18 [ label = "'!'..'~'" ]; 187 | 17 -> err_17 [ label = "DEF / err_appname" ]; 188 | 18 -> 19 [ label = "'!'..'~'" ]; 189 | 18 -> err_18 [ label = "DEF / err_appname" ]; 190 | 19 -> 20 [ label = "'!'..'~'" ]; 191 | 19 -> err_19 [ label = "DEF / err_appname" ]; 192 | 20 -> 21 [ label = "'!'..'~'" ]; 193 | 20 -> err_20 [ label = "DEF / err_appname" ]; 194 | 21 -> 22 [ label = "'!'..'~'" ]; 195 | 21 -> err_21 [ label = "DEF / err_appname" ]; 196 | 22 -> 23 [ label = "'!'..'~'" ]; 197 | 22 -> err_22 [ label = "DEF / err_appname" ]; 198 | 23 -> 24 [ label = "'!'..'~'" ]; 199 | 23 -> err_23 [ label = "DEF / err_appname" ]; 200 | 24 -> 25 [ label = "'!'..'~'" ]; 201 | 24 -> err_24 [ label = "DEF / err_appname" ]; 202 | 25 -> 26 [ label = "'!'..'~'" ]; 203 | 25 -> err_25 [ label = "DEF / err_appname" ]; 204 | 26 -> 27 [ label = "'!'..'~'" ]; 205 | 26 -> err_26 [ label = "DEF / err_appname" ]; 206 | 27 -> 28 [ label = "'!'..'~'" ]; 207 | 27 -> err_27 [ label = "DEF / err_appname" ]; 208 | 28 -> 29 [ label = "'!'..'~'" ]; 209 | 28 -> err_28 [ label = "DEF / err_appname" ]; 210 | 29 -> 30 [ label = "'!'..'~'" ]; 211 | 29 -> err_29 [ label = "DEF / err_appname" ]; 212 | 30 -> 31 [ label = "'!'..'~'" ]; 213 | 30 -> err_30 [ label = "DEF / err_appname" ]; 214 | 31 -> 32 [ label = "'!'..'~'" ]; 215 | 31 -> err_31 [ label = "DEF / err_appname" ]; 216 | 32 -> 33 [ label = "'!'..'~'" ]; 217 | 32 -> err_32 [ label = "DEF / err_appname" ]; 218 | 33 -> 34 [ label = "'!'..'~'" ]; 219 | 33 -> err_33 [ label = "DEF / err_appname" ]; 220 | 34 -> 35 [ label = "'!'..'~'" ]; 221 | 34 -> err_34 [ label = "DEF / err_appname" ]; 222 | 35 -> 36 [ label = "'!'..'~'" ]; 223 | 35 -> err_35 [ label = "DEF / err_appname" ]; 224 | 36 -> 37 [ label = "'!'..'~'" ]; 225 | 36 -> err_36 [ label = "DEF / err_appname" ]; 226 | 37 -> 38 [ label = "'!'..'~'" ]; 227 | 37 -> err_37 [ label = "DEF / err_appname" ]; 228 | 38 -> 39 [ label = "'!'..'~'" ]; 229 | 38 -> err_38 [ label = "DEF / err_appname" ]; 230 | 39 -> 40 [ label = "'!'..'~'" ]; 231 | 39 -> err_39 [ label = "DEF / err_appname" ]; 232 | 40 -> 41 [ label = "'!'..'~'" ]; 233 | 40 -> err_40 [ label = "DEF / err_appname" ]; 234 | 41 -> 42 [ label = "'!'..'~'" ]; 235 | 41 -> err_41 [ label = "DEF / err_appname" ]; 236 | 42 -> 43 [ label = "'!'..'~'" ]; 237 | 42 -> err_42 [ label = "DEF / err_appname" ]; 238 | 43 -> 44 [ label = "'!'..'~'" ]; 239 | 43 -> err_43 [ label = "DEF / err_appname" ]; 240 | 44 -> 45 [ label = "'!'..'~'" ]; 241 | 44 -> err_44 [ label = "DEF / err_appname" ]; 242 | 45 -> 46 [ label = "'!'..'~'" ]; 243 | 45 -> err_45 [ label = "DEF / err_appname" ]; 244 | 46 -> 47 [ label = "'!'..'~'" ]; 245 | 46 -> err_46 [ label = "DEF / err_appname" ]; 246 | 47 -> 48 [ label = "'!'..'~'" ]; 247 | 47 -> err_47 [ label = "DEF / err_appname" ]; 248 | 48 -> 49 [ label = "'!'..'~'" ]; 249 | 48 -> err_48 [ label = "DEF / err_appname" ]; 250 | 49 -> err_49 [ label = "DEF / err_appname" ]; 251 | ENTRY -> 1 [ label = "IN" ]; 252 | 1 -> eof_1 [ label = "EOF / err_appname" ]; 253 | 2 -> eof_2 [ label = "EOF / set_appname" ]; 254 | 3 -> eof_3 [ label = "EOF / set_appname" ]; 255 | 4 -> eof_4 [ label = "EOF / set_appname" ]; 256 | 5 -> eof_5 [ label = "EOF / set_appname" ]; 257 | 6 -> eof_6 [ label = "EOF / set_appname" ]; 258 | 7 -> eof_7 [ label = "EOF / set_appname" ]; 259 | 8 -> eof_8 [ label = "EOF / set_appname" ]; 260 | 9 -> eof_9 [ label = "EOF / set_appname" ]; 261 | 10 -> eof_10 [ label = "EOF / set_appname" ]; 262 | 11 -> eof_11 [ label = "EOF / set_appname" ]; 263 | 12 -> eof_12 [ label = "EOF / set_appname" ]; 264 | 13 -> eof_13 [ label = "EOF / set_appname" ]; 265 | 14 -> eof_14 [ label = "EOF / set_appname" ]; 266 | 15 -> eof_15 [ label = "EOF / set_appname" ]; 267 | 16 -> eof_16 [ label = "EOF / set_appname" ]; 268 | 17 -> eof_17 [ label = "EOF / set_appname" ]; 269 | 18 -> eof_18 [ label = "EOF / set_appname" ]; 270 | 19 -> eof_19 [ label = "EOF / set_appname" ]; 271 | 20 -> eof_20 [ label = "EOF / set_appname" ]; 272 | 21 -> eof_21 [ label = "EOF / set_appname" ]; 273 | 22 -> eof_22 [ label = "EOF / set_appname" ]; 274 | 23 -> eof_23 [ label = "EOF / set_appname" ]; 275 | 24 -> eof_24 [ label = "EOF / set_appname" ]; 276 | 25 -> eof_25 [ label = "EOF / set_appname" ]; 277 | 26 -> eof_26 [ label = "EOF / set_appname" ]; 278 | 27 -> eof_27 [ label = "EOF / set_appname" ]; 279 | 28 -> eof_28 [ label = "EOF / set_appname" ]; 280 | 29 -> eof_29 [ label = "EOF / set_appname" ]; 281 | 30 -> eof_30 [ label = "EOF / set_appname" ]; 282 | 31 -> eof_31 [ label = "EOF / set_appname" ]; 283 | 32 -> eof_32 [ label = "EOF / set_appname" ]; 284 | 33 -> eof_33 [ label = "EOF / set_appname" ]; 285 | 34 -> eof_34 [ label = "EOF / set_appname" ]; 286 | 35 -> eof_35 [ label = "EOF / set_appname" ]; 287 | 36 -> eof_36 [ label = "EOF / set_appname" ]; 288 | 37 -> eof_37 [ label = "EOF / set_appname" ]; 289 | 38 -> eof_38 [ label = "EOF / set_appname" ]; 290 | 39 -> eof_39 [ label = "EOF / set_appname" ]; 291 | 40 -> eof_40 [ label = "EOF / set_appname" ]; 292 | 41 -> eof_41 [ label = "EOF / set_appname" ]; 293 | 42 -> eof_42 [ label = "EOF / set_appname" ]; 294 | 43 -> eof_43 [ label = "EOF / set_appname" ]; 295 | 44 -> eof_44 [ label = "EOF / set_appname" ]; 296 | 45 -> eof_45 [ label = "EOF / set_appname" ]; 297 | 46 -> eof_46 [ label = "EOF / set_appname" ]; 298 | 47 -> eof_47 [ label = "EOF / set_appname" ]; 299 | 48 -> eof_48 [ label = "EOF / set_appname" ]; 300 | 49 -> eof_49 [ label = "EOF / set_appname" ]; 301 | } 302 | -------------------------------------------------------------------------------- /docs/rfc5424_msg.dot: -------------------------------------------------------------------------------- 1 | digraph rfc5424 { 2 | rankdir=LR; 3 | node [ shape = point ]; 4 | ENTRY; 5 | node [ shape = circle, height = 0.2 ]; 6 | node [ fixedsize = true, height = 0.65, shape = doublecircle ]; 7 | 1; 8 | 2; 9 | node [ shape = circle ]; 10 | 1 -> 2 [ label = "DEF / select_msg_mode" ]; 11 | ENTRY -> 1 [ label = "IN" ]; 12 | } 13 | -------------------------------------------------------------------------------- /docs/rfc5424_msg_any.dot: -------------------------------------------------------------------------------- 1 | digraph rfc5424 { 2 | rankdir=LR; 3 | node [ shape = point ]; 4 | ENTRY; 5 | eof_0; 6 | eof_1; 7 | node [ shape = circle, height = 0.2 ]; 8 | node [ fixedsize = true, height = 0.65, shape = doublecircle ]; 9 | 0; 10 | 1; 11 | node [ shape = circle ]; 12 | 0 -> 1 [ label = "DEF / mark, markmsg" ]; 13 | 1 -> 1 [ label = "DEF" ]; 14 | ENTRY -> 0 [ label = "IN" ]; 15 | 0 -> eof_0 [ label = "EOF / mark, markmsg, set_msg" ]; 16 | 1 -> eof_1 [ label = "EOF / set_msg" ]; 17 | } 18 | -------------------------------------------------------------------------------- /docs/rfc5424_msg_compliant.dot: -------------------------------------------------------------------------------- 1 | digraph rfc5424 { 2 | rankdir=LR; 3 | node [ shape = point ]; 4 | ENTRY; 5 | eof_1; 6 | eof_2; 7 | eof_3; 8 | eof_4; 9 | eof_5; 10 | eof_6; 11 | eof_7; 12 | eof_8; 13 | eof_9; 14 | eof_10; 15 | eof_11; 16 | eof_12; 17 | node [ shape = circle, height = 0.2 ]; 18 | err_1 [ label=""]; 19 | err_2 [ label=""]; 20 | err_3 [ label=""]; 21 | err_4 [ label=""]; 22 | err_5 [ label=""]; 23 | err_6 [ label=""]; 24 | err_7 [ label=""]; 25 | err_12 [ label=""]; 26 | node [ fixedsize = true, height = 0.65, shape = doublecircle ]; 27 | 8; 28 | 9; 29 | 10; 30 | 11; 31 | 12; 32 | node [ shape = circle ]; 33 | 1 -> 12 [ label = "128..191" ]; 34 | 1 -> err_1 [ label = "DEF / err_msg" ]; 35 | 2 -> 1 [ label = "160..191" ]; 36 | 2 -> err_2 [ label = "DEF / err_msg" ]; 37 | 3 -> 1 [ label = "128..191" ]; 38 | 3 -> err_3 [ label = "DEF / err_msg" ]; 39 | 4 -> 1 [ label = "128..159" ]; 40 | 4 -> err_4 [ label = "DEF / err_msg" ]; 41 | 5 -> 3 [ label = "144..191" ]; 42 | 5 -> err_5 [ label = "DEF / err_msg" ]; 43 | 6 -> 3 [ label = "128..191" ]; 44 | 6 -> err_6 [ label = "DEF / err_msg" ]; 45 | 7 -> 3 [ label = "128..143" ]; 46 | 7 -> err_7 [ label = "DEF / err_msg" ]; 47 | 8 -> 10 [ label = "239 / mark, markmsg" ]; 48 | 8 -> 9 [ label = "DEF / mark, markmsg" ]; 49 | 9 -> 9 [ label = "DEF" ]; 50 | 10 -> 11 [ label = "187" ]; 51 | 10 -> 9 [ label = "DEF" ]; 52 | 11 -> 12 [ label = "191" ]; 53 | 11 -> 9 [ label = "DEF" ]; 54 | 12 -> err_12 [ label = "128..193, 245..255 / err_msg" ]; 55 | 12 -> 1 [ label = "194..223" ]; 56 | 12 -> 2 [ label = "224" ]; 57 | 12 -> 3 [ label = "225..236, 238..239" ]; 58 | 12 -> 4 [ label = "237" ]; 59 | 12 -> 5 [ label = "240" ]; 60 | 12 -> 6 [ label = "241..243" ]; 61 | 12 -> 7 [ label = "244" ]; 62 | 12 -> 12 [ label = "DEF" ]; 63 | ENTRY -> 8 [ label = "IN" ]; 64 | 1 -> eof_1 [ label = "EOF / err_msg" ]; 65 | 2 -> eof_2 [ label = "EOF / err_msg" ]; 66 | 3 -> eof_3 [ label = "EOF / err_msg" ]; 67 | 4 -> eof_4 [ label = "EOF / err_msg" ]; 68 | 5 -> eof_5 [ label = "EOF / err_msg" ]; 69 | 6 -> eof_6 [ label = "EOF / err_msg" ]; 70 | 7 -> eof_7 [ label = "EOF / err_msg" ]; 71 | 8 -> eof_8 [ label = "EOF / mark, markmsg, set_msg" ]; 72 | 9 -> eof_9 [ label = "EOF / set_msg" ]; 73 | 10 -> eof_10 [ label = "EOF / set_msg" ]; 74 | 11 -> eof_11 [ label = "EOF / set_msg" ]; 75 | 12 -> eof_12 [ label = "EOF / set_msg" ]; 76 | } 77 | -------------------------------------------------------------------------------- /docs/rfc5424_msgid.dot: -------------------------------------------------------------------------------- 1 | digraph rfc5424 { 2 | rankdir=LR; 3 | node [ shape = point ]; 4 | ENTRY; 5 | eof_1; 6 | eof_2; 7 | eof_3; 8 | eof_4; 9 | eof_5; 10 | eof_6; 11 | eof_7; 12 | eof_8; 13 | eof_9; 14 | eof_10; 15 | eof_11; 16 | eof_12; 17 | eof_13; 18 | eof_14; 19 | eof_15; 20 | eof_16; 21 | eof_17; 22 | eof_18; 23 | eof_19; 24 | eof_20; 25 | eof_21; 26 | eof_22; 27 | eof_23; 28 | eof_24; 29 | eof_25; 30 | eof_26; 31 | eof_27; 32 | eof_28; 33 | eof_29; 34 | eof_30; 35 | eof_31; 36 | eof_32; 37 | eof_33; 38 | node [ shape = circle, height = 0.2 ]; 39 | err_1 [ label=""]; 40 | err_2 [ label=""]; 41 | err_3 [ label=""]; 42 | err_4 [ label=""]; 43 | err_5 [ label=""]; 44 | err_6 [ label=""]; 45 | err_7 [ label=""]; 46 | err_8 [ label=""]; 47 | err_9 [ label=""]; 48 | err_10 [ label=""]; 49 | err_11 [ label=""]; 50 | err_12 [ label=""]; 51 | err_13 [ label=""]; 52 | err_14 [ label=""]; 53 | err_15 [ label=""]; 54 | err_16 [ label=""]; 55 | err_17 [ label=""]; 56 | err_18 [ label=""]; 57 | err_19 [ label=""]; 58 | err_20 [ label=""]; 59 | err_21 [ label=""]; 60 | err_22 [ label=""]; 61 | err_23 [ label=""]; 62 | err_24 [ label=""]; 63 | err_25 [ label=""]; 64 | err_26 [ label=""]; 65 | err_27 [ label=""]; 66 | err_28 [ label=""]; 67 | err_29 [ label=""]; 68 | err_30 [ label=""]; 69 | err_31 [ label=""]; 70 | err_32 [ label=""]; 71 | err_33 [ label=""]; 72 | node [ fixedsize = true, height = 0.65, shape = doublecircle ]; 73 | 2; 74 | 3; 75 | 4; 76 | 5; 77 | 6; 78 | 7; 79 | 8; 80 | 9; 81 | 10; 82 | 11; 83 | 12; 84 | 13; 85 | 14; 86 | 15; 87 | 16; 88 | 17; 89 | 18; 90 | 19; 91 | 20; 92 | 21; 93 | 22; 94 | 23; 95 | 24; 96 | 25; 97 | 26; 98 | 27; 99 | 28; 100 | 29; 101 | 30; 102 | 31; 103 | 32; 104 | 33; 105 | node [ shape = circle ]; 106 | 1 -> 2 [ label = "'!'..'~' / mark" ]; 107 | 1 -> err_1 [ label = "DEF / err_msgid" ]; 108 | 2 -> 3 [ label = "'!'..'~'" ]; 109 | 2 -> err_2 [ label = "DEF / err_msgid" ]; 110 | 3 -> 4 [ label = "'!'..'~'" ]; 111 | 3 -> err_3 [ label = "DEF / err_msgid" ]; 112 | 4 -> 5 [ label = "'!'..'~'" ]; 113 | 4 -> err_4 [ label = "DEF / err_msgid" ]; 114 | 5 -> 6 [ label = "'!'..'~'" ]; 115 | 5 -> err_5 [ label = "DEF / err_msgid" ]; 116 | 6 -> 7 [ label = "'!'..'~'" ]; 117 | 6 -> err_6 [ label = "DEF / err_msgid" ]; 118 | 7 -> 8 [ label = "'!'..'~'" ]; 119 | 7 -> err_7 [ label = "DEF / err_msgid" ]; 120 | 8 -> 9 [ label = "'!'..'~'" ]; 121 | 8 -> err_8 [ label = "DEF / err_msgid" ]; 122 | 9 -> 10 [ label = "'!'..'~'" ]; 123 | 9 -> err_9 [ label = "DEF / err_msgid" ]; 124 | 10 -> 11 [ label = "'!'..'~'" ]; 125 | 10 -> err_10 [ label = "DEF / err_msgid" ]; 126 | 11 -> 12 [ label = "'!'..'~'" ]; 127 | 11 -> err_11 [ label = "DEF / err_msgid" ]; 128 | 12 -> 13 [ label = "'!'..'~'" ]; 129 | 12 -> err_12 [ label = "DEF / err_msgid" ]; 130 | 13 -> 14 [ label = "'!'..'~'" ]; 131 | 13 -> err_13 [ label = "DEF / err_msgid" ]; 132 | 14 -> 15 [ label = "'!'..'~'" ]; 133 | 14 -> err_14 [ label = "DEF / err_msgid" ]; 134 | 15 -> 16 [ label = "'!'..'~'" ]; 135 | 15 -> err_15 [ label = "DEF / err_msgid" ]; 136 | 16 -> 17 [ label = "'!'..'~'" ]; 137 | 16 -> err_16 [ label = "DEF / err_msgid" ]; 138 | 17 -> 18 [ label = "'!'..'~'" ]; 139 | 17 -> err_17 [ label = "DEF / err_msgid" ]; 140 | 18 -> 19 [ label = "'!'..'~'" ]; 141 | 18 -> err_18 [ label = "DEF / err_msgid" ]; 142 | 19 -> 20 [ label = "'!'..'~'" ]; 143 | 19 -> err_19 [ label = "DEF / err_msgid" ]; 144 | 20 -> 21 [ label = "'!'..'~'" ]; 145 | 20 -> err_20 [ label = "DEF / err_msgid" ]; 146 | 21 -> 22 [ label = "'!'..'~'" ]; 147 | 21 -> err_21 [ label = "DEF / err_msgid" ]; 148 | 22 -> 23 [ label = "'!'..'~'" ]; 149 | 22 -> err_22 [ label = "DEF / err_msgid" ]; 150 | 23 -> 24 [ label = "'!'..'~'" ]; 151 | 23 -> err_23 [ label = "DEF / err_msgid" ]; 152 | 24 -> 25 [ label = "'!'..'~'" ]; 153 | 24 -> err_24 [ label = "DEF / err_msgid" ]; 154 | 25 -> 26 [ label = "'!'..'~'" ]; 155 | 25 -> err_25 [ label = "DEF / err_msgid" ]; 156 | 26 -> 27 [ label = "'!'..'~'" ]; 157 | 26 -> err_26 [ label = "DEF / err_msgid" ]; 158 | 27 -> 28 [ label = "'!'..'~'" ]; 159 | 27 -> err_27 [ label = "DEF / err_msgid" ]; 160 | 28 -> 29 [ label = "'!'..'~'" ]; 161 | 28 -> err_28 [ label = "DEF / err_msgid" ]; 162 | 29 -> 30 [ label = "'!'..'~'" ]; 163 | 29 -> err_29 [ label = "DEF / err_msgid" ]; 164 | 30 -> 31 [ label = "'!'..'~'" ]; 165 | 30 -> err_30 [ label = "DEF / err_msgid" ]; 166 | 31 -> 32 [ label = "'!'..'~'" ]; 167 | 31 -> err_31 [ label = "DEF / err_msgid" ]; 168 | 32 -> 33 [ label = "'!'..'~'" ]; 169 | 32 -> err_32 [ label = "DEF / err_msgid" ]; 170 | 33 -> err_33 [ label = "DEF / err_msgid" ]; 171 | ENTRY -> 1 [ label = "IN" ]; 172 | 1 -> eof_1 [ label = "EOF / err_msgid" ]; 173 | 2 -> eof_2 [ label = "EOF / set_msgid" ]; 174 | 3 -> eof_3 [ label = "EOF / set_msgid" ]; 175 | 4 -> eof_4 [ label = "EOF / set_msgid" ]; 176 | 5 -> eof_5 [ label = "EOF / set_msgid" ]; 177 | 6 -> eof_6 [ label = "EOF / set_msgid" ]; 178 | 7 -> eof_7 [ label = "EOF / set_msgid" ]; 179 | 8 -> eof_8 [ label = "EOF / set_msgid" ]; 180 | 9 -> eof_9 [ label = "EOF / set_msgid" ]; 181 | 10 -> eof_10 [ label = "EOF / set_msgid" ]; 182 | 11 -> eof_11 [ label = "EOF / set_msgid" ]; 183 | 12 -> eof_12 [ label = "EOF / set_msgid" ]; 184 | 13 -> eof_13 [ label = "EOF / set_msgid" ]; 185 | 14 -> eof_14 [ label = "EOF / set_msgid" ]; 186 | 15 -> eof_15 [ label = "EOF / set_msgid" ]; 187 | 16 -> eof_16 [ label = "EOF / set_msgid" ]; 188 | 17 -> eof_17 [ label = "EOF / set_msgid" ]; 189 | 18 -> eof_18 [ label = "EOF / set_msgid" ]; 190 | 19 -> eof_19 [ label = "EOF / set_msgid" ]; 191 | 20 -> eof_20 [ label = "EOF / set_msgid" ]; 192 | 21 -> eof_21 [ label = "EOF / set_msgid" ]; 193 | 22 -> eof_22 [ label = "EOF / set_msgid" ]; 194 | 23 -> eof_23 [ label = "EOF / set_msgid" ]; 195 | 24 -> eof_24 [ label = "EOF / set_msgid" ]; 196 | 25 -> eof_25 [ label = "EOF / set_msgid" ]; 197 | 26 -> eof_26 [ label = "EOF / set_msgid" ]; 198 | 27 -> eof_27 [ label = "EOF / set_msgid" ]; 199 | 28 -> eof_28 [ label = "EOF / set_msgid" ]; 200 | 29 -> eof_29 [ label = "EOF / set_msgid" ]; 201 | 30 -> eof_30 [ label = "EOF / set_msgid" ]; 202 | 31 -> eof_31 [ label = "EOF / set_msgid" ]; 203 | 32 -> eof_32 [ label = "EOF / set_msgid" ]; 204 | 33 -> eof_33 [ label = "EOF / set_msgid" ]; 205 | } 206 | -------------------------------------------------------------------------------- /docs/rfc5424_pri.dot: -------------------------------------------------------------------------------- 1 | digraph rfc5424 { 2 | rankdir=LR; 3 | node [ shape = point ]; 4 | ENTRY; 5 | eof_1; 6 | eof_2; 7 | eof_3; 8 | eof_4; 9 | eof_5; 10 | eof_6; 11 | node [ shape = circle, height = 0.2 ]; 12 | err_1 [ label=""]; 13 | err_2 [ label=""]; 14 | err_3 [ label=""]; 15 | err_4 [ label=""]; 16 | err_5 [ label=""]; 17 | err_6 [ label=""]; 18 | node [ fixedsize = true, height = 0.65, shape = doublecircle ]; 19 | 7; 20 | node [ shape = circle ]; 21 | 1 -> 2 [ label = "'<'" ]; 22 | 1 -> err_1 [ label = "DEF / err_pri" ]; 23 | 2 -> 3 [ label = "'0' / mark" ]; 24 | 2 -> 4 [ label = "'1' / mark" ]; 25 | 2 -> 5 [ label = "'2'..'9' / mark" ]; 26 | 2 -> err_2 [ label = "DEF / err_prival, err_pri" ]; 27 | 3 -> 7 [ label = "'>' / set_prival" ]; 28 | 3 -> err_3 [ label = "DEF / set_prival, err_prival, err_pri" ]; 29 | 4 -> 5 [ label = "'0'..'8' / set_prival" ]; 30 | 4 -> 6 [ label = "'9' / set_prival" ]; 31 | 4 -> 7 [ label = "'>' / set_prival" ]; 32 | 4 -> err_4 [ label = "DEF / set_prival, err_prival, err_pri" ]; 33 | 5 -> 3 [ label = "'0'..'9' / set_prival" ]; 34 | 5 -> 7 [ label = "'>' / set_prival" ]; 35 | 5 -> err_5 [ label = "DEF / set_prival, err_prival, err_pri" ]; 36 | 6 -> 3 [ label = "'0'..'1' / set_prival" ]; 37 | 6 -> 7 [ label = "'>' / set_prival" ]; 38 | 6 -> err_6 [ label = "DEF / set_prival, err_prival, err_pri" ]; 39 | ENTRY -> 1 [ label = "IN" ]; 40 | 1 -> eof_1 [ label = "EOF / err_pri" ]; 41 | 2 -> eof_2 [ label = "EOF / err_prival, err_pri" ]; 42 | 3 -> eof_3 [ label = "EOF / err_prival, err_pri" ]; 43 | 4 -> eof_4 [ label = "EOF / err_prival, err_pri" ]; 44 | 5 -> eof_5 [ label = "EOF / err_prival, err_pri" ]; 45 | 6 -> eof_6 [ label = "EOF / err_prival, err_pri" ]; 46 | } 47 | -------------------------------------------------------------------------------- /docs/rfc5424_timestamp.dot: -------------------------------------------------------------------------------- 1 | digraph rfc5424 { 2 | rankdir=LR; 3 | node [ shape = point ]; 4 | ENTRY; 5 | eof_1; 6 | eof_2; 7 | eof_3; 8 | eof_4; 9 | eof_5; 10 | eof_6; 11 | eof_7; 12 | eof_8; 13 | eof_9; 14 | eof_10; 15 | eof_11; 16 | eof_12; 17 | eof_13; 18 | eof_14; 19 | eof_15; 20 | eof_16; 21 | eof_17; 22 | eof_18; 23 | eof_19; 24 | eof_20; 25 | eof_21; 26 | eof_22; 27 | eof_23; 28 | eof_24; 29 | eof_25; 30 | eof_26; 31 | eof_27; 32 | eof_28; 33 | eof_29; 34 | eof_30; 35 | eof_31; 36 | eof_32; 37 | eof_33; 38 | eof_34; 39 | eof_35; 40 | eof_36; 41 | eof_37; 42 | eof_39; 43 | node [ shape = circle, height = 0.2 ]; 44 | err_1 [ label=""]; 45 | err_2 [ label=""]; 46 | err_3 [ label=""]; 47 | err_4 [ label=""]; 48 | err_5 [ label=""]; 49 | err_6 [ label=""]; 50 | err_7 [ label=""]; 51 | err_8 [ label=""]; 52 | err_9 [ label=""]; 53 | err_10 [ label=""]; 54 | err_11 [ label=""]; 55 | err_12 [ label=""]; 56 | err_13 [ label=""]; 57 | err_14 [ label=""]; 58 | err_15 [ label=""]; 59 | err_16 [ label=""]; 60 | err_17 [ label=""]; 61 | err_18 [ label=""]; 62 | err_19 [ label=""]; 63 | err_20 [ label=""]; 64 | err_21 [ label=""]; 65 | err_22 [ label=""]; 66 | err_23 [ label=""]; 67 | err_24 [ label=""]; 68 | err_25 [ label=""]; 69 | err_26 [ label=""]; 70 | err_27 [ label=""]; 71 | err_28 [ label=""]; 72 | err_29 [ label=""]; 73 | err_30 [ label=""]; 74 | err_31 [ label=""]; 75 | err_32 [ label=""]; 76 | err_33 [ label=""]; 77 | err_34 [ label=""]; 78 | err_35 [ label=""]; 79 | err_36 [ label=""]; 80 | err_37 [ label=""]; 81 | err_39 [ label=""]; 82 | node [ fixedsize = true, height = 0.65, shape = doublecircle ]; 83 | 38; 84 | 39; 85 | node [ shape = circle ]; 86 | 1 -> 38 [ label = "'-'" ]; 87 | 1 -> 2 [ label = "'0'..'9' / mark" ]; 88 | 1 -> err_1 [ label = "DEF / err_timestamp" ]; 89 | 2 -> 3 [ label = "'0'..'9'" ]; 90 | 2 -> err_2 [ label = "DEF / err_timestamp" ]; 91 | 3 -> 4 [ label = "'0'..'9'" ]; 92 | 3 -> err_3 [ label = "DEF / err_timestamp" ]; 93 | 4 -> 5 [ label = "'0'..'9'" ]; 94 | 4 -> err_4 [ label = "DEF / err_timestamp" ]; 95 | 5 -> 6 [ label = "'-'" ]; 96 | 5 -> err_5 [ label = "DEF / err_timestamp" ]; 97 | 6 -> 7 [ label = "'0'" ]; 98 | 6 -> 37 [ label = "'1'" ]; 99 | 6 -> err_6 [ label = "DEF / err_timestamp" ]; 100 | 7 -> 8 [ label = "'1'..'9'" ]; 101 | 7 -> err_7 [ label = "DEF / err_timestamp" ]; 102 | 8 -> 9 [ label = "'-'" ]; 103 | 8 -> err_8 [ label = "DEF / err_timestamp" ]; 104 | 9 -> 10 [ label = "'0'" ]; 105 | 9 -> 35 [ label = "'1'..'2'" ]; 106 | 9 -> 36 [ label = "'3'" ]; 107 | 9 -> err_9 [ label = "DEF / err_timestamp" ]; 108 | 10 -> 11 [ label = "'1'..'9'" ]; 109 | 10 -> err_10 [ label = "DEF / err_timestamp" ]; 110 | 11 -> 12 [ label = "'T'" ]; 111 | 11 -> err_11 [ label = "DEF / err_timestamp" ]; 112 | 12 -> 13 [ label = "'0'..'1'" ]; 113 | 12 -> 34 [ label = "'2'" ]; 114 | 12 -> err_12 [ label = "DEF / err_timestamp" ]; 115 | 13 -> 14 [ label = "'0'..'9'" ]; 116 | 13 -> err_13 [ label = "DEF / err_timestamp" ]; 117 | 14 -> 15 [ label = "':'" ]; 118 | 14 -> err_14 [ label = "DEF / err_timestamp" ]; 119 | 15 -> 16 [ label = "'0'..'5'" ]; 120 | 15 -> err_15 [ label = "DEF / err_timestamp" ]; 121 | 16 -> 17 [ label = "'0'..'9'" ]; 122 | 16 -> err_16 [ label = "DEF / err_timestamp" ]; 123 | 17 -> 18 [ label = "':'" ]; 124 | 17 -> err_17 [ label = "DEF / err_timestamp" ]; 125 | 18 -> 19 [ label = "'0'..'5'" ]; 126 | 18 -> err_18 [ label = "DEF / err_timestamp" ]; 127 | 19 -> 20 [ label = "'0'..'9'" ]; 128 | 19 -> err_19 [ label = "DEF / err_timestamp" ]; 129 | 20 -> 21 [ label = "'+', '-'" ]; 130 | 20 -> 27 [ label = "'.'" ]; 131 | 20 -> 39 [ label = "'Z'" ]; 132 | 20 -> err_20 [ label = "DEF / err_timestamp" ]; 133 | 21 -> 22 [ label = "'0'..'1'" ]; 134 | 21 -> 26 [ label = "'2'" ]; 135 | 21 -> err_21 [ label = "DEF / err_timestamp" ]; 136 | 22 -> 23 [ label = "'0'..'9'" ]; 137 | 22 -> err_22 [ label = "DEF / err_timestamp" ]; 138 | 23 -> 24 [ label = "':'" ]; 139 | 23 -> err_23 [ label = "DEF / err_timestamp" ]; 140 | 24 -> 25 [ label = "'0'..'5'" ]; 141 | 24 -> err_24 [ label = "DEF / err_timestamp" ]; 142 | 25 -> 39 [ label = "'0'..'9'" ]; 143 | 25 -> err_25 [ label = "DEF / err_timestamp" ]; 144 | 26 -> 23 [ label = "'0'..'3'" ]; 145 | 26 -> err_26 [ label = "DEF / err_timestamp" ]; 146 | 27 -> 28 [ label = "'0'..'9'" ]; 147 | 27 -> err_27 [ label = "DEF / err_timestamp" ]; 148 | 28 -> 21 [ label = "'+', '-'" ]; 149 | 28 -> 29 [ label = "'0'..'9'" ]; 150 | 28 -> 39 [ label = "'Z'" ]; 151 | 28 -> err_28 [ label = "DEF / err_timestamp" ]; 152 | 29 -> 21 [ label = "'+', '-'" ]; 153 | 29 -> 30 [ label = "'0'..'9'" ]; 154 | 29 -> 39 [ label = "'Z'" ]; 155 | 29 -> err_29 [ label = "DEF / err_timestamp" ]; 156 | 30 -> 21 [ label = "'+', '-'" ]; 157 | 30 -> 31 [ label = "'0'..'9'" ]; 158 | 30 -> 39 [ label = "'Z'" ]; 159 | 30 -> err_30 [ label = "DEF / err_timestamp" ]; 160 | 31 -> 21 [ label = "'+', '-'" ]; 161 | 31 -> 32 [ label = "'0'..'9'" ]; 162 | 31 -> 39 [ label = "'Z'" ]; 163 | 31 -> err_31 [ label = "DEF / err_timestamp" ]; 164 | 32 -> 21 [ label = "'+', '-'" ]; 165 | 32 -> 33 [ label = "'0'..'9'" ]; 166 | 32 -> 39 [ label = "'Z'" ]; 167 | 32 -> err_32 [ label = "DEF / err_timestamp" ]; 168 | 33 -> 21 [ label = "'+', '-'" ]; 169 | 33 -> 39 [ label = "'Z'" ]; 170 | 33 -> err_33 [ label = "DEF / err_timestamp" ]; 171 | 34 -> 14 [ label = "'0'..'3'" ]; 172 | 34 -> err_34 [ label = "DEF / err_timestamp" ]; 173 | 35 -> 11 [ label = "'0'..'9'" ]; 174 | 35 -> err_35 [ label = "DEF / err_timestamp" ]; 175 | 36 -> 11 [ label = "'0'..'1'" ]; 176 | 36 -> err_36 [ label = "DEF / err_timestamp" ]; 177 | 37 -> 8 [ label = "'0'..'2'" ]; 178 | 37 -> err_37 [ label = "DEF / err_timestamp" ]; 179 | 39 -> err_39 [ label = "DEF / set_timestamp" ]; 180 | ENTRY -> 1 [ label = "IN" ]; 181 | 1 -> eof_1 [ label = "EOF / err_timestamp" ]; 182 | 2 -> eof_2 [ label = "EOF / err_timestamp" ]; 183 | 3 -> eof_3 [ label = "EOF / err_timestamp" ]; 184 | 4 -> eof_4 [ label = "EOF / err_timestamp" ]; 185 | 5 -> eof_5 [ label = "EOF / err_timestamp" ]; 186 | 6 -> eof_6 [ label = "EOF / err_timestamp" ]; 187 | 7 -> eof_7 [ label = "EOF / err_timestamp" ]; 188 | 8 -> eof_8 [ label = "EOF / err_timestamp" ]; 189 | 9 -> eof_9 [ label = "EOF / err_timestamp" ]; 190 | 10 -> eof_10 [ label = "EOF / err_timestamp" ]; 191 | 11 -> eof_11 [ label = "EOF / err_timestamp" ]; 192 | 12 -> eof_12 [ label = "EOF / err_timestamp" ]; 193 | 13 -> eof_13 [ label = "EOF / err_timestamp" ]; 194 | 14 -> eof_14 [ label = "EOF / err_timestamp" ]; 195 | 15 -> eof_15 [ label = "EOF / err_timestamp" ]; 196 | 16 -> eof_16 [ label = "EOF / err_timestamp" ]; 197 | 17 -> eof_17 [ label = "EOF / err_timestamp" ]; 198 | 18 -> eof_18 [ label = "EOF / err_timestamp" ]; 199 | 19 -> eof_19 [ label = "EOF / err_timestamp" ]; 200 | 20 -> eof_20 [ label = "EOF / err_timestamp" ]; 201 | 21 -> eof_21 [ label = "EOF / err_timestamp" ]; 202 | 22 -> eof_22 [ label = "EOF / err_timestamp" ]; 203 | 23 -> eof_23 [ label = "EOF / err_timestamp" ]; 204 | 24 -> eof_24 [ label = "EOF / err_timestamp" ]; 205 | 25 -> eof_25 [ label = "EOF / err_timestamp" ]; 206 | 26 -> eof_26 [ label = "EOF / err_timestamp" ]; 207 | 27 -> eof_27 [ label = "EOF / err_timestamp" ]; 208 | 28 -> eof_28 [ label = "EOF / err_timestamp" ]; 209 | 29 -> eof_29 [ label = "EOF / err_timestamp" ]; 210 | 30 -> eof_30 [ label = "EOF / err_timestamp" ]; 211 | 31 -> eof_31 [ label = "EOF / err_timestamp" ]; 212 | 32 -> eof_32 [ label = "EOF / err_timestamp" ]; 213 | 33 -> eof_33 [ label = "EOF / err_timestamp" ]; 214 | 34 -> eof_34 [ label = "EOF / err_timestamp" ]; 215 | 35 -> eof_35 [ label = "EOF / err_timestamp" ]; 216 | 36 -> eof_36 [ label = "EOF / err_timestamp" ]; 217 | 37 -> eof_37 [ label = "EOF / err_timestamp" ]; 218 | 39 -> eof_39 [ label = "EOF / set_timestamp" ]; 219 | } 220 | -------------------------------------------------------------------------------- /docs/rfc5424_version.dot: -------------------------------------------------------------------------------- 1 | digraph rfc5424 { 2 | rankdir=LR; 3 | node [ shape = point ]; 4 | ENTRY; 5 | eof_1; 6 | eof_2; 7 | eof_3; 8 | eof_4; 9 | node [ shape = circle, height = 0.2 ]; 10 | err_1 [ label=""]; 11 | err_3 [ label=""]; 12 | err_4 [ label=""]; 13 | node [ fixedsize = true, height = 0.65, shape = doublecircle ]; 14 | 2; 15 | 3; 16 | 4; 17 | node [ shape = circle ]; 18 | 1 -> 2 [ label = "'1'..'9' / mark" ]; 19 | 1 -> err_1 [ label = "DEF / err_version" ]; 20 | 2 -> 3 [ label = "'0'..'9' / set_version" ]; 21 | 3 -> 4 [ label = "'0'..'9' / set_version" ]; 22 | 3 -> err_3 [ label = "DEF / set_version, err_version" ]; 23 | 4 -> err_4 [ label = "DEF / set_version, err_version" ]; 24 | ENTRY -> 1 [ label = "IN" ]; 25 | 1 -> eof_1 [ label = "EOF / err_version" ]; 26 | 2 -> eof_2 [ label = "EOF / set_version" ]; 27 | 3 -> eof_3 [ label = "EOF / set_version" ]; 28 | 4 -> eof_4 [ label = "EOF / set_version" ]; 29 | } 30 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/influxdata/go-syslog/v3 2 | 3 | require ( 4 | github.com/davecgh/go-spew v1.1.1 5 | github.com/leodido/ragel-machinery v0.0.0-20181214104525-299bdde78165 6 | github.com/pmezard/go-difflib v1.0.0 // indirect 7 | github.com/stretchr/testify v1.2.2 8 | golang.org/x/text v0.3.8 9 | ) 10 | 11 | go 1.13 12 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 2 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 3 | github.com/google/go-cmp v0.2.0 h1:+dTQ8DZQJz0Mb/HjFlkptS1FeQ4cWSnN941F8aEG4SQ= 4 | github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= 5 | github.com/leodido/ragel-machinery v0.0.0-20181214104525-299bdde78165 h1:bCiVCRCs1Heq84lurVinUPy19keqGEe4jh5vtK37jcg= 6 | github.com/leodido/ragel-machinery v0.0.0-20181214104525-299bdde78165/go.mod h1:WZxr2/6a/Ar9bMDc2rN/LJrE/hF6bXE4LPyDSIxwAfg= 7 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 8 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 9 | github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w= 10 | github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 11 | github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= 12 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 13 | golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= 14 | golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= 15 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 16 | golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 17 | golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= 18 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 19 | golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 20 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 21 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 22 | golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 23 | golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 24 | golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 25 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 26 | golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= 27 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 28 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 29 | golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= 30 | golang.org/x/text v0.3.8 h1:nAL+RVCQ9uMn3vJZbV+MRnydTJFPf8qqY42YiA6MrqY= 31 | golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= 32 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 33 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 34 | golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= 35 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 36 | -------------------------------------------------------------------------------- /makefile: -------------------------------------------------------------------------------- 1 | SHELL := /bin/bash 2 | RAGEL := ragel -I common 3 | 4 | export GO_TEST=env GOTRACEBACK=all GO111MODULE=on go test $(GO_ARGS) 5 | 6 | .PHONY: build 7 | build: rfc5424/machine.go rfc5424/builder.go nontransparent/parser.go rfc3164/machine.go 8 | @gofmt -w -s ./rfc5424 9 | @gofmt -w -s ./rfc3164 10 | @gofmt -w -s ./octetcounting 11 | @gofmt -w -s ./nontransparent 12 | 13 | rfc5424/machine.go: rfc5424/machine.go.rl common/common.rl 14 | 15 | rfc5424/builder.go: rfc5424/builder.go.rl common/common.rl 16 | 17 | rfc3164/machine.go: rfc3164/machine.go.rl common/common.rl 18 | 19 | nontransparent/parser.go: nontransparent/parser.go.rl 20 | 21 | rfc5424/builder.go rfc5424/machine.go: 22 | $(RAGEL) -Z -G2 -e -o $@ $< 23 | @sed -i '/^\/\/line/d' $@ 24 | $(MAKE) file=$@ snake2camel 25 | 26 | rfc3164/machine.go: 27 | $(RAGEL) -Z -G2 -e -o $@ $< 28 | @sed -i '/^\/\/line/d' $@ 29 | $(MAKE) file=$@ snake2camel 30 | 31 | nontransparent/parser.go: 32 | $(RAGEL) -Z -G2 -e -o $@ $< 33 | @sed -i '/^\/\/line/d' $@ 34 | $(MAKE) file=$@ snake2camel 35 | 36 | .PHONY: snake2camel 37 | snake2camel: 38 | @awk -i inplace '{ \ 39 | while ( match($$0, /(.*)([a-z]+[0-9]*)_([a-zA-Z0-9])(.*)/, cap) ) \ 40 | $$0 = cap[1] cap[2] toupper(cap[3]) cap[4]; \ 41 | print \ 42 | }' $(file) 43 | 44 | .PHONY: bench 45 | bench: rfc5424/*_test.go rfc5424/machine.go octetcounting/performance_test.go 46 | go test -bench=. -benchmem -benchtime=5s ./... 47 | 48 | .PHONY: tests 49 | tests: 50 | $(GO_TEST) ./... 51 | 52 | docs/nontransparent.dot: nontransparent/parser.go.rl 53 | $(RAGEL) -Z -Vp $< -o $@ 54 | 55 | docs/nontransparent.png: docs/nontransparent.dot 56 | dot $< -Tpng -o $@ 57 | 58 | docs/rfc5424.dot: rfc5424/machine.go.rl common/common.rl 59 | $(RAGEL) -Z -Vp $< -o $@ 60 | 61 | docs/rfc5424_pri.dot: rfc5424/machine.go.rl common/common.rl 62 | $(RAGEL) -Z -Vp -M pri $< -o $@ 63 | 64 | docs/rfc5424_pri.png: docs/rfc5424_pri.dot 65 | dot $< -Tpng -o $@ 66 | 67 | docs/rfc5424_version.dot: rfc5424/machine.go.rl common/common.rl 68 | $(RAGEL) -Z -Vp -M version $< -o $@ 69 | 70 | docs/rfc5424_version.png: docs/rfc5424_version.dot 71 | dot $< -Tpng -o $@ 72 | 73 | docs/rfc5424_timestamp.dot: rfc5424/machine.go.rl common/common.rl 74 | $(RAGEL) -Z -Vp -M timestamp $< -o $@ 75 | 76 | docs/rfc5424_timestamp.png: docs/rfc5424_timestamp.dot 77 | dot $< -Tpng -o $@ 78 | 79 | docs/rfc5424_hostname.dot: rfc5424/machine.go.rl common/common.rl 80 | $(RAGEL) -Z -Vp -M hostname $< -o $@ 81 | 82 | docs/rfc5424_hostname.png: docs/rfc5424_hostname.dot 83 | dot $< -Tpng -o $@ 84 | 85 | docs/rfc5424_appname.dot: rfc5424/machine.go.rl common/common.rl 86 | $(RAGEL) -Z -Vp -M appname $< -o $@ 87 | 88 | docs/rfc5424_appname.png: docs/rfc5424_appname.dot 89 | dot $< -Tpng -o $@ 90 | 91 | docs/rfc5424_procid.dot: rfc5424/machine.go.rl common/common.rl 92 | $(RAGEL) -Z -Vp -M procid $< -o $@ 93 | 94 | docs/rfc5424_procid.png: docs/rfc5424_procid.dot 95 | dot $< -Tpng -o $@ 96 | 97 | docs/rfc5424_msgid.dot: rfc5424/machine.go.rl common/common.rl 98 | $(RAGEL) -Z -Vp -M msgid $< -o $@ 99 | 100 | docs/rfc5424_msgid.png: docs/rfc5424_msgid.dot 101 | dot $< -Tpng -o $@ 102 | 103 | docs/rfc5424_structureddata.dot: rfc5424/machine.go.rl common/common.rl 104 | $(RAGEL) -Z -Vp -M structureddata $< -o $@ 105 | 106 | docs/rfc5424_structureddata.png: docs/rfc5424_structureddata.dot 107 | dot $< -Tpng -o $@ 108 | 109 | docs/rfc5424_msg.dot: rfc5424/machine.go.rl common/common.rl 110 | $(RAGEL) -Z -Vp -M msg $< -o $@ 111 | 112 | docs/rfc5424_msg.png: docs/rfc5424_msg.dot 113 | dot $< -Tpng -o $@ 114 | 115 | docs/rfc5424_msg_any.dot: rfc5424/machine.go.rl common/common.rl 116 | $(RAGEL) -Z -Vp -M msg_any $< -o $@ 117 | 118 | docs/rfc5424_msg_any.png: docs/rfc5424_msg_any.dot 119 | dot $< -Tpng -o $@ 120 | 121 | docs/rfc5424_msg_compliant.dot: rfc5424/machine.go.rl common/common.rl 122 | $(RAGEL) -Z -Vp -M msg_compliant $< -o $@ 123 | 124 | docs/rfc5424_msg_compliant.png: docs/rfc5424_msg_compliant.dot 125 | dot $< -Tpng -o $@ 126 | 127 | docs/rfc3164.dot: rfc3164/machine.go.rl common/common.rl 128 | $(RAGEL) -Z -Vp $< -o $@ 129 | 130 | docs/rfc3164_pri.dot: rfc3164/machine.go.rl common/common.rl 131 | $(RAGEL) -Z -Vp -M pri $< -o $@ 132 | 133 | docs/rfc3164_pri.png: docs/rfc3164_pri.dot 134 | dot $< -Tpng -o $@ 135 | 136 | docs/rfc3164_timestamp.dot: rfc3164/machine.go.rl common/common.rl 137 | $(RAGEL) -Z -Vp -M timestamp $< -o $@ 138 | 139 | docs/rfc3164_timestamp.png: docs/rfc3164_timestamp.dot 140 | dot $< -Tpng -o $@ 141 | 142 | docs/rfc3164_hostname.dot: rfc3164/machine.go.rl common/common.rl 143 | $(RAGEL) -Z -Vp -M hostname $< -o $@ 144 | 145 | docs/rfc3164_hostname.png: docs/rfc3164_hostname.dot 146 | dot $< -Tpng -o $@ 147 | 148 | docs/rfc3164_tag.dot: rfc3164/machine.go.rl common/common.rl 149 | $(RAGEL) -Z -Vp -M tag $< -o $@ 150 | 151 | docs/rfc3164_tag.png: docs/rfc3164_tag.dot 152 | dot $< -Tpng -o $@ 153 | 154 | docs/rfc3164_content.dot: rfc3164/machine.go.rl common/common.rl 155 | $(RAGEL) -Z -Vp -M content $< -o $@ 156 | 157 | docs/rfc3164_content.png: docs/rfc3164_content.dot 158 | dot $< -Tpng -o $@ 159 | 160 | docs/rfc3164_msg.dot: rfc3164/machine.go.rl common/common.rl 161 | $(RAGEL) -Z -Vp -M msg $< -o $@ 162 | 163 | docs/rfc3164_msg.png: docs/rfc3164_msg.dot 164 | dot $< -Tpng -o $@ 165 | 166 | docs: 167 | @mkdir -p docs 168 | 169 | .PHONY: dots 170 | dots: docs 171 | $(MAKE) -s docs/nontransparent.dot docs/rfc5424.dot docs/rfc5424_pri.dot docs/rfc5424_version.dot docs/rfc5424_timestamp.dot docs/rfc5424_hostname.dot docs/rfc5424_appname.dot docs/rfc5424_procid.dot docs/rfc5424_msgid.dot docs/rfc5424_structureddata.dot docs/rfc5424_msg.dot docs/rfc5424_msg_any.dot docs/rfc5424_msg_compliant.dot docs/rfc3164.dot docs/rfc3164_pri.dot docs/rfc3164_timestamp.dot docs/rfc3164_hostname.dot docs/rfc3164_tag.dot docs/rfc3164_content.dot docs/rfc3164_msg.dot 172 | 173 | .PHONY: imgs 174 | imgs: dots docs/nontransparent.png docs/rfc5424_pri.png docs/rfc5424_version.png docs/rfc5424_timestamp.png docs/rfc5424_hostname.png docs/rfc5424_appname.png docs/rfc5424_procid.png docs/rfc5424_msgid.png docs/rfc5424_structureddata.png docs/rfc5424_msg.png docs/rfc5424_msg_any.png docs/rfc5424_msg_compliant.png docs/rfc3164_pri.png docs/rfc3164_timestamp.png docs/rfc3164_hostname.png docs/rfc3164_tag.png docs/rfc3164_content.png docs/rfc3164_msg.png 175 | 176 | .PHONY: clean 177 | clean: rfc5424/machine.go rfc5424/builder.go nontransparent/parser.go rfc3164/machine.go 178 | @rm -f $? 179 | @rm -rf docs 180 | -------------------------------------------------------------------------------- /nontransparent/example_test.go: -------------------------------------------------------------------------------- 1 | package nontransparent 2 | 3 | import ( 4 | "github.com/davecgh/go-spew/spew" 5 | "io" 6 | "math/rand" 7 | "strings" 8 | 9 | "github.com/influxdata/go-syslog/v3" 10 | "time" 11 | ) 12 | 13 | func Example_withoutTrailerAtEnd() { 14 | results := []syslog.Result{} 15 | acc := func(res *syslog.Result) { 16 | results = append(results, *res) 17 | } 18 | // Notice the message ends without trailer but we catch it anyway 19 | r := strings.NewReader("<1>1 2003-10-11T22:14:15.003Z host.local - - - - mex") 20 | NewParser(syslog.WithListener(acc)).Parse(r) 21 | output(results) 22 | // Output: 23 | // ([]syslog.Result) (len=1) { 24 | // (syslog.Result) { 25 | // Message: (*rfc5424.SyslogMessage)({ 26 | // Base: (syslog.Base) { 27 | // Facility: (*uint8)(0), 28 | // Severity: (*uint8)(1), 29 | // Priority: (*uint8)(1), 30 | // Timestamp: (*time.Time)(2003-10-11 22:14:15.003 +0000 UTC), 31 | // Hostname: (*string)((len=10) "host.local"), 32 | // Appname: (*string)(), 33 | // ProcID: (*string)(), 34 | // MsgID: (*string)(), 35 | // Message: (*string)((len=3) "mex") 36 | // }, 37 | // Version: (uint16) 1, 38 | // StructuredData: (*map[string]map[string]string)() 39 | // }), 40 | // Error: (*ragel.ReadingError)(unexpected EOF) 41 | // } 42 | // } 43 | } 44 | 45 | func Example_bestEffortOnLastOne() { 46 | results := []syslog.Result{} 47 | acc := func(res *syslog.Result) { 48 | results = append(results, *res) 49 | } 50 | r := strings.NewReader("<1>1 - - - - - - -\n<3>1\n") 51 | NewParser(syslog.WithBestEffort(), syslog.WithListener(acc)).Parse(r) 52 | output(results) 53 | // Output: 54 | // ([]syslog.Result) (len=2) { 55 | // (syslog.Result) { 56 | // Message: (*rfc5424.SyslogMessage)({ 57 | // Base: (syslog.Base) { 58 | // Facility: (*uint8)(0), 59 | // Severity: (*uint8)(1), 60 | // Priority: (*uint8)(1), 61 | // Timestamp: (*time.Time)(), 62 | // Hostname: (*string)(), 63 | // Appname: (*string)(), 64 | // ProcID: (*string)(), 65 | // MsgID: (*string)(), 66 | // Message: (*string)((len=1) "-") 67 | // }, 68 | // Version: (uint16) 1, 69 | // StructuredData: (*map[string]map[string]string)() 70 | // }), 71 | // Error: (error) 72 | // }, 73 | // (syslog.Result) { 74 | // Message: (*rfc5424.SyslogMessage)({ 75 | // Base: (syslog.Base) { 76 | // Facility: (*uint8)(0), 77 | // Severity: (*uint8)(3), 78 | // Priority: (*uint8)(3), 79 | // Timestamp: (*time.Time)(), 80 | // Hostname: (*string)(), 81 | // Appname: (*string)(), 82 | // ProcID: (*string)(), 83 | // MsgID: (*string)(), 84 | // Message: (*string)() 85 | // }, 86 | // Version: (uint16) 1, 87 | // StructuredData: (*map[string]map[string]string)() 88 | // }), 89 | // Error: (*errors.errorString)(parsing error [col 4]) 90 | // } 91 | // } 92 | } 93 | 94 | func Example_intoChannelWithLF() { 95 | messages := []string{ 96 | "<2>1 - - - - - - A\nB", 97 | "<1>1 -", 98 | "<1>1 - - - - - - A\nB\nC\nD", 99 | } 100 | 101 | r, w := io.Pipe() 102 | 103 | go func() { 104 | defer w.Close() 105 | 106 | for _, m := range messages { 107 | // Write message (containing trailers to be interpreted as part of the syslog MESSAGE) 108 | w.Write([]byte(m)) 109 | // Write non-transparent frame boundary 110 | w.Write([]byte{10}) 111 | // Wait a random amount of time 112 | time.Sleep(time.Millisecond * time.Duration(rand.Intn(100))) 113 | } 114 | }() 115 | 116 | results := make(chan *syslog.Result) 117 | ln := func(x *syslog.Result) { 118 | // Emit the result 119 | results <- x 120 | } 121 | 122 | p := NewParser(syslog.WithListener(ln), syslog.WithBestEffort()) 123 | go func() { 124 | defer close(results) 125 | defer r.Close() 126 | p.Parse(r) 127 | }() 128 | 129 | // Consume results 130 | for r := range results { 131 | output(r) 132 | } 133 | 134 | // Output: 135 | // (*syslog.Result)({ 136 | // Message: (*rfc5424.SyslogMessage)({ 137 | // Base: (syslog.Base) { 138 | // Facility: (*uint8)(0), 139 | // Severity: (*uint8)(2), 140 | // Priority: (*uint8)(2), 141 | // Timestamp: (*time.Time)(), 142 | // Hostname: (*string)(), 143 | // Appname: (*string)(), 144 | // ProcID: (*string)(), 145 | // MsgID: (*string)(), 146 | // Message: (*string)((len=3) "A\nB") 147 | // }, 148 | // Version: (uint16) 1, 149 | // StructuredData: (*map[string]map[string]string)() 150 | // }), 151 | // Error: (error) 152 | // }) 153 | // (*syslog.Result)({ 154 | // Message: (*rfc5424.SyslogMessage)({ 155 | // Base: (syslog.Base) { 156 | // Facility: (*uint8)(0), 157 | // Severity: (*uint8)(1), 158 | // Priority: (*uint8)(1), 159 | // Timestamp: (*time.Time)(), 160 | // Hostname: (*string)(), 161 | // Appname: (*string)(), 162 | // ProcID: (*string)(), 163 | // MsgID: (*string)(), 164 | // Message: (*string)() 165 | // }, 166 | // Version: (uint16) 1, 167 | // StructuredData: (*map[string]map[string]string)() 168 | // }), 169 | // Error: (*errors.errorString)(parsing error [col 6]) 170 | // }) 171 | // (*syslog.Result)({ 172 | // Message: (*rfc5424.SyslogMessage)({ 173 | // Base: (syslog.Base) { 174 | // Facility: (*uint8)(0), 175 | // Severity: (*uint8)(1), 176 | // Priority: (*uint8)(1), 177 | // Timestamp: (*time.Time)(), 178 | // Hostname: (*string)(), 179 | // Appname: (*string)(), 180 | // ProcID: (*string)(), 181 | // MsgID: (*string)(), 182 | // Message: (*string)((len=7) "A\nB\nC\nD") 183 | // }, 184 | // Version: (uint16) 1, 185 | // StructuredData: (*map[string]map[string]string)() 186 | // }), 187 | // Error: (error) 188 | // }) 189 | 190 | } 191 | 192 | func Example_intoChannelWithNUL() { 193 | messages := []string{ 194 | "<2>1 - - - - - - A\x00B", 195 | "<1>1 -", 196 | "<1>1 - - - - - - A\x00B\x00C\x00D", 197 | } 198 | 199 | r, w := io.Pipe() 200 | 201 | go func() { 202 | defer w.Close() 203 | 204 | for _, m := range messages { 205 | // Write message (containing trailers to be interpreted as part of the syslog MESSAGE) 206 | w.Write([]byte(m)) 207 | // Write non-transparent frame boundary 208 | w.Write([]byte{0}) 209 | // Wait a random amount of time 210 | time.Sleep(time.Millisecond * time.Duration(rand.Intn(100))) 211 | } 212 | }() 213 | 214 | results := make(chan *syslog.Result) 215 | ln := func(x *syslog.Result) { 216 | // Emit the result 217 | results <- x 218 | } 219 | 220 | p := NewParser(syslog.WithListener(ln), WithTrailer(NUL)) 221 | 222 | go func() { 223 | defer close(results) 224 | defer r.Close() 225 | p.Parse(r) 226 | }() 227 | 228 | // Range over the results channel 229 | for r := range results { 230 | output(r) 231 | } 232 | 233 | // Output: 234 | // (*syslog.Result)({ 235 | // Message: (*rfc5424.SyslogMessage)({ 236 | // Base: (syslog.Base) { 237 | // Facility: (*uint8)(0), 238 | // Severity: (*uint8)(2), 239 | // Priority: (*uint8)(2), 240 | // Timestamp: (*time.Time)(), 241 | // Hostname: (*string)(), 242 | // Appname: (*string)(), 243 | // ProcID: (*string)(), 244 | // MsgID: (*string)(), 245 | // Message: (*string)((len=3) "A\x00B") 246 | // }, 247 | // Version: (uint16) 1, 248 | // StructuredData: (*map[string]map[string]string)() 249 | // }), 250 | // Error: (error) 251 | // }) 252 | // (*syslog.Result)({ 253 | // Message: (syslog.Message) , 254 | // Error: (*errors.errorString)(parsing error [col 6]) 255 | // }) 256 | // (*syslog.Result)({ 257 | // Message: (*rfc5424.SyslogMessage)({ 258 | // Base: (syslog.Base) { 259 | // Facility: (*uint8)(0), 260 | // Severity: (*uint8)(1), 261 | // Priority: (*uint8)(1), 262 | // Timestamp: (*time.Time)(), 263 | // Hostname: (*string)(), 264 | // Appname: (*string)(), 265 | // ProcID: (*string)(), 266 | // MsgID: (*string)(), 267 | // Message: (*string)((len=7) "A\x00B\x00C\x00D") 268 | // }, 269 | // Version: (uint16) 1, 270 | // StructuredData: (*map[string]map[string]string)() 271 | // }), 272 | // Error: (error) 273 | // }) 274 | } 275 | 276 | func output(out interface{}) { 277 | spew.Config.DisableCapacities = true 278 | spew.Config.DisablePointerAddresses = true 279 | spew.Dump(out) 280 | } 281 | -------------------------------------------------------------------------------- /nontransparent/parser.go: -------------------------------------------------------------------------------- 1 | package nontransparent 2 | 3 | import ( 4 | "io" 5 | 6 | syslog "github.com/influxdata/go-syslog/v3" 7 | "github.com/influxdata/go-syslog/v3/rfc5424" 8 | parser "github.com/leodido/ragel-machinery/parser" 9 | ) 10 | 11 | const nontransparentStart int = 1 12 | const nontransparentError int = 0 13 | 14 | const nontransparentEnMain int = 1 15 | 16 | type machine struct { 17 | trailertyp TrailerType // default is 0 thus TrailerType(LF) 18 | trailer byte 19 | candidate []byte 20 | bestEffort bool 21 | internal syslog.Machine 22 | emit syslog.ParserListener 23 | readError error 24 | lastChunk []byte // store last candidate message also if it does not ends with a trailer 25 | } 26 | 27 | // Exec implements the ragel.Parser interface. 28 | func (m *machine) Exec(s *parser.State) (int, int) { 29 | // Retrieve previously stored parsing variables 30 | cs, p, pe, eof, data := s.Get() 31 | 32 | { 33 | var _widec int16 34 | if p == pe { 35 | goto _testEof 36 | } 37 | switch cs { 38 | case 1: 39 | goto stCase1 40 | case 0: 41 | goto stCase0 42 | case 2: 43 | goto stCase2 44 | case 3: 45 | goto stCase3 46 | } 47 | goto stOut 48 | stCase1: 49 | if data[p] == 60 { 50 | goto tr0 51 | } 52 | goto st0 53 | stCase0: 54 | st0: 55 | cs = 0 56 | goto _out 57 | tr0: 58 | 59 | if len(m.candidate) > 0 { 60 | m.process() 61 | } 62 | m.candidate = make([]byte, 0) 63 | 64 | goto st2 65 | st2: 66 | if p++; p == pe { 67 | goto _testEof2 68 | } 69 | stCase2: 70 | _widec = int16(data[p]) 71 | switch { 72 | case data[p] > 0: 73 | if 10 <= data[p] && data[p] <= 10 { 74 | _widec = 256 + (int16(data[p]) - 0) 75 | if m.trailertyp == LF { 76 | _widec += 256 77 | } 78 | } 79 | default: 80 | _widec = 768 + (int16(data[p]) - 0) 81 | if m.trailertyp == NUL { 82 | _widec += 256 83 | } 84 | } 85 | switch _widec { 86 | case 266: 87 | goto st2 88 | case 522: 89 | goto tr3 90 | case 768: 91 | goto st2 92 | case 1024: 93 | goto tr3 94 | } 95 | switch { 96 | case _widec > 9: 97 | if 11 <= _widec { 98 | goto st2 99 | } 100 | case _widec >= 1: 101 | goto st2 102 | } 103 | goto st0 104 | tr3: 105 | 106 | m.candidate = append(m.candidate, data...) 107 | 108 | goto st3 109 | st3: 110 | if p++; p == pe { 111 | goto _testEof3 112 | } 113 | stCase3: 114 | _widec = int16(data[p]) 115 | switch { 116 | case data[p] > 0: 117 | if 10 <= data[p] && data[p] <= 10 { 118 | _widec = 256 + (int16(data[p]) - 0) 119 | if m.trailertyp == LF { 120 | _widec += 256 121 | } 122 | } 123 | default: 124 | _widec = 768 + (int16(data[p]) - 0) 125 | if m.trailertyp == NUL { 126 | _widec += 256 127 | } 128 | } 129 | switch _widec { 130 | case 60: 131 | goto tr0 132 | case 266: 133 | goto st2 134 | case 522: 135 | goto tr3 136 | case 768: 137 | goto st2 138 | case 1024: 139 | goto tr3 140 | } 141 | switch { 142 | case _widec > 9: 143 | if 11 <= _widec { 144 | goto st2 145 | } 146 | case _widec >= 1: 147 | goto st2 148 | } 149 | goto st0 150 | stOut: 151 | _testEof2: 152 | cs = 2 153 | goto _testEof 154 | _testEof3: 155 | cs = 3 156 | goto _testEof 157 | 158 | _testEof: 159 | { 160 | } 161 | _out: 162 | { 163 | } 164 | } 165 | 166 | // Update parsing variables 167 | s.Set(cs, p, pe, eof) 168 | return p, pe 169 | } 170 | 171 | func (m *machine) OnErr(chunk []byte, err error) { 172 | // Store the last chunk of bytes ending without a trailer - ie., unexpected EOF from the reader 173 | m.lastChunk = chunk 174 | m.readError = err 175 | } 176 | 177 | func (m *machine) OnEOF(chunk []byte) { 178 | } 179 | 180 | func (m *machine) OnCompletion() { 181 | if len(m.candidate) > 0 { 182 | m.process() 183 | } 184 | // Try to parse last chunk as a candidate 185 | if m.readError != nil && len(m.lastChunk) > 0 { 186 | res, err := m.internal.Parse(m.lastChunk) 187 | if err == nil { 188 | err = m.readError 189 | } 190 | m.emit(&syslog.Result{ 191 | Message: res, 192 | Error: err, 193 | }) 194 | } 195 | } 196 | 197 | // NewParser returns a syslog.Parser suitable to parse syslog messages sent with non-transparent framing - ie. RFC 6587. 198 | func NewParser(options ...syslog.ParserOption) syslog.Parser { 199 | m := &machine{ 200 | emit: func(*syslog.Result) { /* noop */ }, 201 | } 202 | 203 | for _, opt := range options { 204 | m = opt(m).(*machine) 205 | } 206 | 207 | // No error can happens since during its setting we check the trailer type passed in 208 | trailer, _ := m.trailertyp.Value() 209 | m.trailer = byte(trailer) 210 | 211 | // Create internal parser depending on options 212 | if m.bestEffort { 213 | m.internal = rfc5424.NewMachine(rfc5424.WithBestEffort()) 214 | } else { 215 | m.internal = rfc5424.NewMachine() 216 | } 217 | 218 | return m 219 | } 220 | 221 | // WithMaxMessageLength does nothing for this parser 222 | func (m *machine) WithMaxMessageLength(length int) {} 223 | 224 | // HasBestEffort tells whether the receiving parser has best effort mode on or off. 225 | func (m *machine) HasBestEffort() bool { 226 | return m.bestEffort 227 | } 228 | 229 | // WithTrailer ... todo(leodido) 230 | func WithTrailer(t TrailerType) syslog.ParserOption { 231 | return func(m syslog.Parser) syslog.Parser { 232 | if val, err := t.Value(); err == nil { 233 | m.(*machine).trailer = byte(val) 234 | m.(*machine).trailertyp = t 235 | } 236 | return m 237 | } 238 | } 239 | 240 | // WithBestEffort implements the syslog.BestEfforter interface. 241 | // 242 | // The generic options uses it. 243 | func (m *machine) WithBestEffort() { 244 | m.bestEffort = true 245 | } 246 | 247 | // WithListener implements the syslog.Parser interface. 248 | // 249 | // The generic options uses it. 250 | func (m *machine) WithListener(f syslog.ParserListener) { 251 | m.emit = f 252 | } 253 | 254 | // Parse parses the io.Reader incoming bytes. 255 | // 256 | // It stops parsing when an error regarding RFC 6587 is found. 257 | func (m *machine) Parse(reader io.Reader) { 258 | r := parser.ArbitraryReader(reader, m.trailer) 259 | parser.New(r, m, parser.WithStart(1)).Parse() 260 | } 261 | 262 | func (m *machine) process() { 263 | lastByte := len(m.candidate) - 1 264 | if m.candidate[lastByte] == m.trailer { 265 | m.candidate = m.candidate[:lastByte] 266 | } 267 | res, err := m.internal.Parse(m.candidate) 268 | m.emit(&syslog.Result{ 269 | Message: res, 270 | Error: err, 271 | }) 272 | } 273 | -------------------------------------------------------------------------------- /nontransparent/parser.go.rl: -------------------------------------------------------------------------------- 1 | package nontransparent 2 | 3 | import ( 4 | "io" 5 | 6 | parser "github.com/leodido/ragel-machinery/parser" 7 | syslog "github.com/influxdata/go-syslog/v3" 8 | "github.com/influxdata/go-syslog/v3/rfc5424" 9 | ) 10 | 11 | %%{ 12 | machine nontransparent; 13 | 14 | # unsigned alphabet 15 | alphtype uint8; 16 | 17 | action on_trailer { 18 | m.candidate = append(m.candidate, data...) 19 | } 20 | 21 | action on_init { 22 | if len(m.candidate) > 0 { 23 | m.process() 24 | } 25 | m.candidate = make([]byte, 0) 26 | } 27 | 28 | t = 10 when { m.trailertyp == LF } | 29 | 00 when { m.trailertyp == NUL }; 30 | 31 | main := 32 | start: ( 33 | '<' >on_init (any)* -> trailer 34 | ), 35 | trailer: ( 36 | t >on_trailer -> final | 37 | t >on_trailer -> start 38 | ); 39 | 40 | }%% 41 | 42 | %% write data nofinal; 43 | 44 | type machine struct{ 45 | trailertyp TrailerType // default is 0 thus TrailerType(LF) 46 | trailer byte 47 | candidate []byte 48 | bestEffort bool 49 | internal syslog.Machine 50 | emit syslog.ParserListener 51 | readError error 52 | lastChunk []byte // store last candidate message also if it does not ends with a trailer 53 | } 54 | 55 | // Exec implements the ragel.Parser interface. 56 | func (m *machine) Exec(s *parser.State) (int, int) { 57 | // Retrieve previously stored parsing variables 58 | cs, p, pe, eof, data := s.Get() 59 | %% write exec; 60 | // Update parsing variables 61 | s.Set(cs, p, pe, eof) 62 | return p, pe 63 | } 64 | 65 | func (m *machine) OnErr(chunk []byte, err error) { 66 | // Store the last chunk of bytes ending without a trailer - ie., unexpected EOF from the reader 67 | m.lastChunk = chunk 68 | m.readError = err 69 | } 70 | 71 | func (m *machine) OnEOF(chunk []byte) { 72 | } 73 | 74 | func (m *machine) OnCompletion() { 75 | if len(m.candidate) > 0 { 76 | m.process() 77 | } 78 | // Try to parse last chunk as a candidate 79 | if m.readError != nil && len(m.lastChunk) > 0 { 80 | res, err := m.internal.Parse(m.lastChunk) 81 | if err == nil { 82 | err = m.readError 83 | } 84 | m.emit(&syslog.Result{ 85 | Message: res, 86 | Error: err, 87 | }) 88 | } 89 | } 90 | 91 | // NewParser returns a syslog.Parser suitable to parse syslog messages sent with non-transparent framing - ie. RFC 6587. 92 | func NewParser(options ...syslog.ParserOption) syslog.Parser { 93 | m := &machine{ 94 | emit: func(*syslog.Result) { /* noop */ }, 95 | } 96 | 97 | for _, opt := range options { 98 | m = opt(m).(*machine) 99 | } 100 | 101 | // No error can happens since during its setting we check the trailer type passed in 102 | trailer, _ := m.trailertyp.Value() 103 | m.trailer = byte(trailer) 104 | 105 | // Create internal parser depending on options 106 | if m.bestEffort { 107 | m.internal = rfc5424.NewMachine(rfc5424.WithBestEffort()) 108 | } else { 109 | m.internal = rfc5424.NewMachine() 110 | } 111 | 112 | return m 113 | } 114 | 115 | // HasBestEffort tells whether the receiving parser has best effort mode on or off. 116 | func (m *machine) HasBestEffort() bool { 117 | return m.bestEffort 118 | } 119 | 120 | // WithTrailer ... todo(leodido) 121 | func WithTrailer(t TrailerType) syslog.ParserOption { 122 | return func(m syslog.Parser) syslog.Parser { 123 | if val, err := t.Value(); err == nil { 124 | m.(*machine).trailer = byte(val) 125 | m.(*machine).trailertyp = t 126 | } 127 | return m 128 | } 129 | } 130 | 131 | // WithBestEffort implements the syslog.BestEfforter interface. 132 | // 133 | // The generic options uses it. 134 | func (m *machine) WithBestEffort() { 135 | m.bestEffort = true 136 | } 137 | 138 | // WithListener implements the syslog.Parser interface. 139 | // 140 | // The generic options uses it. 141 | func (m *machine) WithListener(f syslog.ParserListener) { 142 | m.emit = f 143 | } 144 | 145 | // Parse parses the io.Reader incoming bytes. 146 | // 147 | // It stops parsing when an error regarding RFC 6587 is found. 148 | func (m *machine) Parse(reader io.Reader) { 149 | r := parser.ArbitraryReader(reader, m.trailer) 150 | parser.New(r, m, parser.WithStart(%%{ write start; }%%)).Parse() 151 | } 152 | 153 | func (m *machine) process() { 154 | lastByte := len(m.candidate) - 1 155 | if m.candidate[lastByte] == m.trailer { 156 | m.candidate = m.candidate[:lastByte] 157 | } 158 | res, err := m.internal.Parse(m.candidate) 159 | m.emit(&syslog.Result{ 160 | Message: res, 161 | Error: err, 162 | }) 163 | } 164 | -------------------------------------------------------------------------------- /nontransparent/parser_test.go: -------------------------------------------------------------------------------- 1 | package nontransparent 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "strings" 7 | "testing" 8 | 9 | "github.com/influxdata/go-syslog/v3" 10 | "github.com/influxdata/go-syslog/v3/rfc5424" 11 | "github.com/leodido/ragel-machinery" 12 | "github.com/stretchr/testify/assert" 13 | ) 14 | 15 | type testCase struct { 16 | descr string 17 | input string 18 | substitute bool 19 | results []syslog.Result 20 | pResults []syslog.Result 21 | } 22 | 23 | var testCases []testCase 24 | 25 | func getParsingError(col int) error { 26 | return fmt.Errorf("parsing error [col %d]", col) 27 | } 28 | 29 | func getTestCases() []testCase { 30 | return []testCase{ 31 | // fixme(leodido) 32 | // { 33 | // "empty", 34 | // "", 35 | // []syslog.Result{}, 36 | // []syslog.Result{}, 37 | // }, 38 | 39 | { 40 | "1st ok", 41 | "<1>1 - - - - - -%[1]s", 42 | true, 43 | []syslog.Result{ 44 | { 45 | Message: (&rfc5424.SyslogMessage{}).SetPriority(1).SetVersion(1), 46 | }, 47 | }, 48 | []syslog.Result{ 49 | { 50 | Message: (&rfc5424.SyslogMessage{}).SetPriority(1).SetVersion(1), 51 | }, 52 | }, 53 | }, 54 | { 55 | "1st ok//notrailer", 56 | "<3>1 - - - - - -", 57 | false, 58 | []syslog.Result{ 59 | { 60 | Message: (&rfc5424.SyslogMessage{}).SetPriority(3).SetVersion(1), 61 | Error: ragel.NewReadingError(io.ErrUnexpectedEOF.Error()), 62 | }, 63 | }, 64 | []syslog.Result{ 65 | { 66 | Message: (&rfc5424.SyslogMessage{}).SetPriority(3).SetVersion(1), 67 | Error: ragel.NewReadingError(io.ErrUnexpectedEOF.Error()), 68 | }, 69 | }, 70 | }, 71 | { 72 | "1st ok/2nd ok", 73 | "<1>1 - - - - - -%[1]s<2>1 - - - - - -%[1]s", 74 | true, 75 | []syslog.Result{ 76 | { 77 | Message: (&rfc5424.SyslogMessage{}).SetPriority(1).SetVersion(1), 78 | }, 79 | { 80 | Message: (&rfc5424.SyslogMessage{}).SetPriority(2).SetVersion(1), 81 | }, 82 | }, 83 | []syslog.Result{ 84 | { 85 | Message: (&rfc5424.SyslogMessage{}).SetPriority(1).SetVersion(1), 86 | }, 87 | { 88 | Message: (&rfc5424.SyslogMessage{}).SetPriority(2).SetVersion(1), 89 | }, 90 | }, 91 | }, 92 | { 93 | "1st ok/2nd ok//notrailer", 94 | "<1>1 - - - - - -%[1]s<2>1 - - - - - -", 95 | true, 96 | []syslog.Result{ 97 | { 98 | Message: (&rfc5424.SyslogMessage{}).SetPriority(1).SetVersion(1), 99 | }, 100 | { 101 | Message: (&rfc5424.SyslogMessage{}).SetPriority(2).SetVersion(1), 102 | Error: ragel.NewReadingError(io.ErrUnexpectedEOF.Error()), 103 | }, 104 | }, 105 | []syslog.Result{ 106 | { 107 | Message: (&rfc5424.SyslogMessage{}).SetPriority(1).SetVersion(1), 108 | }, 109 | { 110 | Message: (&rfc5424.SyslogMessage{}).SetPriority(2).SetVersion(1), 111 | Error: ragel.NewReadingError(io.ErrUnexpectedEOF.Error()), 112 | }, 113 | }, 114 | }, 115 | { 116 | "1st ok//incomplete/2nd ok//incomplete", 117 | "<1>1%[1]s<2>1%[1]s", 118 | true, 119 | []syslog.Result{ 120 | { 121 | Error: getParsingError(4), 122 | }, 123 | { 124 | Error: getParsingError(4), 125 | }, 126 | }, 127 | []syslog.Result{ 128 | { 129 | Message: (&rfc5424.SyslogMessage{}).SetPriority(1).SetVersion(1), 130 | Error: getParsingError(4), 131 | }, 132 | { 133 | Message: (&rfc5424.SyslogMessage{}).SetPriority(2).SetVersion(1), 134 | Error: getParsingError(4), 135 | }, 136 | }, 137 | }, 138 | 139 | // todo(leodido) 140 | // { 141 | // "1st ok//incomplete/2nd ok//incomplete", 142 | // "", 143 | // }, 144 | // { 145 | // "1st ok//incomplete/2nd ok//incomplete", 146 | // "", 147 | // }, 148 | // { 149 | // "1st ok//incomplete/2nd ok//incomplete", 150 | // "", 151 | // }, 152 | } 153 | } 154 | 155 | func init() { 156 | testCases = getTestCases() 157 | } 158 | 159 | func TestParse(t *testing.T) { 160 | for _, tc := range testCases { 161 | tc := tc 162 | 163 | // Test with trailer LF 164 | var inputWithLF = tc.input 165 | if tc.substitute { 166 | lf, _ := LF.Value() 167 | inputWithLF = fmt.Sprintf(tc.input, string(rune(lf))) 168 | } 169 | t.Run(fmt.Sprintf("strict/LF/%s", tc.descr), func(t *testing.T) { 170 | t.Parallel() 171 | 172 | res := []syslog.Result{} 173 | strictParser := NewParser(syslog.WithListener(func(r *syslog.Result) { 174 | res = append(res, *r) 175 | })) 176 | strictParser.Parse(strings.NewReader(inputWithLF)) 177 | 178 | assert.Equal(t, tc.results, res) 179 | }) 180 | t.Run(fmt.Sprintf("effort/LF/%s", tc.descr), func(t *testing.T) { 181 | t.Parallel() 182 | 183 | res := []syslog.Result{} 184 | effortParser := NewParser(syslog.WithBestEffort(), syslog.WithListener(func(r *syslog.Result) { 185 | res = append(res, *r) 186 | })) 187 | effortParser.Parse(strings.NewReader(inputWithLF)) 188 | 189 | assert.Equal(t, tc.pResults, res) 190 | }) 191 | 192 | // Test with trailer NUL 193 | inputWithNUL := tc.input 194 | if tc.substitute { 195 | nul, _ := NUL.Value() 196 | inputWithNUL = fmt.Sprintf(tc.input, string(rune(nul))) 197 | } 198 | t.Run(fmt.Sprintf("strict/NL/%s", tc.descr), func(t *testing.T) { 199 | t.Parallel() 200 | 201 | res := []syslog.Result{} 202 | strictParser := NewParser(syslog.WithListener(func(r *syslog.Result) { 203 | res = append(res, *r) 204 | }), WithTrailer(NUL)) 205 | strictParser.Parse(strings.NewReader(inputWithNUL)) 206 | 207 | assert.Equal(t, tc.results, res) 208 | }) 209 | t.Run(fmt.Sprintf("effort/NL/%s", tc.descr), func(t *testing.T) { 210 | t.Parallel() 211 | 212 | res := []syslog.Result{} 213 | effortParser := NewParser(syslog.WithBestEffort(), syslog.WithListener(func(r *syslog.Result) { 214 | res = append(res, *r) 215 | }), WithTrailer(NUL)) 216 | effortParser.Parse(strings.NewReader(inputWithNUL)) 217 | 218 | assert.Equal(t, tc.pResults, res) 219 | }) 220 | } 221 | } 222 | 223 | func TestParserBestEffortOption(t *testing.T) { 224 | p1 := NewParser().(syslog.BestEfforter) 225 | assert.False(t, p1.HasBestEffort()) 226 | 227 | p2 := NewParser(syslog.WithBestEffort()).(syslog.BestEfforter) 228 | assert.True(t, p2.HasBestEffort()) 229 | } 230 | -------------------------------------------------------------------------------- /nontransparent/trailer_type.go: -------------------------------------------------------------------------------- 1 | package nontransparent 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | ) 7 | 8 | // TrailerType is the king of supported trailers for non-transparent frames. 9 | type TrailerType int 10 | 11 | const ( 12 | // LF is the line feed - ie., byte 10. Also the default one. 13 | LF TrailerType = iota 14 | // NUL is the nul byte - ie., byte 0. 15 | NUL 16 | ) 17 | 18 | var names = [...]string{"LF", "NUL"} 19 | var bytes = []int{10, 0} 20 | 21 | func (t TrailerType) String() string { 22 | if t < LF || t > NUL { 23 | return "" 24 | } 25 | 26 | return names[t] 27 | } 28 | 29 | // Value returns the byte corresponding to the receiving TrailerType. 30 | func (t TrailerType) Value() (int, error) { 31 | if t < LF || t > NUL { 32 | return -1, fmt.Errorf("unknown TrailerType") 33 | } 34 | 35 | return bytes[t], nil 36 | } 37 | 38 | // TrailerTypeFromString returns a TrailerType given a string. 39 | func TrailerTypeFromString(s string) (TrailerType, error) { 40 | switch strings.ToUpper(s) { 41 | case `"LF"`: 42 | fallthrough 43 | case `'LF'`: 44 | fallthrough 45 | case `LF`: 46 | return LF, nil 47 | 48 | case `"NUL"`: 49 | fallthrough 50 | case `'NUL'`: 51 | fallthrough 52 | case `NUL`: 53 | return NUL, nil 54 | } 55 | return -1, fmt.Errorf("unknown TrailerType") 56 | } 57 | 58 | // UnmarshalTOML decodes trailer type from TOML data. 59 | func (t *TrailerType) UnmarshalTOML(data []byte) (err error) { 60 | return t.UnmarshalText(data) 61 | } 62 | 63 | // UnmarshalText implements encoding.TextUnmarshaler 64 | func (t *TrailerType) UnmarshalText(data []byte) (err error) { 65 | *t, err = TrailerTypeFromString(string(data)) 66 | return err 67 | } 68 | 69 | // MarshalText implements encoding.TextMarshaler 70 | func (t TrailerType) MarshalText() ([]byte, error) { 71 | s := t.String() 72 | if s != "" { 73 | return []byte(s), nil 74 | } 75 | return nil, fmt.Errorf("unknown TrailerType") 76 | } 77 | -------------------------------------------------------------------------------- /nontransparent/trailer_type_test.go: -------------------------------------------------------------------------------- 1 | package nontransparent 2 | 3 | import ( 4 | "encoding/json" 5 | 6 | "github.com/stretchr/testify/assert" 7 | 8 | "testing" 9 | ) 10 | 11 | type trailerWrapper struct { 12 | Trailer TrailerType `json:"trailer"` 13 | } 14 | 15 | func TestUnmarshalTOML(t *testing.T) { 16 | var t1 TrailerType 17 | t1.UnmarshalTOML([]byte(`"LF"`)) 18 | assert.Equal(t, LF, t1) 19 | 20 | var t2 TrailerType 21 | t2.UnmarshalTOML([]byte(`LF`)) 22 | assert.Equal(t, LF, t2) 23 | 24 | var t3 TrailerType 25 | t3.UnmarshalTOML([]byte(`'LF'`)) 26 | assert.Equal(t, LF, t3) 27 | 28 | var t4 TrailerType 29 | t4.UnmarshalTOML([]byte(`"NUL"`)) 30 | assert.Equal(t, NUL, t4) 31 | 32 | var t5 TrailerType 33 | t5.UnmarshalTOML([]byte(`NUL`)) 34 | assert.Equal(t, NUL, t5) 35 | 36 | var t6 TrailerType 37 | t6.UnmarshalTOML([]byte(`'NUL'`)) 38 | assert.Equal(t, NUL, t6) 39 | 40 | var t7 TrailerType 41 | err := t7.UnmarshalTOML([]byte(`wrong`)) 42 | assert.Equal(t, TrailerType(-1), t7) 43 | assert.Error(t, err) 44 | } 45 | 46 | func TestUnmarshalLowercase(t *testing.T) { 47 | x := &trailerWrapper{} 48 | in := []byte(`{"trailer": "lf"}`) 49 | err := json.Unmarshal(in, x) 50 | assert.Nil(t, err) 51 | assert.Equal(t, &trailerWrapper{Trailer: LF}, x) 52 | } 53 | 54 | func TestUnmarshalUnknown(t *testing.T) { 55 | x := &trailerWrapper{} 56 | in := []byte(`{"trailer": "UNK"}`) 57 | err := json.Unmarshal(in, x) 58 | assert.Error(t, err) 59 | assert.Equal(t, &trailerWrapper{Trailer: -1}, x) 60 | } 61 | 62 | func TestUnmarshal(t *testing.T) { 63 | x := &trailerWrapper{} 64 | in := []byte(`{"trailer": "NUL"}`) 65 | err := json.Unmarshal(in, x) 66 | assert.Nil(t, err) 67 | assert.Equal(t, &trailerWrapper{Trailer: NUL}, x) 68 | } 69 | 70 | func TestMarshalUnknown(t *testing.T) { 71 | res, err := json.Marshal(&trailerWrapper{Trailer: TrailerType(-2)}) 72 | assert.Error(t, err) 73 | assert.Empty(t, res) 74 | } 75 | 76 | func TestMarshal(t *testing.T) { 77 | res, err := json.Marshal(&trailerWrapper{Trailer: NUL}) 78 | assert.Nil(t, err) 79 | assert.Equal(t, `{"trailer":"NUL"}`, string(res)) 80 | } 81 | -------------------------------------------------------------------------------- /octetcounting/example_test.go: -------------------------------------------------------------------------------- 1 | package octetcounting 2 | 3 | import ( 4 | "io" 5 | "strings" 6 | "time" 7 | 8 | "github.com/davecgh/go-spew/spew" 9 | syslog "github.com/influxdata/go-syslog/v3" 10 | ) 11 | 12 | func output(out interface{}) { 13 | spew.Config.DisableCapacities = true 14 | spew.Config.DisablePointerAddresses = true 15 | spew.Dump(out) 16 | } 17 | 18 | func Example() { 19 | results := []syslog.Result{} 20 | acc := func(res *syslog.Result) { 21 | results = append(results, *res) 22 | } 23 | r := strings.NewReader("48 <1>1 2003-10-11T22:14:15.003Z host.local - - - -25 <3>1 - host.local - - - -38 <2>1 - host.local su - - - κόσμε") 24 | NewParser(syslog.WithBestEffort(), syslog.WithListener(acc)).Parse(r) 25 | output(results) 26 | // Output: 27 | // ([]syslog.Result) (len=3) { 28 | // (syslog.Result) { 29 | // Message: (*rfc5424.SyslogMessage)({ 30 | // Base: (syslog.Base) { 31 | // Facility: (*uint8)(0), 32 | // Severity: (*uint8)(1), 33 | // Priority: (*uint8)(1), 34 | // Timestamp: (*time.Time)(2003-10-11 22:14:15.003 +0000 UTC), 35 | // Hostname: (*string)((len=10) "host.local"), 36 | // Appname: (*string)(), 37 | // ProcID: (*string)(), 38 | // MsgID: (*string)(), 39 | // Message: (*string)() 40 | // }, 41 | // Version: (uint16) 1, 42 | // StructuredData: (*map[string]map[string]string)() 43 | // }), 44 | // Error: (error) 45 | // }, 46 | // (syslog.Result) { 47 | // Message: (*rfc5424.SyslogMessage)({ 48 | // Base: (syslog.Base) { 49 | // Facility: (*uint8)(0), 50 | // Severity: (*uint8)(3), 51 | // Priority: (*uint8)(3), 52 | // Timestamp: (*time.Time)(), 53 | // Hostname: (*string)((len=10) "host.local"), 54 | // Appname: (*string)(), 55 | // ProcID: (*string)(), 56 | // MsgID: (*string)(), 57 | // Message: (*string)() 58 | // }, 59 | // Version: (uint16) 1, 60 | // StructuredData: (*map[string]map[string]string)() 61 | // }), 62 | // Error: (error) 63 | // }, 64 | // (syslog.Result) { 65 | // Message: (*rfc5424.SyslogMessage)({ 66 | // Base: (syslog.Base) { 67 | // Facility: (*uint8)(0), 68 | // Severity: (*uint8)(2), 69 | // Priority: (*uint8)(2), 70 | // Timestamp: (*time.Time)(), 71 | // Hostname: (*string)((len=10) "host.local"), 72 | // Appname: (*string)((len=2) "su"), 73 | // ProcID: (*string)(), 74 | // MsgID: (*string)(), 75 | // Message: (*string)((len=11) "κόσμε") 76 | // }, 77 | // Version: (uint16) 1, 78 | // StructuredData: (*map[string]map[string]string)() 79 | // }), 80 | // Error: (error) 81 | // } 82 | // } 83 | } 84 | 85 | func Example_channel() { 86 | messages := []string{ 87 | "16 <1>1 - - - - - -", 88 | "17 <2>12 A B C D E -", 89 | "16 <1>1", 90 | } 91 | 92 | r, w := io.Pipe() 93 | 94 | go func() { 95 | defer w.Close() 96 | 97 | for _, m := range messages { 98 | w.Write([]byte(m)) 99 | time.Sleep(time.Millisecond * 220) 100 | } 101 | }() 102 | 103 | c := make(chan syslog.Result) 104 | emit := func(res *syslog.Result) { 105 | c <- *res 106 | } 107 | 108 | parser := NewParser(syslog.WithBestEffort(), syslog.WithListener(emit)) 109 | go func() { 110 | defer close(c) 111 | parser.Parse(r) 112 | }() 113 | 114 | for r := range c { 115 | output(r) 116 | } 117 | 118 | r.Close() 119 | 120 | // Output: 121 | // (syslog.Result) { 122 | // Message: (*rfc5424.SyslogMessage)({ 123 | // Base: (syslog.Base) { 124 | // Facility: (*uint8)(0), 125 | // Severity: (*uint8)(1), 126 | // Priority: (*uint8)(1), 127 | // Timestamp: (*time.Time)(), 128 | // Hostname: (*string)(), 129 | // Appname: (*string)(), 130 | // ProcID: (*string)(), 131 | // MsgID: (*string)(), 132 | // Message: (*string)() 133 | // }, 134 | // Version: (uint16) 1, 135 | // StructuredData: (*map[string]map[string]string)() 136 | // }), 137 | // Error: (error) 138 | // } 139 | // (syslog.Result) { 140 | // Message: (*rfc5424.SyslogMessage)({ 141 | // Base: (syslog.Base) { 142 | // Facility: (*uint8)(0), 143 | // Severity: (*uint8)(2), 144 | // Priority: (*uint8)(2), 145 | // Timestamp: (*time.Time)(), 146 | // Hostname: (*string)(), 147 | // Appname: (*string)(), 148 | // ProcID: (*string)(), 149 | // MsgID: (*string)(), 150 | // Message: (*string)() 151 | // }, 152 | // Version: (uint16) 12, 153 | // StructuredData: (*map[string]map[string]string)() 154 | // }), 155 | // Error: (*errors.errorString)(expecting a RFC3339MICRO timestamp or a nil value [col 6]) 156 | // } 157 | // (syslog.Result) { 158 | // Message: (*rfc5424.SyslogMessage)({ 159 | // Base: (syslog.Base) { 160 | // Facility: (*uint8)(0), 161 | // Severity: (*uint8)(1), 162 | // Priority: (*uint8)(1), 163 | // Timestamp: (*time.Time)(), 164 | // Hostname: (*string)(), 165 | // Appname: (*string)(), 166 | // ProcID: (*string)(), 167 | // MsgID: (*string)(), 168 | // Message: (*string)() 169 | // }, 170 | // Version: (uint16) 1, 171 | // StructuredData: (*map[string]map[string]string)() 172 | // }), 173 | // Error: (*errors.errorString)(parsing error [col 4]) 174 | // } 175 | } 176 | -------------------------------------------------------------------------------- /octetcounting/parser.go: -------------------------------------------------------------------------------- 1 | package octetcounting 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | 7 | syslog "github.com/influxdata/go-syslog/v3" 8 | "github.com/influxdata/go-syslog/v3/rfc5424" 9 | ) 10 | 11 | // parser is capable to parse the input stream containing syslog messages with octetcounting framing. 12 | // 13 | // Use NewParser function to instantiate one. 14 | type parser struct { 15 | msglen int64 16 | maxMessageLength int 17 | s Scanner 18 | internal syslog.Machine 19 | last Token 20 | stepback bool // Wheter to retrieve the last token or not 21 | bestEffort bool // Best effort mode flag 22 | emit syslog.ParserListener 23 | } 24 | 25 | // NewParser returns a syslog.Parser suitable to parse syslog messages sent with transparent - ie. octet counting (RFC 5425) - framing. 26 | func NewParser(opts ...syslog.ParserOption) syslog.Parser { 27 | p := &parser{ 28 | emit: func(*syslog.Result) { /* noop */ }, 29 | maxMessageLength: 8192, // size as per RFC5425#section-4.3.1 30 | } 31 | 32 | for _, opt := range opts { 33 | p = opt(p).(*parser) 34 | } 35 | 36 | // Create internal parser depending on options 37 | if p.bestEffort { 38 | p.internal = rfc5424.NewMachine(rfc5424.WithBestEffort()) 39 | } else { 40 | p.internal = rfc5424.NewMachine() 41 | } 42 | 43 | return p 44 | } 45 | 46 | func (p *parser) WithMaxMessageLength(length int) { 47 | p.maxMessageLength = length 48 | } 49 | 50 | // HasBestEffort tells whether the receiving parser has best effort mode on or off. 51 | func (p *parser) HasBestEffort() bool { 52 | return p.bestEffort 53 | } 54 | 55 | // WithBestEffort implements the syslog.BestEfforter interface. 56 | // 57 | // The generic options uses it. 58 | func (p *parser) WithBestEffort() { 59 | p.bestEffort = true 60 | } 61 | 62 | // WithListener implements the syslog.Parser interface. 63 | // 64 | // The generic options uses it. 65 | func (p *parser) WithListener(f syslog.ParserListener) { 66 | p.emit = f 67 | } 68 | 69 | // Parse parses the io.Reader incoming bytes. 70 | // 71 | // It stops parsing when an error regarding RFC 5425 is found. 72 | func (p *parser) Parse(r io.Reader) { 73 | p.s = *NewScanner(r, p.maxMessageLength) 74 | p.run() 75 | } 76 | 77 | func (p *parser) run() { 78 | for { 79 | var tok Token 80 | 81 | // First token MUST be a MSGLEN 82 | if tok = p.scan(); tok.typ != MSGLEN { 83 | p.emit(&syslog.Result{ 84 | Error: fmt.Errorf("found %s, expecting a %s", tok, MSGLEN), 85 | }) 86 | break 87 | } 88 | 89 | if int(p.s.msglen) > p.maxMessageLength { 90 | p.emit(&syslog.Result{ 91 | Error: fmt.Errorf("message too long to parse. was size %d, max length %d", p.s.msglen, p.maxMessageLength), 92 | }) 93 | break 94 | } 95 | 96 | // Next we MUST see a WS 97 | if tok = p.scan(); tok.typ != WS { 98 | p.emit(&syslog.Result{ 99 | Error: fmt.Errorf("found %s, expecting a %s", tok, WS), 100 | }) 101 | break 102 | } 103 | 104 | // Next we MUST see a SYSLOGMSG with length equal to MSGLEN 105 | if tok = p.scan(); tok.typ != SYSLOGMSG { 106 | e := fmt.Errorf(`found %s after "%s", expecting a %s containing %d octets`, tok, tok.lit, SYSLOGMSG, p.s.msglen) 107 | // Underflow case 108 | if len(tok.lit) < int(p.s.msglen) && p.bestEffort { 109 | // Though MSGLEN was not respected, we try to parse the existing SYSLOGMSG as a RFC5424 syslog message 110 | result := p.parse(tok.lit) 111 | if result.Error == nil { 112 | result.Error = e 113 | } 114 | p.emit(result) 115 | break 116 | } 117 | 118 | p.emit(&syslog.Result{ 119 | Error: e, 120 | }) 121 | break 122 | } 123 | 124 | // Parse the SYSLOGMSG literal pretending it is a RFC5424 syslog message 125 | result := p.parse(tok.lit) 126 | if p.bestEffort || result.Error == nil { 127 | p.emit(result) 128 | } 129 | if !p.bestEffort && result.Error != nil { 130 | p.emit(&syslog.Result{Error: result.Error}) 131 | break 132 | } 133 | 134 | // Next we MUST see an EOF otherwise the parsing we'll start again 135 | if tok = p.scan(); tok.typ == EOF { 136 | break 137 | } else { 138 | p.unscan() 139 | } 140 | } 141 | } 142 | 143 | func (p *parser) parse(input []byte) *syslog.Result { 144 | sys, err := p.internal.Parse(input) 145 | 146 | return &syslog.Result{ 147 | Message: sys, 148 | Error: err, 149 | } 150 | } 151 | 152 | // scan returns the next token from the underlying scanner; 153 | // if a token has been unscanned then read that instead. 154 | func (p *parser) scan() Token { 155 | // If we have a token on the buffer, then return it. 156 | if p.stepback { 157 | p.stepback = false 158 | return p.last 159 | } 160 | 161 | // Otherwise read the next token from the scanner. 162 | tok := p.s.Scan() 163 | 164 | // Save it to the buffer in case we unscan later. 165 | p.last = tok 166 | 167 | return tok 168 | } 169 | 170 | // unscan pushes the previously read token back onto the buffer. 171 | func (p *parser) unscan() { 172 | p.stepback = true 173 | } 174 | -------------------------------------------------------------------------------- /octetcounting/performance_test.go: -------------------------------------------------------------------------------- 1 | package octetcounting 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "testing" 7 | 8 | "github.com/influxdata/go-syslog/v3" 9 | syslogtesting "github.com/influxdata/go-syslog/v3/testing" 10 | ) 11 | 12 | // This is here to avoid compiler optimizations that 13 | // could remove the actual call we are benchmarking 14 | // during benchmarks 15 | var benchParseResult syslog.Message 16 | 17 | type benchCase struct { 18 | input []byte 19 | label string 20 | maxLength int 21 | } 22 | 23 | var benchCases = []benchCase{ 24 | { 25 | label: "Small Message Size", 26 | input: []byte("48 <1>1 2003-10-11T22:14:15.003Z host.local - - - -25 <3>1 - host.local - - - -38 <2>1 - host.local su - - - κόσμε"), 27 | }, 28 | { 29 | label: "Default Max Message Size", 30 | input: []byte(fmt.Sprintf( 31 | "8192 <%d>%d %s %s %s %s %s - %s", 32 | syslogtesting.MaxPriority, 33 | syslogtesting.MaxVersion, 34 | syslogtesting.MaxRFC3339MicroTimestamp, 35 | string(syslogtesting.MaxHostname), 36 | string(syslogtesting.MaxAppname), 37 | string(syslogtesting.MaxProcID), 38 | string(syslogtesting.MaxMsgID), 39 | string(syslogtesting.MaxMessage), 40 | )), 41 | }, 42 | { 43 | label: "UDP Max Message Size", 44 | input: []byte(fmt.Sprintf( 45 | "65529 <%d>%d %s %s %s %s %s - %s", 46 | syslogtesting.MaxPriority, 47 | syslogtesting.MaxVersion, 48 | syslogtesting.MaxRFC3339MicroTimestamp, 49 | string(syslogtesting.MaxHostname), 50 | string(syslogtesting.MaxAppname), 51 | string(syslogtesting.MaxProcID), 52 | string(syslogtesting.MaxMsgID), 53 | string(syslogtesting.LongerMaxMessage), 54 | )), 55 | maxLength: 65529, 56 | }, 57 | } 58 | 59 | func BenchmarkParse(b *testing.B) { 60 | for _, tc := range benchCases { 61 | tc := tc 62 | if tc.maxLength == 0 { 63 | tc.maxLength = 8192 64 | } 65 | m := NewParser(syslog.WithBestEffort()) 66 | b.Run(syslogtesting.RightPad(tc.label, 50), func(b *testing.B) { 67 | for i := 0; i < b.N; i++ { 68 | reader := bytes.NewReader(tc.input) 69 | m.Parse(reader) 70 | } 71 | }) 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /octetcounting/scanner.go: -------------------------------------------------------------------------------- 1 | package octetcounting 2 | 3 | import ( 4 | "bufio" 5 | "bytes" 6 | "io" 7 | "strconv" 8 | ) 9 | 10 | // eof represents a marker byte for the end of the reader 11 | var eof = byte(0) 12 | 13 | // ws represents the whitespace 14 | var ws = byte(32) 15 | 16 | // lt represents the "<" character 17 | var lt = byte(60) 18 | 19 | // isDigit returns true if the byte represents a number in [0,9] 20 | func isDigit(ch byte) bool { 21 | return (ch >= 47 && ch <= 57) 22 | } 23 | 24 | // isNonZeroDigit returns true if the byte represents a number in ]0,9] 25 | func isNonZeroDigit(ch byte) bool { 26 | return (ch >= 48 && ch <= 57) 27 | } 28 | 29 | // Scanner represents the lexical scanner for octet counting transport. 30 | type Scanner struct { 31 | r *bufio.Reader 32 | msglen uint64 33 | ready bool 34 | } 35 | 36 | // NewScanner returns a pointer to a new instance of Scanner. 37 | func NewScanner(r io.Reader, maxLength int) *Scanner { 38 | return &Scanner{ 39 | r: bufio.NewReaderSize(r, maxLength+20), // max uint64 is 19 characters + a space 40 | } 41 | } 42 | 43 | // read reads the next byte from the buffered reader 44 | // it returns the byte(0) if an error occurs (or io.EOF is returned) 45 | func (s *Scanner) read() byte { 46 | b, err := s.r.ReadByte() 47 | if err != nil { 48 | return eof 49 | } 50 | return b 51 | } 52 | 53 | // unread places the previously read byte back on the reader 54 | func (s *Scanner) unread() { 55 | _ = s.r.UnreadByte() 56 | } 57 | 58 | // Scan returns the next token. 59 | func (s *Scanner) Scan() (tok Token) { 60 | // Read the next byte. 61 | b := s.read() 62 | 63 | if isNonZeroDigit(b) { 64 | s.unread() 65 | s.ready = false 66 | return s.scanMsgLen() 67 | } 68 | 69 | // Otherwise read the individual character 70 | switch b { 71 | case eof: 72 | s.ready = false 73 | return Token{ 74 | typ: EOF, 75 | } 76 | case ws: 77 | s.ready = true 78 | return Token{ 79 | typ: WS, 80 | lit: []byte{ws}, 81 | } 82 | case lt: 83 | if s.msglen > 0 && s.ready { 84 | s.unread() 85 | return s.scanSyslogMsg() 86 | } 87 | } 88 | 89 | return Token{ 90 | typ: ILLEGAL, 91 | lit: []byte{b}, 92 | } 93 | } 94 | 95 | func (s *Scanner) scanMsgLen() Token { 96 | // Create a buffer and read the current character into it 97 | var buf bytes.Buffer 98 | buf.WriteByte(s.read()) 99 | 100 | // Read every subsequent digit character into the buffer 101 | // Non-digit characters and EOF will cause the loop to exit 102 | for { 103 | if b := s.read(); b == eof { 104 | break 105 | } else if !isDigit(b) { 106 | s.unread() 107 | break 108 | } else { 109 | buf.WriteByte(b) 110 | } 111 | } 112 | 113 | msglen := buf.String() 114 | s.msglen, _ = strconv.ParseUint(msglen, 10, 64) 115 | 116 | return Token{ 117 | typ: MSGLEN, 118 | lit: buf.Bytes(), 119 | } 120 | } 121 | 122 | func (s *Scanner) scanSyslogMsg() Token { 123 | // Check the reader contains almost MSGLEN characters 124 | n := int(s.msglen) 125 | b, err := s.r.Peek(n) 126 | if err != nil { 127 | return Token{ 128 | typ: EOF, 129 | lit: b, 130 | } 131 | } 132 | // Advance the reader of MSGLEN characters 133 | s.r.Discard(n) 134 | 135 | // Reset status 136 | s.ready = false 137 | s.msglen = 0 138 | 139 | // Return SYSLOGMSG token 140 | return Token{ 141 | typ: SYSLOGMSG, 142 | lit: b, 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /octetcounting/tokens.go: -------------------------------------------------------------------------------- 1 | package octetcounting 2 | 3 | import ( 4 | "strconv" 5 | ) 6 | 7 | // Token represents a lexical token of the octetcounting. 8 | type Token struct { 9 | typ TokenType 10 | lit []byte 11 | } 12 | 13 | // TokenType represents a lexical token type of the octetcounting. 14 | type TokenType int 15 | 16 | // Tokens 17 | const ( 18 | ILLEGAL TokenType = iota 19 | EOF 20 | WS 21 | MSGLEN 22 | SYSLOGMSG 23 | ) 24 | 25 | // String outputs the string representation of the receiving Token. 26 | func (t Token) String() string { 27 | switch t.typ { 28 | case WS, EOF: 29 | return t.typ.String() 30 | default: 31 | return t.typ.String() + "(" + string(t.lit) + ")" 32 | } 33 | } 34 | 35 | const tokentypename = "ILLEGALEOFWSMSGLENSYSLOGMSG" 36 | 37 | var tokentypeindex = [...]uint8{0, 7, 10, 12, 18, 27} 38 | 39 | // String outputs the string representation of the receiving TokenType. 40 | func (i TokenType) String() string { 41 | if i < 0 || i >= TokenType(len(tokentypeindex)-1) { 42 | return "TokenType(" + strconv.FormatInt(int64(i), 10) + ")" 43 | } 44 | return tokentypename[tokentypeindex[i]:tokentypeindex[i+1]] 45 | } 46 | -------------------------------------------------------------------------------- /octetcounting/tokens_test.go: -------------------------------------------------------------------------------- 1 | package octetcounting 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func TestTokenTypeString(t *testing.T) { 11 | const NOTEXISTING = 1000 12 | assert.Equal(t, fmt.Sprintf("TokenType(%d)", NOTEXISTING), TokenType(NOTEXISTING).String()) 13 | assert.Equal(t, "ILLEGAL", ILLEGAL.String()) 14 | assert.Equal(t, "WS", TokenType(WS).String()) 15 | } 16 | 17 | func TestTokenString(t *testing.T) { 18 | tok := Token{typ: SYSLOGMSG, lit: []byte("<1>1 - - - - - -")} 19 | assert.Equal(t, "SYSLOGMSG(<1>1 - - - - - -)", tok.String()) 20 | } 21 | -------------------------------------------------------------------------------- /options.go: -------------------------------------------------------------------------------- 1 | package syslog 2 | 3 | // WithListener returns a generic option that sets the emit function for syslog parsers. 4 | func WithListener(f ParserListener) ParserOption { 5 | return func(p Parser) Parser { 6 | p.WithListener(f) 7 | return p 8 | } 9 | } 10 | 11 | // WithBestEffort returns a generic options that enables best effort mode for syslog parsers. 12 | // 13 | // When passed to a parser it tries to recover as much of the syslog messages as possible. 14 | func WithBestEffort() ParserOption { 15 | return func(p Parser) Parser { 16 | p.WithBestEffort() 17 | return p 18 | } 19 | } 20 | 21 | // WithMaxMessageLength sets the length of the buffer for octect parsing. 22 | func WithMaxMessageLength(length int) ParserOption { 23 | return func(p Parser) Parser { 24 | p.WithMaxMessageLength(length) 25 | return p 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /rfc3164/example_test.go: -------------------------------------------------------------------------------- 1 | package rfc3164 2 | 3 | import ( 4 | // "time" 5 | 6 | "time" 7 | 8 | "github.com/davecgh/go-spew/spew" 9 | ) 10 | 11 | func output(out interface{}) { 12 | spew.Config.DisableCapacities = true 13 | spew.Config.DisablePointerAddresses = true 14 | spew.Dump(out) 15 | } 16 | 17 | func Example() { 18 | i := []byte(`<13>Dec 2 16:31:03 host app: Test`) 19 | p := NewParser() 20 | m, _ := p.Parse(i) 21 | output(m) 22 | // Output: 23 | // (*rfc3164.SyslogMessage)({ 24 | // Base: (syslog.Base) { 25 | // Facility: (*uint8)(1), 26 | // Severity: (*uint8)(5), 27 | // Priority: (*uint8)(13), 28 | // Timestamp: (*time.Time)(0000-12-02 16:31:03 +0000 UTC), 29 | // Hostname: (*string)((len=4) "host"), 30 | // Appname: (*string)((len=3) "app"), 31 | // ProcID: (*string)(), 32 | // MsgID: (*string)(), 33 | // Message: (*string)((len=4) "Test") 34 | // } 35 | // }) 36 | } 37 | 38 | func Example_currentyear() { 39 | i := []byte(`<13>Dec 2 16:31:03 host app: Test`) 40 | p := NewParser(WithYear(CurrentYear{})) 41 | m, _ := p.Parse(i) 42 | output(m) 43 | // Output: 44 | // (*rfc3164.SyslogMessage)({ 45 | // Base: (syslog.Base) { 46 | // Facility: (*uint8)(1), 47 | // Severity: (*uint8)(5), 48 | // Priority: (*uint8)(13), 49 | // Timestamp: (*time.Time)(2021-12-02 16:31:03 +0000 UTC), 50 | // Hostname: (*string)((len=4) "host"), 51 | // Appname: (*string)((len=3) "app"), 52 | // ProcID: (*string)(), 53 | // MsgID: (*string)(), 54 | // Message: (*string)((len=4) "Test") 55 | // } 56 | // }) 57 | } 58 | 59 | func Example_withtimezone() { 60 | cet, _ := time.LoadLocation("CET") 61 | i := []byte(`<13>Jan 30 02:08:03 host app: Test`) 62 | p := NewParser(WithTimezone(cet)) 63 | m, _ := p.Parse(i) 64 | output(m) 65 | // Output: 66 | // (*rfc3164.SyslogMessage)({ 67 | // Base: (syslog.Base) { 68 | // Facility: (*uint8)(1), 69 | // Severity: (*uint8)(5), 70 | // Priority: (*uint8)(13), 71 | // Timestamp: (*time.Time)(0000-01-30 03:08:03 +0100 CET), 72 | // Hostname: (*string)((len=4) "host"), 73 | // Appname: (*string)((len=3) "app"), 74 | // ProcID: (*string)(), 75 | // MsgID: (*string)(), 76 | // Message: (*string)((len=4) "Test") 77 | // } 78 | // }) 79 | } 80 | 81 | func Example_withlocaletimezone() { 82 | pst, _ := time.LoadLocation("America/New_York") 83 | i := []byte(`<13>Nov 22 17:09:42 xxx kernel: [118479565.921459] EXT4-fs warning (device sda8): ext4_dx_add_entry:2006: Directory index full!`) 84 | p := NewParser(WithLocaleTimezone(pst)) 85 | m, _ := p.Parse(i) 86 | output(m) 87 | // Output: 88 | // (*rfc3164.SyslogMessage)({ 89 | // Base: (syslog.Base) { 90 | // Facility: (*uint8)(1), 91 | // Severity: (*uint8)(5), 92 | // Priority: (*uint8)(13), 93 | // Timestamp: (*time.Time)(0000-11-22 17:09:42 -0456 LMT), 94 | // Hostname: (*string)((len=3) "xxx"), 95 | // Appname: (*string)((len=6) "kernel"), 96 | // ProcID: (*string)(), 97 | // MsgID: (*string)(), 98 | // Message: (*string)((len=95) "[118479565.921459] EXT4-fs warning (device sda8): ext4_dx_add_entry:2006: Directory index full!") 99 | // } 100 | // }) 101 | } 102 | 103 | func Example_withtimezone_and_year() { 104 | est, _ := time.LoadLocation("EST") 105 | i := []byte(`<13>Jan 30 02:08:03 host app: Test`) 106 | p := NewParser(WithTimezone(est), WithYear(Year{YYYY: 1987})) 107 | m, _ := p.Parse(i) 108 | output(m) 109 | // Output: 110 | // (*rfc3164.SyslogMessage)({ 111 | // Base: (syslog.Base) { 112 | // Facility: (*uint8)(1), 113 | // Severity: (*uint8)(5), 114 | // Priority: (*uint8)(13), 115 | // Timestamp: (*time.Time)(1987-01-29 21:08:03 -0500 EST), 116 | // Hostname: (*string)((len=4) "host"), 117 | // Appname: (*string)((len=3) "app"), 118 | // ProcID: (*string)(), 119 | // MsgID: (*string)(), 120 | // Message: (*string)((len=4) "Test") 121 | // } 122 | // }) 123 | } 124 | 125 | func Example_besteffort() { 126 | i := []byte(`<13>Dec 2 16:31:03 -`) 127 | p := NewParser(WithBestEffort()) 128 | m, _ := p.Parse(i) 129 | output(m) 130 | // Output: 131 | // (*rfc3164.SyslogMessage)({ 132 | // Base: (syslog.Base) { 133 | // Facility: (*uint8)(1), 134 | // Severity: (*uint8)(5), 135 | // Priority: (*uint8)(13), 136 | // Timestamp: (*time.Time)(0000-12-02 16:31:03 +0000 UTC), 137 | // Hostname: (*string)(), 138 | // Appname: (*string)(), 139 | // ProcID: (*string)(), 140 | // MsgID: (*string)(), 141 | // Message: (*string)() 142 | // } 143 | // }) 144 | } 145 | 146 | func Example_rfc3339timestamp() { 147 | i := []byte(`<28>2019-12-02T16:49:23+02:00 host app[23410]: Test`) 148 | p := NewParser(WithRFC3339()) 149 | m, _ := p.Parse(i) 150 | output(m) 151 | // Output: 152 | // (*rfc3164.SyslogMessage)({ 153 | // Base: (syslog.Base) { 154 | // Facility: (*uint8)(3), 155 | // Severity: (*uint8)(4), 156 | // Priority: (*uint8)(28), 157 | // Timestamp: (*time.Time)(2019-12-02 16:49:23 +0200 +0200), 158 | // Hostname: (*string)((len=4) "host"), 159 | // Appname: (*string)((len=3) "app"), 160 | // ProcID: (*string)((len=5) "23410"), 161 | // MsgID: (*string)(), 162 | // Message: (*string)((len=4) "Test") 163 | // } 164 | // }) 165 | } 166 | 167 | func Example_stamp_also_when_rfc3339() { 168 | i := []byte(`<28>Dec 2 16:49:23 host app[23410]: Test`) 169 | p := NewParser(WithYear(Year{YYYY: 2019}), WithRFC3339()) 170 | m, _ := p.Parse(i) 171 | output(m) 172 | // Output: 173 | // (*rfc3164.SyslogMessage)({ 174 | // Base: (syslog.Base) { 175 | // Facility: (*uint8)(3), 176 | // Severity: (*uint8)(4), 177 | // Priority: (*uint8)(28), 178 | // Timestamp: (*time.Time)(2019-12-02 16:49:23 +0000 UTC), 179 | // Hostname: (*string)((len=4) "host"), 180 | // Appname: (*string)((len=3) "app"), 181 | // ProcID: (*string)((len=5) "23410"), 182 | // MsgID: (*string)(), 183 | // Message: (*string)((len=4) "Test") 184 | // } 185 | // }) 186 | } 187 | -------------------------------------------------------------------------------- /rfc3164/machine.go.rl: -------------------------------------------------------------------------------- 1 | package rfc3164 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | 7 | "github.com/influxdata/go-syslog/v3" 8 | "github.com/influxdata/go-syslog/v3/common" 9 | ) 10 | 11 | var ( 12 | errPrival = "expecting a priority value in the range 1-191 or equal to 0 [col %d]" 13 | errPri = "expecting a priority value within angle brackets [col %d]" 14 | errTimestamp = "expecting a Stamp timestamp [col %d]" 15 | errRFC3339 = "expecting a Stamp or a RFC3339 timestamp [col %d]" 16 | errHostname = "expecting an hostname (from 1 to max 255 US-ASCII characters) [col %d]" 17 | errTag = "expecting an alphanumeric tag (max 32 characters) [col %d]" 18 | errContentStart = "expecting a content part starting with a non-alphanumeric character [col %d]" 19 | errContent = "expecting a content part composed by visible characters only [col %d]" 20 | errParse = "parsing error [col %d]" 21 | ) 22 | 23 | %%{ 24 | machine rfc3164; 25 | 26 | include common "common.rl"; 27 | 28 | # unsigned alphabet 29 | alphtype uint8; 30 | 31 | action mark { 32 | m.pb = m.p 33 | } 34 | 35 | action set_prival { 36 | output.priority = uint8(common.UnsafeUTF8DecimalCodePointsToInt(m.text())) 37 | output.prioritySet = true 38 | } 39 | 40 | action set_timestamp { 41 | if t, e := time.Parse(time.Stamp, string(m.text())); e != nil { 42 | m.err = fmt.Errorf("%s [col %d]", e, m.p) 43 | fhold; 44 | fgoto fail; 45 | } else { 46 | if m.timezone != nil { 47 | t, _ = time.ParseInLocation(time.Stamp, string(m.text()), m.timezone) 48 | } 49 | output.timestamp = t.AddDate(m.yyyy, 0, 0) 50 | if m.loc != nil { 51 | output.timestamp = output.timestamp.In(m.loc) 52 | } 53 | output.timestampSet = true 54 | } 55 | } 56 | 57 | action set_rfc3339 { 58 | if t, e := time.Parse(time.RFC3339, string(m.text())); e != nil { 59 | m.err = fmt.Errorf("%s [col %d]", e, m.p) 60 | fhold; 61 | fgoto fail; 62 | } else { 63 | output.timestamp = t 64 | output.timestampSet = true 65 | } 66 | } 67 | 68 | action set_hostname { 69 | output.hostname = string(m.text()) 70 | } 71 | 72 | action set_tag { 73 | output.tag = string(m.text()) 74 | } 75 | 76 | action set_content { 77 | output.content = string(m.text()) 78 | } 79 | 80 | action set_message { 81 | output.message = string(m.text()) 82 | } 83 | 84 | action err_prival { 85 | m.err = fmt.Errorf(errPrival, m.p) 86 | fhold; 87 | fgoto fail; 88 | } 89 | 90 | action err_pri { 91 | m.err = fmt.Errorf(errPri, m.p) 92 | fhold; 93 | fgoto fail; 94 | } 95 | 96 | action err_timestamp { 97 | m.err = fmt.Errorf(errTimestamp, m.p) 98 | fhold; 99 | fgoto fail; 100 | } 101 | 102 | action err_rfc3339 { 103 | m.err = fmt.Errorf(errRFC3339, m.p) 104 | fhold; 105 | fgoto fail; 106 | } 107 | 108 | action err_hostname { 109 | m.err = fmt.Errorf(errHostname, m.p) 110 | fhold; 111 | fgoto fail; 112 | } 113 | 114 | action err_tag { 115 | m.err = fmt.Errorf(errTag, m.p) 116 | fhold; 117 | fgoto fail; 118 | } 119 | 120 | action err_contentstart { 121 | m.err = fmt.Errorf(errContentStart, m.p) 122 | fhold; 123 | fgoto fail; 124 | } 125 | 126 | action err_content { 127 | m.err = fmt.Errorf(errContent, m.p) 128 | fhold; 129 | fgoto fail; 130 | } 131 | 132 | pri = ('<' prival >mark %from(set_prival) $err(err_prival) '>') @err(err_pri); 133 | 134 | timestamp = (datemmm sp datemday sp hhmmss) >mark %set_timestamp @err(err_timestamp); 135 | 136 | rfc3339 = fulldate >mark 'T' hhmmss timeoffset %set_rfc3339 @err(err_rfc3339); 137 | 138 | # note > RFC 3164 says "The Domain Name MUST NOT be included in the HOSTNAME field" 139 | # note > this could mean that the we may need to create and to use a labelrange = graph{1,63} here if we want the parser to be stricter. 140 | hostname = hostnamerange >mark %set_hostname $err(err_hostname); 141 | 142 | # Section 4.1.3 143 | # note > alnum{1,32} is too restrictive (eg., no dashes) 144 | # note > see https://tools.ietf.org/html/rfc2234#section-2.1 for an interpretation of "ABNF alphanumeric" as stated by RFC 3164 regarding the tag 145 | # note > while RFC3164 assumes only ABNF alphanumeric process names, many BSD-syslog contains processe names with additional characters (-, _, .) 146 | tag = (print -- [ :\[]){1,32} >mark %set_tag @err(err_tag); 147 | 148 | visible = print | 0x80..0xFF; 149 | 150 | # The first not alphanumeric character starts the content (usually containing a PID) part of the message part 151 | contentval = !alnum @err(err_contentstart) >mark print* %set_content @err(err_content); 152 | 153 | content = '[' contentval ']'; # todo(leodido) > support ':' and ' ' too. Also they have to match? 154 | 155 | mex = visible+ >mark %set_message; 156 | 157 | msg = (tag content? ':' sp)? mex; 158 | 159 | fail := (any - [\n\r])* @err{ fgoto main; }; 160 | 161 | main := pri (timestamp | (rfc3339 when { m.rfc3339 })) sp hostname sp msg; 162 | 163 | }%% 164 | 165 | %% write data noerror noprefix; 166 | 167 | type machine struct { 168 | data []byte 169 | cs int 170 | p, pe, eof int 171 | pb int 172 | err error 173 | bestEffort bool 174 | yyyy int 175 | rfc3339 bool 176 | loc *time.Location 177 | timezone *time.Location 178 | } 179 | 180 | // NewMachine creates a new FSM able to parse RFC3164 syslog messages. 181 | func NewMachine(options ...syslog.MachineOption) syslog.Machine { 182 | m := &machine{} 183 | 184 | for _, opt := range options { 185 | opt(m) 186 | } 187 | 188 | %% access m.; 189 | %% variable p m.p; 190 | %% variable pe m.pe; 191 | %% variable eof m.eof; 192 | %% variable data m.data; 193 | 194 | return m 195 | } 196 | 197 | // WithBestEffort enables best effort mode. 198 | func (m *machine) WithBestEffort() { 199 | m.bestEffort = true 200 | } 201 | 202 | // HasBestEffort tells whether the receiving machine has best effort mode on or off. 203 | func (m *machine) HasBestEffort() bool { 204 | return m.bestEffort 205 | } 206 | 207 | // WithYear sets the year for the Stamp timestamp of the RFC 3164 syslog message. 208 | func (m *machine) WithYear(o YearOperator) { 209 | m.yyyy = YearOperation{o}.Operate() 210 | } 211 | 212 | // WithTimezone sets the time zone for the Stamp timestamp of the RFC 3164 syslog message. 213 | func (m *machine) WithTimezone(loc *time.Location) { 214 | m.loc = loc 215 | } 216 | 217 | // WithLocaleTimezone sets the locale time zone for the Stamp timestamp of the RFC 3164 syslog message. 218 | func (m *machine) WithLocaleTimezone(loc *time.Location) { 219 | m.timezone = loc 220 | } 221 | 222 | // WithRFC3339 enables ability to ALSO match RFC3339 timestamps. 223 | // 224 | // Notice this does not disable the default and correct timestamps - ie., Stamp timestamps. 225 | func (m *machine) WithRFC3339() { 226 | m.rfc3339 = true 227 | } 228 | 229 | // Err returns the error that occurred on the last call to Parse. 230 | // 231 | // If the result is nil, then the line was parsed successfully. 232 | func (m *machine) Err() error { 233 | return m.err 234 | } 235 | 236 | func (m *machine) text() []byte { 237 | return m.data[m.pb:m.p] 238 | } 239 | 240 | // Parse parses the input byte array as a RFC3164 syslog message. 241 | func (m *machine) Parse(input []byte) (syslog.Message, error) { 242 | m.data = input 243 | m.p = 0 244 | m.pb = 0 245 | m.pe = len(input) 246 | m.eof = len(input) 247 | m.err = nil 248 | output := &syslogMessage{} 249 | 250 | %% write init; 251 | %% write exec; 252 | 253 | if m.cs < first_final || m.cs == en_fail { 254 | if m.bestEffort && output.minimal() { 255 | // An error occurred but partial parsing is on and partial message is minimally valid 256 | return output.export(), m.err 257 | } 258 | return nil, m.err 259 | } 260 | 261 | return output.export(), nil 262 | } 263 | 264 | -------------------------------------------------------------------------------- /rfc3164/machine_test.go: -------------------------------------------------------------------------------- 1 | package rfc3164 2 | 3 | import ( 4 | "testing" 5 | "time" 6 | 7 | "github.com/influxdata/go-syslog/v3" 8 | syslogtesting "github.com/influxdata/go-syslog/v3/testing" 9 | "github.com/stretchr/testify/assert" 10 | ) 11 | 12 | // todo > add support for testing `best effort` mode 13 | 14 | type testCase struct { 15 | input []byte 16 | valid bool 17 | value syslog.Message 18 | errorString string 19 | partialValue syslog.Message 20 | } 21 | 22 | var testCases = []testCase{ 23 | { 24 | []byte(`<34>Jan 12 06:30:00 xxx apache: 1.2.3.4 - - [12/Jan/2011:06:29:59 +0100] "GET /foo/bar.html HTTP/1.1" 301 96 "-" "Mozilla/5.0 (Windows; U; Windows NT 5.1; fr; rv:1.9.2.12) Gecko/20101026 Firefox/3.6.12 ( .NET CLR 3.5.30729)" PID 18904 Time Taken 0`), 25 | true, 26 | &SyslogMessage{ 27 | Base: syslog.Base{ 28 | Priority: syslogtesting.Uint8Address(34), 29 | Severity: syslogtesting.Uint8Address(2), 30 | Facility: syslogtesting.Uint8Address(4), 31 | Timestamp: syslogtesting.TimeParse(time.Stamp, "Jan 12 06:30:00"), 32 | Hostname: syslogtesting.StringAddress("xxx"), 33 | Appname: syslogtesting.StringAddress("apache"), 34 | Message: syslogtesting.StringAddress(`1.2.3.4 - - [12/Jan/2011:06:29:59 +0100] "GET /foo/bar.html HTTP/1.1" 301 96 "-" "Mozilla/5.0 (Windows; U; Windows NT 5.1; fr; rv:1.9.2.12) Gecko/20101026 Firefox/3.6.12 ( .NET CLR 3.5.30729)" PID 18904 Time Taken 0`), 35 | }, 36 | }, 37 | "", 38 | nil, 39 | }, 40 | { 41 | []byte(`<34>Aug 7 06:30:00 xxx aaa: message from 1.2.3.4`), 42 | true, 43 | &SyslogMessage{ 44 | Base: syslog.Base{ 45 | Priority: syslogtesting.Uint8Address(34), 46 | Severity: syslogtesting.Uint8Address(2), 47 | Facility: syslogtesting.Uint8Address(4), 48 | Timestamp: syslogtesting.TimeParse(time.Stamp, "Aug 7 06:30:00"), 49 | Hostname: syslogtesting.StringAddress("xxx"), 50 | Appname: syslogtesting.StringAddress("aaa"), 51 | Message: syslogtesting.StringAddress(`message from 1.2.3.4`), 52 | }, 53 | }, 54 | "", 55 | nil, 56 | }, 57 | { 58 | input: []byte(`<85>Jan 24 15:50:41 ip-172-31-30-110 sudo[6040]: ec2-user : TTY=pts/0 ; PWD=/var/log ; USER=root ; COMMAND=/bin/tail secure`), 59 | valid: true, 60 | value: &SyslogMessage{ 61 | Base: syslog.Base{ 62 | Priority: syslogtesting.Uint8Address(85), 63 | Facility: syslogtesting.Uint8Address(10), 64 | Severity: syslogtesting.Uint8Address(5), 65 | Timestamp: syslogtesting.TimeParse(time.Stamp, "Jan 24 15:50:41"), 66 | Hostname: syslogtesting.StringAddress("ip-172-31-30-110"), 67 | Appname: syslogtesting.StringAddress("sudo"), 68 | ProcID: syslogtesting.StringAddress("6040"), 69 | Message: syslogtesting.StringAddress(`ec2-user : TTY=pts/0 ; PWD=/var/log ; USER=root ; COMMAND=/bin/tail secure`), 70 | }, 71 | }, 72 | }, 73 | { 74 | input: []byte(`<166>Jul 6 20:33:28 ABC-1-234567 Some message here`), 75 | valid: true, 76 | value: &SyslogMessage{ 77 | Base: syslog.Base{ 78 | Priority: syslogtesting.Uint8Address(166), 79 | Facility: syslogtesting.Uint8Address(20), 80 | Severity: syslogtesting.Uint8Address(6), 81 | Timestamp: syslogtesting.TimeParse(time.Stamp, "Jul 6 20:33:28"), 82 | Hostname: syslogtesting.StringAddress("ABC-1-234567"), 83 | Message: syslogtesting.StringAddress("Some message here"), 84 | }, 85 | }, 86 | }, 87 | { 88 | input: []byte(`<34>Oct 11 22:14:15 mymachine su: 'su root' failed for lonvick on /dev/pts/8`), 89 | valid: true, 90 | value: &SyslogMessage{ 91 | Base: syslog.Base{ 92 | Priority: syslogtesting.Uint8Address(34), 93 | Facility: syslogtesting.Uint8Address(4), 94 | Severity: syslogtesting.Uint8Address(2), 95 | Timestamp: syslogtesting.TimeParse(time.Stamp, "Oct 11 22:14:15"), 96 | Hostname: syslogtesting.StringAddress("mymachine"), 97 | Appname: syslogtesting.StringAddress("su"), 98 | Message: syslogtesting.StringAddress("'su root' failed for lonvick on /dev/pts/8"), 99 | }, 100 | }, 101 | }, 102 | { 103 | input: []byte(`<13>Feb 5 17:32:18 10.0.0.99 Use the BFG!`), 104 | valid: true, 105 | value: &SyslogMessage{ 106 | Base: syslog.Base{ 107 | Priority: syslogtesting.Uint8Address(13), 108 | Facility: syslogtesting.Uint8Address(1), 109 | Severity: syslogtesting.Uint8Address(5), 110 | Timestamp: syslogtesting.TimeParse(time.Stamp, "Feb 5 17:32:18"), 111 | Hostname: syslogtesting.StringAddress("10.0.0.99"), 112 | Message: syslogtesting.StringAddress("Use the BFG!"), 113 | }, 114 | }, 115 | }, 116 | { 117 | input: []byte(`<165>Aug 24 05:34:00 mymachine myproc[10]: %% It's time to make the do-nuts. %% Ingredients: Mix=OK, Jelly=OK # Devices: Mixer=OK, Jelly_Injector=OK, Frier=OK # Transport: Conveyer1=OK, Conveyer2=OK # %%`), 118 | valid: true, 119 | value: &SyslogMessage{ 120 | Base: syslog.Base{ 121 | Priority: syslogtesting.Uint8Address(165), 122 | Facility: syslogtesting.Uint8Address(20), 123 | Severity: syslogtesting.Uint8Address(5), 124 | Timestamp: syslogtesting.TimeParse(time.Stamp, "Aug 24 05:34:00"), 125 | Hostname: syslogtesting.StringAddress("mymachine"), 126 | Appname: syslogtesting.StringAddress("myproc"), 127 | ProcID: syslogtesting.StringAddress("10"), 128 | Message: syslogtesting.StringAddress(`%% It's time to make the do-nuts. %% Ingredients: Mix=OK, Jelly=OK # Devices: Mixer=OK, Jelly_Injector=OK, Frier=OK # Transport: Conveyer1=OK, Conveyer2=OK # %%`), 129 | }, 130 | }, 131 | }, 132 | { 133 | input: []byte(`<0>Oct 22 10:52:01 10.1.2.3 sched[0]: That's All Folks!`), 134 | valid: true, 135 | value: &SyslogMessage{ 136 | Base: syslog.Base{ 137 | Priority: syslogtesting.Uint8Address(0), 138 | Facility: syslogtesting.Uint8Address(0), 139 | Severity: syslogtesting.Uint8Address(0), 140 | Timestamp: syslogtesting.TimeParse(time.Stamp, "Oct 22 10:52:01"), 141 | Hostname: syslogtesting.StringAddress("10.1.2.3"), 142 | Appname: syslogtesting.StringAddress("sched"), 143 | ProcID: syslogtesting.StringAddress("0"), 144 | Message: syslogtesting.StringAddress(`That's All Folks!`), 145 | }, 146 | }, 147 | }, 148 | // todo > other test cases pleaaaase 149 | } 150 | 151 | func TestMachineParse(t *testing.T) { 152 | for _, tc := range testCases { 153 | tc := tc 154 | t.Run(syslogtesting.RightPad(string(tc.input), 50), func(t *testing.T) { 155 | t.Parallel() 156 | 157 | message, merr := NewMachine().Parse(tc.input) 158 | partial, perr := NewMachine(WithBestEffort()).Parse(tc.input) 159 | 160 | if !tc.valid { 161 | assert.Nil(t, message) 162 | assert.Error(t, merr) 163 | assert.EqualError(t, merr, tc.errorString) 164 | 165 | assert.Equal(t, tc.partialValue, partial) 166 | assert.EqualError(t, perr, tc.errorString) 167 | } 168 | if tc.valid { 169 | assert.Nil(t, merr) 170 | assert.NotEmpty(t, message) 171 | assert.Equal(t, message, partial) 172 | assert.Equal(t, merr, perr) 173 | } 174 | 175 | assert.Equal(t, tc.value, message) 176 | }) 177 | } 178 | } 179 | -------------------------------------------------------------------------------- /rfc3164/options.go: -------------------------------------------------------------------------------- 1 | package rfc3164 2 | 3 | import ( 4 | "time" 5 | 6 | syslog "github.com/influxdata/go-syslog/v3" 7 | ) 8 | 9 | // WithBestEffort enables the best effort mode. 10 | func WithBestEffort() syslog.MachineOption { 11 | return func(m syslog.Machine) syslog.Machine { 12 | m.WithBestEffort() 13 | return m 14 | } 15 | } 16 | 17 | // WithYear sets the strategy to decide the year for the Stamp timestamp of RFC 3164. 18 | func WithYear(o YearOperator) syslog.MachineOption { 19 | return func(m syslog.Machine) syslog.Machine { 20 | m.(*machine).WithYear(o) 21 | return m 22 | } 23 | } 24 | 25 | // WithTimezone sets the strategy to decide the timezone to apply to the Stamp timestamp of RFC 3164. 26 | func WithTimezone(loc *time.Location) syslog.MachineOption { 27 | return func(m syslog.Machine) syslog.Machine { 28 | m.(*machine).WithTimezone(loc) 29 | return m 30 | } 31 | } 32 | 33 | // WithLocaleTimezone sets the strategy to decide the timezone to apply to the Stamp timestamp of RFC 3164. 34 | func WithLocaleTimezone(loc *time.Location) syslog.MachineOption { 35 | return func(m syslog.Machine) syslog.Machine { 36 | m.(*machine).WithLocaleTimezone(loc) 37 | return m 38 | } 39 | } 40 | 41 | // todo > WithStrictHostname() option - see RFC3164 page 10 42 | // WithStrictHostname tells the parser to match the hostnames strictly as per RFC 3164 recommentations. 43 | // 44 | // The HOSTNAME field will contain only the hostname, the IPv4 address, or the IPv6 address of the originator of the message. 45 | // The preferred value is the hostname. 46 | // If the hostname is used, the HOSTNAME field MUST contain the hostname of the device as specified in STD 13 [4]. 47 | // It should be noted that this MUST NOT contain any embedded spaces. 48 | // The Domain Name MUST NOT be included in the HOSTNAME field. 49 | // If the IPv4 address is used, it MUST be shown as the dotted decimal notation as used in STD 13 [5]. 50 | // If an IPv6 address is used, any valid representation used in RFC 2373 [6] MAY be used. 51 | // A single space character MUST also follow the HOSTNAME field. 52 | // func WithStrictHostname() syslog.MachineOption { 53 | // return func(m syslog.Machine) syslog.Machine { 54 | // m.(*machine).WithStrictHostname() 55 | // return m 56 | // } 57 | // } 58 | 59 | // WithRFC3339 tells the parser to look for RFC3339 timestamps, too. 60 | // 61 | // It tells the parser to accept also RFC3339 timestamps even if they are not in the RFC3164 timestamp part. 62 | // Note that WithYear option will be ignored when an RFC3339 timestamp will match. 63 | func WithRFC3339() syslog.MachineOption { 64 | return func(m syslog.Machine) syslog.Machine { 65 | m.(*machine).WithRFC3339() 66 | return m 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /rfc3164/parser.go: -------------------------------------------------------------------------------- 1 | package rfc3164 2 | 3 | import ( 4 | "sync" 5 | 6 | syslog "github.com/influxdata/go-syslog/v3" 7 | ) 8 | 9 | // parser represent a RFC3164 parser with mutex capabilities. 10 | type parser struct { 11 | sync.Mutex 12 | *machine 13 | } 14 | 15 | // NewParser creates a syslog.Machine that parses RFC3164 syslog messages. 16 | func NewParser(options ...syslog.MachineOption) syslog.Machine { 17 | p := &parser{ 18 | machine: NewMachine(options...).(*machine), 19 | } 20 | 21 | return p 22 | } 23 | 24 | // HasBestEffort tells whether the receiving parser has best effort mode on or off. 25 | func (p *parser) HasBestEffort() bool { 26 | return p.bestEffort 27 | } 28 | 29 | // Parse parses the input RFC3164 syslog message using its FSM. 30 | // 31 | // Best effort mode enables the partial parsing. 32 | func (p *parser) Parse(input []byte) (syslog.Message, error) { 33 | p.Lock() 34 | defer p.Unlock() 35 | 36 | msg, err := p.machine.Parse(input) 37 | if err != nil { 38 | if p.bestEffort { 39 | return msg, err 40 | } 41 | return nil, err 42 | } 43 | 44 | return msg, nil 45 | } 46 | -------------------------------------------------------------------------------- /rfc3164/syslog_message.go: -------------------------------------------------------------------------------- 1 | package rfc3164 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/influxdata/go-syslog/v3" 7 | "github.com/influxdata/go-syslog/v3/common" 8 | ) 9 | 10 | type syslogMessage struct { 11 | prioritySet bool // We explictly flag the setting of priority since its zero value is a valid priority by RFC 3164 12 | timestampSet bool // We explictly flag the setting of timestamp since its zero value is a valid timestamp by RFC 3164 13 | priority uint8 14 | timestamp time.Time 15 | hostname string 16 | tag string 17 | content string 18 | message string 19 | } 20 | 21 | func (sm *syslogMessage) minimal() bool { 22 | return sm.prioritySet && common.ValidPriority(sm.priority) 23 | } 24 | 25 | // export is meant to be called on minimally-valid messages 26 | // thus it presumes priority and version values exists and are correct 27 | func (sm *syslogMessage) export() *SyslogMessage { 28 | out := &SyslogMessage{} 29 | out.ComputeFromPriority(sm.priority) 30 | 31 | if sm.timestampSet { 32 | out.Timestamp = &sm.timestamp 33 | } 34 | if sm.hostname != "-" && sm.hostname != "" { 35 | out.Hostname = &sm.hostname 36 | } 37 | if sm.tag != "-" && sm.tag != "" { 38 | out.Appname = &sm.tag 39 | } 40 | if sm.content != "-" && sm.content != "" { 41 | // Content is usually process ID 42 | // See https://tools.ietf.org/html/rfc3164#section-5.3 43 | out.ProcID = &sm.content 44 | } 45 | if sm.message != "" { 46 | out.Message = &sm.message 47 | } 48 | 49 | return out 50 | } 51 | 52 | // SyslogMessage represents a RFC3164 syslog message. 53 | type SyslogMessage struct { 54 | syslog.Base 55 | } 56 | -------------------------------------------------------------------------------- /rfc3164/year.go: -------------------------------------------------------------------------------- 1 | package rfc3164 2 | 3 | import ( 4 | "time" 5 | ) 6 | 7 | // YearOperator is an interface that the operation inferring the year have to implement. 8 | type YearOperator interface { 9 | Apply() int 10 | } 11 | 12 | // YearOperation represents the operation to perform to obtain the year depending on the inner operator/strategy. 13 | type YearOperation struct { 14 | Operator YearOperator 15 | } 16 | 17 | // Operate gets the year depending on the current strategy. 18 | func (y YearOperation) Operate() int { 19 | return y.Operator.Apply() 20 | } 21 | 22 | // CurrentYear is a strategy to obtain the current year in RFC 3164 syslog messages. 23 | type CurrentYear struct{} 24 | 25 | // Apply gets the current year 26 | func (CurrentYear) Apply() int { 27 | return time.Now().Year() 28 | } 29 | 30 | // Year is a strategy to obtain the specified year in the RFC 3164 syslog messages. 31 | type Year struct { 32 | YYYY int 33 | } 34 | 35 | // Apply gets the specified year 36 | func (y Year) Apply() int { 37 | return y.YYYY 38 | } 39 | -------------------------------------------------------------------------------- /rfc5424/builder.go.rl: -------------------------------------------------------------------------------- 1 | package rfc5424 2 | 3 | import ( 4 | "time" 5 | "sort" 6 | "fmt" 7 | 8 | "github.com/influxdata/go-syslog/v3/common" 9 | ) 10 | 11 | // todo(leodido) > support best effort for builder ? 12 | 13 | %%{ 14 | machine builder; 15 | 16 | include common "common.rl"; 17 | 18 | action mark { 19 | pb = p 20 | } 21 | 22 | action set_timestamp { 23 | if t, e := time.Parse(RFC3339MICRO, string(data[pb:p])); e == nil { 24 | sm.Timestamp = &t 25 | } 26 | } 27 | 28 | action set_hostname { 29 | if s := string(data[pb:p]); s != "-" { 30 | sm.Hostname = &s 31 | } 32 | } 33 | 34 | action set_appname { 35 | if s := string(data[pb:p]); s != "-" { 36 | sm.Appname = &s 37 | } 38 | } 39 | 40 | action set_procid { 41 | if s := string(data[pb:p]); s != "-" { 42 | sm.ProcID = &s 43 | } 44 | } 45 | 46 | action set_msgid { 47 | if s := string(data[pb:p]); s != "-" { 48 | sm.MsgID = &s 49 | } 50 | } 51 | 52 | action set_sdid { 53 | if sm.StructuredData == nil { 54 | sm.StructuredData = &(map[string]map[string]string{}) 55 | } 56 | 57 | id := string(data[pb:p]) 58 | elements := *sm.StructuredData 59 | if _, ok := elements[id]; !ok { 60 | elements[id] = map[string]string{} 61 | } 62 | } 63 | 64 | action set_sdpn { 65 | // Assuming SD map already exists, contains currentid key (set from outside) 66 | elements := *sm.StructuredData 67 | elements[currentid][string(data[pb:p])] = "" 68 | } 69 | 70 | action markslash { 71 | backslashes = append(backslashes, p) 72 | } 73 | 74 | action set_sdpv { 75 | // Store text 76 | text := data[pb:p] 77 | // Strip backslashes only when there are ... 78 | if len(backslashes) > 0 { 79 | text = common.RemoveBytes(text, backslashes, pb) 80 | } 81 | // Assuming SD map already exists, contains currentid key and currentparamname key (set from outside) 82 | elements := *sm.StructuredData 83 | elements[currentid][currentparamname] = string(text) 84 | } 85 | 86 | action set_msg { 87 | if s := string(data[pb:p]); s != "" { 88 | sm.Message = &s 89 | } 90 | } 91 | 92 | timestamp := (fulldate 'T' fulltime) >mark %set_timestamp; 93 | 94 | hostname := hostnamerange >mark %set_hostname; 95 | 96 | appname := appnamerange >mark %set_appname; 97 | 98 | procid := procidrange >mark %set_procid; 99 | 100 | msgid := msgidrange >mark %set_msgid; 101 | 102 | sdid := sdname >mark %set_sdid; 103 | 104 | sdpn := sdname >mark %set_sdpn; 105 | 106 | escapes = (bs >markslash toescape); 107 | 108 | sdpv := (utf8charwodelims* escapes*)+ >mark %set_sdpv; 109 | 110 | msg := any* >mark %set_msg; 111 | 112 | write data noerror nofinal; 113 | }%% 114 | 115 | type entrypoint int 116 | 117 | const ( 118 | timestamp entrypoint = iota 119 | hostname 120 | appname 121 | procid 122 | msgid 123 | sdid 124 | sdpn 125 | sdpv 126 | msg 127 | ) 128 | 129 | func (e entrypoint) translate() int { 130 | switch e { 131 | case timestamp: 132 | return builder_en_timestamp 133 | case hostname: 134 | return builder_en_hostname 135 | case appname: 136 | return builder_en_appname 137 | case procid: 138 | return builder_en_procid 139 | case msgid: 140 | return builder_en_msgid 141 | case sdid: 142 | return builder_en_sdid 143 | case sdpn: 144 | return builder_en_sdpn 145 | case sdpv: 146 | return builder_en_sdpv 147 | case msg: 148 | return builder_en_msg 149 | default: 150 | return builder_start 151 | } 152 | } 153 | 154 | var currentid string 155 | var currentparamname string 156 | 157 | func (sm *SyslogMessage) set(from entrypoint, value string) *SyslogMessage { 158 | data := []byte(value) 159 | p := 0 160 | pb := 0 161 | pe := len(data) 162 | eof := len(data) 163 | cs := from.translate() 164 | backslashes := []int{} 165 | %% write exec; 166 | 167 | return sm 168 | } 169 | 170 | // SetPriority set the priority value and the computed facility and severity codes accordingly. 171 | // 172 | // It ignores incorrect priority values (range [0, 191]). 173 | func (sm *SyslogMessage) SetPriority(value uint8) Builder { 174 | if common.ValidPriority(value) { 175 | sm.ComputeFromPriority(value) 176 | } 177 | 178 | return sm 179 | } 180 | 181 | // SetVersion set the version value. 182 | // 183 | // It ignores incorrect version values (range ]0, 999]). 184 | func (sm *SyslogMessage) SetVersion(value uint16) Builder { 185 | if common.ValidVersion(value) { 186 | sm.Version = value 187 | } 188 | 189 | return sm 190 | } 191 | 192 | // SetTimestamp set the timestamp value. 193 | func (sm *SyslogMessage) SetTimestamp(value string) Builder { 194 | return sm.set(timestamp, value) 195 | } 196 | 197 | // SetHostname set the hostname value. 198 | func (sm *SyslogMessage) SetHostname(value string) Builder { 199 | return sm.set(hostname, value) 200 | } 201 | 202 | // SetAppname set the appname value. 203 | func (sm *SyslogMessage) SetAppname(value string) Builder { 204 | return sm.set(appname, value) 205 | } 206 | 207 | // SetProcID set the procid value. 208 | func (sm *SyslogMessage) SetProcID(value string) Builder { 209 | return sm.set(procid, value) 210 | } 211 | 212 | // SetMsgID set the msgid value. 213 | func (sm *SyslogMessage) SetMsgID(value string) Builder { 214 | return sm.set(msgid, value) 215 | } 216 | 217 | // SetElementID set a structured data id. 218 | // 219 | // When the provided id already exists the operation is discarded. 220 | func (sm *SyslogMessage) SetElementID(value string) Builder { 221 | return sm.set(sdid, value) 222 | } 223 | 224 | // SetParameter set a structured data parameter belonging to the given element. 225 | // 226 | // If the element does not exist it creates one with the given element id. 227 | // When a parameter with the given name already exists for the given element the operation is discarded. 228 | func (sm *SyslogMessage) SetParameter(id string, name string, value string) Builder { 229 | // Create an element with the given id (or re-use the existing one) 230 | sm.set(sdid, id) 231 | 232 | // We can create parameter iff the given element id exists 233 | if sm.StructuredData != nil { 234 | elements := *sm.StructuredData 235 | if _, ok := elements[id]; ok { 236 | currentid = id 237 | sm.set(sdpn, name) 238 | // We can assign parameter value iff the given parameter key exists 239 | if _, ok := elements[id][name]; ok { 240 | currentparamname = name 241 | sm.set(sdpv, value) 242 | } 243 | } 244 | } 245 | 246 | return sm 247 | } 248 | 249 | // SetMessage set the message value. 250 | func (sm *SyslogMessage) SetMessage(value string) Builder { 251 | return sm.set(msg, value) 252 | } 253 | 254 | func (sm *SyslogMessage) String() (string, error) { 255 | if !sm.Valid() { 256 | return "", fmt.Errorf("invalid syslog") 257 | } 258 | 259 | template := "<%d>%d %s %s %s %s %s %s%s" 260 | 261 | t := "-" 262 | hn := "-" 263 | an := "-" 264 | pid := "-" 265 | mid := "-" 266 | sd := "-" 267 | m := "" 268 | if sm.Timestamp != nil { 269 | t = sm.Timestamp.Format("2006-01-02T15:04:05.999999Z07:00") // verify 07:00 270 | } 271 | if sm.Hostname != nil { 272 | hn = *sm.Hostname 273 | } 274 | if sm.Appname != nil { 275 | an = *sm.Appname 276 | } 277 | if sm.ProcID != nil { 278 | pid = *sm.ProcID 279 | } 280 | if sm.MsgID != nil { 281 | mid = *sm.MsgID 282 | } 283 | if sm.StructuredData != nil { 284 | // Sort element identifiers 285 | identifiers := make([]string, 0) 286 | for k, _ := range *sm.StructuredData { 287 | identifiers = append(identifiers, k) 288 | } 289 | sort.Strings(identifiers) 290 | 291 | sd = "" 292 | for _, id := range identifiers { 293 | sd += fmt.Sprintf("[%s", id) 294 | 295 | // Sort parameter names 296 | params := (*sm.StructuredData)[id] 297 | names := make([]string, 0) 298 | for n, _ := range params { 299 | names = append(names, n) 300 | } 301 | sort.Strings(names) 302 | 303 | for _, name := range names { 304 | sd += fmt.Sprintf(" %s=\"%s\"", name, common.EscapeBytes(params[name])) 305 | } 306 | sd += "]" 307 | } 308 | } 309 | if sm.Message != nil { 310 | m = " " + *sm.Message 311 | } 312 | 313 | return fmt.Sprintf(template, *sm.Priority, sm.Version, t, hn, an, pid, mid, sd, m), nil 314 | } 315 | -------------------------------------------------------------------------------- /rfc5424/builder_test.go: -------------------------------------------------------------------------------- 1 | package rfc5424 2 | 3 | import ( 4 | "testing" 5 | "time" 6 | 7 | "github.com/influxdata/go-syslog/v3" 8 | "github.com/stretchr/testify/assert" 9 | ) 10 | 11 | func TestSetTimestamp(t *testing.T) { 12 | m := &SyslogMessage{} 13 | 14 | assert.Equal(t, time.Date(2003, 10, 11, 22, 14, 15, 0, time.UTC), *m.SetTimestamp("2003-10-11T22:14:15Z").(*SyslogMessage).Timestamp) 15 | assert.Equal(t, time.Date(2003, 10, 11, 22, 14, 15, 3000, time.UTC), *m.SetTimestamp("2003-10-11T22:14:15.000003Z").(*SyslogMessage).Timestamp) 16 | 17 | // (note) > timestamp is invalid but it accepts until valid - ie., Z char 18 | // (note) > this dependes on the builder internal parser which does not have a final state, nor we check for any error or final state 19 | // (todo) > decide whether to be more strict or not 20 | assert.Equal(t, time.Date(2003, 10, 11, 22, 14, 15, 3000, time.UTC), *m.SetTimestamp("2003-10-11T22:14:15.000003Z+02:00").(*SyslogMessage).Timestamp) 21 | } 22 | 23 | func TestFacilityAndSeverity(t *testing.T) { 24 | m := &SyslogMessage{} 25 | 26 | assert.Nil(t, m.Facility) 27 | assert.Nil(t, m.FacilityMessage()) 28 | assert.Nil(t, m.FacilityLevel()) 29 | assert.Nil(t, m.Severity) 30 | assert.Nil(t, m.SeverityMessage()) 31 | assert.Nil(t, m.SeverityLevel()) 32 | assert.Nil(t, m.SeverityShortLevel()) 33 | 34 | m.SetPriority(1) 35 | 36 | assert.Equal(t, uint8(0), *m.Facility) 37 | assert.Equal(t, "kernel messages", *m.FacilityMessage()) 38 | assert.Equal(t, "kern", *m.FacilityLevel()) 39 | assert.Equal(t, uint8(1), *m.Severity) 40 | assert.Equal(t, "action must be taken immediately", *m.SeverityMessage()) 41 | assert.Equal(t, "alert", *m.SeverityLevel()) 42 | assert.Equal(t, "alert", *m.SeverityShortLevel()) 43 | 44 | m.SetPriority(120) 45 | 46 | assert.Equal(t, uint8(15), *m.Facility) 47 | assert.Equal(t, "clock daemon messages", *m.FacilityMessage()) 48 | assert.Equal(t, "cron2", *m.FacilityLevel()) 49 | assert.Equal(t, uint8(0), *m.Severity) 50 | assert.Equal(t, "system is unusable", *m.SeverityMessage()) 51 | assert.Equal(t, "emergency", *m.SeverityLevel()) 52 | assert.Equal(t, "emerg", *m.SeverityShortLevel()) 53 | 54 | m.SetPriority(99) 55 | 56 | assert.Equal(t, uint8(12), *m.Facility) 57 | assert.Equal(t, "NTP subsystem messages", *m.FacilityMessage()) 58 | assert.Equal(t, "ntp", *m.FacilityLevel()) // MUST fallback to message 59 | assert.Equal(t, uint8(3), *m.Severity) 60 | assert.Equal(t, "error conditions", *m.SeverityMessage()) 61 | assert.Equal(t, "error", *m.SeverityLevel()) 62 | assert.Equal(t, "err", *m.SeverityShortLevel()) 63 | } 64 | 65 | func TestSetNilTimestamp(t *testing.T) { 66 | m := &SyslogMessage{} 67 | assert.Nil(t, m.SetTimestamp("-").(*SyslogMessage).Timestamp) 68 | } 69 | 70 | func TestSetIncompleteTimestamp(t *testing.T) { 71 | m := &SyslogMessage{} 72 | date := []byte("2003-11-02T23:12:46.012345") 73 | prev := make([]byte, 0, len(date)) 74 | for _, d := range date { 75 | prev = append(prev, d) 76 | assert.Nil(t, m.SetTimestamp(string(prev)).(*SyslogMessage).Timestamp) 77 | } 78 | } 79 | 80 | func TestSetSyntacticallyCompleteButIncorrectTimestamp(t *testing.T) { 81 | m := &SyslogMessage{} 82 | assert.Nil(t, m.SetTimestamp("2003-42-42T22:14:15Z").(*SyslogMessage).Timestamp) 83 | } 84 | 85 | func TestSetImpossibleButSyntacticallyCorrectTimestamp(t *testing.T) { 86 | m := &SyslogMessage{} 87 | assert.Nil(t, m.SetTimestamp("2003-09-31T22:14:15Z").(*SyslogMessage).Timestamp) 88 | } 89 | 90 | func TestSetTooLongHostname(t *testing.T) { 91 | m := &SyslogMessage{} 92 | m.SetHostname("abcdefghilmnopqrstuvzabcdefghilmnopqrstuvzabcdefghilmnopqrstuvzabcdefghilmnopqrstuvzabcdefghilmnopqrstuvzabcdefghilmnopqrstuvzabcdefghilmnopqrstuvzabcdefghilmnopqrstuvzabcdefghilmnopqrstuvzabcdefghilmnopqrstuvzabcdefghilmnopqrstuvzabcdefghilmnopqrstuvzabcX") 93 | assert.Nil(t, m.Hostname) 94 | } 95 | 96 | func TestSetNilOrEmptyHostname(t *testing.T) { 97 | m := &SyslogMessage{} 98 | assert.Nil(t, m.SetHostname("-").(*SyslogMessage).Hostname) 99 | assert.Nil(t, m.SetHostname("").(*SyslogMessage).Hostname) 100 | } 101 | 102 | func TestSetHostname(t *testing.T) { 103 | m := &SyslogMessage{} 104 | 105 | maxlen := []byte("abcdefghilmnopqrstuvzabcdefghilmnopqrstuvzabcdefghilmnopqrstuvzabcdefghilmnopqrstuvzabcdefghilmnopqrstuvzabcdefghilmnopqrstuvzabcdefghilmnopqrstuvzabcdefghilmnopqrstuvzabcdefghilmnopqrstuvzabcdefghilmnopqrstuvzabcdefghilmnopqrstuvzabcdefghilmnopqrstuvzabc") 106 | 107 | prev := make([]byte, 0, len(maxlen)) 108 | for _, input := range maxlen { 109 | prev = append(prev, input) 110 | str := string(prev) 111 | assert.Equal(t, str, *m.SetHostname(str).(*SyslogMessage).Hostname) 112 | } 113 | } 114 | 115 | func TestSetAppname(t *testing.T) { 116 | m := &SyslogMessage{} 117 | 118 | maxlen := []byte("abcdefghilmnopqrstuvzabcdefghilmnopqrstuvzabcdef") 119 | 120 | prev := make([]byte, 0, len(maxlen)) 121 | for _, input := range maxlen { 122 | prev = append(prev, input) 123 | str := string(prev) 124 | assert.Equal(t, str, *m.SetAppname(str).(*SyslogMessage).Appname) 125 | } 126 | } 127 | 128 | func TestSetProcID(t *testing.T) { 129 | m := &SyslogMessage{} 130 | 131 | maxlen := []byte("abcdefghilmnopqrstuvzabcdefghilmnopqrstuvzabcdefghilmnopqrstuvzabcdefghilmnopqrstuvzabcdefghilmnopqrstuvzabcdefghilmnopqrstuvzab") 132 | 133 | prev := make([]byte, 0, len(maxlen)) 134 | for _, input := range maxlen { 135 | prev = append(prev, input) 136 | str := string(prev) 137 | assert.Equal(t, str, *m.SetProcID(str).(*SyslogMessage).ProcID) 138 | } 139 | } 140 | 141 | func TestSetMsgID(t *testing.T) { 142 | m := &SyslogMessage{} 143 | 144 | maxlen := []byte("abcdefghilmnopqrstuvzabcdefghilm") 145 | 146 | prev := make([]byte, 0, len(maxlen)) 147 | for _, input := range maxlen { 148 | prev = append(prev, input) 149 | str := string(prev) 150 | assert.Equal(t, str, *m.SetMsgID(str).(*SyslogMessage).MsgID) 151 | } 152 | } 153 | 154 | func TestSetSyntacticallyWrongHostnameAppnameProcIDMsgID(t *testing.T) { 155 | m := &SyslogMessage{} 156 | assert.Nil(t, m.SetHostname("white space not possible").(*SyslogMessage).Hostname) 157 | assert.Nil(t, m.SetHostname(string([]byte{0x0})).(*SyslogMessage).Hostname) 158 | assert.Nil(t, m.SetAppname("white space not possible").(*SyslogMessage).Appname) 159 | assert.Nil(t, m.SetAppname(string([]byte{0x0})).(*SyslogMessage).Appname) 160 | assert.Nil(t, m.SetProcID("white space not possible").(*SyslogMessage).ProcID) 161 | assert.Nil(t, m.SetProcID(string([]byte{0x0})).(*SyslogMessage).ProcID) 162 | assert.Nil(t, m.SetMsgID("white space not possible").(*SyslogMessage).MsgID) 163 | assert.Nil(t, m.SetMsgID(string([]byte{0x0})).(*SyslogMessage).MsgID) 164 | } 165 | 166 | func TestSetMessage(t *testing.T) { 167 | m := &SyslogMessage{} 168 | greek := "κόσμε" 169 | assert.Equal(t, greek, *m.SetMessage(greek).(*SyslogMessage).Message) 170 | } 171 | 172 | func TestSetEmptyMessage(t *testing.T) { 173 | m := &SyslogMessage{} 174 | m.SetMessage("") 175 | assert.Nil(t, m.Message) 176 | } 177 | 178 | func TestSetWrongUTF8Message(t *testing.T) { 179 | // todo(leodido) 180 | } 181 | 182 | func TestSetMessageWithBOM(t *testing.T) { 183 | // todo(leodido) 184 | } 185 | 186 | func TestSetMessageWithNewline(t *testing.T) { 187 | // todo(leodido) 188 | } 189 | 190 | func TestSetOutOfRangeVersion(t *testing.T) { 191 | m := &SyslogMessage{} 192 | m.SetVersion(1000) 193 | assert.Equal(t, m.Version, uint16(0)) // 0 signals nil for version 194 | m.SetVersion(0) 195 | assert.Equal(t, m.Version, uint16(0)) // 0 signals nil for version 196 | } 197 | 198 | func TestSetOutOfRangePriority(t *testing.T) { 199 | m := &SyslogMessage{} 200 | m.SetPriority(192) 201 | assert.Nil(t, m.Priority) 202 | } 203 | 204 | func TestSetVersion(t *testing.T) { 205 | m := &SyslogMessage{} 206 | m.SetVersion(1) 207 | assert.Equal(t, m.Version, uint16(1)) 208 | m.SetVersion(999) 209 | assert.Equal(t, m.Version, uint16(999)) 210 | } 211 | 212 | func TestSetPriority(t *testing.T) { 213 | m := &SyslogMessage{} 214 | m.SetPriority(0) 215 | assert.Equal(t, *m.Priority, uint8(0)) 216 | m.SetPriority(1) 217 | assert.Equal(t, *m.Priority, uint8(1)) 218 | m.SetPriority(191) 219 | assert.Equal(t, *m.Priority, uint8(191)) 220 | } 221 | 222 | func TestSetSDID(t *testing.T) { 223 | identifier := "one" 224 | m := &SyslogMessage{} 225 | assert.Nil(t, m.StructuredData) 226 | m.SetElementID(identifier) 227 | sd := m.StructuredData 228 | assert.NotNil(t, sd) 229 | assert.IsType(t, (*map[string]map[string]string)(nil), sd) 230 | assert.NotNil(t, (*sd)[identifier]) 231 | assert.IsType(t, map[string]string{}, (*sd)[identifier]) 232 | m.SetElementID(identifier) 233 | assert.Len(t, *sd, 1) 234 | } 235 | 236 | func TestSetAllLenghtsSDID(t *testing.T) { 237 | m := &SyslogMessage{} 238 | 239 | maxlen := []byte("abcdefghilmnopqrstuvzabcdefghilm") 240 | 241 | prev := make([]byte, 0, len(maxlen)) 242 | for i, input := range maxlen { 243 | prev = append(prev, input) 244 | id := string(prev) 245 | m.SetElementID(id) 246 | assert.Len(t, *m.StructuredData, i+1) 247 | assert.IsType(t, map[string]string{}, (*m.StructuredData)[id]) 248 | } 249 | } 250 | 251 | func TestSetTooLongSDID(t *testing.T) { 252 | m := &SyslogMessage{} 253 | m.SetElementID("abcdefghilmnopqrstuvzabcdefghilmX") 254 | assert.Nil(t, m.StructuredData) 255 | } 256 | 257 | func TestSetSyntacticallyWrongSDID(t *testing.T) { 258 | m := &SyslogMessage{} 259 | m.SetElementID("no whitespaces") 260 | assert.Nil(t, m.StructuredData) 261 | m.SetElementID(" ") 262 | assert.Nil(t, m.StructuredData) 263 | m.SetElementID(`"`) 264 | assert.Nil(t, m.StructuredData) 265 | m.SetElementID(`no"`) 266 | assert.Nil(t, m.StructuredData) 267 | m.SetElementID(`"no`) 268 | assert.Nil(t, m.StructuredData) 269 | m.SetElementID("]") 270 | assert.Nil(t, m.StructuredData) 271 | m.SetElementID("no]") 272 | assert.Nil(t, m.StructuredData) 273 | m.SetElementID("]no") 274 | assert.Nil(t, m.StructuredData) 275 | } 276 | 277 | func TestSetEmptySDID(t *testing.T) { 278 | m := &SyslogMessage{} 279 | m.SetElementID("") 280 | assert.Nil(t, m.StructuredData) 281 | } 282 | 283 | func TestSetSDParam(t *testing.T) { 284 | id := "one" 285 | pn := "pname" 286 | pv := "pvalue" 287 | m := &SyslogMessage{} 288 | m.SetParameter(id, pn, pv) 289 | sd := m.StructuredData 290 | assert.NotNil(t, sd) 291 | assert.IsType(t, (*map[string]map[string]string)(nil), sd) 292 | assert.NotNil(t, (*sd)[id]) 293 | assert.IsType(t, map[string]string{}, (*sd)[id]) 294 | assert.Len(t, *sd, 1) 295 | assert.Len(t, (*sd)[id], 1) 296 | assert.Equal(t, pv, (*sd)[id][pn]) 297 | 298 | pn1 := "pname1" 299 | pv1 := "κόσμε" 300 | m.SetParameter(id, pn1, pv1) 301 | assert.Len(t, (*sd)[id], 2) 302 | assert.Equal(t, pv1, (*sd)[id][pn1]) 303 | 304 | id1 := "another" 305 | m.SetParameter(id1, pn1, pv1).SetParameter(id1, pn, pv) 306 | assert.Len(t, *sd, 2) 307 | assert.Len(t, (*sd)[id1], 2) 308 | assert.Equal(t, pv1, (*sd)[id1][pn1]) 309 | assert.Equal(t, pv, (*sd)[id1][pn]) 310 | 311 | id2 := "tre" 312 | pn2 := "meta" 313 | m.SetParameter(id2, pn, `valid\\`).SetParameter(id2, pn1, `\]valid`).SetParameter(id2, pn2, `is\"valid`) 314 | assert.Len(t, *sd, 3) 315 | assert.Len(t, (*sd)[id2], 3) 316 | assert.Equal(t, `valid\`, (*sd)[id2][pn]) 317 | assert.Equal(t, `]valid`, (*sd)[id2][pn1]) 318 | assert.Equal(t, `is"valid`, (*sd)[id2][pn2]) 319 | // Cannot contain \, ], " unless escaped 320 | m.SetParameter(id2, pn, `is\valid`).SetParameter(id2, pn1, `is]valid`).SetParameter(id2, pn2, `is"valid`) 321 | assert.Len(t, (*sd)[id2], 3) 322 | } 323 | 324 | func TestSetEmptySDParam(t *testing.T) { 325 | id := "id" 326 | pn := "pn" 327 | m := &SyslogMessage{} 328 | m.SetParameter(id, pn, "") 329 | sd := m.StructuredData 330 | assert.Len(t, *sd, 1) 331 | assert.Len(t, (*sd)[id], 1) 332 | assert.Equal(t, "", (*sd)[id][pn]) 333 | } 334 | 335 | func TestSerialization(t *testing.T) { 336 | var res string 337 | var err error 338 | var pout syslog.Message 339 | var perr error 340 | 341 | p := NewParser() 342 | 343 | // Valid syslog message 344 | m := &SyslogMessage{} 345 | m.SetPriority(1) 346 | m.SetVersion(1) 347 | res, err = m.String() 348 | assert.Nil(t, err) 349 | assert.Equal(t, "<1>1 - - - - - -", res) 350 | 351 | pout, perr = p.Parse([]byte(res)) 352 | assert.Equal(t, m, pout) 353 | assert.Nil(t, perr) 354 | 355 | m.SetMessage("-") // does not means nil in this case, remember 356 | res, err = m.String() 357 | assert.Nil(t, err) 358 | assert.Equal(t, "<1>1 - - - - - - -", res) 359 | 360 | pout, perr = p.Parse([]byte(res)) 361 | assert.Equal(t, m, pout) 362 | assert.Nil(t, perr) 363 | 364 | m. 365 | SetParameter("mega", "x", "a"). 366 | SetParameter("mega", "y", "b"). 367 | SetParameter("mega", "z", `\" \] \\`). 368 | SetParameter("peta", "a", "name"). 369 | SetParameter("giga", "1", ""). 370 | SetParameter("peta", "c", "nomen") 371 | 372 | res, err = m.String() 373 | assert.Nil(t, err) 374 | assert.Equal(t, `<1>1 - - - - - [giga 1=""][mega x="a" y="b" z="\" \] \\"][peta a="name" c="nomen"] -`, res) 375 | 376 | pout, perr = p.Parse([]byte(res)) 377 | assert.Equal(t, m, pout) 378 | assert.Nil(t, perr) 379 | 380 | m.SetHostname("host1") 381 | res, err = m.String() 382 | assert.Nil(t, err) 383 | assert.Equal(t, `<1>1 - host1 - - - [giga 1=""][mega x="a" y="b" z="\" \] \\"][peta a="name" c="nomen"] -`, res) 384 | 385 | pout, perr = p.Parse([]byte(res)) 386 | assert.Equal(t, m, pout) 387 | assert.Nil(t, perr) 388 | 389 | m.SetAppname("su") 390 | res, err = m.String() 391 | assert.Nil(t, err) 392 | assert.Equal(t, `<1>1 - host1 su - - [giga 1=""][mega x="a" y="b" z="\" \] \\"][peta a="name" c="nomen"] -`, res) 393 | 394 | pout, perr = p.Parse([]byte(res)) 395 | assert.Equal(t, m, pout) 396 | assert.Nil(t, perr) 397 | 398 | m.SetProcID("22") 399 | res, err = m.String() 400 | assert.Nil(t, err) 401 | assert.Equal(t, `<1>1 - host1 su 22 - [giga 1=""][mega x="a" y="b" z="\" \] \\"][peta a="name" c="nomen"] -`, res) 402 | 403 | pout, perr = p.Parse([]byte(res)) 404 | assert.Equal(t, m, pout) 405 | assert.Nil(t, perr) 406 | 407 | m.SetMsgID("#1") 408 | res, err = m.String() 409 | assert.Nil(t, err) 410 | assert.Equal(t, `<1>1 - host1 su 22 #1 [giga 1=""][mega x="a" y="b" z="\" \] \\"][peta a="name" c="nomen"] -`, res) 411 | 412 | pout, perr = p.Parse([]byte(res)) 413 | assert.Equal(t, m, pout) 414 | assert.Nil(t, perr) 415 | 416 | m.SetTimestamp("2002-10-22T16:33:15.000087+01:00") 417 | res, err = m.String() 418 | assert.Nil(t, err) 419 | assert.Equal(t, `<1>1 2002-10-22T16:33:15.000087+01:00 host1 su 22 #1 [giga 1=""][mega x="a" y="b" z="\" \] \\"][peta a="name" c="nomen"] -`, res) 420 | 421 | pout, perr = p.Parse([]byte(res)) 422 | assert.Equal(t, m, pout) 423 | assert.Nil(t, perr) 424 | 425 | m.SetMessage("κόσμε") 426 | res, err = m.String() 427 | assert.Nil(t, err) 428 | assert.Equal(t, `<1>1 2002-10-22T16:33:15.000087+01:00 host1 su 22 #1 [giga 1=""][mega x="a" y="b" z="\" \] \\"][peta a="name" c="nomen"] κόσμε`, res) 429 | 430 | pout, perr = p.Parse([]byte(res)) 431 | assert.Equal(t, m, pout) 432 | assert.Nil(t, perr) 433 | 434 | // Invalid syslog message 435 | m2 := &SyslogMessage{} 436 | m2.SetPriority(192) 437 | m2.SetVersion(9999) 438 | res, err = m2.String() 439 | assert.Empty(t, res) 440 | assert.Error(t, err) 441 | } 442 | -------------------------------------------------------------------------------- /rfc5424/example_test.go: -------------------------------------------------------------------------------- 1 | package rfc5424 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/davecgh/go-spew/spew" 7 | ) 8 | 9 | func output(out interface{}) { 10 | spew.Config.DisableCapacities = true 11 | spew.Config.DisablePointerAddresses = true 12 | spew.Dump(out) 13 | } 14 | 15 | func Example() { 16 | i := []byte(`<165>4 2018-10-11T22:14:15.003Z mymach.it e - 1 [ex@32473 iut="3"] An application event log entry...`) 17 | p := NewParser() 18 | m, _ := p.Parse(i) 19 | output(m) 20 | msg := m.(*SyslogMessage) 21 | fmt.Println(*msg.Message) 22 | fmt.Println(*msg.Hostname) 23 | output(*msg.StructuredData) 24 | // Output: 25 | // (*rfc5424.SyslogMessage)({ 26 | // Base: (syslog.Base) { 27 | // Facility: (*uint8)(20), 28 | // Severity: (*uint8)(5), 29 | // Priority: (*uint8)(165), 30 | // Timestamp: (*time.Time)(2018-10-11 22:14:15.003 +0000 UTC), 31 | // Hostname: (*string)((len=9) "mymach.it"), 32 | // Appname: (*string)((len=1) "e"), 33 | // ProcID: (*string)(), 34 | // MsgID: (*string)((len=1) "1"), 35 | // Message: (*string)((len=33) "An application event log entry...") 36 | // }, 37 | // Version: (uint16) 4, 38 | // StructuredData: (*map[string]map[string]string)((len=1) { 39 | // (string) (len=8) "ex@32473": (map[string]string) (len=1) { 40 | // (string) (len=3) "iut": (string) (len=1) "3" 41 | // } 42 | // }) 43 | // }) 44 | // An application event log entry... 45 | // mymach.it 46 | // (map[string]map[string]string) (len=1) { 47 | // (string) (len=8) "ex@32473": (map[string]string) (len=1) { 48 | // (string) (len=3) "iut": (string) (len=1) "3" 49 | // } 50 | // } 51 | } 52 | 53 | func Example_besteffort() { 54 | i := []byte(`<1>1 A - - - - - -`) 55 | p := NewParser(WithBestEffort()) 56 | m, e := p.Parse(i) 57 | output(m) 58 | fmt.Println(e) 59 | // Output: 60 | // (*rfc5424.SyslogMessage)({ 61 | // Base: (syslog.Base) { 62 | // Facility: (*uint8)(0), 63 | // Severity: (*uint8)(1), 64 | // Priority: (*uint8)(1), 65 | // Timestamp: (*time.Time)(), 66 | // Hostname: (*string)(), 67 | // Appname: (*string)(), 68 | // ProcID: (*string)(), 69 | // MsgID: (*string)(), 70 | // Message: (*string)() 71 | // }, 72 | // Version: (uint16) 1, 73 | // StructuredData: (*map[string]map[string]string)() 74 | // }) 75 | // expecting a RFC3339MICRO timestamp or a nil value [col 5] 76 | } 77 | 78 | func Example_builder() { 79 | msg := &SyslogMessage{} 80 | msg.SetTimestamp("not a RFC3339MICRO timestamp") 81 | fmt.Println("Valid?", msg.Valid()) 82 | msg.SetPriority(191) 83 | msg.SetVersion(1) 84 | fmt.Println("Valid?", msg.Valid()) 85 | output(msg) 86 | str, _ := msg.String() 87 | fmt.Println(str) 88 | // Output: 89 | // Valid? false 90 | // Valid? true 91 | // (*rfc5424.SyslogMessage)({ 92 | // Base: (syslog.Base) { 93 | // Facility: (*uint8)(23), 94 | // Severity: (*uint8)(7), 95 | // Priority: (*uint8)(191), 96 | // Timestamp: (*time.Time)(), 97 | // Hostname: (*string)(), 98 | // Appname: (*string)(), 99 | // ProcID: (*string)(), 100 | // MsgID: (*string)(), 101 | // Message: (*string)() 102 | // }, 103 | // Version: (uint16) 1, 104 | // StructuredData: (*map[string]map[string]string)() 105 | // }) 106 | // <191>1 - - - - - - 107 | } 108 | -------------------------------------------------------------------------------- /rfc5424/machine.go.rl: -------------------------------------------------------------------------------- 1 | package rfc5424 2 | 3 | import ( 4 | "time" 5 | "fmt" 6 | 7 | "github.com/influxdata/go-syslog/v3" 8 | "github.com/influxdata/go-syslog/v3/common" 9 | ) 10 | 11 | // ColumnPositionTemplate is the template used to communicate the column where errors occur. 12 | var ColumnPositionTemplate = " [col %d]" 13 | 14 | const ( 15 | // ErrPrival represents an error in the priority value (PRIVAL) inside the PRI part of the RFC5424 syslog message. 16 | ErrPrival = "expecting a priority value in the range 1-191 or equal to 0" 17 | // ErrPri represents an error in the PRI part of the RFC5424 syslog message. 18 | ErrPri = "expecting a priority value within angle brackets" 19 | // ErrVersion represents an error in the VERSION part of the RFC5424 syslog message. 20 | ErrVersion = "expecting a version value in the range 1-999" 21 | // ErrTimestamp represents an error in the TIMESTAMP part of the RFC5424 syslog message. 22 | ErrTimestamp = "expecting a RFC3339MICRO timestamp or a nil value" 23 | // ErrHostname represents an error in the HOSTNAME part of the RFC5424 syslog message. 24 | ErrHostname = "expecting an hostname (from 1 to max 255 US-ASCII characters) or a nil value" 25 | // ErrAppname represents an error in the APP-NAME part of the RFC5424 syslog message. 26 | ErrAppname = "expecting an app-name (from 1 to max 48 US-ASCII characters) or a nil value" 27 | // ErrProcID represents an error in the PROCID part of the RFC5424 syslog message. 28 | ErrProcID = "expecting a procid (from 1 to max 128 US-ASCII characters) or a nil value" 29 | // ErrMsgID represents an error in the MSGID part of the RFC5424 syslog message. 30 | ErrMsgID = "expecting a msgid (from 1 to max 32 US-ASCII characters) or a nil value" 31 | // ErrStructuredData represents an error in the STRUCTURED DATA part of the RFC5424 syslog message. 32 | ErrStructuredData = "expecting a structured data section containing one or more elements (`[id( key=\"value\")*]+`) or a nil value" 33 | // ErrSdID represents an error regarding the ID of a STRUCTURED DATA element of the RFC5424 syslog message. 34 | ErrSdID = "expecting a structured data element id (from 1 to max 32 US-ASCII characters; except `=`, ` `, `]`, and `\"`" 35 | // ErrSdIDDuplicated represents an error occurring when two STRUCTURED DATA elementes have the same ID in a RFC5424 syslog message. 36 | ErrSdIDDuplicated = "duplicate structured data element id" 37 | // ErrSdParam represents an error regarding a STRUCTURED DATA PARAM of the RFC5424 syslog message. 38 | ErrSdParam = "expecting a structured data parameter (`key=\"value\"`, both part from 1 to max 32 US-ASCII characters; key cannot contain `=`, ` `, `]`, and `\"`, while value cannot contain `]`, backslash, and `\"` unless escaped)" 39 | // ErrMsg represents an error in the MESSAGE part of the RFC5424 syslog message. 40 | ErrMsg = "expecting a free-form optional message in UTF-8 (starting with or without BOM)" 41 | // ErrMsgNotCompliant represents an error in the MESSAGE part of the RFC5424 syslog message if WithCompliatMsg option is on. 42 | ErrMsgNotCompliant = ErrMsg + " or a free-form optional message in any encoding (starting without BOM)" 43 | // ErrEscape represents the error for a RFC5424 syslog message occurring when a STRUCTURED DATA PARAM value contains '"', '\', or ']' not escaped. 44 | ErrEscape = "expecting chars `]`, `\"`, and `\\` to be escaped within param value" 45 | // ErrParse represents a general parsing error for a RFC5424 syslog message. 46 | ErrParse = "parsing error" 47 | ) 48 | 49 | // RFC3339MICRO represents the timestamp format that RFC5424 mandates. 50 | const RFC3339MICRO = "2006-01-02T15:04:05.999999Z07:00" 51 | 52 | %%{ 53 | machine rfc5424; 54 | 55 | include common "common.rl"; 56 | 57 | # unsigned alphabet 58 | alphtype uint8; 59 | 60 | action mark { 61 | m.pb = m.p 62 | } 63 | 64 | action markmsg { 65 | m.msgat = m.p 66 | } 67 | 68 | action select_msg_mode { 69 | fhold; 70 | 71 | if m.compliantMsg { 72 | fgoto msg_compliant; 73 | } 74 | fgoto msg_any; 75 | } 76 | 77 | action set_prival { 78 | output.priority = uint8(common.UnsafeUTF8DecimalCodePointsToInt(m.text())) 79 | output.prioritySet = true 80 | } 81 | 82 | action set_version { 83 | output.version = uint16(common.UnsafeUTF8DecimalCodePointsToInt(m.text())) 84 | } 85 | 86 | action set_timestamp { 87 | if t, e := time.Parse(RFC3339MICRO, string(m.text())); e != nil { 88 | m.err = fmt.Errorf("%s [col %d]", e, m.p) 89 | fhold; 90 | fgoto fail; 91 | } else { 92 | output.timestamp = t 93 | output.timestampSet = true 94 | } 95 | } 96 | 97 | action set_hostname { 98 | output.hostname = string(m.text()) 99 | } 100 | 101 | action set_appname { 102 | output.appname = string(m.text()) 103 | } 104 | 105 | action set_procid { 106 | output.procID = string(m.text()) 107 | } 108 | 109 | action set_msgid { 110 | output.msgID = string(m.text()) 111 | } 112 | 113 | action ini_elements { 114 | output.structuredData = map[string]map[string]string{} 115 | } 116 | 117 | action set_id { 118 | if _, ok := output.structuredData[string(m.text())]; ok { 119 | // As per RFC5424 section 6.3.2 SD-ID MUST NOT exist more than once in a message 120 | m.err = fmt.Errorf(ErrSdIDDuplicated + ColumnPositionTemplate, m.p) 121 | fhold; 122 | fgoto fail; 123 | } else { 124 | id := string(m.text()) 125 | output.structuredData[id] = map[string]string{} 126 | output.hasElements = true 127 | m.currentelem = id 128 | } 129 | } 130 | 131 | action ini_sdparam { 132 | m.backslashat = []int{} 133 | } 134 | 135 | action add_slash { 136 | m.backslashat = append(m.backslashat, m.p) 137 | } 138 | 139 | action set_paramname { 140 | m.currentparam = string(m.text()) 141 | } 142 | 143 | action set_paramvalue { 144 | if output.hasElements { 145 | // (fixme) > what if SD-PARAM-NAME already exist for the current element (ie., current SD-ID)? 146 | 147 | // Store text 148 | text := m.text() 149 | 150 | // Strip backslashes only when there are ... 151 | if len(m.backslashat) > 0 { 152 | text = common.RemoveBytes(text, m.backslashat, m.pb) 153 | } 154 | output.structuredData[m.currentelem][m.currentparam] = string(text) 155 | } 156 | } 157 | 158 | action set_msg { 159 | output.message = string(m.text()) 160 | } 161 | 162 | action err_prival { 163 | m.err = fmt.Errorf(ErrPrival + ColumnPositionTemplate, m.p) 164 | fhold; 165 | fgoto fail; 166 | } 167 | 168 | action err_pri { 169 | m.err = fmt.Errorf(ErrPri + ColumnPositionTemplate, m.p) 170 | fhold; 171 | fgoto fail; 172 | } 173 | 174 | action err_version { 175 | m.err = fmt.Errorf(ErrVersion + ColumnPositionTemplate, m.p) 176 | fhold; 177 | fgoto fail; 178 | } 179 | 180 | action err_timestamp { 181 | m.err = fmt.Errorf(ErrTimestamp + ColumnPositionTemplate, m.p) 182 | fhold; 183 | fgoto fail; 184 | } 185 | 186 | action err_hostname { 187 | m.err = fmt.Errorf(ErrHostname + ColumnPositionTemplate, m.p) 188 | fhold; 189 | fgoto fail; 190 | } 191 | 192 | action err_appname { 193 | m.err = fmt.Errorf(ErrAppname + ColumnPositionTemplate, m.p) 194 | fhold; 195 | fgoto fail; 196 | } 197 | 198 | action err_procid { 199 | m.err = fmt.Errorf(ErrProcID + ColumnPositionTemplate, m.p) 200 | fhold; 201 | fgoto fail; 202 | } 203 | 204 | action err_msgid { 205 | m.err = fmt.Errorf(ErrMsgID + ColumnPositionTemplate, m.p) 206 | fhold; 207 | fgoto fail; 208 | } 209 | 210 | action err_structureddata { 211 | m.err = fmt.Errorf(ErrStructuredData + ColumnPositionTemplate, m.p) 212 | fhold; 213 | fgoto fail; 214 | } 215 | 216 | action err_sdid { 217 | delete(output.structuredData, m.currentelem) 218 | if len(output.structuredData) == 0 { 219 | output.hasElements = false 220 | } 221 | m.err = fmt.Errorf(ErrSdID + ColumnPositionTemplate, m.p) 222 | fhold; 223 | fgoto fail; 224 | } 225 | 226 | action err_sdparam { 227 | if len(output.structuredData) > 0 { 228 | delete(output.structuredData[m.currentelem], m.currentparam) 229 | } 230 | m.err = fmt.Errorf(ErrSdParam + ColumnPositionTemplate, m.p) 231 | fhold; 232 | fgoto fail; 233 | } 234 | 235 | action err_msg { 236 | // If error encountered within the message rule ... 237 | if m.msgat > 0 { 238 | // Save the text until valid (m.p is where the parser has stopped) 239 | output.message = string(m.data[m.msgat:m.p]) 240 | } 241 | 242 | if m.compliantMsg { 243 | m.err = fmt.Errorf(ErrMsgNotCompliant + ColumnPositionTemplate, m.p) 244 | } else { 245 | m.err = fmt.Errorf(ErrMsg + ColumnPositionTemplate, m.p) 246 | } 247 | 248 | fhold; 249 | fgoto fail; 250 | } 251 | 252 | action err_escape { 253 | m.err = fmt.Errorf(ErrEscape + ColumnPositionTemplate, m.p) 254 | fhold; 255 | fgoto fail; 256 | } 257 | 258 | action err_parse { 259 | m.err = fmt.Errorf(ErrParse + ColumnPositionTemplate, m.p) 260 | fhold; 261 | fgoto fail; 262 | } 263 | 264 | nilvalue = '-'; 265 | 266 | pri = ('<' prival >mark %from(set_prival) $err(err_prival) '>') @err(err_pri); 267 | 268 | version = (nonzerodigit digit{0,2} mark %from(set_version) %eof(set_version) @err(err_version); 269 | 270 | timestamp = (nilvalue | (fulldate >mark 'T' fulltime %set_timestamp %err(set_timestamp))) @err(err_timestamp); 271 | 272 | hostname = hostnamerange >mark %set_hostname $err(err_hostname); 273 | 274 | appname = appnamerange >mark %set_appname $err(err_appname); 275 | 276 | procid = procidrange >mark %set_procid $err(err_procid); 277 | 278 | msgid = msgidrange >mark %set_msgid $err(err_msgid); 279 | 280 | header = (pri version sp timestamp sp hostname sp appname sp procid sp msgid) <>err(err_parse); 281 | 282 | # \", \], \\ 283 | escapes = (bs >add_slash toescape) $err(err_escape); 284 | 285 | # As per section 6.3.3 param value MUST NOT contain '"', '\' and ']', unless they are escaped. 286 | # A backslash '\' followed by none of the this three characters is an invalid escape sequence. 287 | # In this case, treat it as a regular backslash and the following character as a regular character (not altering the invalid sequence). 288 | paramvalue = (utf8charwodelims* escapes*)+ >mark %set_paramvalue; 289 | 290 | paramname = sdname >mark %set_paramname; 291 | 292 | sdparam = (paramname '=' dq paramvalue dq) >ini_sdparam $err(err_sdparam); 293 | 294 | # (note) > finegrained semantics of section 6.3.2 not represented here since not so useful for parsing goal 295 | sdid = sdname >mark %set_id %err(set_id) $err(err_sdid); 296 | 297 | sdelement = ('[' sdid (sp sdparam)* ']'); 298 | 299 | structureddata = nilvalue | sdelement+ >ini_elements $err(err_structureddata); 300 | 301 | msg_any := any* >mark >markmsg %set_msg $err(err_msg); 302 | 303 | # MSG-ANY = *OCTET ; not starting with BOM 304 | # MSG-UTF8 = BOM *OCTECT ; UTF-8 string as specified in RFC 3629 305 | # MSG = MSG-ANY | MSG-UTF8 306 | msg_compliant := ((bom utf8octets) | (any* - (bom any*))) >mark >markmsg %set_msg $err(err_msg); 307 | 308 | msg = any? @select_msg_mode; 309 | 310 | fail := (any - [\n\r])* @err{ fgoto main; }; 311 | 312 | main := header sp structureddata (sp msg)? $err(err_parse); 313 | 314 | }%% 315 | 316 | %% write data noerror noprefix; 317 | 318 | type machine struct { 319 | data []byte 320 | cs int 321 | p, pe, eof int 322 | pb int 323 | err error 324 | currentelem string 325 | currentparam string 326 | msgat int 327 | backslashat []int 328 | bestEffort bool 329 | compliantMsg bool 330 | } 331 | 332 | // NewMachine creates a new FSM able to parse RFC5424 syslog messages. 333 | func NewMachine(options ...syslog.MachineOption) syslog.Machine { 334 | m := &machine{} 335 | 336 | for _, opt := range options { 337 | opt(m) 338 | } 339 | 340 | %% access m.; 341 | %% variable p m.p; 342 | %% variable pe m.pe; 343 | %% variable eof m.eof; 344 | %% variable data m.data; 345 | 346 | return m 347 | } 348 | 349 | // WithBestEffort enables best effort mode. 350 | func (m *machine) WithBestEffort() { 351 | m.bestEffort = true 352 | } 353 | 354 | // HasBestEffort tells whether the receiving machine has best effort mode on or off. 355 | func (m *machine) HasBestEffort() bool { 356 | return m.bestEffort 357 | } 358 | 359 | // Err returns the error that occurred on the last call to Parse. 360 | // 361 | // If the result is nil, then the line was parsed successfully. 362 | func (m *machine) Err() error { 363 | return m.err 364 | } 365 | 366 | func (m *machine) text() []byte { 367 | return m.data[m.pb:m.p] 368 | } 369 | 370 | // Parse parses the input byte array as a RFC5424 syslog message. 371 | // 372 | // When a valid RFC5424 syslog message is given it outputs its structured representation. 373 | // If the parsing detects an error it returns it with the position where the error occurred. 374 | // 375 | // It can also partially parse input messages returning a partially valid structured representation 376 | // and the error that stopped the parsing. 377 | func (m *machine) Parse(input []byte) (syslog.Message, error) { 378 | m.data = input 379 | m.p = 0 380 | m.pb = 0 381 | m.msgat = 0 382 | m.backslashat = []int{} 383 | m.pe = len(input) 384 | m.eof = len(input) 385 | m.err = nil 386 | output := &syslogMessage{} 387 | 388 | %% write init; 389 | %% write exec; 390 | 391 | if m.cs < first_final || m.cs == en_fail { 392 | if m.bestEffort && output.minimal() { 393 | // An error occurred but partial parsing is on and partial message is minimally valid 394 | return output.export(), m.err 395 | } 396 | return nil, m.err 397 | } 398 | 399 | return output.export(), nil 400 | } 401 | -------------------------------------------------------------------------------- /rfc5424/options.go: -------------------------------------------------------------------------------- 1 | package rfc5424 2 | 3 | import ( 4 | syslog "github.com/influxdata/go-syslog/v3" 5 | ) 6 | 7 | // WithBestEffort enables the best effort mode. 8 | func WithBestEffort() syslog.MachineOption { 9 | return func(m syslog.Machine) syslog.Machine { 10 | m.WithBestEffort() 11 | return m 12 | } 13 | } 14 | 15 | // WithCompliantMsg enables the parsing of the MSG part of the Syslog as per RFC5424. 16 | // 17 | // When this is on, the MSG can either be: 18 | // - an UTF-8 string which starts with a BOM marker 19 | // or 20 | // - a free-form message (0-255) not starting with a BOM marker. 21 | // 22 | // Ref.: https://tools.ietf.org/html/rfc5424#section-6.4 23 | // Ref.: https://tools.ietf.org/html/rfc5424#section-6 24 | func WithCompliantMsg() syslog.MachineOption { 25 | return func(m syslog.Machine) syslog.Machine { 26 | m.(*machine).compliantMsg = true 27 | return m 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /rfc5424/parser.go: -------------------------------------------------------------------------------- 1 | package rfc5424 2 | 3 | import ( 4 | "sync" 5 | 6 | syslog "github.com/influxdata/go-syslog/v3" 7 | ) 8 | 9 | // parser represent a RFC5424 parser with mutex capabilities. 10 | type parser struct { 11 | sync.Mutex 12 | *machine 13 | } 14 | 15 | // NewParser creates a syslog.Machine that parses RFC5424 syslog messages. 16 | func NewParser(options ...syslog.MachineOption) syslog.Machine { 17 | p := &parser{ 18 | machine: NewMachine(options...).(*machine), 19 | } 20 | 21 | return p 22 | } 23 | 24 | // HasBestEffort tells whether the receiving parser has best effort mode on or off. 25 | func (p *parser) HasBestEffort() bool { 26 | return p.bestEffort 27 | } 28 | 29 | // Parse parses the input RFC5424 syslog message using its FSM. 30 | // 31 | // Best effort mode enables the partial parsing. 32 | func (p *parser) Parse(input []byte) (syslog.Message, error) { 33 | p.Lock() 34 | defer p.Unlock() 35 | 36 | msg, err := p.machine.Parse(input) 37 | if err != nil { 38 | if p.bestEffort { 39 | return msg, err 40 | } 41 | return nil, err 42 | } 43 | 44 | return msg, nil 45 | } 46 | -------------------------------------------------------------------------------- /rfc5424/parser_test.go: -------------------------------------------------------------------------------- 1 | package rfc5424 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/influxdata/go-syslog/v3" 7 | syslogtesting "github.com/influxdata/go-syslog/v3/testing" 8 | "github.com/stretchr/testify/assert" 9 | ) 10 | 11 | func TestParserBestEffortOption(t *testing.T) { 12 | p1 := NewParser().(syslog.BestEfforter) 13 | assert.False(t, p1.HasBestEffort()) 14 | 15 | p2 := NewParser(WithBestEffort()).(syslog.BestEfforter) 16 | assert.True(t, p2.HasBestEffort()) 17 | } 18 | 19 | func TestParserParse(t *testing.T) { 20 | p := NewParser() 21 | pBest := NewParser(WithBestEffort()) 22 | for _, tc := range testCases { 23 | tc := tc 24 | t.Run(syslogtesting.RightPad(string(tc.input), 50), func(t *testing.T) { 25 | t.Parallel() 26 | 27 | message, merr := p.Parse(tc.input) 28 | partial, perr := pBest.Parse(tc.input) 29 | 30 | if !tc.valid { 31 | assert.Nil(t, message) 32 | assert.Error(t, merr) 33 | assert.EqualError(t, merr, tc.errorString) 34 | 35 | assert.Equal(t, tc.partialValue, partial) 36 | assert.EqualError(t, perr, tc.errorString) 37 | } 38 | if tc.valid { 39 | assert.Nil(t, merr) 40 | assert.NotEmpty(t, message) 41 | assert.Equal(t, message, partial) 42 | assert.Equal(t, merr, perr) 43 | } 44 | 45 | assert.Equal(t, tc.value, message) 46 | }) 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /rfc5424/performance_test.go: -------------------------------------------------------------------------------- 1 | package rfc5424 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/influxdata/go-syslog/v3" 7 | syslogtesting "github.com/influxdata/go-syslog/v3/testing" 8 | ) 9 | 10 | // This is here to avoid compiler optimizations that 11 | // could remove the actual call we are benchmarking 12 | // during benchmarks 13 | var benchParseResult syslog.Message 14 | 15 | type benchCase struct { 16 | input []byte 17 | label string 18 | } 19 | 20 | var benchCases = []benchCase{ 21 | { 22 | label: "[no] empty input", 23 | input: []byte(``), 24 | }, 25 | { 26 | label: "[no] multiple syslog messages on multiple lines", 27 | input: []byte("<1>1 - - - - - -\x0A<2>1 - - - - - -"), 28 | }, 29 | { 30 | label: "[no] impossible timestamp", 31 | input: []byte(`<101>11 2003-09-31T22:14:15.003Z`), 32 | }, 33 | { 34 | label: "[no] malformed structured data", 35 | input: []byte("<1>1 - - - - - X"), 36 | }, 37 | { 38 | label: "[no] with duplicated structured data id", 39 | input: []byte("<165>3 2003-10-11T22:14:15.003Z example.com evnts - ID27 [id1][id1]"), 40 | }, 41 | { 42 | label: "[ok] minimal", 43 | input: []byte(`<1>1 - - - - - -`), 44 | }, 45 | { 46 | label: "[ok] average message", 47 | input: []byte(`<29>1 2016-02-21T04:32:57+00:00 web1 someservice - - [origin x-service="someservice"][meta sequenceId="14125553"] 127.0.0.1 - - 1456029177 "GET /v1/ok HTTP/1.1" 200 145 "-" "hacheck 0.9.0" 24306 127.0.0.1:40124 575`), 48 | }, 49 | { 50 | label: "[ok] complicated message", 51 | input: []byte(`<78>1 2016-01-15T00:04:01Z host1 CROND 10391 - [meta sequenceId="29" sequenceBlah="foo"][my key="value"] some_message`), 52 | }, 53 | { 54 | label: "[ok] very long message", 55 | input: []byte(`<190>1 2016-02-21T01:19:11+00:00 batch6sj - - - [meta sequenceId="21881798" x-group="37051387"][origin x-service="tracking"] metascutellar conversationalist nephralgic exogenetic graphy streng outtaken acouasm amateurism prenotice Lyonese bedull antigrammatical diosphenol gastriloquial bayoneteer sweetener naggy roughhouser dighter addend sulphacid uneffectless ferroprussiate reveal Mazdaist plaudite Australasian distributival wiseman rumness Seidel topazine shahdom sinsion mesmerically pinguedinous ophthalmotonometer scuppler wound eciliate expectedly carriwitchet dictatorialism bindweb pyelitic idic atule kokoon poultryproof rusticial seedlip nitrosate splenadenoma holobenthic uneternal Phocaean epigenic doubtlessly indirection torticollar robomb adoptedly outspeak wappenschawing talalgia Goop domitic savola unstrafed carded unmagnified mythologically orchester obliteration imperialine undisobeyed galvanoplastical cycloplegia quinquennia foremean umbonal marcgraviaceous happenstance theoretical necropoles wayworn Igbira pseudoangelic raising unfrounced lamasary centaurial Japanolatry microlepidoptera`), 56 | }, 57 | { 58 | label: "[ok] all max length and complete", 59 | input: []byte(`<191>999 2018-12-31T23:59:59.999999-23:59 abcdefghilmnopqrstuvzabcdefghilmnopqrstuvzabcdefghilmnopqrstuvzabcdefghilmnopqrstuvzabcdefghilmnopqrstuvzabcdefghilmnopqrstuvzabcdefghilmnopqrstuvzabcdefghilmnopqrstuvzabcdefghilmnopqrstuvzabcdefghilmnopqrstuvzabcdefghilmnopqrstuvzabcdefghilmnopqrstuvzabc abcdefghilmnopqrstuvzabcdefghilmnopqrstuvzabcdef abcdefghilmnopqrstuvzabcdefghilmnopqrstuvzabcdefghilmnopqrstuvzabcdefghilmnopqrstuvzabcdefghilmnopqrstuvzabcdefghilmnopqrstuvzab abcdefghilmnopqrstuvzabcdefghilm [an@id key1="val1" key2="val2"][another@id key1="val1"] Some message "GET"`), 60 | }, 61 | { 62 | label: "[ok] all max length except structured data and message", 63 | input: []byte(`<191>999 2018-12-31T23:59:59.999999-23:59 abcdefghilmnopqrstuvzabcdefghilmnopqrstuvzabcdefghilmnopqrstuvzabcdefghilmnopqrstuvzabcdefghilmnopqrstuvzabcdefghilmnopqrstuvzabcdefghilmnopqrstuvzabcdefghilmnopqrstuvzabcdefghilmnopqrstuvzabcdefghilmnopqrstuvzabcdefghilmnopqrstuvzabcdefghilmnopqrstuvzabc abcdefghilmnopqrstuvzabcdefghilmnopqrstuvzabcdef abcdefghilmnopqrstuvzabcdefghilmnopqrstuvzabcdefghilmnopqrstuvzabcdefghilmnopqrstuvzabcdefghilmnopqrstuvzabcdefghilmnopqrstuvzab abcdefghilmnopqrstuvzabcdefghilm -`), 64 | }, 65 | { 66 | label: "[ok] minimal with message containing newline", 67 | input: []byte("<1>1 - - - - - - x\x0Ay"), 68 | }, 69 | { 70 | label: "[ok] w/o procid, w/o structured data, with message starting with BOM", 71 | input: []byte("<34>1 2003-10-11T22:14:15.003Z mymachine.example.com su - ID47 - " + BOM + "'su root' failed for lonvick on /dev/pts/8"), 72 | }, 73 | { 74 | label: "[ok] minimal with UTF-8 message", 75 | input: []byte("<0>1 - - - - - - ⠊⠀⠉⠁⠝⠀⠑⠁⠞⠀⠛⠇⠁⠎⠎⠀⠁⠝⠙⠀⠊⠞⠀⠙⠕⠑⠎⠝⠞⠀⠓⠥⠗⠞⠀⠍⠑"), 76 | }, 77 | { 78 | label: "[ok] minimal with UTF-8 message starting with BOM", 79 | input: []byte("<0>1 - - - - - - " + BOM + "⠊⠀⠉⠁⠝⠀⠑⠁⠞⠀⠛⠇⠁⠎⠎⠀⠁⠝⠙⠀⠊⠞⠀⠙⠕⠑⠎⠝⠞⠀⠓⠥⠗⠞⠀⠍⠑"), 80 | }, 81 | { 82 | label: "[ok] with structured data id, w/o structured data params", 83 | input: []byte(`<29>50 2016-01-15T01:00:43Z hn S - - [my@id]`), 84 | }, 85 | { 86 | label: "[ok] with multiple structured data", 87 | input: []byte(`<29>50 2016-01-15T01:00:43Z hn S - - [my@id1 k="v"][my@id2 c="val"]`), 88 | }, 89 | { 90 | label: "[ok] with escaped backslash within structured data param value, with message", 91 | input: []byte(`<29>50 2016-01-15T01:00:43Z hn S - - [meta es="\\valid"] 1452819643`), 92 | }, 93 | { 94 | label: "[ok] with UTF-8 structured data param value, with message", 95 | input: []byte(`<78>1 2016-01-15T00:04:01+00:00 host1 CROND 10391 - [sdid x="⌘"] some_message`), 96 | }, 97 | } 98 | 99 | func BenchmarkParse(b *testing.B) { 100 | for _, tc := range benchCases { 101 | tc := tc 102 | m := NewMachine(WithBestEffort()) 103 | b.Run(syslogtesting.RightPad(tc.label, 50), func(b *testing.B) { 104 | for i := 0; i < b.N; i++ { 105 | benchParseResult, _ = m.Parse(tc.input) 106 | } 107 | }) 108 | } 109 | } 110 | 111 | func BenchmarkParseCompliantMessage(b *testing.B) { 112 | for _, tc := range benchCases { 113 | tc := tc 114 | m := NewMachine(WithBestEffort(), WithCompliantMsg()) 115 | b.Run(syslogtesting.RightPad(tc.label, 50), func(b *testing.B) { 116 | for i := 0; i < b.N; i++ { 117 | benchParseResult, _ = m.Parse(tc.input) 118 | } 119 | }) 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /rfc5424/syslog_message.go: -------------------------------------------------------------------------------- 1 | package rfc5424 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/influxdata/go-syslog/v3" 7 | "github.com/influxdata/go-syslog/v3/common" 8 | ) 9 | 10 | type syslogMessage struct { 11 | prioritySet bool // We explictly flag the setting of priority since its zero value is a valid priority by RFC 5424 12 | timestampSet bool // We explictly flag the setting of timestamp since its zero value is a valid timestamp by RFC 5424 13 | hasElements bool 14 | priority uint8 15 | version uint16 16 | timestamp time.Time 17 | hostname string 18 | appname string 19 | procID string 20 | msgID string 21 | structuredData map[string]map[string]string 22 | message string 23 | } 24 | 25 | func (sm *syslogMessage) minimal() bool { 26 | return sm.prioritySet && common.ValidPriority(sm.priority) && common.ValidVersion(sm.version) 27 | } 28 | 29 | // export is meant to be called on minimally-valid messages 30 | // thus it presumes priority and version values exists and are correct 31 | func (sm *syslogMessage) export() *SyslogMessage { 32 | out := &SyslogMessage{} 33 | out.ComputeFromPriority(sm.priority) 34 | out.Version = sm.version 35 | 36 | if sm.timestampSet { 37 | out.Timestamp = &sm.timestamp 38 | } 39 | if sm.hostname != "-" && sm.hostname != "" { 40 | out.Hostname = &sm.hostname 41 | } 42 | if sm.appname != "-" && sm.appname != "" { 43 | out.Appname = &sm.appname 44 | } 45 | if sm.procID != "-" && sm.procID != "" { 46 | out.ProcID = &sm.procID 47 | } 48 | if sm.msgID != "-" && sm.msgID != "" { 49 | out.MsgID = &sm.msgID 50 | } 51 | if sm.hasElements { 52 | out.StructuredData = &sm.structuredData 53 | } 54 | if sm.message != "" { 55 | out.Message = &sm.message 56 | } 57 | 58 | return out 59 | } 60 | 61 | // Builder represents a RFC5424 syslog message builder. 62 | type Builder interface { 63 | syslog.Message 64 | 65 | SetPriority(value uint8) Builder 66 | SetVersion(value uint16) Builder 67 | SetTimestamp(value string) Builder 68 | SetHostname(value string) Builder 69 | SetAppname(value string) Builder 70 | SetProcID(value string) Builder 71 | SetMsgID(value string) Builder 72 | SetElementID(value string) Builder 73 | SetParameter(id string, name string, value string) Builder 74 | SetMessage(value string) Builder 75 | } 76 | 77 | // SyslogMessage represents a RFC5424 syslog message. 78 | type SyslogMessage struct { 79 | syslog.Base 80 | 81 | Version uint16 // Grammar mandates that version cannot be 0, so we can use the 0 value of uint16 to signal nil 82 | StructuredData *map[string]map[string]string 83 | } 84 | 85 | // Valid tells whether the receiving RFC5424 SyslogMessage is well-formed or not. 86 | // 87 | // A minimally well-formed RFC5424 syslog message contains at least a priority ([1, 191] or 0) and the version (]0, 999]). 88 | func (sm *SyslogMessage) Valid() bool { 89 | // A nil priority or a 0 version means that the message is not valid 90 | return sm.Base.Valid() && common.ValidVersion(sm.Version) 91 | } 92 | -------------------------------------------------------------------------------- /syslog.go: -------------------------------------------------------------------------------- 1 | // Package syslog provides generic interfaces and structs for syslog messages and transport. 2 | // Subpackages contains various parsers or scanners for different syslog formats. 3 | package syslog 4 | 5 | import ( 6 | "io" 7 | "time" 8 | 9 | "github.com/influxdata/go-syslog/v3/common" 10 | ) 11 | 12 | // BestEfforter is an interface that wraps the HasBestEffort method. 13 | type BestEfforter interface { 14 | WithBestEffort() 15 | HasBestEffort() bool 16 | } 17 | 18 | // MaxMessager sets the max message size the parser should be able to parse 19 | type MaxMessager interface { 20 | WithMaxMessageLength(length int) 21 | } 22 | 23 | // Machine represent a FSM able to parse an entire syslog message and return it in an structured way. 24 | type Machine interface { 25 | Parse(input []byte) (Message, error) 26 | BestEfforter 27 | } 28 | 29 | // MachineOption represents the type of option setters for Machine instances. 30 | type MachineOption func(m Machine) Machine 31 | 32 | // Parser is an interface that wraps the Parse method. 33 | type Parser interface { 34 | Parse(r io.Reader) 35 | WithListener(ParserListener) 36 | BestEfforter 37 | MaxMessager 38 | } 39 | 40 | // ParserOption represent the type of option setters for Parser instances. 41 | type ParserOption func(p Parser) Parser 42 | 43 | // ParserListener is a function that receives syslog parsing results, one by one. 44 | type ParserListener func(*Result) 45 | 46 | // Result wraps the outcomes obtained parsing a syslog message. 47 | type Result struct { 48 | Message Message 49 | Error error 50 | } 51 | 52 | // Message represent a minimal syslog message. 53 | type Message interface { 54 | Valid() bool 55 | FacilityMessage() *string 56 | FacilityLevel() *string 57 | SeverityMessage() *string 58 | SeverityLevel() *string 59 | SeverityShortLevel() *string 60 | 61 | ComputeFromPriority(value uint8) 62 | } 63 | 64 | // Base represents a base struct for syslog messages. 65 | // 66 | // It contains the fields in common among different formats. 67 | type Base struct { 68 | Facility *uint8 69 | Severity *uint8 70 | Priority *uint8 71 | Timestamp *time.Time 72 | Hostname *string 73 | Appname *string 74 | ProcID *string 75 | MsgID *string 76 | Message *string 77 | } 78 | 79 | // Valid tells whether the receiving message is well-formed or not. 80 | // 81 | // A minimally well-formed RFC3164 syslog message contains at least the priority ([1, 191] or 0). 82 | // A minimally well-formed RFC5424 syslog message also contains the version. 83 | func (m *Base) Valid() bool { 84 | // A nil priority or a 0 version means that the message is not valid 85 | return m.Priority != nil && common.ValidPriority(*m.Priority) 86 | } 87 | 88 | // ComputeFromPriority set the priority values and computes facility and severity from it. 89 | // 90 | // It does NOT check the input value validity. 91 | func (m *Base) ComputeFromPriority(value uint8) { 92 | m.Priority = &value 93 | facility := uint8(value / 8) 94 | severity := uint8(value % 8) 95 | m.Facility = &facility 96 | m.Severity = &severity 97 | } 98 | 99 | // FacilityMessage returns the text message for the current facility value. 100 | func (m *Base) FacilityMessage() *string { 101 | if m.Facility != nil { 102 | msg := common.Facility[*m.Facility] 103 | return &msg 104 | } 105 | 106 | return nil 107 | } 108 | 109 | // FacilityLevel returns the 110 | func (m *Base) FacilityLevel() *string { 111 | if m.Facility != nil { 112 | if msg, ok := common.FacilityKeywords[*m.Facility]; ok { 113 | return &msg 114 | } 115 | 116 | // Fallback to facility message 117 | msg := common.Facility[*m.Facility] 118 | return &msg 119 | } 120 | 121 | return nil 122 | } 123 | 124 | // SeverityMessage returns the text message for the current severity value. 125 | func (m *Base) SeverityMessage() *string { 126 | if m.Severity != nil { 127 | msg := common.SeverityMessages[*m.Severity] 128 | return &msg 129 | } 130 | 131 | return nil 132 | } 133 | 134 | // SeverityLevel returns the text level for the current severity value. 135 | func (m *Base) SeverityLevel() *string { 136 | if m.Severity != nil { 137 | msg := common.SeverityLevels[*m.Severity] 138 | return &msg 139 | } 140 | 141 | return nil 142 | } 143 | 144 | // SeverityShortLevel returns the short text level for the current severity value. 145 | func (m *Base) SeverityShortLevel() *string { 146 | if m.Severity != nil { 147 | msg := common.SeverityLevelsShort[*m.Severity] 148 | return &msg 149 | } 150 | 151 | return nil 152 | } 153 | -------------------------------------------------------------------------------- /testing/testing.go: -------------------------------------------------------------------------------- 1 | package testing 2 | 3 | import ( 4 | "math/rand" 5 | "strings" 6 | "time" 7 | ) 8 | 9 | func init() { 10 | rand.Seed(time.Now().Unix()) 11 | } 12 | 13 | const ( 14 | letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" 15 | letterIdxBits = 6 // 6 bits to represent a letter index 16 | letterIdxMask = 1<= 0; { 26 | if remain == 0 { 27 | cache, remain = src.Int63(), letterIdxMax 28 | } 29 | if idx := int(cache & letterIdxMask); idx < len(letterBytes) { 30 | b[i] = letterBytes[idx] 31 | i-- 32 | } 33 | cache >>= letterIdxBits 34 | remain-- 35 | } 36 | 37 | return b 38 | } 39 | 40 | const ( 41 | // MaxPriority contains the maximum priority value that a RFC5424 syslog message can have. 42 | MaxPriority = uint8(191) 43 | // MaxVersion contains the maximum version value that a RFC5424 syslog message can have. 44 | MaxVersion = uint16(999) 45 | // MaxRFC3339MicroTimestamp contains the maximum length RFC3339MICRO timestamp that a RFC5424 syslog message can have. 46 | MaxRFC3339MicroTimestamp = "2018-12-31T23:59:59.999999-23:59" 47 | ) 48 | 49 | var ( 50 | // MaxHostname is a maximum length hostname that a RFC5424 syslog message can have. 51 | MaxHostname = RandomBytes(255) 52 | // MaxAppname is a maximum length app-name that a RFC5424 syslog message can have. 53 | MaxAppname = RandomBytes(48) 54 | // MaxProcID is a maximum length app-name that a RFC5424 syslog message can have. 55 | MaxProcID = RandomBytes(128) 56 | // MaxMsgID is a maximum length app-name that a RFC5424 syslog message can have. 57 | MaxMsgID = RandomBytes(32) 58 | // MaxMessage is a maximum length message that a RFC5424 syslog message can contain when all other fields are at their maximum length. 59 | MaxMessage = RandomBytes(7681) 60 | // LongerMaxMessage is a maximum length message that a RFC5424 syslog message can contain when all other fields are at their maximum length. 61 | LongerMaxMessage = RandomBytes(65018) 62 | ) 63 | 64 | // RightPad pads a string with spaces until the given limit, or it cuts the string to the given limit. 65 | func RightPad(str string, limit int) string { 66 | str = str + strings.Repeat(" ", limit) 67 | return str[:limit] 68 | } 69 | 70 | // StringAddress returns the address of the input string. 71 | func StringAddress(str string) *string { 72 | return &str 73 | } 74 | 75 | // Uint8Address returns the address of the input uint8. 76 | func Uint8Address(x uint8) *uint8 { 77 | return &x 78 | } 79 | 80 | // TimeParse parses a time string, for the given layout, into a pointer to a time.Time instance. 81 | func TimeParse(layout, value string) *time.Time { 82 | t, _ := time.Parse(layout, value) 83 | return &t 84 | } 85 | 86 | // YearTime returns a time.Time of the given month, day, hour, minutes, and seconds for the current year (in UTC). 87 | func YearTime(mm, dd, hh, min, ss int) time.Time { 88 | return time.Date(time.Now().Year(), time.Month(mm), dd, hh, min, ss, 0, time.UTC) 89 | } 90 | --------------------------------------------------------------------------------