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:
Cuong Manh Le
2025-07-21 19:50:43 +07:00
committed by Cuong Manh Le
parent 2996a161cd
commit ddbb0f0db4
15 changed files with 400 additions and 135 deletions

View File

@@ -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")

View File

@@ -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.

View File

@@ -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())

View File

@@ -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)
}

View File

@@ -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() {

View File

@@ -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())
}

View File

@@ -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)
}

View File

@@ -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()
}

View File

@@ -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)
})
}

View File

@@ -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)

View File

@@ -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)