Wednesday 4 December 2013

Ray Gun Post Panic Recover

Ok so I have created my first Go open source package I can add back to the community.

go_raygun
Usage guide can be found at the bottom of this post.

That being said, it is a working progress and any improvement suggestions are welcome, I will be expanding it to cover more areas.

In this blog I will be explaining what I have learnt from the Go 'defer, panic and recover', Go's runtime package, and Ray Gun.

First RayGun is a real time error reporting cloud service that can be set up in under 5 minutes, as they say anyway. The benefits of this application are greater than what I am using it for.

There are the many supported languages '.Net, Java, Python....' but they also provide a REST JSON endpoint here. This is what I use to post the messages to RayGun.

Secondly defer, panic and recover. There is a good blog on how it works here. I will explain more later in the blog on how I am using it.

Third and final the runtime package in Go. The two functions I use are runtime.Caller and runtime.Stack.  The Stack does what it says, it returns a byte array stack for what ever byte length you set.

stack := make([]byte, 1<<16)
stack = stack[:runtime.Stack(stack, false)]
Deconstructing this it does a couple of things. first it makes an object called stack of a byte array with length 1<<16, the 1<<16 syntax is short hand for binary integer to the power 16. I am using the runtime.Stack function. This populates the stack object created with current stack tract.

note: There is also the ability to run a stack tract from debug.Stack. The way I have explained above is the preferred Go way.

The Caller function allows you to get the line and filePath from the stack. You also have the ability to skip a set amount of callers from top of the stack. I am setting the caller to skip 4 levels.
_, filePath, line, _ := runtime.Caller(4)
 The reason I am skipping 4 is so that I return the Caller from where the panic happened. The comes back to explaining how I am using defer, panic and recover.

defer: If you are from a c# background like me it is similar to a finally block. defer will always happen at the end of the function no matter what happens in child functions. The most common use of this is opening connections. As soon as you open a connection you create a defer that closes the connections.

panic: A panic is raised when a runtime exception happens. When a panic happens it stop on that line of code and ends the function. (any defer's that are on that method will still be called, it is important to not set any defer's in this method if you are using this goRayGun package as it will return an incorrect value from runtime.Caller)

recover: The recover function can be called in defer via a inline if statement.
if r := recover(); r != nil { }
Recover will check to see if a panic has been raised. r is of type interface and needs to be type cast to be used. In Go you can call a panic passing in an object. However if you get a runtime panic it will always return an error object with the error message.
errorMsg := r.(error).Error() 

go_raygun set up


As you will be able to see in the from the github link at the top there is an example of how to use the package. In short though, you need to set up the RayGunConfig.json with your application and RayGun settings and save this into the same directory as code you are using it for. The first thing you have to do is create an instance of the package. I have added an interface for the package if people want to use it, (this will enable mocking as explain in my last blog and will be helpful when testing your application). I have not used the interface in my example as wanted to keep it as simple and small as I could.

Before you can use the RaygunRecovery function you must first call LoadRaygunSettings. This only has to be done once from your main function as long as you declare a package global instance of the goraygun package.

Once it is set up you can use the RaygunRecovery in a defer before you call any function that could panic.

An extension I am looking to do is pass back an error object from the defer. This could be done by passing an error pointer object to the defer. I will keep you updated, but if anyone would like to create a fork of the repo could work paired on it.

Note: I have made some amendments.

1) It is now possible to have one defer in your code and any functions below will be caught by the RaygunRecovery.

2) I am now parsing the stack trace into an array of stack events, passing them to Raygun. This helps in determining where the panic came from and the root it had taken.