Go introduced slog in Go1.21. That makes logging more flexible and consistent. And when I tried to use it on one of my side projects I found that it logs the time in local timezone, which isn't the best choice for me. Cause for example, I may own a server machine at Stockholm and I live in Egypt, that case when the logs is written, it will be logged in UTC+1 (Stockholm Time), however Egypt is UTC+2. So, I had to interpret it in my mind every time I read a log line. This is tedious. Not only that, imagine a team scattered around the globe, each one will try to do the interpenetration his own. And even sharing log lines won't be efficient for the same reason.
Therefore, a UTC would solve that issue, and wherever I'm located, we will going interpret it straight forward. So, tried to find out how this can be done. But didn't, even asked GPT models, but didn't give me answer (at least the free one) But finally I manged to make it. Here is a full working go program that writes log in UTC time. Further more, it write it in a secure way. The key player here is the ReplaceAttr
function in the slog. Where you can override certain log message.
package main
import (
"log/slog"
"os"
"time"
)
func main() {
cardPan := "4701322211111234"
cardCvv := "789"
secLog := SecureLogger()
secLog.Info("This is secure logger", "secret", cardPan, "secret", cardCvv)
// time=2024-02-17T15:52:01.119Z level=INFO msg="This is secure logger" secret=47013######11234 secret=***
}
// SecureLogger write logs in a secure way in UTC time
func SecureLogger() *slog.Logger {
l := slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{
ReplaceAttr: func(groups []string, a slog.Attr) slog.Attr {
if a.Key == "time" {
a.Value = slog.AnyValue(time.Now().UTC())
}
if a.Key == "secret" {
// This is probably a card pan, so we can only reveal
// The first 5 and last 5 and the rest can be masked with #
if len(a.Value.String()) == 16 {
pan := a.Value.String()
first5 := pan[:5]
last5 := pan[11:]
masked := first5 + "######" + last5
a.Value = slog.StringValue(masked)
} else {
a.Value = slog.StringValue("***")
}
}
return a
},
})
return slog.New(l)
}
Each log entry has built-in keys which is time
, level
and message
. So I override the time
one.
And introduced new secret
key, which supposed to be used for logging sensitive elements.
And that's all!