Codebase list golang-github-influxdata-go-syslog / debian/latest rfc5424 / builder.go.rl
debian/latest

Tree @debian/latest (Download .tar.gz)

builder.go.rl @debian/latestraw · history · blame

package rfc5424

import (
    "time"
    "sort"
    "fmt"

    "github.com/influxdata/go-syslog/v2/common"
)

%%{
machine builder;

include common "common.rl";

action mark {
    pb = p
}

action set_timestamp {
	if t, e := time.Parse(RFC3339MICRO, string(data[pb:p])); e == nil {
        sm.timestamp = &t
    }
}

action set_hostname {
    if s := string(data[pb:p]); s != "-" {
        sm.hostname = &s
    }
}

action set_appname {
    if s := string(data[pb:p]); s != "-" {
        sm.appname = &s
    }
}

action set_procid {
    if s := string(data[pb:p]); s != "-" {
        sm.procID = &s
    }
}

action set_msgid {
    if s := string(data[pb:p]); s != "-" {
        sm.msgID = &s
    }
}

action set_sdid {
    if sm.structuredData == nil {
        sm.structuredData = &(map[string]map[string]string{})
    }

    id := string(data[pb:p])
    elements := *sm.structuredData
    if _, ok := elements[id]; !ok {
        elements[id] = map[string]string{}
    }
}

action set_sdpn {
    // Assuming SD map already exists, contains currentid key (set from outside)
    elements := *sm.structuredData
    elements[currentid][string(data[pb:p])] = ""
}

action markslash {
    backslashes = append(backslashes, p)
}

action set_sdpv {
    // Store text
	text := data[pb:p]
	// Strip backslashes only when there are ...
    if len(backslashes) > 0 {
        text = common.RemoveBytes(text, backslashes, pb)
    }
	// Assuming SD map already exists, contains currentid key and currentparamname key (set from outside)
    elements := *sm.structuredData
    elements[currentid][currentparamname] = string(text)
}

action set_msg {
    if s := string(data[pb:p]); s != "" {
        sm.message = &s
    }
}

timestamp := (fulldate 'T' fulltime) >mark %set_timestamp;

hostname := hostnamerange >mark %set_hostname;

appname := appnamerange >mark %set_appname;

procid := procidrange >mark %set_procid;

msgid := msgidrange >mark %set_msgid;

sdid := sdname >mark %set_sdid;

sdpn := sdname >mark %set_sdpn;

escapes = (bs >markslash toescape);

sdpv := (utf8charwodelims* escapes*)+ >mark %set_sdpv;

msg := (bom? utf8octets) >mark %set_msg;

write data noerror nofinal;
}%%

type entrypoint int

const (
	timestamp entrypoint = iota
	hostname
	appname
	procid
	msgid
    sdid
    sdpn
    sdpv
    msg
)

func (e entrypoint) translate() int {
    switch e {
    case timestamp:
        return builder_en_timestamp
    case hostname:
        return builder_en_hostname
    case appname:
        return builder_en_appname
    case procid:
        return builder_en_procid
    case msgid:
        return builder_en_msgid
    case sdid:
        return builder_en_sdid
    case sdpn:
        return builder_en_sdpn
    case sdpv:
        return builder_en_sdpv
    case msg:
        return builder_en_msg
    default:
        return builder_start
    }
}

var currentid string
var currentparamname string

func (sm *SyslogMessage) set(from entrypoint, value string) *SyslogMessage {
    data := []byte(value)
	p := 0
	pb := 0
	pe := len(data)
	eof := len(data)
    cs := from.translate()
    backslashes := []int{}
    %% write exec;

    return sm
}

// SetPriority set the priority value and the computed facility and severity codes accordingly.
//
// It ignores incorrect priority values (range [0, 191]).
func (sm *SyslogMessage) SetPriority(value uint8) *SyslogMessage {
	if value >= 0 && value <= 191 {
		sm.setPriority(value)
	}

	return sm
}

// SetVersion set the version value.
//
// It ignores incorrect version values (range ]0, 999]).
func (sm *SyslogMessage) SetVersion(value uint16) *SyslogMessage {
	if value > 0 && value <= 999 {
		sm.version = value
	}

	return sm
}

// SetTimestamp set the timestamp value.
func (sm *SyslogMessage) SetTimestamp(value string) *SyslogMessage {
    return sm.set(timestamp, value)
}

// SetHostname set the hostname value.
func (sm *SyslogMessage) SetHostname(value string) *SyslogMessage {
    return sm.set(hostname, value)
}

// SetAppname set the appname value.
func (sm *SyslogMessage) SetAppname(value string) *SyslogMessage {
    return sm.set(appname, value)
}

// SetProcID set the procid value.
func (sm *SyslogMessage) SetProcID(value string) *SyslogMessage {
    return sm.set(procid, value)
}

// SetMsgID set the msgid value.
func (sm *SyslogMessage) SetMsgID(value string) *SyslogMessage {
    return sm.set(msgid, value)
}

// SetElementID set a structured data id.
//
// When the provided id already exists the operation is discarded.
func (sm *SyslogMessage) SetElementID(value string) *SyslogMessage {
    return sm.set(sdid, value)
}

// SetParameter set a structured data parameter belonging to the given element.
//
// If the element does not exist it creates one with the given element id.
// When a parameter with the given name already exists for the given element the operation is discarded.
func (sm *SyslogMessage) SetParameter(id string, name string, value string) *SyslogMessage {
    // Create an element with the given id (or re-use the existing one)
    sm.set(sdid, id)

    // We can create parameter iff the given element id exists
    if sm.structuredData != nil {
        elements := *sm.structuredData
        if _, ok := elements[id]; ok {
            currentid = id
            sm.set(sdpn, name)
            // We can assign parameter value iff the given parameter key exists
            if _, ok := elements[id][name]; ok {
                currentparamname = name
                sm.set(sdpv, value)
            }
        }
    }

    return sm
}

// SetMessage set the message value.
func (sm *SyslogMessage) SetMessage(value string) *SyslogMessage {
	return sm.set(msg, value)
}

func (sm *SyslogMessage) String() (string, error) {
    if !sm.Valid() {
        return "", fmt.Errorf("invalid syslog")
    }

    template := "<%d>%d %s %s %s %s %s %s%s"

    t := "-"
    hn := "-"
    an := "-"
    pid := "-"
    mid := "-"
    sd := "-"
    m := ""
    if sm.timestamp != nil {
        t = sm.timestamp.Format("2006-01-02T15:04:05.999999Z07:00") // verify 07:00
    }
    if sm.hostname != nil {
        hn = *sm.hostname
    }
    if sm.appname != nil {
        an = *sm.appname
    }
    if sm.procID != nil {
        pid = *sm.procID
    }
    if sm.msgID != nil {
        mid = *sm.msgID
    }
    if sm.structuredData != nil {
        // Sort element identifiers
        identifiers := make([]string, 0)
        for k, _ := range *sm.structuredData {
            identifiers = append(identifiers, k)
        }
        sort.Strings(identifiers)

        sd = ""
        for _, id := range identifiers {
            sd += fmt.Sprintf("[%s", id)

            // Sort parameter names
            params := (*sm.structuredData)[id]
            names := make([]string, 0)
            for n, _ := range params {
                names = append(names, n)
            }
            sort.Strings(names)

            for _, name := range names {
                sd += fmt.Sprintf(" %s=\"%s\"", name, common.EscapeBytes(params[name]))
            }
            sd += "]"
        }
    }
    if sm.message != nil {
        m = " " + *sm.message
    }

    return fmt.Sprintf(template, *sm.priority, sm.version, t, hn, an, pid, mid, sd, m), nil
}