github.com/observiq/carbon@v0.9.11-0.20200820160507-1b872e368a5e/operator/builtin/parser/syslog.go (about) 1 package parser 2 3 import ( 4 "context" 5 "fmt" 6 "time" 7 8 "github.com/observiq/carbon/entry" 9 "github.com/observiq/carbon/operator" 10 "github.com/observiq/carbon/operator/helper" 11 syslog "github.com/observiq/go-syslog/v3" 12 "github.com/observiq/go-syslog/v3/rfc3164" 13 "github.com/observiq/go-syslog/v3/rfc5424" 14 ) 15 16 func init() { 17 operator.Register("syslog_parser", func() operator.Builder { return NewSyslogParserConfig("") }) 18 } 19 20 func NewSyslogParserConfig(operatorID string) *SyslogParserConfig { 21 return &SyslogParserConfig{ 22 ParserConfig: helper.NewParserConfig(operatorID, "syslog_parser"), 23 } 24 } 25 26 // SyslogParserConfig is the configuration of a syslog parser operator. 27 type SyslogParserConfig struct { 28 helper.ParserConfig `yaml:",inline"` 29 30 Protocol string `json:"protocol,omitempty" yaml:"protocol,omitempty"` 31 } 32 33 // Build will build a JSON parser operator. 34 func (c SyslogParserConfig) Build(context operator.BuildContext) (operator.Operator, error) { 35 if c.ParserConfig.TimeParser == nil { 36 parseFromField := entry.NewRecordField("timestamp") 37 c.ParserConfig.TimeParser = &helper.TimeParser{ 38 ParseFrom: &parseFromField, 39 LayoutType: helper.NativeKey, 40 } 41 } 42 43 parserOperator, err := c.ParserConfig.Build(context) 44 if err != nil { 45 return nil, err 46 } 47 48 if c.Protocol == "" { 49 return nil, fmt.Errorf("missing field 'protocol'") 50 } 51 52 syslogParser := &SyslogParser{ 53 ParserOperator: parserOperator, 54 protocol: c.Protocol, 55 } 56 57 return syslogParser, nil 58 } 59 60 func buildMachine(protocol string) (syslog.Machine, error) { 61 switch protocol { 62 case "rfc3164": 63 return rfc3164.NewMachine(), nil 64 case "rfc5424": 65 return rfc5424.NewMachine(), nil 66 default: 67 return nil, fmt.Errorf("invalid protocol %s", protocol) 68 } 69 } 70 71 // SyslogParser is an operator that parses syslog. 72 type SyslogParser struct { 73 helper.ParserOperator 74 protocol string 75 } 76 77 // Process will parse an entry field as syslog. 78 func (s *SyslogParser) Process(ctx context.Context, entry *entry.Entry) error { 79 return s.ParserOperator.ProcessWith(ctx, entry, s.parse) 80 } 81 82 // parse will parse a value as syslog. 83 func (s *SyslogParser) parse(value interface{}) (interface{}, error) { 84 bytes, err := toBytes(value) 85 if err != nil { 86 return nil, err 87 } 88 89 machine, err := buildMachine(s.protocol) 90 if err != nil { 91 return nil, err 92 } 93 94 syslog, err := machine.Parse(bytes) 95 if err != nil { 96 return nil, err 97 } 98 99 switch message := syslog.(type) { 100 case *rfc3164.SyslogMessage: 101 return s.parseRFC3164(message) 102 case *rfc5424.SyslogMessage: 103 return s.parseRFC5424(message) 104 default: 105 return nil, fmt.Errorf("parsed value was not rfc3164 or rfc5424 compliant") 106 } 107 } 108 109 // parseRFC3164 will parse an RFC3164 syslog message. 110 func (s *SyslogParser) parseRFC3164(syslogMessage *rfc3164.SyslogMessage) (map[string]interface{}, error) { 111 value := map[string]interface{}{ 112 "timestamp": syslogMessage.Timestamp, 113 "priority": syslogMessage.Priority, 114 "facility": syslogMessage.Facility, 115 "severity": syslogMessage.Severity, 116 "hostname": syslogMessage.Hostname, 117 "appname": syslogMessage.Appname, 118 "proc_id": syslogMessage.ProcID, 119 "msg_id": syslogMessage.MsgID, 120 "message": syslogMessage.Message, 121 } 122 return s.toSafeMap(value) 123 } 124 125 // parseRFC5424 will parse an RFC5424 syslog message. 126 func (s *SyslogParser) parseRFC5424(syslogMessage *rfc5424.SyslogMessage) (map[string]interface{}, error) { 127 value := map[string]interface{}{ 128 "timestamp": syslogMessage.Timestamp, 129 "priority": syslogMessage.Priority, 130 "facility": syslogMessage.Facility, 131 "severity": syslogMessage.Severity, 132 "hostname": syslogMessage.Hostname, 133 "appname": syslogMessage.Appname, 134 "proc_id": syslogMessage.ProcID, 135 "msg_id": syslogMessage.MsgID, 136 "message": syslogMessage.Message, 137 "structured_data": syslogMessage.StructuredData, 138 "version": syslogMessage.Version, 139 } 140 return s.toSafeMap(value) 141 } 142 143 // toSafeMap will dereference any pointers on the supplied map. 144 func (s *SyslogParser) toSafeMap(message map[string]interface{}) (map[string]interface{}, error) { 145 for key, val := range message { 146 switch v := val.(type) { 147 case *string: 148 if v == nil { 149 delete(message, key) 150 continue 151 } 152 message[key] = *v 153 case *uint8: 154 if v == nil { 155 delete(message, key) 156 continue 157 } 158 message[key] = int(*v) 159 case uint16: 160 message[key] = int(v) 161 case *time.Time: 162 if v == nil { 163 delete(message, key) 164 continue 165 } 166 message[key] = *v 167 case *map[string]map[string]string: 168 if v == nil { 169 delete(message, key) 170 continue 171 } 172 message[key] = *v 173 default: 174 return nil, fmt.Errorf("key %s has unknown field of type %T", key, v) 175 } 176 } 177 178 return message, nil 179 } 180 181 func toBytes(value interface{}) ([]byte, error) { 182 switch v := value.(type) { 183 case string: 184 return []byte(v), nil 185 case []byte: 186 return v, nil 187 default: 188 return nil, fmt.Errorf("unable to convert type '%T' to bytes", value) 189 } 190 }