Documentation
¶
Overview ¶
Package syslog provides an RFC 5424 syslog audit.Output implementation supporting TCP, UDP, and TCP+TLS (including mTLS) transport.
Construction ¶
New dials the syslog server immediately — the server must be reachable at construction time:
out, err := syslog.New(&syslog.Config{
Network: "tcp+tls",
Address: "syslog.example.com:6514",
TLSCA: "/etc/audit/ca.pem",
})
Valid [Config.Network] values: "tcp" (default), "udp", "tcp+tls". Use "tcp+tls" with [Config.TLSCert] and [Config.TLSKey] for mTLS.
Reconnection ¶
TCP and TLS connections are re-established automatically on write failure, up to [Config.MaxRetries] attempts (default 10). UDP is connectionless and does not reconnect, but messages exceeding the UDP MTU are silently truncated by the network.
To observe reconnect events, wire an audit.OutputMetrics value via WithOutputMetrics at construction. If the value also implements ReconnectRecorder its RecordReconnect method is called on each reconnect attempt (structural typing — no explicit registration needed).
Recommended import alias:
import auditsyslog "github.com/axonops/audit/syslog"
Index ¶
- Constants
- func NewFactory(factory audit.OutputMetricsFactory) audit.OutputFactory
- type Config
- type Option
- type Output
- func (s *Output) Close() error
- func (s *Output) DestinationKey() string
- func (s *Output) LastDeliveryNanos() int64
- func (s *Output) Name() string
- func (s *Output) ReportsDelivery() bool
- func (s *Output) Write(data []byte) error
- func (s *Output) WriteWithMetadata(data []byte, meta audit.EventMetadata) error
- type ReconnectRecorder
Examples ¶
Constants ¶
const ( // DefaultAppName is the default application name in the // syslog header when [Config.AppName] is empty. DefaultAppName = "audit" // DefaultFacility is the default syslog facility when // [Config.Facility] is empty. DefaultFacility = "local0" // DefaultMaxRetries is the default number of reconnection // attempts before giving up. DefaultMaxRetries = 10 // MaxMaxRetries is the upper bound for MaxRetries. Values above // this are rejected to prevent unbounded retry loops. MaxMaxRetries = 20 // DefaultBufferSize is the default async buffer capacity for the // syslog output. Matches the default for all other async outputs. DefaultBufferSize = 10_000 // MaxOutputBufferSize is the maximum allowed per-output async // buffer capacity. MaxOutputBufferSize = 100_000 // DefaultBatchSize is the default number of events accumulated // before a flush is triggered. Matches [loki.DefaultBatchSize]. DefaultBatchSize = 100 // MaxBatchSize is the upper bound for BatchSize. Values above // this cause [New] to return an error wrapping // [audit.ErrConfigInvalid]. MaxBatchSize = 10_000 // DefaultFlushInterval is the default maximum time between // flushes. Matches [loki.DefaultFlushInterval]. DefaultFlushInterval = 5 * time.Second // MinFlushInterval is the lower bound for FlushInterval. Values // below this cause [New] to return an error — a sub-millisecond // interval would busy-spin the writeLoop. MinFlushInterval = 1 * time.Millisecond // MaxFlushInterval is the upper bound for FlushInterval. MaxFlushInterval = 1 * time.Hour // DefaultMaxBatchBytes is the default maximum accumulated batch // size in bytes before a flush is triggered. 1 MiB matches // [loki.DefaultMaxBatchBytes]. Events exceeding this threshold // alone trigger an immediate flush (the event is sent in its own // batch; it is never dropped). DefaultMaxBatchBytes = 1 << 20 // 1 MiB // MinMaxBatchBytes is the lower bound for MaxBatchBytes. MinMaxBatchBytes = 1 << 10 // 1 KiB // MaxMaxBatchBytes is the upper bound for MaxBatchBytes. Capped // at 10 MiB to match [loki.MaxMaxBatchBytes]; real-world syslog // receivers (syslog-ng, rsyslog) typically reject single messages // over 64 KiB, but batches of many smaller messages can legitimately // reach several MiB. MaxMaxBatchBytes = 10 << 20 // 10 MiB // DefaultMaxEventBytes is the default per-event size cap at // [Output.Write] entry. Events with data longer than this are // rejected with [audit.ErrEventTooLarge] (#688) — a defence // against consumer-controlled memory pressure. 1 MiB matches // the cap used by loki and webhook. DefaultMaxEventBytes = 1 << 20 // 1 MiB // MinMaxEventBytes is the lower bound for MaxEventBytes. MinMaxEventBytes = 1 << 10 // 1 KiB // MaxMaxEventBytes is the upper bound for MaxEventBytes. MaxMaxEventBytes = 10 << 20 // 10 MiB // DefaultTLSHandshakeTimeout is the default total budget for the // TCP dial + TLS handshake on `network: tcp+tls` connections. // Matches [net/http.DefaultTransport.TLSHandshakeTimeout]. Without // a bound, a server that completes the TCP three-way handshake // but never sends ServerHello would wedge [New] indefinitely // (#746). DefaultTLSHandshakeTimeout = 10 * time.Second // MinTLSHandshakeTimeout is the lower bound for TLSHandshakeTimeout. // 100ms is the practical floor — typical TLS 1.3 handshakes on a // LAN complete in 5–20ms but cold-start handshakes (cert verify // with stapled OCSP, key derivation) can hit ~80ms on slow // hardware. Production deployments SHOULD use ≥1s. MinTLSHandshakeTimeout = 100 * time.Millisecond // MaxTLSHandshakeTimeout is the upper bound for TLSHandshakeTimeout. // 60s is the absolute ceiling; any single handshake taking longer // is almost certainly a wedged connection that benefits from // being failed and retried. MaxTLSHandshakeTimeout = 60 * time.Second )
Variables ¶
This section is empty.
Functions ¶
func NewFactory ¶
func NewFactory(factory audit.OutputMetricsFactory) audit.OutputFactory
NewFactory returns an audit.OutputFactory that creates syslog outputs from YAML configuration and wires per-output metrics via the supplied audit.OutputMetricsFactory. When factory is non-nil, the returned audit.Output receives its per-output audit.OutputMetrics via WithOutputMetrics at construction time; if the returned metrics also implement ReconnectRecorder, reconnection telemetry is wired in automatically. Pass nil to disable per-output metrics.
Signature is identical to the other output modules' `NewFactory` (file, webhook, loki) for consistency (#581).
Types ¶
type Config ¶
type Config struct {
// Network is the transport protocol: "tcp", "udp", or "tcp+tls".
// Empty defaults to "tcp". Note: UDP syslog may silently truncate
// or drop messages larger than ~2048 bytes (RFC 5424 §6.1).
// Use TCP or TCP+TLS for reliable delivery of large audit events.
Network string
// Address is the syslog server address in host:port format.
// REQUIRED; an empty address causes [New] to return
// an error.
Address string
// AppName is the application name in the syslog header.
// Empty defaults to [DefaultAppName] ("audit").
AppName string
// Facility is the syslog facility name. Supported values:
// kern, user, mail, daemon, auth, syslog, lpr, news, uucp,
// cron, authpriv, ftp, local0 through local7.
// Empty defaults to [DefaultFacility] ("local0").
// Unknown values cause [New] to return an error.
Facility string
// TLSCert is the path to the client certificate for mTLS.
// Both TLSCert and TLSKey must be set for client authentication.
TLSCert string
// TLSKey is the path to the client private key for mTLS.
// Both TLSCert and TLSKey must be set for client authentication.
TLSKey string
// TLSCA is the path to the CA certificate for server verification.
// When set, the server's certificate is verified against this CA.
TLSCA string
// TLSPolicy controls TLS version and cipher suite policy. When nil,
// the default policy (TLS 1.3 only) is used. See [audit.TLSPolicy]
// for details on enabling TLS 1.2 fallback.
TLSPolicy *audit.TLSPolicy
// Hostname overrides the hostname in the syslog RFC 5424 header.
// When empty, [os.Hostname] is used at construction time. Set this
// to match the auditor-wide host value from [audit.WithHost].
Hostname string
// TLSHandshakeTimeout bounds the total time spent on the TCP dial
// plus the TLS handshake during [New] and on every reconnect.
// Without this bound, a server that completes the TCP three-way
// handshake but never sends ServerHello would wedge [New]
// indefinitely (#746).
//
// Zero defaults to [DefaultTLSHandshakeTimeout] (10s — matches
// [net/http.DefaultTransport.TLSHandshakeTimeout]). Values
// outside [MinTLSHandshakeTimeout]–[MaxTLSHandshakeTimeout]
// (100ms–60s) cause [New] to return an error wrapping
// [audit.ErrConfigInvalid].
//
// On non-TLS networks (`tcp`, `udp`) this field is silently
// ignored: it has no effect and is not validated.
//
// A handshake that exceeds this budget at runtime returns a
// transient error containing the substring "tls handshake
// timeout"; the existing reconnect path retries the connection.
TLSHandshakeTimeout time.Duration
// MaxRetries is the maximum number of consecutive reconnection
// attempts before giving up. Zero defaults to
// [DefaultMaxRetries] (10).
MaxRetries int
// BufferSize is the internal async buffer capacity. When full,
// new events are dropped and [audit.OutputMetrics.RecordDrop] is
// called. Zero defaults to [DefaultBufferSize] (10,000). Values
// above [MaxOutputBufferSize] (100,000) cause [New] to return an
// error wrapping [audit.ErrConfigInvalid].
BufferSize int
// BatchSize is the number of events accumulated in the write
// loop before triggering a flush to the syslog server. Zero
// defaults to [DefaultBatchSize] (100). Values above
// [MaxBatchSize] (10,000) cause [New] to return an error
// wrapping [audit.ErrConfigInvalid]. Set to 1 to disable
// batching (every event flushes immediately — effectively
// restoring pre-#599 per-event write behaviour).
//
// Matches the conventions established by [loki.Config.BatchSize]
// and [github.com/axonops/audit/webhook.Config.BatchSize] so an
// operator tuning multi-output deployments sees the same knob
// across all batching outputs.
BatchSize int
// FlushInterval is the maximum time between flushes, regardless
// of whether the batch has reached [Config.BatchSize]. Zero
// defaults to [DefaultFlushInterval] (5 s). Values below
// [MinFlushInterval] (1 ms) or above [MaxFlushInterval] (1 h)
// cause [New] to return an error wrapping
// [audit.ErrConfigInvalid].
//
// With the 5 s default, a single-event-per-5s audit trickle can
// see up to 5 s of delivery latency. Consumers needing lower
// latency should lower FlushInterval or set [Config.BatchSize]
// to 1.
FlushInterval time.Duration
// MaxBatchBytes is the maximum accumulated payload size (sum of
// event data lengths) before a flush is triggered, independent
// of [Config.BatchSize]. Zero defaults to [DefaultMaxBatchBytes]
// (1 MiB). Values below [MinMaxBatchBytes] (1 KiB) or above
// [MaxMaxBatchBytes] (10 MiB) cause [New] to return an error
// wrapping [audit.ErrConfigInvalid].
//
// A single event exceeding MaxBatchBytes is flushed alone — it is
// never dropped. Events are never split across frames; RFC 5425
// octet-counting framing is preserved per message.
MaxBatchBytes int
// MaxEventBytes is the maximum byte length accepted by
// [Output.Write] for a single event. Events exceeding this cap
// are rejected with [audit.ErrEventTooLarge] wrapping
// [audit.ErrValidation] and [audit.OutputMetrics.RecordDrop] is
// called. Zero defaults to [DefaultMaxEventBytes] (1 MiB).
// Values below [MinMaxEventBytes] (1 KiB) or above
// [MaxMaxEventBytes] (10 MiB) cause [New] to return an error
// wrapping [audit.ErrConfigInvalid].
//
// Introduced by #688 as a defence against consumer-controlled
// memory pressure: a single oversized event in a batching path
// can be held in the async channel, the batch slice, and the
// retry buffer simultaneously. A default 10 000-slot buffer
// carrying 10 MiB events could pin ~100 GiB before backpressure
// triggers.
MaxEventBytes int
}
Config holds configuration for Output.
Example (Mtls) ¶
package main
import (
"fmt"
"github.com/axonops/audit/syslog"
)
func main() {
// mTLS syslog with client certificate authentication.
cfg := &syslog.Config{
Network: "tcp+tls",
Address: "syslog.example.com:6514",
TLSCert: "/etc/audit/client-cert.pem",
TLSKey: "/etc/audit/client-key.pem",
TLSCA: "/etc/audit/ca.pem",
}
fmt.Printf("network=%s address=%s cert=%s key=%s ca=%s\n",
cfg.Network, cfg.Address, cfg.TLSCert, cfg.TLSKey, cfg.TLSCA)
}
Output: network=tcp+tls address=syslog.example.com:6514 cert=/etc/audit/client-cert.pem key=/etc/audit/client-key.pem ca=/etc/audit/ca.pem
Example (Tcp) ¶
package main
import (
"fmt"
"github.com/axonops/audit/syslog"
)
func main() {
// Plain TCP syslog — the simplest configuration.
cfg := &syslog.Config{
Network: "tcp",
Address: "syslog.example.com:514",
Facility: "local0",
AppName: "myapp",
}
fmt.Printf("network=%s address=%s facility=%s app=%s\n",
cfg.Network, cfg.Address, cfg.Facility, cfg.AppName)
}
Output: network=tcp address=syslog.example.com:514 facility=local0 app=myapp
Example (Tls) ¶
package main
import (
"fmt"
"github.com/axonops/audit/syslog"
)
func main() {
// TLS syslog with CA verification.
cfg := &syslog.Config{
Network: "tcp+tls",
Address: "syslog.example.com:6514",
TLSCA: "/etc/audit/ca.pem",
}
fmt.Printf("network=%s address=%s ca=%s\n", cfg.Network, cfg.Address, cfg.TLSCA)
}
Output: network=tcp+tls address=syslog.example.com:6514 ca=/etc/audit/ca.pem
func (Config) Format ¶ added in v0.1.2
Format writes the redacted representation to the formatter. This prevents TLS key path leakage via %+v and all other format verbs.
func (Config) GoString ¶ added in v0.1.2
GoString returns the same redacted representation as Config.String. This prevents TLS key path leakage when configs are formatted via %#v.
type Option ¶ added in v0.1.11
type Option func(*options)
Option configures a syslog Output at construction time. Options are passed as variadic arguments to New and applied in order before any configuration validation, TLS setup, or warning emission.
func WithDiagnosticLogger ¶ added in v0.1.11
WithDiagnosticLogger routes construction-time and runtime warnings (TLS policy, reconnection backoff, buffer-full drops) to the given logger. When nil or not supplied, warnings go to slog.Default.
Consumers normally do not call this directly when using github.com/axonops/audit/outputconfig.Load — outputconfig plumbs the auditor's diagnostic logger into every output it constructs. Use this option when constructing a syslog output programmatically and you want its warnings to match your application's log handler.
The option mirrors github.com/axonops/audit.WithDiagnosticLogger at the auditor level; the same logger may be passed to both for consistent routing.
func WithOutputMetrics ¶ added in v0.1.12
func WithOutputMetrics(m audit.OutputMetrics) Option
WithOutputMetrics sets the audit.OutputMetrics sink for this output. When omitted or nil, metrics calls become no-ops via audit.NoOpOutputMetrics. Mirrors WithDiagnosticLogger in usage and zero-value semantics.
If the supplied value also implements ReconnectRecorder, reconnection telemetry is wired in automatically (structural typing — see net/http.Flusher precedent).
Consumers normally do not call this directly when using github.com/axonops/audit/outputconfig.Load — outputconfig wires per-output metrics through the audit.OutputMetricsFactory supplied via outputconfig.WithOutputMetricsFactory.
type Output ¶
type Output struct {
// contains filtered or unexported fields
}
Output writes serialised audit events to a syslog server over TCP, UDP, or TCP+TLS (including mTLS). Events are formatted as RFC 5424 structured syslog messages with the pre-serialised audit payload (JSON or CEF) as the message body.
Write enqueues events into an internal buffered channel and returns immediately. A background goroutine reads from the channel and performs the actual syslog write with reconnection handling.
Reconnection ¶
On connection failure, the background goroutine attempts bounded exponential backoff reconnection (100ms to 30s with jitter, up to [Config.MaxRetries] attempts). If all retries are exhausted, the event is dropped and an error metric is recorded. The goroutine continues processing subsequent events.
UDP limitations ¶
UDP syslog is fire-and-forget. Write over UDP rarely returns an error even if no server is listening. RFC 5424 recommends receivers support messages up to 2048 bytes on UDP; larger payloads may be silently truncated or dropped by the OS. Consumers with large audit events SHOULD use TCP or TCP+TLS.
TLS certificates ¶
TLS certificate files are loaded once at construction time and are NOT hot-reloaded. If a certificate expires and is rotated on disk, the output continues using the old certificate until the process is restarted.
Output is safe for concurrent use.
func New ¶
New creates a new Output from the given config. It validates the config, establishes the initial connection, and starts the background writeLoop goroutine.
Per-output metrics may be supplied at construction via WithOutputMetrics. When omitted, telemetry calls become no-ops.
Optional Option arguments tune construction-time behaviour. Pass WithDiagnosticLogger to route TLS-policy warnings to a custom logger.
func (*Output) Close ¶
Close signals the background goroutine to drain and flush, then waits for completion and closes the syslog connection. Close is idempotent and safe for concurrent use.
func (*Output) DestinationKey ¶
DestinationKey returns the syslog server address, enabling duplicate destination detection via audit.DestinationKeyer.
func (*Output) LastDeliveryNanos ¶ added in v0.1.12
LastDeliveryNanos returns the wall-clock UnixNano of the most recent successful syslog write, or 0 if no write has yet succeeded. Implements audit.LastDeliveryReporter (#753).
func (*Output) ReportsDelivery ¶ added in v0.1.11
ReportsDelivery returns true, indicating that Output reports its own delivery metrics from the background writeLoop after actual syslog delivery, not from the Write enqueue path.
func (*Output) Write ¶
Write enqueues a serialised audit event for async delivery to the syslog server with the default severity (LOG_INFO). The data is copied before enqueuing. If the internal buffer is full, the event is dropped. Write never blocks the caller.
func (*Output) WriteWithMetadata ¶
func (s *Output) WriteWithMetadata(data []byte, meta audit.EventMetadata) error
WriteWithMetadata enqueues a serialised audit event for async delivery with the syslog severity derived from the audit event's severity field.
type ReconnectRecorder ¶ added in v0.1.12
type ReconnectRecorder interface {
// RecordReconnect records a syslog reconnection attempt. success
// indicates whether the reconnection succeeded. address is the
// configured host:port. Implementations SHOULD NOT use address
// as an unbounded metric label.
RecordReconnect(address string, success bool)
}
ReconnectRecorder is an OPTIONAL extension interface for syslog reconnection telemetry. A consumer's audit.OutputMetrics implementation MAY also implement ReconnectRecorder. When the syslog output receives per-output metrics via WithOutputMetrics (or factory wiring via outputconfig.WithOutputMetricsFactory), it type-asserts for ReconnectRecorder and invokes RecordReconnect on every connect attempt. Precedent: net/http.Flusher as an optional extension on [http.ResponseWriter].
Consumers who do not care about reconnection telemetry need not implement this interface — the base audit.OutputMetrics contract is sufficient.