-
Notifications
You must be signed in to change notification settings - Fork 330
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Backend for improving test logging (#1022)
* Create TLogger, bringing leveled logging to tests Inspired heavily by go-logr, brings leveled logging to tests while using the highly reliable zap logger backend to fulfill writing to multiple log files. Now that metrics logger has been removed, SetupLoggingFlags() is redundant. Moved to test/logging * Cleanup TLogger and document Add TLogger.Collect() to support collecting outcomes (either positive strings or error types) which will be processed in a subtest upon completion of the currently-running (sub-)test. StructuredError prints its output in a more-readable form now. Call-site should now work for .Log/.Logf-using functions. Use cancel from NewTLogger instead of t.CleanUp() Use a zapcore SpewEncoder when writing to testing.T Means even non-json-encodable structs will get printed to the test log output, while preserving encodable structured output for other log files. Removed timestamp from printing to t.Log * Fix the automatically-detected issues Spelling, codegen, some coverage (coverage of memory_encoder.go should not be necessary but the coverage tool is broken and needs to be fixed). * Increase unit test coverage slightly and fix tiny comment * Set attribute the way the coverage program wants * Cleanup based on notes * Comment cleanup and privitize var * Mistake; left purposely-failing tests enabled * Reduce long lines * Consolidate cleanup code; make variable expressive * Cleanup and bugfix Failing functions now tested by having an internal constructor option to skip calls to t.Fail()/t.FailNow(). Fixed some bugs with internal method errorWithRuntimeCheck and added tests for it. Address minor typos from comments. * Tiny name/optimization fixes
- Loading branch information
Showing
22 changed files
with
2,171 additions
and
222 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
memory_encoder.go coverage-excluded=true |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
/* | ||
Copyright 2020 The Knative Authors | ||
Licensed under the Apache License, Version 2.0 (the "License"); | ||
you may not use this file except in compliance with the License. | ||
You may obtain a copy of the License at | ||
http://www.apache.org/licenses/LICENSE-2.0 | ||
Unless required by applicable law or agreed to in writing, software | ||
distributed under the License is distributed on an "AS IS" BASIS, | ||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
See the License for the specific language governing permissions and | ||
limitations under the License. | ||
*/ | ||
|
||
/* | ||
Package logging assists setting up test logging and using leveled logging in tests. | ||
The TLogger is designed to assist the test writer in creating more useful tests and | ||
collecting log data in multiple streams, optimizing for human readability in one and | ||
machine readability in another. It's designed to mimic the testing.T object rather closely and | ||
use Zap logging semantics, both things already in use in Knative, to minimize the time developers | ||
need to spend learning the tool. | ||
Inspired by and uses go-logr. | ||
Advantages | ||
The TLogger enhances test design through subtle nudges and affordances: | ||
* It encourages only logging with .V(), giving the writer a nudge to think about how important it is, | ||
but without requiring them to fit it in a narrowly-defined category. | ||
* Reduces boilerplate of carrying around context for errors in several different variables, | ||
using .WithValues(), which results in more consistent and reusable code across the tests. | ||
Porting | ||
To port code from using testing.T to logging.TLogger, the interfaces knative.dev/pkg/test.T and | ||
knative.dev/pkg/test.TLegacy have been created. All library functions should be refactored to use | ||
one interface and all .Log() calls rewritten to use structured format, which works with testing and | ||
TLogger. If a library function needs test functions not available even in test.TLegacy, | ||
it's probably badly written. | ||
Then any test can be incrementally rewritten to use TLogger, as it coexists with testing.T without issue. | ||
*/ | ||
package logging |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
/* | ||
Copyright 2020 The Knative Authors | ||
Licensed under the Apache License, Version 2.0 (the "License"); | ||
you may not use this file except in compliance with the License. | ||
You may obtain a copy of the License at | ||
http://www.apache.org/licenses/LICENSE-2.0 | ||
Unless required by applicable law or agreed to in writing, software | ||
distributed under the License is distributed on an "AS IS" BASIS, | ||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
See the License for the specific language governing permissions and | ||
limitations under the License. | ||
*/ | ||
|
||
package logging | ||
|
||
import ( | ||
"fmt" | ||
|
||
"github.com/davecgh/go-spew/spew" | ||
) | ||
|
||
// StructuredError is an error which can hold arbitrary key-value arguments. | ||
// | ||
// TODO(coryrc): The Structured Error is experimental and likely to be removed, but is currently in use in a refactored test. | ||
type StructuredError interface { | ||
error | ||
GetValues() []interface{} | ||
WithValues(...interface{}) StructuredError | ||
DisableValuePrinting() | ||
EnableValuePrinting() | ||
} | ||
|
||
type structuredError struct { | ||
msg string | ||
keysAndValues []interface{} | ||
print bool | ||
} | ||
|
||
func keysAndValuesToSpewedMap(args ...interface{}) map[string]string { | ||
m := make(map[string]string, len(args)/2) | ||
for i := 0; i < len(args); i += 2 { | ||
key, val := args[i], args[i+1] | ||
if keyStr, ok := key.(string); ok { | ||
m[keyStr] = spew.Sdump(val) | ||
} | ||
} | ||
return m | ||
} | ||
|
||
// Implement `error` interface | ||
func (e structuredError) Error() string { | ||
// TODO(coryrc): accept zap.Field entries? | ||
if e.print { | ||
// %v for fmt.Sprintf does print keys sorted | ||
return fmt.Sprintf("Error: %s\nContext:\n%v", e.msg, keysAndValuesToSpewedMap(e.keysAndValues...)) | ||
} else { | ||
return e.msg | ||
} | ||
} | ||
|
||
// GetValues gives you the structured key values in a plist | ||
func (e structuredError) GetValues() []interface{} { | ||
return e.keysAndValues | ||
} | ||
|
||
// DisableValuePrinting disables printing out the keys and values from the Error() method | ||
func (e *structuredError) DisableValuePrinting() { | ||
e.print = false | ||
} | ||
|
||
// EnableValuePrinting enables printing out the keys and values from the Error() method | ||
func (e *structuredError) EnableValuePrinting() { | ||
e.print = true | ||
} | ||
|
||
// Create a StructuredError. Gives a little better logging when given to a TLogger. | ||
// This may prove to not be useful if users use the logger's WithValues() better. | ||
func Error(msg string, keysAndValues ...interface{}) *structuredError { | ||
return &structuredError{msg, keysAndValues, true} | ||
} | ||
|
||
// WithValues operates just like TLogger's WithValues but stores them in the error object. | ||
func (e *structuredError) WithValues(keysAndValues ...interface{}) StructuredError { | ||
newKAV := make([]interface{}, 0, len(keysAndValues)+len(e.keysAndValues)) | ||
newKAV = append(newKAV, e.keysAndValues...) | ||
newKAV = append(newKAV, keysAndValues...) | ||
return &structuredError{e.msg, newKAV, e.print} | ||
} |
Oops, something went wrong.