cache-apt-pkgs-action/internal/logging/logger.go
2025-10-11 21:41:23 -07:00

146 lines
4 KiB
Go

package logging
import (
"encoding/json"
"io"
"log"
"os"
"sync"
)
// loggerWrapper encapsulates a standard logger with additional functionality.
type loggerWrapper struct {
wrapped *log.Logger // The underlying standard logger
}
// DebugEnabled controls whether debug messages are logged.
// When true, Debug() calls will output messages; when false, they are ignored.
var DebugEnabled = false
var (
loggerMu sync.Mutex // Protects logger operations
logger = createDefault()
)
// create instantiates a new logger with the specified output writers.
// Multiple writers can be provided to output logs to multiple destinations.
func create(writers ...io.Writer) loggerWrapper {
loggerMu.Lock()
defer loggerMu.Unlock()
return loggerWrapper{
wrapped: log.New(io.MultiWriter(writers...), "", log.LstdFlags),
}
}
// createDefault provides the default behavior for the log Go module
func createDefault() loggerWrapper {
return create(os.Stderr)
}
// SetOutput overrides the default output destination for the logger.
// This affects all subsequent log messages from this package.
// Thread-safe operation that can be called at any time.
func SetOutput(writer io.Writer) {
loggerMu.Lock()
defer loggerMu.Unlock()
logger.wrapped.SetOutput(writer)
}
func recreateFileWriter() *os.File {
logFilepath := os.Args[0] + ".log"
// Ignore error if file doesn't exist
_ = os.Remove(logFilepath)
file, err := os.OpenFile(logFilepath, os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
log.Fatal(err)
}
return file
}
// InitDefault resets the logger to its default state, writing only to stderr.
// Any existing log files or custom writers are discarded.
func InitDefault() {
DebugEnabled = false
logger = createDefault()
}
// Init initializes a new logger that writes to both a file and stderr.
// The log file is named after the binary with a .log extension.
// Previous log file content is discarded.
//
// Parameters:
// - debug: Enable or disable debug logging
func Init(debug bool) {
file := recreateFileWriter()
DebugEnabled = debug
logger = create(file, os.Stderr)
}
// InitWithWriter initializes a new logger with custom output writers.
// Writes to both a log file and the specified writer.
//
// Parameters:
// - debug: Enable or disable debug logging
// - writer: Additional output destination besides the log file
func InitWithWriter(debug bool, writer io.Writer) {
file := recreateFileWriter()
DebugEnabled = debug
logger = create(file, writer)
}
// DebugLazy logs a debug message using a lazy evaluation function.
// The message generator function is only called if debug logging is enabled,
// making it efficient for expensive debug message creation.
//
// The getLine function should return the message to be logged.
func DebugLazy(getLine func() string) {
if DebugEnabled {
logger.wrapped.Println(getLine())
}
}
// Debug logs a formatted debug message if debug logging is enabled.
// Uses fmt.Printf style formatting. No-op if debug is disabled.
//
// Parameters:
// - format: Printf-style format string
// - a: Arguments for the format string
func Debug(format string, a ...any) {
if DebugEnabled {
logger.wrapped.Printf(format, a...)
}
}
// DumpVars logs the JSON representation of variables if debug is enabled.
// Each variable is converted to JSON format before logging.
// Continues to next variable if one fails to convert.
func DumpVars(a ...any) {
if DebugEnabled {
for _, v := range a {
content, err := json.MarshalIndent(v, "", " ")
if err != nil {
Info("warning: unable to dump variable: %v", err)
continue
}
logger.wrapped.Println(content)
}
}
}
// Info logs a formatted message at info level.
// Always logs regardless of debug setting.
// Adds a newline to the end of the message.
func Info(format string, a ...any) {
logger.wrapped.Printf(format+"\n", a...)
}
// Fatal logs an error message and terminates the program.
// Calls os.Exit(1) after logging the error.
func Fatal(err error) {
logger.wrapped.Fatal(err)
}
func Fatalf(format string, a ...any) {
logger.wrapped.Fatalf(format+"\n", a...)
}