v0.22.1 cli flag and behavior, and test output improvement
This commit is contained in:
1 parent
71d1b82f83
commit
4eba6fc834
89 files changed
+1531
-661
No files matched your search
+4
-4
@@ -18,10 +18,10 @@ import (
|
||||
"strings"
|
||||
"syscall"
|
||||
|
||||
"logwisp/internal/authz"
|
||||
"logwisp/internal/config"
|
||||
"logwisp/internal/core"
|
||||
"logwisp/internal/tlsx"
|
||||
"github.com/lixenwraith/logwisp/internal/authz"
|
||||
"github.com/lixenwraith/logwisp/internal/config"
|
||||
"github.com/lixenwraith/logwisp/internal/core"
|
||||
"github.com/lixenwraith/logwisp/internal/tlsx"
|
||||
|
||||
"github.com/lixenwraith/auth"
|
||||
)
|
||||
|
||||
+1
-1
@@ -8,7 +8,7 @@ import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"logwisp/internal/authz"
|
||||
"github.com/lixenwraith/logwisp/internal/authz"
|
||||
|
||||
"github.com/lixenwraith/auth"
|
||||
)
|
||||
|
||||
+22
-17
@@ -2,27 +2,28 @@ package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
_ "logwisp/internal/source/console"
|
||||
_ "logwisp/internal/source/file"
|
||||
_ "logwisp/internal/source/httpchain"
|
||||
_ "logwisp/internal/source/null"
|
||||
_ "logwisp/internal/source/random"
|
||||
_ "logwisp/internal/source/tcpchain"
|
||||
_ "github.com/lixenwraith/logwisp/internal/source/console"
|
||||
_ "github.com/lixenwraith/logwisp/internal/source/file"
|
||||
_ "github.com/lixenwraith/logwisp/internal/source/httpchain"
|
||||
_ "github.com/lixenwraith/logwisp/internal/source/null"
|
||||
_ "github.com/lixenwraith/logwisp/internal/source/random"
|
||||
_ "github.com/lixenwraith/logwisp/internal/source/tcpchain"
|
||||
|
||||
_ "logwisp/internal/sink/console"
|
||||
_ "logwisp/internal/sink/file"
|
||||
_ "logwisp/internal/sink/http"
|
||||
_ "logwisp/internal/sink/httpchain"
|
||||
_ "logwisp/internal/sink/null"
|
||||
_ "logwisp/internal/sink/tcp"
|
||||
_ "logwisp/internal/sink/tcpchain"
|
||||
_ "github.com/lixenwraith/logwisp/internal/sink/console"
|
||||
_ "github.com/lixenwraith/logwisp/internal/sink/file"
|
||||
_ "github.com/lixenwraith/logwisp/internal/sink/http"
|
||||
_ "github.com/lixenwraith/logwisp/internal/sink/httpchain"
|
||||
_ "github.com/lixenwraith/logwisp/internal/sink/null"
|
||||
_ "github.com/lixenwraith/logwisp/internal/sink/tcp"
|
||||
_ "github.com/lixenwraith/logwisp/internal/sink/tcpchain"
|
||||
|
||||
"logwisp/internal/config"
|
||||
"logwisp/internal/service"
|
||||
"logwisp/internal/version"
|
||||
"github.com/lixenwraith/logwisp/internal/config"
|
||||
"github.com/lixenwraith/logwisp/internal/service"
|
||||
"github.com/lixenwraith/logwisp/internal/version"
|
||||
|
||||
"github.com/lixenwraith/log"
|
||||
"github.com/lixenwraith/log/sanitizer"
|
||||
@@ -48,6 +49,10 @@ func bootstrapInitial(ctx context.Context, cfg *config.Config) (*service.Service
|
||||
return svc, statusCancel, nil
|
||||
}
|
||||
|
||||
// errServiceStopped is a reload that failed after the old service stopped:
|
||||
// no pipeline runs until a later reload succeeds.
|
||||
var errServiceStopped = errors.New("old service stopped, new one failed to start")
|
||||
|
||||
// handleReload orchestrates the entire hot-reload process including status reporter lifecycle
|
||||
func handleReload(ctx context.Context, newCfg *config.Config, oldSvc *service.Service, statusCancel context.CancelFunc) (*service.Service, *config.Config, context.CancelFunc, error) {
|
||||
logger.Info("msg", "Starting configuration hot reload")
|
||||
@@ -78,7 +83,7 @@ func handleReload(ctx context.Context, newCfg *config.Config, oldSvc *service.Se
|
||||
if err := newService.Start(); err != nil {
|
||||
newService.Shutdown()
|
||||
logger.Error("msg", "Failed to start new service pipelines after reload. The application may be in a non-functional state.", "error", err)
|
||||
return nil, nil, nil, fmt.Errorf("failed to start new service: %w", err)
|
||||
return nil, nil, nil, fmt.Errorf("%w: %w", errServiceStopped, err)
|
||||
}
|
||||
|
||||
// Manage status reporter lifecycle
|
||||
|
||||
+18
-5
@@ -2,15 +2,18 @@ package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"logwisp/internal/version"
|
||||
"os"
|
||||
|
||||
"github.com/lixenwraith/logwisp/internal/version"
|
||||
)
|
||||
|
||||
// helpText is the CLI usage reference. Scalar flags map 1:1 to TOML config paths.
|
||||
const helpText = `LogWisp %s - log collection, processing, and distribution
|
||||
|
||||
Usage:
|
||||
lw [options]
|
||||
lw [options] [< input] No file, no pipeline options: stdin to stdout,
|
||||
line for line, like cat; exits at end of input
|
||||
lw -c <file> [options] Run the file's pipelines
|
||||
lw --check [options] Build every pipeline and plugin, report, exit
|
||||
lw help | -h | --help
|
||||
lw --version
|
||||
@@ -25,10 +28,12 @@ Scalar configuration keys are settable as flags using their TOML paths:
|
||||
Common options:
|
||||
-c, --config <path> Configuration file (default: ./logwisp.toml)
|
||||
--quiet Suppress console output
|
||||
--status_reporter=<bool> Periodic status logging (default: true)
|
||||
--status_reporter=<bool> Periodic status logging (default: true with
|
||||
a configuration file)
|
||||
--auto_reload=<bool> Config hot reload on file change (default: false)
|
||||
|
||||
Logging:
|
||||
Logging (lw's own log goes to stderr, at info with a configuration file and
|
||||
at warn without one):
|
||||
--logging.output=<mode> file|stdout|stderr|split|all|none
|
||||
--logging.level=<level> debug|info|warn|error
|
||||
--logging.file.directory=<path>
|
||||
@@ -41,9 +46,17 @@ Pipelines (replace the file's pipelines; see doc/cli.md):
|
||||
--format <spec> json|txt|raw[,key=value...]
|
||||
--rate-limit <spec> rate=N[,burst=N,policy=drop|pass]
|
||||
--heartbeat <spec> interval_ms=N[,include_stats=true,...]
|
||||
A pipeline without --source reads stdin, one without --sink writes stdout.
|
||||
Keys nest with '.' (tls.cert_file=...), a repeated key makes a list, and
|
||||
'\' escapes ',' '=' '\' in values. -- ends option parsing.
|
||||
|
||||
Examples:
|
||||
lw < app.log > copy.log Copy, line for line
|
||||
tail -F app.log | lw --filter include,patterns=ERROR,patterns=WARN
|
||||
journalctl -f | lw --sink http,host=127.0.0.1,port=8080
|
||||
Serve stdin as a live stream
|
||||
lw --source file,directory=/var/log/app,pattern='*.log' --format txt
|
||||
|
||||
Environment:
|
||||
LOGWISP_<PATH> Config path, '.' -> '_', uppercase
|
||||
e.g. LOGWISP_LOGGING_LEVEL=debug
|
||||
@@ -59,7 +72,7 @@ Signals:
|
||||
SIGHUP, SIGUSR1 Reload configuration
|
||||
|
||||
Exit codes:
|
||||
0 success
|
||||
0 success, including the end of input
|
||||
1 general error
|
||||
2 configuration file not found
|
||||
`
|
||||
|
||||
+32
-4
@@ -9,11 +9,12 @@ import (
|
||||
"reflect"
|
||||
"syscall"
|
||||
|
||||
"logwisp/internal/config"
|
||||
"logwisp/internal/core"
|
||||
"logwisp/internal/version"
|
||||
"github.com/lixenwraith/logwisp/internal/config"
|
||||
"github.com/lixenwraith/logwisp/internal/core"
|
||||
"github.com/lixenwraith/logwisp/internal/version"
|
||||
|
||||
"github.com/lixenwraith/log"
|
||||
"golang.org/x/term"
|
||||
)
|
||||
|
||||
var logger *log.Logger
|
||||
@@ -88,6 +89,10 @@ func main() {
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if !cfg.Quiet && readsStdin(cfg) && term.IsTerminal(int(os.Stdin.Fd())) {
|
||||
fmt.Fprintln(os.Stderr, "lw: reading standard input; Ctrl-D ends it (lw --help for usage)")
|
||||
}
|
||||
|
||||
// --- 3. Setup signals and shutdown ---
|
||||
sigChan := make(chan os.Signal, 1)
|
||||
signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM, syscall.SIGHUP, syscall.SIGUSR1)
|
||||
@@ -112,6 +117,9 @@ func main() {
|
||||
logger.Info("msg", "Shutdown complete")
|
||||
}()
|
||||
|
||||
// Closed when every pipeline's input ended (stdin); nil while none runs
|
||||
done := svc.Done()
|
||||
|
||||
reload := func(fromDisk bool) {
|
||||
var next *config.Config
|
||||
var err error
|
||||
@@ -130,8 +138,12 @@ func main() {
|
||||
return
|
||||
}
|
||||
newSvc, newCfg, newStatusCancel, err := handleReload(ctx, next, svc, statusReporterCancel)
|
||||
if err == nil {
|
||||
switch {
|
||||
case err == nil:
|
||||
svc, cfg, statusReporterCancel = newSvc, newCfg, newStatusCancel
|
||||
done = svc.Done()
|
||||
case errors.Is(err, errServiceStopped):
|
||||
done = nil // the stopped service's pipelines did not reach the end of input
|
||||
}
|
||||
}
|
||||
|
||||
@@ -158,6 +170,10 @@ func main() {
|
||||
reload(false)
|
||||
}
|
||||
|
||||
case <-done:
|
||||
logger.Info("msg", "Every pipeline finished: its input ended")
|
||||
return
|
||||
|
||||
case <-ctx.Done():
|
||||
return // Exit the loop and trigger deferred shutdown
|
||||
}
|
||||
@@ -172,3 +188,15 @@ func shutdownLogger() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// readsStdin reports a console source: typed into, it waits for the keyboard
|
||||
func readsStdin(cfg *config.Config) bool {
|
||||
for _, p := range cfg.Pipelines {
|
||||
for _, src := range p.PluginSources {
|
||||
if src.Type == "console" {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
@@ -11,9 +11,9 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"logwisp/internal/plugin"
|
||||
"logwisp/internal/session"
|
||||
"logwisp/internal/testutil"
|
||||
"github.com/lixenwraith/logwisp/internal/plugin"
|
||||
"github.com/lixenwraith/logwisp/internal/session"
|
||||
"github.com/lixenwraith/logwisp/internal/testutil"
|
||||
|
||||
lconfig "github.com/lixenwraith/config"
|
||||
)
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@ import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"logwisp/internal/service"
|
||||
"github.com/lixenwraith/logwisp/internal/service"
|
||||
)
|
||||
|
||||
// startStatusReporter starts a new status reporter for a service and returns its cancel function.
|
||||
|
||||
@@ -3,8 +3,8 @@ package main
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"logwisp/internal/config"
|
||||
"logwisp/internal/testutil"
|
||||
"github.com/lixenwraith/logwisp/internal/config"
|
||||
"github.com/lixenwraith/logwisp/internal/testutil"
|
||||
|
||||
"github.com/lixenwraith/log"
|
||||
)
|
||||
|
||||
Reference in new issue
Block a user