A collection of production-ready Go packages for building robust backend services.
Structured Errors Β· HTTP Client Β· Structured Logging
ADK (Alhamsya Development Kit) is a modular Go SDK that provides essential utilities for building production-ready backend services. Each package is designed as an independent Go module, allowing you to import only what you need.
| Package | Description | Import Path |
|---|---|---|
xerr |
Structured error handling with automatic gRPC & HTTP status code mapping | github.com/alhamsya/adk/xerr |
xhttp |
HTTP client with built-in retry and config-driven upstream management | github.com/alhamsya/adk/xhttp |
zlog |
Structured logging powered by zerolog with diode buffering & context annotations | github.com/alhamsya/adk/zlog |
Each package is an independent Go module. Install only what you need:
# Structured Error Handling
go get github.com/alhamsya/adk/xerr
# HTTP Client with Retry
go get github.com/alhamsya/adk/xhttp
# Structured Logging
go get github.com/alhamsya/adk/zlogRequirements: Go 1.24+
package main
import (
"context"
"fmt"
"log/slog"
"time"
"github.com/alhamsya/adk/xerr"
"github.com/alhamsya/adk/xhttp"
"github.com/alhamsya/adk/zlog"
)
func main() {
// π΄ Structured Errors
err := xerr.New(xerr.TypeNotFound, "user not found")
fmt.Println(err.HTTPStatus()) // 404
fmt.Println(err.GRPCStatus()) // code: NotFound
// π HTTP Client
client := xhttp.NewClient()
client.SetTimeout(10 * time.Second)
res, _ := client.Get(context.Background(), "https://api.example.com/health")
fmt.Println(res.StatusCode)
// π Structured Logging
ctx := zlog.CtxWithAnnotation(context.Background(), zlog.DefaultAnnotation())
zlog.AddAnnotation(ctx, map[string]any{"request_id": "req-123"})
zlog.FromContext(ctx).Info().Msg("service started")
// Also works with standard slog
slog.Info("hello from slog", "user", "alhamsya")
time.Sleep(100 * time.Millisecond) // wait for async log flush
}Package xerr provides structured error handling that automatically maps errors to both gRPC status codes and HTTP status codes. Designed for services that serve multiple protocols (REST + gRPC) simultaneously.
go get github.com/alhamsya/adk/xerr- β Dual-protocol mapping β one error type automatically maps to both gRPC & HTTP codes
- β 17 pre-defined error types β covers common backend service scenarios
- β Pre-built error sentinels β ready to use without re-initialization
- β Functional options pattern β flexible error customization
- β
Error wrapping/unwrapping β fully compatible with
errors.Is()anderrors.As() - β
Stack trace support β integration with
pkg/errors - β
Pointer-safe β
FromErrorwith options never modifies global presets
// Create a new error
err := xerr.New(xerr.TypeNotFound, "user not found")
// Create an error with a wrapped cause
err := xerr.NewWithWrap(xerr.TypeSystemError, originalErr, "database connection failed")
// Use pre-built sentinel errors
if errors.Is(err, xerr.ErrUnauthorized) {
// handle unauthorized
}ADK provides ready-to-use error sentinels:
// Use directly
return xerr.ErrNotFound // 404 / NotFound
return xerr.ErrUnauthorized // 401 / Unauthenticated
return xerr.ErrInvalidParameter // 400 / InvalidArgument
return xerr.ErrSystemError // 500 / Internal
return xerr.ErrServiceBusy // 429 / ResourceExhausted
// Customize from sentinel β SAFE, does not modify the global preset
err := xerr.FromError(xerr.ErrNotFound, xerr.WithMessage("product ID 123 not found"))err := xerr.New(xerr.TypeUnauthorized, "invalid token")
// gRPC
grpcStatus := err.GRPCStatus()
// grpcStatus.Code() == codes.Unauthenticated
// HTTP
httpCode := err.HTTPStatus()
// httpCode == 401// Wrap an external error
dbErr := sql.ErrNoRows
err := xerr.NewWithWrap(xerr.TypeNotFound, dbErr, "user not found")
// Unwrap
cause := errors.Unwrap(err) // sql.ErrNoRows
// Comparison
errors.Is(err, anotherXerr) // compares Code, Message, and Type// Convert from a generic error with options
err := xerr.FromError(someError,
xerr.WithMessage("custom message"), // override message
xerr.WithType(xerr.TypeNotFound), // override type (also updates gRPC code)
xerr.WithStack(), // attach stack trace
xerr.WithMessageType(), // use type name as message if empty
)Complete mapping table between error Type, HTTP Status Code, and gRPC Code:
| Type | Value | HTTP | gRPC Code |
|---|---|---|---|
TypeOK |
OK |
200 OK |
OK (0) |
TypeInvalidParameter |
INVALID_PARAMETER |
400 Bad Request |
InvalidArgument (3) |
TypeUnauthorized |
UNAUTHORIZED |
401 Unauthorized |
Unauthenticated (16) |
TypeForbidden |
FORBIDDEN |
403 Forbidden |
PermissionDenied (7) |
TypeNoTradingPermission |
NO_TRADING_PERMISSION |
403 Forbidden |
PermissionDenied (7) |
TypeNoSubscription |
NO_SUBSCRIPTION |
403 Forbidden |
PermissionDenied (7) |
TypeNotFound |
NOT_FOUND |
404 Not Found |
NotFound (5) |
TypeDuplicateCall |
DUPLICATE_CALL |
409 Conflict |
AlreadyExists (6) |
TypeAlreadyExists |
ALREADY_EXISTS |
409 Conflict |
AlreadyExists (6) |
TypeAborted |
ABORTED |
409 Conflict |
Aborted (10) |
TypeServiceBusy |
SERVICE_BUSY |
429 Too Many Requests |
ResourceExhausted (8) |
TypeRequestCanceled |
REQUEST_CANCELED |
499 Client Closed Request |
Canceled (1) |
TypeSystemError |
SYSTEM_ERROR |
500 Internal Server Error |
Internal (13) |
TypeVendorError |
VENDOR_ERROR |
500 Internal Server Error |
Internal (13) |
TypeUnimplemented |
UNIMPLEMENTED |
501 Not Implemented |
Unimplemented (12) |
TypeBadGateway |
BAD_GATEWAY |
502 Bad Gateway |
Unavailable (14) |
TypeMaintenance |
MAINTENANCE |
503 Service Unavailable |
Unavailable (14) |
TypeGatewayTimeout |
GATEWAY_TIMEOUT |
504 Gateway Timeout |
DeadlineExceeded (4) |
xerr provides interfaces for custom integration:
// Implement these interfaces on your custom errors
type GRPCCode interface {
Code() codes.Code
}
type GRPCErrType interface {
Type() Type
}
type HTTPStatusCode interface {
HTTPStatus() int
}func UnaryServerInterceptor() grpc.UnaryServerInterceptor {
return func(ctx context.Context, req any, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (any, error) {
resp, err := handler(ctx, req)
if err != nil {
xerrErr := xerr.FromError(err)
return nil, xerrErr.GRPCStatus().Err()
}
return resp, nil
}
}func ErrorHandler(w http.ResponseWriter, err error) {
xerrErr := xerr.FromError(err)
w.WriteHeader(xerrErr.HTTPStatus())
json.NewEncoder(w).Encode(map[string]string{
"error": xerrErr.Type.String(),
"message": xerrErr.Message,
})
}Package xhttp provides an HTTP client with built-in retry support and config-driven upstream management. Ideal for service-to-service communication that requires resilience.
go get github.com/alhamsya/adk/xhttp- β
Built-in retry β automatic retries with
retry-go(exponential & fixed backoff) - β Config-driven upstreams β manage multiple upstream services via JSON/YAML configuration
- β JSON auto-serialization β request body is automatically marshaled to JSON
- β Response unmarshaling β easily unmarshal response body into structs
- β Per-upstream timeout β configure timeout per upstream service
- β Extras metadata β store additional metadata per upstream (API keys, partner IDs, etc.)
client := xhttp.NewClient()
client.SetTimeout(10 * time.Second)
// GET request
res, err := client.Get(ctx, "https://api.example.com/users/123")
if err != nil {
log.Fatal(err)
}
fmt.Println(res.StatusCode) // 200
fmt.Println(string(res.Body)) // response body
// Unmarshal response
var user User
err = res.Unmarshal(&user)// POST with body β automatically serialized to JSON
body := map[string]string{"name": "Alhamsya", "role": "engineer"}
res, err := client.Post(ctx, "https://api.example.com/users", body)
// PUT
res, err := client.Put(ctx, "https://api.example.com/users/123", updatedUser)
// DELETE
res, err := client.Delete(ctx, "https://api.example.com/users/123")A more powerful way to manage multiple upstream services:
{
"upstream": {
"payment-service": {
"host": "https://payment.internal.svc",
"timeout": "5s",
"type": "rest",
"global-retry": {
"attempts": 3,
"delay": "100ms",
"type": "exponential"
},
"endpoints": {
"create-payment": { "path": "/api/v1/payments" },
"get-status": { "path": "/api/v1/payments/status" }
},
"extras": {
"api-key": "sk_live_xxx",
"partner-id": "PARTNER001"
}
},
"notification-service": {
"host": "https://notif.internal.svc",
"timeout": "3s",
"endpoints": {
"send-email": { "path": "/api/v1/email/send" },
"send-push": { "path": "/api/v1/push/send" }
}
}
}
}upstream:
payment-service:
host: https://payment.internal.svc
timeout: 5s
type: rest
global-retry:
attempts: 3
delay: 100ms
type: exponential
endpoints:
create-payment:
path: /api/v1/payments
get-status:
path: /api/v1/payments/status
extras:
api-key: sk_live_xxx
partner-id: PARTNER001// Initialize client with config
client := xhttp.NewClient()
client.SetConfig(cfg) // cfg loaded from JSON/YAML
// Make requests using upstream & endpoint names
res, err := client.GetEndpoint(ctx, "payment-service", "get-status")
res, err := client.PostEndpoint(ctx, "payment-service", "create-payment", paymentReq)
res, err := client.PutEndpoint(ctx, "notification-service", "send-email", emailReq)
res, err := client.DeleteEndpoint(ctx, "payment-service", "cancel-payment")| Strategy | Constant | Description |
|---|---|---|
| Exponential Backoff | xhttp.RetryTypeExponential |
Delay increases exponentially between retries |
| Fixed Delay | xhttp.RetryTypeFixed |
Constant delay between retries |
// Main configuration
type Config struct {
Upstream map[string]Upstream
}
type Upstream struct {
Host string // Base URL of the upstream service
Timeout string // Per-request timeout (e.g., "5s")
Type string // Service type (e.g., "rest", "grpc")
GlobalRetry RetryConfig // Global retry configuration
Endpoints map[string]Endpoint // Endpoint definitions
Extras map[string]interface{} // Additional metadata
}
type Endpoint struct {
Path string // Endpoint path (e.g., "/api/v1/users")
}
type RetryConfig struct {
Attempts int // Number of retry attempts
Delay string // Delay between retries (e.g., "100ms")
Timeout string // Timeout per retry attempt
Type string // "exponential" or "fixed"
}
// Response
type Res struct {
Header http.Header
StatusCode int
Body []byte
}Package zlog provides high-performance structured logging built on top of zerolog with log/slog compatibility. It features diode buffering for non-blocking I/O and context-scoped annotations.
go get github.com/alhamsya/adk/zlog- β High-performance β diode-buffered writer for async, non-blocking logging
- β
Dual API β use
zerologdirectly OR the standardlog/sloglibrary - β Context annotations β inject metadata into context, automatically included in every log entry
- β
RFC3339 timestamps β standard timestamp format (field:
timestamp) - β
Stack trace support β automatically logs stack traces for
pkg/errorserrors - β
Environment-based level β set log level via the
ZLOG_LEVELenv variable - β
Thread-safe annotations β uses
sync.Mapfor concurrent access
Simply import zlog to automatically configure log/slog:
package main
import (
"log/slog"
_ "github.com/alhamsya/adk/zlog" // import for side-effects
)
func main() {
slog.Info("Hello World", "user", "alhamsya")
// Output: {"level":"info","timestamp":"2026-03-14T19:00:00+07:00","source":{"function":"..."},"msg":"Hello World","user":"alhamsya"}
}Inject metadata into the context that is automatically included in every log entry:
// 1. Initialize context with annotation support
ctx := context.Background()
ctx = zlog.CtxWithAnnotation(ctx, zlog.DefaultAnnotation())
// 2. Add annotations
zlog.AddAnnotation(ctx, map[string]any{
"user_id": "12345",
"request_id": "req-abc-789",
"service": "payment",
})
// 3. Log β annotations are automatically attached
logger := zlog.FromContext(ctx)
logger.Info().Msg("Payment processed")
// Output:
// {
// "level": "info",
// "annotation": {"user_id": "12345", "request_id": "req-abc-789", "service": "payment"},
// "timestamp": "2026-03-14T19:00:00+07:00",
// "message": "Payment processed"
// }Errors from pkg/errors automatically include stack traces:
import "github.com/pkg/errors"
err := errors.New("database connection timeout")
logger.Error().Err(err).Msg("Failed to query database")
// Output includes a "stack" field with filtered stack framesfunc handler(ctx context.Context) {
// Create a new logger with hooks
ctx, logger := zlog.NewContext(ctx, myCustomHook)
// Update context fields
logger.UpdateContext(func(c zerolog.Context) zerolog.Context {
return c.Str("handler", "payment")
})
// Retrieve logger from context
log := zlog.FromContext(ctx)
log.Info().Msg("Handler executed")
}Set the log level via environment variable:
# Options: debug, info, warn, error
export ZLOG_LEVEL=info| Level | Env Value | Description |
|---|---|---|
| Debug | debug |
All logs are shown (default) |
| Info | info |
Info, warn, and error only |
| Warn | warn |
Warn and error only |
| Error | error |
Error only |
// Initialize context
ctx = zlog.CtxWithAnnotation(ctx, zlog.DefaultAnnotation())
// Add annotations (thread-safe, can be called from different goroutines)
zlog.AddAnnotation(ctx, map[string]any{"key": "value"})
// Retrieve annotation from context
annotation := zlog.AnnotationFromCtx(ctx)
// Marshal to JSON
data, _ := json.Marshal(annotation)package main
import (
"context"
"time"
"github.com/alhamsya/adk/zlog"
"github.com/pkg/errors"
)
func main() {
ctx := context.Background()
ctx = zlog.CtxWithAnnotation(ctx, zlog.DefaultAnnotation())
// Inject metadata
zlog.AddAnnotation(ctx, map[string]any{
"data": "test",
})
zlog.AddAnnotation(ctx, map[string]any{
"data1": "test1",
})
// Info log
zlog.FromContext(ctx).Info().Msg("success send message")
// Error log with stack trace
err := errors.New("something went wrong")
zlog.FromContext(ctx).Error().Err(err).Msg("failed process")
// Wait for async log flush
time.Sleep(100 * time.Millisecond)
}adk/
βββ go.mod # Root module
βββ go.work # Go workspace definition
βββ README.md # This documentation
β
βββ xerr/ # π΄ Structured Error Handling
β βββ go.mod # Module: github.com/alhamsya/adk/xerr
β βββ error.go # Core functions: New, FromError, Wrap, etc.
β βββ enum.go # Error type constants & mappings
β βββ type.go # Type definitions (Error struct)
β βββ iface.go # Interfaces (GRPCCode, GRPCErrType, HTTPStatusCode)
β βββ options.go # Functional options (WithMessage, WithType, etc.)
β βββ stack.go # Stack trace support
β βββ error_test.go # Unit tests
β
βββ xhttp/ # π HTTP Client
β βββ go.mod # Module: github.com/alhamsya/adk/xhttp
β βββ client.go # HTTP client with retry
β βββ config.go # Upstream & endpoint configuration
β βββ client_test.go # Unit tests
β βββ client_config_test.go # Config integration tests
β βββ config_test.go # Config parsing tests
β
βββ zlog/ # π Structured Logging
β βββ go.mod # Module: github.com/alhamsya/adk/zlog
β βββ README.md # Package-level documentation
β βββ logger.go # Logger setup, init, constructors
β βββ annotation.go # Context annotation system
β βββ types.go # Type definitions & constants
β
βββ cmd/ # π Example Programs
βββ xerr/main.go # xerr usage example
βββ zlog/main.go # zlog usage example
| Package | Dependency | Version | Purpose |
|---|---|---|---|
xerr |
github.com/pkg/errors |
v0.9.1 | Error wrapping & stack traces |
xerr |
google.golang.org/grpc |
v1.79.2 | gRPC status codes |
xhttp |
github.com/avast/retry-go/v5 |
v5.0.0 | Retry mechanism |
xhttp |
github.com/pkg/errors |
v0.9.1 | Error wrapping |
zlog |
github.com/rs/zerolog |
v1.34.0 | High-performance logging |
Run unit tests for each package:
# Test all packages
go test ./xerr/... ./xhttp/... ./zlog/...
# Test with verbose output
go test -v ./xerr/...
go test -v ./xhttp/...
# Test with coverage
go test -cover ./xerr/... ./xhttp/...MIT License β see the LICENSE file for details.
Built with β€οΈ by Alhamsya