go-lint-cerrl is a golang linter that helps finding unlogged errors
created by the cerror
package from your project.
There are number of cases when errors are created with a cerror.New...
call
but not logged with a Log...
method.
For example:
err := cerror.NewF(...)
- bad, error is not logged
err := cerror.NewF(...).LogError()
- good, error is logged
It's so easy to forget about the Log...
and very hard to catch this during
a code review. Moreover this defect is totally invisible for the end user.
Highly likely we'll find out it only when catch a bug and try to find
some useful inforamtion in logs. These aspects make such kind of bugs quite
dangerous and should be a motivation to use the linter.
Note that the linter expects the .Log()
in chain, so the following cases:
err := cerror.New()
_ = err.Log()
will be highlited as errors. Fill free to use //nolint:cerrl
if you
use the linter as golangci-lint
pluign.
The linter checks only files that contain cerror package import.
The import must have the /pkg/cerror
suffix.
The linter also understands import aliases. For example:
import cerr "my_proj/pkg/cerror"
...
err := cerr.New(...).LogError()
...
It's very important to remember that plugins programs that load them must be built with the same configuration (go version, os, arch, ...).
All the golangci-lint
builds and docker images have CGO_ENABLED=0
that breaks the go plugin system requirements CGO_ENABLED=1
.
So most likely you'll have to install golangci-lint
from source:
go install github.com/golangci/golangci-lint/cmd/golangci-lint@v1.45.0
Before installation ensure that you have CGO_ENABLED=1
locally:
go env | grep CGO_ENABLED
Clone or download the repo:
git clone https://github.com/edenlabllc/go-lint-cerrl
Switch to the root directory and build the plugin:
#build into the current directory
make build-plugin
#or into a custom directory
go build --buildmode plugin --output {custom dir path} plugin/cerrl.go
Finally you'll get the *.so
file that you can add to your golangci-lint
config file.
See the .golangci.yml
configuration example with the plugin in our repo.
If you see an error during golangci-lint run
that looks similar too:
... unable to load plugin: plugin.Open(...): plugin was built with a different version of ...
Solution - try to remove all the installed versions of golangci-lint
and install it again:
#see the path to golangci-lint
which golangci-lint
#remove it
rm $(which golangci-lint)
#ensure that you don't have another copy
which golangci-lint
#should see golangci-lint not found. If no delete this copy too
#install
go install github.com/golangci/golangci-lint/cmd/golangci-lint@v1.45.0
If some of your teammate haven't manage to install the plugin you can keep
two golangci-lint
configs - with and without plugin and refer them inside
your IDE individually.
For example for vscode
add the following lines to settings.json
:
"go.lintFlags": [
"-c",
"/path/to/.golangci-no-plugin.yml",
]
And use them from cli as we did in our repo.
If you don't want to install the plugin locally and you don't need
online linter issues discovering, you can run golangci-lint
inside
the docker.
As it was mentioned here we can't use one of the golangci-lint
docker images. So we have to run the golang
image and install golangci-lint
and build the plugin inside the container. You can see an example of
docker-compose
file in our repo
Unfortunately we can't use the original golangci-lint
github action
because of CGO_ENABLED=0. We have to install the plugin and golangci-lint
by our own during the github action and then run the linter. See an example of such
action inside our repo. You can use this approach for the entire linting
process or separate the linting into two jobs (as wee deed):
- lint using config without plugins and use the original
golangci-lint
github action - lint using config with plugins only and use a custom action
If you want to use only plugins in your .golangci.yml
config you'll need
to add at least one default linter (e.g. bodyclose
) too because golangci-lint
crashes otherwise.
Rememeber that nolint
directives won't work outside the golangci-lint
.
Install using go:
go install github.com/edenlabllc/go-lint-cerrl/cmd/cerrl
then use like:
cerrl ./...
Also you can integrate the binary into go vet
:
go vet -vettool=$(which cerrl) ./...
If you want to read more about how to build your own linter
and integrate it into golangci-lint
see the following resources:
- build a custom linter from scratch and integrate it into
go vet
andgolangci-lint
- a simple example of
golangci-lint
plugin - how to add a new linter into
golangci-lint
- golang AST visualizer