Errorf creates a redactable error that has an error string identical to that of a [fmt.Errorf] error. Calling [Redact] on the result will redact all format arguments from the error message instead of redacting the entire string. When redacting the error string, Errorf replaces arguments that implem
(format string, a ...any)
| 121 | // Errorf("cannot find user %s", Safe(username)).Error() |
| 122 | // // cannot find user bob |
| 123 | func Errorf(format string, a ...any) error { |
| 124 | // Capture a stack trace. |
| 125 | safeErr := &safeError{ |
| 126 | callers: make([]uintptr, 32), |
| 127 | } |
| 128 | n := runtime.Callers(2, safeErr.callers) |
| 129 | safeErr.callers = safeErr.callers[:n] |
| 130 | |
| 131 | // Create the "normal" unredacted error. We need to remove the safe wrapper |
| 132 | // from any args so that fmt.Errorf can detect and format their type |
| 133 | // correctly. |
| 134 | args := make([]any, len(a)) |
| 135 | for i := range a { |
| 136 | if safe, ok := a[i].(safe); ok { |
| 137 | args[i] = safe.a |
| 138 | } else { |
| 139 | args[i] = a[i] |
| 140 | } |
| 141 | } |
| 142 | safeErr.err = fmt.Errorf(format, args...) |
| 143 | |
| 144 | // Now create the redacted error by replacing all args with their redacted |
| 145 | // version or by inserting a placeholder if the arg can't be redacted. |
| 146 | for i := range a { |
| 147 | switch t := a[i].(type) { |
| 148 | case safe: |
| 149 | args[i] = t.a |
| 150 | case error: |
| 151 | args[i] = Error(t) |
| 152 | case redactor: |
| 153 | args[i] = formatter(t.Redact()) |
| 154 | default: |
| 155 | args[i] = formatter(placeholder(t)) |
| 156 | } |
| 157 | } |
| 158 | safeErr.redacted = fmt.Errorf(format, args...) |
| 159 | return safeErr |
| 160 | } |
| 161 | |
| 162 | // redactor defines the Redact interface for types that can format themselves |
| 163 | // in redacted errors. |