-
Notifications
You must be signed in to change notification settings - Fork 17.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
proposal: Tracing errors with the tracer
interface.
#23271
Comments
Alternatively, the To help detect tracing "bugs" earlier on, the compiler could enforce that if a trace assignment happens to some |
tracer
interface.
The only time I’ve needed to trace to source in my applications is with panic calls that have the stack trace. Any time I’ve had to look up errors in libraries (such as github.com/lib/pq) I’ve been able to find the source of the error. If an application has multiple sources of an error and can’t panic why not include the trace in the error type already available with Go 1?
|
The effects of proposed mechanism can be achieved with no language changes, see eg: https://github.com/pkg/errors |
“terror” and “Terror” are a very poor choice of name, due to a collision with another English word. “t_error” also being a poor choice, because it obscures the elements constructing it. Go does not use io.RWCloser, it uses io.ReadWriteCloser to make it clear all the functions that are a part of the interface. (Yes, Go has sync.RWLock, but this is a concrete type, not an interface.) So, first thing before any other criticism, it would need to be called “TraceError” or “traceerror”… Second, an perhaps far more importantly, I do not think I am alone in absolutely detesting the hidden mechanism of tracing, let alone the necessity of adding syntax to allow for the trace. If one needs to trace errors, then they should implement a Tracer interface, and explicitly call the Trace function themselves. |
I think plenty of people are having issues with this, and it's the reason why things like
That example doesn't include filename, funcname, & lineno. I want more or less a stack trace, and I don't want to have to figure out what string to put in there. The point is, this should be easy.
That's what I'm suggesting, more or less, but with a bit of modification in Golang's stdlib and type system to make it all easier:
+1 if that's what we go with, ok. But
Ha! I added the
My wrists, they hurt. Also, knowing programmers, they're not going to put trace marks in the right places unless it's easy. Frankly, I'd prefer the code transformer that inserts this trace automatically with no need to type |
I've updated the proposal to make it clear what is being proposed for Golang. Any syntax changes can be decided by third-party tools, but it's optional. The name can change from |
Literally each of the first three discussions you link to are, “you are using github.com/pkg/errors wrongly.” Your solution is in search of a problem. “error” is an exceptional case of a builtin interface because it is used extensively, and there was no simpler way to achieve the desired simple behavior. Your proposal is already covered by github.com/pkg/errors. “But some people use github.com/pkg/errors wrong.” Some people use Go lang itself wrong, and end up with infinite recursions from things as simple as A stacktrace at the generation of the error itself should be sufficient to isolate where the error was generated, and that is precisely what github.com/pkg/errors.New and github.com/pkg/errors.Errorf do. |
You're right, literally each link was tangential to my point. Oops!
That's unfortunately not true because most errors are not generated with
So even if you generate a stack trace at the source, callers of your function (which returns an error) will in general want to wrap it again to add more context, and the preferred method is to use Perhaps there could be a more intelligent automatic way to choose between So OK, if In general, it breaks the intent of Golang to wrap an error with type FooError struct{}
func (_ FooError) Error() string { return "" }
type BarError error
func externalFunc() error { return nil }
func main() {
err := externalFunc()
switch err.(type) {
case FooError:
// handle FooError
case BarError:
// handle BarError
}
fmt.Println("Hello, playground")
} And The solution is to not wrap an error, but to add trace information to the same error object. Ultimately it is But you're still right in that this could all be done in user-land. We can always do more or less the following: I just think it's worth fixing, because the state of error handling and tracing is broken as is, and I stand by that statement. |
Precise details dealing with issues internal to the “I just think it's worth fixing, because the state of error handling and tracing is broken as is, and I stand by that statement.” And the public interfaces of these libraries has been established and cannot really be altered. If the pkg/errors package could be rewritten, he would have done it different. This has been extensively documented by that author. But throwing away all that the |
Thanks. Replied!
I wrote in that discussion how the public interface could be preserved while fixing the implementation. It's still suboptimal compared to native support for
Dare I say, all that learning has been distilled into this very issue! Ok, that's a cocky statement, but I haven't heard a good rebuttal yet. I'm not suggesting a paradigm shift. I'm suggesting making The standard library's |
Knowing the source of the error in the library doesn't tell you where the error was received in your own code.
That's pretty good within a single root-level project, but it doesn't work for modular libraries (and it would make it difficult to refactor such application code to spawn off independent modular libraries). Callers mid-way in the stack don't know how to append trace information to some particular struct, because they don't know that the A better solution is to use interfaces, i.e. with |
One cannot expand the global error interface without breaking all of every error concrete type. |
@puellanivis could you link me to where the @davecheney has documented how he would like to have written pkg/errors? |
@nhooyr I looked to find some at some point, but it’s spread out a lot through numerous proposals on the pkg/errors issues, and can be difficult to find any specific examples. It has however been more of a vague, (paraphrased) “your proposal is good, but a lot of people rely upon the API as currently designed… and we would cause great difficulty to lots of people in the event of a fundamental redesign of the API… no matter how noble it might be.” So, much more like the |
The piece missing is the justification for this proposal to be implemented. There's clearly been a discussion on implementation details and third-party packages. In the same vain, third party libraries are great too, but using their existence to demonstrate necessity is incomplete without a very compelling use-case. |
@as, the fundamental problem that made me adopt pkg/errors (despite it's warts) is that simply finding the original I believe one of the fundamental issues here is that Go assumes all errors must be super-high performance. Certainly, hot pieces of code need that ability, but for many projects the amount of performance-critical code is dwarfed by all the ancillary stuff. |
Thanks, but we definitely don't want to capture a trace for every call to If you want to make a different proposal about adding error traces as a predefined functionality like the predefined |
The
error
system is great, but sometimes it's hard to figure out where an error came from. Here's a proposal that could more or less be implemented today with a Golang source code transpiler, but would benefit from official Golang support. This is a proposal for Go1. (For Go2 I would rather propose thaterror
be a tracer itself)The proposal is to support the following:
terror interface {}
(name can change)"pkg/errors"
,func Terror(e error) terror {...}
as implemented below (name can change)"pkg/errors"
, keep the same signature inerrors.New() error {...}
but have it return aterror
that can be run-time converted.Everything else can be handled by third-party libraries and code transpilers. What follows is just one example of syntax support from a transpiler with support for an assignment modifier
!
.The pain-point with
error
is that it doesn't trace. This seems like a way to inject tracing with minimal syntactic change and good performance.The transpiler could also work without explicit
!
, but rather automatically every time something is assigned to avar _ terror
.The text was updated successfully, but these errors were encountered: