Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,12 @@ module github.com/stacklok/toolhive-core
go 1.25.6

require (
github.com/go-logr/logr v1.4.3
github.com/go-logr/zapr v1.3.0
github.com/google/cel-go v0.27.0
github.com/stretchr/testify v1.11.1
go.uber.org/mock v0.6.0
go.uber.org/zap v1.27.1
golang.org/x/net v0.49.0
)

Expand Down
197 changes: 197 additions & 0 deletions logger/logger.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,197 @@
// SPDX-FileCopyrightText: Copyright 2025 Stacklok, Inc.
// SPDX-License-Identifier: Apache-2.0

// Package logger provides a logging capability for toolhive projects for running locally as a CLI and in Kubernetes
package logger

import (
"strconv"
"time"

"github.com/go-logr/logr"

Check failure on line 11 in logger/logger.go

View workflow job for this annotation

GitHub Actions / Security / Go Vulnerability Check

could not import github.com/go-logr/logr (invalid package name: "")

Check failure on line 11 in logger/logger.go

View workflow job for this annotation

GitHub Actions / Security / Go Vulnerability Check

missing go.sum entry for module providing package github.com/go-logr/logr (imported by github.com/stacklok/toolhive-core/logger); to add:

Check failure on line 11 in logger/logger.go

View workflow job for this annotation

GitHub Actions / Test / Unit Tests

missing go.sum entry for module providing package github.com/go-logr/logr (imported by github.com/stacklok/toolhive-core/logger); to add:
"github.com/go-logr/zapr"

Check failure on line 12 in logger/logger.go

View workflow job for this annotation

GitHub Actions / Security / Go Vulnerability Check

could not import github.com/go-logr/zapr (invalid package name: "")

Check failure on line 12 in logger/logger.go

View workflow job for this annotation

GitHub Actions / Security / Go Vulnerability Check

missing go.sum entry for module providing package github.com/go-logr/zapr (imported by github.com/stacklok/toolhive-core/logger); to add:

Check failure on line 12 in logger/logger.go

View workflow job for this annotation

GitHub Actions / Test / Unit Tests

missing go.sum entry for module providing package github.com/go-logr/zapr (imported by github.com/stacklok/toolhive-core/logger); to add:
"go.uber.org/zap"

Check failure on line 13 in logger/logger.go

View workflow job for this annotation

GitHub Actions / Security / Go Vulnerability Check

could not import go.uber.org/zap (invalid package name: "")

Check failure on line 13 in logger/logger.go

View workflow job for this annotation

GitHub Actions / Security / Go Vulnerability Check

missing go.sum entry for module providing package go.uber.org/zap (imported by github.com/stacklok/toolhive-core/logger); to add:

Check failure on line 13 in logger/logger.go

View workflow job for this annotation

GitHub Actions / Test / Unit Tests

missing go.sum entry for module providing package go.uber.org/zap (imported by github.com/stacklok/toolhive-core/logger); to add:
"go.uber.org/zap/zapcore"

Check failure on line 14 in logger/logger.go

View workflow job for this annotation

GitHub Actions / Security / Go Vulnerability Check

could not import go.uber.org/zap/zapcore (invalid package name: "")

Check failure on line 14 in logger/logger.go

View workflow job for this annotation

GitHub Actions / Security / Go Vulnerability Check

missing go.sum entry for module providing package go.uber.org/zap/zapcore (imported by github.com/stacklok/toolhive-core/logger); to add:

Check failure on line 14 in logger/logger.go

View workflow job for this annotation

GitHub Actions / Test / Unit Tests

missing go.sum entry for module providing package go.uber.org/zap/zapcore (imported by github.com/stacklok/toolhive-core/logger); to add:

"github.com/stacklok/toolhive-core/env"
)

// Debug logs a message at debug level using the singleton logger.
func Debug(msg string) {
zap.S().Debug(msg)
}

// Debugf logs a message at debug level using the singleton logger.
func Debugf(msg string, args ...any) {
zap.S().Debugf(msg, args...)
}

// Debugw logs a message at debug level using the singleton logger with additional key-value pairs.
func Debugw(msg string, keysAndValues ...any) {
zap.S().Debugw(msg, keysAndValues...)
}

// Info logs a message at info level using the singleton logger.
func Info(msg string) {
zap.S().Info(msg)
}

// Infof logs a message at info level using the singleton logger.
func Infof(msg string, args ...any) {
zap.S().Infof(msg, args...)
}

// Infow logs a message at info level using the singleton logger with additional key-value pairs.
func Infow(msg string, keysAndValues ...any) {
zap.S().Infow(msg, keysAndValues...)
}

// Warn logs a message at warning level using the singleton logger.
func Warn(msg string) {
zap.S().Warn(msg)
}

// Warnf logs a message at warning level using the singleton logger.
func Warnf(msg string, args ...any) {
zap.S().Warnf(msg, args...)
}

// Warnw logs a message at warning level using the singleton logger with additional key-value pairs.
func Warnw(msg string, keysAndValues ...any) {
zap.S().Warnw(msg, keysAndValues...)
}

// Error logs a message at error level using the singleton logger.
func Error(msg string) {
zap.S().Error(msg)
}

// Errorf logs a message at error level using the singleton logger.
func Errorf(msg string, args ...any) {
zap.S().Errorf(msg, args...)
}

// Errorw logs a message at error level using the singleton logger with additional key-value pairs.
func Errorw(msg string, keysAndValues ...any) {
zap.S().Errorw(msg, keysAndValues...)
}

// Panic logs a message at error level using the singleton logger and panics the program.
func Panic(msg string) {
zap.S().Panic(msg)
}

// Panicf logs a message at error level using the singleton logger and panics the program.
func Panicf(msg string, args ...any) {
zap.S().Panicf(msg, args...)
}

// Panicw logs a message at error level using the singleton logger with additional key-value pairs and panics the program.
func Panicw(msg string, keysAndValues ...any) {
zap.S().Panicw(msg, keysAndValues...)
}

// DPanic logs a message at error level using the singleton logger and panics the program.
func DPanic(msg string) {
zap.S().DPanic(msg)
}

// DPanicf logs a message at error level using the singleton logger and panics the program.
func DPanicf(msg string, args ...any) {
zap.S().DPanicf(msg, args...)
}

// DPanicw logs a message at error level using the singleton logger with additional key-value pairs and panics the program.
func DPanicw(msg string, keysAndValues ...any) {
zap.S().DPanicw(msg, keysAndValues...)
}

// Fatal logs a message at error level using the singleton logger and exits the program.
func Fatal(msg string) {
zap.S().Fatal(msg)
}

// Fatalf logs a message at error level using the singleton logger and exits the program.
func Fatalf(msg string, args ...any) {
zap.S().Fatalf(msg, args...)
}

// Fatalw logs a message at error level using the singleton logger with additional key-value pairs and exits the program.
func Fatalw(msg string, keysAndValues ...any) {
zap.S().Fatalw(msg, keysAndValues...)
}

// NewLogr returns a logr.Logger which uses zap logger
func NewLogr() logr.Logger {
return zapr.NewLogger(zap.L())
}

// DebugProvider is an interface for checking if debug mode is enabled.
// This allows different projects to plug in their own debug flag implementation.
type DebugProvider interface {
IsDebug() bool
}

// defaultDebugProvider provides a default implementation that returns false.
type defaultDebugProvider struct{}

func (*defaultDebugProvider) IsDebug() bool {
return false
}

// Initialize creates and configures the appropriate logger using the default debug provider.
// If the UNSTRUCTURED_LOGS is set to true, it will output plain log message
// with only time and LogLevelType (INFO, DEBUG, ERROR, WARN).
// Otherwise it will create a standard structured slog logger.
func Initialize() {
InitializeWithOptions(&env.OSReader{}, &defaultDebugProvider{})
}

// InitializeWithDebug creates and configures the logger with a custom debug provider.
// This allows callers to plug in their own debug flag implementation (e.g., viper).
func InitializeWithDebug(debugProvider DebugProvider) {
InitializeWithOptions(&env.OSReader{}, debugProvider)
}

// InitializeWithEnv creates and configures the appropriate logger with a custom environment reader.
// This allows for dependency injection of environment variable access for testing.
// Deprecated: Use InitializeWithOptions instead.
func InitializeWithEnv(envReader env.Reader) {
InitializeWithOptions(envReader, &defaultDebugProvider{})
}

// InitializeWithOptions creates and configures the logger with custom environment reader and debug provider.
// This provides full control over logger configuration for both testing and production use.
func InitializeWithOptions(envReader env.Reader, debugProvider DebugProvider) {
var config zap.Config
if unstructuredLogsWithEnv(envReader) {
config = zap.NewDevelopmentConfig()
config.EncoderConfig.EncodeLevel = zapcore.CapitalColorLevelEncoder
config.EncoderConfig.EncodeTime = zapcore.TimeEncoderOfLayout(time.Kitchen)
config.OutputPaths = []string{"stderr"}
config.DisableStacktrace = true
config.DisableCaller = true
} else {
config = zap.NewProductionConfig()
config.OutputPaths = []string{"stdout"}
}

// Set log level based on current debug flag
if debugProvider.IsDebug() {
config.Level = zap.NewAtomicLevelAt(zap.DebugLevel)
} else {
config.Level = zap.NewAtomicLevelAt(zap.InfoLevel)
}

zap.ReplaceGlobals(zap.Must(config.Build()))
}

func unstructuredLogsWithEnv(envReader env.Reader) bool {
unstructuredLogs, err := strconv.ParseBool(envReader.Getenv("UNSTRUCTURED_LOGS"))
if err != nil {
// at this point if the error is not nil, the env var wasn't set, or is ""
// which means we just default to outputting unstructured logs.
return true
}
return unstructuredLogs
}
Loading
Loading