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  }