journal/conn.go

104 lines
2.4 KiB
Go

package journal
import (
"net"
"slices"
"sync"
)
const (
// WellKnownSocketPath is where we generally expect to find the systemd
// journal daemon's socket.
WellKnownSocketPath = "/run/systemd/journal/socket"
)
// Conn represents an active, open connection to the systemd journal that is
// ready to write log entries.
type Conn struct {
// Common attributes which are emitted for all messages.
Common []Attr
// ErrHandler is called if there is an error writing to the log. If nil,
// the error is silently discarded.
ErrHandler func(error)
s *net.UnixConn
bufs sync.Pool
atts sync.Pool
}
// Connect to the systemd journal. If the path string is empty, then it uses the
// well-known socket path.
func Connect(path string) (*Conn, error) {
if path == "" {
path = WellKnownSocketPath
}
s, err := net.DialUnix("unixgram", nil, &net.UnixAddr{
Name: path,
Net: "unixgram",
})
if err != nil {
return nil, err
}
c := &Conn{
s: s,
bufs: sync.Pool{
New: func() any {
return &net.Buffers{}
},
},
}
c.atts = sync.Pool{
New: func() any {
return make([]Attr, 0, 2+len(c.Common))
},
}
return c, nil
}
var messageAttrKey = AttrKey{key: "MESSAGE"}
// Entry emits a log entry. It will add PRIORITY and MESSAGE key/value pairs
// as well as any Common attributes.
func (c *Conn) Entry(pri Priority, msg string, attrs []Attr) {
err := c.EntryErr(pri, msg, attrs)
switch {
case err == nil:
return
case c.ErrHandler == nil:
return
default:
c.ErrHandler(err)
}
}
// EntryErr is like Entry, but will propagate errors to the caller, rather than
// using the built-in error handler.
func (c *Conn) EntryErr(pri Priority, msg string, attrs []Attr) error {
a := c.atts.Get().([]Attr)
a = slices.Grow(a[:0], 2+len(c.Common)+len(attrs))
a = append(a, pri.Attr(), Attr{
Key: messageAttrKey,
Value: []byte(msg),
})
a = append(a, c.Common...)
a = append(a, attrs...)
err := c.WriteAttrs(a)
slices.Delete(a, 0, len(a)) // ensure GC doesn't see refs to old data
c.atts.Put(a)
return err
}
// WriteAttrs is a low-level method which writes a journal entry comprised of
// only the given set of attributes.
func (c *Conn) WriteAttrs(attrs []Attr) error {
buf := c.bufs.Get().(*net.Buffers)
*buf = (*buf)[:0]
err := WireWrite(buf, c.s, attrs)
slices.Delete(*buf, 0, len(*buf)) // ensure GC doesn't see refs to old data
c.bufs.Put(buf)
return err
}