fix: lifecycle-safe logger teardown without global state

Store file handle per module, swap lifecycle logger before closing file,
return teardown errors, remove duplicate Module() func.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-06-29 18:01:08 -07:00
parent 7fcb19ad49
commit 626e1bb5ac
9 changed files with 258 additions and 149 deletions
+51
View File
@@ -0,0 +1,51 @@
package applog
import (
"io"
"log/slog"
"gitea.auvem.com/go-toolkit/app"
)
// ModuleName is the name of the module provided by applog.
const ModuleName = "app logger"
// AppLogOpts contains options for configuring the application logger.
type AppLogOpts struct {
// ConsoleOutput is the writer for pretty-printed console logs. If nil,
// console output is disabled. Generally recommended to set this to
// os.Stderr, leaving os.Stdout for application output (usually via
// fmt.Print*).
ConsoleOutput io.Writer
// ConsoleLevel is the minimum log level for console output. Defaults to
// slog.LevelInfo when console output is enabled.
ConsoleLevel slog.Level
// FileOutput is the path to a file where JSON formatted logs will be
// written. If empty, file output is disabled.
FileOutput string
// FileLevel is the minimum log level for file output. Defaults to
// slog.LevelInfo when file output is enabled.
FileLevel slog.Level
// SetDefault sets slog.SetDefault to the configured logger.
SetDefault bool
// DisableAnnouncement skips the startup log line after initialization.
DisableAnnouncement bool
}
// Module returns an app module that configures slog for the lifecycle.
func (opts AppLogOpts) Module() *app.Module {
state := &moduleState{opts: opts}
return app.NewModule(ModuleName, app.ModuleOpts{
Setup: func(m *app.Module) error {
return state.setup(m)
},
Teardown: func(m *app.Module) error {
return state.teardown(m.Lifecycle())
},
})
}