Skip to content

Commit

Permalink
Add structured logging
Browse files Browse the repository at this point in the history
  • Loading branch information
Lars Ekman committed Sep 7, 2022
1 parent ca262b1 commit e15c490
Show file tree
Hide file tree
Showing 5 changed files with 537 additions and 3 deletions.
201 changes: 201 additions & 0 deletions docs/logging.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,201 @@
# Meridio - logging

Meridio uses structured logging implemented by
[go-logr/logr](https://github.com/go-logr/logr). Structured logging
means that printf-like formatted messages are not used, instead
key/object pairs are passed to the log functions.

```go
import "github.com/nordix/meridio/pkg/log"

var config Config
err := envconfig.Process("ipam", &config)
if err != nil {
panic(err) // We can't log since we have no logger yet
}
logger := log.New("Meridio-ipam", config.LogLevel)
logger.Info("Configuration read", "config", config)
```

When executed this will produce (formatted with
[jq](https://stedolan.github.io/jq/));

```json
{
"severity": "info",
"timestamp": "2022-08-31T09:04:03.482+00:00",
"service_id": "Meridio-ipam",
"message": "Configuration read",
"version": "1.0.0",
"extra_data": {
"config": {
"Port": 7777,
"Datasource": "/run/ipam/data/registry.db",
"TrenchName": "red",
"NSPService": "meridio-nsp-red:7778",
"PrefixIPv4": "172.16.0.0/16",
"ConduitPrefixLengthIPv4": 20,
"NodePrefixLengthIPv4": 24,
"PrefixIPv6": "fd00::172.16.0.0/112",
"ConduitPrefixLengthIPv6": 116,
"NodePrefixLengthIPv6": 120,
"IPFamily": "dualstack",
"LogLevel": "DEBUG"
}
}
}
```

Structured logs can be scanned with [jq](https://stedolan.github.io/jq/).

```
kubectl logs -n red meridio-load-balancer-6dbbb9556f-f5cc4 -c load-balancer \
| grep '^{' | jq 'select(.extra_data.class == "SimpleNetworkService")'
kubectl logs -n red meridio-load-balancer-6dbbb9556f-f5cc4 -c load-balancer \
| grep '^{' | jq 'select(.extra_data.class == "SimpleNetworkService")|select(.message == "updateVips")'
```

## Logger from context

A logger should be created in `main()` and be used for logging
everywhere. The logger is not passed in every call but a
[go context](https://pkg.go.dev/context) should. Functions should
use the logger from the context;

```go
// In main();
ctx = logr.NewContext(ctx, logger)
// In a function;
logger = log.FromContextOrGlobal(ctx)
```

Functions really should always have a context as first parameter but
they might not. A global logger is provided;

```
log.Logger.Info("Configuration read", "config", config)
```

The global logger is set by the *first* call to `log.New`. A global logger
named "Meridio" on INFO level is pre-installed before `log.New` is called.



## Log levels

Severity `debug`, `info`, `error` and `critical` are used (not
`warning`). The `Info()` call can have different "verbosity", set with the
`V(n)` method;

```go
logger.Info("This is a normal info message")
logger.V(1).Info("This is a debug message")
logger.V(2).Info("This is a trace message")
```

There is no defined "trace" level in output so both trace and debug
messages will have severity "debug". The level filtering is still valid
though, trace messages are suppressed unless TRACE level is set.

The `Fatal()` function logs on `critical` level.

### Costly parameter computations

**This is important!**

Consider;

```go
logger.V(2).Info("Gathered data", "collected", verySlowFunction())
```

The `verySlowFunction()` will *always* be executed, even if not on
`trace` level. A few of these may have a severe impact on
performance but you may *really* want them for trace. Luckily there is
a [trick](https://github.com/go-logr/logr/issues/149);

```
if loggerV := logger.V(2); loggerV.Enabled() {
loggerV.Info("Gathered data", "collected", verySlowFunction())
}
```

Now the `verySlowFunction()` is *only* executed when trace level is set.


## Fatal

```go
import "github.com/nordix/meridio/pkg/log"
logger := log.New("Meridio-ipam", config.LogLevel)
log.Fatal(logger, "Can't read crucial data", "error", err)
```

The logger is a pure `logr.Logger` logger so there is no `Fatal()`
method. However we want to print a termination message using the same
formatting as other log items so the `logger` is passed as a parameter.

Example output;
```json
{
"severity": "critical",
"timestamp": "2022-08-31T13:42:29.345+02:00",
"service_id": "Meridio-test",
"message": "Can't read crucial data",
"version": "1.1.0",
"extra_data": {
"error": "Not found"
}
}
```


## Design patterns

Patterns must evolve slowly to get really good so these are mere
ideas. It is very easy to get carried away and impose some
over-structured logging that floods the logs with useless data.


### Class logger

A logger used in a type (Class) can be decorated with `class` and
`instance` records;

```go
type someHandler struct {
ctx context.Context
logger logr.Logger
Adr *net.TCPAddr // (optional; capitalized to make it visible)
}

func newHandler(ctx context.Context, addr string) *someHandler {
logger := log.FromContextOrGlobal(ctx).WithValues("class", "someHandler")
adr, err := net.ResolveTCPAddr("tcp", addr)
if err != nil {
log.Fatal(logger, "ResolveTCPAddr", "error", err)
}
h := &someHandler{
ctx: ctx,
logger: logger.WithValues("instance", adr),
Adr: adr,
}
h.logger.Info("Created", "object", h)
return h
}

func (h *someHandler) connect() error {
logger := h.logger.WithValues("func", "connect")
logger.Info("Called")
return nil
}
```

The `class` is the name of the type and `instance` can be anything
that identifies an instance. The instance field must be
capitalized if you want it visible.

The example shows a `func` entry to identify a function. This should
*not* be used as a common pattern but may be handy in some cases.

6 changes: 5 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ go 1.19
require (
github.com/antonfisher/nested-logrus-formatter v1.3.1
github.com/edwarnicke/grpcfd v1.1.2
github.com/go-logr/logr v1.2.3
github.com/go-logr/zapr v1.2.3
github.com/golang/mock v1.6.0
github.com/golang/protobuf v1.5.2
github.com/google/nftables v0.0.0-20220808154552-2eca00135732
Expand All @@ -21,6 +23,7 @@ require (
github.com/stretchr/testify v1.8.0
github.com/vishvananda/netlink v1.2.1-beta.2.0.20220630165224-c591ada0fb2b
go.uber.org/goleak v1.1.12
go.uber.org/zap v1.23.0
golang.org/x/sys v0.0.0-20220829200755-d48e67d00261
google.golang.org/grpc v1.49.0
google.golang.org/protobuf v1.28.1
Expand All @@ -45,7 +48,6 @@ require (
github.com/edwarnicke/serialize v1.0.7 // indirect
github.com/emicklei/go-restful/v3 v3.8.0 // indirect
github.com/ghodss/yaml v1.0.0 // indirect
github.com/go-logr/logr v1.2.3 // indirect
github.com/go-logr/stdr v1.2.0 // indirect
github.com/go-openapi/jsonpointer v0.19.5 // indirect
github.com/go-openapi/jsonreference v0.19.5 // indirect
Expand Down Expand Up @@ -93,6 +95,8 @@ require (
go.opentelemetry.io/otel/sdk/metric v0.26.0 // indirect
go.opentelemetry.io/otel/trace v1.3.0 // indirect
go.opentelemetry.io/proto/otlp v0.11.0 // indirect
go.uber.org/atomic v1.7.0 // indirect
go.uber.org/multierr v1.6.0 // indirect
golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd // indirect
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect
golang.org/x/net v0.0.0-20220722155237-a158d28d115b // indirect
Expand Down
13 changes: 11 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kd
github.com/antonfisher/nested-logrus-formatter v1.3.1 h1:NFJIr+pzwv5QLHTPyKz9UMEoHck02Q9L0FP13b/xSbQ=
github.com/antonfisher/nested-logrus-formatter v1.3.1/go.mod h1:6WTfyWFkBc9+zyBaKIqRrg/KwMqBbodBjgbHjDz7zjA=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
github.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT8A=
github.com/benbjohnson/clock v1.3.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
Expand Down Expand Up @@ -107,11 +108,13 @@ github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2
github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas=
github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.2.1/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0=
github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/stdr v1.2.0 h1:j4LrlVXgrbIWO83mmQUnK0Hi+YnbD+vzrE1z/EphbFE=
github.com/go-logr/stdr v1.2.0/go.mod h1:YkVgnZu1ZjjL7xTxrfm/LLZBfkhTqSR1ydtm6jTKKwI=
github.com/go-logr/zapr v1.2.0 h1:n4JnPI1T3Qq1SFEi/F8rwLrZERp2bso19PJZDB9dayk=
github.com/go-logr/zapr v1.2.3 h1:a9vnzlIBPQBBkeaR9IuMUfmVOrQlkoC4YfPoFkX3T7A=
github.com/go-logr/zapr v1.2.3/go.mod h1:eIauM6P8qSvTw5o2ez6UEAfGjQKrxQTl5EoK+Qa2oG4=
github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY=
github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
Expand Down Expand Up @@ -302,6 +305,7 @@ github.com/open-policy-agent/opa v0.16.1 h1:BDADmi1Xl08aPcubaYgSEU0lJ/zrWDwmFMRX
github.com/open-policy-agent/opa v0.16.1/go.mod h1:P0xUE/GQAAgnvV537GzA0Ikw4+icPELRT327QJPkaKY=
github.com/peterh/liner v0.0.0-20170211195444-bf27d3ba8e1d/go.mod h1:xIteQHvHuaLYG9IFj6mSxM0fCKrs34IrEQUhOYuGPHc=
github.com/pkg/errors v0.0.0-20181023235946-059132a15dd0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
Expand Down Expand Up @@ -393,11 +397,15 @@ go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqe
go.opentelemetry.io/proto/otlp v0.11.0 h1:cLDgIBTf4lLOlztkhzAEdQsJ4Lj+i5Wc9k6Nn0K1VyU=
go.opentelemetry.io/proto/otlp v0.11.0/go.mod h1:QpEjXPrNQzrFDZgoTo49dgHR9RYRSrg3NAKnUGl9YpQ=
go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw=
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A=
go.uber.org/goleak v1.1.12 h1:gZAh5/EyT/HQwlpkCy6wTpqfH9H8Lz8zbm3dZh+OyzA=
go.uber.org/goleak v1.1.12/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4=
go.uber.org/zap v1.19.1 h1:ue41HOKd1vGURxrmeKIgELGb3jPW9DMUDGtsinblHwI=
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
go.uber.org/zap v1.19.0/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI=
go.uber.org/zap v1.23.0 h1:OjGQ5KQDEUawVHxNwQgPpiypGHOxo2mNZsOqTak4fFY=
go.uber.org/zap v1.23.0/go.mod h1:D+nX8jyLsMHMYrln8A0rJjFt/T/9/bGgIhAqxv5URuY=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
Expand Down Expand Up @@ -748,6 +756,7 @@ gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gorm.io/driver/sqlite v1.3.6 h1:Fi8xNYCUplOqWiPa3/GuCeowRNBRGTf62DEmhMDHeQQ=
Expand Down
Loading

0 comments on commit e15c490

Please sign in to comment.