Documentation
¶
Overview ¶
Package slogassert provides a slog Handler that allows testing that expected logging messages were made in your test code.
Normal Usage ¶
Normal usage looks like this:
func TestSomething(t *testing.T) {
// This automatically registers a Cleanup function to assert
// that all log messages are accounted for.
handler := slogassert.New(t, slog.LevelWarn)
logger := slog.New(handler)
// inject the logger into your test code and run it
// Now start asserting things:
handler.AssertSomeOf("some log message")
// automatically at the end of your function, an
// assertion will run that all log messages are accounted
// for.
}
A variety of assertions at varying levels of detail are available on the Handler.
Index ¶
- Constants
- func NullHandler() slog.Handler
- func NullLogger() *slog.Logger
- type Handler
- func (h *Handler) AssertEmpty()
- func (h *Handler) AssertMessage(msg string)
- func (h *Handler) AssertMessageLevel(msg string, level slog.Level)
- func (h *Handler) AssertPrecise(lmm LogMessageMatch)
- func (h *Handler) AssertSomeMessage(msg string)
- func (h *Handler) AssertSomeMessageLevel(msg string, level slog.Level)
- func (h *Handler) AssertSomePrecise(lmm LogMessageMatch)
- func (h *Handler) Enabled(_ context.Context, level slog.Level) bool
- func (h *Handler) Handle(ctx context.Context, record slog.Record) error
- func (h *Handler) Reset()
- func (h *Handler) Unasserted() []LogMessage
- func (h *Handler) WithAttrs(attrs []slog.Attr) slog.Handler
- func (h *Handler) WithGroup(name string) slog.Handler
- type LogMessage
- type LogMessageMatch
Constants ¶
const ( // LevelDontCare can be used in a LogMessageMatch to indicate // that the level does not need to match. LevelDontCare = slog.Level(-255000000) )
Variables ¶
This section is empty.
Functions ¶
func NullHandler ¶ added in v0.0.5
NullHandler returns a slog.Handler that does nothing.
func NullLogger ¶ added in v0.0.5
NullLogger returns a *slog.Logger pointed at a NullHandler.
Types ¶
type Handler ¶
type Handler struct {
// contains filtered or unexported fields
}
Handler implements the slog.Handler interface, with additional methods for testing.
All methods on this Handler are thread-safe.
func New ¶
New creates a new testing logger, logging with the given level.
This will automatically call a t.Cleanup to assert that the logger is empty. If you want to do this manually or not at all, call NewWithoutCleanup.
If wrapped is not nil, Handle calls will be passed down to that handler as well.
func NewWithoutCleanup ¶
NewWithoutCleanup creates a new testing logger, logging with the given level.
This does not automatically register a cleanup function to assert that the logger is empty.
If wrapped is not nil, Handle calls will be passed down to that handler as well.
func (*Handler) AssertEmpty ¶
func (h *Handler) AssertEmpty()
AssertEmpty asserts that all log messages have now been accounted for and there is nothing left.
A call to this method will be automatically deferred through the testing system if you use New(), but you can also use New
func (*Handler) AssertMessage ¶
AssertMessage asserts a logging message recorded with the giving logging message.
func (*Handler) AssertMessageLevel ¶
AssertMessageLevel asserts a logging message with the given message and level.
func (*Handler) AssertPrecise ¶
func (h *Handler) AssertPrecise(lmm LogMessageMatch)
AssertPrecise takes a LogMessageMatch and asserts the first log message that matches it.
func (*Handler) AssertSomeMessage ¶
AssertSomeMessage asserts that some logging events were recorded with the given message.
func (*Handler) AssertSomeMessageLevel ¶
AssertSomeMessageLevel is a weak assertion that asserts that some logging events were recorded with the given message, at the given logging level.
func (*Handler) AssertSomePrecise ¶
func (h *Handler) AssertSomePrecise(lmm LogMessageMatch)
AssertSomePrecise asserts all the messages in the log that match the LogMessageMatch criteria.
func (*Handler) Enabled ¶
Enabled implements slog.Handler, reporting back to slog whether or not the handler is enabled for this level of log message.
func (*Handler) Handle ¶
Handle implements slog.Handler, recording a log message into the root handler.
func (*Handler) Reset ¶
func (h *Handler) Reset()
Reset will simply empty out the log entirely. This can be used in anger to simply make tests pass, or when you legitimately have some logging messages you don't want to bind your tests to (for instance this package's own call to testing/slogtest).
func (*Handler) Unasserted ¶ added in v0.0.7
func (h *Handler) Unasserted() []LogMessage
Unasserted returns all the log messages that are currently unasserted within the slog assert. The returned result is a deep copy. This method does NOT assert them; after a call to this method, if there are any messages an AssertEmpty will still fail.
It is probably superficially tempting to just use this and examine the result with code. However, bear in mind that using the assertion functions in conjuction with the default AssertEmpty on test cleanup already handles making sure everything is asserted. There's a lot of bugs easy to write with direct code examination.
However, sometimes you just need to check the messages with code.
type LogMessage ¶ added in v0.0.7
type LogMessage struct {
Message string
Level slog.Level
Stacktrace string
// key is the slash-encoded group path to this value
Attrs map[string]slog.Value
// this package deliberately ignores this, but passing
// testing/slogtest requires us to store this
Time time.Time
}
LogMessage is a struct for storing the log messages picked up by slogassert's handler.
func (*LogMessage) Print ¶ added in v0.0.7
func (lm *LogMessage) Print(w io.Writer)
Print is a default method that can dump a LogMessage out to a writer; this is used by slogassert to print unasserted log messages.
type LogMessageMatch ¶
type LogMessageMatch struct {
Message string
Level slog.Level
Attrs map[string]any
AllAttrsMatch bool
}
LogMessageMatch defines a precise message to match.
The Message works as you'd expect; an equality check. It is always checked, so an empty message means to verify that the message logged was empty.
If Level is LevelDontCare, the level won't be matched. Otherwise, it will also be an equality check.
Attrs is a map of string to any. The strings will be the groups for the given attribute, joined together by dots. For instance, an ungrouped key called "url" will be "url". If it is in a "request" group, it will be keyed by "request.url". If that is also in a "webserver" group, the key will be "webserver.request.url". Any dots in the keys themselves will be backslash encoded, so a top-level key called "a.b" will be "a\.b" in this map.
The value is a matcher on the attribute, which may be one of three things.
It can be a function "func (slog.Value) bool", which will be passed the value. If it returns true, it is considered to match; false is considered to be not a match.
It can be a function "func (T) bool", where "T" matches the concrete value behind the Kind of the slog.Value. In that case, the same rules apply. For KindAny, this must be precisely "func(any) bool"; this is done via type switching, not a lot of `reflect` calls, so only and exactly "func (any) bool" will work.
It can be a concrete value, in which case it must be equal to the value contained in the attribute. Type-appropriate equality is used, e.g., time.Time's are compared via time.Equal.
Any other value will result in an error being returned when used to match.
AllAttrsMatch indicate whether the Attrs map must contain matches for all attributes in the match. If true, and there are unmatched attribtues in the log message, the match will fail. If false, extra attributes in the log message won't fail the match.