How to use params'Address' in package 'github.com/btcsuite/btcd/rpcclient'? - go

When I'm using the rpcclient to get a new address for the account 'default',running the code below.
client.WalletPassphrase("test1",600)
address,err:=client.GetNewAddress("default")
addresses,err :=client.GetAddressesByAccount("default")
fmt.Println(address)
fmt.Println(addresses)
The output is
<nil>
[]
When I use the command line "btcctl --simnet --rpcuser=rpcuser --rpcpass=rpcpass --wallet getaddressesbyaccount default".It actually output the addresses.
btcctl --simnet --rpcuser=rpcuser --rpcpass=rpcpass --wallet getaddressesbyaccount default
[
"SY2R4vpLPvucTC4MZX1FEEAdX917FZps1H",
"SfBUHVbQ2FRqWPe9wHJuJx2yUstHgRbyBi",
"SehRVnJnhqQ8QeRBgB3B92WZTGo3s9hyCi",
"SUPkJTmx9EjiSMb6qsoSmjShzrejEkhjgn",
"SXu8VAv1cKqC5dkJqredVuhzgzD8QVQvQt",
"SXm73AFZfMqUpCUvEwjdhFxDAwFyD3WVfZ",
"SgN9K7kEsvWgpSzaP4ZZTYaomQfLyYqdkF",
"SUKk1E1JR7wGNTdzLtkb7xJmWN8UgAGdH8",
"SSqpybNox5qXwsDcax8xnA3JqaUqYEzzTq",
"SNpyvoZSEHudpBC4bogkbySioh6HddpEie",
"SimTTUz59RytXsXpTGYwaCqgomiGY8egpo",
"SW9vY6YfJYtHspqVfpijyoi548XXqfrHtt",
"SXJXnLyxYwL2BG8pcAZgW4HCB5ZVCrsS3G",
"SRna6gpPf6bqdfvrmCZz5dzfU9qNbnMZvF",
"SZTUE5WK4EPnF9899a8p5htjpdK9ocN16d",
"Sk2CH4gy9Jhv1wW5YwZzgZmNPgPoHdeYML",
"SkBZ2bTdtYykfE7fSRUc3vAM6w1ub3vTbq"
]
So how can I get the these addresses in my golang function?

I would recommand you in each step check the error and log it to see what is the exact error:
( do not forget to import log)
something like the following code could help you what is the error.
client.WalletPassphrase("test1",600)
address,err:=client.GetNewAddress("default")
if err != nil {
log.Fatal(err)
}
addresses,err :=client.GetAddressesByAccount("default")
if err != nil {
log.Fatal(err)
}
fmt.Println(address)
fmt.Println(addresses)

Related

Unable to get mp4 tkhd info using go-mp4

Using package github.com/abema/go-mp4 when i try to run the example in the docs i get following error:
invalid operation: box (variable of type *mp4.BoxInfo) is not an interface
Here is the example that i am trying:
// extract specific boxes
boxes, err := mp4.ExtractBox(file, nil, mp4.BoxPath{mp4.BoxTypeMoov(), mp4.BoxTypeTrak(), mp4.BoxTypeTkhd()})
if err != nil {
:
}
for _, box := range boxes {
tkhd := box.(*mp4.Tkhd)
fmt.Println("track ID:", tkhd.TrackID)
}
https://pkg.go.dev/github.com/abema/go-mp4#section-readme
Sorry, it is an error of my sample code.
I created Pull Request to fix it.
https://github.com/abema/go-mp4/pull/113/files
When you find any other problems about go-mp4, I would be grateful if you could create new Issue on GitHub.
https://github.com/abema/go-mp4/issues

To what extent are errors strings guaranteed to not change?

One of the main issues I have with Golang is that the error handling is basically a check for a string (I would love to be wrong, do not hesitate :))
In the example below, I am trying to create a directory, but will have different behaviour depending on the kind of issue. Specifically, if a directory exists I will just pass.
package main
import (
"fmt"
"os"
)
func main() {
err := os.Mkdir("test", 0644)
if err != nil {
fmt.Printf("error: %v", err)
if err.Error() == "mkdir test: Cannot create a file when that file already exists" {
fmt.Printf("the dir already exists")
} else {
panic(err)
}
}
}
It does not work, repeated attempts are not logged. Why? Ah, crap, I forgot the dot at the end of the mkdir test: Cannot create a file when that file already exists string.
I feel that relying on an error string is fragile, as opposed to having something like err.ErrorType().DirectoryExists() kind of check (which sometimes exists, in net for instance).
My question: to what extent can I rely on the fact that the error strings will not change? (in other words, that mkdir test: Cannot create a file when that file already exists. will not be worded differently, or ported to another national language, etc.)
I had some hope with errors.Is() but it ultimately relies on the string comparison.
Go error strings don't change arbitrarily, but they also aren't covered by the Go compatibility policy: they can be changed if the increase in clarity outweighs the (inevitable) cost of breaking programs that make (fragile, unsupported) assumptions about the string contents.
The errors package is the robust way to check for specific types of errors.
Use errors.Is to check for equivalence to a canonical error (https://play.golang.org/p/co6ukgQrr58):
err := os.Mkdir(dir, 0644)
if errors.Is(err, os.ErrExist) {
t.Logf("the dir already exists")
} else if err != nil {
t.Fatal(err)
}
Use errors.As to check for a particular type of error (https://play.golang.org/p/UR1nUCRMUY6):
err := os.Mkdir(dir, 0644)
var pe *os.PathError
if errors.As(err, &pe) {
t.Logf("error creating %q: %v", pe.Path, pe.Err)
} else if err != nil {
t.Fatal(err)
}
In this case, you can use os.IsExist(err)
err := os.Mkdir("test", 0644)
if err != nil {
if os.IsExist(err){
fmt.Printf("the dir already exists")
} else {
panic(err)
}
}
Good libraries should allow you to inspect errors without relying on string comparison. Various methods exist to do so:
Comparaison with sentinel values if err == os.EOF
Utility function: os.IsExist(err)
Type assertion: pathErr := err.(*os.PathError)
There is always a way to inspect errors in the standard library without relying on strings. Check the function/package documentation for details about how to do it.
Note:
errors.Is() and errors.As() are a (~recent) generalisation of == and type assertion but for errors that could contain other errors. See https://go.dev/blog/go1.13-errors
From https://pkg.go.dev/os#Mkdir:
Mkdir creates a new directory with the specified name and permission bits (before umask). If there is an error, it will be of type *PathError.
This means you could type-assert the returned error to get more information.
if err != nil {
pathErr := err.(*os.PathError)
}
With errors returned from functions in package os specifically, also take note of these two functions:
https://pkg.go.dev/os#IsExist
https://pkg.go.dev/os#IsNotExist
to what extent can I rely on the fact that the error strings will not change?
To the extent which is guaranteed by the function's contract, which as in most programming languages conventionally is written in documenting comments above the function. In the case of os.MkDir(): you cannot.

What is good practice for nested function's return error? [duplicate]

I wondering what is the best way to handle error form multiple level abstraction in go. Every time if I must add a new level abstraction to program, I am forced to transfer error code from level less to level high. Thereby is duplicate communitaces in log file or I must remmember to delete communicate form level low and transfer him to level higher. Below simply example. I skipped creating each object to more shortly and celar code, but I think You understand my problem
type ObjectOne struct{
someValue int
}
func (o* ObjectOne)CheckValue()error{
if o.someValue == 0 {
SomeLogger.Printf("Value is 0 error program") // communicate form first level abstraction to logger
return errors.New("Internal value in object is 0")
}
return nil
}
type ObjectTwoHigherLevel struct{
objectOne ObjectOne
}
func (oT* ObjectTwoHigherLevel)CheckObjectOneIsReady() error{
if err := oT.objectOne.CheckValue() ; err != nil{
SomeLogger.Printf("Value in objectOne is not correct for objectTwo %s" , err) // second communicate
return err
}
return nil
}
type ObjectThreeHiggerLevel struct{
oT ObjectTwoHigherLevel
}
func (oTh* ObjectThreeHiggerLevel)CheckObjectTwoIsReady()error{
if err := oTh.oT.CheckObjectOneIsReady() ; err != nil{
SomeLogger.Printf("Value in objectTwo is not correct for objectThree %s" , err)
return err
}
return nil
}
In result in log file I get duplicate posts
Value is 0 error program
Value in objectOne is not correct for objectTwo Internal value in object is 0
Value in objectTwo is not correct for objectThree Internal value in object is 0
In turn if I only transfer some err to higher level without additional log I lost information what happend in each level.
How this solve ? How privent duplicate communicates ? Or My way is the good and the only ?
Problem is more frustrating if I create a few object which search something in database on a few abstraction level then I get also few lines form this same task in logFile.
EDIT: This answer pre-dates Go 1.13 which provides something similar to the presented technique. Please check The Go Blog: Working with Errors in Go 1.13.
You should either handle an error, or not handle it but delegate it to a higher level (to the caller). Handling the error and returning it is bad practice as if the caller also does the same, the error might get handled several times.
Handling an error means inspecting it and making a decision based on that, which may be you simply log it, but that also counts as "handling" it.
If you choose to not handle but delegate it to a higher level, that may be perfectly fine, but don't just return the error value you got, as it may be meaningless to the caller without context.
Annotating errors
A really nice and recommended way of delegation is Annotating errors. This means you create and return a new error value, but the old one is also wrapped in the returned value. The wrapper provides the context for the wrapped error.
There is a public library for annotating errors: github.com/pkg/errors; and its godoc: errors
It basically has 2 functions: 1 for wrapping an existing error:
func Wrap(cause error, message string) error
And one for extracting a wrapped error:
func Cause(err error) error
Using these, this is how your error handling may look like:
func (o *ObjectOne) CheckValue() error {
if o.someValue == 0 {
return errors.New("Object1 illegal state: value is 0")
}
return nil
}
And the second level:
func (oT *ObjectTwoHigherLevel) CheckObjectOneIsReady() error {
if err := oT.objectOne.CheckValue(); err != nil {
return errors.Wrap(err, "Object2 illegal state: Object1 is invalid")
}
return nil
}
And the third level: call only the 2nd level check:
func (oTh *ObjectThreeHiggerLevel) CheckObjectTwoIsReady() error {
if err := oTh.ObjectTwoHigherLevel.CheckObjectOneIsReady(); err != nil {
return errors.Wrap(err, "Object3 illegal state: Object2 is invalid")
}
return nil
}
Note that since the CheckXX() methods do not handle the errors, they don't log anything. They are delegating annotated errors.
If someone using ObjectThreeHiggerLevel decides to handle the error:
o3 := &ObjectThreeHiggerLevel{}
if err := o3.CheckObjectTwoIsReady(); err != nil {
fmt.Println(err)
}
The following nice output will be presented:
Object3 illegal state: Object2 is invalid: Object2 illegal state: Object1 is invalid: Object1 illegal state: value is 0
There is no pollution of multiple logs, and all the details and context are preserved because we used errors.Wrap() which produces an error value which formats to a string which preserves the wrapped errors, recursively: the error stack.
You can read more about this technique in blog post:
Dave Cheney: Don’t just check errors, handle them gracefully
"Extending" errors
If you like things simpler and / or you don't want to hassle with external libraries and you're fine with not being able to extract the original error (the exact error value, not the error string which you can), then you may simply extend the error with the context and return this new, extended error.
Extending an error is easiest done by using fmt.Errorf() which allows you to create a "nice" formatted error message, and it returns you a value of type error so you can directly return that.
Using fmt.Errorf(), this is how your error handling may look like:
func (o *ObjectOne) CheckValue() error {
if o.someValue == 0 {
return fmt.Errorf("Object1 illegal state: value is %d", o.someValue)
}
return nil
}
And the second level:
func (oT *ObjectTwoHigherLevel) CheckObjectOneIsReady() error {
if err := oT.objectOne.CheckValue(); err != nil {
return fmt.Errorf("Object2 illegal state: %v", err)
}
return nil
}
And the third level: call only the 2nd level check:
func (oTh *ObjectThreeHiggerLevel) CheckObjectTwoIsReady() error {
if err := oTh.ObjectTwoHigherLevel.CheckObjectOneIsReady(); err != nil {
return fmt.Errorf("Object3 illegal state: %v", err)
}
return nil
}
And the following error message would be presented at ObjectThreeHiggerLevel should it decide to "handle" it:
o3 := &ObjectThreeHiggerLevel{}
if err := o3.CheckObjectTwoIsReady(); err != nil {
fmt.Println(err)
}
The following nice output will be presented:
Object3 illegal state: Object2 illegal state: Object1 illegal state: value is 0
Be sure to also read blog post: Error handling and Go
There are various libraries that embed stack traces in Go errors. Simply create your error with one of those, and it will bubble up with the full stack context you can later inspect or log.
One such library:
https://github.com/go-errors/errors
And there are a few others I forgot.

Best practice to handle error from multiple abstract level

I wondering what is the best way to handle error form multiple level abstraction in go. Every time if I must add a new level abstraction to program, I am forced to transfer error code from level less to level high. Thereby is duplicate communitaces in log file or I must remmember to delete communicate form level low and transfer him to level higher. Below simply example. I skipped creating each object to more shortly and celar code, but I think You understand my problem
type ObjectOne struct{
someValue int
}
func (o* ObjectOne)CheckValue()error{
if o.someValue == 0 {
SomeLogger.Printf("Value is 0 error program") // communicate form first level abstraction to logger
return errors.New("Internal value in object is 0")
}
return nil
}
type ObjectTwoHigherLevel struct{
objectOne ObjectOne
}
func (oT* ObjectTwoHigherLevel)CheckObjectOneIsReady() error{
if err := oT.objectOne.CheckValue() ; err != nil{
SomeLogger.Printf("Value in objectOne is not correct for objectTwo %s" , err) // second communicate
return err
}
return nil
}
type ObjectThreeHiggerLevel struct{
oT ObjectTwoHigherLevel
}
func (oTh* ObjectThreeHiggerLevel)CheckObjectTwoIsReady()error{
if err := oTh.oT.CheckObjectOneIsReady() ; err != nil{
SomeLogger.Printf("Value in objectTwo is not correct for objectThree %s" , err)
return err
}
return nil
}
In result in log file I get duplicate posts
Value is 0 error program
Value in objectOne is not correct for objectTwo Internal value in object is 0
Value in objectTwo is not correct for objectThree Internal value in object is 0
In turn if I only transfer some err to higher level without additional log I lost information what happend in each level.
How this solve ? How privent duplicate communicates ? Or My way is the good and the only ?
Problem is more frustrating if I create a few object which search something in database on a few abstraction level then I get also few lines form this same task in logFile.
EDIT: This answer pre-dates Go 1.13 which provides something similar to the presented technique. Please check The Go Blog: Working with Errors in Go 1.13.
You should either handle an error, or not handle it but delegate it to a higher level (to the caller). Handling the error and returning it is bad practice as if the caller also does the same, the error might get handled several times.
Handling an error means inspecting it and making a decision based on that, which may be you simply log it, but that also counts as "handling" it.
If you choose to not handle but delegate it to a higher level, that may be perfectly fine, but don't just return the error value you got, as it may be meaningless to the caller without context.
Annotating errors
A really nice and recommended way of delegation is Annotating errors. This means you create and return a new error value, but the old one is also wrapped in the returned value. The wrapper provides the context for the wrapped error.
There is a public library for annotating errors: github.com/pkg/errors; and its godoc: errors
It basically has 2 functions: 1 for wrapping an existing error:
func Wrap(cause error, message string) error
And one for extracting a wrapped error:
func Cause(err error) error
Using these, this is how your error handling may look like:
func (o *ObjectOne) CheckValue() error {
if o.someValue == 0 {
return errors.New("Object1 illegal state: value is 0")
}
return nil
}
And the second level:
func (oT *ObjectTwoHigherLevel) CheckObjectOneIsReady() error {
if err := oT.objectOne.CheckValue(); err != nil {
return errors.Wrap(err, "Object2 illegal state: Object1 is invalid")
}
return nil
}
And the third level: call only the 2nd level check:
func (oTh *ObjectThreeHiggerLevel) CheckObjectTwoIsReady() error {
if err := oTh.ObjectTwoHigherLevel.CheckObjectOneIsReady(); err != nil {
return errors.Wrap(err, "Object3 illegal state: Object2 is invalid")
}
return nil
}
Note that since the CheckXX() methods do not handle the errors, they don't log anything. They are delegating annotated errors.
If someone using ObjectThreeHiggerLevel decides to handle the error:
o3 := &ObjectThreeHiggerLevel{}
if err := o3.CheckObjectTwoIsReady(); err != nil {
fmt.Println(err)
}
The following nice output will be presented:
Object3 illegal state: Object2 is invalid: Object2 illegal state: Object1 is invalid: Object1 illegal state: value is 0
There is no pollution of multiple logs, and all the details and context are preserved because we used errors.Wrap() which produces an error value which formats to a string which preserves the wrapped errors, recursively: the error stack.
You can read more about this technique in blog post:
Dave Cheney: Don’t just check errors, handle them gracefully
"Extending" errors
If you like things simpler and / or you don't want to hassle with external libraries and you're fine with not being able to extract the original error (the exact error value, not the error string which you can), then you may simply extend the error with the context and return this new, extended error.
Extending an error is easiest done by using fmt.Errorf() which allows you to create a "nice" formatted error message, and it returns you a value of type error so you can directly return that.
Using fmt.Errorf(), this is how your error handling may look like:
func (o *ObjectOne) CheckValue() error {
if o.someValue == 0 {
return fmt.Errorf("Object1 illegal state: value is %d", o.someValue)
}
return nil
}
And the second level:
func (oT *ObjectTwoHigherLevel) CheckObjectOneIsReady() error {
if err := oT.objectOne.CheckValue(); err != nil {
return fmt.Errorf("Object2 illegal state: %v", err)
}
return nil
}
And the third level: call only the 2nd level check:
func (oTh *ObjectThreeHiggerLevel) CheckObjectTwoIsReady() error {
if err := oTh.ObjectTwoHigherLevel.CheckObjectOneIsReady(); err != nil {
return fmt.Errorf("Object3 illegal state: %v", err)
}
return nil
}
And the following error message would be presented at ObjectThreeHiggerLevel should it decide to "handle" it:
o3 := &ObjectThreeHiggerLevel{}
if err := o3.CheckObjectTwoIsReady(); err != nil {
fmt.Println(err)
}
The following nice output will be presented:
Object3 illegal state: Object2 illegal state: Object1 illegal state: value is 0
Be sure to also read blog post: Error handling and Go
There are various libraries that embed stack traces in Go errors. Simply create your error with one of those, and it will bubble up with the full stack context you can later inspect or log.
One such library:
https://github.com/go-errors/errors
And there are a few others I forgot.

How to get the stack trace pointing to actual error reason

Let's say I have some code like this:
value, err := some3rdpartylib.DoSomething()
if err != nil {
panic(err)
}
In case err != nil I will get something like this:
panic: some error explanation here
goroutine 1 [running]:
main.main()
/tmp/blabla/main.go:6 +0x80
This stack trace is completely legit but sometimes these error messages may not clarify what happened and so I'd like to dig deeper into the source code of the 3rd party library to investigate what exactly causes this error to be returned. However when my code panics like this, there is no way to get the actual place that returned this error.
A little bit more clarification: as I'm coming from JVM world where exceptions are thrown I can completely trace what exactly line of code thrown the exception and thus can easily find the place and see what gone wrong. Go stack trace ends exactly where my code panics and thus not too useful in my case.
I've created a playground here and ideally I'd like to be able to trace the error to the place it was actually returned from, not panic. (e.g. to line 17, return "", errors.New("some error explanation here"))
Is this even possible?
I think that there is an easier way to achieve this. You can try wrapping errors using the golang "default" third party library error package:
You need to define the interface to be implemented by your error :
type stackTracer interface {
StackTrace() errors.StackTrace
}
Then use it when wrapping/processing an error :
err, ok := errors.(stackTracer) // ok is false if errors doesn't implement stackTracer
stack := err.StackTrace()
fmt.Println(stack) // here you'll have your stack trace
Shortly: this is not possible.
Since errors are values, they are not treated in any special way. Due to this, when function (normally) returns, stack is no more available (ie. another function call may overwrite memory used by returning-error function' stack).
There is a tool called trace which was introduced with go1.5, but for now, there is no comprehensive tutorial available neither any of those I found says that this kind of feature will be included.
As others have pointed out tracing errors in go isn't trivial. There are projects like juju/errgo, that allow you to wrap errors and then trace these errors back. For that to work tough, you must use them consistently throughout your project and that won't help you with errors in 3rd party libraries or with errors that get handled and never get returned.
Because this is such a common issue and I really got annoyed with this, I wrote a small debug utility that will add debug code to go files that logs every returned error (value that implements error) and the function in which it was returned to STDOUT (if you need more advanced logging just hack the logger in the project, it's really simple).
Installation
go get github.com/gellweiler/errgotrace
Usage
To debug all files in the current directory:
$ find . -name '*.go' -print0 | xargs -0 errgotrace -w
To remove the added debug code from the go files:
$ find . -name '*.go' -print0 | xargs -0 errgotrace -w -r
Then just simply compile & run your code or your test cases.
Sample Output
[...]
2017/12/13 00:54:39 [ERRGOTRACE] parser.*Parser.objectKey: EOF token found
2017/12/13 00:54:39 [ERRGOTRACE] parser.*Parser.objectItem: EOF token found
2017/12/13 00:54:39 [ERRGOTRACE] parser.*Parser.objectKey: EOF token found
2017/12/13 00:54:39 [ERRGOTRACE] parser.*Parser.objectItem: EOF token found
2017/12/13 00:54:39 [ERRGOTRACE] parser.*Parser.objectKey: At 3:4: nested object expected: LBRACE got: ASSIGN
2017/12/13 00:54:39 [ERRGOTRACE] parser.*Parser.objectItem: At 3:4: nested object expected: LBRACE got: ASSIGN
2017/12/13 00:54:39 [ERRGOTRACE] parser.*Parser.objectList: At 3:4: nested object expected: LBRACE got: ASSIGN
2017/12/13 00:54:39 [ERRGOTRACE] parser.*Parser.Parse: At 2:31: literal not terminated
2017/12/13 00:54:39 [ERRGOTRACE] parser.Parse: At 2:31: literal not terminated
2017/12/13 00:54:39 [ERRGOTRACE] hcl.parse: At 2:31: literal not terminated
2017/12/13 00:54:39 [ERRGOTRACE] hcl.ParseBytes: At 2:31: literal not terminated
2017/12/13 00:54:39 [ERRGOTRACE] formula.parse: parsing failed
[...]
As you can see from this output, it's really easy to tell in which function the error originally occurred. Once you know that, you can use a debugger to get more context.
Take a look at https://github.com/ztrue/tracerr
I created this package in order to have both stack trace and source fragments to be able to debug faster and log errors with much more details.
Here is a code example:
package main
import (
"io/ioutil"
"github.com/ztrue/tracerr"
)
func main() {
if err := read(); err != nil {
tracerr.PrintSourceColor(err)
}
}
func read() error {
return readNonExistent()
}
func readNonExistent() error {
_, err := ioutil.ReadFile("/tmp/non_existent_file")
// Add stack trace to existing error, no matter if it's nil.
return tracerr.Wrap(err)
}
And here is the output:
package main
import (
"errors"
"fmt"
)
func main() {
value, err := DoSomething()
if err != nil {
panic(err)
}
fmt.Println(value)
}
func DoSomething() (string, error) {
return "", errors.New("some error explanation here")
}
The problem is the standard package errors does not attach the stack trace at the point it occurs. You can use github.com/pkg/errors to do that:
package main
import (
"github.com/pkg/errors"
"fmt"
)
func main() {
value, err := DoSomething()
if err != nil {
fmt.Printf("%+v", err)
}
fmt.Println(value)
}
func DoSomething() (string, error) {
return "", errors.New("some error explanation here")
}
$ go run stacktrace.go
some error explanation here
main.DoSomething
/Users/quanta/go/src/github.com/quantonganh/errors/stacktrace.go:18
main.main
/Users/quanta/go/src/github.com/quantonganh/errors/stacktrace.go:10
runtime.main
/usr/local/Cellar/go/1.15.2/libexec/src/runtime/proc.go:204
runtime.goexit
/usr/local/Cellar/go/1.15.2/libexec/src/runtime/asm_amd64.s:1374
More details: https://dave.cheney.net/2016/06/12/stack-traces-and-the-errors-package
Take a look at: https://github.com/efimovalex/stackerr
Is this the thing you are looking for?
package main
import "github.com/efimovalex/stackerr"
import "fmt"
func f1() *stackerr.Err {
err := stackerr.Error("message")
return err.Stack()
}
func f2() *stackerr.Err {
err := f1()
return err.Stack()
}
type t1 struct{}
func (t *t1) f3() *stackerr.Err {
err := f2()
return err.Stack()
}
func main() {
ts := t1{}
err := ts.f3()
err.Log()
}
Result:
2017/08/31 12:13:47 Error Stacktrace:
-> github.com/efimovalex/stackerr/example/main.go:25 (main.main)
-> github.com/efimovalex/stackerr/example/main.go:19 (main.(*t1).f3)
-> github.com/efimovalex/stackerr/example/main.go:12 (main.f2)
-> github.com/efimovalex/stackerr/example/main.go:7 (main.f1)
As I know, stackrerror is the simplest stack display package. You can use all the native log library records or output the call stack yourself. For example:
package main
import "github.com/lingdor/stackerror"
func act1()error {
return stackerror.New("here Error")
}
func main(){
err:=act1()
fmt.println(err.Error()) //panic(err) and log.Info(err) are ok
}
output :
*stackError.stackError : here Error
at main.act1( /Users/user/go/testMain/src/main/main.go:17 )
at main.main( /Users/user/go/testMain/src/main/main.go:22 )
at runtime.main( /usr/local/Cellar/go/1.13.4/libexec/src/runtime/proc.go:203 )
You can use the built-in Recover function to handle panic and print the stack trace.
From https://blog.golang.org/defer-panic-and-recover
Recover is a built-in function that regains control of a panicking
goroutine. Recover is only useful inside deferred functions. During
normal execution, a call to recover will return nil and have no other
effect. If the current goroutine is panicking, a call to recover will
capture the value given to panic and resume normal execution.
I have modified your example to use recover and eris. Eris provides a better way to handle, trace, and log errors in Go.
package main
import (
"github.com/rotisserie/eris"
"fmt"
)
func main() {
value, err := DoSomething()
defer func() {
if r := recover(); r!= nil {
fmt.Println(fmt.Sprintf("%+v", r))
}
}()
if err != nil {
panic(err)
}
fmt.Println(value)
}
func DoSomething() (string, error) {
return "", eris.New("some error explanation here")
}
The output is:
some error explanation here
main.DoSomething: /tmp/sandbox147128055/prog.go: 23
main.main: /tmp/sandbox147128055/prog.go: 9
runtime.main: /usr/local/go/src/runtime/proc.go: 203
runtime.goexit: /usr/local/go/src/runtime/asm_amd64p32.s: 523
See it in action here https://play.golang.org/p/jgkaR42ub5q

Resources