mirror of
https://github.com/kubernetes-sigs/prometheus-adapter.git
synced 2026-04-07 02:07:58 +00:00
Add vendor folder to git
This commit is contained in:
parent
66cf5eaafb
commit
183585f56f
6916 changed files with 2629581 additions and 1 deletions
66
vendor/github.com/coreos/go-systemd/sdjournal/functions.go
generated
vendored
Normal file
66
vendor/github.com/coreos/go-systemd/sdjournal/functions.go
generated
vendored
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
// Copyright 2015 RedHat, Inc.
|
||||
// Copyright 2015 CoreOS, Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package sdjournal
|
||||
|
||||
import (
|
||||
"github.com/coreos/pkg/dlopen"
|
||||
"sync"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
var (
|
||||
// lazy initialized
|
||||
libsystemdHandle *dlopen.LibHandle
|
||||
|
||||
libsystemdMutex = &sync.Mutex{}
|
||||
libsystemdFunctions = map[string]unsafe.Pointer{}
|
||||
libsystemdNames = []string{
|
||||
// systemd < 209
|
||||
"libsystemd-journal.so.0",
|
||||
"libsystemd-journal.so",
|
||||
|
||||
// systemd >= 209 merged libsystemd-journal into libsystemd proper
|
||||
"libsystemd.so.0",
|
||||
"libsystemd.so",
|
||||
}
|
||||
)
|
||||
|
||||
func getFunction(name string) (unsafe.Pointer, error) {
|
||||
libsystemdMutex.Lock()
|
||||
defer libsystemdMutex.Unlock()
|
||||
|
||||
if libsystemdHandle == nil {
|
||||
h, err := dlopen.GetHandle(libsystemdNames)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
libsystemdHandle = h
|
||||
}
|
||||
|
||||
f, ok := libsystemdFunctions[name]
|
||||
if !ok {
|
||||
var err error
|
||||
f, err = libsystemdHandle.GetSymbolPointer(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
libsystemdFunctions[name] = f
|
||||
}
|
||||
|
||||
return f, nil
|
||||
}
|
||||
36
vendor/github.com/coreos/go-systemd/sdjournal/functions_test.go
generated
vendored
Normal file
36
vendor/github.com/coreos/go-systemd/sdjournal/functions_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
// Copyright 2015 RedHat, Inc.
|
||||
// Copyright 2015 CoreOS, Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package sdjournal
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestGetFunction(t *testing.T) {
|
||||
f, err := getFunction("sd_journal_open")
|
||||
|
||||
if err != nil {
|
||||
t.Errorf("Error getting an existing function: %s", err)
|
||||
}
|
||||
|
||||
if f == nil {
|
||||
t.Error("Got nil function pointer")
|
||||
}
|
||||
|
||||
_, err = getFunction("non_existent_function")
|
||||
|
||||
if err == nil {
|
||||
t.Error("Expected to get an error, got nil")
|
||||
}
|
||||
}
|
||||
1024
vendor/github.com/coreos/go-systemd/sdjournal/journal.go
generated
vendored
Normal file
1024
vendor/github.com/coreos/go-systemd/sdjournal/journal.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load diff
399
vendor/github.com/coreos/go-systemd/sdjournal/journal_test.go
generated
vendored
Normal file
399
vendor/github.com/coreos/go-systemd/sdjournal/journal_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,399 @@
|
|||
// Copyright 2015 RedHat, Inc.
|
||||
// Copyright 2015 CoreOS, Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package sdjournal
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"math/rand"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/coreos/go-systemd/journal"
|
||||
)
|
||||
|
||||
func TestJournalFollow(t *testing.T) {
|
||||
r, err := NewJournalReader(JournalReaderConfig{
|
||||
Since: time.Duration(-15) * time.Second,
|
||||
Matches: []Match{
|
||||
{
|
||||
Field: SD_JOURNAL_FIELD_SYSTEMD_UNIT,
|
||||
Value: "NetworkManager.service",
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
t.Fatalf("Error opening journal: %s", err)
|
||||
}
|
||||
|
||||
if r == nil {
|
||||
t.Fatal("Got a nil reader")
|
||||
}
|
||||
|
||||
defer r.Close()
|
||||
|
||||
// start writing some test entries
|
||||
done := make(chan struct{}, 1)
|
||||
defer close(done)
|
||||
go func() {
|
||||
for {
|
||||
select {
|
||||
case <-done:
|
||||
return
|
||||
default:
|
||||
if err := journal.Print(journal.PriInfo, "test message %s", time.Now()); err != nil {
|
||||
t.Fatalf("Error writing to journal: %s", err)
|
||||
}
|
||||
|
||||
time.Sleep(time.Second)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
// and follow the reader synchronously
|
||||
timeout := time.Duration(5) * time.Second
|
||||
if err = r.Follow(time.After(timeout), os.Stdout); err != ErrExpired {
|
||||
t.Fatalf("Error during follow: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestJournalGetUsage(t *testing.T) {
|
||||
j, err := NewJournal()
|
||||
|
||||
if err != nil {
|
||||
t.Fatalf("Error opening journal: %s", err)
|
||||
}
|
||||
|
||||
if j == nil {
|
||||
t.Fatal("Got a nil journal")
|
||||
}
|
||||
|
||||
defer j.Close()
|
||||
|
||||
_, err = j.GetUsage()
|
||||
|
||||
if err != nil {
|
||||
t.Fatalf("Error getting journal size: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestJournalCursorGetSeekAndTest(t *testing.T) {
|
||||
j, err := NewJournal()
|
||||
if err != nil {
|
||||
t.Fatalf("Error opening journal: %s", err)
|
||||
}
|
||||
|
||||
if j == nil {
|
||||
t.Fatal("Got a nil journal")
|
||||
}
|
||||
|
||||
defer j.Close()
|
||||
|
||||
waitAndNext := func(j *Journal) error {
|
||||
r := j.Wait(time.Duration(1) * time.Second)
|
||||
if r < 0 {
|
||||
return errors.New("Error waiting to journal")
|
||||
}
|
||||
|
||||
n, err := j.Next()
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error reading to journal: %s", err)
|
||||
}
|
||||
|
||||
if n == 0 {
|
||||
return fmt.Errorf("Error reading to journal: %s", io.EOF)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
err = journal.Print(journal.PriInfo, "test message for cursor %s", time.Now())
|
||||
if err != nil {
|
||||
t.Fatalf("Error writing to journal: %s", err)
|
||||
}
|
||||
|
||||
if err = waitAndNext(j); err != nil {
|
||||
t.Fatalf(err.Error())
|
||||
}
|
||||
|
||||
c, err := j.GetCursor()
|
||||
if err != nil {
|
||||
t.Fatalf("Error getting cursor from journal: %s", err)
|
||||
}
|
||||
|
||||
err = j.SeekCursor(c)
|
||||
if err != nil {
|
||||
t.Fatalf("Error seeking cursor to journal: %s", err)
|
||||
}
|
||||
|
||||
if err = waitAndNext(j); err != nil {
|
||||
t.Fatalf(err.Error())
|
||||
}
|
||||
|
||||
err = j.TestCursor(c)
|
||||
if err != nil {
|
||||
t.Fatalf("Error testing cursor to journal: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewJournalFromDir(t *testing.T) {
|
||||
// test for error handling
|
||||
dir := "/ClearlyNonExistingPath/"
|
||||
j, err := NewJournalFromDir(dir)
|
||||
if err == nil {
|
||||
defer j.Close()
|
||||
t.Fatalf("Error expected when opening dummy path (%s)", dir)
|
||||
}
|
||||
// test for main code path
|
||||
dir, err = ioutil.TempDir("", "go-systemd-test")
|
||||
if err != nil {
|
||||
t.Fatalf("Error creating tempdir: %s", err)
|
||||
}
|
||||
defer os.RemoveAll(dir)
|
||||
j, err = NewJournalFromDir(dir)
|
||||
if err != nil {
|
||||
t.Fatalf("Error opening journal: %s", err)
|
||||
}
|
||||
if j == nil {
|
||||
t.Fatal("Got a nil journal")
|
||||
}
|
||||
j.Close()
|
||||
}
|
||||
|
||||
func setupJournalRoundtrip() (*Journal, map[string]string, error) {
|
||||
j, err := NewJournal()
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("Error opening journal: %s", err)
|
||||
}
|
||||
|
||||
if j == nil {
|
||||
return nil, nil, fmt.Errorf("Got a nil journal")
|
||||
}
|
||||
|
||||
j.FlushMatches()
|
||||
|
||||
matchField := "TESTJOURNALENTRY"
|
||||
matchValue := fmt.Sprintf("%d", time.Now().UnixNano())
|
||||
m := Match{Field: matchField, Value: matchValue}
|
||||
err = j.AddMatch(m.String())
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("Error adding matches to journal: %s", err)
|
||||
}
|
||||
|
||||
msg := fmt.Sprintf("test journal get entry message %s", time.Now())
|
||||
data := map[string]string{matchField: matchValue}
|
||||
err = journal.Send(msg, journal.PriInfo, data)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("Error writing to journal: %s", err)
|
||||
}
|
||||
|
||||
time.Sleep(time.Duration(1) * time.Second)
|
||||
|
||||
n, err := j.Next()
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("Error reading from journal: %s", err)
|
||||
}
|
||||
|
||||
if n == 0 {
|
||||
return nil, nil, fmt.Errorf("Error reading from journal: %s", io.EOF)
|
||||
}
|
||||
|
||||
data["MESSAGE"] = msg
|
||||
|
||||
return j, data, nil
|
||||
}
|
||||
|
||||
func TestJournalGetData(t *testing.T) {
|
||||
j, wantEntry, err := setupJournalRoundtrip()
|
||||
if err != nil {
|
||||
t.Fatal(err.Error())
|
||||
}
|
||||
|
||||
defer j.Close()
|
||||
|
||||
for k, v := range wantEntry {
|
||||
data := fmt.Sprintf("%s=%s", k, v)
|
||||
|
||||
dataStr, err := j.GetData(k)
|
||||
if err != nil {
|
||||
t.Fatalf("GetData() error: %v", err)
|
||||
}
|
||||
|
||||
if dataStr != data {
|
||||
t.Fatalf("Invalid data for \"%s\": got %s, want %s", k, dataStr, data)
|
||||
}
|
||||
|
||||
dataBytes, err := j.GetDataBytes(k)
|
||||
if err != nil {
|
||||
t.Fatalf("GetDataBytes() error: %v", err)
|
||||
}
|
||||
|
||||
if string(dataBytes) != data {
|
||||
t.Fatalf("Invalid data bytes for \"%s\": got %s, want %s", k, string(dataBytes), data)
|
||||
}
|
||||
|
||||
valStr, err := j.GetDataValue(k)
|
||||
if err != nil {
|
||||
t.Fatalf("GetDataValue() error: %v", err)
|
||||
}
|
||||
|
||||
if valStr != v {
|
||||
t.Fatalf("Invalid data value for \"%s\": got %s, want %s", k, valStr, v)
|
||||
}
|
||||
|
||||
valBytes, err := j.GetDataValueBytes(k)
|
||||
if err != nil {
|
||||
t.Fatalf("GetDataValueBytes() error: %v", err)
|
||||
}
|
||||
|
||||
if string(valBytes) != v {
|
||||
t.Fatalf("Invalid data value bytes for \"%s\": got %s, want %s", k, string(valBytes), v)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestJournalGetEntry(t *testing.T) {
|
||||
j, wantEntry, err := setupJournalRoundtrip()
|
||||
if err != nil {
|
||||
t.Fatal(err.Error())
|
||||
}
|
||||
|
||||
defer j.Close()
|
||||
|
||||
entry, err := j.GetEntry()
|
||||
if err != nil {
|
||||
t.Fatalf("Error getting the entry to journal: %s", err)
|
||||
}
|
||||
|
||||
for k, wantV := range wantEntry {
|
||||
gotV := entry.Fields[k]
|
||||
if gotV != wantV {
|
||||
t.Fatalf("Bad result for entry.Fields[\"%s\"]: got %s, want %s", k, gotV, wantV)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check for incorrect read into small buffers,
|
||||
// see https://github.com/coreos/go-systemd/issues/172
|
||||
func TestJournalReaderSmallReadBuffer(t *testing.T) {
|
||||
// Write a long entry ...
|
||||
delim := "%%%%%%"
|
||||
longEntry := strings.Repeat("a", 256)
|
||||
matchField := "TESTJOURNALREADERSMALLBUF"
|
||||
matchValue := fmt.Sprintf("%d", time.Now().UnixNano())
|
||||
r, err := NewJournalReader(JournalReaderConfig{
|
||||
Since: time.Duration(-15) * time.Second,
|
||||
Matches: []Match{
|
||||
{
|
||||
Field: matchField,
|
||||
Value: matchValue,
|
||||
},
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Error opening journal: %s", err)
|
||||
}
|
||||
if r == nil {
|
||||
t.Fatal("Got a nil reader")
|
||||
}
|
||||
defer r.Close()
|
||||
|
||||
want := fmt.Sprintf("%slongentry %s%s", delim, longEntry, delim)
|
||||
err = journal.Send(want, journal.PriInfo, map[string]string{matchField: matchValue})
|
||||
if err != nil {
|
||||
t.Fatal("Error writing to journal", err)
|
||||
}
|
||||
time.Sleep(time.Second)
|
||||
|
||||
// ... and try to read it back piece by piece via a small buffer
|
||||
finalBuff := new(bytes.Buffer)
|
||||
var e error
|
||||
for c := -1; c != 0 && e == nil; {
|
||||
smallBuf := make([]byte, 5)
|
||||
c, e = r.Read(smallBuf)
|
||||
if c > len(smallBuf) {
|
||||
t.Fatalf("Got unexpected read length: %d vs %d", c, len(smallBuf))
|
||||
}
|
||||
_, _ = finalBuff.Write(smallBuf)
|
||||
}
|
||||
b := finalBuff.String()
|
||||
got := strings.Split(b, delim)
|
||||
if len(got) != 3 {
|
||||
t.Fatalf("Got unexpected entry %s", b)
|
||||
}
|
||||
if got[1] != strings.Trim(want, delim) {
|
||||
t.Fatalf("Got unexpected message %s", got[1])
|
||||
}
|
||||
}
|
||||
|
||||
func TestJournalGetUniqueValues(t *testing.T) {
|
||||
j, err := NewJournal()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
defer j.Close()
|
||||
|
||||
uniqueString := generateRandomField(20)
|
||||
testEntries := []string{"A", "B", "C", "D"}
|
||||
for _, v := range testEntries {
|
||||
err := journal.Send("TEST: "+uniqueString, journal.PriInfo, map[string]string{uniqueString: v})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: add proper `waitOnMatch` function which should wait for journal entry with filter to commit.
|
||||
time.Sleep(time.Millisecond * 500)
|
||||
|
||||
values, err := j.GetUniqueValues(uniqueString)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if len(values) != len(testEntries) {
|
||||
t.Fatalf("Expect %d entries. Got %d", len(testEntries), len(values))
|
||||
}
|
||||
|
||||
if !contains(values, "A") || !contains(values, "B") || !contains(values, "C") || !contains(values, "D") {
|
||||
t.Fatalf("Expect 4 values for %s field: A,B,C,D. Got %s", uniqueString, values)
|
||||
}
|
||||
}
|
||||
|
||||
func contains(s []string, v string) bool {
|
||||
for _, entry := range s {
|
||||
if entry == v {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func generateRandomField(n int) string {
|
||||
letters := []rune("ABCDEFGHIJKLMNOPQRSTUVWXYZ")
|
||||
s := make([]rune, n)
|
||||
rand.Seed(time.Now().UnixNano())
|
||||
for i := range s {
|
||||
s[i] = letters[rand.Intn(len(letters))]
|
||||
}
|
||||
return string(s)
|
||||
}
|
||||
260
vendor/github.com/coreos/go-systemd/sdjournal/read.go
generated
vendored
Normal file
260
vendor/github.com/coreos/go-systemd/sdjournal/read.go
generated
vendored
Normal file
|
|
@ -0,0 +1,260 @@
|
|||
// Copyright 2015 RedHat, Inc.
|
||||
// Copyright 2015 CoreOS, Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package sdjournal
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrExpired = errors.New("Timeout expired")
|
||||
)
|
||||
|
||||
// JournalReaderConfig represents options to drive the behavior of a JournalReader.
|
||||
type JournalReaderConfig struct {
|
||||
// The Since, NumFromTail and Cursor options are mutually exclusive and
|
||||
// determine where the reading begins within the journal. The order in which
|
||||
// options are written is exactly the order of precedence.
|
||||
Since time.Duration // start relative to a Duration from now
|
||||
NumFromTail uint64 // start relative to the tail
|
||||
Cursor string // start relative to the cursor
|
||||
|
||||
// Show only journal entries whose fields match the supplied values. If
|
||||
// the array is empty, entries will not be filtered.
|
||||
Matches []Match
|
||||
|
||||
// If not empty, the journal instance will point to a journal residing
|
||||
// in this directory. The supplied path may be relative or absolute.
|
||||
Path string
|
||||
}
|
||||
|
||||
// JournalReader is an io.ReadCloser which provides a simple interface for iterating through the
|
||||
// systemd journal. A JournalReader is not safe for concurrent use by multiple goroutines.
|
||||
type JournalReader struct {
|
||||
journal *Journal
|
||||
msgReader *strings.Reader
|
||||
}
|
||||
|
||||
// NewJournalReader creates a new JournalReader with configuration options that are similar to the
|
||||
// systemd journalctl tool's iteration and filtering features.
|
||||
func NewJournalReader(config JournalReaderConfig) (*JournalReader, error) {
|
||||
r := &JournalReader{}
|
||||
|
||||
// Open the journal
|
||||
var err error
|
||||
if config.Path != "" {
|
||||
r.journal, err = NewJournalFromDir(config.Path)
|
||||
} else {
|
||||
r.journal, err = NewJournal()
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Add any supplied matches
|
||||
for _, m := range config.Matches {
|
||||
r.journal.AddMatch(m.String())
|
||||
}
|
||||
|
||||
// Set the start position based on options
|
||||
if config.Since != 0 {
|
||||
// Start based on a relative time
|
||||
start := time.Now().Add(config.Since)
|
||||
if err := r.journal.SeekRealtimeUsec(uint64(start.UnixNano() / 1000)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else if config.NumFromTail != 0 {
|
||||
// Start based on a number of lines before the tail
|
||||
if err := r.journal.SeekTail(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Move the read pointer into position near the tail. Go one further than
|
||||
// the option so that the initial cursor advancement positions us at the
|
||||
// correct starting point.
|
||||
skip, err := r.journal.PreviousSkip(config.NumFromTail + 1)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// If we skipped fewer lines than expected, we have reached journal start.
|
||||
// Thus, we seek to head so that next invocation can read the first line.
|
||||
if skip != config.NumFromTail+1 {
|
||||
if err := r.journal.SeekHead(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
} else if config.Cursor != "" {
|
||||
// Start based on a custom cursor
|
||||
if err := r.journal.SeekCursor(config.Cursor); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return r, nil
|
||||
}
|
||||
|
||||
// Read reads entries from the journal. Read follows the Reader interface so
|
||||
// it must be able to read a specific amount of bytes. Journald on the other
|
||||
// hand only allows us to read full entries of arbitrary size (without byte
|
||||
// granularity). JournalReader is therefore internally buffering entries that
|
||||
// don't fit in the read buffer. Callers should keep calling until 0 and/or an
|
||||
// error is returned.
|
||||
func (r *JournalReader) Read(b []byte) (int, error) {
|
||||
var err error
|
||||
|
||||
if r.msgReader == nil {
|
||||
var c uint64
|
||||
|
||||
// Advance the journal cursor. It has to be called at least one time
|
||||
// before reading
|
||||
c, err = r.journal.Next()
|
||||
|
||||
// An unexpected error
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
// EOF detection
|
||||
if c == 0 {
|
||||
return 0, io.EOF
|
||||
}
|
||||
|
||||
// Build a message
|
||||
var msg string
|
||||
msg, err = r.buildMessage()
|
||||
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
r.msgReader = strings.NewReader(msg)
|
||||
}
|
||||
|
||||
// Copy and return the message
|
||||
var sz int
|
||||
sz, err = r.msgReader.Read(b)
|
||||
if err == io.EOF {
|
||||
// The current entry has been fully read. Don't propagate this
|
||||
// EOF, so the next entry can be read at the next Read()
|
||||
// iteration.
|
||||
r.msgReader = nil
|
||||
return sz, nil
|
||||
}
|
||||
if err != nil {
|
||||
return sz, err
|
||||
}
|
||||
if r.msgReader.Len() == 0 {
|
||||
r.msgReader = nil
|
||||
}
|
||||
|
||||
return sz, nil
|
||||
}
|
||||
|
||||
// Close closes the JournalReader's handle to the journal.
|
||||
func (r *JournalReader) Close() error {
|
||||
return r.journal.Close()
|
||||
}
|
||||
|
||||
// Rewind attempts to rewind the JournalReader to the first entry.
|
||||
func (r *JournalReader) Rewind() error {
|
||||
r.msgReader = nil
|
||||
return r.journal.SeekHead()
|
||||
}
|
||||
|
||||
// Follow synchronously follows the JournalReader, writing each new journal entry to writer. The
|
||||
// follow will continue until a single time.Time is received on the until channel.
|
||||
func (r *JournalReader) Follow(until <-chan time.Time, writer io.Writer) (err error) {
|
||||
|
||||
// Process journal entries and events. Entries are flushed until the tail or
|
||||
// timeout is reached, and then we wait for new events or the timeout.
|
||||
var msg = make([]byte, 64*1<<(10))
|
||||
process:
|
||||
for {
|
||||
c, err := r.Read(msg)
|
||||
if err != nil && err != io.EOF {
|
||||
break process
|
||||
}
|
||||
|
||||
select {
|
||||
case <-until:
|
||||
return ErrExpired
|
||||
default:
|
||||
if c > 0 {
|
||||
if _, err = writer.Write(msg[:c]); err != nil {
|
||||
break process
|
||||
}
|
||||
continue process
|
||||
}
|
||||
}
|
||||
|
||||
// We're at the tail, so wait for new events or time out.
|
||||
// Holds journal events to process. Tightly bounded for now unless there's a
|
||||
// reason to unblock the journal watch routine more quickly.
|
||||
events := make(chan int, 1)
|
||||
pollDone := make(chan bool, 1)
|
||||
go func() {
|
||||
for {
|
||||
select {
|
||||
case <-pollDone:
|
||||
return
|
||||
default:
|
||||
events <- r.journal.Wait(time.Duration(1) * time.Second)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
select {
|
||||
case <-until:
|
||||
pollDone <- true
|
||||
return ErrExpired
|
||||
case e := <-events:
|
||||
pollDone <- true
|
||||
switch e {
|
||||
case SD_JOURNAL_NOP, SD_JOURNAL_APPEND, SD_JOURNAL_INVALIDATE:
|
||||
// TODO: need to account for any of these?
|
||||
default:
|
||||
log.Printf("Received unknown event: %d\n", e)
|
||||
}
|
||||
continue process
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// buildMessage returns a string representing the current journal entry in a simple format which
|
||||
// includes the entry timestamp and MESSAGE field.
|
||||
func (r *JournalReader) buildMessage() (string, error) {
|
||||
var msg string
|
||||
var usec uint64
|
||||
var err error
|
||||
|
||||
if msg, err = r.journal.GetData("MESSAGE"); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if usec, err = r.journal.GetRealtimeUsec(); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
timestamp := time.Unix(0, int64(usec)*int64(time.Microsecond))
|
||||
|
||||
return fmt.Sprintf("%s %s\n", timestamp, msg), nil
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue