| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181 |
- package honeybadger
- import (
- "encoding/json"
- "net/url"
- "os"
- "regexp"
- "time"
- "github.com/pborman/uuid"
- "github.com/shirou/gopsutil/load"
- "github.com/shirou/gopsutil/mem"
- )
- // ErrorClass represents the class name of the error which is sent to
- // Honeybadger.
- type ErrorClass struct {
- Name string
- }
- // Fingerprint represents the fingerprint of the error, which controls grouping
- // in Honeybadger.
- type Fingerprint struct {
- Content string
- }
- func (f *Fingerprint) String() string {
- return f.Content
- }
- // Notice is a representation of the error which is sent to Honeybadger, and
- // implements the Payload interface.
- type Notice struct {
- APIKey string
- Error Error
- Token string
- ErrorMessage string
- ErrorClass string
- Tags []string
- Hostname string
- Env string
- Backtrace []*Frame
- ProjectRoot string
- Context Context
- Params Params
- CGIData CGIData
- URL string
- Fingerprint string
- }
- func (n *Notice) asJSON() *hash {
- return &hash{
- "api_key": n.APIKey,
- "notifier": &hash{
- "name": "honeybadger",
- "url": "https://github.com/honeybadger-io/honeybadger-go",
- "version": VERSION,
- },
- "error": &hash{
- "token": n.Token,
- "message": n.ErrorMessage,
- "class": n.ErrorClass,
- "tags": n.Tags,
- "backtrace": n.Backtrace,
- "fingerprint": n.Fingerprint,
- },
- "request": &hash{
- "context": n.Context,
- "params": n.Params,
- "cgi_data": n.CGIData,
- "url": n.URL,
- },
- "server": &hash{
- "project_root": n.ProjectRoot,
- "environment_name": n.Env,
- "hostname": n.Hostname,
- "time": time.Now().UTC(),
- "pid": os.Getpid(),
- "stats": getStats(),
- },
- }
- }
- func bytesToKB(bytes uint64) float64 {
- return float64(bytes) / 1024.0
- }
- func getStats() *hash {
- var m, l *hash
- if stat, err := mem.VirtualMemory(); err == nil {
- m = &hash{
- "total": bytesToKB(stat.Total),
- "free": bytesToKB(stat.Free),
- "buffers": bytesToKB(stat.Buffers),
- "cached": bytesToKB(stat.Cached),
- "free_total": bytesToKB(stat.Free + stat.Buffers + stat.Cached),
- }
- }
- if stat, err := load.Avg(); err == nil {
- l = &hash{
- "one": stat.Load1,
- "five": stat.Load5,
- "fifteen": stat.Load15,
- }
- }
- return &hash{"mem": m, "load": l}
- }
- func (n *Notice) toJSON() []byte {
- out, err := json.Marshal(n.asJSON())
- if err == nil {
- return out
- }
- panic(err)
- }
- func (n *Notice) setContext(context Context) {
- n.Context.Update(context)
- }
- func composeStack(stack []*Frame, root string) (frames []*Frame) {
- if root == "" {
- return stack
- }
- re, err := regexp.Compile("^" + regexp.QuoteMeta(root))
- if err != nil {
- return stack
- }
- for _, frame := range stack {
- file := re.ReplaceAllString(frame.File, "[PROJECT_ROOT]")
- frames = append(frames, &Frame{
- File: file,
- Number: frame.Number,
- Method: frame.Method,
- })
- }
- return
- }
- func newNotice(config *Configuration, err Error, extra ...interface{}) *Notice {
- notice := Notice{
- APIKey: config.APIKey,
- Error: err,
- Token: uuid.NewRandom().String(),
- ErrorMessage: err.Message,
- ErrorClass: err.Class,
- Env: config.Env,
- Hostname: config.Hostname,
- Backtrace: composeStack(err.Stack, config.Root),
- ProjectRoot: config.Root,
- Context: Context{},
- }
- for _, thing := range extra {
- switch t := thing.(type) {
- case Context:
- notice.setContext(t)
- case ErrorClass:
- notice.ErrorClass = t.Name
- case Tags:
- for _, tag := range t {
- notice.Tags = append(notice.Tags, tag)
- }
- case Fingerprint:
- notice.Fingerprint = t.String()
- case Params:
- notice.Params = t
- case CGIData:
- notice.CGIData = t
- case url.URL:
- notice.URL = t.String()
- }
- }
- return ¬ice
- }
|