Skip to content

[RFC] Structured Error type #357

@tallclair

Description

@tallclair

/kind feature

Describe the solution you'd like
Sometimes I want to be able to add structured logging key-value pairs where an error is generated, but it isn't logged until way up the stack. It would be nice if structured logging information could be attached to the error itself, and logged with the error.

Rough proposal:

+ type StructuredError struct {
+   error
+   KeysAndValues []any
+ }
+ 
+ func (err StructuredError) RecursiveKeysAndValues() []any {
+	kvs := err.KeysAndValues
+	var inner *StructuredError
+	if errors.As(err.error, &inner) {
+		// When duplicate keys are present, should the inner most or outermost error win?
+		kvs = serialize.MergeKVs(inner.RecursiveKeysAndValues(), kvs)
+ 	}
+ 	return kvs
+ }

func (l *loggingT) errorS(err error, logger *logr.Logger, filter LogFilter, depth int, msg string, keysAndValues ...interface{}) {
+	var structuredErr *StructuredError
+	if errors.As(err, &structuredErr) {
+ 		keysAndValues = append(keysAndValues, structuredErr.RecursiveKeysAndValues()...)
+ 	}
	if filter != nil {
		msg, keysAndValues = filter.FilterS(msg, keysAndValues)
	}
	if logger != nil {
		logger.WithCallDepth(depth+2).Error(err, msg, keysAndValues...)
		return
	}
	l.printS(err, severity.ErrorLog, depth+1, msg, keysAndValues...)
}

Open Question: should the message returned by structuredErr.Error() include the keyvalue pairs? In that case they should be omitted from the message when logged with klog, but that gets complicated when you have a non-structured error wrapping a structured error wrapping a non-structured error. My inclination is to not include them in the default Error() message for this reason.

Open Question: should the StructuredError type be hoisted to logr? cc @thockin

Metadata

Metadata

Assignees

Labels

kind/featureCategorizes issue or PR as related to a new feature.lifecycle/rottenDenotes an issue or PR that has aged beyond stale and will be auto-closed.needs-triageIndicates an issue or PR lacks a `triage/foo` label and requires one.

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions