mirror of
https://github.com/Control-D-Inc/ctrld.git
synced 2026-06-09 00:53:53 +02: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
016c566307
commit
d41334c66f
+9
-9
@@ -31,9 +31,9 @@ import (
|
|||||||
"github.com/kardianos/service"
|
"github.com/kardianos/service"
|
||||||
"github.com/miekg/dns"
|
"github.com/miekg/dns"
|
||||||
"github.com/pelletier/go-toml/v2"
|
"github.com/pelletier/go-toml/v2"
|
||||||
"github.com/rs/zerolog"
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
|
"go.uber.org/zap"
|
||||||
"tailscale.com/logtail/backoff"
|
"tailscale.com/logtail/backoff"
|
||||||
"tailscale.com/net/netmon"
|
"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 addr, err := net.ResolveUnixAddr("unix", sockPath); err == nil {
|
||||||
if conn, err := net.Dial(addr.Network(), addr.String()); err == nil {
|
if conn, err := net.Dial(addr.Network(), addr.String()); err == nil {
|
||||||
lc := &logConn{conn: conn}
|
lc := &logConn{conn: conn}
|
||||||
consoleWriter.Out = io.MultiWriter(os.Stdout, lc)
|
consoleWriter = newHumanReadableZapCore(io.MultiWriter(os.Stdout, lc), consoleWriterLevel)
|
||||||
p.logConn = lc
|
p.logConn = lc
|
||||||
} else {
|
} else {
|
||||||
if !errors.Is(err, os.ErrNotExist) {
|
if !errors.Is(err, os.ErrNotExist) {
|
||||||
@@ -307,7 +307,7 @@ func run(appCallback *AppCallback, stopCh chan struct{}) {
|
|||||||
return
|
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.
|
// Performs self-uninstallation if the ControlD device does not exist.
|
||||||
var uer *controld.ErrorResponse
|
var uer *controld.ErrorResponse
|
||||||
if errors.As(err, &uer) && uer.ErrorField.Code == controld.InvalidConfigCode {
|
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 {
|
if newLogPath := cfg.Service.LogPath; newLogPath != "" && oldLogPath != newLogPath {
|
||||||
// After processCDFlags, log config may change, so reset mainLog and re-init logging.
|
// After processCDFlags, log config may change, so reset mainLog and re-init logging.
|
||||||
l := zerolog.New(io.Discard)
|
l := zap.NewNop()
|
||||||
mainLog.Store(&ctrld.Logger{Logger: &l})
|
mainLog.Store(&ctrld.Logger{Logger: l})
|
||||||
|
|
||||||
// Copy logs written so far to new log file if possible.
|
// Copy logs written so far to new log file if possible.
|
||||||
if buf, err := os.ReadFile(oldLogPath); err == nil {
|
if buf, err := os.ReadFile(oldLogPath); err == nil {
|
||||||
@@ -603,11 +603,11 @@ func deactivationPinSet() bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func processCDFlags(cfg *ctrld.Config) (*controld.ResolverConfig, error) {
|
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)
|
logger.Info().Msgf("fetching Controld D configuration from API: %s", cdUID)
|
||||||
bo := backoff.NewBackoff("processCDFlags", logf, 30*time.Second)
|
bo := backoff.NewBackoff("processCDFlags", logf, 30*time.Second)
|
||||||
bo.LogLongerThan = 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)
|
resolverConfig, err := controld.FetchResolverConfig(ctx, cdUID, rootCmd.Version, cdDev)
|
||||||
for {
|
for {
|
||||||
if errUrlNetworkError(err) {
|
if errUrlNetworkError(err) {
|
||||||
@@ -1210,7 +1210,7 @@ func tryUpdateListenerConfig(cfg *ctrld.Config, notifyFunc func(), fatal bool) (
|
|||||||
return errors.Join(udpErr, tcpErr)
|
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 {
|
e.MsgFunc(func() string {
|
||||||
return fmt.Sprintf("listener.%d %s", listenerNum, fmt.Sprintf(format, v...))
|
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.
|
// 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)
|
s, err := newService(p, svcConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Warn().Err(err).Msg("failed to create new service")
|
logger.Warn().Err(err).Msg("failed to create new service")
|
||||||
|
|||||||
+1
-1
@@ -270,7 +270,7 @@ NOTE: running "ctrld start" without any arguments will start already installed c
|
|||||||
_, _ = patchNetIfaceName(iff)
|
_, _ = patchNetIfaceName(iff)
|
||||||
name = iff.Name
|
name = iff.Name
|
||||||
}
|
}
|
||||||
logger := mainLog.Load().With().Str("iface", name).Logger()
|
logger := mainLog.Load().With().Str("iface", name)
|
||||||
logger.Debug().Msg("setting DNS successfully")
|
logger.Debug().Msg("setting DNS successfully")
|
||||||
if res.All {
|
if res.All {
|
||||||
// Log that DNS is set for other interfaces.
|
// Log that DNS is set for other interfaces.
|
||||||
|
|||||||
@@ -1099,7 +1099,7 @@ func (p *prog) doSelfUninstall(pr *proxyResponse) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
logger := p.logger.Load().With().Str("mode", "self-uninstall").Logger()
|
logger := p.logger.Load().With().Str("mode", "self-uninstall")
|
||||||
if p.refusedQueryCount > selfUninstallMaxQueries {
|
if p.refusedQueryCount > selfUninstallMaxQueries {
|
||||||
p.checkingSelfUninstall = true
|
p.checkingSelfUninstall = true
|
||||||
loggerCtx := ctrld.LoggerCtx(context.Background(), p.logger.Load())
|
loggerCtx := ctrld.LoggerCtx(context.Background(), p.logger.Load())
|
||||||
|
|||||||
+69
-25
@@ -10,7 +10,8 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/rs/zerolog"
|
"go.uber.org/zap"
|
||||||
|
"go.uber.org/zap/zapcore"
|
||||||
|
|
||||||
"github.com/Control-D-Inc/ctrld"
|
"github.com/Control-D-Inc/ctrld"
|
||||||
)
|
)
|
||||||
@@ -95,16 +96,15 @@ func (lw *logWriter) Write(p []byte) (int, error) {
|
|||||||
|
|
||||||
// initLogging initializes global logging setup.
|
// initLogging initializes global logging setup.
|
||||||
func (p *prog) initLogging(backup bool) {
|
func (p *prog) initLogging(backup bool) {
|
||||||
zerolog.TimeFieldFormat = time.RFC3339 + ".000"
|
logCores := initLoggingWithBackup(backup)
|
||||||
logWriters := initLoggingWithBackup(backup)
|
|
||||||
|
|
||||||
// Initializing internal logging after global logging.
|
// Initializing internal logging after global logging.
|
||||||
p.initInternalLogging(logWriters)
|
p.initInternalLogging(logCores)
|
||||||
p.logger.Store(mainLog.Load())
|
p.logger.Store(mainLog.Load())
|
||||||
}
|
}
|
||||||
|
|
||||||
// initInternalLogging performs internal logging if there's no log enabled.
|
// 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() {
|
if !p.needInternalLogging() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -118,27 +118,25 @@ func (p *prog) initInternalLogging(writers []io.Writer) {
|
|||||||
lw := p.internalLogWriter
|
lw := p.internalLogWriter
|
||||||
wlw := p.internalWarnLogWriter
|
wlw := p.internalWarnLogWriter
|
||||||
p.mu.Unlock()
|
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.
|
// have enough information for troubleshooting.
|
||||||
if verbose == 0 {
|
internalCore := newHumanReadableZapCore(lw, zapcore.DebugLevel)
|
||||||
for i := range writers {
|
cores = append(cores, internalCore)
|
||||||
w := &zerolog.FilteredLevelWriter{
|
|
||||||
Writer: zerolog.LevelWriterAdapter{Writer: writers[i]},
|
// Add core for internal warn log writer
|
||||||
Level: zerolog.NoticeLevel,
|
warnCore := newHumanReadableZapCore(wlw, zapcore.WarnLevel)
|
||||||
}
|
cores = append(cores, warnCore)
|
||||||
writers[i] = w
|
|
||||||
}
|
// Create a multi-core logger
|
||||||
zerolog.SetGlobalLevel(zerolog.DebugLevel)
|
multiCore := zapcore.NewTee(cores...)
|
||||||
}
|
logger := zap.New(multiCore)
|
||||||
writers = append(writers, lw)
|
mainLog.Store(&ctrld.Logger{Logger: logger})
|
||||||
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})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// needInternalLogging reports whether prog needs to run internal logging.
|
// needInternalLogging reports whether prog needs to run internal logging.
|
||||||
@@ -202,3 +200,49 @@ func (p *prog) logReader() (*logReader, error) {
|
|||||||
}
|
}
|
||||||
return lr, nil
|
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)
|
||||||
|
}
|
||||||
|
|||||||
+56
-35
@@ -5,10 +5,10 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/kardianos/service"
|
"github.com/kardianos/service"
|
||||||
"github.com/rs/zerolog"
|
"go.uber.org/zap"
|
||||||
|
"go.uber.org/zap/zapcore"
|
||||||
|
|
||||||
"github.com/Control-D-Inc/ctrld"
|
"github.com/Control-D-Inc/ctrld"
|
||||||
)
|
)
|
||||||
@@ -40,9 +40,10 @@ var (
|
|||||||
cleanup bool
|
cleanup bool
|
||||||
startOnly bool
|
startOnly bool
|
||||||
|
|
||||||
mainLog atomic.Pointer[ctrld.Logger]
|
mainLog atomic.Pointer[ctrld.Logger]
|
||||||
consoleWriter zerolog.ConsoleWriter
|
consoleWriter zapcore.Core
|
||||||
noConfigStart bool
|
consoleWriterLevel zapcore.Level
|
||||||
|
noConfigStart bool
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -53,8 +54,8 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
l := zerolog.New(io.Discard)
|
l := zap.NewNop()
|
||||||
mainLog.Store(&ctrld.Logger{Logger: &l})
|
mainLog.Store(&ctrld.Logger{Logger: l})
|
||||||
}
|
}
|
||||||
|
|
||||||
func Main() {
|
func Main() {
|
||||||
@@ -82,23 +83,23 @@ func normalizeLogFilePath(logFilePath string) string {
|
|||||||
|
|
||||||
// initConsoleLogging initializes console logging, then storing to mainLog.
|
// initConsoleLogging initializes console logging, then storing to mainLog.
|
||||||
func initConsoleLogging() {
|
func initConsoleLogging() {
|
||||||
consoleWriter = zerolog.NewConsoleWriter(func(w *zerolog.ConsoleWriter) {
|
consoleWriterLevel = zapcore.InfoLevel
|
||||||
w.TimeFormat = time.StampMilli
|
|
||||||
})
|
|
||||||
multi := zerolog.MultiLevelWriter(consoleWriter)
|
|
||||||
l := mainLog.Load().Output(multi).With().Timestamp().Logger()
|
|
||||||
mainLog.Store(&ctrld.Logger{Logger: &l})
|
|
||||||
|
|
||||||
switch {
|
switch {
|
||||||
case silent:
|
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:
|
case verbose == 1:
|
||||||
zerolog.SetGlobalLevel(zerolog.InfoLevel)
|
// Info level is default
|
||||||
case verbose > 1:
|
case verbose > 1:
|
||||||
zerolog.SetGlobalLevel(zerolog.DebugLevel)
|
// Debug level
|
||||||
|
consoleWriterLevel = zapcore.DebugLevel
|
||||||
default:
|
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
|
// initInteractiveLogging is like initLogging, but the ProxyLogger is discarded
|
||||||
@@ -108,7 +109,6 @@ func initConsoleLogging() {
|
|||||||
func initInteractiveLogging() {
|
func initInteractiveLogging() {
|
||||||
old := cfg.Service.LogPath
|
old := cfg.Service.LogPath
|
||||||
cfg.Service.LogPath = ""
|
cfg.Service.LogPath = ""
|
||||||
zerolog.TimeFieldFormat = time.RFC3339 + ".000"
|
|
||||||
initLoggingWithBackup(false)
|
initLoggingWithBackup(false)
|
||||||
cfg.Service.LogPath = old
|
cfg.Service.LogPath = old
|
||||||
}
|
}
|
||||||
@@ -119,7 +119,7 @@ func initInteractiveLogging() {
|
|||||||
// This is only used in runCmd for special handling in case of logging config
|
// 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
|
// change in cd mode. Without special reason, the caller should use initLogging
|
||||||
// wrapper instead of calling this function directly.
|
// wrapper instead of calling this function directly.
|
||||||
func initLoggingWithBackup(doBackup bool) []io.Writer {
|
func initLoggingWithBackup(doBackup bool) []zapcore.Core {
|
||||||
var writers []io.Writer
|
var writers []io.Writer
|
||||||
if logFilePath := normalizeLogFilePath(cfg.Service.LogPath); logFilePath != "" {
|
if logFilePath := normalizeLogFilePath(cfg.Service.LogPath); logFilePath != "" {
|
||||||
// Create parent directory if necessary.
|
// Create parent directory if necessary.
|
||||||
@@ -146,32 +146,53 @@ func initLoggingWithBackup(doBackup bool) []io.Writer {
|
|||||||
}
|
}
|
||||||
writers = append(writers, logFile)
|
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
|
logLevel := cfg.Service.LogLevel
|
||||||
switch {
|
switch {
|
||||||
case silent:
|
case silent:
|
||||||
zerolog.SetGlobalLevel(zerolog.NoLevel)
|
// For silent mode, use a no-op logger
|
||||||
return writers
|
l := zap.NewNop()
|
||||||
|
mainLog.Store(&ctrld.Logger{Logger: l})
|
||||||
|
return cores
|
||||||
case verbose == 1:
|
case verbose == 1:
|
||||||
logLevel = "info"
|
logLevel = "info"
|
||||||
case verbose > 1:
|
case verbose > 1:
|
||||||
logLevel = "debug"
|
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 {
|
consoleWriter.Enabled(level)
|
||||||
mainLog.Load().Warn().Err(err).Msg("could not set log level")
|
// Add cores for all writers
|
||||||
return 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() {
|
func initCache() {
|
||||||
|
|||||||
+16
-3
@@ -5,7 +5,8 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/rs/zerolog"
|
"go.uber.org/zap"
|
||||||
|
"go.uber.org/zap/zapcore"
|
||||||
|
|
||||||
"github.com/Control-D-Inc/ctrld"
|
"github.com/Control-D-Inc/ctrld"
|
||||||
)
|
)
|
||||||
@@ -13,7 +14,19 @@ import (
|
|||||||
var logOutput strings.Builder
|
var logOutput strings.Builder
|
||||||
|
|
||||||
func TestMain(m *testing.M) {
|
func TestMain(m *testing.M) {
|
||||||
l := zerolog.New(&logOutput)
|
// Create a custom writer that writes to logOutput
|
||||||
mainLog.Store(&ctrld.Logger{Logger: &l})
|
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())
|
os.Exit(m.Run())
|
||||||
}
|
}
|
||||||
|
|||||||
+12
-13
@@ -24,7 +24,6 @@ import (
|
|||||||
|
|
||||||
"github.com/Masterminds/semver/v3"
|
"github.com/Masterminds/semver/v3"
|
||||||
"github.com/kardianos/service"
|
"github.com/kardianos/service"
|
||||||
"github.com/rs/zerolog"
|
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
"golang.org/x/sync/singleflight"
|
"golang.org/x/sync/singleflight"
|
||||||
"tailscale.com/net/netmon"
|
"tailscale.com/net/netmon"
|
||||||
@@ -296,7 +295,7 @@ func (p *prog) apiConfigReload() {
|
|||||||
ticker := time.NewTicker(timeDurationOrDefault(p.cfg.Service.RefetchTime, 3600) * time.Second)
|
ticker := time.NewTicker(timeDurationOrDefault(p.cfg.Service.RefetchTime, 3600) * time.Second)
|
||||||
defer ticker.Stop()
|
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")
|
logger.Debug().Msg("starting custom config reload timer")
|
||||||
lastUpdated := time.Now().Unix()
|
lastUpdated := time.Now().Unix()
|
||||||
curVerStr := curVersion()
|
curVerStr := curVersion()
|
||||||
@@ -310,7 +309,7 @@ func (p *prog) apiConfigReload() {
|
|||||||
l.Msgf("current version is not stable, skipping self-upgrade: %s", curVerStr)
|
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())
|
loggerCtx := ctrld.LoggerCtx(context.Background(), p.logger.Load())
|
||||||
resolverConfig, err := controld.FetchResolverConfig(loggerCtx, cdUID, rootCmd.Version, cdDev)
|
resolverConfig, err := controld.FetchResolverConfig(loggerCtx, cdUID, rootCmd.Version, cdDev)
|
||||||
selfUninstallCheck(err, p, logger)
|
selfUninstallCheck(err, p, logger)
|
||||||
@@ -321,7 +320,7 @@ func (p *prog) apiConfigReload() {
|
|||||||
|
|
||||||
// Performing self-upgrade check for production version.
|
// Performing self-upgrade check for production version.
|
||||||
if isStable {
|
if isStable {
|
||||||
_ = selfUpgradeCheck(resolverConfig.Ctrld.VersionTarget, curVer, &logger)
|
_ = selfUpgradeCheck(resolverConfig.Ctrld.VersionTarget, curVer, logger)
|
||||||
}
|
}
|
||||||
|
|
||||||
if resolverConfig.DeactivationPin != nil {
|
if resolverConfig.DeactivationPin != nil {
|
||||||
@@ -384,7 +383,7 @@ func (p *prog) apiConfigReload() {
|
|||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case <-p.apiForceReloadCh:
|
case <-p.apiForceReloadCh:
|
||||||
doReloadApiConfig(true, logger.With().Bool("forced", true).Logger())
|
doReloadApiConfig(true, logger.With().Bool("forced", true))
|
||||||
case <-ticker.C:
|
case <-ticker.C:
|
||||||
doReloadApiConfig(false, logger)
|
doReloadApiConfig(false, logger)
|
||||||
case <-p.stopCh:
|
case <-p.stopCh:
|
||||||
@@ -578,7 +577,7 @@ func (p *prog) run(reload bool, reloadCh chan struct{}) {
|
|||||||
|
|
||||||
if !reload {
|
if !reload {
|
||||||
// Stop writing log to unix socket.
|
// Stop writing log to unix socket.
|
||||||
consoleWriter.Out = os.Stdout
|
consoleWriter = newHumanReadableZapCore(os.Stdout, consoleWriterLevel)
|
||||||
p.initLogging(false)
|
p.initLogging(false)
|
||||||
if p.logConn != nil {
|
if p.logConn != nil {
|
||||||
_ = p.logConn.Close()
|
_ = p.logConn.Close()
|
||||||
@@ -758,7 +757,7 @@ func (p *prog) setDnsForRunningIface(nameservers []string) (runningIface *net.In
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
logger := p.logger.Load().With().Str("iface", p.runningIface).Logger()
|
logger := p.logger.Load().With().Str("iface", p.runningIface)
|
||||||
|
|
||||||
const maxDNSRetryAttempts = 3
|
const maxDNSRetryAttempts = 3
|
||||||
const retryDelay = 1 * time.Second
|
const retryDelay = 1 * time.Second
|
||||||
@@ -774,7 +773,7 @@ func (p *prog) setDnsForRunningIface(nameservers []string) (runningIface *net.In
|
|||||||
newIface := p.findWorkingInterface()
|
newIface := p.findWorkingInterface()
|
||||||
if newIface != p.runningIface {
|
if newIface != p.runningIface {
|
||||||
p.runningIface = newIface
|
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")
|
logger.Info().Msg("switched to new interface")
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@@ -930,7 +929,7 @@ func (p *prog) resetDNSForRunningIface(isStart bool, restoreStatic bool) (runnin
|
|||||||
p.Debug().Msg("no running interface, skipping resetDNS")
|
p.Debug().Msg("no running interface, skipping resetDNS")
|
||||||
return
|
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)
|
netIface, err := netInterface(p.runningIface)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Error().Err(err).Msg("could not get interface")
|
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.
|
// 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
|
var uer *controld.ErrorResponse
|
||||||
if errors.As(uninstallErr, &uer) && uer.ErrorField.Code == controld.InvalidConfigCode {
|
if errors.As(uninstallErr, &uer) && uer.ErrorField.Code == controld.InvalidConfigCode {
|
||||||
p.stopDnsWatchers()
|
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.
|
// The callers must ensure curVer and logger are non-nil.
|
||||||
// Returns true if upgrade is allowed, false otherwise.
|
// 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 == "" {
|
if vt == "" {
|
||||||
logger.Debug().Msg("no version target set, skipped checking self-upgrade")
|
logger.Debug().Msg("no version target set, skipped checking self-upgrade")
|
||||||
return false
|
return false
|
||||||
@@ -1468,7 +1467,7 @@ func shouldUpgrade(vt string, cv *semver.Version, logger *zerolog.Logger) bool {
|
|||||||
|
|
||||||
// performUpgrade executes the self-upgrade command.
|
// performUpgrade executes the self-upgrade command.
|
||||||
// Returns true if upgrade was initiated successfully, false otherwise.
|
// 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()
|
exe, err := os.Executable()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Error().Err(err).Msg("failed to get executable path, skipped self-upgrade")
|
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.
|
// The callers must ensure curVer and logger are non-nil.
|
||||||
// Returns true if upgrade is allowed and should proceed, false otherwise.
|
// 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) {
|
if shouldUpgrade(vt, cv, logger) {
|
||||||
return performUpgrade(vt, logger)
|
return performUpgrade(vt, logger)
|
||||||
}
|
}
|
||||||
|
|||||||
+7
-7
@@ -1,33 +1,33 @@
|
|||||||
package cli
|
package cli
|
||||||
|
|
||||||
import "github.com/rs/zerolog"
|
import "github.com/Control-D-Inc/ctrld"
|
||||||
|
|
||||||
// Debug starts a new message with debug level.
|
// 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()
|
return p.logger.Load().Debug()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Warn starts a new message with warn level.
|
// 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()
|
return p.logger.Load().Warn()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Info starts a new message with info level.
|
// 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()
|
return p.logger.Load().Info()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fatal starts a new message with fatal level.
|
// 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()
|
return p.logger.Load().Fatal()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Error starts a new message with error level.
|
// 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()
|
return p.logger.Load().Error()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Notice starts a new message with notice level.
|
// 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()
|
return p.logger.Load().Notice()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,8 +5,8 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/Masterminds/semver/v3"
|
"github.com/Masterminds/semver/v3"
|
||||||
"github.com/rs/zerolog"
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
"go.uber.org/zap"
|
||||||
|
|
||||||
"github.com/Control-D-Inc/ctrld"
|
"github.com/Control-D-Inc/ctrld"
|
||||||
)
|
)
|
||||||
@@ -173,10 +173,10 @@ func Test_shouldUpgrade(t *testing.T) {
|
|||||||
tc := tc
|
tc := tc
|
||||||
t.Run(tc.name, func(t *testing.T) {
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
// Create test logger
|
// Create test logger
|
||||||
testLogger := zerolog.New(zerolog.NewTestWriter(t)).With().Logger()
|
testLogger := &ctrld.Logger{Logger: zap.NewNop()}
|
||||||
|
|
||||||
// Call the function and capture the result
|
// 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 the expected result
|
||||||
assert.Equal(t, tc.shouldUpgrade, result, tc.description)
|
assert.Equal(t, tc.shouldUpgrade, result, tc.description)
|
||||||
@@ -221,10 +221,10 @@ func Test_selfUpgradeCheck(t *testing.T) {
|
|||||||
tc := tc
|
tc := tc
|
||||||
t.Run(tc.name, func(t *testing.T) {
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
// Create test logger
|
// Create test logger
|
||||||
testLogger := zerolog.New(zerolog.NewTestWriter(t)).With().Logger()
|
testLogger := &ctrld.Logger{Logger: zap.NewNop()}
|
||||||
|
|
||||||
// Call the function and capture the result
|
// 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 the expected result
|
||||||
assert.Equal(t, tc.shouldUpgrade, result, tc.description)
|
assert.Equal(t, tc.shouldUpgrade, result, tc.description)
|
||||||
@@ -256,8 +256,10 @@ func Test_performUpgrade(t *testing.T) {
|
|||||||
for _, tc := range tests {
|
for _, tc := range tests {
|
||||||
tc := tc
|
tc := tc
|
||||||
t.Run(tc.name, func(t *testing.T) {
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
// Create test logger
|
||||||
|
testLogger := &ctrld.Logger{Logger: zap.NewNop()}
|
||||||
// Call the function and capture the result
|
// Call the function and capture the result
|
||||||
result := performUpgrade(tc.versionTarget)
|
result := performUpgrade(tc.versionTarget, testLogger)
|
||||||
assert.Equal(t, tc.expectedResult, result, tc.description)
|
assert.Equal(t, tc.expectedResult, result, tc.description)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,10 +5,10 @@ package cli
|
|||||||
import (
|
import (
|
||||||
"os"
|
"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) {
|
if uninstallInvalidCdUID(p, logger, false) {
|
||||||
logger.Warn().Msgf("service was uninstalled because device %q does not exist", cdUID)
|
logger.Warn().Msgf("service was uninstalled because device %q does not exist", cdUID)
|
||||||
os.Exit(0)
|
os.Exit(0)
|
||||||
|
|||||||
@@ -9,10 +9,10 @@ import (
|
|||||||
"runtime"
|
"runtime"
|
||||||
"syscall"
|
"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" {
|
if runtime.GOOS == "linux" {
|
||||||
selfUninstallLinux(p, logger)
|
selfUninstallLinux(p, logger)
|
||||||
}
|
}
|
||||||
@@ -37,7 +37,7 @@ func selfUninstall(p *prog, logger zerolog.Logger) {
|
|||||||
os.Exit(0)
|
os.Exit(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
func selfUninstallLinux(p *prog, logger zerolog.Logger) {
|
func selfUninstallLinux(p *prog, logger *ctrld.Logger) {
|
||||||
if uninstallInvalidCdUID(p, logger, true) {
|
if uninstallInvalidCdUID(p, logger, true) {
|
||||||
logger.Warn().Msgf("service was uninstalled because device %q does not exist", cdUID)
|
logger.Warn().Msgf("service was uninstalled because device %q does not exist", cdUID)
|
||||||
os.Exit(0)
|
os.Exit(0)
|
||||||
|
|||||||
@@ -30,11 +30,11 @@ require (
|
|||||||
github.com/prometheus/client_model v0.5.0
|
github.com/prometheus/client_model v0.5.0
|
||||||
github.com/prometheus/prom2json v1.3.3
|
github.com/prometheus/prom2json v1.3.3
|
||||||
github.com/quic-go/quic-go v0.48.2
|
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/cobra v1.8.1
|
||||||
github.com/spf13/viper v1.16.0
|
github.com/spf13/viper v1.16.0
|
||||||
github.com/stretchr/testify v1.9.0
|
github.com/stretchr/testify v1.9.0
|
||||||
github.com/vishvananda/netlink v1.2.1-beta.2
|
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/net v0.38.0
|
||||||
golang.org/x/sync v0.12.0
|
golang.org/x/sync v0.12.0
|
||||||
golang.org/x/sys v0.31.0
|
golang.org/x/sys v0.31.0
|
||||||
@@ -65,8 +65,6 @@ require (
|
|||||||
github.com/kr/text v0.2.0 // indirect
|
github.com/kr/text v0.2.0 // indirect
|
||||||
github.com/leodido/go-urn v1.2.1 // indirect
|
github.com/leodido/go-urn v1.2.1 // indirect
|
||||||
github.com/magiconair/properties v1.8.7 // 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/mattn/go-runewidth v0.0.14 // indirect
|
||||||
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
|
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
|
||||||
github.com/mdlayher/netlink v1.7.2 // 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/u-root/uio v0.0.0-20240118234441-a3c409a6018e // indirect
|
||||||
github.com/vishvananda/netns v0.0.4 // indirect
|
github.com/vishvananda/netns v0.0.4 // indirect
|
||||||
go.uber.org/mock v0.4.0 // 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/mem v0.0.0-20220726221520-4f986261bf13 // indirect
|
||||||
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba // indirect
|
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba // indirect
|
||||||
golang.org/x/crypto v0.36.0 // indirect
|
golang.org/x/crypto v0.36.0 // indirect
|
||||||
|
|||||||
@@ -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/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 h1:RN9w6+7QoMeJVGyfmbcgs28Br8cvmnucEXnY0rYXWg0=
|
||||||
github.com/Masterminds/semver/v3 v3.2.1/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ=
|
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 h1:LHTHcTQiSGT7VVbI0o4wBRNQIgn917usHWOd6VAffYI=
|
||||||
github.com/alexbrainman/sspi v0.0.0-20231016080023-1a75b4708caa/go.mod h1:cEWa1LVoE5KvSD9ONXsZrj0z6KqySlCCNKHlLzbqAt4=
|
github.com/alexbrainman/sspi v0.0.0-20231016080023-1a75b4708caa/go.mod h1:cEWa1LVoE5KvSD9ONXsZrj0z6KqySlCCNKHlLzbqAt4=
|
||||||
github.com/ameshkov/dnsstamps v1.0.3 h1:Srzik+J9mivH1alRACTbys2xOxs0lRH9qnTA7Y1OYVo=
|
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/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 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY=
|
||||||
github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
|
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.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 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU=
|
||||||
github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
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.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 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=
|
||||||
github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=
|
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/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 h1:stMpOSZFs//0Lv29HduCmli3GUfpFoF3Y1Q/aXj/wVM=
|
||||||
github.com/spf13/afero v1.9.5/go.mod h1:UBogFpq8E9Hx+xc5CNTTEpTnuHVmXDwZcZcE1eb/UhQ=
|
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.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||||
go.opencensus.io v0.22.4/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.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 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU=
|
||||||
go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc=
|
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 h1:CbZeCBZ0aZj8EfVgnqQcYZgf0lpZ3H9rmp5nkDTAst8=
|
||||||
go4.org/mem v0.0.0-20220726221520-4f986261bf13/go.mod h1:reUoABIJ9ikfM5sgtSF3Wushcza7+WeD01VB9Lirh3g=
|
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=
|
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-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-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-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.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.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.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 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
|
||||||
golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
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=
|
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||||
|
|||||||
+8
-9
@@ -3,7 +3,6 @@ package net
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"io"
|
|
||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
@@ -12,7 +11,7 @@ import (
|
|||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/rs/zerolog"
|
"go.uber.org/zap"
|
||||||
"tailscale.com/logtail/backoff"
|
"tailscale.com/logtail/backoff"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -34,8 +33,8 @@ var Dialer = &net.Dialer{
|
|||||||
Dial: func(ctx context.Context, network, address string) (net.Conn, error) {
|
Dial: func(ctx context.Context, network, address string) (net.Conn, error) {
|
||||||
d := ParallelDialer{}
|
d := ParallelDialer{}
|
||||||
d.Timeout = 10 * time.Second
|
d.Timeout = 10 * time.Second
|
||||||
l := zerolog.New(io.Discard)
|
l := zap.NewNop()
|
||||||
return d.DialContext(ctx, "udp", []string{v4BootstrapDNS, v6BootstrapDNS}, &l)
|
return d.DialContext(ctx, "udp", []string{v4BootstrapDNS, v6BootstrapDNS}, l)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -161,7 +160,7 @@ type ParallelDialer struct {
|
|||||||
net.Dialer
|
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 {
|
if len(addrs) == 0 {
|
||||||
return nil, errors.New("empty addresses")
|
return nil, errors.New("empty addresses")
|
||||||
}
|
}
|
||||||
@@ -181,16 +180,16 @@ func (d *ParallelDialer) DialContext(ctx context.Context, network string, addrs
|
|||||||
for _, addr := range addrs {
|
for _, addr := range addrs {
|
||||||
go func(addr string) {
|
go func(addr string) {
|
||||||
defer wg.Done()
|
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)
|
conn, err := d.Dialer.DialContext(ctx, network, addr)
|
||||||
if err != nil {
|
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 {
|
select {
|
||||||
case ch <- ¶llelDialerResult{conn: conn, err: err}:
|
case ch <- ¶llelDialerResult{conn: conn, err: err}:
|
||||||
case <-done:
|
case <-done:
|
||||||
if conn != nil {
|
if conn != nil {
|
||||||
logger.Debug().Msgf("connection closed: %s", conn.RemoteAddr())
|
logger.Debug("connection closed", zap.String("remote_address", conn.RemoteAddr().String()))
|
||||||
conn.Close()
|
conn.Close()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -201,7 +200,7 @@ func (d *ParallelDialer) DialContext(ctx context.Context, network string, addrs
|
|||||||
for res := range ch {
|
for res := range ch {
|
||||||
if res.err == nil {
|
if res.err == nil {
|
||||||
cancel()
|
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
|
return res.conn, res.err
|
||||||
}
|
}
|
||||||
errs = append(errs, res.err)
|
errs = append(errs, res.err)
|
||||||
|
|||||||
@@ -3,8 +3,11 @@ package ctrld
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"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.
|
// 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.
|
// A Logger provides fast, leveled, structured logging.
|
||||||
type Logger struct {
|
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.
|
// 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.
|
// 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.
|
// ReqIdCtxKey is the context.Context key for a request id.
|
||||||
type ReqIdCtxKey struct{}
|
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.
|
// 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)
|
id, ok := ctx.Value(ReqIdCtxKey{}).(string)
|
||||||
if !ok {
|
if !ok {
|
||||||
e.Msgf(format, v...)
|
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...))
|
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