A Go service framework library that provides a standardized way to build HTTP services with Sentry integration, structured logging, and graceful shutdown capabilities.
- Standardized Service Architecture: Interface → Constructor → Struct → Method pattern
- Sentry Integration: Built-in error reporting and exception handling
- Graceful Shutdown: Context-aware service lifecycle management
- HTTP Server Utilities: Health checks, metrics, and routing with Gorilla Mux
- Concurrent Execution: Run multiple service components with proper error handling
- Automatic Error Handling: Panic recovery, error logging, and Sentry reporting
- Argument Parsing: Type-safe CLI argument parsing with struct tags
- Testing Support: Counterfeiter mock generation and Ginkgo/Gomega test framework
go get github.com/bborbe/serviceHere's a minimal example to get started:
package main
import (
"context"
"os"
libsentry "github.com/bborbe/sentry"
"github.com/bborbe/service"
)
type application struct {
SentryDSN string `required:"true" arg:"sentry-dsn" env:"SENTRY_DSN"`
SentryProxy string `arg:"sentry-proxy" env:"SENTRY_PROXY"`
}
func (a *application) Run(ctx context.Context, sentryClient libsentry.Client) error {
// Your application logic here
<-ctx.Done()
return ctx.Err()
}
func main() {
app := &application{}
os.Exit(service.Main(context.Background(), app, &app.SentryDSN, &app.SentryProxy))
}Run with:
./myservice --sentry-dsn="https://[email protected]/project"Your application must implement the Application interface:
type Application interface {
Run(ctx context.Context, sentryClient libsentry.Client) error
}The framework handles:
- Argument Parsing: Automatically parses CLI arguments and environment variables from struct tags
- Sentry Setup: Configures error reporting with the provided DSN
- Global Settings: Sets timezone to UTC, configures logging, and GOMAXPROCS
- Graceful Shutdown: Manages context cancellation and signal handling
- Exit Codes: Returns appropriate exit codes (0=success, 1=runtime error, 2=Sentry failure, 3=missing DSN, 4=argument error)
Use service.Run() to execute multiple concurrent functions with automatic error handling:
func (a *application) Run(ctx context.Context, sentryClient libsentry.Client) error {
return service.Run(
ctx,
a.createHTTPServer(),
a.createBackgroundWorker(),
)
}Each function automatically receives:
- Panic Recovery: Automatic panic catching and error conversion
- Error Logging: Structured logging with glog
- Error Filtering: Excludes expected errors (like
context.Canceled) from Sentry - Context Cancellation: Stops all functions when any one finishes or fails
Use struct tags to define CLI arguments and environment variables:
type application struct {
SentryDSN string `required:"true" arg:"sentry-dsn" env:"SENTRY_DSN" usage:"Sentry DSN for error reporting"`
Listen string `required:"true" arg:"listen" env:"LISTEN" usage:"Address to listen on"`
}Supported tags:
required: Whether the argument is mandatoryarg: CLI flag name (e.g.,--listen)env: Environment variable nameusage: Help text descriptiondisplay: How to display value in logs ("length"masks sensitive data)
Complete HTTP service with health checks and metrics:
package main
import (
"context"
"os"
libhttp "github.com/bborbe/http"
"github.com/bborbe/run"
libsentry "github.com/bborbe/sentry"
"github.com/bborbe/service"
"github.com/golang/glog"
"github.com/gorilla/mux"
"github.com/prometheus/client_golang/prometheus/promhttp"
)
func main() {
app := &application{}
os.Exit(service.Main(context.Background(), app, &app.SentryDSN, &app.SentryProxy))
}
type application struct {
SentryDSN string `required:"true" arg:"sentry-dsn" env:"SENTRY_DSN" display:"length"`
SentryProxy string `required:"false" arg:"sentry-proxy" env:"SENTRY_PROXY"`
Listen string `required:"true" arg:"listen" env:"LISTEN" usage:"address to listen to"`
}
func (a *application) Run(ctx context.Context, sentryClient libsentry.Client) error {
return service.Run(
ctx,
a.createBackgroundWorker(),
a.createHTTPServer(),
)
}
func (a *application) createBackgroundWorker() run.Func {
return func(ctx context.Context) error {
// Background task logic
<-ctx.Done()
return ctx.Err()
}
}
func (a *application) createHTTPServer() run.Func {
return func(ctx context.Context) error {
ctx, cancel := context.WithCancel(ctx)
defer cancel()
router := mux.NewRouter()
router.Path("/healthz").Handler(libhttp.NewPrintHandler("OK"))
router.Path("/readiness").Handler(libhttp.NewPrintHandler("OK"))
router.Path("/metrics").Handler(promhttp.Handler())
glog.V(2).Infof("starting http server listen on %s", a.Listen)
return libhttp.NewServer(a.Listen, router).Run(ctx)
}
}Run with:
./myservice --sentry-dsn="https://[email protected]/project" --listen=":8080"The service provides:
- Health check endpoint:
http://localhost:8080/healthz - Readiness endpoint:
http://localhost:8080/readiness - Prometheus metrics:
http://localhost:8080/metrics
For complete API documentation, visit pkg.go.dev/github.com/bborbe/service.
make testTests run with race detection enabled and coverage reporting.
Generate mocks for testing:
make generateThis creates Counterfeiter mocks for all interfaces in the mocks/ directory.
Before committing changes:
make precommitThis runs:
- Dependency verification
- Code formatting (gofmt, goimports-reviser, golines)
- Code generation (mocks)
- Tests with race detection and coverage
- Static analysis (vet, errcheck, lint)
- Security scanning (gosec, trivy, osv-scanner, govulncheck)
- License header verification
Example test using Counterfeiter mocks:
package myapp_test
import (
"context"
"testing"
libsentry "github.com/bborbe/sentry"
"github.com/bborbe/service"
"github.com/bborbe/service/mocks"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)
func TestService(t *testing.T) {
RegisterFailHandler(Fail)
RunSpecs(t, "MyApp Suite")
}
var _ = Describe("Application", func() {
var (
app *application
sentryClient *mocks.SentryClient
)
BeforeEach(func() {
sentryClient = &mocks.SentryClient{}
app = &application{
Listen: ":8080",
}
})
It("should run successfully", func() {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
// Test your application logic
err := app.Run(ctx, sentryClient)
Expect(err).To(BeNil())
})
})The framework returns standard exit codes:
0: Success1: Runtime error2: Sentry setup failure3: Missing Sentry DSN4: Argument parsing failure
Key runtime dependencies:
- github.com/bborbe/argument/v2 - Type-safe argument parsing
- github.com/bborbe/sentry - Sentry error reporting wrapper
- github.com/bborbe/run - Concurrent function execution
- github.com/bborbe/http - HTTP server utilities
- github.com/gorilla/mux - HTTP routing
- github.com/onsi/ginkgo/v2 - BDD testing framework
- github.com/onsi/gomega - Matcher library
Copyright (c) 2024-2025 Benjamin Borbe
This project is licensed under the BSD-2-Clause License - see the LICENSE file for details.