Skip to content
generated from atomicgo/template

πŸ“¦ Template repository for AtomicGo repositories

License

Notifications You must be signed in to change notification settings

atomicgo/service

Repository files navigation

AtomicGo | service

Downloads Latest Release Tests Coverage Unit test count License: MIT Go report


Documentation | Contributing | Code of Conduct


AtomicGo

go get atomicgo.dev/service


A minimal boilerplate wrapper for building production-ready Go HTTP services. This library reduces the boilerplate of writing production/enterprise-grade Go services to a minimum.

What this library provides:

  • Essential production features out of the box (metrics, health checks, graceful shutdown)
  • Kubernetes and containerization boilerplate
  • Lightweight wrapper around http.Server for high availability services

What this library does NOT provide:

  • HTTP framework or routing
  • Business logic or application patterns
  • Restrictions on how you write your HTTP handlers
  • Opinionated application architecture

Write HTTP handlers exactly as you prefer, using any patterns or frameworks you choose. This library handles the operational concerns while staying out of your application logic.

Features

  • Minimal Boilerplate: Reduces production service setup to a few lines of code
  • HTTP Server Wrapper: Lightweight wrapper around http.Server with production defaults
  • Metrics: Built-in Prometheus metrics collection with automatic request tracking
  • Logging: Structured logging with slog integration and context-aware loggers
  • Middleware: Extensible middleware system with built-in recovery and logging
  • Configuration: Environment-based configuration with sensible defaults
  • Graceful Shutdown: Signal handling with configurable shutdown hooks
  • Health Checks: Built-in health check endpoints for Kubernetes
  • Framework Agnostic: Works with any HTTP patterns or frameworks you prefer (as long as the framework supports the standard http package)

Quick Start

Minimal boilerplate to get a production-ready service with metrics, health checks, and graceful shutdown:

package main

import (
    "log/slog"
    "net/http"
    "os"

    "atomicgo.dev/service"
)

func main() {
    // Create service with default configuration
    svc := service.New("my-service", nil)

    // Write HTTP handlers exactly as you prefer
    svc.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        logger := service.GetLogger(r) // Easy access to the logger
        logger.Info("Hello, World!")
        w.Write([]byte("Hello, World!"))
    })

    // Start service (includes graceful shutdown, metrics, health checks)
    if err := svc.Start(); err != nil {
        os.Exit(1)
    }
}

That's it! Your service now has:

  • Prometheus metrics at :9090/metrics
  • Comprehensive health checks at :9090/health
  • Kubernetes readiness probe at :9090/ready
  • Kubernetes liveness probe at :9090/live
  • Graceful shutdown handling
  • Structured logging
  • Kubernetes-ready configuration

Configuration

The framework supports configuration via environment variables with sensible defaults:

Variable Default Description
ADDR :8080 HTTP server address
METRICS_ADDR :9090 Metrics server address
METRICS_PATH /metrics Metrics endpoint path
HEALTH_PATH /health Health check endpoint path
READINESS_PATH /ready Readiness probe endpoint path
LIVENESS_PATH /live Liveness probe endpoint path
SERVICE_VERSION v1.0.0 Service version for health checks
READ_TIMEOUT 10s HTTP read timeout
WRITE_TIMEOUT 10s HTTP write timeout
IDLE_TIMEOUT 120s HTTP idle timeout
SHUTDOWN_TIMEOUT 30s Graceful shutdown timeout
// Load configuration from environment
config, err := service.LoadFromEnv()
if err != nil {
    log.Fatal(err)
}

// Create service with custom configuration
svc := service.New("my-service", config)

Middleware

The framework includes several built-in middleware:

  • LoggerMiddleware: Injects logger into request context
  • RecoveryMiddleware: Recovers from panics and logs errors
  • RequestLoggingMiddleware: Logs incoming requests
  • MetricsMiddleware: Tracks HTTP metrics for Prometheus
// Add custom middleware
svc.Use(func(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        w.Header().Set("X-Custom", "value")
        next.ServeHTTP(w, r)
    })
})

Metrics

The framework provides a flexible metrics system with built-in HTTP metrics and support for custom metrics.

Built-in HTTP Metrics

The framework automatically collects these Prometheus metrics for every service:

  • {service_name}_http_requests_total: Total HTTP requests by method, endpoint, and status
  • {service_name}_http_request_duration_seconds: Request duration histogram by method, endpoint, and status
  • {service_name}_http_requests_in_flight: Current number of in-flight requests

These metrics are provided automatically without any configuration required.

Custom Metrics

You can easily register and use custom metrics in your service:

func main() {
    svc := service.New("my-service", nil)
    
    // Register custom metrics
    svc.RegisterCounter(service.MetricConfig{
        Name:   "user_registrations_total",
        Help:   "Total number of user registrations",
        Labels: []string{"source", "status"},
    })
    
    svc.RegisterGauge(service.MetricConfig{
        Name:   "active_users",
        Help:   "Number of currently active users",
        Labels: []string{"user_type"},
    })
    
    svc.RegisterHistogram(service.MetricConfig{
        Name:   "request_processing_duration_seconds",
        Help:   "Time spent processing requests",
        Labels: []string{"operation"},
        Buckets: []float64{0.001, 0.01, 0.1, 1.0, 10.0},
    })
    
    svc.RegisterSummary(service.MetricConfig{
        Name:   "response_size_bytes",
        Help:   "Size of responses in bytes",
        Labels: []string{"endpoint"},
        Objectives: map[float64]float64{0.5: 0.05, 0.9: 0.01, 0.99: 0.001},
    })
    
    // Use metrics in handlers
    svc.HandleFunc("/register", func(w http.ResponseWriter, r *http.Request) {
        // Increment counter
        service.IncCounter(r, "user_registrations_total", "web", "success")
        
        // Set gauge value
        service.SetGauge(r, "active_users", 42.0, "premium")
        
        // Observe histogram
        service.ObserveHistogram(r, "request_processing_duration_seconds", 0.25, "registration")
        
        // Observe summary
        service.ObserveSummary(r, "response_size_bytes", 1024.0, "/register")
        
        w.Write([]byte("User registered"))
    })
    
    svc.Start()
}

Metric Types

The framework supports all standard Prometheus metric types:

  • Counter: Monotonically increasing values (e.g., total requests, errors)
  • Gauge: Values that can go up and down (e.g., active connections, memory usage)
  • Histogram: Observations in configurable buckets (e.g., request duration, response size)
  • Summary: Observations with configurable quantiles (e.g., request latency percentiles)

Direct Access

For advanced use cases, you can access the metrics collector directly:

func myHandler(w http.ResponseWriter, r *http.Request) {
    metrics := service.GetMetrics(r)
    if metrics != nil {
        // Direct access to metrics collector
        metrics.IncCounter("my_counter", "label_value")
        metrics.SetGauge("my_gauge", 42.0, "label_value")
        
        // Access the underlying Prometheus registry
        registry := metrics.GetRegistry()
        // Use registry for custom integrations
    }
}

All metrics are available at :9090/metrics by default.

Graceful Shutdown

The framework includes graceful shutdown by default with signal handling and custom hooks:

// Add shutdown hooks
svc.AddShutdownHook(func() error {
    // Cleanup resources
    return nil
})

// Start service (includes graceful shutdown)
svc.Start()

Logging

The framework uses structured logging with slog and provides context-aware loggers:

func myHandler(w http.ResponseWriter, r *http.Request) {
    logger := service.GetLogger(r)
    logger.Info("request processed", "path", r.URL.Path)
}

Health Checks

The framework integrates with HelloFresh's health-go library to provide comprehensive health checking capabilities.

Built-in Health Endpoints

Health check endpoints are automatically available on the metrics server:

  • :9090/health: Comprehensive health check with detailed status information
  • :9090/ready: Kubernetes readiness probe endpoint
  • :9090/live: Kubernetes liveness probe endpoint
  • :9090/metrics: Prometheus metrics

Adding Custom Health Checks

You can register custom health checks using the RegisterHealthCheck method:

func main() {
    svc := service.New("my-service", nil)

    // Register a database health check
    svc.RegisterHealthCheck(health.Config{
        Name:      "database",
        Timeout:   time.Second * 5,
        SkipOnErr: false, // This check is critical
        Check: func(ctx context.Context) error {
            // Your database health check logic here
            return db.PingContext(ctx)
        },
    })

    // Register a Redis health check
    svc.RegisterHealthCheck(health.Config{
        Name:      "redis",
        Timeout:   time.Second * 3,
        SkipOnErr: true, // This check is optional
        Check: func(ctx context.Context) error {
            // Your Redis health check logic here
            return redisClient.Ping(ctx).Err()
        },
    })

    svc.Start()
}

Using Built-in Health Checkers

The health-go library provides several built-in health checkers for common services:

import (
    "github.com/hellofresh/health-go/v5"
    healthPostgres "github.com/hellofresh/health-go/v5/checks/postgres"
    healthRedis "github.com/hellofresh/health-go/v5/checks/redis"
)

func main() {
    svc := service.New("my-service", nil)

    // PostgreSQL health check
    svc.RegisterHealthCheck(health.Config{
        Name:      "postgresql",
        Timeout:   time.Second * 5,
        SkipOnErr: false, // Critical check - service is unhealthy if DB is down
        Check: healthPostgres.New(healthPostgres.Config{
            DSN: dbURL,
        }),
    })

    // Redis health check
    svc.RegisterHealthCheck(health.Config{
        Name:    "redis",
        Timeout: time.Second * 3,
        Check: healthRedis.New(healthRedis.Config{
            Addr: "localhost:6379",
        }),
    })

    svc.Start()
}

Health Check Configuration

You can configure health check endpoints using environment variables:

Variable Default Description
HEALTH_PATH /health Main health check endpoint path
READINESS_PATH /ready Kubernetes readiness probe path
LIVENESS_PATH /live Kubernetes liveness probe path

Accessing Health Checker in Handlers

You can access the health checker in your HTTP handlers:

func myHandler(w http.ResponseWriter, r *http.Request) {
    healthChecker := service.GetHealthChecker(r)
    if healthChecker != nil {
        check := healthChecker.Measure(r.Context())
        // Use status information
        w.Write([]byte(fmt.Sprintf("Health checks: %+v", check)))
    }
}

Kubernetes Deployment

The library provides all the boilerplate needed for Kubernetes deployments:

  • Graceful shutdown handling SIGTERM
  • Health check endpoints for liveness/readiness probes
  • Prometheus metrics for monitoring
  • Configurable resource limits via environment variables
  • No additional Kubernetes-specific code required

Examples

See the _examples/ directory for complete working examples demonstrating:

  • minimal/: Basic service setup with default configuration
  • custom-metrics/: Comprehensive custom metrics registration and usage
  • prometheus-counter/: Simple custom counter example (HTTP metrics are automatic)
  • health-check-*: Various health check integrations
  • shutdown-hook/: Graceful shutdown with custom cleanup

Best Practices

  1. Minimal setup - Start with default configuration and customize only what you need
  2. Write HTTP handlers naturally - Use any patterns or frameworks you prefer
  3. Add custom shutdown hooks for resource cleanup when needed
  4. Use structured logging for better observability
  5. Monitor metrics in production environments

Contributing

We welcome contributions! Please see our Contributing Guide for details.

License

This project is licensed under the MIT License - see the LICENSE file for details.


AtomicGo.dev  ·  with ❀️ by @MarvinJWendt | MarvinJWendt.com

About

πŸ“¦ Template repository for AtomicGo repositories

Topics

Resources

License

Code of conduct

Stars

Watchers

Forks

Sponsor this project

 

Packages

No packages published

Languages