Skip to content

Commit 6e7ae08

Browse files
committed
logging: include information about contextual logging
In Kubernetes 1.24, the necessary infrastructure got added. Now developers can take advantage of it.
1 parent 308bb11 commit 6e7ae08

File tree

1 file changed

+125
-16
lines changed

1 file changed

+125
-16
lines changed

contributors/devel/sig-instrumentation/migration-to-structured-logging.md

Lines changed: 125 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,20 @@
1-
# Structured Logging migration instructions
1+
# Structured and Contextual Logging migration instructions
22

3-
This document describes instructions for migration proposed by [Structured Logging KEP]. It describes new structured
3+
This document describes instructions for the migration proposed by [Structured Logging KEP] and [Contextual Logging KEP]. It describes new
44
functions introduced in `klog` (Kubernetes logging library) and how log calls should be changed to utilize new features.
5-
This document was written for the initial migration of `kubernetes/kubernetes` repository proposed for Alpha stage, but
5+
This document was written for the initial migration of `kubernetes/kubernetes` repository proposed for Alpha stage of structured logging, but
66
should be applicable at later stages or for other projects using `klog` logging library.
77

88
[Structured Logging KEP]: https://github.com/kubernetes/enhancements/tree/master/keps/sig-instrumentation/1602-structured-logging
9+
[Contextual Logging KEP]: https://github.com/kubernetes/enhancements/tree/master/keps/sig-instrumentation/3077-contextual-logging
910

1011
## How to contribute
1112

1213
### About the migration
1314

14-
We would like for the Kubernetes community to settle on one preferred log message structure, that will be enforced by new klog methods. The goal of the migration is to switch C like format string logs to structured logs with explicit metadata about parameters.
15+
We would like for the Kubernetes community to settle on one preferred log message structure and log calls as defined by [logr].
16+
The goal of the migration is to switch C like format string logs to structured logs with explicit metadata about parameters and
17+
to remove the global logger in klog with a `logr.Logger` instance that gets chosen by the caller of a function.
1518

1619
Migration within the structured logging working group happens in two different ways - organized & non-organized.
1720

@@ -42,6 +45,7 @@ Before sending a PR our way, please ensure that there isn't one already in place
4245
* 1.21 Kubelet was migrated
4346
* 1.22 Collected feedback and improved the process.
4447
* 1.23 kube-scheduler and kube-proxy were migrated.
48+
* 1.24 Contextual logging infrastructure (updated klog, component-base/logs enables it) in place.
4549

4650
## Sending a Structured Logging Migration Pull Request
4751

@@ -78,7 +82,7 @@ Migrate <directory/file> to structured logging
7882

7983
### Why my PR was rejected?
8084

81-
Even though the Kubernetes project is organizing migration of Structured Logging, this doesn't mean that we are able to accept all the PRs that come our way. We reserve the right to reject the Pull Request in situations listed below:
85+
Even though the Kubernetes project is organizing migration of logging, this doesn't mean that we are able to accept all the PRs that come our way. We reserve the right to reject the Pull Request in situations listed below:
8286

8387
* Pull request is below minimum quality standards and clearly shows that author hasn't read the guide at all, for example PR just renames `Infof` to `InfoS`.
8488
* Pull request migrates components that the owners have decided against migrating. List of those components:
@@ -168,47 +172,129 @@ type KMetadata interface {
168172
}
169173
```
170174

175+
## Contextual logging in Kubernetes
176+
177+
Contextual logging builds on top of structured logging because the parameters
178+
for individual log calls are the same. The difference is that different
179+
functions need to be used:
180+
181+
- `klog.ErrorS` -> `logger.Error`
182+
- `klog.InfoS` -> `logger.Info`
183+
- `klog.V().InfoS` -> `logger.V().Info`
184+
185+
In all of these cases, `logger` is a `logr.Logger` instance. `klog.Logger` is
186+
an alias for that type. Determining where that instance comes from is the main
187+
challenge when migrating code to contextual logging.
188+
189+
Several new klog functions help with that:
190+
191+
- [`klog.FromContext`](https://pkg.go.dev/k8s.io/klog/v2#FromContext)
192+
- [`klog.Background`](https://pkg.go.dev/k8s.io/klog/v2#Background)
193+
- [`klog.TODO`](https://pkg.go.dev/k8s.io/klog/v2#TODO)
194+
195+
The preferred approach is to retrieve the instance with `klog.FromContext` from
196+
a `ctx context` parameter. If a function or method does not have one, consider
197+
adding it. This API change then implies that all callers also need to be
198+
updated. If there are any `context.TODO` calls in the modified functions,
199+
replace with the new `ctx` parameter.
200+
201+
In performance critical code it may be faster to add a `logger klog.Logger`
202+
parameter. This needs to be decided on a case-by-case basis.
203+
204+
When such API changes trickle up to a unit test, then enable contextual logging
205+
with per-test output with
206+
[`ktesting`](https://pkg.go.dev/k8s.io/klog/[email protected]/ktesting):
207+
208+
```go
209+
import (
210+
"testing"
211+
212+
"k8s.io/klog/v2/ktesting"
213+
_ "k8s.io/klog/v2/ktesting/init" # Add command line flags for ktesting.
214+
)
215+
216+
func TestFoo(t *testing.T) {
217+
_, ctx := ktesting.NewTestContext(t)
218+
doSomething(ctx)
219+
}
220+
```
221+
222+
If a logger instance is needed instead, then use:
223+
224+
```go
225+
logger, _ := ktesting.NewTestContext(t)
226+
```
227+
228+
The KEP has further instructions about the [transition to contextual
229+
logging](https://github.com/kubernetes/enhancements/tree/master/keps/sig-instrumentation/3077-contextual-logging#transition). It
230+
also lists several
231+
[pitfalls](https://github.com/kubernetes/enhancements/tree/master/keps/sig-instrumentation/3077-contextual-logging#pitfalls-during-usage)
232+
that developers and reviewers need to be aware of.
233+
171234
## Migration
172235

173-
1. Change log functions to structured equivalent
236+
1. Optional: find code locations that need to be changed:
237+
238+
- `(cd hack/tools && go install k8s.io/klog/hack/tools/logcheck)`
239+
- structured logging: `$GOPATH/bin/logcheck -check-structured ./pkg/controller/...`
240+
- contextual logging: `$GOPATH/bin/logcheck -check-contextual ./pkg/scheduler/...`
241+
242+
1. Change log functions to structured or (better!) contextual equivalent
174243
1. Remove string formatting from log message
175244
1. Name arguments
176245
1. Ensure that value is properly passed
177246
1. Verify log output
247+
1. Prevent re-adding banned functions after migration
178248

179-
## Change log functions to structured equivalent
249+
## Change log functions
180250

181251
Structured logging functions follow a different logging interface design than other functions in `klog`. They follow
182252
minimal design from [logr] thus there is no one-to-one mapping.
183253

184-
Simplified mapping between functions:
254+
Simplified mapping between functions for structured logging:
185255
* `klog.Infof`, `klog.Info`, `klog.Infoln` -> `klog.InfoS`
186256
* `klog.InfoDepth` -> `klog.InfoSDepth`
187257
* `klog.V(N).Infof`, `klog.V(N).Info`, `klog.V(N).Infoln` -> `klog.V(N).InfoS`
188258
* `klog.Warning`, `klog.Warningf`, `klog.Warningln` -> `klog.InfoS`
189259
* `klog.WarningDepth` -> `klog.InfoSDepth`
190260
* `klog.Error`, `klog.Errorf`, `klog.Errorln` -> `klog.ErrorS`
191261
* `klog.ErrorDepth` -> `klog.ErrorSDepth`
192-
* `klog.Fatal`, `klog.Fatalf`, `klog.Fatalln` -> `klog.ErrorS` followed by `os.Exit(1)` ([see below])
193-
* `klog.FatalDepth` -> `klog.ErrorDepth` followed by `os.Exit(1)` ([see below])
262+
* `klog.Fatal`, `klog.Fatalf`, `klog.Fatalln` -> `klog.ErrorS` followed by `klog.FlushAndExit(klog.ExitFlushTimeout, klog.1)` ([see below])
263+
* `klog.FatalDepth` -> `klog.ErrorDepth` followed by `klog.FlushAndExit(klog.ExitFlushTimeout, klog.1)` ([see below])
264+
265+
For contextual logging, replace furthermore:
266+
267+
- `klog.ErrorS` -> `logger.Error`
268+
- `klog.InfoS` -> `logger.Info`
269+
- `klog.V().InfoS` -> `logger.V().Info`
194270

195271
[see below]: #replacing-fatal-calls
196272

197-
### Using ErrorS
273+
### Using ErrorS or logger.Error
198274

199275
With `klog` structured logging borrowing the interface from [logr] it also inherits it's differences in semantic of
200276
error function. Logs generated by `ErrorS` command may be enhanced with additional debug information
201277
(such as stack traces) or be additionally sent to special error recording tools. Errors should be used to indicate
202278
unexpected behaviours in code, like unexpected errors returned by subroutine function calls.
279+
In contrast to info log calls, error log calls always record the log entry, regardless of the current verbosity
280+
settings.
203281

204-
Calling `ErrorS` with `nil` as error is semi-acceptable if there is error condition that deserves a stack trace at this
205-
origin point. For expected errors (`errors` that can happen during routine operations) please consider using
282+
Calling `ErrorS` with `nil` as error is acceptable if there is an error condition that deserves a stack trace at this
283+
origin point or always must be logged. For expected errors (`errors` that can happen during routine operations) please consider using
206284
`klog.InfoS` and pass error in `err` key instead.
207285

208286
### Replacing Fatal calls
209287

210288
Use of Fatal should be discouraged and it's not available in new functions. Instead of depending on the logger to exit
211-
the process, you should call `os.Exit()` yourself.
289+
the process, you should:
290+
- rewrite code to return an `error` and let the caller deal with it or, if that is not feasible,
291+
- log and exit separately.
292+
293+
`os.Exit` should be avoided because it skips log data flushing. Instead use
294+
[`klog.FlushAndExit`](https://pkg.go.dev/k8s.io/klog/v2#FlushAndExit). The first
295+
parameter determines how long the program is allowed to flush log data before
296+
`os.Exit` is called. If unsure, use `klog.ExitFlushTimeout`, the value used
297+
by `klog.Fatal`.
212298

213299
Fatal calls use a default exit code of 255. When migrating, please use an exit code of 1 and include an "ACTION REQUIRED:" release note.
214300

@@ -217,7 +303,6 @@ For example
217303
func validateFlags(cfg *config.Config, flags *pflag.FlagSet) error {
218304
if err := cfg.ReadAndValidate(flags); err != nil {
219305
klog.FatalF("Error in reading and validating flags %s", err)
220-
os.Exit(1)
221306
}
222307
}
223308
```
@@ -226,7 +311,7 @@ should be changed to
226311
func validateFlags(cfg *config.Config, flags *pflag.FlagSet) error {
227312
if err := cfg.ReadAndValidate(flags); err != nil {
228313
klog.ErrorS(err, "Error in reading and validating flags")
229-
os.Exit(1)
314+
klog.FlushAndExit(klog.ExitFlushTimeout, 1)
230315
}
231316
}
232317
```
@@ -526,3 +611,27 @@ After
526611
```
527612
I0528 19:15:22.737588 47512 logtest.go:55] "Received HTTP request" verb="GET" URI="/metrics" latency="1s" resp=200 userAgent="Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0. 2272.118 Safari/537.36." srcIP="127.0.0.1"
528613
```
614+
615+
## Prevent re-adding banned functions after migration
616+
617+
After a package has been migrated to structured and/or contextual logging, we
618+
want to ensure that all log calls that get added in future PRs are structured
619+
resp. contextual.
620+
621+
For structured logging, the list of migrated packages in
622+
[`hack/logcheck.conf`](https://github.com/kubernetes/kubernetes/blob/b9792a9daef4d978c5c30b6d10cbcdfa77a9b6ac/hack/logcheck.conf#L16-L22)
623+
can be extended.
624+
625+
For contextual logging, a new list can be added at the bottom once the code is
626+
ready, with content like this:
627+
628+
```
629+
# Packages that have been migrated to contextual logging:
630+
contextual k8s.io/kubernetes/pkg/scheduler/.*
631+
```
632+
633+
The corresponding line with `structured k8s.io/kubernetes/pkg/scheduler/.*`
634+
then is redundant and can be removed because "contextual" implies "structured".
635+
636+
Both lists should be sorted alphabetically. That reduces the risk of code
637+
conflicts and makes the file more readable.

0 commit comments

Comments
 (0)