if an error happens in a function down the execution path, we show it from the top level, possibly appending error messages produced by intermediate calling. That is, if we have functions a(), b() and c() and the call sequence is a->b->c, and c errors out, then it is common to have in c:
if err != nil {
return ftm.Errorf("c went wrong at OID %d: %v", oid, err)
and sometimes in b as well (other times it just passes the error to the upper level):
if err != nil {
return fmt.Errrof("operation from b went wrong: %v", err)
and finally in a:
if err != nil {
logger.WithError(err).Error("a failed")
resulting in:
"a failed", {"error": "operation from b went wrong: c went wrong at OID 1233: foo() system call failed"}
followed by a stack trace originating from a.
This approach doesn't preserve a structure in the error message (we have no separate OID field), produces ad-hoc stack traces in the error messages and obfuscates the actual stack trace, that will start with a and not with c.
I'd propose to change it, so that the error is reported right away when it happens (i.e. c() will call logger.WithError(err).WithOID(oid).Error("c went wrong"), as well as passed upstream, where the first function that does not abort after seeing the error would report the error condition and possible workaround by itself (i.e. a will report logger.WithError("operation X failed, will retry after 5 seconds") without including the original error. That way, we would avoid showing the same error twice, while at the same time inform the user about the error as early as possible (together with a precise location in the code), as well as make the user know how the error has affected the flow.
Objections?
if an error happens in a function down the execution path, we show it from the top level, possibly appending error messages produced by intermediate calling. That is, if we have functions a(), b() and c() and the call sequence is a->b->c, and c errors out, then it is common to have in c:
and sometimes in b as well (other times it just passes the error to the upper level):
and finally in a:
resulting in:
followed by a stack trace originating from a.
This approach doesn't preserve a structure in the error message (we have no separate OID field), produces ad-hoc stack traces in the error messages and obfuscates the actual stack trace, that will start with a and not with c.
I'd propose to change it, so that the error is reported right away when it happens (i.e. c() will call
logger.WithError(err).WithOID(oid).Error("c went wrong"), as well as passed upstream, where the first function that does not abort after seeing the error would report the error condition and possible workaround by itself (i.e. a will report logger.WithError("operation X failed, will retry after 5 seconds") without including the original error. That way, we would avoid showing the same error twice, while at the same time inform the user about the error as early as possible (together with a precise location in the code), as well as make the user know how the error has affected the flow.Objections?