masq
is a redacting utility to conceal sensitive data for slog that is official Go structured logging library. The concealing feature reduce risk to store secret values (API token, password and such things) and sensitive data like PII (Personal Identifiable Information) such as address, phone number, email address and etc into logging storage.
u := struct {
ID string
Email EmailAddr
}{
ID: "u123",
Email: "[email protected]",
}
logger := slog.New(
slog.NewJSONHandler(
os.Stdout,
&slog.HandlerOptions{
ReplaceAttr: masq.New(masq.WithType[EmailAddr]()),
},
),
)
logger.Info("hello", slog.Any("user", u))
Then, output is following (jq formatted).
{
"time": "2022-12-25T09:00:00.123456789",
"level": "INFO",
"msg": "hello",
"user": {
"ID": "u123",
"Email": "[FILTERED]" // <- Concealed
}
}
masq.New()
provides a function for ReplaceAttr
of slog.HandlerOptions
. masq.New
can specify one or multiple masq.Option
to identify value and field to be concealed.
logger := slog.New(
slog.NewJSONHandler(
os.Stdout,
&slog.HandlerOptions{
ReplaceAttr: masq.New(
// By user defined custom type
masq.WithType[AccessToken](),
// By regex of phone number as e164 format
masq.WithRegex(regexp.MustCompile(`^\+[1-9]\d{1,14}$`)),
// By field tag such as masq:"secret"
masq.WithTag("secret"),
// By by field name prefix. Concealing SecureXxx field
masq.WithFieldPrefix("Secure"),
),
},
),
)
type password string
type myRecord struct {
ID string
Password password
}
record := myRecord{
ID: "m-mizutani",
Password: "abcd1234",
}
logger := slog.New(
slog.NewJSONHandler(
os.Stdout,
&slog.HandlerOptions{
ReplaceAttr: masq.New(masq.WithType[password]()),
},
),
)
logger.With("record", record).Info("Got record")
out.Flush()
// Output:
// {"level":"INFO","msg":"Got record","record":{"ID":"m-mizutani","Password":"[FILTERED]"},"time":"2022-12-25T09:00:00.123456789"}
const issuedToken = "abcd1234"
authHeader := "Authorization: Bearer " + issuedToken
logger := slog.New(
slog.NewJSONHandler(
os.Stdout,
&slog.HandlerOptions{
ReplaceAttr: masq.New(masq.WithContain("abcd1234")),
},
),
)
logger.With("auth", authHeader).Info("send header")
out.Flush()
// Output:
// {"auth":"[REDACTED]","level":"INFO","msg":"send header","time":"2022-12-25T09:00:00.123456789"}
type myRecord struct {
ID string
Phone string
}
record := myRecord{
ID: "m-mizutani",
Phone: "090-0000-0000",
}
logger := slog.New(
slog.NewJSONHandler(
os.Stdout,
&slog.HandlerOptions{
ReplaceAttr: masq.New(masq.WithRegex(regexp.MustCompile(`^\d{3}-\d{4}-\d{4}$`)),
},
),
)
logger.With("record", record).Info("Got record")
out.Flush()
// Output:
// {"level":"INFO","msg":"Got record","record":{"ID":"m-mizutani","Phone":"[FILTERED]"},"time":"2022-12-25T09:00:00.123456789"}
type myRecord struct {
ID string
EMail string `masq:"secret"`
}
record := myRecord{
ID: "m-mizutani",
EMail: "[email protected]",
}
logger := slog.New(
slog.NewJSONHandler(
os.Stdout,
&slog.HandlerOptions{
ReplaceAttr: masq.New(masq.WithTag("secret")),
},
),
)
logger.With("record", record).Info("Got record")
out.Flush()
// Output:
// {"level":"INFO","msg":"Got record","record":{"EMail":"[FILTERED]","ID":"m-mizutani"},"time":"2022-12-25T09:00:00.123456789"}
You can change the tag key by masq.WithCustomTagKey
option.
type myRecord struct {
ID string
EMail string `custom:"secret"`
}
record := myRecord{
ID: "m-mizutani",
EMail: "[email protected]",
}
logger := newLogger(out, masq.New(
masq.WithCustomTagKey("custom"),
masq.WithTag("secret"),
))
logger.With("record", record).Info("Got record")
out.Flush()
// Output:
// {"level":"INFO","msg":"Got record","record":{"EMail":"[REDACTED]","ID":"m-mizutani"},"time":"2022-12-25T09:00:00.123456789"}
type myRecord struct {
ID string
Phone string
}
record := myRecord{
ID: "m-mizutani",
Phone: "090-0000-0000",
}
logger := slog.New(
slog.NewJSONHandler(
os.Stdout,
&slog.HandlerOptions{
ReplaceAttr: masq.New(masq.WithFieldName("Phone")),
},
),
)
logger.With("record", record).Info("Got record")
out.Flush()
// Output:
// {"level":"INFO","msg":"Got record","record":{"ID":"m-mizutani","Phone":"[FILTERED]"},"time":"2022-12-25T09:00:00.123456789"}
type myRecord struct {
ID string
SecurePhone string
}
record := myRecord{
ID: "m-mizutani",
SecurePhone: "090-0000-0000",
}
logger := slog.New(
slog.NewJSONHandler(
os.Stdout,
&slog.HandlerOptions{
ReplaceAttr: masq.New(masq.WithFieldPrefix("Secure")),
},
),
)
logger.With("record", record).Info("Got record")
out.Flush()
// Output:
// {"level":"INFO","msg":"Got record","record":{"ID":"m-mizutani","SecurePhone":"[FILTERED]"},"time":"2022-12-25T09:00:00.123456789"}
Apache License v2.0