114 lines
2.2 KiB
Go
114 lines
2.2 KiB
Go
package journald
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"iter"
|
|
"strings"
|
|
"sync"
|
|
"time"
|
|
|
|
"github.com/coreos/go-systemd/v22/sdjournal"
|
|
)
|
|
|
|
func syslogMatchData(syslogIdentifier string) string {
|
|
builder := strings.Builder{}
|
|
|
|
builder.WriteString(sdjournal.SD_JOURNAL_FIELD_SYSLOG_IDENTIFIER)
|
|
builder.WriteString("=")
|
|
builder.WriteString(syslogIdentifier)
|
|
|
|
return builder.String()
|
|
}
|
|
|
|
type Journald struct {
|
|
journal *sdjournal.Journal
|
|
lock sync.Mutex
|
|
}
|
|
|
|
func NewJournald(syslogName string) (*Journald, error) {
|
|
journal, err := sdjournal.NewJournal()
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to initialize journal: %w", err)
|
|
}
|
|
|
|
if syslogName != "" {
|
|
matchData := syslogMatchData(syslogName)
|
|
err = journal.AddMatch(matchData)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to add match %s to journal: %w", matchData, err)
|
|
}
|
|
}
|
|
|
|
err = journal.SeekTail()
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to seek to tail: %w", err)
|
|
}
|
|
|
|
_, err = journal.Previous()
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to go to tail message: %w", err)
|
|
}
|
|
|
|
return &Journald{
|
|
journal: journal,
|
|
}, nil
|
|
}
|
|
|
|
func (j *Journald) Close() error {
|
|
err := j.journal.Close()
|
|
if err != nil {
|
|
return fmt.Errorf("failed to close journal: %w", err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (j *Journald) Next() (*sdjournal.JournalEntry, error) {
|
|
j.lock.Lock()
|
|
defer j.lock.Unlock()
|
|
|
|
entryCount, err := j.journal.Next()
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to get next journald entry: %w", err)
|
|
}
|
|
|
|
if entryCount == 0 {
|
|
return nil, nil
|
|
}
|
|
|
|
entry, err := j.journal.GetEntry()
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to get journald entry: %w", err)
|
|
}
|
|
|
|
return entry, nil
|
|
}
|
|
|
|
func (j *Journald) Iter(ctx context.Context) iter.Seq2[*sdjournal.JournalEntry, error] {
|
|
return func(yield func(*sdjournal.JournalEntry, error) bool) {
|
|
for {
|
|
j.journal.Wait(time.Second)
|
|
if ctx.Err() != nil {
|
|
yield(nil, fmt.Errorf("failed to iterate on journald, context error: %w", ctx.Err()))
|
|
return
|
|
}
|
|
|
|
entry, err := j.Next()
|
|
if err != nil {
|
|
yield(nil, err)
|
|
return
|
|
}
|
|
|
|
// no more entries at the moment
|
|
if entry == nil {
|
|
continue
|
|
}
|
|
|
|
// we were told to stop
|
|
if !yield(entry, nil) {
|
|
return
|
|
}
|
|
}
|
|
}
|
|
}
|