fast-build-update-tool/internal/config/logger.go (101 lines of code) (raw):
package config
import (
"fmt"
"io"
"log/slog"
"os"
"path/filepath"
"github.com/aws/smithy-go/logging"
"github.com/pterm/pterm"
)
const logFileName = AppName + ".log"
type ApplicationLogger struct {
Logger *slog.Logger
AwsLogger logging.Logger
logsDir string
prevLogsDir string
logFile *os.File
}
// InitializeLogger will initialize the app logger. Use the verbose flag to configure the level of logging that will be used.
func InitializeLogger(verbose bool) (*ApplicationLogger, error) {
result := &ApplicationLogger{
AwsLogger: &awsLogger{},
logsDir: logsDir(),
prevLogsDir: logsDir() + "-prev",
}
if err := result.initializeLogDirectories(); err != nil {
return result, err
}
if err := result.initializeSlog(verbose); err != nil {
return result, err
}
return result, nil
}
func (a *ApplicationLogger) initializeLogDirectories() error {
// Remove an existing prev log dir if we have one
err := os.RemoveAll(a.prevLogsDir)
if err != nil {
return fmt.Errorf("error removing old log directory %w", err)
}
// Move existing log dir to prev
if doesFileExist(a.logsDir) {
err = os.Rename(a.logsDir, a.prevLogsDir)
if err != nil {
return fmt.Errorf("error renaming previous log directory %w", err)
}
}
// Create a folder for storing our logs
err = os.MkdirAll(a.logsDir, os.ModePerm)
if err != nil {
return fmt.Errorf("error making new log directory %w", err)
}
return nil
}
func (a *ApplicationLogger) initializeSlog(verbose bool) (err error) {
var handler slog.Handler
if verbose {
if a.logFile == nil {
// If we have verbose logs, use the default slog.TextHandler, and write everything to STDOUT, and a log file
a.logFile, err = os.OpenFile(filepath.Join(a.logsDir, logFileName), os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0666)
if err != nil {
return fmt.Errorf("error creating log file %w", err)
}
}
level := &slog.LevelVar{}
level.Set(slog.LevelDebug)
handler = slog.NewTextHandler(io.MultiWriter(a.logFile, os.Stdout), &slog.HandlerOptions{Level: level})
} else {
// Otherwise use the pretty logger at Warn level, and only log to STDOUT
pterm.DefaultLogger.Level = pterm.LogLevelWarn
handler = pterm.NewSlogHandler(&pterm.DefaultLogger)
}
a.Logger = slog.New(handler)
return nil
}
func (a *ApplicationLogger) Close() {
if a.logFile != nil {
err := a.logFile.Close()
if err != nil {
fmt.Println("error closing log file", err)
}
}
}
func logsDir() string {
return filepath.Join(".", AppName+"-logs")
}
// GetLogPathForFile will return the proper path where application logs can be written
func GetLogPathForFile(fileName string) string {
return filepath.Join(logsDir(), fileName)
}
// awsLogger implements the logger interface required by the AWS SDK, and writes logs to the standard slog logger
type awsLogger struct{}
func (a *awsLogger) Logf(classification logging.Classification, format string, v ...interface{}) {
switch classification {
case logging.Warn:
slog.Warn("(AWSSDK) " + fmt.Sprintf(format, v...))
default:
slog.Debug("(AWSSDK) " + fmt.Sprintf(format, v...))
}
}
type errorLogger struct {
context string
}
func (e *errorLogger) Write(p []byte) (n int, err error) {
slog.Error(string(p), "context", e.context)
return n, nil
}
// NewErrorLogger returns an io.Writer that can be used to handle logs that would normally be written to STDERR
func NewErrorLogger(context string) io.Writer {
return &errorLogger{context: context}
}