@@ -89,39 +89,119 @@ func New(w io.Writer, level slog.Level, format Format, debug bool) *slog.Logger
8989// - LOG_LEVEL: string representation of the log level. It panics if no such log level exists.
9090// - LOG_FORMAT: format in which to output logs (e.g. json, text). It panics if no such format exists.
9191// - LOG_DEBUG: enable the most detailed debug logging. It panics iff the given value is not a valid boolean.
92- func NewFromEnv (envPrefix string ) * slog.Logger {
93- return newFromEnv (envPrefix , os .Getenv )
92+ //
93+ // You can customize the default values for when no environment variables are
94+ // found using [Option] like [WithDefaultLevel].
95+ func NewFromEnv (envPrefix string , opts ... Option ) * slog.Logger {
96+ return newFromEnv (envPrefix , opts ... )
97+ }
98+
99+ // options is a holding structure for configurable options.
100+ type options struct {
101+ level slog.Level
102+ format Format
103+ debug bool
104+ target * os.File
105+ getenv func (s string ) string
106+ }
107+
108+ // Option represents a configuration function for the logger. It's primarily
109+ // used with [NewFromEnv] to set defaults.
110+ type Option func (o * options ) * options
111+
112+ // WithDefaultLevel sets the default level of the logger if no value is set.
113+ func WithDefaultLevel (l slog.Level ) Option {
114+ return func (o * options ) * options {
115+ o .level = l
116+ return o
117+ }
118+ }
119+
120+ // WithDefaultFormat sets the default format of the logger if no value is set.
121+ func WithDefaultFormat (f Format ) Option {
122+ return func (o * options ) * options {
123+ o .format = f
124+ return o
125+ }
126+ }
127+
128+ // WithDefaultDebug sets the default debug mode of the logger if no value is set.
129+ func WithDefaultDebug (b bool ) Option {
130+ return func (o * options ) * options {
131+ o .debug = b
132+ return o
133+ }
134+ }
135+
136+ // WithDefaultTarget sets the default output of the logger if no value is set.
137+ // If you use something other than [os.Stdout] or [os.Stderr], the caller is
138+ // responsible for closing the provided [os.File] handle.
139+ func WithDefaultTarget (t * os.File ) Option {
140+ return func (o * options ) * options {
141+ o .target = t
142+ return o
143+ }
144+ }
145+
146+ // WithGetenv overrides the function to get envvars. It's primarily used for
147+ // testing.
148+ func WithGetenv (f func (string ) string ) Option {
149+ return func (o * options ) * options {
150+ o .getenv = f
151+ return o
152+ }
94153}
95154
96155// newFromEnv is a helper that makes it easier to test [NewFromEnv].
97- func newFromEnv (envPrefix string , getenvFunc func (string ) string ) * slog.Logger {
98- levelEnvVarKey , levelEnvVarValue := multiGetenv (getenvFunc , envPrefix + "LOG_LEVEL" , "LOG_LEVEL" )
99- level , err := LookupLevel (levelEnvVarValue )
100- if err != nil {
101- panic (fmt .Sprintf ("log level: invalid value for %s: %s" , levelEnvVarKey , err ))
156+ func newFromEnv (envPrefix string , opts ... Option ) * slog.Logger {
157+ o := & options {
158+ level : slog .LevelInfo ,
159+ format : FormatJSON ,
160+ target : os .Stdout ,
161+ debug : false ,
162+ getenv : os .Getenv ,
163+ }
164+ for _ , opt := range opts {
165+ o = opt (o )
166+ }
167+
168+ levelEnvVarKey , levelEnvVarValue := multiGetenv (o .getenv , envPrefix + "LOG_LEVEL" , "LOG_LEVEL" )
169+ if levelEnvVarValue != "" {
170+ level , err := LookupLevel (levelEnvVarValue )
171+ if err != nil {
172+ panic (fmt .Sprintf ("log level: invalid value for %s: %s" , levelEnvVarKey , err ))
173+ }
174+ o .level = level
102175 }
103176
104- formatEnvVarKey , formatEnvVarValue := multiGetenv (getenvFunc , envPrefix + "LOG_FORMAT" , "LOG_FORMAT" )
105- format , err := LookupFormat (formatEnvVarValue )
106- if err != nil {
107- panic (fmt .Sprintf ("log format: invalid value for %s: %s" , formatEnvVarKey , err ))
177+ formatEnvVarKey , formatEnvVarValue := multiGetenv (o .getenv , envPrefix + "LOG_FORMAT" , "LOG_FORMAT" )
178+ if formatEnvVarValue != "" {
179+ format , err := LookupFormat (formatEnvVarValue )
180+ if err != nil {
181+ panic (fmt .Sprintf ("log format: invalid value for %s: %s" , formatEnvVarKey , err ))
182+ }
183+ o .format = format
108184 }
109185
110- debugEnvVarKey , debugEnvVarValue := multiGetenv (getenvFunc , envPrefix + "LOG_DEBUG" , "LOG_DEBUG" )
111- debug , err := strconv . ParseBool ( debugEnvVarValue )
112- if err != nil {
113- if debugEnvVarValue != "" {
186+ debugEnvVarKey , debugEnvVarValue := multiGetenv (o . getenv , envPrefix + "LOG_DEBUG" , "LOG_DEBUG" )
187+ if debugEnvVarValue != "" {
188+ debug , err := strconv . ParseBool ( debugEnvVarValue )
189+ if err != nil {
114190 panic (fmt .Sprintf ("log debug: invalid value for %s: %s" , debugEnvVarKey , err ))
115191 }
192+ o .debug = debug
116193 }
117194
118- targetEnvVarKey , targetEnvVarValue := multiGetenv (getenvFunc , envPrefix + "LOG_TARGET" , "LOG_TARGET" )
119- target , err := LookupTarget (targetEnvVarValue )
120- if err != nil {
121- panic (fmt .Sprintf ("log target: invalid value for %s: %s" , targetEnvVarKey , err ))
195+ targetEnvVarKey , targetEnvVarValue := multiGetenv (o .getenv , envPrefix + "LOG_TARGET" , "LOG_TARGET" )
196+ if targetEnvVarValue != "" {
197+ target , err := LookupTarget (targetEnvVarValue )
198+ if err != nil {
199+ panic (fmt .Sprintf ("log target: invalid value for %s: %s" , targetEnvVarKey , err ))
200+ }
201+ o .target = target
122202 }
123203
124- return New (target , level , format , debug )
204+ return New (o . target , o . level , o . format , o . debug )
125205}
126206
127207// multiGetenv is a helper function for looking up a collection of environment
0 commit comments