Thanks to visit codestin.com
Credit goes to pkg.go.dev

service

package module
v1.9.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Dec 5, 2025 License: BSD-2-Clause Imports: 12 Imported by: 30

README

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.

Documentation

Overview

Package service provides a standardized framework for building Go services with Sentry integration, structured logging, graceful shutdown, and signal handling.

The framework handles cross-cutting concerns including:

  • Command-line argument parsing via github.com/bborbe/argument
  • Sentry error reporting with configurable exclusions
  • Concurrent function execution with proper error handling
  • Signal-based context cancellation
  • Panic recovery and logging

Example usage:

type app struct {
	SentryDSN string `required:"true" arg:"sentry-dsn" env:"SENTRY_DSN"`
}

func (a *app) Run(ctx context.Context, sentryClient libsentry.Client) error {
	return service.Run(ctx, a.createServer(), a.createWorker())
}

func main() {
	app := &app{}
	os.Exit(service.Main(context.Background(), app, &app.SentryDSN, nil))
}

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func FilterErrors

func FilterErrors(fn run.Func, filteredErrors ...error) run.Func

FilterErrors wraps a run.Func to suppress specified errors, returning nil instead of the error if it matches any of the provided filteredErrors using errors.Is. This is useful for filtering out expected errors like context.Canceled during graceful shutdown.

func Main

func Main(
	ctx context.Context,
	app Application,
	sentryDSN *string,
	sentryProxy *string,
	fns ...OptionsFn,
) int

Main initializes and runs the service application with Sentry integration. It handles argument parsing, timezone configuration (UTC), Sentry setup, signal handling, and graceful shutdown. Returns an exit code: 0 for success, 1 for runtime error, 2 for Sentry setup failure, 3 for missing Sentry DSN, 4 for argument parsing failure.

func MainCmd

func MainCmd(
	ctx context.Context,
	app run.Runnable,
) int

MainCmd initializes and runs a command-line application without Sentry integration. Unlike Main, this function is designed for CLI tools that do not require error reporting to Sentry and uses reduced logging verbosity (V(3) instead of V(0)). Returns an exit code: 0 for success, 1 for runtime error, 4 for argument parsing failure.

func Run

func Run(ctx context.Context, funcs ...run.Func) error

Run executes multiple functions concurrently with automatic error logging, panic recovery, and context.Canceled error filtering. Each function is wrapped with logging, panic recovery, and error filtering middleware. Returns on first function completion using the CancelOnFirstFinishWait strategy, which cancels all other functions when any one completes.

Types

type Application

type Application interface {
	Run(ctx context.Context, sentryClient libsentry.Client) error
}

Application defines the contract for services that can be executed with Sentry integration. Implementations receive a configured Sentry client for error reporting and should implement the Run method to contain the application's business logic.

type Options

type Options struct {
	ExcludeErrors sentry.ExcludeErrors
}

Options configures behavior for service execution, particularly Sentry error reporting. It allows customization of which errors should be excluded from Sentry reports.

func NewOptions

func NewOptions(fns ...OptionsFn) Options

NewOptions creates a new Options instance with sensible defaults and applies the given functional options. By default, context.Canceled and context.DeadlineExceeded errors are excluded from Sentry reporting as they represent expected shutdown conditions.

type OptionsFn

type OptionsFn func(option *Options)

OptionsFn is a functional option pattern function for configuring Options. It allows callers to customize service behavior by modifying the Options struct.

type Service

type Service interface {
	Run(ctx context.Context) error
}

Service wraps application execution with Sentry error reporting and structured logging. It provides a layer between the main entry point and the application logic, handling error capture and reporting to Sentry automatically.

func NewService

func NewService(
	sentryClient libsentry.Client,
	app Application,
) Service

NewService creates a new Service instance that wraps the given application with Sentry integration. The sentryClient will be used for error reporting, and the app will receive it for use in business logic.

Directories

Path Synopsis
Code generated by counterfeiter.
Code generated by counterfeiter.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL