{"id":29480960,"url":"https://github.com/atomicgo/service","last_synced_at":"2025-07-14T23:55:11.965Z","repository":{"id":304229338,"uuid":"1018157801","full_name":"atomicgo/service","owner":"atomicgo","description":"📦 Template repository for AtomicGo repositories","archived":false,"fork":false,"pushed_at":"2025-07-11T20:15:19.000Z","size":35,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-07-11T20:33:33.378Z","etag":null,"topics":["atomicgo","go","golang","golang-library","hacktoberfest"],"latest_commit_sha":null,"homepage":"https://atomicgo.dev","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/atomicgo.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null},"funding":{"github":["MarvinJWendt"]}},"created_at":"2025-07-11T17:51:24.000Z","updated_at":"2025-07-11T18:34:58.000Z","dependencies_parsed_at":"2025-07-11T20:33:36.236Z","dependency_job_id":"08958fc6-1225-41a0-824c-fc6bcc8976f1","html_url":"https://github.com/atomicgo/service","commit_stats":null,"previous_names":["atomicgo/service"],"tags_count":2,"template":false,"template_full_name":"atomicgo/template","purl":"pkg:github/atomicgo/service","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/atomicgo%2Fservice","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/atomicgo%2Fservice/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/atomicgo%2Fservice/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/atomicgo%2Fservice/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/atomicgo","download_url":"https://codeload.github.com/atomicgo/service/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/atomicgo%2Fservice/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":265375220,"owners_count":23755188,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["atomicgo","go","golang","golang-library","hacktoberfest"],"created_at":"2025-07-14T23:55:10.492Z","updated_at":"2025-07-14T23:55:11.937Z","avatar_url":"https://github.com/atomicgo.png","language":"Go","funding_links":["https://github.com/sponsors/MarvinJWendt"],"categories":[],"sub_categories":[],"readme":"\u003ch1 align=\"center\"\u003eAtomicGo | service\u003c/h1\u003e\n\n\u003cp align=\"center\"\u003e\n\u003cimg src=\"https://img.shields.io/endpoint?url=https%3A%2F%2Fatomicgo.dev%2Fapi%2Fshields%2Fservice\u0026style=flat-square\" alt=\"Downloads\"\u003e\n\n\u003ca href=\"https://github.com/atomicgo/service/releases\"\u003e\n\u003cimg src=\"https://img.shields.io/github/v/release/atomicgo/service?style=flat-square\" alt=\"Latest Release\"\u003e\n\u003c/a\u003e\n\n\u003ca href=\"https://codecov.io/gh/atomicgo/service\" target=\"_blank\"\u003e\n\u003cimg src=\"https://img.shields.io/github/actions/workflow/status/atomicgo/service/go.yml?style=flat-square\" alt=\"Tests\"\u003e\n\u003c/a\u003e\n\n\u003ca href=\"https://codecov.io/gh/atomicgo/service\" target=\"_blank\"\u003e\n\u003cimg src=\"https://img.shields.io/codecov/c/gh/atomicgo/service?color=magenta\u0026logo=codecov\u0026style=flat-square\" alt=\"Coverage\"\u003e\n\u003c/a\u003e\n\n\u003ca href=\"https://codecov.io/gh/atomicgo/service\"\u003e\n\u003c!-- unittestcount:start --\u003e\u003cimg src=\"https://img.shields.io/badge/Unit_Tests-72-magenta?style=flat-square\" alt=\"Unit test count\"\u003e\u003c!-- unittestcount:end --\u003e\n\u003c/a\u003e\n\n\u003ca href=\"https://opensource.org/licenses/MIT\" target=\"_blank\"\u003e\n\u003cimg src=\"https://img.shields.io/badge/License-MIT-yellow.svg?style=flat-square\" alt=\"License: MIT\"\u003e\n\u003c/a\u003e\n  \n\u003ca href=\"https://goreportcard.com/report/github.com/atomicgo/service\" target=\"_blank\"\u003e\n\u003cimg src=\"https://goreportcard.com/badge/github.com/atomicgo/service?style=flat-square\" alt=\"Go report\"\u003e\n\u003c/a\u003e   \n\n\u003c/p\u003e\n\n---\n\n\u003cp align=\"center\"\u003e\n\u003cstrong\u003e\u003ca href=\"https://pkg.go.dev/atomicgo.dev/service#section-documentation\" target=\"_blank\"\u003eDocumentation\u003c/a\u003e\u003c/strong\u003e\n|\n\u003cstrong\u003e\u003ca href=\"https://github.com/atomicgo/atomicgo/blob/main/CONTRIBUTING.md\" target=\"_blank\"\u003eContributing\u003c/a\u003e\u003c/strong\u003e\n|\n\u003cstrong\u003e\u003ca href=\"https://github.com/atomicgo/atomicgo/blob/main/CODE_OF_CONDUCT.md\" target=\"_blank\"\u003eCode of Conduct\u003c/a\u003e\u003c/strong\u003e\n\u003c/p\u003e\n\n---\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"https://raw.githubusercontent.com/atomicgo/atomicgo/main/assets/header.png\" alt=\"AtomicGo\"\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n\u003ctable\u003e\n\u003ctbody\u003e\n\u003c/tbody\u003e\n\u003c/table\u003e\n\u003c/p\u003e\n\u003ch3  align=\"center\"\u003e\u003cpre\u003ego get atomicgo.dev/service\u003c/pre\u003e\u003c/h3\u003e\n\u003cp align=\"center\"\u003e\n\u003ctable\u003e\n\u003ctbody\u003e\n\u003c/tbody\u003e\n\u003c/table\u003e\n\u003c/p\u003e\n\n---\n\nA 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.\n\n**What this library provides:**\n- Essential production features out of the box (metrics, health checks, graceful shutdown)\n- Kubernetes and containerization boilerplate\n- Lightweight wrapper around http.Server for high availability services\n\n**What this library does NOT provide:**\n- HTTP framework or routing\n- Business logic or application patterns\n- Restrictions on how you write your HTTP handlers\n- Opinionated application architecture\n\nWrite 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.\n\n## Features\n\n- **Minimal Boilerplate**: Reduces production service setup to a few lines of code\n- **HTTP Server Wrapper**: Lightweight wrapper around http.Server with production defaults\n- **Metrics**: Built-in Prometheus metrics collection with automatic request tracking\n- **Logging**: Structured logging with slog integration and context-aware loggers\n- **Middleware**: Extensible middleware system with built-in recovery and logging\n- **Configuration**: Environment-based configuration with sensible defaults\n- **Graceful Shutdown**: Signal handling with configurable shutdown hooks\n- **Health Checks**: Built-in health check endpoints for Kubernetes\n- **Framework Agnostic**: Works with any HTTP patterns or frameworks you prefer (as long as the framework supports the standard `http` package)\n\n## Quick Start\n\nMinimal boilerplate to get a production-ready service with metrics, health checks, and graceful shutdown:\n\n```go\npackage main\n\nimport (\n    \"log/slog\"\n    \"net/http\"\n    \"os\"\n\n    \"atomicgo.dev/service\"\n)\n\nfunc main() {\n    // Create service with default configuration\n    svc := service.New(\"my-service\", nil)\n\n    // Write HTTP handlers exactly as you prefer\n    svc.HandleFunc(\"/\", func(w http.ResponseWriter, r *http.Request) {\n        logger := service.GetLogger(r) // Easy access to the logger\n        logger.Info(\"Hello, World!\")\n        w.Write([]byte(\"Hello, World!\"))\n    })\n\n    // Start service (includes graceful shutdown, metrics, health checks)\n    if err := svc.Start(); err != nil {\n        os.Exit(1)\n    }\n}\n```\n\nThat's it! Your service now has:\n- Prometheus metrics at `:9090/metrics`\n- Comprehensive health checks at `:9090/health`\n- Kubernetes readiness probe at `:9090/ready`\n- Kubernetes liveness probe at `:9090/live`\n- Graceful shutdown handling\n- Structured logging\n- Kubernetes-ready configuration\n\n## Configuration\n\nThe framework supports configuration via environment variables with sensible defaults:\n\n| Variable | Default | Description |\n|----------|---------|-------------|\n| `ADDR` | `:8080` | HTTP server address |\n| `METRICS_ADDR` | `:9090` | Metrics server address |\n| `METRICS_PATH` | `/metrics` | Metrics endpoint path |\n| `HEALTH_PATH` | `/health` | Health check endpoint path |\n| `READINESS_PATH` | `/ready` | Readiness probe endpoint path |\n| `LIVENESS_PATH` | `/live` | Liveness probe endpoint path |\n| `SERVICE_VERSION` | `v1.0.0` | Service version for health checks |\n| `READ_TIMEOUT` | `10s` | HTTP read timeout |\n| `WRITE_TIMEOUT` | `10s` | HTTP write timeout |\n| `IDLE_TIMEOUT` | `120s` | HTTP idle timeout |\n| `SHUTDOWN_TIMEOUT` | `30s` | Graceful shutdown timeout |\n\n```go\n// Load configuration from environment\nconfig, err := service.LoadFromEnv()\nif err != nil {\n    log.Fatal(err)\n}\n\n// Create service with custom configuration\nsvc := service.New(\"my-service\", config)\n```\n\n## Middleware\n\nThe framework includes several built-in middleware:\n\n- **LoggerMiddleware**: Injects logger into request context\n- **RecoveryMiddleware**: Recovers from panics and logs errors\n- **RequestLoggingMiddleware**: Logs incoming requests\n- **MetricsMiddleware**: Tracks HTTP metrics for Prometheus\n\n```go\n// Add custom middleware\nsvc.Use(func(next http.Handler) http.Handler {\n    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n        w.Header().Set(\"X-Custom\", \"value\")\n        next.ServeHTTP(w, r)\n    })\n})\n```\n\n## Metrics\n\nThe framework provides a flexible metrics system with built-in HTTP metrics and support for custom metrics.\n\n### Built-in HTTP Metrics\n\nThe framework automatically collects these Prometheus metrics for every service:\n\n- `{service_name}_http_requests_total`: Total HTTP requests by method, endpoint, and status\n- `{service_name}_http_request_duration_seconds`: Request duration histogram by method, endpoint, and status\n- `{service_name}_http_requests_in_flight`: Current number of in-flight requests\n\nThese metrics are provided automatically without any configuration required.\n\n### Custom Metrics\n\nYou can easily register and use custom metrics in your service:\n\n```go\nfunc main() {\n    svc := service.New(\"my-service\", nil)\n    \n    // Register custom metrics\n    svc.RegisterCounter(service.MetricConfig{\n        Name:   \"user_registrations_total\",\n        Help:   \"Total number of user registrations\",\n        Labels: []string{\"source\", \"status\"},\n    })\n    \n    svc.RegisterGauge(service.MetricConfig{\n        Name:   \"active_users\",\n        Help:   \"Number of currently active users\",\n        Labels: []string{\"user_type\"},\n    })\n    \n    svc.RegisterHistogram(service.MetricConfig{\n        Name:   \"request_processing_duration_seconds\",\n        Help:   \"Time spent processing requests\",\n        Labels: []string{\"operation\"},\n        Buckets: []float64{0.001, 0.01, 0.1, 1.0, 10.0},\n    })\n    \n    svc.RegisterSummary(service.MetricConfig{\n        Name:   \"response_size_bytes\",\n        Help:   \"Size of responses in bytes\",\n        Labels: []string{\"endpoint\"},\n        Objectives: map[float64]float64{0.5: 0.05, 0.9: 0.01, 0.99: 0.001},\n    })\n    \n    // Use metrics in handlers\n    svc.HandleFunc(\"/register\", func(w http.ResponseWriter, r *http.Request) {\n        // Increment counter\n        service.IncCounter(r, \"user_registrations_total\", \"web\", \"success\")\n        \n        // Set gauge value\n        service.SetGauge(r, \"active_users\", 42.0, \"premium\")\n        \n        // Observe histogram\n        service.ObserveHistogram(r, \"request_processing_duration_seconds\", 0.25, \"registration\")\n        \n        // Observe summary\n        service.ObserveSummary(r, \"response_size_bytes\", 1024.0, \"/register\")\n        \n        w.Write([]byte(\"User registered\"))\n    })\n    \n    svc.Start()\n}\n```\n\n### Metric Types\n\nThe framework supports all standard Prometheus metric types:\n\n- **Counter**: Monotonically increasing values (e.g., total requests, errors)\n- **Gauge**: Values that can go up and down (e.g., active connections, memory usage)\n- **Histogram**: Observations in configurable buckets (e.g., request duration, response size)\n- **Summary**: Observations with configurable quantiles (e.g., request latency percentiles)\n\n### Direct Access\n\nFor advanced use cases, you can access the metrics collector directly:\n\n```go\nfunc myHandler(w http.ResponseWriter, r *http.Request) {\n    metrics := service.GetMetrics(r)\n    if metrics != nil {\n        // Direct access to metrics collector\n        metrics.IncCounter(\"my_counter\", \"label_value\")\n        metrics.SetGauge(\"my_gauge\", 42.0, \"label_value\")\n        \n        // Access the underlying Prometheus registry\n        registry := metrics.GetRegistry()\n        // Use registry for custom integrations\n    }\n}\n```\n\nAll metrics are available at `:9090/metrics` by default.\n\n## Graceful Shutdown\n\nThe framework includes graceful shutdown by default with signal handling and custom hooks:\n\n```go\n// Add shutdown hooks\nsvc.AddShutdownHook(func() error {\n    // Cleanup resources\n    return nil\n})\n\n// Start service (includes graceful shutdown)\nsvc.Start()\n```\n\n## Logging\n\nThe framework uses structured logging with slog and provides context-aware loggers:\n\n```go\nfunc myHandler(w http.ResponseWriter, r *http.Request) {\n    logger := service.GetLogger(r)\n    logger.Info(\"request processed\", \"path\", r.URL.Path)\n}\n```\n\n## Health Checks\n\nThe framework integrates with [HelloFresh's health-go library](https://github.com/hellofresh/health-go) to provide comprehensive health checking capabilities.\n\n### Built-in Health Endpoints\n\nHealth check endpoints are automatically available on the metrics server:\n\n- `:9090/health`: Comprehensive health check with detailed status information\n- `:9090/ready`: Kubernetes readiness probe endpoint\n- `:9090/live`: Kubernetes liveness probe endpoint\n- `:9090/metrics`: Prometheus metrics\n\n### Adding Custom Health Checks\n\nYou can register custom health checks using the `RegisterHealthCheck` method:\n\n```go\nfunc main() {\n    svc := service.New(\"my-service\", nil)\n\n    // Register a database health check\n    svc.RegisterHealthCheck(health.Config{\n        Name:      \"database\",\n        Timeout:   time.Second * 5,\n        SkipOnErr: false, // This check is critical\n        Check: func(ctx context.Context) error {\n            // Your database health check logic here\n            return db.PingContext(ctx)\n        },\n    })\n\n    // Register a Redis health check\n    svc.RegisterHealthCheck(health.Config{\n        Name:      \"redis\",\n        Timeout:   time.Second * 3,\n        SkipOnErr: true, // This check is optional\n        Check: func(ctx context.Context) error {\n            // Your Redis health check logic here\n            return redisClient.Ping(ctx).Err()\n        },\n    })\n\n    svc.Start()\n}\n```\n\n### Using Built-in Health Checkers\n\nThe health-go library provides several built-in health checkers for common services:\n\n```go\nimport (\n    \"github.com/hellofresh/health-go/v5\"\n    healthPostgres \"github.com/hellofresh/health-go/v5/checks/postgres\"\n    healthRedis \"github.com/hellofresh/health-go/v5/checks/redis\"\n)\n\nfunc main() {\n    svc := service.New(\"my-service\", nil)\n\n    // PostgreSQL health check\n    svc.RegisterHealthCheck(health.Config{\n        Name:      \"postgresql\",\n        Timeout:   time.Second * 5,\n        SkipOnErr: false, // Critical check - service is unhealthy if DB is down\n        Check: healthPostgres.New(healthPostgres.Config{\n            DSN: dbURL,\n        }),\n    })\n\n    // Redis health check\n    svc.RegisterHealthCheck(health.Config{\n        Name:    \"redis\",\n        Timeout: time.Second * 3,\n        Check: healthRedis.New(healthRedis.Config{\n            Addr: \"localhost:6379\",\n        }),\n    })\n\n    svc.Start()\n}\n```\n\n### Health Check Configuration\n\nYou can configure health check endpoints using environment variables:\n\n| Variable | Default | Description |\n|----------|---------|-------------|\n| `HEALTH_PATH` | `/health` | Main health check endpoint path |\n| `READINESS_PATH` | `/ready` | Kubernetes readiness probe path |\n| `LIVENESS_PATH` | `/live` | Kubernetes liveness probe path |\n\n### Accessing Health Checker in Handlers\n\nYou can access the health checker in your HTTP handlers:\n\n```go\nfunc myHandler(w http.ResponseWriter, r *http.Request) {\n    healthChecker := service.GetHealthChecker(r)\n    if healthChecker != nil {\n        check := healthChecker.Measure(r.Context())\n        // Use status information\n        w.Write([]byte(fmt.Sprintf(\"Health checks: %+v\", check)))\n    }\n}\n```\n\n## Kubernetes Deployment\n\nThe library provides all the boilerplate needed for Kubernetes deployments:\n\n- Graceful shutdown handling SIGTERM\n- Health check endpoints for liveness/readiness probes\n- Prometheus metrics for monitoring\n- Configurable resource limits via environment variables\n- No additional Kubernetes-specific code required\n\n## Examples\n\nSee the `_examples/` directory for complete working examples demonstrating:\n\n- **minimal/**: Basic service setup with default configuration\n- **custom-metrics/**: Comprehensive custom metrics registration and usage\n- **prometheus-counter/**: Simple custom counter example (HTTP metrics are automatic)\n- **health-check-***: Various health check integrations\n- **shutdown-hook/**: Graceful shutdown with custom cleanup\n\n## Best Practices\n\n1. **Minimal setup** - Start with default configuration and customize only what you need\n2. **Write HTTP handlers naturally** - Use any patterns or frameworks you prefer\n3. **Add custom shutdown hooks** for resource cleanup when needed\n4. **Use structured logging** for better observability\n5. **Monitor metrics** in production environments\n\n## Contributing\n\nWe welcome contributions! Please see our [Contributing Guide](https://github.com/atomicgo/atomicgo/blob/main/CONTRIBUTING.md) for details.\n\n## License\n\nThis project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.\n\n---\n\n\u003e [AtomicGo.dev](https://atomicgo.dev) \u0026nbsp;\u0026middot;\u0026nbsp;\n\u003e with ❤️ by [@MarvinJWendt](https://github.com/MarvinJWendt) |\n\u003e [MarvinJWendt.com](https://marvinjwendt.com)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fatomicgo%2Fservice","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fatomicgo%2Fservice","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fatomicgo%2Fservice/lists"}