eec8c28fb3
Go Benchmark / Performance Regression Check (push) Has been cancelled
Cerberus Integration / Cerberus Security Stack Integration (push) Has been cancelled
Upload Coverage to Codecov / Backend Codecov Upload (push) Has been cancelled
Upload Coverage to Codecov / Frontend Codecov Upload (push) Has been cancelled
CodeQL - Analyze / CodeQL analysis (go) (push) Has been cancelled
CodeQL - Analyze / CodeQL analysis (javascript-typescript) (push) Has been cancelled
CrowdSec Integration / CrowdSec Bouncer Integration (push) Has been cancelled
Docker Build, Publish & Test / build-and-push (push) Has been cancelled
Quality Checks / Auth Route Protection Contract (push) Has been cancelled
Quality Checks / Codecov Trigger/Comment Parity Guard (push) Has been cancelled
Quality Checks / Backend (Go) (push) Has been cancelled
Quality Checks / Frontend (React) (push) Has been cancelled
Rate Limit integration / Rate Limiting Integration (push) Has been cancelled
Security Scan (PR) / Trivy Binary Scan (push) Has been cancelled
Supply Chain Verification (PR) / Verify Supply Chain (push) Has been cancelled
WAF integration / Coraza WAF Integration (push) Has been cancelled
Docker Build, Publish & Test / Security Scan PR Image (push) Has been cancelled
Repo Health Check / Repo health (push) Has been cancelled
History Rewrite Dry-Run / Dry-run preview for history rewrite (push) Has been cancelled
Prune Renovate Branches / prune (push) Has been cancelled
Renovate / renovate (push) Has been cancelled
Nightly Build & Package / sync-development-to-nightly (push) Has been cancelled
Nightly Build & Package / Trigger Nightly Validation Workflows (push) Has been cancelled
Nightly Build & Package / build-and-push-nightly (push) Has been cancelled
Nightly Build & Package / test-nightly-image (push) Has been cancelled
Nightly Build & Package / verify-nightly-supply-chain (push) Has been cancelled
Update GeoLite2 Checksum / update-checksum (push) Has been cancelled
Container Registry Prune / prune-ghcr (push) Has been cancelled
Container Registry Prune / prune-dockerhub (push) Has been cancelled
Container Registry Prune / summarize (push) Has been cancelled
Supply Chain Verification / Verify SBOM (push) Has been cancelled
Supply Chain Verification / Verify Release Artifacts (push) Has been cancelled
Supply Chain Verification / Verify Docker Image Supply Chain (push) Has been cancelled
Monitor Caddy Major Release / check-caddy-major (push) Has been cancelled
Weekly Nightly to Main Promotion / Verify Nightly Branch Health (push) Has been cancelled
Weekly Nightly to Main Promotion / Create Promotion PR (push) Has been cancelled
Weekly Nightly to Main Promotion / Trigger Missing Required Checks (push) Has been cancelled
Weekly Nightly to Main Promotion / Notify on Failure (push) Has been cancelled
Weekly Nightly to Main Promotion / Workflow Summary (push) Has been cancelled
Weekly Security Rebuild / Security Rebuild & Scan (push) Has been cancelled
128 lines
3.1 KiB
Go
Executable File
128 lines
3.1 KiB
Go
Executable File
// Package logger provides logging functionality with broadcast capabilities for real-time log streaming.
|
|
package logger
|
|
|
|
import (
|
|
"io"
|
|
"os"
|
|
"sync"
|
|
|
|
"github.com/sirupsen/logrus"
|
|
)
|
|
|
|
var _log = logrus.New()
|
|
var _broadcastHook *BroadcastHook
|
|
|
|
// Init initializes the global logger with output writer and debug level.
|
|
func Init(debug bool, out io.Writer) {
|
|
if out == nil {
|
|
out = os.Stdout
|
|
}
|
|
_log.SetOutput(out)
|
|
if debug {
|
|
_log.SetLevel(logrus.DebugLevel)
|
|
_log.SetFormatter(&logrus.TextFormatter{FullTimestamp: true})
|
|
} else {
|
|
_log.SetLevel(logrus.InfoLevel)
|
|
_log.SetFormatter(&logrus.JSONFormatter{})
|
|
}
|
|
|
|
// Initialize and add broadcast hook
|
|
_broadcastHook = NewBroadcastHook()
|
|
_log.AddHook(_broadcastHook)
|
|
}
|
|
|
|
// Log returns a standard logger entry to use across packages.
|
|
func Log() *logrus.Entry {
|
|
return logrus.NewEntry(_log)
|
|
}
|
|
|
|
// WithFields returns a logger entry with provided fields.
|
|
func WithFields(fields logrus.Fields) *logrus.Entry {
|
|
return Log().WithFields(fields)
|
|
}
|
|
|
|
// GetBroadcastHook returns the global broadcast hook instance.
|
|
func GetBroadcastHook() *BroadcastHook {
|
|
if _broadcastHook == nil {
|
|
_broadcastHook = NewBroadcastHook()
|
|
_log.AddHook(_broadcastHook)
|
|
}
|
|
return _broadcastHook
|
|
}
|
|
|
|
// BroadcastHook implements logrus.Hook to broadcast log entries to active listeners.
|
|
type BroadcastHook struct {
|
|
mu sync.RWMutex
|
|
listeners map[string]chan *logrus.Entry
|
|
}
|
|
|
|
// NewBroadcastHook creates a new BroadcastHook instance.
|
|
func NewBroadcastHook() *BroadcastHook {
|
|
return &BroadcastHook{
|
|
listeners: make(map[string]chan *logrus.Entry),
|
|
}
|
|
}
|
|
|
|
// Levels returns all log levels that this hook should fire for.
|
|
func (h *BroadcastHook) Levels() []logrus.Level {
|
|
return logrus.AllLevels
|
|
}
|
|
|
|
// Fire broadcasts the log entry to all active listeners.
|
|
func (h *BroadcastHook) Fire(entry *logrus.Entry) error {
|
|
h.mu.RLock()
|
|
defer h.mu.RUnlock()
|
|
|
|
// Broadcast to all listeners (non-blocking)
|
|
for _, ch := range h.listeners {
|
|
select {
|
|
case ch <- entry:
|
|
default:
|
|
// Skip if channel is full (prevents blocking)
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Subscribe adds a new listener and returns a channel for receiving log entries.
|
|
// The caller must call Unsubscribe when done to prevent resource leaks.
|
|
func (h *BroadcastHook) Subscribe(id string) <-chan *logrus.Entry {
|
|
h.mu.Lock()
|
|
defer h.mu.Unlock()
|
|
|
|
ch := make(chan *logrus.Entry, 100) // Buffer to prevent blocking
|
|
h.listeners[id] = ch
|
|
return ch
|
|
}
|
|
|
|
// Unsubscribe removes a listener and closes its channel.
|
|
func (h *BroadcastHook) Unsubscribe(id string) {
|
|
h.mu.Lock()
|
|
defer h.mu.Unlock()
|
|
|
|
if ch, ok := h.listeners[id]; ok {
|
|
close(ch)
|
|
delete(h.listeners, id)
|
|
}
|
|
}
|
|
|
|
// ActiveListeners returns the count of active listeners.
|
|
func (h *BroadcastHook) ActiveListeners() int {
|
|
h.mu.RLock()
|
|
defer h.mu.RUnlock()
|
|
return len(h.listeners)
|
|
}
|
|
|
|
// ListenerIDs returns the IDs of all active listeners. Intended for tests/observability only.
|
|
func (h *BroadcastHook) ListenerIDs() []string {
|
|
h.mu.RLock()
|
|
defer h.mu.RUnlock()
|
|
|
|
ids := make([]string, 0, len(h.listeners))
|
|
for id := range h.listeners {
|
|
ids = append(ids, id)
|
|
}
|
|
|
|
return ids
|
|
}
|