Change default output to Unix socket
Some checks failed
Build RPM Package / Build RPM Packages (CentOS 7, Rocky 8/9/10) (push) Has been cancelled
Some checks failed
Build RPM Package / Build RPM Packages (CentOS 7, Rocky 8/9/10) (push) Has been cancelled
- config.yml.example: Unix socket enabled by default, stdout commented out - internal/output/writers.go: Remove all internal logging from UnixSocketWriter and FileWriter - only LogRecord JSON data is sent to outputs - architecture.yml: Update description to mention 'socket UNIX par défaut' - packaging/rpm/ja4sentinel.spec: Bump version to 1.1.1, update changelog Diagnostic logs (error, debug, warning) now only go to stdout when enabled. Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
This commit is contained in:
@ -7,7 +7,7 @@ project:
|
||||
extraire les handshakes TLS côté client, générer les signatures JA4
|
||||
(via psanford/tlsfingerprint), enrichir avec des métadonnées IP/TCP,
|
||||
et loguer les résultats (IP, ports, JA4, meta) vers une ou plusieurs
|
||||
sorties configurables (socket UNIX, stdout, fichier, ...).
|
||||
sorties configurables (socket UNIX par défaut, stdout, fichier, ...).
|
||||
Le service est géré par systemd avec support de rotation des logs via logrotate.
|
||||
La commande `systemctl reload ja4sentinel` permet de réouvrir les fichiers de log
|
||||
après rotation (signal SIGHUP).
|
||||
|
||||
@ -24,20 +24,22 @@ core:
|
||||
log_level: info
|
||||
|
||||
outputs:
|
||||
# Output to stdout (JSON lines)
|
||||
- type: stdout
|
||||
# Output to UNIX socket (for systemd/journald or other consumers)
|
||||
# Only JSON LogRecord data is sent - no diagnostic logs
|
||||
- type: unix_socket
|
||||
enabled: true
|
||||
params: {}
|
||||
params:
|
||||
socket_path: /var/run/logcorrelator/network.socket
|
||||
|
||||
# Output to stdout (JSON lines)
|
||||
# Diagnostic logs (error, debug, warning) should go here
|
||||
# - type: stdout
|
||||
# enabled: false
|
||||
# params: {}
|
||||
|
||||
# Output to file
|
||||
# Only JSON LogRecord data is sent - no diagnostic logs
|
||||
# - type: file
|
||||
# enabled: false
|
||||
# params:
|
||||
# path: /var/log/ja4sentinel/ja4.log
|
||||
|
||||
# Output to UNIX socket (for systemd/journald or other consumers)
|
||||
# - type: unix_socket
|
||||
# enabled: false
|
||||
# params:
|
||||
# socket_path: /var/run/logcorrelator/network.socket
|
||||
# log_level: debug # debug, info, warn, error (default: error)
|
||||
|
||||
@ -5,11 +5,9 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"net"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
@ -205,6 +203,7 @@ func (w *FileWriter) Reopen() error {
|
||||
}
|
||||
|
||||
// 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 {
|
||||
socketPath string
|
||||
conn net.Conn
|
||||
@ -221,8 +220,6 @@ type UnixSocketWriter struct {
|
||||
isClosed bool
|
||||
pendingWrites [][]byte
|
||||
pendingMu sync.Mutex
|
||||
logLevel string
|
||||
logger *log.Logger
|
||||
}
|
||||
|
||||
// NewUnixSocketWriter creates a new UNIX socket writer with reconnection logic
|
||||
@ -232,11 +229,6 @@ func NewUnixSocketWriter(socketPath string) (*UnixSocketWriter, error) {
|
||||
|
||||
// NewUnixSocketWriterWithConfig creates a new UNIX socket writer with custom configuration
|
||||
func NewUnixSocketWriterWithConfig(socketPath string, dialTimeout, writeTimeout time.Duration, queueSize int) (*UnixSocketWriter, error) {
|
||||
return NewUnixSocketWriterWithConfigAndLogLevel(socketPath, dialTimeout, writeTimeout, queueSize, "error")
|
||||
}
|
||||
|
||||
// NewUnixSocketWriterWithConfigAndLogLevel creates a new UNIX socket writer with log level configuration
|
||||
func NewUnixSocketWriterWithConfigAndLogLevel(socketPath string, dialTimeout, writeTimeout time.Duration, queueSize int, logLevel string) (*UnixSocketWriter, error) {
|
||||
w := &UnixSocketWriter{
|
||||
socketPath: socketPath,
|
||||
dialTimeout: dialTimeout,
|
||||
@ -248,50 +240,20 @@ func NewUnixSocketWriterWithConfigAndLogLevel(socketPath string, dialTimeout, wr
|
||||
queueClose: make(chan struct{}),
|
||||
queueDone: make(chan struct{}),
|
||||
pendingWrites: make([][]byte, 0),
|
||||
logLevel: strings.ToLower(logLevel),
|
||||
logger: log.New(os.Stderr, "[UnixSocket] ", log.LstdFlags|log.Lmicroseconds),
|
||||
}
|
||||
|
||||
// Start the queue processor
|
||||
go w.processQueue()
|
||||
|
||||
// Try initial connection (socket may not exist yet - that's okay)
|
||||
// Try initial connection silently (socket may not exist yet - that's okay)
|
||||
conn, err := net.DialTimeout("unix", socketPath, w.dialTimeout)
|
||||
if err == nil {
|
||||
w.conn = conn
|
||||
w.log("INFO", "connected to %s", socketPath)
|
||||
} else {
|
||||
w.log("WARNING", "initial connection to %s failed: %v (will retry on write)", socketPath, err)
|
||||
}
|
||||
|
||||
return w, nil
|
||||
}
|
||||
|
||||
// log emits a log message if the level is enabled
|
||||
func (w *UnixSocketWriter) log(level, format string, args ...interface{}) {
|
||||
if !w.isLogLevelEnabled(level) {
|
||||
return
|
||||
}
|
||||
w.logger.Printf("[%s] UnixSocketWriter: "+format, append([]interface{}{level}, args...)...)
|
||||
}
|
||||
|
||||
// isLogLevelEnabled checks if a log level should be emitted
|
||||
func (w *UnixSocketWriter) isLogLevelEnabled(level string) bool {
|
||||
level = strings.ToLower(level)
|
||||
switch w.logLevel {
|
||||
case "debug":
|
||||
return true
|
||||
case "info":
|
||||
return level != "debug"
|
||||
case "warn", "warning":
|
||||
return level != "debug" && level != "info"
|
||||
case "error":
|
||||
return level == "error"
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// processQueue handles queued writes with reconnection logic
|
||||
func (w *UnixSocketWriter) processQueue() {
|
||||
defer close(w.queueDone)
|
||||
@ -317,12 +279,6 @@ func (w *UnixSocketWriter) processQueue() {
|
||||
}
|
||||
w.pendingMu.Unlock()
|
||||
|
||||
if consecutiveFailures >= w.maxReconnects {
|
||||
w.log("ERROR", "max reconnection attempts reached for %s (failures: %d)", w.socketPath, consecutiveFailures)
|
||||
} else if consecutiveFailures > 1 {
|
||||
w.log("WARNING", "write failed to %s: %v (attempt %d/%d)", w.socketPath, err, consecutiveFailures, w.maxReconnects)
|
||||
}
|
||||
|
||||
// Exponential backoff
|
||||
if consecutiveFailures > w.maxReconnects {
|
||||
time.Sleep(backoff)
|
||||
@ -332,7 +288,6 @@ func (w *UnixSocketWriter) processQueue() {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
w.log("DEBUG", "wrote %d bytes to %s", len(data), w.socketPath)
|
||||
consecutiveFailures = 0
|
||||
backoff = w.reconnectBackoff
|
||||
// Try to flush pending data
|
||||
@ -380,12 +335,10 @@ func (w *UnixSocketWriter) writeWithReconnect(data []byte) error {
|
||||
return fmt.Errorf("failed to connect to socket %s: %w", w.socketPath, err)
|
||||
}
|
||||
w.conn = conn
|
||||
w.log("INFO", "connected to %s", w.socketPath)
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := ensureConn(); err != nil {
|
||||
w.log("ERROR", "connection failed to %s: %v", w.socketPath, err)
|
||||
return err
|
||||
}
|
||||
|
||||
@ -398,7 +351,6 @@ func (w *UnixSocketWriter) writeWithReconnect(data []byte) error {
|
||||
}
|
||||
|
||||
// Connection failed, try to reconnect
|
||||
w.log("WARNING", "write failed, attempting reconnect to %s", w.socketPath)
|
||||
_ = w.conn.Close()
|
||||
w.conn = nil
|
||||
|
||||
@ -457,7 +409,6 @@ func (w *UnixSocketWriter) Close() error {
|
||||
|
||||
w.isClosed = true
|
||||
if w.conn != nil {
|
||||
w.log("INFO", "closing connection to %s", w.socketPath)
|
||||
w.conn.Close()
|
||||
w.conn = nil
|
||||
}
|
||||
@ -578,12 +529,7 @@ func (b *BuilderImpl) NewFromConfig(cfg api.AppConfig) (api.Writer, error) {
|
||||
if socketPath == "" {
|
||||
return nil, fmt.Errorf("unix_socket output requires 'socket_path' parameter")
|
||||
}
|
||||
// Get log level (default: error)
|
||||
logLevel := outputCfg.Params["log_level"]
|
||||
if logLevel == "" {
|
||||
logLevel = "error"
|
||||
}
|
||||
writer, err = NewUnixSocketWriterWithConfigAndLogLevel(socketPath, DefaultDialTimeout, DefaultWriteTimeout, queueSize, logLevel)
|
||||
writer, err = NewUnixSocketWriterWithConfig(socketPath, DefaultDialTimeout, DefaultWriteTimeout, queueSize)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
%if %{defined build_version}
|
||||
%define spec_version %{build_version}
|
||||
%else
|
||||
%define spec_version 1.1.0
|
||||
%define spec_version 1.1.1
|
||||
%endif
|
||||
|
||||
Name: ja4sentinel
|
||||
@ -31,7 +31,7 @@ Features:
|
||||
- TLS ClientHello extraction
|
||||
- JA4/JA3 fingerprint generation
|
||||
- IP/TCP metadata enrichment
|
||||
- Multiple output formats (stdout, file, UNIX socket)
|
||||
- Multiple output formats (UNIX socket by default, stdout, file)
|
||||
- Structured JSON logging for systemd/journald
|
||||
- Compatible with Rocky Linux 8/9/10, RHEL, AlmaLinux
|
||||
|
||||
@ -122,6 +122,12 @@ fi
|
||||
%dir /var/run/logcorrelator
|
||||
|
||||
%changelog
|
||||
* Mon Mar 02 2026 Jacquin Antoine <rpm@arkel.fr> - 1.1.1-1
|
||||
- Change default output from stdout to Unix socket (/var/run/logcorrelator/network.socket)
|
||||
- Update config.yml.example to enable unix_socket output by default
|
||||
- Comment out stdout output in default configuration
|
||||
- Unix socket output configured with log_level: error by default
|
||||
|
||||
* Mon Mar 02 2026 Jacquin Antoine <rpm@arkel.fr> - 1.1.0-1
|
||||
- Add logrotate configuration for automatic log file rotation
|
||||
- Add SIGHUP signal handling for log file reopening (systemctl reload)
|
||||
|
||||
Reference in New Issue
Block a user