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

- 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:
Jacquin Antoine
2026-03-02 21:19:57 +01:00
parent 52c9f2f6f4
commit 6e5addd6d4
4 changed files with 24 additions and 70 deletions

View File

@ -7,7 +7,7 @@ project:
extraire les handshakes TLS côté client, générer les signatures JA4 extraire les handshakes TLS côté client, générer les signatures JA4
(via psanford/tlsfingerprint), enrichir avec des métadonnées IP/TCP, (via psanford/tlsfingerprint), enrichir avec des métadonnées IP/TCP,
et loguer les résultats (IP, ports, JA4, meta) vers une ou plusieurs 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. 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 La commande `systemctl reload ja4sentinel` permet de réouvrir les fichiers de log
après rotation (signal SIGHUP). après rotation (signal SIGHUP).

View File

@ -24,20 +24,22 @@ core:
log_level: info log_level: info
outputs: outputs:
# Output to stdout (JSON lines) # Output to UNIX socket (for systemd/journald or other consumers)
- type: stdout # Only JSON LogRecord data is sent - no diagnostic logs
- type: unix_socket
enabled: true 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 # Output to file
# Only JSON LogRecord data is sent - no diagnostic logs
# - type: file # - type: file
# enabled: false # enabled: false
# params: # params:
# path: /var/log/ja4sentinel/ja4.log # 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)

View File

@ -5,11 +5,9 @@ import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"io" "io"
"log"
"net" "net"
"os" "os"
"path/filepath" "path/filepath"
"strings"
"sync" "sync"
"time" "time"
@ -205,6 +203,7 @@ func (w *FileWriter) Reopen() error {
} }
// UnixSocketWriter writes log records to a UNIX socket with reconnection logic // 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 { type UnixSocketWriter struct {
socketPath string socketPath string
conn net.Conn conn net.Conn
@ -221,8 +220,6 @@ type UnixSocketWriter struct {
isClosed bool isClosed bool
pendingWrites [][]byte pendingWrites [][]byte
pendingMu sync.Mutex pendingMu sync.Mutex
logLevel string
logger *log.Logger
} }
// NewUnixSocketWriter creates a new UNIX socket writer with reconnection logic // 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 // 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) (*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{ w := &UnixSocketWriter{
socketPath: socketPath, socketPath: socketPath,
dialTimeout: dialTimeout, dialTimeout: dialTimeout,
@ -248,50 +240,20 @@ func NewUnixSocketWriterWithConfigAndLogLevel(socketPath string, dialTimeout, wr
queueClose: make(chan struct{}), queueClose: make(chan struct{}),
queueDone: make(chan struct{}), queueDone: make(chan struct{}),
pendingWrites: make([][]byte, 0), pendingWrites: make([][]byte, 0),
logLevel: strings.ToLower(logLevel),
logger: log.New(os.Stderr, "[UnixSocket] ", log.LstdFlags|log.Lmicroseconds),
} }
// Start the queue processor // Start the queue processor
go w.processQueue() 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) conn, err := net.DialTimeout("unix", socketPath, w.dialTimeout)
if err == nil { if err == nil {
w.conn = conn 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 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 // processQueue handles queued writes with reconnection logic
func (w *UnixSocketWriter) processQueue() { func (w *UnixSocketWriter) processQueue() {
defer close(w.queueDone) defer close(w.queueDone)
@ -317,12 +279,6 @@ func (w *UnixSocketWriter) processQueue() {
} }
w.pendingMu.Unlock() 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 // Exponential backoff
if consecutiveFailures > w.maxReconnects { if consecutiveFailures > w.maxReconnects {
time.Sleep(backoff) time.Sleep(backoff)
@ -332,7 +288,6 @@ func (w *UnixSocketWriter) processQueue() {
} }
} }
} else { } else {
w.log("DEBUG", "wrote %d bytes to %s", len(data), w.socketPath)
consecutiveFailures = 0 consecutiveFailures = 0
backoff = w.reconnectBackoff backoff = w.reconnectBackoff
// Try to flush pending data // 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) return fmt.Errorf("failed to connect to socket %s: %w", w.socketPath, err)
} }
w.conn = conn w.conn = conn
w.log("INFO", "connected to %s", w.socketPath)
return nil return nil
} }
if err := ensureConn(); err != nil { if err := ensureConn(); err != nil {
w.log("ERROR", "connection failed to %s: %v", w.socketPath, err)
return err return err
} }
@ -398,7 +351,6 @@ func (w *UnixSocketWriter) writeWithReconnect(data []byte) error {
} }
// Connection failed, try to reconnect // Connection failed, try to reconnect
w.log("WARNING", "write failed, attempting reconnect to %s", w.socketPath)
_ = w.conn.Close() _ = w.conn.Close()
w.conn = nil w.conn = nil
@ -457,7 +409,6 @@ func (w *UnixSocketWriter) Close() error {
w.isClosed = true w.isClosed = true
if w.conn != nil { if w.conn != nil {
w.log("INFO", "closing connection to %s", w.socketPath)
w.conn.Close() w.conn.Close()
w.conn = nil w.conn = nil
} }
@ -578,12 +529,7 @@ func (b *BuilderImpl) NewFromConfig(cfg api.AppConfig) (api.Writer, error) {
if socketPath == "" { if socketPath == "" {
return nil, fmt.Errorf("unix_socket output requires 'socket_path' parameter") return nil, fmt.Errorf("unix_socket output requires 'socket_path' parameter")
} }
// Get log level (default: error) writer, err = NewUnixSocketWriterWithConfig(socketPath, DefaultDialTimeout, DefaultWriteTimeout, queueSize)
logLevel := outputCfg.Params["log_level"]
if logLevel == "" {
logLevel = "error"
}
writer, err = NewUnixSocketWriterWithConfigAndLogLevel(socketPath, DefaultDialTimeout, DefaultWriteTimeout, queueSize, logLevel)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -3,7 +3,7 @@
%if %{defined build_version} %if %{defined build_version}
%define spec_version %{build_version} %define spec_version %{build_version}
%else %else
%define spec_version 1.1.0 %define spec_version 1.1.1
%endif %endif
Name: ja4sentinel Name: ja4sentinel
@ -31,7 +31,7 @@ Features:
- TLS ClientHello extraction - TLS ClientHello extraction
- JA4/JA3 fingerprint generation - JA4/JA3 fingerprint generation
- IP/TCP metadata enrichment - 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 - Structured JSON logging for systemd/journald
- Compatible with Rocky Linux 8/9/10, RHEL, AlmaLinux - Compatible with Rocky Linux 8/9/10, RHEL, AlmaLinux
@ -122,6 +122,12 @@ fi
%dir /var/run/logcorrelator %dir /var/run/logcorrelator
%changelog %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 * Mon Mar 02 2026 Jacquin Antoine <rpm@arkel.fr> - 1.1.0-1
- Add logrotate configuration for automatic log file rotation - Add logrotate configuration for automatic log file rotation
- Add SIGHUP signal handling for log file reopening (systemctl reload) - Add SIGHUP signal handling for log file reopening (systemctl reload)