Exercise 3
Question
- Assume you have a simple logging function that looks like this:
func Log(ctx context.Context, level Level, message string) {
var inLevel Level
// TODO get a logging level out of the context and assign it to inLevel
if level == Debug && inLevel == Debug {
fmt.Println(message)
}
if level == Info && (inLevel == Debug || inLevel == Info) {
fmt.Println(message)
}
}
Define a type called Level
whose underlying type is string
. Define two constants of this type, Debug
and Info
, set to "debug"
and "info"
, respectively.
Create functions to store the log level in the context and to extract it.
Create a middleware function to get the logging level from a query parameter called log_level
. The valid values for log_level
are debug
and info
.
Finally, fill in the TODO
in Log
to properly extract the log level from the context. If the log level is not assigned or is not a valid value, nothing should be printed.
Solution
We start by defining Level
and its constants:
type Level string
const (
Debug Level = "debug"
Info Level = "info"
)
Next, we need to make our log level context management functions. We first define an unexported type for the key's type, and an unexported constant for the key:
type logKey int
const (
_ logKey = iota
key
)
After that, we use the two types we've defined to write our context value management functions:
func ContextWithLevel(ctx context.Context, level Level) context.Context {
return context.WithValue(ctx, key, level)
}
func LevelFromContext(ctx context.Context) (Level, bool) {
level, ok := ctx.Value(key).(Level)
return level, ok
}
Now we can use the ContextWithLevel
function and the Level
type to write our middleware:
func Middleware(h http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
level := r.URL.Query().Get("log_level")
ctx := ContextWithLevel(r.Context(), Level(level))
r = r.WithContext(ctx)
h.ServeHTTP(w, r)
})
}
Finally, we can fill in the missing lines in the Log
function using LevelFromContext
:
inLevel, ok := LevelFromContext(ctx)
if !ok {
return
}