Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
102 changes: 102 additions & 0 deletions examples/grpc/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
# gRPC Airbrake Example

This example demonstrates how to integrate Airbrake error tracking and performance monitoring with a gRPC server using the `grpc` package from gobrake.

## Features

- **Error Tracking**: Automatically captures and reports gRPC errors to Airbrake
- **Performance Monitoring**: Tracks request timing and status codes for APM
- **Unary Interceptor**: Monitors standard request-response RPCs
- **Stream Interceptor**: Monitors streaming RPCs

## Usage

### 1. Initialize the Airbrake Notifier

```go
notifier := gobrake.NewNotifierWithOptions(&gobrake.NotifierOptions{
ProjectId: 123456,
ProjectKey: "your-project-key",
Environment: "production",
})
defer notifier.Close()
```

### 2. Add Interceptors to Your gRPC Server

```go
import grpcbrake "github.com/airbrake/gobrake/v5/grpc"

server := grpc.NewServer(
grpc.ChainUnaryInterceptor(
grpcbrake.UnaryServerInterceptor(notifier),
),
grpc.ChainStreamInterceptor(
grpcbrake.StreamServerInterceptor(notifier),
),
)
```

### 3. Register Your Services and Start the Server

```go
pb.RegisterYourServiceServer(server, &yourServiceImpl{})

listener, err := net.Listen("tcp", ":50051")
if err != nil {
log.Fatalf("failed to listen: %v", err)
}

if err := server.Serve(listener); err != nil {
log.Fatalf("failed to serve: %v", err)
}
```

## What Gets Tracked

### Errors
- All gRPC errors are automatically sent to Airbrake
- Error context includes the full method path (e.g., `/package.Service/Method`)
- gRPC status codes are converted to HTTP equivalents for consistency

### Performance Metrics
- Request duration
- Method path
- Status code (converted from gRPC to HTTP status codes)
- All requests are tracked, including successful ones

## gRPC to HTTP Status Code Mapping

The interceptor maps gRPC status codes to HTTP status codes for APM:

- `OK` → 200
- `Canceled` → 499
- `InvalidArgument` → 400
- `DeadlineExceeded` → 504
- `NotFound` → 404
- `AlreadyExists` → 409
- `PermissionDenied` → 403
- `ResourceExhausted` → 429
- `Unauthenticated` → 401
- `Unimplemented` → 501
- `Internal` → 500
- `Unavailable` → 503
- And more...

## Running the Example

1. Replace the placeholder project ID and key with your actual Airbrake credentials
2. Generate your gRPC service from proto files
3. Register your service implementation
4. Run the server:

```bash
go run main.go
```

## Notes

- The interceptors work with any gRPC service definition
- Both unary and streaming RPCs are supported
- Errors are sent asynchronously to avoid blocking your service
- Performance metrics are collected for APM (Application Performance Monitoring)
84 changes: 84 additions & 0 deletions examples/grpc/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
package main

import (
"context"
"fmt"
"log"
"net"

"github.com/airbrake/gobrake/v5"
grpcbrake "github.com/airbrake/gobrake/v5/grpc"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)

// Example proto definitions
// In a real application, these would be generated from .proto files

type HelloRequest struct {
Name string
}

type HelloResponse struct {
Message string
}

type GreeterServer interface {
SayHello(context.Context, *HelloRequest) (*HelloResponse, error)
}

// greeterServer implements the GreeterServer interface
type greeterServer struct{}

func (s *greeterServer) SayHello(ctx context.Context, req *HelloRequest) (*HelloResponse, error) {
if req.Name == "" {
return nil, status.Error(codes.InvalidArgument, "name cannot be empty")
}

// Simulate an error for demonstration
if req.Name == "error" {
return nil, status.Error(codes.Internal, "simulated internal error")
}

return &HelloResponse{
Message: fmt.Sprintf("Hello, %s!", req.Name),
}, nil
}

func main() {
// Initialize Airbrake notifier
notifier := gobrake.NewNotifierWithOptions(&gobrake.NotifierOptions{
ProjectId: 123456, // Replace with your project ID
ProjectKey: "FIXME", // Replace with your project key
Environment: "development",
})
defer notifier.Close()

// Create a gRPC server with Airbrake interceptors
server := grpc.NewServer(
grpc.ChainUnaryInterceptor(
grpcbrake.UnaryServerInterceptor(notifier),
),
grpc.ChainStreamInterceptor(
grpcbrake.StreamServerInterceptor(notifier),
),
)

// Register your service
// In a real application, you would use generated RegisterXXXServer functions
// RegisterGreeterServer(server, &greeterServer{})

// Start the server
listener, err := net.Listen("tcp", ":50051")
if err != nil {
log.Fatalf("failed to listen: %v", err)
}

log.Println("gRPC server listening on :50051")
log.Println("Airbrake integration enabled")

if err := server.Serve(listener); err != nil {
log.Fatalf("failed to serve: %v", err)
}
}
28 changes: 15 additions & 13 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module github.com/airbrake/gobrake/v5

go 1.17
go 1.24.0

require (
github.com/apex/log v1.9.0
Expand All @@ -21,6 +21,7 @@ require (
github.com/urfave/negroni v1.0.0
github.com/valyala/fasthttp v1.43.0
go.uber.org/zap v1.24.0
google.golang.org/grpc v1.77.0
)

require (
Expand All @@ -33,7 +34,7 @@ require (
github.com/andybalholm/brotli v1.0.4 // indirect
github.com/aymerick/douceur v0.2.0 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/cespare/xxhash/v2 v2.1.2 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/dustin/go-humanize v1.0.0 // indirect
github.com/eknkc/amber v0.0.0-20171010120322-cdade1c07385 // indirect
github.com/fatih/color v1.13.0 // indirect
Expand All @@ -42,7 +43,7 @@ require (
github.com/flosch/pongo2/v4 v4.0.2 // indirect
github.com/fsnotify/fsnotify v1.5.4 // indirect
github.com/gin-contrib/sse v0.1.0 // indirect
github.com/go-logr/logr v1.2.3 // indirect
github.com/go-logr/logr v1.4.3 // indirect
github.com/go-playground/locales v0.14.0 // indirect
github.com/go-playground/universal-translator v0.18.0 // indirect
github.com/go-playground/validator/v10 v10.11.1 // indirect
Expand All @@ -64,10 +65,10 @@ require (
github.com/gobuffalo/validate/v3 v3.3.3 // indirect
github.com/goccy/go-json v0.9.11 // indirect
github.com/gofrs/uuid v4.3.0+incompatible // indirect
github.com/golang/protobuf v1.5.2 // indirect
github.com/golang/protobuf v1.5.4 // indirect
github.com/golang/snappy v0.0.4 // indirect
github.com/google/go-cmp v0.5.9 // indirect
github.com/google/uuid v1.3.0 // indirect
github.com/google/go-cmp v0.7.0 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/gorilla/css v1.0.0 // indirect
github.com/gorilla/handlers v1.5.1 // indirect
github.com/gorilla/securecookie v1.1.1 // indirect
Expand Down Expand Up @@ -137,14 +138,15 @@ require (
github.com/yosssi/ace v0.0.5 // indirect
go.uber.org/atomic v1.9.0 // indirect
go.uber.org/multierr v1.7.0 // indirect
golang.org/x/crypto v0.4.0 // indirect
golang.org/x/net v0.4.0 // indirect
golang.org/x/sync v0.0.0-20220929204114-8fcdb60fdcc0 // indirect
golang.org/x/sys v0.3.0 // indirect
golang.org/x/term v0.3.0 // indirect
golang.org/x/text v0.5.0 // indirect
golang.org/x/crypto v0.43.0 // indirect
golang.org/x/net v0.46.1-0.20251013234738-63d1a5100f82 // indirect
golang.org/x/sync v0.17.0 // indirect
golang.org/x/sys v0.37.0 // indirect
golang.org/x/term v0.36.0 // indirect
golang.org/x/text v0.30.0 // indirect
golang.org/x/time v0.3.0 // indirect
google.golang.org/protobuf v1.28.1 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20251124214823-79d6a2a48846 // indirect
google.golang.org/protobuf v1.36.10 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
Expand Down
Loading