210 lines
4.9 KiB
Go
210 lines
4.9 KiB
Go
// FILE: src/internal/service/logstream.go
|
|
package service
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"path/filepath"
|
|
"sync"
|
|
"time"
|
|
|
|
"logwisp/src/internal/config"
|
|
"logwisp/src/internal/filter"
|
|
"logwisp/src/internal/monitor"
|
|
"logwisp/src/internal/transport"
|
|
|
|
"github.com/lixenwraith/log"
|
|
)
|
|
|
|
type LogStream struct {
|
|
Name string
|
|
Config config.StreamConfig
|
|
Monitor monitor.Monitor
|
|
FilterChain *filter.Chain
|
|
TCPServer *transport.TCPStreamer
|
|
HTTPServer *transport.HTTPStreamer
|
|
Stats *StreamStats
|
|
logger *log.Logger
|
|
|
|
ctx context.Context
|
|
cancel context.CancelFunc
|
|
}
|
|
|
|
type StreamStats struct {
|
|
StartTime time.Time
|
|
MonitorStats monitor.Stats
|
|
TCPConnections int32
|
|
HTTPConnections int32
|
|
TotalBytesServed uint64
|
|
TotalEntriesServed uint64
|
|
FilterStats map[string]any
|
|
}
|
|
|
|
func (ls *LogStream) Shutdown() {
|
|
ls.logger.Info("msg", "Shutting down stream",
|
|
"component", "logstream",
|
|
"stream", ls.Name)
|
|
|
|
// Stop servers first
|
|
var wg sync.WaitGroup
|
|
|
|
if ls.TCPServer != nil {
|
|
wg.Add(1)
|
|
go func() {
|
|
defer wg.Done()
|
|
ls.TCPServer.Stop()
|
|
}()
|
|
}
|
|
|
|
if ls.HTTPServer != nil {
|
|
wg.Add(1)
|
|
go func() {
|
|
defer wg.Done()
|
|
ls.HTTPServer.Stop()
|
|
}()
|
|
}
|
|
|
|
// Cancel context
|
|
ls.cancel()
|
|
|
|
// Wait for servers
|
|
wg.Wait()
|
|
|
|
// Stop monitor
|
|
ls.Monitor.Stop()
|
|
|
|
ls.logger.Info("msg", "Stream shutdown complete",
|
|
"component", "logstream",
|
|
"stream", ls.Name)
|
|
}
|
|
|
|
func (ls *LogStream) GetStats() map[string]any {
|
|
monStats := ls.Monitor.GetStats()
|
|
|
|
stats := map[string]any{
|
|
"name": ls.Name,
|
|
"uptime_seconds": int(time.Since(ls.Stats.StartTime).Seconds()),
|
|
"monitor": monStats,
|
|
}
|
|
|
|
if ls.FilterChain != nil {
|
|
stats["filters"] = ls.FilterChain.GetStats()
|
|
}
|
|
|
|
if ls.TCPServer != nil {
|
|
currentConnections := ls.TCPServer.GetActiveConnections()
|
|
|
|
stats["tcp"] = map[string]interface{}{
|
|
"enabled": true,
|
|
"port": ls.Config.TCPServer.Port,
|
|
"connections": currentConnections,
|
|
}
|
|
}
|
|
|
|
if ls.HTTPServer != nil {
|
|
currentConnections := ls.HTTPServer.GetActiveConnections()
|
|
|
|
stats["http"] = map[string]interface{}{
|
|
"enabled": true,
|
|
"port": ls.Config.HTTPServer.Port,
|
|
"connections": currentConnections,
|
|
"stream_path": ls.Config.HTTPServer.StreamPath,
|
|
"status_path": ls.Config.HTTPServer.StatusPath,
|
|
}
|
|
}
|
|
|
|
return stats
|
|
}
|
|
|
|
func (ls *LogStream) UpdateTargets(targets []config.MonitorTarget) error {
|
|
// Validate new targets first
|
|
validatedTargets := make([]config.MonitorTarget, 0, len(targets))
|
|
for _, target := range targets {
|
|
// Basic validation
|
|
absPath, err := filepath.Abs(target.Path)
|
|
if err != nil {
|
|
ls.logger.Error("msg", "Invalid target path",
|
|
"component", "logstream",
|
|
"stream", ls.Name,
|
|
"path", target.Path,
|
|
"error", err)
|
|
return fmt.Errorf("invalid target path %s: %w", target.Path, err)
|
|
}
|
|
target.Path = absPath
|
|
validatedTargets = append(validatedTargets, target)
|
|
}
|
|
|
|
// Get current watchers
|
|
oldWatchers := ls.Monitor.GetActiveWatchers()
|
|
|
|
// Add new targets
|
|
for _, target := range validatedTargets {
|
|
if err := ls.Monitor.AddTarget(target.Path, target.Pattern, target.IsFile); err != nil {
|
|
ls.logger.Error("msg", "Failed to add monitor target - rolling back",
|
|
"component", "logstream",
|
|
"stream", ls.Name,
|
|
"target", target.Path,
|
|
"pattern", target.Pattern,
|
|
"error", err)
|
|
// Rollback: restore old watchers
|
|
for _, watcher := range oldWatchers {
|
|
// Best effort restoration
|
|
ls.Monitor.AddTarget(watcher.Path, "", false)
|
|
}
|
|
return fmt.Errorf("failed to add target %s: %w", target.Path, err)
|
|
}
|
|
}
|
|
|
|
// Only remove old targets after new ones are successfully added
|
|
for _, watcher := range oldWatchers {
|
|
ls.Monitor.RemoveTarget(watcher.Path)
|
|
}
|
|
|
|
ls.logger.Info("msg", "Updated monitor targets",
|
|
"component", "logstream",
|
|
"stream", ls.Name,
|
|
"old_count", len(oldWatchers),
|
|
"new_count", len(validatedTargets))
|
|
|
|
return nil
|
|
}
|
|
|
|
func (ls *LogStream) startStatsUpdater(ctx context.Context) {
|
|
go func() {
|
|
ticker := time.NewTicker(1 * time.Second)
|
|
defer ticker.Stop()
|
|
|
|
for {
|
|
select {
|
|
case <-ctx.Done():
|
|
return
|
|
case <-ticker.C:
|
|
// Update cached values
|
|
if ls.TCPServer != nil {
|
|
oldTCP := ls.Stats.TCPConnections
|
|
ls.Stats.TCPConnections = ls.TCPServer.GetActiveConnections()
|
|
if oldTCP != ls.Stats.TCPConnections {
|
|
// This debug should now show changes
|
|
ls.logger.Debug("msg", "TCP connection count changed",
|
|
"component", "logstream",
|
|
"stream", ls.Name,
|
|
"old", oldTCP,
|
|
"new", ls.Stats.TCPConnections)
|
|
}
|
|
}
|
|
if ls.HTTPServer != nil {
|
|
oldHTTP := ls.Stats.HTTPConnections
|
|
ls.Stats.HTTPConnections = ls.HTTPServer.GetActiveConnections()
|
|
if oldHTTP != ls.Stats.HTTPConnections {
|
|
// This debug should now show changes
|
|
ls.logger.Debug("msg", "HTTP connection count changed",
|
|
"component", "logstream",
|
|
"stream", ls.Name,
|
|
"old", oldHTTP,
|
|
"new", ls.Stats.HTTPConnections)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}()
|
|
} |