1
- # Structured Logging migration instructions
1
+ # Structured and Contextual Logging migration instructions
2
2
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
4
4
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
6
6
should be applicable at later stages or for other projects using ` klog ` logging library.
7
7
8
8
[ 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
9
10
10
11
## How to contribute
11
12
12
13
### About the migration
13
14
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.
15
18
16
19
Migration within the structured logging working group happens in two different ways - organized & non-organized.
17
20
@@ -42,6 +45,7 @@ Before sending a PR our way, please ensure that there isn't one already in place
42
45
* 1.21 Kubelet was migrated
43
46
* 1.22 Collected feedback and improved the process.
44
47
* 1.23 kube-scheduler and kube-proxy were migrated.
48
+ * 1.24 Contextual logging infrastructure (updated klog, component-base/logs enables it) in place.
45
49
46
50
## Sending a Structured Logging Migration Pull Request
47
51
@@ -78,7 +82,7 @@ Migrate <directory/file> to structured logging
78
82
79
83
### Why my PR was rejected?
80
84
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:
82
86
83
87
* 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 ` .
84
88
* Pull request migrates components that the owners have decided against migrating. List of those components:
@@ -168,47 +172,129 @@ type KMetadata interface {
168
172
}
169
173
```
170
174
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
+
171
234
## Migration
172
235
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
174
243
1 . Remove string formatting from log message
175
244
1 . Name arguments
176
245
1 . Ensure that value is properly passed
177
246
1 . Verify log output
247
+ 1 . Prevent re-adding banned functions after migration
178
248
179
- ## Change log functions to structured equivalent
249
+ ## Change log functions
180
250
181
251
Structured logging functions follow a different logging interface design than other functions in ` klog ` . They follow
182
252
minimal design from [ logr] thus there is no one-to-one mapping.
183
253
184
- Simplified mapping between functions:
254
+ Simplified mapping between functions for structured logging :
185
255
* ` klog.Infof ` , ` klog.Info ` , ` klog.Infoln ` -> ` klog.InfoS `
186
256
* ` klog.InfoDepth ` -> ` klog.InfoSDepth `
187
257
* ` klog.V(N).Infof ` , ` klog.V(N).Info ` , ` klog.V(N).Infoln ` -> ` klog.V(N).InfoS `
188
258
* ` klog.Warning ` , ` klog.Warningf ` , ` klog.Warningln ` -> ` klog.InfoS `
189
259
* ` klog.WarningDepth ` -> ` klog.InfoSDepth `
190
260
* ` klog.Error ` , ` klog.Errorf ` , ` klog.Errorln ` -> ` klog.ErrorS `
191
261
* ` 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 `
194
270
195
271
[ see below ] : #replacing-fatal-calls
196
272
197
- ### Using ErrorS
273
+ ### Using ErrorS or logger.Error
198
274
199
275
With ` klog ` structured logging borrowing the interface from [ logr] it also inherits it's differences in semantic of
200
276
error function. Logs generated by ` ErrorS ` command may be enhanced with additional debug information
201
277
(such as stack traces) or be additionally sent to special error recording tools. Errors should be used to indicate
202
278
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.
203
281
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
206
284
` klog.InfoS ` and pass error in ` err ` key instead.
207
285
208
286
### Replacing Fatal calls
209
287
210
288
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 ` .
212
298
213
299
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.
214
300
@@ -217,7 +303,6 @@ For example
217
303
func validateFlags (cfg *config .Config , flags *pflag .FlagSet ) error {
218
304
if err := cfg.ReadAndValidate (flags); err != nil {
219
305
klog.FatalF (" Error in reading and validating flags %s " , err)
220
- os.Exit (1 )
221
306
}
222
307
}
223
308
```
@@ -226,7 +311,7 @@ should be changed to
226
311
func validateFlags (cfg *config .Config , flags *pflag .FlagSet ) error {
227
312
if err := cfg.ReadAndValidate (flags); err != nil {
228
313
klog.ErrorS (err, " Error in reading and validating flags" )
229
- os. Exit ( 1 )
314
+ klog. FlushAndExit (klog. ExitFlushTimeout , 1 )
230
315
}
231
316
}
232
317
```
@@ -526,3 +611,27 @@ After
526
611
```
527
612
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"
528
613
```
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