Thanks to visit codestin.com
Credit goes to github.com

Skip to content

bborbe/service

Repository files navigation

Service

Go Reference CI Go Report Card

A Go service framework library that provides a standardized way to build HTTP services with Sentry integration, structured logging, and graceful shutdown capabilities.

Features

  • 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

Installation

go get github.com/bborbe/service

Quick Start

Here'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"

Core Concepts

Application Interface

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)

Running Multiple Components

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

Argument Parsing

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 mandatory
  • arg: CLI flag name (e.g., --listen)
  • env: Environment variable name
  • usage: Help text description
  • display: How to display value in logs ("length" masks sensitive data)

Full Example

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

API Documentation

For complete API documentation, visit pkg.go.dev/github.com/bborbe/service.

Development

Running Tests

make test

Tests run with race detection enabled and coverage reporting.

Code Generation

Generate mocks for testing:

make generate

This creates Counterfeiter mocks for all interfaces in the mocks/ directory.

Full Development Workflow

Before committing changes:

make precommit

This 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

Testing Your Service

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())
    })
})

Exit Codes

The framework returns standard exit codes:

  • 0: Success
  • 1: Runtime error
  • 2: Sentry setup failure
  • 3: Missing Sentry DSN
  • 4: Argument parsing failure

Dependencies

Key runtime dependencies:

License

Copyright (c) 2024-2025 Benjamin Borbe

This project is licensed under the BSD-2-Clause License - see the LICENSE file for details.

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Packages

No packages published