DEV Community

Bryan Sazon
Bryan Sazon

Posted on

Go: Error handling and tracing with OpenCensus supported platforms

GoDoc

errors

I want to share my drop-in replacement for github.com/pkg/errors.

I created this package to help my current team view the details of my Stackdriver traces with Stackdriver logs.

Goals

Add OpenCensus trace attributes when creating a new error.

Basic Usage

Drop-in replacement. Creating an error.

err := errors.New("msg") // Wrap .. Wrapf .. Errof ..

Creating an error with context useful for monitoring.

func main() {
    _, span := trace.StartSpan(context.Background(), "ExampleNewT")
    defer span.End()

    err := errors.NewT(span, "error")
    fmt.Println(err)

    if erctx, ok := err.(errors.Error); ok {
        fmt.Println(erctx.SourceLocation().Function)
        fmt.Println(erctx.SourceLocation().File)
        fmt.Println(erctx.SourceLocation().Line)
        fmt.Println(erctx.TraceContext().TraceID)
        fmt.Println(erctx.TraceContext().SpanID)
    }
}

Output

main.main
/Users/jb/Golang/src/github.com/bzon/errors/examples/main.go
15
d2d2b126d0474947821106525e32b6e0
1644b4b9be26c929

How I use it

package main

import (
    "context"
    "flag"
    "fmt"
    "os"
    "time"

    "contrib.go.opencensus.io/exporter/jaeger"
    "contrib.go.opencensus.io/exporter/stackdriver"
    "github.com/bzon/errors"
    "github.com/go-kit/kit/log"
    "go.opencensus.io/trace"
)

func main() {
    var useStackDriver = flag.Bool("stack-driver", false, "Use stackdriver instead of jaeger.")
    flag.Parse()

    // logging
    var logger log.Logger
    {
        logger = log.NewJSONLogger(log.NewSyncWriter(os.Stdout))
        logger = log.With(logger, "ts", log.DefaultTimestamp, "caller", log.DefaultCaller)
    }

    // tracing
    switch {
    case *useStackDriver:
        // stackdriver
        sd, err := stackdriver.NewExporter(stackdriver.Options{
            ProjectID: os.Getenv("GCP_PROJECT"),
        })
        if err != nil {
            panic(fmt.Sprintf("Failed to create the stackdriver exporter: %v", err))
        }
        defer sd.Flush()
        trace.RegisterExporter(sd)
    default:
        je, err := jaeger.NewExporter(jaeger.Options{
            AgentEndpoint:     "localhost:6831",
            CollectorEndpoint: "http://localhost:14268/api/traces",
            ServiceName:       "erroring-service",
        })
        if err != nil {
            panic(fmt.Sprintf("Failed to create the Jaeger exporter: %v", err))
        }
        trace.RegisterExporter(je)
    }

    trace.ApplyConfig(trace.Config{DefaultSampler: trace.AlwaysSample()})

    for {
        workerr := work(context.Background(), logger)
        if ec, ok := workerr.(errors.Error); ok {
            logger.Log(
                "message", ec.Error(),
                "logging.googleapis.com/spanId", ec.TraceContext().SpanID,
                "logging.googleapis.com/trace", ec.TraceContext().TraceID,
                "logging.googleapis.com/sourceLocation", ec.SourceLocation(),
            )
        }
        time.Sleep(3 * time.Second)
    }

}

func work(ctx context.Context, logger log.Logger) error {
    _, span := trace.StartSpan(ctx, "work")
    defer span.End()

    // error with context
    err := errors.NewT(span, "error")
    return err
}

You can also see the source code of the example server in github.

Useful for logging error with tracing context.

$ go run main.go | jq '.'

{
  "caller": "main.go:38",
  "logging.googleapis.com/sourceLocation": {
    "function": "main.work",
    "file": "/Users/jb/Golang/src/github.com/bzon/errors/examples/main.go",
    "line": 55
  },
  "logging.googleapis.com/spanId": "1eb9494ab1a7831c",
  "logging.googleapis.com/trace": "e97499da53ef2a8fdf9681beddbe3d64",
  "message": "error",
  "ts": "2019-07-15T09:37:06.885078+02:00"
}

Automatic annotations will be applied in supported tracing platform for OpenCensus.

img

Top comments (0)