v0.13.1 folder restructure, test script added, format adapter async fix

This commit is contained in:
2026-07-17 09:09:00 -04:00
parent 87e57784da
commit b8dd591b4b
49 changed files with 402 additions and 206 deletions
+212
View File
@@ -0,0 +1,212 @@
package service
import (
"context"
"errors"
"fmt"
"sync"
"logwisp/internal/config"
"logwisp/internal/pipeline"
"github.com/lixenwraith/log"
)
// Service manages a collection of log processing pipelines
type Service struct {
pipelines map[string]*pipeline.Pipeline
mu sync.RWMutex
ctx context.Context
cancel context.CancelFunc
wg sync.WaitGroup
logger *log.Logger
}
// NewService creates a new, empty service
func NewService(ctx context.Context, cfg *config.Config, logger *log.Logger) (*Service, error) {
serviceCtx, cancel := context.WithCancel(ctx)
svc := &Service{
pipelines: make(map[string]*pipeline.Pipeline),
ctx: serviceCtx,
cancel: cancel,
logger: logger,
}
var errs error
// Initialize pipelines
for _, pipelineCfg := range cfg.Pipelines {
pipelineName := pipelineCfg.Name
logger.Info("msg", "Initializing pipeline", "pipeline", pipelineName)
// Create the pipeline
if pl, err := pipeline.NewPipeline(&pipelineCfg, logger); err != nil {
logger.Error("msg", "Failed to create pipeline",
"pipeline", pipelineCfg.Name,
"error", err)
errs = errors.Join(errs, fmt.Errorf("failed to initialize pipeline %s: %w", pipelineName, err))
} else {
svc.pipelines[pipelineName] = pl
}
}
logger.Info("msg", "Service initialization completed", "pipelines", len(svc.pipelines))
return svc, errs
}
// Start starts all or specific pipelines
func (svc *Service) Start(names ...string) error {
svc.mu.RLock()
defer svc.mu.RUnlock()
var errs error
// If no names are provided, start all pipelines
if len(names) == 0 {
svc.logger.Info("msg", "Starting all pipelines")
for name, p := range svc.pipelines {
if err := p.Start(); err != nil {
errs = errors.Join(errs, fmt.Errorf("failed to start pipeline %s: %w", name, err))
}
}
} else {
// Start only the specified pipelines
svc.logger.Info("msg", "Starting specified pipelines", "pipelines", names)
for _, name := range names {
if p, exists := svc.pipelines[name]; exists {
if err := p.Start(); err != nil {
errs = errors.Join(errs, fmt.Errorf("failed to start pipeline %s: %w", name, err))
}
} else {
errs = errors.Join(errs, fmt.Errorf("pipeline %s not found", name))
}
}
}
svc.logger.Debug("msg", "Finished starting pipeline(s)", "pipelines", names)
return errs
}
// Stop stops all or specific pipeline
func (svc *Service) Stop(names ...string) error {
svc.mu.RLock()
defer svc.mu.RUnlock()
var errs error
// If no names are provided, stop all pipelines
if len(names) == 0 {
svc.logger.Info("msg", "Stopping all pipelines")
for name, p := range svc.pipelines {
if err := p.Stop(); err != nil {
errs = errors.Join(errs, fmt.Errorf("failed to stop pipeline %s: %w", name, err))
}
}
} else {
// Stop only the specified pipelines
svc.logger.Info("msg", "Stopping specified pipelines", "pipelines", names)
for _, name := range names {
if p, exists := svc.pipelines[name]; exists {
if err := p.Stop(); err != nil {
errs = errors.Join(errs, fmt.Errorf("failed to stop pipeline %s: %w", name, err))
}
} else {
errs = errors.Join(errs, fmt.Errorf("pipeline %s not found", name))
}
}
}
svc.logger.Debug("msg", "Finished stopping pipeline(s)", "pipelines", names)
return errs
}
// GetPipeline returns a pipeline by its name
func (svc *Service) GetPipeline(name string) (*pipeline.Pipeline, error) {
svc.mu.RLock()
defer svc.mu.RUnlock()
pipeline, exists := svc.pipelines[name]
if !exists {
return nil, fmt.Errorf("pipeline '%s' not found", name)
}
return pipeline, nil
}
// ListPipelines returns the names of all currently managed pipelines
func (svc *Service) ListPipelines() []string {
svc.mu.RLock()
defer svc.mu.RUnlock()
names := make([]string, 0, len(svc.pipelines))
for name := range svc.pipelines {
names = append(names, name)
}
return names
}
// RemovePipeline stops and removes a pipeline from the service
func (svc *Service) RemovePipeline(name string) error {
svc.mu.Lock()
defer svc.mu.Unlock()
pl, exists := svc.pipelines[name]
if !exists {
err := fmt.Errorf("pipeline '%s' not found", name)
svc.logger.Warn("msg", "Cannot remove non-existent pipeline",
"component", "service",
"pipeline", name,
"error", err)
return err
}
svc.logger.Info("msg", "Removing pipeline", "pipeline", name)
pl.Shutdown()
delete(svc.pipelines, name)
return nil
}
// Shutdown gracefully stops all pipelines managed by the service
func (svc *Service) Shutdown() {
svc.logger.Info("msg", "Service shutdown initiated")
svc.mu.Lock()
pipelines := make([]*pipeline.Pipeline, 0, len(svc.pipelines))
for _, pl := range svc.pipelines {
pipelines = append(pipelines, pl)
}
svc.mu.Unlock()
// Stop all pipelines concurrently
var wg sync.WaitGroup
for _, pl := range pipelines {
wg.Add(1)
go func(p *pipeline.Pipeline) {
defer wg.Done()
p.Shutdown()
}(pl)
}
wg.Wait()
svc.cancel()
svc.wg.Wait()
svc.logger.Info("msg", "Service shutdown complete")
}
// GetGlobalStats returns statistics for all pipelines
func (svc *Service) GetGlobalStats() map[string]any {
svc.mu.RLock()
defer svc.mu.RUnlock()
stats := map[string]any{
"pipelines": make(map[string]any),
"total_pipelines": len(svc.pipelines),
}
for name, pl := range svc.pipelines {
stats["pipelines"].(map[string]any)[name] = pl.GetStats()
}
return stats
}