Implements comprehensive Leader-coordinated contextual intelligence system for BZZZ: • Core SLURP Architecture (pkg/slurp/): - Context types with bounded hierarchical resolution - Intelligence engine with multi-language analysis - Encrypted storage with multi-tier caching - DHT-based distribution network - Decision temporal graph (decision-hop analysis) - Role-based access control and encryption • Leader Election Integration: - Project Manager role for elected BZZZ Leader - Context generation coordination - Failover and state management • Enterprise Security: - Role-based encryption with 5 access levels - Comprehensive audit logging - TLS encryption with mutual authentication - Key management with rotation • Production Infrastructure: - Docker and Kubernetes deployment manifests - Prometheus monitoring and Grafana dashboards - Comprehensive testing suites - Performance optimization and caching • Key Features: - Leader-only context generation for consistency - Role-specific encrypted context delivery - Decision influence tracking (not time-based) - 85%+ storage efficiency through hierarchy - Sub-10ms context resolution latency System provides AI agents with rich contextual understanding of codebases while maintaining strict security boundaries and enterprise-grade operations. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
513 lines
12 KiB
Go
513 lines
12 KiB
Go
package leader
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"log"
|
|
"os"
|
|
"strings"
|
|
"sync"
|
|
"time"
|
|
)
|
|
|
|
// LogLevel represents different logging levels
|
|
type LogLevel int
|
|
|
|
const (
|
|
LogLevelDebug LogLevel = iota
|
|
LogLevelInfo
|
|
LogLevelWarn
|
|
LogLevelError
|
|
LogLevelCritical
|
|
)
|
|
|
|
// String returns string representation of log level
|
|
func (ll LogLevel) String() string {
|
|
switch ll {
|
|
case LogLevelDebug:
|
|
return "DEBUG"
|
|
case LogLevelInfo:
|
|
return "INFO"
|
|
case LogLevelWarn:
|
|
return "WARN"
|
|
case LogLevelError:
|
|
return "ERROR"
|
|
case LogLevelCritical:
|
|
return "CRITICAL"
|
|
default:
|
|
return "UNKNOWN"
|
|
}
|
|
}
|
|
|
|
// ContextLogger provides structured logging for context operations
|
|
type ContextLogger struct {
|
|
mu sync.RWMutex
|
|
level LogLevel
|
|
outputs []LogOutput
|
|
fields map[string]interface{}
|
|
nodeID string
|
|
component string
|
|
}
|
|
|
|
// LogOutput represents a logging output destination
|
|
type LogOutput interface {
|
|
Write(entry *LogEntry) error
|
|
Close() error
|
|
}
|
|
|
|
// LogEntry represents a single log entry
|
|
type LogEntry struct {
|
|
Timestamp time.Time `json:"timestamp"`
|
|
Level LogLevel `json:"level"`
|
|
Message string `json:"message"`
|
|
Component string `json:"component"`
|
|
NodeID string `json:"node_id"`
|
|
Fields map[string]interface{} `json:"fields"`
|
|
Context map[string]string `json:"context,omitempty"`
|
|
RequestID string `json:"request_id,omitempty"`
|
|
JobID string `json:"job_id,omitempty"`
|
|
ElectionTerm int64 `json:"election_term,omitempty"`
|
|
StackTrace string `json:"stack_trace,omitempty"`
|
|
}
|
|
|
|
// NewContextLogger creates a new context logger
|
|
func NewContextLogger(nodeID, component string, level LogLevel) *ContextLogger {
|
|
logger := &ContextLogger{
|
|
level: level,
|
|
fields: make(map[string]interface{}),
|
|
nodeID: nodeID,
|
|
component: component,
|
|
outputs: make([]LogOutput, 0),
|
|
}
|
|
|
|
// Add default console output
|
|
logger.AddOutput(NewConsoleOutput())
|
|
|
|
return logger
|
|
}
|
|
|
|
// SetLevel sets the logging level
|
|
func (cl *ContextLogger) SetLevel(level LogLevel) {
|
|
cl.mu.Lock()
|
|
defer cl.mu.Unlock()
|
|
cl.level = level
|
|
}
|
|
|
|
// AddOutput adds a log output destination
|
|
func (cl *ContextLogger) AddOutput(output LogOutput) {
|
|
cl.mu.Lock()
|
|
defer cl.mu.Unlock()
|
|
cl.outputs = append(cl.outputs, output)
|
|
}
|
|
|
|
// WithField adds a field to all subsequent log entries
|
|
func (cl *ContextLogger) WithField(key string, value interface{}) *ContextLogger {
|
|
cl.mu.Lock()
|
|
defer cl.mu.Unlock()
|
|
|
|
newLogger := &ContextLogger{
|
|
level: cl.level,
|
|
fields: make(map[string]interface{}),
|
|
nodeID: cl.nodeID,
|
|
component: cl.component,
|
|
outputs: cl.outputs,
|
|
}
|
|
|
|
// Copy existing fields
|
|
for k, v := range cl.fields {
|
|
newLogger.fields[k] = v
|
|
}
|
|
|
|
// Add new field
|
|
newLogger.fields[key] = value
|
|
|
|
return newLogger
|
|
}
|
|
|
|
// WithFields adds multiple fields to all subsequent log entries
|
|
func (cl *ContextLogger) WithFields(fields map[string]interface{}) *ContextLogger {
|
|
cl.mu.Lock()
|
|
defer cl.mu.Unlock()
|
|
|
|
newLogger := &ContextLogger{
|
|
level: cl.level,
|
|
fields: make(map[string]interface{}),
|
|
nodeID: cl.nodeID,
|
|
component: cl.component,
|
|
outputs: cl.outputs,
|
|
}
|
|
|
|
// Copy existing fields
|
|
for k, v := range cl.fields {
|
|
newLogger.fields[k] = v
|
|
}
|
|
|
|
// Add new fields
|
|
for k, v := range fields {
|
|
newLogger.fields[k] = v
|
|
}
|
|
|
|
return newLogger
|
|
}
|
|
|
|
// WithContext creates a logger with context information
|
|
func (cl *ContextLogger) WithContext(ctx context.Context) *ContextLogger {
|
|
// Extract context values if present
|
|
fields := make(map[string]interface{})
|
|
|
|
if requestID := ctx.Value("request_id"); requestID != nil {
|
|
fields["request_id"] = requestID
|
|
}
|
|
if jobID := ctx.Value("job_id"); jobID != nil {
|
|
fields["job_id"] = jobID
|
|
}
|
|
if term := ctx.Value("election_term"); term != nil {
|
|
fields["election_term"] = term
|
|
}
|
|
|
|
return cl.WithFields(fields)
|
|
}
|
|
|
|
// Debug logs a debug message
|
|
func (cl *ContextLogger) Debug(message string, args ...interface{}) {
|
|
cl.log(LogLevelDebug, message, args...)
|
|
}
|
|
|
|
// Info logs an info message
|
|
func (cl *ContextLogger) Info(message string, args ...interface{}) {
|
|
cl.log(LogLevelInfo, message, args...)
|
|
}
|
|
|
|
// Warn logs a warning message
|
|
func (cl *ContextLogger) Warn(message string, args ...interface{}) {
|
|
cl.log(LogLevelWarn, message, args...)
|
|
}
|
|
|
|
// Error logs an error message
|
|
func (cl *ContextLogger) Error(message string, args ...interface{}) {
|
|
cl.log(LogLevelError, message, args...)
|
|
}
|
|
|
|
// Critical logs a critical message
|
|
func (cl *ContextLogger) Critical(message string, args ...interface{}) {
|
|
cl.log(LogLevelCritical, message, args...)
|
|
}
|
|
|
|
// LogContextGeneration logs context generation events
|
|
func (cl *ContextLogger) LogContextGeneration(event string, req *ContextGenerationRequest, job *ContextGenerationJob, err error) {
|
|
fields := map[string]interface{}{
|
|
"event": event,
|
|
}
|
|
|
|
if req != nil {
|
|
fields["request_id"] = req.ID
|
|
fields["ucxl_address"] = req.UCXLAddress.String()
|
|
fields["file_path"] = req.FilePath
|
|
fields["role"] = req.Role
|
|
fields["priority"] = req.Priority.String()
|
|
fields["requested_by"] = req.RequestedBy
|
|
}
|
|
|
|
if job != nil {
|
|
fields["job_id"] = job.ID
|
|
fields["job_status"] = job.Status
|
|
fields["started_at"] = job.StartedAt
|
|
if job.CompletedAt != nil {
|
|
fields["completed_at"] = *job.CompletedAt
|
|
fields["duration"] = job.CompletedAt.Sub(job.StartedAt)
|
|
}
|
|
fields["progress"] = job.Progress
|
|
fields["node_id"] = job.NodeID
|
|
}
|
|
|
|
logger := cl.WithFields(fields)
|
|
|
|
if err != nil {
|
|
logger.Error("Context generation event: %s - Error: %v", event, err)
|
|
} else {
|
|
logger.Info("Context generation event: %s", event)
|
|
}
|
|
}
|
|
|
|
// LogLeadershipChange logs leadership change events
|
|
func (cl *ContextLogger) LogLeadershipChange(event, oldLeader, newLeader string, term int64, metadata map[string]interface{}) {
|
|
fields := map[string]interface{}{
|
|
"event": event,
|
|
"old_leader": oldLeader,
|
|
"new_leader": newLeader,
|
|
"term": term,
|
|
}
|
|
|
|
// Add metadata
|
|
for k, v := range metadata {
|
|
fields[k] = v
|
|
}
|
|
|
|
logger := cl.WithFields(fields)
|
|
logger.Info("Leadership change: %s", event)
|
|
}
|
|
|
|
// LogElectionEvent logs election-related events
|
|
func (cl *ContextLogger) LogElectionEvent(event string, term int64, candidates []string, winner string, metadata map[string]interface{}) {
|
|
fields := map[string]interface{}{
|
|
"event": event,
|
|
"term": term,
|
|
"candidates": candidates,
|
|
"winner": winner,
|
|
}
|
|
|
|
// Add metadata
|
|
for k, v := range metadata {
|
|
fields[k] = v
|
|
}
|
|
|
|
logger := cl.WithFields(fields)
|
|
logger.Info("Election event: %s", event)
|
|
}
|
|
|
|
// LogFailoverEvent logs failover events
|
|
func (cl *ContextLogger) LogFailoverEvent(event, oldLeader, newLeader string, duration time.Duration, success bool, issues []string) {
|
|
fields := map[string]interface{}{
|
|
"event": event,
|
|
"old_leader": oldLeader,
|
|
"new_leader": newLeader,
|
|
"duration": duration,
|
|
"success": success,
|
|
"issues": issues,
|
|
}
|
|
|
|
logger := cl.WithFields(fields)
|
|
|
|
if success {
|
|
logger.Info("Failover event: %s", event)
|
|
} else {
|
|
logger.Error("Failover event: %s - Failed with issues: %v", event, issues)
|
|
}
|
|
}
|
|
|
|
// LogHealthEvent logs health monitoring events
|
|
func (cl *ContextLogger) LogHealthEvent(event string, nodeID string, healthScore float64, status HealthStatus, issues []string) {
|
|
fields := map[string]interface{}{
|
|
"event": event,
|
|
"node_id": nodeID,
|
|
"health_score": healthScore,
|
|
"status": status,
|
|
"issues": issues,
|
|
}
|
|
|
|
logger := cl.WithFields(fields)
|
|
|
|
switch status {
|
|
case HealthStatusHealthy:
|
|
logger.Debug("Health event: %s", event)
|
|
case HealthStatusDegraded:
|
|
logger.Warn("Health event: %s - Node degraded", event)
|
|
case HealthStatusUnhealthy:
|
|
logger.Error("Health event: %s - Node unhealthy: %v", event, issues)
|
|
case HealthStatusCritical:
|
|
logger.Critical("Health event: %s - Node critical: %v", event, issues)
|
|
}
|
|
}
|
|
|
|
// LogMetrics logs metrics information
|
|
func (cl *ContextLogger) LogMetrics(metrics *ContextMetrics) {
|
|
fields := map[string]interface{}{
|
|
"uptime": metrics.Uptime,
|
|
"total_requests": metrics.TotalRequests,
|
|
"success_rate": metrics.SuccessRate,
|
|
"throughput": metrics.Throughput,
|
|
"average_latency": metrics.AverageLatency,
|
|
"queue_length": metrics.MaxQueueLength,
|
|
"leadership_changes": metrics.LeadershipChanges,
|
|
}
|
|
|
|
logger := cl.WithFields(fields)
|
|
logger.Debug("Context generation metrics")
|
|
}
|
|
|
|
// log is the internal logging method
|
|
func (cl *ContextLogger) log(level LogLevel, message string, args ...interface{}) {
|
|
cl.mu.RLock()
|
|
defer cl.mu.RUnlock()
|
|
|
|
// Check if level is enabled
|
|
if level < cl.level {
|
|
return
|
|
}
|
|
|
|
// Format message
|
|
formattedMessage := message
|
|
if len(args) > 0 {
|
|
formattedMessage = fmt.Sprintf(message, args...)
|
|
}
|
|
|
|
// Create log entry
|
|
entry := &LogEntry{
|
|
Timestamp: time.Now(),
|
|
Level: level,
|
|
Message: formattedMessage,
|
|
Component: cl.component,
|
|
NodeID: cl.nodeID,
|
|
Fields: make(map[string]interface{}),
|
|
}
|
|
|
|
// Copy fields
|
|
for k, v := range cl.fields {
|
|
entry.Fields[k] = v
|
|
}
|
|
|
|
// Write to all outputs
|
|
for _, output := range cl.outputs {
|
|
if err := output.Write(entry); err != nil {
|
|
// Fallback to standard log if output fails
|
|
log.Printf("Failed to write log entry: %v", err)
|
|
}
|
|
}
|
|
}
|
|
|
|
// Close closes all log outputs
|
|
func (cl *ContextLogger) Close() error {
|
|
cl.mu.Lock()
|
|
defer cl.mu.Unlock()
|
|
|
|
var errors []string
|
|
for _, output := range cl.outputs {
|
|
if err := output.Close(); err != nil {
|
|
errors = append(errors, err.Error())
|
|
}
|
|
}
|
|
|
|
if len(errors) > 0 {
|
|
return fmt.Errorf("errors closing log outputs: %s", strings.Join(errors, ", "))
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// ConsoleOutput writes logs to console
|
|
type ConsoleOutput struct {
|
|
colorize bool
|
|
}
|
|
|
|
// NewConsoleOutput creates a new console output
|
|
func NewConsoleOutput() *ConsoleOutput {
|
|
return &ConsoleOutput{
|
|
colorize: true, // TODO: Detect if terminal supports colors
|
|
}
|
|
}
|
|
|
|
// Write writes a log entry to console
|
|
func (co *ConsoleOutput) Write(entry *LogEntry) error {
|
|
var levelPrefix string
|
|
if co.colorize {
|
|
switch entry.Level {
|
|
case LogLevelDebug:
|
|
levelPrefix = "\033[36mDEBUG\033[0m" // Cyan
|
|
case LogLevelInfo:
|
|
levelPrefix = "\033[32mINFO\033[0m" // Green
|
|
case LogLevelWarn:
|
|
levelPrefix = "\033[33mWARN\033[0m" // Yellow
|
|
case LogLevelError:
|
|
levelPrefix = "\033[31mERROR\033[0m" // Red
|
|
case LogLevelCritical:
|
|
levelPrefix = "\033[35mCRIT\033[0m" // Magenta
|
|
}
|
|
} else {
|
|
levelPrefix = entry.Level.String()
|
|
}
|
|
|
|
timestamp := entry.Timestamp.Format("2006-01-02 15:04:05.000")
|
|
|
|
// Format basic log line
|
|
logLine := fmt.Sprintf("%s [%s] [%s:%s] %s",
|
|
timestamp,
|
|
levelPrefix,
|
|
entry.Component,
|
|
entry.NodeID,
|
|
entry.Message,
|
|
)
|
|
|
|
// Add fields if any
|
|
if len(entry.Fields) > 0 {
|
|
if fieldsJSON, err := json.Marshal(entry.Fields); err == nil {
|
|
logLine += fmt.Sprintf(" | %s", string(fieldsJSON))
|
|
}
|
|
}
|
|
|
|
fmt.Println(logLine)
|
|
return nil
|
|
}
|
|
|
|
// Close closes the console output (no-op)
|
|
func (co *ConsoleOutput) Close() error {
|
|
return nil
|
|
}
|
|
|
|
// FileOutput writes logs to a file
|
|
type FileOutput struct {
|
|
mu sync.Mutex
|
|
file *os.File
|
|
filename string
|
|
}
|
|
|
|
// NewFileOutput creates a new file output
|
|
func NewFileOutput(filename string) (*FileOutput, error) {
|
|
file, err := os.OpenFile(filename, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &FileOutput{
|
|
file: file,
|
|
filename: filename,
|
|
}, nil
|
|
}
|
|
|
|
// Write writes a log entry to file
|
|
func (fo *FileOutput) Write(entry *LogEntry) error {
|
|
fo.mu.Lock()
|
|
defer fo.mu.Unlock()
|
|
|
|
// Convert to JSON
|
|
entryJSON, err := json.Marshal(entry)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Write to file with newline
|
|
_, err = fo.file.Write(append(entryJSON, '\n'))
|
|
return err
|
|
}
|
|
|
|
// Close closes the file output
|
|
func (fo *FileOutput) Close() error {
|
|
fo.mu.Lock()
|
|
defer fo.mu.Unlock()
|
|
|
|
if fo.file != nil {
|
|
err := fo.file.Close()
|
|
fo.file = nil
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// Priority extension for logging
|
|
func (p Priority) String() string {
|
|
switch p {
|
|
case PriorityLow:
|
|
return "low"
|
|
case PriorityNormal:
|
|
return "normal"
|
|
case PriorityHigh:
|
|
return "high"
|
|
case PriorityCritical:
|
|
return "critical"
|
|
case PriorityUrgent:
|
|
return "urgent"
|
|
default:
|
|
return "unknown"
|
|
}
|
|
} |