Skip to content

Commit

Permalink
Fix #5 - support unprintable messages in MESSAGE from journal, add tests
Browse files Browse the repository at this point in the history
  • Loading branch information
SjonHortensius committed Mar 12, 2019
1 parent 8b37b7b commit 32cd91d
Show file tree
Hide file tree
Showing 3 changed files with 164 additions and 1 deletion.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
SystemdJournal2Gelf
.idea/
28 changes: 27 additions & 1 deletion SystemdJournal2Gelf.go
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,32 @@ func (this *SystemdJournalEntry) process() {
this.Message = re.ReplaceAllString(this.Message, "")
}

// Custom wrapper to support unprintable chars in message
func (this *SystemdJournalEntry) UnmarshalJSON(data []byte) error {
// use an alias to prevent recursion
type entryAlias SystemdJournalEntry
aux := (*entryAlias)(this)

if err := json.Unmarshal(data, &aux); err == nil {
return nil
} else if ute, ok := err.(*json.UnmarshalTypeError); ok && ute.Field == "MESSAGE" && ute.Value == "array" {
// Include brackets, which is why we subtract and add by one
len := int64(strings.Index(string(data[ute.Offset:]), `]`)) + 1

var message []byte
if err := json.Unmarshal(data[ute.Offset-1:ute.Offset+len], &message); err != nil {
return err
}

// only the failing field is skipped, so we can still use the rest
this.Message = string(message)

return nil
} else {
return err
}
}

func (this *SystemdJournalEntry) send() {
message := this.toGelf()

Expand All @@ -155,7 +181,7 @@ func (this *SystemdJournalEntry) send() {
}

func (this *SystemdJournalEntry) isJsonMessage() bool {
return len(this.Message) > 64 && this.Message[0:2] == `{"`
return this.Message[0:2] == `{"`
}

type pendingEntry struct {
Expand Down
136 changes: 136 additions & 0 deletions SystemdJournal2Gelf_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
package main

import (
"encoding/json"
"testing"
)

func TestUnmarshalEntry(t *testing.T) {
entry := SystemdJournalEntry{}

err := json.Unmarshal([]byte(`{
"MESSAGE" : "Linux version 4.20.6-arch1-1-ARCH (builduser@heftig-32156) (gcc version 8.2.1 20181127 (GCC)) #1 SMP PREEMPT Thu Jan 31 08:22:01 UTC 2019",
"PRIORITY" : "5",
"__REALTIME_TIMESTAMP" : "1549067421724300",
"_TRANSPORT" : "kernel",
"SYSLOG_FACILITY" : "0",
"SYSLOG_IDENTIFIER" : "kernel",
"_HOSTNAME" : "machine.nl",
"_BOOT_ID" : "61c0e40c739f4f009c785cef13b46e17",
"_UID" : "99",
"_PID" : "1234"
}`), &entry)

AssertNotError(t, err)

gelf := entry.toGelf()
AssertEquals(t, "machine.nl", gelf.Host)
AssertEquals(t, "Linux version 4.20.6-arch1-1-ARCH (builduser@heftig-32156) (gcc version 8.2.1 20181127 (GCC)) #1 SMP PREEMPT Thu Jan 31 08:22:01 UTC 2019", gelf.Short)
AssertEquals(t, "", gelf.Full)
AssertEquals(t, float64(1549067421.7243001), gelf.TimeUnix)
AssertEquals(t, int32(5), gelf.Level)
AssertEquals(t, "kernel", gelf.Facility)

AssertEquals(t, 3, len(gelf.Extra))
AssertEquals(t, "61c0e40c739f4f009c785cef13b46e17", gelf.Extra["Boot_id"])
AssertEquals(t, "99", gelf.Extra["Uid"])
AssertEquals(t, "1234", gelf.Extra["Pid"])

}

func TestJsonMessageOverridesNormalProperties(t *testing.T) {
entry := SystemdJournalEntry{}

err := json.Unmarshal([]byte(`{
"_HOSTNAME" : "machine.nl",
"MESSAGE" : "{\"Message\":\"actually something else\",\"FullMessage\":\"additional data\"}",
"SYSLOG_IDENTIFIER" : "kernel"
}`), &entry)

AssertNotError(t, err)

gelf := entry.toGelf()

AssertEquals(t, "machine.nl", gelf.Host)
AssertEquals(t, "actually something else", gelf.Short)
AssertEquals(t, "additional data", gelf.Full)
AssertEquals(t, "kernel", gelf.Facility)
AssertEquals(t, 3, len(gelf.Extra))
}

func TestJsonMessageIncludeDataInExtra(t *testing.T) {
entry := SystemdJournalEntry{}

err := json.Unmarshal([]byte(`{
"_HOSTNAME" : "machine.nl",
"MESSAGE" : "{\"Message\":\"actually something else\",\"stuff\":\"things and stuff and more like that\"}",
"SYSLOG_IDENTIFIER" : "kernel"
}`), &entry)

AssertNotError(t, err)

gelf := entry.toGelf()

AssertEquals(t, "machine.nl", gelf.Host)
AssertEquals(t, "actually something else", gelf.Short)
AssertEquals(t, "kernel", gelf.Facility)
AssertEquals(t, 4, len(gelf.Extra))
AssertEquals(t, "things and stuff and more like that", gelf.Extra["stuff"])
}

func TestUnmarshalUnprintableEntry(t *testing.T) {
entry := SystemdJournalEntry{}

err := json.Unmarshal([]byte(`{
"_HOSTNAME" : "machine.nl",
"MESSAGE" : [ 116, 104, 105, 115, 32, 105, 115, 32, 97, 32, 98, 105, 110, 97, 114, 121, 32, 118, 97, 108, 117, 101, 32, 7 ],
"SYSLOG_IDENTIFIER" : "kernel"
}`), &entry)

AssertNotError(t, err)

gelf := entry.toGelf()
AssertEquals(t, "this is a binary value \a", gelf.Short)

AssertEquals(t, "kernel", gelf.Facility)
AssertEquals(t, "machine.nl", gelf.Host)

}

func AssertEquals(t *testing.T, expected, actual interface{}) {
if expected != actual {
t.Errorf("AssertEquals: %[1]T(%#[1]v) does not match %[2]T(%#[2]v))", actual, expected)
}
}

func AssertNotEquals(t *testing.T, expected, actual interface{}) {
if expected == actual {
t.Errorf("AssertNotEquals: %[1]T(%#[1]v) unexpectedly matches %[2]T(%#[2]v)", actual, expected)
}
}

func AssertError(t *testing.T, err interface{}) {
_, ok := err.(error)

if !ok {
t.Errorf("AssertError: %[1]T(%#[1]v) is not an error", err)
}
}

func AssertSpecificError(t *testing.T, err interface{}, specific error) {
_, ok := err.(error)

if !ok {
t.Errorf("AssertError: %[1]T(%#[1]v) is not an error", err)
} else if specific != nil && err != specific {
t.Errorf("AssertError: %[1]T(%#[1]v) is not an %[1]T(%#[1]v)", err, specific)
}
}

func AssertNotError(t *testing.T, err interface{}) {
_, ok := err.(error)

if ok {
t.Errorf("AssertNotError: %#[1]v is unexpectedly an error", err)
}
}

0 comments on commit 32cd91d

Please sign in to comment.