release: version 1.1.2 - Add error callback mechanism and comprehensive test suite
Some checks failed
Build RPM Package / Build RPM Packages (CentOS 7, Rocky 8/9/10) (push) Has been cancelled

Features:
- Add ErrorCallback type for UNIX socket connection error reporting
- Add WithErrorCallback option for UnixSocketWriter configuration
- Add BuilderImpl.WithErrorCallback() for propagating callbacks
- Add consecutive failure tracking in processQueue

Testing (50+ new tests):
- Add integration tests for full pipeline (capture → tlsparse → fingerprint → output)
- Add tests for FileWriter.rotate() and Reopen() log rotation
- Add tests for cleanupExpiredFlows() and cleanupLoop() in TLS parser
- Add tests for extractSNIFromPayload() and extractJA4Hash() helpers
- Add tests for config load error paths (invalid YAML, permission denied)
- Add tests for capture.Run() error conditions
- Add tests for signal handling documentation

Documentation:
- Update architecture.yml with new fields (LogLevel, TLSClientHello extensions)
- Update architecture.yml with Close() methods for Capture and Parser interfaces
- Update RPM spec changelog

Cleanup:
- Remove empty internal/api/ directory

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
This commit is contained in:
Jacquin Antoine
2026-03-02 23:24:56 +01:00
parent 6e5addd6d4
commit 23f3012fb1
10 changed files with 2058 additions and 10 deletions

View File

@ -202,6 +202,9 @@ func (w *FileWriter) Reopen() error {
return nil
}
// ErrorCallback is a function type for reporting socket connection errors
type ErrorCallback func(socketPath string, err error, attempt int)
// UnixSocketWriter writes log records to a UNIX socket with reconnection logic
// No internal logging - only LogRecord JSON data is sent to the socket
type UnixSocketWriter struct {
@ -220,6 +223,9 @@ type UnixSocketWriter struct {
isClosed bool
pendingWrites [][]byte
pendingMu sync.Mutex
errorCallback ErrorCallback
consecutiveFailures int
failuresMu sync.Mutex
}
// NewUnixSocketWriter creates a new UNIX socket writer with reconnection logic
@ -227,8 +233,18 @@ func NewUnixSocketWriter(socketPath string) (*UnixSocketWriter, error) {
return NewUnixSocketWriterWithConfig(socketPath, DefaultDialTimeout, DefaultWriteTimeout, DefaultQueueSize)
}
// UnixSocketWriterOption is a function type for configuring UnixSocketWriter
type UnixSocketWriterOption func(*UnixSocketWriter)
// WithErrorCallback sets an error callback for socket connection errors
func WithErrorCallback(cb ErrorCallback) UnixSocketWriterOption {
return func(w *UnixSocketWriter) {
w.errorCallback = cb
}
}
// NewUnixSocketWriterWithConfig creates a new UNIX socket writer with custom configuration
func NewUnixSocketWriterWithConfig(socketPath string, dialTimeout, writeTimeout time.Duration, queueSize int) (*UnixSocketWriter, error) {
func NewUnixSocketWriterWithConfig(socketPath string, dialTimeout, writeTimeout time.Duration, queueSize int, opts ...UnixSocketWriterOption) (*UnixSocketWriter, error) {
w := &UnixSocketWriter{
socketPath: socketPath,
dialTimeout: dialTimeout,
@ -242,6 +258,11 @@ func NewUnixSocketWriterWithConfig(socketPath string, dialTimeout, writeTimeout
pendingWrites: make([][]byte, 0),
}
// Apply options
for _, opt := range opts {
opt(w)
}
// Start the queue processor
go w.processQueue()
@ -259,7 +280,6 @@ func (w *UnixSocketWriter) processQueue() {
defer close(w.queueDone)
backoff := w.reconnectBackoff
consecutiveFailures := 0
for {
select {
@ -271,7 +291,14 @@ func (w *UnixSocketWriter) processQueue() {
}
if err := w.writeWithReconnect(data); err != nil {
consecutiveFailures++
w.failuresMu.Lock()
w.consecutiveFailures++
failures := w.consecutiveFailures
w.failuresMu.Unlock()
// Report error via callback if configured
w.reportError(err, failures)
// Queue for retry
w.pendingMu.Lock()
if len(w.pendingWrites) < DefaultQueueSize {
@ -280,7 +307,7 @@ func (w *UnixSocketWriter) processQueue() {
w.pendingMu.Unlock()
// Exponential backoff
if consecutiveFailures > w.maxReconnects {
if failures > w.maxReconnects {
time.Sleep(backoff)
backoff *= 2
if backoff > w.maxBackoff {
@ -288,7 +315,9 @@ func (w *UnixSocketWriter) processQueue() {
}
}
} else {
consecutiveFailures = 0
w.failuresMu.Lock()
w.consecutiveFailures = 0
w.failuresMu.Unlock()
backoff = w.reconnectBackoff
// Try to flush pending data
w.flushPendingData()
@ -301,6 +330,13 @@ func (w *UnixSocketWriter) processQueue() {
}
}
// reportError reports a socket connection error via the configured callback
func (w *UnixSocketWriter) reportError(err error, attempt int) {
if w.errorCallback != nil {
w.errorCallback(w.socketPath, err, attempt)
}
}
// flushPendingData attempts to write any pending data
func (w *UnixSocketWriter) flushPendingData() {
w.pendingMu.Lock()
@ -486,13 +522,21 @@ func (mw *MultiWriter) Reopen() error {
}
// BuilderImpl implements the api.Builder interface
type BuilderImpl struct{}
type BuilderImpl struct {
errorCallback ErrorCallback
}
// NewBuilder creates a new output builder
func NewBuilder() *BuilderImpl {
return &BuilderImpl{}
}
// WithErrorCallback sets an error callback for all unix_socket writers created by this builder
func (b *BuilderImpl) WithErrorCallback(cb ErrorCallback) *BuilderImpl {
b.errorCallback = cb
return b
}
// NewFromConfig constructs writers from AppConfig
// Uses AsyncBuffer from OutputConfig if specified, otherwise uses DefaultQueueSize
func (b *BuilderImpl) NewFromConfig(cfg api.AppConfig) (api.Writer, error) {
@ -529,7 +573,12 @@ func (b *BuilderImpl) NewFromConfig(cfg api.AppConfig) (api.Writer, error) {
if socketPath == "" {
return nil, fmt.Errorf("unix_socket output requires 'socket_path' parameter")
}
writer, err = NewUnixSocketWriterWithConfig(socketPath, DefaultDialTimeout, DefaultWriteTimeout, queueSize)
// Build options list
var opts []UnixSocketWriterOption
if b.errorCallback != nil {
opts = append(opts, WithErrorCallback(b.errorCallback))
}
writer, err = NewUnixSocketWriterWithConfig(socketPath, DefaultDialTimeout, DefaultWriteTimeout, queueSize, opts...)
if err != nil {
return nil, err
}