packageloggerimport("fmt""honeypot/internal/database""honeypot/internal/types""io""log/slog""net/http""os""path/filepath""time""github.com/prometheus/client_golang/prometheus")// MetricsCollector is an interface for recording metrics from events.typeMetricsCollectorinterface{RecordEvent(etypes.LogEvent)GetHandler()http.HandlerSetDatabase(db*database.Database)}// EventSink is an optional sink for streaming honeypot events to external// consumers (for example, the internal websocket dashboard).typeEventSinkinterface{EmitEvent(etypes.LogEvent)}// TopNFieldRegistrar is an interface for registering fields for top-N tracking.typeTopNFieldRegistrarinterface{RegisterTopNField(honeypotType,fieldNamestring)}// RegisterTopNField registers a field for top-N tracking for a specific honeypot type.funcRegisterTopNField(honeypotType,fieldNamestring){ifglobalMetricsCollector!=nil{ifregistrar,ok:=globalMetricsCollector.(TopNFieldRegistrar);ok{registrar.RegisterTopNField(honeypotType,fieldName)}}}// RegisterCollector registers a Prometheus collector for metrics export.// This allows honeypots to register their own custom metrics.funcRegisterCollector(cprometheus.Collector)error{returnprometheus.Register(c)}varglobalMetricsCollectorMetricsCollectorvarglobalEventSinkEventSinkvarglobalDatabasedatabase.Database// Setup configures and returns a JSON logger with optional metrics collector.// maxLogSize is the maximum log file size in bytes before rotation (0 = no rotation, uses regular file).funcSetup(logFilestring)(*slog.Logger,error){varoutputio.Writer=os.StdoutiflogFile!=""{// Create the log file directory if it doesn't existdir:=filepath.Dir(logFile)if_,err:=os.Stat(dir);os.IsNotExist(err){iferr:=os.MkdirAll(dir,0700);err!=nil{returnnil,err}}// Create the log file if it doesn't exist, and open for appendingf,err:=os.OpenFile(logFile,os.O_CREATE|os.O_APPEND|os.O_WRONLY,0600)iferr!=nil{returnnil,err}output=f}handler:=slog.NewJSONHandler(output,&slog.HandlerOptions{Level:slog.LevelInfo,})logger:=slog.New(handler)slog.SetDefault(logger)returnlogger,nil}funcRegisterMetricsCollector(metricsMetricsCollector){globalMetricsCollector=metrics}funcRegisterDatabase(database*database.Database){globalDatabase=*database}// RegisterEventSink sets the global sink used for streaming honeypot events.// It is safe to call this during startup before events are logged.funcRegisterEventSink(sinkEventSink){globalEventSink=sink}// LogEvent logs a standardized honeypot event in JSON format.funcLogEvent(logger*slog.Logger,etypes.LogEvent){attrs:=[]any{"type",e.Type,"event",e.Event,}ife.RemoteAddr!=""{attrs=append(attrs,"remote_addr",e.RemoteAddr)}ife.RemotePort!=0{attrs=append(attrs,"remote_port",e.RemotePort)}ife.DstPort!=0{attrs=append(attrs,"dst_port",e.DstPort)}// Add all additional fieldsfork,v:=rangee.Fields{attrs=append(attrs,k,v)}logger.Info("honeypot_event",attrs...)// Record event in metrics if collector is availableifglobalMetricsCollector!=nil{globalMetricsCollector.RecordEvent(e)}e.Time=time.Now().UTC().Format(time.RFC3339Nano)// Stream event to external sink if configured.ifglobalEventSink!=nil{globalEventSink.EmitEvent(e)}// Record event in database if database is availableifglobalDatabase.DB!=nil{err:=globalDatabase.InsertEvent(&e)iferr!=nil{logger.Error("failed to insert event into database","error",err)}}}// LogError logs an error event.funcLogError(logger*slog.Logger,honeypotTypetypes.HoneypotType,eventstring,errerror,args[]any){attrs:=[]any{"event",event,"error",err,}attrs=append(attrs,args...)logger.Error("honeypot_error",attrs...)fmt.Fprintf(os.Stderr,"%s: %s: %v %v\n","honeypot_error",event,err,args)}// LogInfo logs an informational message with honeypot context.funcLogInfo(logger*slog.Logger,honeypotTypetypes.HoneypotType,eventstring,args[]any){attrs:=[]any{"type",honeypotType,"event",event,}attrs=append(attrs,args...)logger.Info("honeypot_info",attrs...)fmt.Fprintf(os.Stdout,"%s: %s: %v\n","honeypot_info",event,args)}