Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ The linter has several options, so you can adjust it to your own code style.

* Forbid mixing key-value pairs and attributes within a single function call (default)
* Enforce using either key-value pairs or attributes for the entire project (optional)
* Enforce using methods that accept a context (optional)
* Enforce using constants instead of raw keys (optional)
* Enforce putting arguments on separate lines (optional)

Expand Down Expand Up @@ -53,6 +54,22 @@ In contrast, the `-attr-only` flag causes `sloglint` to report any use of key-va
slog.Info("a user has logged in", "user_id", 42) // sloglint: key-value pairs should not be used
```

### Context only

Some `slog.Handler` implementations make use of the given `context.Context` (e.g. to access context values).
For them to work properly, you need to pass a context to all logger calls.
The `-context-only` flag causes `sloglint` to report the use of methods without a context.

```go
slog.Info("a user has logged in") // sloglint: methods without a context should not be used
```

This report can be fixed by using the equivalent method with the `Context` suffix.

```go
slog.InfoContext(ctx, "a user has logged in")
```

### No raw keys

To prevent typos, you may want to forbid the use of raw keys altogether.
Expand Down
9 changes: 9 additions & 0 deletions sloglint.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (
type Options struct {
KVOnly bool // Enforce using key-value pairs only (incompatible with AttrOnly).
AttrOnly bool // Enforce using attributes only (incompatible with KVOnly).
ContextOnly bool // Enforce using methods that accept a context.
NoRawKeys bool // Enforce using constants instead of raw keys.
ArgsOnSepLines bool // Enforce putting arguments on separate lines.
}
Expand Down Expand Up @@ -56,6 +57,7 @@ func flags(opts *Options) flag.FlagSet {

boolVar(&opts.KVOnly, "kv-only", "enforce using key-value pairs only (incompatible with -attr-only)")
boolVar(&opts.AttrOnly, "attr-only", "enforce using attributes only (incompatible with -kv-only)")
boolVar(&opts.ContextOnly, "context-only", "enforce using methods that accept a context")
boolVar(&opts.NoRawKeys, "no-raw-keys", "enforce using constants instead of raw keys")
boolVar(&opts.ArgsOnSepLines, "args-on-sep-lines", "enforce putting arguments on separate lines")

Expand Down Expand Up @@ -113,6 +115,13 @@ func run(pass *analysis.Pass, opts *Options) {
return
}

if opts.ContextOnly {
typ := pass.TypesInfo.TypeOf(call.Args[0])
if typ != nil && typ.String() != "context.Context" {
pass.Reportf(call.Pos(), "methods without a context should not be used")
}
}

// NOTE: we assume that the arguments have already been validated by govet.
args := call.Args[argsPos:]
if len(args) == 0 {
Expand Down
5 changes: 5 additions & 0 deletions sloglint_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,11 @@ func TestAnalyzer(t *testing.T) {
analysistest.Run(t, testdata, analyzer, "attr_only")
})

t.Run("context only", func(t *testing.T) {
analyzer := sloglint.New(&sloglint.Options{ContextOnly: true})
analysistest.Run(t, testdata, analyzer, "context_only")
})

t.Run("no raw keys", func(t *testing.T) {
analyzer := sloglint.New(&sloglint.Options{NoRawKeys: true})
analysistest.Run(t, testdata, analyzer, "no_raw_keys")
Expand Down
33 changes: 33 additions & 0 deletions testdata/src/context_only/context_only.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package context_only

import (
"context"
"log/slog"
)

func tests() {
logger := slog.New(nil)
ctx := context.Background()

slog.Log(ctx, slog.LevelInfo, "msg")
slog.DebugContext(ctx, "msg")
slog.InfoContext(ctx, "msg")
slog.WarnContext(ctx, "msg")
slog.ErrorContext(ctx, "msg")

logger.Log(ctx, slog.LevelInfo, "msg")
logger.DebugContext(ctx, "msg")
logger.InfoContext(ctx, "msg")
logger.WarnContext(ctx, "msg")
logger.ErrorContext(ctx, "msg")

slog.Debug("msg") // want `methods without a context should not be used`
slog.Info("msg") // want `methods without a context should not be used`
slog.Warn("msg") // want `methods without a context should not be used`
slog.Error("msg") // want `methods without a context should not be used`

logger.Debug("msg") // want `methods without a context should not be used`
logger.Info("msg") // want `methods without a context should not be used`
logger.Warn("msg") // want `methods without a context should not be used`
logger.Error("msg") // want `methods without a context should not be used`
}