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 }