200 lines
4.9 KiB
Go
200 lines
4.9 KiB
Go
package syslog
|
|
|
|
import (
|
|
"errors"
|
|
"math"
|
|
"os"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/influxdata/go-syslog/rfc5424"
|
|
"github.com/influxdata/telegraf"
|
|
)
|
|
|
|
type SyslogMapper struct {
|
|
DefaultSdid string
|
|
DefaultSeverityCode uint8
|
|
DefaultFacilityCode uint8
|
|
DefaultAppname string
|
|
Sdids []string
|
|
Separator string
|
|
reservedKeys map[string]bool
|
|
}
|
|
|
|
// MapMetricToSyslogMessage maps metrics tags/fields to syslog messages
|
|
func (sm *SyslogMapper) MapMetricToSyslogMessage(metric telegraf.Metric) (*rfc5424.SyslogMessage, error) {
|
|
msg := &rfc5424.SyslogMessage{}
|
|
|
|
sm.mapPriority(metric, msg)
|
|
sm.mapStructuredData(metric, msg)
|
|
sm.mapAppname(metric, msg)
|
|
mapHostname(metric, msg)
|
|
mapTimestamp(metric, msg)
|
|
mapMsgID(metric, msg)
|
|
mapVersion(metric, msg)
|
|
mapProcID(metric, msg)
|
|
mapMsg(metric, msg)
|
|
|
|
if !msg.Valid() {
|
|
return nil, errors.New("metric could not produce valid syslog message")
|
|
}
|
|
return msg, nil
|
|
}
|
|
|
|
func (sm *SyslogMapper) mapStructuredData(metric telegraf.Metric, msg *rfc5424.SyslogMessage) {
|
|
for _, tag := range metric.TagList() {
|
|
sm.mapStructuredDataItem(tag.Key, tag.Value, msg)
|
|
}
|
|
for _, field := range metric.FieldList() {
|
|
sm.mapStructuredDataItem(field.Key, formatValue(field.Value), msg)
|
|
}
|
|
}
|
|
|
|
func (sm *SyslogMapper) mapStructuredDataItem(key string, value string, msg *rfc5424.SyslogMessage) {
|
|
if sm.reservedKeys[key] {
|
|
return
|
|
}
|
|
isExplicitSdid := false
|
|
for _, sdid := range sm.Sdids {
|
|
k := strings.TrimLeft(key, sdid+sm.Separator)
|
|
if len(key) > len(k) {
|
|
isExplicitSdid = true
|
|
msg.SetParameter(sdid, k, value)
|
|
break
|
|
}
|
|
}
|
|
if !isExplicitSdid && len(sm.DefaultSdid) > 0 {
|
|
k := strings.TrimPrefix(key, sm.DefaultSdid+sm.Separator)
|
|
msg.SetParameter(sm.DefaultSdid, k, value)
|
|
}
|
|
}
|
|
|
|
func (sm *SyslogMapper) mapAppname(metric telegraf.Metric, msg *rfc5424.SyslogMessage) {
|
|
if value, ok := metric.GetTag("appname"); ok {
|
|
msg.SetAppname(formatValue(value))
|
|
} else {
|
|
//Use default appname
|
|
msg.SetAppname(sm.DefaultAppname)
|
|
}
|
|
}
|
|
|
|
func mapMsgID(metric telegraf.Metric, msg *rfc5424.SyslogMessage) {
|
|
if value, ok := metric.GetField("msgid"); ok {
|
|
msg.SetMsgID(formatValue(value))
|
|
} else {
|
|
// We default to metric name
|
|
msg.SetMsgID(metric.Name())
|
|
}
|
|
}
|
|
|
|
func mapVersion(metric telegraf.Metric, msg *rfc5424.SyslogMessage) {
|
|
if value, ok := metric.GetField("version"); ok {
|
|
switch v := value.(type) {
|
|
case uint64:
|
|
msg.SetVersion(uint16(v))
|
|
return
|
|
}
|
|
}
|
|
msg.SetVersion(1)
|
|
}
|
|
|
|
func mapMsg(metric telegraf.Metric, msg *rfc5424.SyslogMessage) {
|
|
if value, ok := metric.GetField("msg"); ok {
|
|
msg.SetMessage(formatValue(value))
|
|
}
|
|
}
|
|
|
|
func mapProcID(metric telegraf.Metric, msg *rfc5424.SyslogMessage) {
|
|
if value, ok := metric.GetField("procid"); ok {
|
|
msg.SetProcID(formatValue(value))
|
|
}
|
|
}
|
|
|
|
func (sm *SyslogMapper) mapPriority(metric telegraf.Metric, msg *rfc5424.SyslogMessage) {
|
|
severityCode := sm.DefaultSeverityCode
|
|
facilityCode := sm.DefaultFacilityCode
|
|
|
|
if value, ok := getFieldCode(metric, "severity_code"); ok {
|
|
severityCode = *value
|
|
}
|
|
|
|
if value, ok := getFieldCode(metric, "facility_code"); ok {
|
|
facilityCode = *value
|
|
}
|
|
|
|
priority := (8 * facilityCode) + severityCode
|
|
msg.SetPriority(priority)
|
|
}
|
|
|
|
func mapHostname(metric telegraf.Metric, msg *rfc5424.SyslogMessage) {
|
|
// Try with hostname, then with source, then with host tags, then take OS Hostname
|
|
if value, ok := metric.GetTag("hostname"); ok {
|
|
msg.SetHostname(formatValue(value))
|
|
} else if value, ok := metric.GetTag("source"); ok {
|
|
msg.SetHostname(formatValue(value))
|
|
} else if value, ok := metric.GetTag("host"); ok {
|
|
msg.SetHostname(formatValue(value))
|
|
} else if value, err := os.Hostname(); err == nil {
|
|
msg.SetHostname(value)
|
|
}
|
|
}
|
|
|
|
func mapTimestamp(metric telegraf.Metric, msg *rfc5424.SyslogMessage) {
|
|
timestamp := metric.Time()
|
|
if value, ok := metric.GetField("timestamp"); ok {
|
|
switch v := value.(type) {
|
|
case int64:
|
|
timestamp = time.Unix(0, v).UTC()
|
|
}
|
|
}
|
|
msg.SetTimestamp(timestamp.Format(time.RFC3339))
|
|
}
|
|
|
|
func formatValue(value interface{}) string {
|
|
switch v := value.(type) {
|
|
case string:
|
|
return v
|
|
case bool:
|
|
if v {
|
|
return "1"
|
|
}
|
|
return "0"
|
|
case uint64:
|
|
return strconv.FormatUint(v, 10)
|
|
case int64:
|
|
return strconv.FormatInt(v, 10)
|
|
case float64:
|
|
if math.IsNaN(v) {
|
|
return ""
|
|
}
|
|
|
|
if math.IsInf(v, 0) {
|
|
return ""
|
|
}
|
|
return strconv.FormatFloat(v, 'f', -1, 64)
|
|
}
|
|
|
|
return ""
|
|
}
|
|
|
|
func getFieldCode(metric telegraf.Metric, fieldKey string) (*uint8, bool) {
|
|
if value, ok := metric.GetField(fieldKey); ok {
|
|
if v, err := strconv.ParseUint(formatValue(value), 10, 8); err == nil {
|
|
r := uint8(v)
|
|
return &r, true
|
|
}
|
|
}
|
|
return nil, false
|
|
}
|
|
|
|
func newSyslogMapper() *SyslogMapper {
|
|
return &SyslogMapper{
|
|
reservedKeys: map[string]bool{
|
|
"version": true, "severity_code": true, "facility_code": true,
|
|
"procid": true, "msgid": true, "msg": true, "timestamp": true, "sdid": true,
|
|
"hostname": true, "source": true, "host": true, "severity": true,
|
|
"facility": true, "appname": true},
|
|
}
|
|
}
|