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") // often useful to finish up with an assertion that // all log messages have been accounted for: handler.AssertEmpty() }
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) Assert(f func(LogMessage) bool) int
- func (h *Handler) AssertEmpty()
- func (h *Handler) AssertMessage(msg string)
- func (h *Handler) AssertPrecise(lmm LogMessageMatch)
- func (h *Handler) AssertSomeMessage(msg string) int
- func (h *Handler) AssertSomePrecise(lmm LogMessageMatch) int
- func (h *Handler) Enabled(_ context.Context, level slog.Level) bool
- func (h *Handler) Fail(msg string, args ...any)
- 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.
If wrapped is not nil, Handle calls will be passed down to that handler as well.
It is recommended to generally call defer handler.AssertEmpty() on the result of this call.
func (*Handler) Assert ¶ added in v0.3.0
func (h *Handler) Assert(f func(LogMessage) bool) int
Assert takes in a function that takes a recorded log message and indicates whether or not it is "correct" according to your tests, and should be removed from the slice of unasserted log messages. Essentially all other assertions provided are just ways of populating this.
The passed-in function will be presented only with the remaining unasserted log messages at the time of the call.
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) 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. The return value is the number of matched messages if there were any. If there was zero, the test fails.
func (*Handler) AssertSomePrecise ¶
func (h *Handler) AssertSomePrecise(lmm LogMessageMatch) int
AssertSomePrecise asserts all the messages in the log that match the LogMessageMatch criteria. The return value is th enumber of matched messages if there were any. (If there aren't any this fails the test.)
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) Fail ¶ added in v0.3.0
Fail will print out the remaining unasserted messages and pass the given msg and args to t.Fatalf. This can be used in your custom assertions to fail them out.
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.