journal/global.go

133 lines
3.3 KiB
Go
Raw Permalink Normal View History

2024-02-18 10:31:14 +00:00
package journal
import (
"errors"
"fmt"
"os"
"sync"
"time"
)
var (
global *Conn
globalLock sync.Mutex
globalCommon []Attr
globalErrHandler func(error)
globalRateLimit time.Time
errGlobalBackoff = errors.New("journal unavailable; retrying after 30s")
)
const (
globalConnectRetry = 30 * time.Second
)
// CheckConnection returns nil if the connection to the journal is OK, or an
// error otherwise. It will attempt to establish a connection to the journal
// if there is not already one in progress.
func CheckConnection() error {
2026-01-18 12:48:32 +00:00
_, err := GetGlobalConn()
2024-02-18 10:31:14 +00:00
return err
}
// SetGlobalConn can be used to override the default connection to the journal
// that is used by the global logging functions (the ones which do not directly
// operate on a Conn).
func SetGlobalConn(c *Conn) {
globalLock.Lock()
defer globalLock.Unlock()
global = c
}
2026-01-18 12:48:32 +00:00
// GetGlobalConn returns the global (default) connection to the journal. It may
// return an error if no connection could be established. If no connection is
// yet established, it will attempt to establish one. This is rate limited to
// avoid excessive connection attempts should there be a persistent problem
// connecting to the journal socket.
func GetGlobalConn() (*Conn, error) {
2024-02-18 10:31:14 +00:00
globalLock.Lock()
defer globalLock.Unlock()
// if already connected, return
if global != nil {
return global, nil
}
// rate limit new connections
if time.Now().Before(globalRateLimit) {
return nil, errGlobalBackoff
}
// attempt a new connection
var err error
global, err = Connect(WellKnownSocketPath)
if err != nil {
fmt.Fprintln(os.Stderr, "[journal] failed to connect:", err)
globalRateLimit = time.Now().Add(globalConnectRetry)
return nil, err
}
global.Common = globalCommon
if globalErrHandler != nil {
global.ErrHandler = globalErrHandler
} else {
global.ErrHandler = newDefaultGlobalErrHandler()
}
return global, nil
}
func newDefaultGlobalErrHandler() func(error) {
var errHandlerOnce sync.Once
return func(err error) {
errHandlerOnce.Do(func() {
fmt.Fprintln(os.Stderr, "[journal] failed to write entry:", err)
globalLock.Lock()
defer globalLock.Unlock()
if global != nil {
global.s.Close()
global = nil
globalRateLimit = time.Now().Add(globalConnectRetry)
}
})
}
}
// SetGlobalCommonAttr sets common attributes which will be added to every entry
// sent through the global connection.
func SetGlobalCommonAttr(attr []Attr) {
globalLock.Lock()
defer globalLock.Unlock()
globalCommon = attr
if global != nil {
global.Common = attr
}
}
// SetGlobalErrHandler allows overriding the default error handler that is
// called when the connection fails. The default will attempt to reconnect
// to the journal socket after a short delay.
func SetGlobalErrHandler(errHandler func(err error)) {
globalLock.Lock()
defer globalLock.Unlock()
globalErrHandler = errHandler
if global != nil {
global.ErrHandler = errHandler
}
}
// Entry writes a journal entry via the global connection. It will establish
// a connection as necessary.
//
// Note: to avoid allocation / garbage, ensure attrs has capacity for an extra
// 2+len(GlobalCommonAttr) values.
func Entry(pri Priority, msg string, attrs []Attr) {
2026-01-18 12:48:32 +00:00
c, _ := GetGlobalConn()
2024-02-18 10:31:14 +00:00
if c == nil {
return
}
c.Entry(pri, msg, attrs)
}