// Package logging provides structured logging for ja4sentinel service components package logging import ( "encoding/json" "fmt" "log" "os" "strings" "sync" "time" "github.com/your-repo/ja4sentinel/api" ) // ServiceLogger handles structured logging for the ja4sentinel service type ServiceLogger struct { level string mutex sync.Mutex out *log.Logger formatter func(api.ServiceLog) ([]byte, error) } // NewServiceLogger creates a new service logger func NewServiceLogger(level string) *ServiceLogger { logger := &ServiceLogger{ level: strings.ToLower(level), out: log.New(os.Stdout, "", 0), formatter: func(s api.ServiceLog) ([]byte, error) { logData := map[string]interface{}{ "timestamp": time.Now().UnixNano(), "level": strings.ToUpper(s.Level), "component": s.Component, "message": s.Message, } if s.Details != nil && len(s.Details) > 0 { for k, v := range s.Details { logData[k] = v } } return json.Marshal(logData) }, } return logger } // Log emits a structured log entry to stdout in JSON format func (l *ServiceLogger) Log(component, level, message string, details map[string]string) { if !l.isLogLevelEnabled(level) { return } // Lock to prevent concurrent writes to stdout l.mutex.Lock() defer l.mutex.Unlock() serviceLog := api.ServiceLog{ Level: level, Component: component, Message: message, Details: details, } jsonData, err := l.formatter(serviceLog) if err != nil { // Fallback to simple logging if JSON formatting fails fmt.Printf(`{"timestamp":%d,"level":"ERROR","component":"logging","message":"%s","original_message":"%s"}`, time.Now().UnixNano(), err.Error(), message) return } fmt.Println(string(jsonData)) } // Debug logs a debug level entry func (l *ServiceLogger) Debug(component, message string, details map[string]string) { if l.isLogLevelEnabled("debug") { l.Log(component, "DEBUG", message, details) } } // Info logs an info level entry func (l *ServiceLogger) Info(component, message string, details map[string]string) { if l.isLogLevelEnabled("info") { l.Log(component, "INFO", message, details) } } // Warn logs a warning level entry func (l *ServiceLogger) Warn(component, message string, details map[string]string) { if l.isLogLevelEnabled("warn") { l.Log(component, "WARN", message, details) } } // Error logs an error level entry func (l *ServiceLogger) Error(component, message string, details map[string]string) { if l.isLogLevelEnabled("error") { l.Log(component, "ERROR", message, details) } } // isLogLevelEnabled checks if a log level should be emitted based on configured level func (l *ServiceLogger) isLogLevelEnabled(messageLevel string) bool { switch l.level { case "debug": return true case "info": return messageLevel != "debug" case "warn": return messageLevel != "debug" && messageLevel != "info" case "error": return messageLevel == "error" default: return false // If level is invalid, don't log anything } }