mirror of
https://github.com/Control-D-Inc/ctrld.git
synced 2026-03-25 23:30:41 +01:00
refactor: migrate from zerolog to zap logging library
Replace github.com/rs/zerolog with go.uber.org/zap throughout the codebase to improve performance and provide better structured logging capabilities. Key changes: - Replace zerolog imports with zap and zapcore - Implement custom Logger wrapper in log.go to maintain zerolog-like API - Add LogEvent struct with chained methods (Str, Int, Err, Bool, etc.) - Update all logging calls to use the new zap-based wrapper - Replace JSON encoders with Console encoders for better readability Benefits: - Better performance with zap's optimized logging - Consistent structured logging across all components - Maintained zerolog-like API for easy migration - Proper field context preservation for debugging - Multi-core logging architecture for better output control All tests pass and build succeeds.
This commit is contained in:
committed by
Cuong Manh Le
parent
2996a161cd
commit
ddbb0f0db4
@@ -31,9 +31,9 @@ import (
|
||||
"github.com/kardianos/service"
|
||||
"github.com/miekg/dns"
|
||||
"github.com/pelletier/go-toml/v2"
|
||||
"github.com/rs/zerolog"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
"go.uber.org/zap"
|
||||
"tailscale.com/logtail/backoff"
|
||||
"tailscale.com/net/netmon"
|
||||
|
||||
@@ -224,7 +224,7 @@ func run(appCallback *AppCallback, stopCh chan struct{}) {
|
||||
if addr, err := net.ResolveUnixAddr("unix", sockPath); err == nil {
|
||||
if conn, err := net.Dial(addr.Network(), addr.String()); err == nil {
|
||||
lc := &logConn{conn: conn}
|
||||
consoleWriter.Out = io.MultiWriter(os.Stdout, lc)
|
||||
consoleWriter = newHumanReadableZapCore(io.MultiWriter(os.Stdout, lc), consoleWriterLevel)
|
||||
p.logConn = lc
|
||||
} else {
|
||||
if !errors.Is(err, os.ErrNotExist) {
|
||||
@@ -307,7 +307,7 @@ func run(appCallback *AppCallback, stopCh chan struct{}) {
|
||||
return
|
||||
}
|
||||
|
||||
cdLogger := p.logger.Load().With().Str("mode", "cd").Logger()
|
||||
cdLogger := p.logger.Load().With().Str("mode", "cd")
|
||||
// Performs self-uninstallation if the ControlD device does not exist.
|
||||
var uer *controld.ErrorResponse
|
||||
if errors.As(err, &uer) && uer.ErrorField.Code == controld.InvalidConfigCode {
|
||||
@@ -339,8 +339,8 @@ func run(appCallback *AppCallback, stopCh chan struct{}) {
|
||||
|
||||
if newLogPath := cfg.Service.LogPath; newLogPath != "" && oldLogPath != newLogPath {
|
||||
// After processCDFlags, log config may change, so reset mainLog and re-init logging.
|
||||
l := zerolog.New(io.Discard)
|
||||
mainLog.Store(&ctrld.Logger{Logger: &l})
|
||||
l := zap.NewNop()
|
||||
mainLog.Store(&ctrld.Logger{Logger: l})
|
||||
|
||||
// Copy logs written so far to new log file if possible.
|
||||
if buf, err := os.ReadFile(oldLogPath); err == nil {
|
||||
@@ -603,11 +603,11 @@ func deactivationPinSet() bool {
|
||||
}
|
||||
|
||||
func processCDFlags(cfg *ctrld.Config) (*controld.ResolverConfig, error) {
|
||||
logger := mainLog.Load().With().Str("mode", "cd").Logger()
|
||||
logger := mainLog.Load().With().Str("mode", "cd")
|
||||
logger.Info().Msgf("fetching Controld D configuration from API: %s", cdUID)
|
||||
bo := backoff.NewBackoff("processCDFlags", logf, 30*time.Second)
|
||||
bo.LogLongerThan = 30 * time.Second
|
||||
ctx := ctrld.LoggerCtx(context.Background(), mainLog.Load())
|
||||
ctx := ctrld.LoggerCtx(context.Background(), logger)
|
||||
resolverConfig, err := controld.FetchResolverConfig(ctx, cdUID, rootCmd.Version, cdDev)
|
||||
for {
|
||||
if errUrlNetworkError(err) {
|
||||
@@ -1210,7 +1210,7 @@ func tryUpdateListenerConfig(cfg *ctrld.Config, notifyFunc func(), fatal bool) (
|
||||
return errors.Join(udpErr, tcpErr)
|
||||
}
|
||||
|
||||
logMsg := func(e *zerolog.Event, listenerNum int, format string, v ...any) {
|
||||
logMsg := func(e *ctrld.LogEvent, listenerNum int, format string, v ...any) {
|
||||
e.MsgFunc(func() string {
|
||||
return fmt.Sprintf("listener.%d %s", listenerNum, fmt.Sprintf(format, v...))
|
||||
})
|
||||
@@ -1773,7 +1773,7 @@ func doValidateCdRemoteConfig(cdUID string, fatal bool) error {
|
||||
}
|
||||
|
||||
// uninstallInvalidCdUID performs self-uninstallation because the ControlD device does not exist.
|
||||
func uninstallInvalidCdUID(p *prog, logger zerolog.Logger, doStop bool) bool {
|
||||
func uninstallInvalidCdUID(p *prog, logger *ctrld.Logger, doStop bool) bool {
|
||||
s, err := newService(p, svcConfig)
|
||||
if err != nil {
|
||||
logger.Warn().Err(err).Msg("failed to create new service")
|
||||
|
||||
@@ -270,7 +270,7 @@ NOTE: running "ctrld start" without any arguments will start already installed c
|
||||
_, _ = patchNetIfaceName(iff)
|
||||
name = iff.Name
|
||||
}
|
||||
logger := mainLog.Load().With().Str("iface", name).Logger()
|
||||
logger := mainLog.Load().With().Str("iface", name)
|
||||
logger.Debug().Msg("setting DNS successfully")
|
||||
if res.All {
|
||||
// Log that DNS is set for other interfaces.
|
||||
|
||||
@@ -1099,7 +1099,7 @@ func (p *prog) doSelfUninstall(pr *proxyResponse) {
|
||||
return
|
||||
}
|
||||
|
||||
logger := p.logger.Load().With().Str("mode", "self-uninstall").Logger()
|
||||
logger := p.logger.Load().With().Str("mode", "self-uninstall")
|
||||
if p.refusedQueryCount > selfUninstallMaxQueries {
|
||||
p.checkingSelfUninstall = true
|
||||
loggerCtx := ctrld.LoggerCtx(context.Background(), p.logger.Load())
|
||||
|
||||
@@ -10,7 +10,8 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/rs/zerolog"
|
||||
"go.uber.org/zap"
|
||||
"go.uber.org/zap/zapcore"
|
||||
|
||||
"github.com/Control-D-Inc/ctrld"
|
||||
)
|
||||
@@ -95,16 +96,15 @@ func (lw *logWriter) Write(p []byte) (int, error) {
|
||||
|
||||
// initLogging initializes global logging setup.
|
||||
func (p *prog) initLogging(backup bool) {
|
||||
zerolog.TimeFieldFormat = time.RFC3339 + ".000"
|
||||
logWriters := initLoggingWithBackup(backup)
|
||||
logCores := initLoggingWithBackup(backup)
|
||||
|
||||
// Initializing internal logging after global logging.
|
||||
p.initInternalLogging(logWriters)
|
||||
p.initInternalLogging(logCores)
|
||||
p.logger.Store(mainLog.Load())
|
||||
}
|
||||
|
||||
// initInternalLogging performs internal logging if there's no log enabled.
|
||||
func (p *prog) initInternalLogging(writers []io.Writer) {
|
||||
func (p *prog) initInternalLogging(externalCores []zapcore.Core) {
|
||||
if !p.needInternalLogging() {
|
||||
return
|
||||
}
|
||||
@@ -118,27 +118,25 @@ func (p *prog) initInternalLogging(writers []io.Writer) {
|
||||
lw := p.internalLogWriter
|
||||
wlw := p.internalWarnLogWriter
|
||||
p.mu.Unlock()
|
||||
// If ctrld was run without explicit verbose level,
|
||||
// run the internal logging at debug level, so we could
|
||||
|
||||
// Create zap cores for different writers
|
||||
var cores []zapcore.Core
|
||||
cores = append(cores, externalCores...)
|
||||
|
||||
// Add core for internal log writer.
|
||||
// Run the internal logging at debug level, so we could
|
||||
// have enough information for troubleshooting.
|
||||
if verbose == 0 {
|
||||
for i := range writers {
|
||||
w := &zerolog.FilteredLevelWriter{
|
||||
Writer: zerolog.LevelWriterAdapter{Writer: writers[i]},
|
||||
Level: zerolog.NoticeLevel,
|
||||
}
|
||||
writers[i] = w
|
||||
}
|
||||
zerolog.SetGlobalLevel(zerolog.DebugLevel)
|
||||
}
|
||||
writers = append(writers, lw)
|
||||
writers = append(writers, &zerolog.FilteredLevelWriter{
|
||||
Writer: zerolog.LevelWriterAdapter{Writer: wlw},
|
||||
Level: zerolog.WarnLevel,
|
||||
})
|
||||
multi := zerolog.MultiLevelWriter(writers...)
|
||||
l := mainLog.Load().Output(multi).With().Logger()
|
||||
mainLog.Store(&ctrld.Logger{Logger: &l})
|
||||
internalCore := newHumanReadableZapCore(lw, zapcore.DebugLevel)
|
||||
cores = append(cores, internalCore)
|
||||
|
||||
// Add core for internal warn log writer
|
||||
warnCore := newHumanReadableZapCore(wlw, zapcore.WarnLevel)
|
||||
cores = append(cores, warnCore)
|
||||
|
||||
// Create a multi-core logger
|
||||
multiCore := zapcore.NewTee(cores...)
|
||||
logger := zap.New(multiCore)
|
||||
mainLog.Store(&ctrld.Logger{Logger: logger})
|
||||
}
|
||||
|
||||
// needInternalLogging reports whether prog needs to run internal logging.
|
||||
@@ -202,3 +200,49 @@ func (p *prog) logReader() (*logReader, error) {
|
||||
}
|
||||
return lr, nil
|
||||
}
|
||||
|
||||
// newHumanReadableZapCore creates a zap core optimized for human-readable log output.
|
||||
//
|
||||
// Features:
|
||||
// - Uses development encoder configuration for enhanced readability
|
||||
// - Console encoding with colored log levels for easy visual scanning
|
||||
// - Millisecond precision timestamps in human-friendly format
|
||||
// - Structured field output with clear key-value pairs
|
||||
// - Ideal for development, debugging, and interactive terminal sessions
|
||||
//
|
||||
// Parameters:
|
||||
// - w: The output writer (e.g., os.Stdout, file, buffer)
|
||||
// - level: Minimum log level to capture (e.g., Debug, Info, Warn, Error)
|
||||
//
|
||||
// Returns a zapcore.Core configured for human consumption.
|
||||
func newHumanReadableZapCore(w io.Writer, level zapcore.Level) zapcore.Core {
|
||||
encoderConfig := zap.NewDevelopmentEncoderConfig()
|
||||
encoderConfig.TimeKey = "time"
|
||||
encoderConfig.EncodeTime = zapcore.TimeEncoderOfLayout(time.StampMilli)
|
||||
encoderConfig.EncodeLevel = zapcore.CapitalColorLevelEncoder
|
||||
encoder := zapcore.NewConsoleEncoder(encoderConfig)
|
||||
return zapcore.NewCore(encoder, zapcore.AddSync(w), level)
|
||||
}
|
||||
|
||||
// newMachineFriendlyZapCore creates a zap core optimized for machine processing and log aggregation.
|
||||
//
|
||||
// Features:
|
||||
// - Uses production encoder configuration for consistent, parseable output
|
||||
// - Console encoding with non-colored log levels for log parsing tools
|
||||
// - Millisecond precision timestamps in ISO-like format
|
||||
// - Structured field output optimized for log aggregation systems
|
||||
// - Ideal for production environments, log shipping, and automated analysis
|
||||
//
|
||||
// Parameters:
|
||||
// - w: The output writer (e.g., os.Stdout, file, buffer)
|
||||
// - level: Minimum log level to capture (e.g., Debug, Info, Warn, Error)
|
||||
//
|
||||
// Returns a zapcore.Core configured for machine consumption and log aggregation.
|
||||
func newMachineFriendlyZapCore(w io.Writer, level zapcore.Level) zapcore.Core {
|
||||
encoderConfig := zap.NewProductionEncoderConfig()
|
||||
encoderConfig.TimeKey = "time"
|
||||
encoderConfig.EncodeTime = zapcore.TimeEncoderOfLayout(time.StampMilli)
|
||||
encoderConfig.EncodeLevel = zapcore.CapitalLevelEncoder
|
||||
encoder := zapcore.NewConsoleEncoder(encoderConfig)
|
||||
return zapcore.NewCore(encoder, zapcore.AddSync(w), level)
|
||||
}
|
||||
|
||||
@@ -5,10 +5,10 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/kardianos/service"
|
||||
"github.com/rs/zerolog"
|
||||
"go.uber.org/zap"
|
||||
"go.uber.org/zap/zapcore"
|
||||
|
||||
"github.com/Control-D-Inc/ctrld"
|
||||
)
|
||||
@@ -40,9 +40,10 @@ var (
|
||||
cleanup bool
|
||||
startOnly bool
|
||||
|
||||
mainLog atomic.Pointer[ctrld.Logger]
|
||||
consoleWriter zerolog.ConsoleWriter
|
||||
noConfigStart bool
|
||||
mainLog atomic.Pointer[ctrld.Logger]
|
||||
consoleWriter zapcore.Core
|
||||
consoleWriterLevel zapcore.Level
|
||||
noConfigStart bool
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -53,8 +54,8 @@ const (
|
||||
)
|
||||
|
||||
func init() {
|
||||
l := zerolog.New(io.Discard)
|
||||
mainLog.Store(&ctrld.Logger{Logger: &l})
|
||||
l := zap.NewNop()
|
||||
mainLog.Store(&ctrld.Logger{Logger: l})
|
||||
}
|
||||
|
||||
func Main() {
|
||||
@@ -82,23 +83,23 @@ func normalizeLogFilePath(logFilePath string) string {
|
||||
|
||||
// initConsoleLogging initializes console logging, then storing to mainLog.
|
||||
func initConsoleLogging() {
|
||||
consoleWriter = zerolog.NewConsoleWriter(func(w *zerolog.ConsoleWriter) {
|
||||
w.TimeFormat = time.StampMilli
|
||||
})
|
||||
multi := zerolog.MultiLevelWriter(consoleWriter)
|
||||
l := mainLog.Load().Output(multi).With().Timestamp().Logger()
|
||||
mainLog.Store(&ctrld.Logger{Logger: &l})
|
||||
|
||||
consoleWriterLevel = zapcore.InfoLevel
|
||||
switch {
|
||||
case silent:
|
||||
zerolog.SetGlobalLevel(zerolog.NoLevel)
|
||||
// For silent mode, use a no-op logger
|
||||
l := zap.NewNop()
|
||||
mainLog.Store(&ctrld.Logger{Logger: l})
|
||||
case verbose == 1:
|
||||
zerolog.SetGlobalLevel(zerolog.InfoLevel)
|
||||
// Info level is default
|
||||
case verbose > 1:
|
||||
zerolog.SetGlobalLevel(zerolog.DebugLevel)
|
||||
// Debug level
|
||||
consoleWriterLevel = zapcore.DebugLevel
|
||||
default:
|
||||
zerolog.SetGlobalLevel(zerolog.NoticeLevel)
|
||||
// Notice level maps to Info in zap
|
||||
}
|
||||
consoleWriter = newHumanReadableZapCore(os.Stdout, consoleWriterLevel)
|
||||
l := zap.New(consoleWriter)
|
||||
mainLog.Store(&ctrld.Logger{Logger: l})
|
||||
}
|
||||
|
||||
// initInteractiveLogging is like initLogging, but the ProxyLogger is discarded
|
||||
@@ -108,7 +109,6 @@ func initConsoleLogging() {
|
||||
func initInteractiveLogging() {
|
||||
old := cfg.Service.LogPath
|
||||
cfg.Service.LogPath = ""
|
||||
zerolog.TimeFieldFormat = time.RFC3339 + ".000"
|
||||
initLoggingWithBackup(false)
|
||||
cfg.Service.LogPath = old
|
||||
}
|
||||
@@ -119,7 +119,7 @@ func initInteractiveLogging() {
|
||||
// This is only used in runCmd for special handling in case of logging config
|
||||
// change in cd mode. Without special reason, the caller should use initLogging
|
||||
// wrapper instead of calling this function directly.
|
||||
func initLoggingWithBackup(doBackup bool) []io.Writer {
|
||||
func initLoggingWithBackup(doBackup bool) []zapcore.Core {
|
||||
var writers []io.Writer
|
||||
if logFilePath := normalizeLogFilePath(cfg.Service.LogPath); logFilePath != "" {
|
||||
// Create parent directory if necessary.
|
||||
@@ -146,32 +146,53 @@ func initLoggingWithBackup(doBackup bool) []io.Writer {
|
||||
}
|
||||
writers = append(writers, logFile)
|
||||
}
|
||||
writers = append(writers, consoleWriter)
|
||||
multi := zerolog.MultiLevelWriter(writers...)
|
||||
l := mainLog.Load().Output(multi).With().Logger()
|
||||
mainLog.Store(&ctrld.Logger{Logger: &l})
|
||||
|
||||
zerolog.SetGlobalLevel(zerolog.NoticeLevel)
|
||||
// Create zap cores for different writers
|
||||
var cores []zapcore.Core
|
||||
cores = append(cores, consoleWriter)
|
||||
|
||||
// Determine log level
|
||||
logLevel := cfg.Service.LogLevel
|
||||
switch {
|
||||
case silent:
|
||||
zerolog.SetGlobalLevel(zerolog.NoLevel)
|
||||
return writers
|
||||
// For silent mode, use a no-op logger
|
||||
l := zap.NewNop()
|
||||
mainLog.Store(&ctrld.Logger{Logger: l})
|
||||
return cores
|
||||
case verbose == 1:
|
||||
logLevel = "info"
|
||||
case verbose > 1:
|
||||
logLevel = "debug"
|
||||
}
|
||||
if logLevel == "" {
|
||||
return writers
|
||||
|
||||
// Parse log level
|
||||
var level zapcore.Level
|
||||
switch logLevel {
|
||||
case "debug":
|
||||
level = zapcore.DebugLevel
|
||||
case "info":
|
||||
level = zapcore.InfoLevel
|
||||
case "warn":
|
||||
level = zapcore.WarnLevel
|
||||
case "error":
|
||||
level = zapcore.ErrorLevel
|
||||
default:
|
||||
level = zapcore.InfoLevel // default level
|
||||
}
|
||||
level, err := zerolog.ParseLevel(logLevel)
|
||||
if err != nil {
|
||||
mainLog.Load().Warn().Err(err).Msg("could not set log level")
|
||||
return writers
|
||||
|
||||
consoleWriter.Enabled(level)
|
||||
// Add cores for all writers
|
||||
for _, writer := range writers {
|
||||
core := newMachineFriendlyZapCore(writer, level)
|
||||
cores = append(cores, core)
|
||||
}
|
||||
zerolog.SetGlobalLevel(level)
|
||||
return writers
|
||||
|
||||
// Create a multi-core logger
|
||||
multiCore := zapcore.NewTee(cores...)
|
||||
logger := zap.New(multiCore)
|
||||
mainLog.Store(&ctrld.Logger{Logger: logger})
|
||||
|
||||
return cores
|
||||
}
|
||||
|
||||
func initCache() {
|
||||
|
||||
@@ -5,7 +5,8 @@ import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/rs/zerolog"
|
||||
"go.uber.org/zap"
|
||||
"go.uber.org/zap/zapcore"
|
||||
|
||||
"github.com/Control-D-Inc/ctrld"
|
||||
)
|
||||
@@ -13,7 +14,19 @@ import (
|
||||
var logOutput strings.Builder
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
l := zerolog.New(&logOutput)
|
||||
mainLog.Store(&ctrld.Logger{Logger: &l})
|
||||
// Create a custom writer that writes to logOutput
|
||||
writer := zapcore.AddSync(&logOutput)
|
||||
|
||||
// Create zap encoder
|
||||
encoderConfig := zap.NewDevelopmentEncoderConfig()
|
||||
encoder := zapcore.NewConsoleEncoder(encoderConfig)
|
||||
|
||||
// Create core that writes to our string builder
|
||||
core := zapcore.NewCore(encoder, writer, zap.DebugLevel)
|
||||
|
||||
// Create logger
|
||||
l := zap.New(core)
|
||||
|
||||
mainLog.Store(&ctrld.Logger{Logger: l})
|
||||
os.Exit(m.Run())
|
||||
}
|
||||
|
||||
@@ -24,7 +24,6 @@ import (
|
||||
|
||||
"github.com/Masterminds/semver/v3"
|
||||
"github.com/kardianos/service"
|
||||
"github.com/rs/zerolog"
|
||||
"github.com/spf13/viper"
|
||||
"golang.org/x/sync/singleflight"
|
||||
"tailscale.com/net/netmon"
|
||||
@@ -296,7 +295,7 @@ func (p *prog) apiConfigReload() {
|
||||
ticker := time.NewTicker(timeDurationOrDefault(p.cfg.Service.RefetchTime, 3600) * time.Second)
|
||||
defer ticker.Stop()
|
||||
|
||||
logger := p.logger.Load().With().Str("mode", "api-reload").Logger()
|
||||
logger := p.logger.Load().With().Str("mode", "api-reload")
|
||||
logger.Debug().Msg("starting custom config reload timer")
|
||||
lastUpdated := time.Now().Unix()
|
||||
curVerStr := curVersion()
|
||||
@@ -310,7 +309,7 @@ func (p *prog) apiConfigReload() {
|
||||
l.Msgf("current version is not stable, skipping self-upgrade: %s", curVerStr)
|
||||
}
|
||||
|
||||
doReloadApiConfig := func(forced bool, logger zerolog.Logger) {
|
||||
doReloadApiConfig := func(forced bool, logger *ctrld.Logger) {
|
||||
loggerCtx := ctrld.LoggerCtx(context.Background(), p.logger.Load())
|
||||
resolverConfig, err := controld.FetchResolverConfig(loggerCtx, cdUID, rootCmd.Version, cdDev)
|
||||
selfUninstallCheck(err, p, logger)
|
||||
@@ -321,7 +320,7 @@ func (p *prog) apiConfigReload() {
|
||||
|
||||
// Performing self-upgrade check for production version.
|
||||
if isStable {
|
||||
_ = selfUpgradeCheck(resolverConfig.Ctrld.VersionTarget, curVer, &logger)
|
||||
_ = selfUpgradeCheck(resolverConfig.Ctrld.VersionTarget, curVer, logger)
|
||||
}
|
||||
|
||||
if resolverConfig.DeactivationPin != nil {
|
||||
@@ -384,7 +383,7 @@ func (p *prog) apiConfigReload() {
|
||||
for {
|
||||
select {
|
||||
case <-p.apiForceReloadCh:
|
||||
doReloadApiConfig(true, logger.With().Bool("forced", true).Logger())
|
||||
doReloadApiConfig(true, logger.With().Bool("forced", true))
|
||||
case <-ticker.C:
|
||||
doReloadApiConfig(false, logger)
|
||||
case <-p.stopCh:
|
||||
@@ -578,7 +577,7 @@ func (p *prog) run(reload bool, reloadCh chan struct{}) {
|
||||
|
||||
if !reload {
|
||||
// Stop writing log to unix socket.
|
||||
consoleWriter.Out = os.Stdout
|
||||
consoleWriter = newHumanReadableZapCore(os.Stdout, consoleWriterLevel)
|
||||
p.initLogging(false)
|
||||
if p.logConn != nil {
|
||||
_ = p.logConn.Close()
|
||||
@@ -758,7 +757,7 @@ func (p *prog) setDnsForRunningIface(nameservers []string) (runningIface *net.In
|
||||
return
|
||||
}
|
||||
|
||||
logger := p.logger.Load().With().Str("iface", p.runningIface).Logger()
|
||||
logger := p.logger.Load().With().Str("iface", p.runningIface)
|
||||
|
||||
const maxDNSRetryAttempts = 3
|
||||
const retryDelay = 1 * time.Second
|
||||
@@ -774,7 +773,7 @@ func (p *prog) setDnsForRunningIface(nameservers []string) (runningIface *net.In
|
||||
newIface := p.findWorkingInterface()
|
||||
if newIface != p.runningIface {
|
||||
p.runningIface = newIface
|
||||
logger = p.logger.Load().With().Str("iface", p.runningIface).Logger()
|
||||
logger = p.logger.Load().With().Str("iface", p.runningIface)
|
||||
logger.Info().Msg("switched to new interface")
|
||||
continue
|
||||
}
|
||||
@@ -930,7 +929,7 @@ func (p *prog) resetDNSForRunningIface(isStart bool, restoreStatic bool) (runnin
|
||||
p.Debug().Msg("no running interface, skipping resetDNS")
|
||||
return
|
||||
}
|
||||
logger := p.logger.Load().With().Str("iface", p.runningIface).Logger()
|
||||
logger := p.logger.Load().With().Str("iface", p.runningIface)
|
||||
netIface, err := netInterface(p.runningIface)
|
||||
if err != nil {
|
||||
logger.Error().Err(err).Msg("could not get interface")
|
||||
@@ -1416,7 +1415,7 @@ func (p *prog) dnsChanged(iface *net.Interface, nameservers []string) bool {
|
||||
}
|
||||
|
||||
// selfUninstallCheck checks if the error dues to controld.InvalidConfigCode, perform self-uninstall then.
|
||||
func selfUninstallCheck(uninstallErr error, p *prog, logger zerolog.Logger) {
|
||||
func selfUninstallCheck(uninstallErr error, p *prog, logger *ctrld.Logger) {
|
||||
var uer *controld.ErrorResponse
|
||||
if errors.As(uninstallErr, &uer) && uer.ErrorField.Code == controld.InvalidConfigCode {
|
||||
p.stopDnsWatchers()
|
||||
@@ -1431,7 +1430,7 @@ func selfUninstallCheck(uninstallErr error, p *prog, logger zerolog.Logger) {
|
||||
//
|
||||
// The callers must ensure curVer and logger are non-nil.
|
||||
// Returns true if upgrade is allowed, false otherwise.
|
||||
func shouldUpgrade(vt string, cv *semver.Version, logger *zerolog.Logger) bool {
|
||||
func shouldUpgrade(vt string, cv *semver.Version, logger *ctrld.Logger) bool {
|
||||
if vt == "" {
|
||||
logger.Debug().Msg("no version target set, skipped checking self-upgrade")
|
||||
return false
|
||||
@@ -1468,7 +1467,7 @@ func shouldUpgrade(vt string, cv *semver.Version, logger *zerolog.Logger) bool {
|
||||
|
||||
// performUpgrade executes the self-upgrade command.
|
||||
// Returns true if upgrade was initiated successfully, false otherwise.
|
||||
func performUpgrade(vt string, logger *zerolog.Logger) bool {
|
||||
func performUpgrade(vt string, logger *ctrld.Logger) bool {
|
||||
exe, err := os.Executable()
|
||||
if err != nil {
|
||||
logger.Error().Err(err).Msg("failed to get executable path, skipped self-upgrade")
|
||||
@@ -1490,7 +1489,7 @@ func performUpgrade(vt string, logger *zerolog.Logger) bool {
|
||||
//
|
||||
// The callers must ensure curVer and logger are non-nil.
|
||||
// Returns true if upgrade is allowed and should proceed, false otherwise.
|
||||
func selfUpgradeCheck(vt string, cv *semver.Version, logger *zerolog.Logger) bool {
|
||||
func selfUpgradeCheck(vt string, cv *semver.Version, logger *ctrld.Logger) bool {
|
||||
if shouldUpgrade(vt, cv, logger) {
|
||||
return performUpgrade(vt, logger)
|
||||
}
|
||||
|
||||
@@ -1,33 +1,33 @@
|
||||
package cli
|
||||
|
||||
import "github.com/rs/zerolog"
|
||||
import "github.com/Control-D-Inc/ctrld"
|
||||
|
||||
// Debug starts a new message with debug level.
|
||||
func (p *prog) Debug() *zerolog.Event {
|
||||
func (p *prog) Debug() *ctrld.LogEvent {
|
||||
return p.logger.Load().Debug()
|
||||
}
|
||||
|
||||
// Warn starts a new message with warn level.
|
||||
func (p *prog) Warn() *zerolog.Event {
|
||||
func (p *prog) Warn() *ctrld.LogEvent {
|
||||
return p.logger.Load().Warn()
|
||||
}
|
||||
|
||||
// Info starts a new message with info level.
|
||||
func (p *prog) Info() *zerolog.Event {
|
||||
func (p *prog) Info() *ctrld.LogEvent {
|
||||
return p.logger.Load().Info()
|
||||
}
|
||||
|
||||
// Fatal starts a new message with fatal level.
|
||||
func (p *prog) Fatal() *zerolog.Event {
|
||||
func (p *prog) Fatal() *ctrld.LogEvent {
|
||||
return p.logger.Load().Fatal()
|
||||
}
|
||||
|
||||
// Error starts a new message with error level.
|
||||
func (p *prog) Error() *zerolog.Event {
|
||||
func (p *prog) Error() *ctrld.LogEvent {
|
||||
return p.logger.Load().Error()
|
||||
}
|
||||
|
||||
// Notice starts a new message with notice level.
|
||||
func (p *prog) Notice() *zerolog.Event {
|
||||
func (p *prog) Notice() *ctrld.LogEvent {
|
||||
return p.logger.Load().Notice()
|
||||
}
|
||||
|
||||
@@ -5,8 +5,8 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/Masterminds/semver/v3"
|
||||
"github.com/rs/zerolog"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"go.uber.org/zap"
|
||||
|
||||
"github.com/Control-D-Inc/ctrld"
|
||||
)
|
||||
@@ -173,10 +173,10 @@ func Test_shouldUpgrade(t *testing.T) {
|
||||
tc := tc
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
// Create test logger
|
||||
testLogger := zerolog.New(zerolog.NewTestWriter(t)).With().Logger()
|
||||
testLogger := &ctrld.Logger{Logger: zap.NewNop()}
|
||||
|
||||
// Call the function and capture the result
|
||||
result := shouldUpgrade(tc.versionTarget, tc.currentVersion, &testLogger)
|
||||
result := shouldUpgrade(tc.versionTarget, tc.currentVersion, testLogger)
|
||||
|
||||
// Assert the expected result
|
||||
assert.Equal(t, tc.shouldUpgrade, result, tc.description)
|
||||
@@ -221,10 +221,10 @@ func Test_selfUpgradeCheck(t *testing.T) {
|
||||
tc := tc
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
// Create test logger
|
||||
testLogger := zerolog.New(zerolog.NewTestWriter(t)).With().Logger()
|
||||
testLogger := &ctrld.Logger{Logger: zap.NewNop()}
|
||||
|
||||
// Call the function and capture the result
|
||||
result := selfUpgradeCheck(tc.versionTarget, tc.currentVersion, &testLogger)
|
||||
result := selfUpgradeCheck(tc.versionTarget, tc.currentVersion, testLogger)
|
||||
|
||||
// Assert the expected result
|
||||
assert.Equal(t, tc.shouldUpgrade, result, tc.description)
|
||||
@@ -256,8 +256,10 @@ func Test_performUpgrade(t *testing.T) {
|
||||
for _, tc := range tests {
|
||||
tc := tc
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
// Create test logger
|
||||
testLogger := &ctrld.Logger{Logger: zap.NewNop()}
|
||||
// Call the function and capture the result
|
||||
result := performUpgrade(tc.versionTarget)
|
||||
result := performUpgrade(tc.versionTarget, testLogger)
|
||||
assert.Equal(t, tc.expectedResult, result, tc.description)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -5,10 +5,10 @@ package cli
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/rs/zerolog"
|
||||
"github.com/Control-D-Inc/ctrld"
|
||||
)
|
||||
|
||||
func selfUninstall(p *prog, logger zerolog.Logger) {
|
||||
func selfUninstall(p *prog, logger *ctrld.Logger) {
|
||||
if uninstallInvalidCdUID(p, logger, false) {
|
||||
logger.Warn().Msgf("service was uninstalled because device %q does not exist", cdUID)
|
||||
os.Exit(0)
|
||||
|
||||
@@ -9,10 +9,10 @@ import (
|
||||
"runtime"
|
||||
"syscall"
|
||||
|
||||
"github.com/rs/zerolog"
|
||||
"github.com/Control-D-Inc/ctrld"
|
||||
)
|
||||
|
||||
func selfUninstall(p *prog, logger zerolog.Logger) {
|
||||
func selfUninstall(p *prog, logger *ctrld.Logger) {
|
||||
if runtime.GOOS == "linux" {
|
||||
selfUninstallLinux(p, logger)
|
||||
}
|
||||
@@ -37,7 +37,7 @@ func selfUninstall(p *prog, logger zerolog.Logger) {
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
func selfUninstallLinux(p *prog, logger zerolog.Logger) {
|
||||
func selfUninstallLinux(p *prog, logger *ctrld.Logger) {
|
||||
if uninstallInvalidCdUID(p, logger, true) {
|
||||
logger.Warn().Msgf("service was uninstalled because device %q does not exist", cdUID)
|
||||
os.Exit(0)
|
||||
|
||||
5
go.mod
5
go.mod
@@ -30,11 +30,11 @@ require (
|
||||
github.com/prometheus/client_model v0.5.0
|
||||
github.com/prometheus/prom2json v1.3.3
|
||||
github.com/quic-go/quic-go v0.48.2
|
||||
github.com/rs/zerolog v1.28.0
|
||||
github.com/spf13/cobra v1.8.1
|
||||
github.com/spf13/viper v1.16.0
|
||||
github.com/stretchr/testify v1.9.0
|
||||
github.com/vishvananda/netlink v1.2.1-beta.2
|
||||
go.uber.org/zap v1.27.0
|
||||
golang.org/x/net v0.38.0
|
||||
golang.org/x/sync v0.12.0
|
||||
golang.org/x/sys v0.31.0
|
||||
@@ -65,8 +65,6 @@ require (
|
||||
github.com/kr/text v0.2.0 // indirect
|
||||
github.com/leodido/go-urn v1.2.1 // indirect
|
||||
github.com/magiconair/properties v1.8.7 // indirect
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.14 // indirect
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
|
||||
github.com/mdlayher/netlink v1.7.2 // indirect
|
||||
@@ -90,6 +88,7 @@ require (
|
||||
github.com/u-root/uio v0.0.0-20240118234441-a3c409a6018e // indirect
|
||||
github.com/vishvananda/netns v0.0.4 // indirect
|
||||
go.uber.org/mock v0.4.0 // indirect
|
||||
go.uber.org/multierr v1.11.0 // indirect
|
||||
go4.org/mem v0.0.0-20220726221520-4f986261bf13 // indirect
|
||||
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba // indirect
|
||||
golang.org/x/crypto v0.36.0 // indirect
|
||||
|
||||
18
go.sum
18
go.sum
@@ -42,8 +42,6 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03
|
||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||
github.com/Masterminds/semver/v3 v3.2.1 h1:RN9w6+7QoMeJVGyfmbcgs28Br8cvmnucEXnY0rYXWg0=
|
||||
github.com/Masterminds/semver/v3 v3.2.1/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ=
|
||||
github.com/Windscribe/zerolog v0.0.0-20241206130353-cc6e8ef5397c h1:UqFsxmwiCh/DBvwJB0m7KQ2QFDd6DdUkosznfMppdhE=
|
||||
github.com/Windscribe/zerolog v0.0.0-20241206130353-cc6e8ef5397c/go.mod h1:bJsvje4Z08ROH4Nhs5iH600c3IkWhwp44iRc54W6wYQ=
|
||||
github.com/alexbrainman/sspi v0.0.0-20231016080023-1a75b4708caa h1:LHTHcTQiSGT7VVbI0o4wBRNQIgn917usHWOd6VAffYI=
|
||||
github.com/alexbrainman/sspi v0.0.0-20231016080023-1a75b4708caa/go.mod h1:cEWa1LVoE5KvSD9ONXsZrj0z6KqySlCCNKHlLzbqAt4=
|
||||
github.com/ameshkov/dnsstamps v1.0.3 h1:Srzik+J9mivH1alRACTbys2xOxs0lRH9qnTA7Y1OYVo=
|
||||
@@ -213,12 +211,6 @@ github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w=
|
||||
github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY=
|
||||
github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY=
|
||||
github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
|
||||
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
||||
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
||||
github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU=
|
||||
github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||
@@ -282,7 +274,6 @@ github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6po
|
||||
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
|
||||
github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=
|
||||
github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=
|
||||
github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/spf13/afero v1.9.5 h1:stMpOSZFs//0Lv29HduCmli3GUfpFoF3Y1Q/aXj/wVM=
|
||||
github.com/spf13/afero v1.9.5/go.mod h1:UBogFpq8E9Hx+xc5CNTTEpTnuHVmXDwZcZcE1eb/UhQ=
|
||||
@@ -330,8 +321,14 @@ go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
|
||||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
||||
go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU=
|
||||
go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc=
|
||||
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
|
||||
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
|
||||
go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
|
||||
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
|
||||
go4.org/mem v0.0.0-20220726221520-4f986261bf13 h1:CbZeCBZ0aZj8EfVgnqQcYZgf0lpZ3H9rmp5nkDTAst8=
|
||||
go4.org/mem v0.0.0-20220726221520-4f986261bf13/go.mod h1:reUoABIJ9ikfM5sgtSF3Wushcza7+WeD01VB9Lirh3g=
|
||||
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba h1:0b9z3AuHCjxk0x/opv64kcgZLBseWJUpBw5I82+2U4M=
|
||||
@@ -482,12 +479,9 @@ golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220622161953-175b2fd9d664/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220817070843-5a390386f1f2/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.4.1-0.20230131160137-e7d7f63158de/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
|
||||
golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||
|
||||
@@ -3,7 +3,6 @@ package net
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"io"
|
||||
"net"
|
||||
"os"
|
||||
"os/signal"
|
||||
@@ -12,7 +11,7 @@ import (
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/rs/zerolog"
|
||||
"go.uber.org/zap"
|
||||
"tailscale.com/logtail/backoff"
|
||||
)
|
||||
|
||||
@@ -34,8 +33,8 @@ var Dialer = &net.Dialer{
|
||||
Dial: func(ctx context.Context, network, address string) (net.Conn, error) {
|
||||
d := ParallelDialer{}
|
||||
d.Timeout = 10 * time.Second
|
||||
l := zerolog.New(io.Discard)
|
||||
return d.DialContext(ctx, "udp", []string{v4BootstrapDNS, v6BootstrapDNS}, &l)
|
||||
l := zap.NewNop()
|
||||
return d.DialContext(ctx, "udp", []string{v4BootstrapDNS, v6BootstrapDNS}, l)
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -161,7 +160,7 @@ type ParallelDialer struct {
|
||||
net.Dialer
|
||||
}
|
||||
|
||||
func (d *ParallelDialer) DialContext(ctx context.Context, network string, addrs []string, logger *zerolog.Logger) (net.Conn, error) {
|
||||
func (d *ParallelDialer) DialContext(ctx context.Context, network string, addrs []string, logger *zap.Logger) (net.Conn, error) {
|
||||
if len(addrs) == 0 {
|
||||
return nil, errors.New("empty addresses")
|
||||
}
|
||||
@@ -181,16 +180,16 @@ func (d *ParallelDialer) DialContext(ctx context.Context, network string, addrs
|
||||
for _, addr := range addrs {
|
||||
go func(addr string) {
|
||||
defer wg.Done()
|
||||
logger.Debug().Msgf("dialing to %s", addr)
|
||||
logger.Debug("dialing to", zap.String("address", addr))
|
||||
conn, err := d.Dialer.DialContext(ctx, network, addr)
|
||||
if err != nil {
|
||||
logger.Debug().Msgf("failed to dial %s: %v", addr, err)
|
||||
logger.Debug("failed to dial", zap.String("address", addr), zap.Error(err))
|
||||
}
|
||||
select {
|
||||
case ch <- ¶llelDialerResult{conn: conn, err: err}:
|
||||
case <-done:
|
||||
if conn != nil {
|
||||
logger.Debug().Msgf("connection closed: %s", conn.RemoteAddr())
|
||||
logger.Debug("connection closed", zap.String("remote_address", conn.RemoteAddr().String()))
|
||||
conn.Close()
|
||||
}
|
||||
}
|
||||
@@ -201,7 +200,7 @@ func (d *ParallelDialer) DialContext(ctx context.Context, network string, addrs
|
||||
for res := range ch {
|
||||
if res.err == nil {
|
||||
cancel()
|
||||
logger.Debug().Msgf("connected to %s", res.conn.RemoteAddr())
|
||||
logger.Debug("connected to", zap.String("remote_address", res.conn.RemoteAddr().String()))
|
||||
return res.conn, res.err
|
||||
}
|
||||
errs = append(errs, res.err)
|
||||
|
||||
206
log.go
206
log.go
@@ -3,8 +3,11 @@ package ctrld
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"time"
|
||||
|
||||
"github.com/rs/zerolog"
|
||||
"go.uber.org/zap"
|
||||
"go.uber.org/zap/zapcore"
|
||||
)
|
||||
|
||||
// LoggerCtxKey is the context.Context key for a logger.
|
||||
@@ -17,13 +20,13 @@ func LoggerCtx(ctx context.Context, l *Logger) context.Context {
|
||||
|
||||
// A Logger provides fast, leveled, structured logging.
|
||||
type Logger struct {
|
||||
*zerolog.Logger
|
||||
*zap.Logger
|
||||
}
|
||||
|
||||
var noOpZeroLogger = zerolog.Nop()
|
||||
var noOpZapLogger = zap.NewNop()
|
||||
|
||||
// NopLogger returns a logger which all operation are no-op.
|
||||
var NopLogger = &Logger{&noOpZeroLogger}
|
||||
var NopLogger = &Logger{noOpZapLogger}
|
||||
|
||||
// LoggerFromCtx returns the logger associated with given ctx.
|
||||
//
|
||||
@@ -38,9 +41,80 @@ func LoggerFromCtx(ctx context.Context) *Logger {
|
||||
// ReqIdCtxKey is the context.Context key for a request id.
|
||||
type ReqIdCtxKey struct{}
|
||||
|
||||
// Log emits the logs for a particular zerolog event.
|
||||
// LogEvent represents a logging event with structured fields
|
||||
type LogEvent struct {
|
||||
logger *zap.Logger
|
||||
level zapcore.Level
|
||||
fields []zap.Field
|
||||
}
|
||||
|
||||
// Msg logs the message with the collected fields
|
||||
func (e *LogEvent) Msg(msg string) {
|
||||
e.logger.Check(e.level, msg).Write(e.fields...)
|
||||
}
|
||||
|
||||
// Msgf logs a formatted message with the collected fields
|
||||
func (e *LogEvent) Msgf(format string, v ...any) {
|
||||
e.Msg(fmt.Sprintf(format, v...))
|
||||
}
|
||||
|
||||
// MsgFunc logs a message from a function with the collected fields
|
||||
func (e *LogEvent) MsgFunc(fn func() string) {
|
||||
e.Msg(fn())
|
||||
}
|
||||
|
||||
// Str adds a string field to the event
|
||||
func (e *LogEvent) Str(key, val string) *LogEvent {
|
||||
e.fields = append(e.fields, zap.String(key, val))
|
||||
return e
|
||||
}
|
||||
|
||||
// Int adds an integer field to the event
|
||||
func (e *LogEvent) Int(key string, val int) *LogEvent {
|
||||
e.fields = append(e.fields, zap.Int(key, val))
|
||||
return e
|
||||
}
|
||||
|
||||
// Int64 adds an int64 field to the event
|
||||
func (e *LogEvent) Int64(key string, val int64) *LogEvent {
|
||||
e.fields = append(e.fields, zap.Int64(key, val))
|
||||
return e
|
||||
}
|
||||
|
||||
// Err adds an error field to the event
|
||||
func (e *LogEvent) Err(err error) *LogEvent {
|
||||
if err != nil {
|
||||
e.fields = append(e.fields, zap.Error(err))
|
||||
}
|
||||
return e
|
||||
}
|
||||
|
||||
// Bool adds a boolean field to the event
|
||||
func (e *LogEvent) Bool(key string, val bool) *LogEvent {
|
||||
e.fields = append(e.fields, zap.Bool(key, val))
|
||||
return e
|
||||
}
|
||||
|
||||
// Interface adds an interface field to the event
|
||||
func (e *LogEvent) Interface(key string, val interface{}) *LogEvent {
|
||||
e.fields = append(e.fields, zap.Any(key, val))
|
||||
return e
|
||||
}
|
||||
|
||||
// Any adds an interface field to the event (alias for Interface)
|
||||
func (e *LogEvent) Any(key string, val interface{}) *LogEvent {
|
||||
return e.Interface(key, val)
|
||||
}
|
||||
|
||||
// Strs adds a string slice field to the event
|
||||
func (e *LogEvent) Strs(key string, vals []string) *LogEvent {
|
||||
e.fields = append(e.fields, zap.Strings(key, vals))
|
||||
return e
|
||||
}
|
||||
|
||||
// Log emits the logs for a particular logging event.
|
||||
// The request id associated with the context will be included if presents.
|
||||
func Log(ctx context.Context, e *zerolog.Event, format string, v ...any) {
|
||||
func Log(ctx context.Context, e *LogEvent, format string, v ...any) {
|
||||
id, ok := ctx.Value(ReqIdCtxKey{}).(string)
|
||||
if !ok {
|
||||
e.Msgf(format, v...)
|
||||
@@ -50,3 +124,123 @@ func Log(ctx context.Context, e *zerolog.Event, format string, v ...any) {
|
||||
return fmt.Sprintf("[%s] %s", id, fmt.Sprintf(format, v...))
|
||||
})
|
||||
}
|
||||
|
||||
// Logger methods that mimic zerolog API
|
||||
func (l *Logger) Debug() *LogEvent {
|
||||
return &LogEvent{
|
||||
logger: l.Logger,
|
||||
level: zapcore.DebugLevel,
|
||||
fields: []zap.Field{},
|
||||
}
|
||||
}
|
||||
|
||||
func (l *Logger) Info() *LogEvent {
|
||||
return &LogEvent{
|
||||
logger: l.Logger,
|
||||
level: zapcore.InfoLevel,
|
||||
fields: []zap.Field{},
|
||||
}
|
||||
}
|
||||
|
||||
func (l *Logger) Warn() *LogEvent {
|
||||
return &LogEvent{
|
||||
logger: l.Logger,
|
||||
level: zapcore.WarnLevel,
|
||||
fields: []zap.Field{},
|
||||
}
|
||||
}
|
||||
|
||||
func (l *Logger) Error() *LogEvent {
|
||||
return &LogEvent{
|
||||
logger: l.Logger,
|
||||
level: zapcore.ErrorLevel,
|
||||
fields: []zap.Field{},
|
||||
}
|
||||
}
|
||||
|
||||
func (l *Logger) Fatal() *LogEvent {
|
||||
return &LogEvent{
|
||||
logger: l.Logger,
|
||||
level: zapcore.FatalLevel,
|
||||
fields: []zap.Field{},
|
||||
}
|
||||
}
|
||||
|
||||
func (l *Logger) Notice() *LogEvent {
|
||||
return &LogEvent{
|
||||
logger: l.Logger,
|
||||
level: zapcore.InfoLevel, // zap doesn't have Notice level, use Info
|
||||
fields: []zap.Field{},
|
||||
}
|
||||
}
|
||||
|
||||
// With returns a logger with additional fields
|
||||
func (l *Logger) With() *Logger {
|
||||
return l
|
||||
}
|
||||
|
||||
// Str adds a string field to the logger
|
||||
func (l *Logger) Str(key, val string) *Logger {
|
||||
// Create a new logger with the field added
|
||||
newLogger := l.Logger.With(zap.String(key, val))
|
||||
return &Logger{newLogger}
|
||||
}
|
||||
|
||||
// Err adds an error field to the logger
|
||||
func (l *Logger) Err(err error) *Logger {
|
||||
// Create a new logger with the error field added
|
||||
newLogger := l.Logger.With(zap.Error(err))
|
||||
return &Logger{newLogger}
|
||||
}
|
||||
|
||||
// Any adds an interface field to the logger
|
||||
func (l *Logger) Any(key string, val interface{}) *Logger {
|
||||
// Create a new logger with the field added
|
||||
newLogger := l.Logger.With(zap.Any(key, val))
|
||||
return &Logger{newLogger}
|
||||
}
|
||||
|
||||
// Bool adds a boolean field to the logger
|
||||
func (l *Logger) Bool(key string, val bool) *Logger {
|
||||
// Create a new logger with the field added
|
||||
newLogger := l.Logger.With(zap.Bool(key, val))
|
||||
return &Logger{newLogger}
|
||||
}
|
||||
|
||||
// Msgf logs a formatted message at info level
|
||||
func (l *Logger) Msgf(format string, v ...any) {
|
||||
l.Info().Msgf(format, v...)
|
||||
}
|
||||
|
||||
// Msg logs a message at info level
|
||||
func (l *Logger) Msg(msg string) {
|
||||
l.Info().Msg(msg)
|
||||
}
|
||||
|
||||
// Output returns a logger with the specified output
|
||||
func (l *Logger) Output(w io.Writer) *Logger {
|
||||
// Create a new zap logger with the writer
|
||||
encoderConfig := zap.NewDevelopmentEncoderConfig()
|
||||
encoderConfig.TimeKey = "time"
|
||||
encoderConfig.EncodeTime = zapcore.TimeEncoderOfLayout(time.RFC3339)
|
||||
encoder := zapcore.NewConsoleEncoder(encoderConfig)
|
||||
core := zapcore.NewCore(encoder, zapcore.AddSync(w), zapcore.InfoLevel)
|
||||
newLogger := zap.New(core)
|
||||
return &Logger{newLogger}
|
||||
}
|
||||
|
||||
// GetLogger returns the underlying logger
|
||||
func (l *Logger) GetLogger() *Logger {
|
||||
return l
|
||||
}
|
||||
|
||||
// Write implements io.Writer to allow direct writing to the logger
|
||||
func (l *Logger) Write(p []byte) (n int, err error) {
|
||||
l.Info().Msg(string(p))
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
// Printf logs a formatted message at info level
|
||||
func (l *Logger) Printf(format string, v ...any) {
|
||||
l.Info().Msgf(format, v...)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user