diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 12:36:04 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 12:36:04 +0000 |
commit | b09c6d56832eb1718c07d74abf3bc6ae3fe4e030 (patch) | |
tree | d2caec2610d4ea887803ec9e9c3cd77136c448ba /dependencies/pkg/mod/github.com/ssgreg | |
parent | Initial commit. (diff) | |
download | icingadb-b09c6d56832eb1718c07d74abf3bc6ae3fe4e030.tar.xz icingadb-b09c6d56832eb1718c07d74abf3bc6ae3fe4e030.zip |
Adding upstream version 1.1.0.upstream/1.1.0upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'dependencies/pkg/mod/github.com/ssgreg')
13 files changed, 830 insertions, 0 deletions
diff --git a/dependencies/pkg/mod/github.com/ssgreg/journald@v1.0.0/.gitignore b/dependencies/pkg/mod/github.com/ssgreg/journald@v1.0.0/.gitignore new file mode 100644 index 0000000..a1338d6 --- /dev/null +++ b/dependencies/pkg/mod/github.com/ssgreg/journald@v1.0.0/.gitignore @@ -0,0 +1,14 @@ +# Binaries for programs and plugins +*.exe +*.dll +*.so +*.dylib + +# Test binary, build with `go test -c` +*.test + +# Output of the go coverage tool, specifically when used with LiteIDE +*.out + +# Project-local glide cache, RE: https://github.com/Masterminds/glide/issues/736 +.glide/ diff --git a/dependencies/pkg/mod/github.com/ssgreg/journald@v1.0.0/.travis.yml b/dependencies/pkg/mod/github.com/ssgreg/journald@v1.0.0/.travis.yml new file mode 100644 index 0000000..79114b8 --- /dev/null +++ b/dependencies/pkg/mod/github.com/ssgreg/journald@v1.0.0/.travis.yml @@ -0,0 +1,25 @@ +language: shell + +sudo: required + +services: + - docker + +env: + global: + - GOPATH=/go + - BUILD_DIR=/go/src/github.com/ssgreg/journald-send + matrix: + - DOCKER_BASE=ssgreg/centos-go + +before_install: + - docker pull ${DOCKER_BASE} + +install: + - docker run -d --privileged -e GOPATH=${GOPATH} -v ${PWD}:${BUILD_DIR} --cidfile=/tmp/cidfile ${DOCKER_BASE} /bin/bash -c "dbus-uuidgen > /etc/machine-id && /usr/lib/systemd/systemd-journald" + +script: + - docker exec `cat /tmp/cidfile` /bin/bash -c "cd ${BUILD_DIR} && go get -u github.com/stretchr/testify && go vet && go test -cpu=2 -race -v && go test -cpu=2 -covermode=atomic -v" + +after_script: + - docker kill `cat /tmp/cidfile` diff --git a/dependencies/pkg/mod/github.com/ssgreg/journald@v1.0.0/CHANGELOG.md b/dependencies/pkg/mod/github.com/ssgreg/journald@v1.0.0/CHANGELOG.md new file mode 100644 index 0000000..5e47132 --- /dev/null +++ b/dependencies/pkg/mod/github.com/ssgreg/journald@v1.0.0/CHANGELOG.md @@ -0,0 +1,8 @@ +# 0.2.0 + +* feature: now client can specify his own field name transformation function in order to uppercase it or something like that +* feature: it is allowed now to pass any type of values to journald.Send + +# 0.1.0 + +* initial version, all tests passed
\ No newline at end of file diff --git a/dependencies/pkg/mod/github.com/ssgreg/journald@v1.0.0/LICENSE b/dependencies/pkg/mod/github.com/ssgreg/journald@v1.0.0/LICENSE new file mode 100644 index 0000000..06827ae --- /dev/null +++ b/dependencies/pkg/mod/github.com/ssgreg/journald@v1.0.0/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2017 Grigory Zubankov + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/dependencies/pkg/mod/github.com/ssgreg/journald@v1.0.0/README.md b/dependencies/pkg/mod/github.com/ssgreg/journald@v1.0.0/README.md new file mode 100644 index 0000000..e3f84db --- /dev/null +++ b/dependencies/pkg/mod/github.com/ssgreg/journald@v1.0.0/README.md @@ -0,0 +1,141 @@ +# journald +[![GoDoc](https://godoc.org/github.com/ssgreg/journald?status.svg)](https://godoc.org/github.com/ssgreg/journald) +[![Build Status](https://travis-ci.org/ssgreg/journald.svg?branch=master)](https://travis-ci.org/ssgreg/journald) +[![Go Report Status](https://goreportcard.com/badge/github.com/ssgreg/journald)](https://goreportcard.com/report/github.com/ssgreg/journald) +[![GoCover](https://gocover.io/_badge/github.com/ssgreg/journald)](https://gocover.io/github.com/ssgreg/journald) + +Package `journald` offers Go implementation of systemd Journal's native API for logging. Key features are: + +* based on a connection-less socket +* work with messages of any size and type +* client can use any number of separate sockets + +## Installation + +Install the package with: + +```shell +go get github.com/ssgreg/journald +``` + +## Usage: The Best Way + +The Best Way to use structured logs (systemd Journal, etc.) is [logf](https://github.com/ssgreg/logf) - the fast, asynchronous, structured logger in Go with zero allocation count and it's journald driver [logfjournald](https://github.com/ssgreg/logfjournald). This driver uses `journald` package. +The following example creates the new `logf` logger with `logfjournald` appender. + +```go +package main + +import ( + "runtime" + + "github.com/ssgreg/logf" + "github.com/ssgreg/logfjournald" +) + +func main() { + // Create journald Appender with default journald Encoder. + appender, appenderClose := logfjournald.NewAppender(logfjournald.NewEncoder.Default()) + defer appenderClose() + + // Create ChannelWriter with journald Encoder. + writer, writerClose := logf.NewChannelWriter(logf.ChannelWriterConfig{ + Appender: appender, + }) + defer writerClose() + + // Create Logger with ChannelWriter. + logger := logf.NewLogger(logf.LevelInfo, writer) + + logger.Info("got cpu info", logf.Int("count", runtime.NumCPU())) +} +``` + +The JSON representation of the journal entry this generates: + +```json +{ + "TS": "2018-11-01T07:25:18Z", + "PRIORITY": "6", + "LEVEL": "info", + "MESSAGE": "got cpu info", + "COUNT": "4", +} +``` + +## Usage: AS-IS + +Let's look at what the `journald` provides as Go APIs for logging: + +```go +package main + +import ( + "github.com/ssgreg/journald" +) + +func main() { + journald.Print(journald.PriorityInfo, "Hello World!") +} +``` + +The JSON representation of the journal entry this generates: + +```json +{ + "PRIORITY": "6", + "MESSAGE": "Hello World!", + "_PID": "3965", + "_COMM": "simple", + "...": "..." +} +``` + +The primary reason for using the Journal's native logging APIs is not just plain logs: it is to allow passing additional structured log messages from the program into the journal. This additional log data may the be used to search the journal for, is available for consumption for other programs, and might help the administrator to track down issues beyond what is expressed in the human readable message text. Here's an example how to do that with `journals.Send`: + +```go +package main + +import ( + "os" + "runtime" + + "github.com/ssgreg/journald" +) + +func main() { + journald.Send("Hello World!", journald.PriorityInfo, map[string]interface{}{ + "HOME": os.Getenv("HOME"), + "TERM": os.Getenv("TERM"), + "N_GOROUTINE": runtime.NumGoroutine(), + "N_CPUS": runtime.NumCPU(), + "TRACE": runtime.ReadTrace(), + }) +} +``` + +This will write a log message to the journal much like the earlier examples. However, this times a few additional, structured fields are attached: + +```json +{ + "PRIORITY": "6", + "MESSAGE": "Hello World!", + "HOME": "/root", + "TERM": "xterm", + "N_GOROUTINE": "2", + "N_CPUS": "4", + "TRACE": [103,111,32,49,46,56,32,116,114,97,99,101,0,0,0,0], + "_PID": "4037", + "_COMM": "send", + "...": "..." +} +``` + +Our structured message includes seven fields. The first two we passed are well-known fields: + +1. `MESSAGE=` is the actual human readable message part of the structured message. +1. `PRIORITY=` is the numeric message priority value as known from BSD syslog formatted as an integer string. + +Applications may relatively freely define additional fields as they see fit (we defined four pretty arbitrary ones in our example). A complete list of the currently well-known fields is available [here](https://www.freedesktop.org/software/systemd/man/systemd.journal-fields.html). + +> Thanks to [http://0pointer.de/blog/](http://0pointer.de/blog/) for the inspiration. diff --git a/dependencies/pkg/mod/github.com/ssgreg/journald@v1.0.0/doc.go b/dependencies/pkg/mod/github.com/ssgreg/journald@v1.0.0/doc.go new file mode 100644 index 0000000..c67836e --- /dev/null +++ b/dependencies/pkg/mod/github.com/ssgreg/journald@v1.0.0/doc.go @@ -0,0 +1,76 @@ +/* +Package journald offers Go implementation of systemd Journal's native API for logging. Key features are: + + - based on connection-less socket + - work with messages of any size and type + - client can use any number of separation sockets + +Let's look at what the journald provides as Go APIs for logging: + + package main + + import ( + "github.com/ssgreg/journald" + ) + + func main() { + journald.Print(journald.PriorityInfo, "Hello World!") + } + +The JSON representation of the journal entry this generates: + + { + "PRIORITY": "6", + "MESSAGE": "Hello World!", + "_PID": "3965", + "_COMM": "simple", + ... + } + +The primary reason for using the Journal's native logging APIs is a not just the source code location however: it is to allow passing additional structured log messages from the program into the journal. This additional log data may the be used to search the journal for, is available for consumption for other programs, and might help the administrator to track down issues beyond what is expressed in the human readable message text. Here's and example how to do that with journals.Send: + + package main + + import ( + "os" + "runtime" + + "github.com/ssgreg/journald" + ) + + func main() { + journald.Send("Hello World!", journald.PriorityInfo, map[string]interface{}{ + "HOME": os.Getenv("HOME"), + "TERM": os.Getenv("TERM"), + "N_GOROUTINE": runtime.NumGoroutine(), + "N_CPUS": runtime.NumCPU(), + "TRACE": runtime.ReadTrace(), + }) + } + +This will write a log message to the journal much like the earlier examples. However, this times a few additional, structured fields are attached: + + { + "PRIORITY": "6", + "MESSAGE": "Hello World!", + "HOME": "/root", + "TERM": "xterm", + "N_GOROUTINE": "2", + "N_CPUS": "4", + "TRACE": [103,111,32,49,46,56,32,116,114,97,99,101,0,0,0,0], + "_PID": "4037", + "_COMM": "send", + ... + } + +Our structured message includes six fields. The first thow we passed are well-known fields: +1. MESSAGE= is the actual human readable message part of the structured message. +2. PRIORITY= is the numeric message priority value as known from BSD syslog formatted as an integer string. + +Applications may relatively freely define additional fields as they see fit (we defined four pretty arbitrary ones in our example). A complete list of the currently well-known fields is available here: https://www.freedesktop.org/software/systemd/man/systemd.journal-fields.html + +For more details visit https://github.com/ssgreg/journald-send + +Thanks to http://0pointer.de/blog/ for the inspiration. +*/ +package journald diff --git a/dependencies/pkg/mod/github.com/ssgreg/journald@v1.0.0/examples/print/main.go b/dependencies/pkg/mod/github.com/ssgreg/journald@v1.0.0/examples/print/main.go new file mode 100644 index 0000000..cddc102 --- /dev/null +++ b/dependencies/pkg/mod/github.com/ssgreg/journald@v1.0.0/examples/print/main.go @@ -0,0 +1,9 @@ +package main + +import ( + "github.com/ssgreg/journald" +) + +func main() { + journald.Print(journald.PriorityInfo, "Hello World!") +} diff --git a/dependencies/pkg/mod/github.com/ssgreg/journald@v1.0.0/examples/send/main.go b/dependencies/pkg/mod/github.com/ssgreg/journald@v1.0.0/examples/send/main.go new file mode 100644 index 0000000..2dd4001 --- /dev/null +++ b/dependencies/pkg/mod/github.com/ssgreg/journald@v1.0.0/examples/send/main.go @@ -0,0 +1,18 @@ +package main + +import ( + "os" + "runtime" + + "github.com/ssgreg/journald" +) + +func main() { + journald.Send("Hello World!", journald.PriorityInfo, map[string]interface{}{ + "HOME": os.Getenv("HOME"), + "TERM": os.Getenv("TERM"), + "N_GOROUTINE": runtime.NumGoroutine(), + "N_CPUS": runtime.NumCPU(), + "TRACE": runtime.ReadTrace(), + }) +} diff --git a/dependencies/pkg/mod/github.com/ssgreg/journald@v1.0.0/send.go b/dependencies/pkg/mod/github.com/ssgreg/journald@v1.0.0/send.go new file mode 100644 index 0000000..19948cf --- /dev/null +++ b/dependencies/pkg/mod/github.com/ssgreg/journald@v1.0.0/send.go @@ -0,0 +1,275 @@ +// Copyright 2017 Grigory Zubankov. All rights reserved. +// Use of this source code is governed by a MIT license +// that can be found in the LICENSE file. +// + +package journald + +import ( + "bytes" + "encoding/binary" + "errors" + "fmt" + "io" + "io/ioutil" + "net" + "os" + "strconv" + "sync" + "syscall" +) + +// Priority is the numeric message priority value as known from BSD syslog +type Priority int + +// Priority values +const ( + PriorityEmerg Priority = iota + PriorityAlert + PriorityCrit + PriorityErr + PriorityWarning + PriorityNotice + PriorityInfo + PriorityDebug +) + +var ( + addr = &net.UnixAddr{Name: "/run/systemd/journal/socket", Net: "unixgram"} + + // DefaultJournal is the default journal and is used by Print and Send. + DefaultJournal = &Journal{} +) + +// IsNotExist checks if the system journal is not exist. +func IsNotExist() bool { + _, err := os.Stat(addr.Name) + return os.IsNotExist(err) +} + +// Journal keeps a connection to the system journal +type Journal struct { + once sync.Once + conn *net.UnixConn + connErr error + + // NormalizeFieldNameFn is a hook that allows client to change + // fields names just before sending if set. + // + // Default value is nil. + // string.ToUpper is a good example of the hook usage. + NormalizeFieldNameFn func(string) string + + // TestModeEnabled allows Journal to do nothing. All messages are + // discarding. + TestModeEnabled bool +} + +// Print may be used to submit simple, plain text log entries to the +// system journal. The first argument is a priority value. This is +// followed by a format string and its parameters. +func (j *Journal) Print(p Priority, format string, a ...interface{}) error { + return j.Send(fmt.Sprintf(format, a...), p, nil) +} + +// WriteMsg writes the given bytes to the systemd journal's socket. +// The caller is in charge of correct data format. +func (j *Journal) WriteMsg(data []byte) error { + if j.TestModeEnabled { + return nil + } + + c, err := j.journalConn() + if err != nil { + return err + } + + _, _, err = c.WriteMsgUnix(data, nil, addr) + if err == nil { + return nil + } + errno := toErrno(err) + if errno != syscall.EMSGSIZE && errno != syscall.ENOBUFS { + return err + } + + // Message doesn't fit... + + // TODO: user memfd when it will be available in go: + // http://man7.org/linux/man-pages/man2/memfd_create.2.html + + f, err := openTempFileUnlinkable() + if err != nil { + return err + } + defer f.Close() + _, err = f.Write(data) + if err != nil { + return err + } + _, err = writeMsgUnix(c, syscall.UnixRights(int(f.Fd())), addr) + + return err + +} + +// Send may be used to submit structured log entries to the system +// journal. It takes a map of fields with names and values. +// +// The field names must be in uppercase and consist only of characters, +// numbers and underscores, and may not begin with an underscore. All +// fields that do not follow this syntax will be ignored. The value can +// be of any size and format. A variable may be assigned more than one +// value per entry. +// +// A number of well known fields are defined, see: +// http://0pointer.de/public/systemd-man/systemd.journal-fields.html +// +func (j *Journal) Send(msg string, p Priority, fields map[string]interface{}) error { + return j.WriteMsg(j.marshal(msg, p, fields)) +} + +// Close closes the underlying connection. +func (j *Journal) Close() error { + if j.connErr != nil { + return j.connErr + } + if j.conn == nil { + return nil + } + + return j.conn.Close() +} + +func (j *Journal) journalConn() (*net.UnixConn, error) { + j.once.Do(func() { + // File from UNIX socket. This is the only way to create a + // connect-less socket. Both Dial and ListenUnixgram do extra + // work it terms of connection. + // + // This UNIX socket is needed only to make a Conn on it's base. + // No reason to switch it to nonblocking mode and set additional + // flags. FileConn will do such things for us for Conn socket. + fd, err := syscall.Socket(syscall.AF_UNIX, syscall.SOCK_DGRAM, 0) + if err != nil { + j.connErr = err + return + } + f := os.NewFile(uintptr(fd), "base UNIX socket") + defer f.Close() + + fc, err := net.FileConn(f) + if err != nil { + j.connErr = err + return + } + uc, ok := fc.(*net.UnixConn) + if !ok { + fc.Close() + j.connErr = errors.New("not a UNIX connection") + return + } + uc.SetWriteBuffer(8 * 1024 * 1024) + j.conn = uc + }) + + return j.conn, j.connErr +} + +// Print may be used to submit simple, plain text log entries to the +// system journal. The first argument is a priority value. This is +// followed by a format string and its parameters. +// +// Print is a wrapper around DefaultJournal.Print. +func Print(p Priority, format string, a ...interface{}) error { + return DefaultJournal.Print(p, format, a...) +} + +// Send may be used to submit structured log entries to the system +// journal. It takes a map of fields with names and values. +// +// The field names must be in uppercase and consist only of characters, +// numbers and underscores, and may not begin with an underscore. All +// fields that do not follow this syntax will be ignored. The value can +// be of any size and format. A variable may be assigned more than one +// value per entry. +// +// A number of well known fields are defined, see: +// http://0pointer.de/public/systemd-man/systemd.journal-fields.html +// +// Send is a wrapper around DefaultJournal.Send. +func Send(msg string, p Priority, fields map[string]interface{}) error { + return DefaultJournal.Send(msg, p, fields) +} + +func toErrno(err error) syscall.Errno { + switch e := err.(type) { + case *net.OpError: + switch e := e.Err.(type) { + case *os.SyscallError: + return e.Err.(syscall.Errno) + } + } + + return 0 +} + +func (j *Journal) marshal(msg string, p Priority, fields map[string]interface{}) []byte { + bb := new(bytes.Buffer) + j.writeField(bb, "PRIORITY", strconv.Itoa(int(p))) + j.writeField(bb, "MESSAGE", msg) + for k, v := range fields { + j.writeField(bb, k, v) + } + + return bb.Bytes() +} + +func (j *Journal) writeField(w io.Writer, name string, value interface{}) { + w.Write([]byte(j.normalizeFieldName(name))) + dv := valueToBytes(value) + if bytes.ContainsRune(dv, '\n') { + // According to the format, if the value includes a newline + // need to write the field name, plus a newline, then the + // size (64bit LE), the field data and a final newline. + + w.Write([]byte{'\n'}) + binary.Write(w, binary.LittleEndian, uint64(len(dv))) + } else { + w.Write([]byte{'='}) + } + w.Write(dv) + w.Write([]byte{'\n'}) +} + +func (j *Journal) normalizeFieldName(s string) string { + if j.NormalizeFieldNameFn != nil { + return j.NormalizeFieldNameFn(s) + } + return s +} + +func valueToBytes(value interface{}) []byte { + switch rv := value.(type) { + case string: + return []byte(rv) + case []byte: + return rv + default: + return []byte(fmt.Sprint(value)) + } +} + +func openTempFileUnlinkable() (*os.File, error) { + f, err := ioutil.TempFile("/dev/shm/", "journald-send-tmp") + if err != nil { + return nil, err + } + // The file will be deleted just after the systemd will close passed fd. + err = syscall.Unlink(f.Name()) + if err != nil { + return nil, err + } + + return f, nil +} diff --git a/dependencies/pkg/mod/github.com/ssgreg/journald@v1.0.0/send_test.go b/dependencies/pkg/mod/github.com/ssgreg/journald@v1.0.0/send_test.go new file mode 100644 index 0000000..5443bbf --- /dev/null +++ b/dependencies/pkg/mod/github.com/ssgreg/journald@v1.0.0/send_test.go @@ -0,0 +1,134 @@ +package journald + +import ( + "fmt" + "math/rand" + "os" + "os/exec" + "runtime" + "strings" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +var ( + uniqueSmallMessageID = "1" + uniqueBigMessageID = "2" + uniquePrintedMessageID = "3" + uniqueLowerMessageID = "4" +) + +func TestMain(m *testing.M) { + uniqueSmallMessageID = time.Now().String() + time.Sleep(time.Millisecond) + uniqueBigMessageID = time.Now().String() + time.Sleep(time.Millisecond) + uniquePrintedMessageID = time.Now().String() + time.Sleep(time.Millisecond) + uniqueLowerMessageID = time.Now().String() + + os.Exit(m.Run()) +} + +func TestIsNotExist(t *testing.T) { + require.False(t, IsNotExist()) +} + +type Test struct { + Field1 string + Field2 int + Field3 map[string]float32 +} + +func TestSendSmallMessage(t *testing.T) { + s := Test{"field1", 2, map[string]float32{"3": 0.4, "5": 6.7}} + trace := runtime.ReadTrace() + err := Send("AnotherMessage", PriorityInfo, map[string]interface{}{"ONE": 1, "TWO": trace, "THREE": s, "FOUR": RandStringRunes(768)}) + require.NoError(t, err) +} + +func TestCheckPrint(t *testing.T) { + err := Print(PriorityNotice, "printed message: %s", uniquePrintedMessageID) + require.NoError(t, err) + + time.Sleep(time.Second * 5) + + out, err := exec.Command("sh", "-c", fmt.Sprintf("journalctl 'MESSAGE=printed message: %s' -o verbose", uniquePrintedMessageID)).Output() + require.NoError(t, err) + require.True(t, strings.Contains(string(out), fmt.Sprintf("MESSAGE=printed message: %s", uniquePrintedMessageID))) + require.True(t, strings.Contains(string(out), "PRIORITY=5")) +} + +func TestCheckSmallMessage(t *testing.T) { + err := Send("SmallMessage", PriorityWarning, map[string]interface{}{"TEST_ID": uniqueSmallMessageID, "WITH_NEWLINES": "b\n\nsd\n"}) + require.NoError(t, err) + + time.Sleep(time.Second * 5) + + out, err := exec.Command("sh", "-c", fmt.Sprintf("journalctl 'TEST_ID=%s' -o verbose", uniqueSmallMessageID)).Output() + require.NoError(t, err) + require.True(t, len(out) > 500) + require.True(t, strings.Contains(string(out), "WITH_NEWLINES")) + require.True(t, strings.Contains(string(out), "MESSAGE=SmallMessage")) + require.True(t, strings.Contains(string(out), "PRIORITY=4")) + + out, err = exec.Command("sh", "-c", fmt.Sprintf("journalctl -o verbose 'TEST_ID=%s' -F WITH_NEWLINES", uniqueSmallMessageID)).Output() + require.NoError(t, err) + require.Equal(t, "b\n\nsd\n"+"\n", string(out)) +} + +func TestCheckBigMessage(t *testing.T) { + err := Send("BigMessage", PriorityErr, map[string]interface{}{"TEST_ID": uniqueBigMessageID, "BIG_MESSAGE": RandStringRunes(1024 * 512)}) + require.NoError(t, err) + + time.Sleep(time.Second * 5) + + out, err := exec.Command("sh", "-c", fmt.Sprintf("journalctl 'TEST_ID=%s' -o verbose", uniqueBigMessageID)).Output() + require.NoError(t, err) + require.True(t, len(out) > 512*1024) + require.True(t, strings.Contains(string(out), "BIG_MESSAGE")) + require.True(t, strings.Contains(string(out), "MESSAGE=BigMessage")) + require.True(t, strings.Contains(string(out), "PRIORITY=3")) +} + +func TestCheckLowerMessage(t *testing.T) { + j := Journal{} + j.NormalizeFieldNameFn = strings.ToUpper + err := j.Send("LowerMessage", PriorityCrit, map[string]interface{}{"test_id": uniqueLowerMessageID}) + require.NoError(t, err) + require.NoError(t, j.Close()) + + time.Sleep(time.Second * 5) + + out, err := exec.Command("sh", "-c", fmt.Sprintf("journalctl 'TEST_ID=%s' -o verbose", uniqueLowerMessageID)).Output() + require.NoError(t, err) + require.True(t, strings.Contains(string(out), "MESSAGE=LowerMessage")) + require.True(t, strings.Contains(string(out), "PRIORITY=2")) +} + +func TestSendClose(t *testing.T) { + j := Journal{} + err := j.Print(PriorityInfo, "SendClose") + require.NoError(t, err) + require.NoError(t, j.Close()) + err = j.Print(PriorityInfo, "SendClose") + assertErr(t, err, " use of closed network connection") +} + +var letterRunes = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ") + +func RandStringRunes(n int) string { + b := make([]rune, n) + for i := range b { + b[i] = letterRunes[rand.Intn(len(letterRunes))] + } + return string(b) +} + +func assertErr(t *testing.T, e error, rx interface{}) { + assert.Error(t, e) + assert.Regexp(t, rx, e.Error()) +} diff --git a/dependencies/pkg/mod/github.com/ssgreg/journald@v1.0.0/sockaddr.go b/dependencies/pkg/mod/github.com/ssgreg/journald@v1.0.0/sockaddr.go new file mode 100644 index 0000000..e0f2b56 --- /dev/null +++ b/dependencies/pkg/mod/github.com/ssgreg/journald@v1.0.0/sockaddr.go @@ -0,0 +1,18 @@ +package journald + +import ( + "net" + "syscall" + "unsafe" +) + +func sockaddr(addr *net.UnixAddr) (unsafe.Pointer, uint8) { + sa := syscall.RawSockaddrUnix{Family: syscall.AF_UNIX} + name := addr.Name + n := len(name) + + for i := 0; i < n; i++ { + sa.Path[i] = int8(name[i]) + } + return unsafe.Pointer(&sa), byte(2 + n + 1) // length is family (uint16), name, NUL. +} diff --git a/dependencies/pkg/mod/github.com/ssgreg/journald@v1.0.0/write_msg.go b/dependencies/pkg/mod/github.com/ssgreg/journald@v1.0.0/write_msg.go new file mode 100644 index 0000000..fe9e15e --- /dev/null +++ b/dependencies/pkg/mod/github.com/ssgreg/journald@v1.0.0/write_msg.go @@ -0,0 +1,50 @@ +// +build !go1.9 + +package journald + +import ( + "net" + "syscall" + "unsafe" + + "golang.org/x/sys/unix" +) + +// Go implementation of sendmsg (UnixConn.WriteMsgUnix) is not suitable +// for systemd's Journal in case of sending file descriptors. It always +// sends a dummy data byte, but journal does not expect to get it: +// +// n = recvmsg(fd, &msghdr, MSG_DONTWAIT|MSG_CMSG_CLOEXEC); +// ... +// if (n > 0 && n_fds == 0) +// server_process_native_message(...); +// else if (n == 0 && n_fds == 1) +// server_process_native_file(...); +// else if (n_fds > 0) +// log_warning("Got too many file descriptors via native socket. Ignoring."); +// +// So we get a warning message (the last line) instead of the new log +// entry. +// +func writeMsgUnix(c *net.UnixConn, oob []byte, addr *net.UnixAddr) (oobn int, err error) { + ptr, salen := sockaddr(addr) + + var msg syscall.Msghdr + msg.Name = (*byte)(ptr) + msg.Namelen = uint32(salen) + msg.Control = (*byte)(unsafe.Pointer(&oob[0])) + msg.SetControllen(len(oob)) + + f, err := c.File() + if err != nil { + return 0, err + } + defer f.Close() + + _, n, errno := syscall.Syscall(unix.SYS_SENDMSG, f.Fd(), uintptr(unsafe.Pointer(&msg)), 0) + if errno != 0 { + return int(n), errno + } + + return int(n), nil +} diff --git a/dependencies/pkg/mod/github.com/ssgreg/journald@v1.0.0/write_msg_go1.9.go b/dependencies/pkg/mod/github.com/ssgreg/journald@v1.0.0/write_msg_go1.9.go new file mode 100644 index 0000000..5e5e368 --- /dev/null +++ b/dependencies/pkg/mod/github.com/ssgreg/journald@v1.0.0/write_msg_go1.9.go @@ -0,0 +1,41 @@ +// +build go1.9 + +package journald + +import ( + "net" + "syscall" + "unsafe" + + "golang.org/x/sys/unix" +) + +// Check function description in write_msg.go +func writeMsgUnix(c *net.UnixConn, oob []byte, addr *net.UnixAddr) (oobn int, err error) { + ptr, salen := sockaddr(addr) + + var msg syscall.Msghdr + msg.Name = (*byte)(ptr) + msg.Namelen = uint32(salen) + msg.Control = (*byte)(unsafe.Pointer(&oob[0])) + msg.SetControllen(len(oob)) + + rawConn, err := c.SyscallConn() + if err != nil { + return 0, err + } + + var n uintptr + var errno syscall.Errno + err = rawConn.Write(func(fd uintptr) bool { + _, n, errno = syscall.Syscall(unix.SYS_SENDMSG, fd, uintptr(unsafe.Pointer(&msg)), 0) + return true + }) + if err == nil { + if errno != 0 { + err = errno + } + } + + return int(n), err +} |