status

package
v1.20.0 Latest Latest
Warning

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

Go to latest
Published: Mar 10, 2026 License: MIT Imports: 25 Imported by: 0

README

Status Package

Go Version

Comprehensive health check and status monitoring system for HTTP APIs with flexible control modes, caching, and multi-format output support.


Table of Contents


Overview

This package provides production-ready health check and status monitoring for Go HTTP APIs. It integrates with the Gin web framework to expose status endpoints that aggregate component health checks with configurable validation strategies.

Design Philosophy
  1. Flexible Validation: Multiple control modes (Must, Should, AnyOf, Quorum) for different component criticality levels
  2. Performance-Focused: Built-in caching with atomic operations reduces overhead for frequent health checks
  3. Thread-Safe: Full concurrency support with proper synchronization primitives
  4. Multi-Format: JSON and plain text output for different consumption scenarios
  5. Integration-Ready: Works seamlessly with github.com/nabbar/golib/monitor for component monitoring

Key Features

  • Component Monitoring: Aggregate health from multiple monitored components
  • Control Modes: Flexible validation strategies (Ignore, Should, Must, AnyOf, Quorum)
  • Caching: 3-second default cache with atomic operations for high-frequency checks
  • Multi-Format Output: JSON (default) and plain text via query parameters or headers
  • Verbosity Control: Short (status only) or full (with component details) responses
  • Map Mode: Structured map output for components instead of list
  • Thread-Safe: Atomic operations and mutex protection for concurrent access
  • Configurable HTTP Codes: Customize return codes for OK (200), Warn (207), KO (500) states
  • Dynamic Component Loading: Load mandatory components from configuration keys
  • Version Tracking: Include application name, version, and build information
  • Gin Integration: Drop-in middleware for Gin web framework

Installation

go get github.com/nabbar/golib/status

Architecture

Package Structure

The package is organized into focused subpackages with clear responsibilities:

status/
├── control/             # Validation mode definitions
│   ├── interface.go     # Mode type and constants
│   ├── encode.go        # Marshaling support (JSON, YAML, TOML, CBOR)
│   └── format.go        # String parsing and formatting
├── mandatory/           # Single component group management
│   ├── interface.go     # Mandatory interface
│   └── model.go         # Thread-safe implementation
├── listmandatory/       # Multiple group management
│   ├── interface.go     # ListMandatory interface
│   └── model.go         # Collection handling
├── interface.go         # Main Status interface
├── model.go             # Core implementation
├── config.go            # Configuration structures
├── cache.go             # Status caching
├── route.go             # HTTP endpoint handler
└── encode.go            # Response marshaling
Component Overview
┌──────────────────────────────────────────────────────┐
│                  Status Package                      │
│  HTTP Endpoint + Component Health Aggregation        │
└──────────────┬────────────┬──────────────┬───────────┘
               │            │              │
      ┌────────▼───┐  ┌─────▼────┐  ┌──────▼──────┐
      │  control   │  │mandatory │  │listmandatory│
      │            │  │          │  │             │
      │ Validation │  │  Group   │  │  Group      │
      │   Modes    │  │ Manager  │  │ Collection  │
      └────────────┘  └──────────┘  └─────────────┘
               │            │              │
               └────────────┴──────────────┘
                            │
                  ┌─────────▼──────────┐
                  │  monitor/types     │
                  │  Component Health  │
                  └────────────────────┘
Component Purpose Thread-Safe Marshaling
status Main coordinator, HTTP endpoint JSON, Text
control Validation mode definitions JSON, YAML, TOML, CBOR
mandatory Component group with mode N/A
listmandatory Multiple group management N/A
Data Flow & Logic

The following diagram illustrates how the status package processes a request, computes the health status, and returns the response.

[HTTP Request] (GET /status)
      │
      ▼
[MiddleWare] (route.go)
      │
      ├─> Parse Query Params & Headers (short, format, map)
      │   Determines verbosity and output format.
      │
      ▼
[Status Computation] (model.go)
      │
      ├─> Check Cache (cache.go)
      │     │
      │     ├─> Valid? ───> Return Cached Status (Fast Path)
      │     │               (Atomic read, < 10ns)
      │     │
      │     └─> Invalid? ─┐ (Slow Path)
      │                   │
      │           [Walk Monitor Pool] (pool.go)
      │           Iterate over all registered monitors.
      │                   │
      │                   ▼
      │           [Apply Control Modes] (control/mandatory)
      │           Evaluate health based on configured rules.
      │                   │
      │             ┌─────┴─────┐
      │             │           │
      │        [Must/Should] [AnyOf/Quorum]
      │             │           │
      │             ▼           ▼
      │        Check Indiv.   Check Group
      │        Component      Logic (Thresholds)
      │             │           │
      │             └─────┬─────┘
      │                   │
      │                   ▼
      │           [Aggregate Status]
      │           Determine Global Status (OK / WARN / KO)
      │                   │
      │                   ▼
      └─<── Update Cache ─┘
            (Atomic write)
      │
      ▼
[Response Encoding] (encode.go)
      │
      ├─> Format: JSON / Text
      ├─> Verbosity: Full (details) / Short (status only)
      ├─> Structure: List / Map
      │
      ▼
[HTTP Response] (Status Code + Body)

Performance

Caching

The package implements efficient caching to reduce overhead:

  • Default Cache: 3 seconds (configurable)
  • Atomic Operations: Lock-free reads via atomic.Value
  • Thread-Safe: Safe for concurrent health checks
  • Cache Methods:
    • IsCacheHealthy(): Check >= Warn (accepts warnings)
    • IsCacheStrictlyHealthy(): Check == OK (strict)

Performance Impact: Cached checks complete in <10ns (no component walking)

Benchmark Results
BenchmarkNew-12                    13,963,723      77.33 ns/op      72 B/op    4 allocs/op
BenchmarkSetMode-12               135,494,511       9.02 ns/op       0 B/op    0 allocs/op
BenchmarkGetMode-12               174,430,036       6.77 ns/op       0 B/op    0 allocs/op
BenchmarkKeyAdd-12                 23,573,498      45.56 ns/op      40 B/op    2 allocs/op
BenchmarkKeyHas-12                100,000,000      10.04 ns/op       0 B/op    0 allocs/op
BenchmarkKeyList-12                21,103,474      57.20 ns/op      80 B/op    1 allocs/op
BenchmarkConcurrentReads-12        37,712,851      32.36 ns/op      48 B/op    1 allocs/op
BenchmarkConcurrentWrites-12       10,351,329     118.60 ns/op      40 B/op    2 allocs/op
BenchmarkMixedOperations-12        31,084,405      38.72 ns/op      15 B/op    0 allocs/op

Measured on AMD Ryzen 9 7900X3D, Linux, Go 1.21+

Concurrency
  • Atomic State: atomic.Int32, atomic.Int64, atomic.Value for cache
  • Mutex Protection: sync.RWMutex for configuration and pool access
  • Zero Races: Verified with go test -race (no data races detected)

Use Cases

This package is designed for scenarios requiring robust health monitoring:

Microservices Health Checks

  • Aggregate health from databases, caches, queues, external APIs
  • Configure critical (Must) vs optional (Should) dependencies
  • Return appropriate HTTP codes for load balancers and orchestrators

Kubernetes/Docker Health Probes

  • Liveness probe: Use IsStrictlyHealthy() for restart signals
  • Readiness probe: Use IsHealthy() to tolerate warnings
  • Startup probe: Check with cached status for efficiency

API Gateway Integration

  • Expose /health and /status endpoints
  • JSON for programmatic consumption
  • Text format for quick visual inspection

Monitoring Systems

  • Integrate with Prometheus, Datadog, New Relic
  • Cache reduces load on monitoring components
  • Detailed component status for diagnostics

Distributed Systems

  • AnyOf mode: Redis cluster with multiple nodes (any healthy = OK)
  • Quorum mode: Database replicas (majority must be healthy)
  • Must mode: Core dependencies (all must be healthy)

Quick Start

Basic Status Endpoint
package main

import (
    "github.com/gin-gonic/gin"
    "github.com/nabbar/golib/context"
    "github.com/nabbar/golib/status"
    "github.com/nabbar/golib/monitor/pool"
    "github.com/nabbar/golib/monitor/info"
)

func main() {
    // Create context
    ctx := context.NewGlobal()
    
    // Create status instance
    sts := status.New(ctx)
    
    // Set application info
    sts.SetInfo("my-api", "v1.0.0", "abc123")
    
    // Create and register monitor pool
    monPool := pool.New(ctx)
    sts.RegisterPool(func() montps.Pool { return monPool })
    
    // Add a component monitor
    dbMonitor := info.New(func(context.Context) (monsts.Status, string, error) {
        // Check database health
        if dbHealthy() {
            return monsts.OK, "Database connected", nil
        }
        return monsts.KO, "Database connection failed", nil
    })
    monPool.MonitorAdd(dbMonitor)
    
    // Set up Gin router
    r := gin.Default()
    r.GET("/status", func(c *gin.Context) {
        sts.MiddleWare(c)
    })
    
    r.Run(":8080")
}

func dbHealthy() bool {
    // Your health check logic
    return true
}
With Configuration
import (
    "net/http"
    "github.com/nabbar/golib/status"
    "github.com/nabbar/golib/status/control"
    monsts "github.com/nabbar/golib/monitor/status"
)

func setupStatus() status.Status {
    sts := status.New(ctx)
    sts.SetInfo("my-api", "v1.0.0", "abc123")
    
    // Configure HTTP return codes and mandatory components
    cfg := status.Config{
        ReturnCode: map[monsts.Status]int{
            monsts.OK:   http.StatusOK,           // 200
            monsts.Warn: http.StatusMultiStatus,  // 207
            monsts.KO:   http.StatusServiceUnavailable, // 503
        },
        Component: []status.Mandatory{
            {
                Mode: control.Must,
                Keys: []string{"database", "cache"},
            },
            {
                Mode: control.Should,
                Keys: []string{"email-service"},
            },
            {
                Mode: control.AnyOf,
                Keys: []string{"redis-1", "redis-2", "redis-3"},
            },
        },
    }
    sts.SetConfig(cfg)
    
    return sts
}
Health Check Methods
// In your application
sts := setupStatus()

// Check if healthy (tolerates warnings)
if sts.IsHealthy() {
    log.Println("Service is healthy")
}

// Check specific components
if sts.IsHealthy("database", "cache") {
    log.Println("Core components healthy")
}

// Strict check (no warnings allowed)
if sts.IsStrictlyHealthy() {
    log.Println("Service is perfectly healthy")
}

// Cached checks (efficient for frequent calls)
if sts.IsCacheHealthy() {
    // Uses cached status (3s default)
}

Subpackages

control Subpackage

Validation mode definitions for component health evaluation.

Available Modes

package control

type Mode uint8

const (
    Ignore Mode = iota  // No validation (component ignored)
    Should              // Warning only (doesn't cause failure)
    Must                // Must be healthy (causes failure if not)
    AnyOf               // At least one in group must be healthy
    Quorum              // Majority (>50%) must be healthy
)

Mode Behavior

Mode Component Status Overall Impact
Ignore Any No impact (skipped)
Should KO → Warn (not KO)
Should Warn → Warn
Should OK No impact
Must KO → KO
Must Warn → Warn
Must OK No impact
AnyOf All KO → KO
AnyOf At least 1 OK No impact
AnyOf Only Warn → Warn
Quorum ≤50% OK+Warn → KO
Quorum >50% OK+Warn No impact or → Warn

String Parsing

import "github.com/nabbar/golib/status/control"

// Parse from string (case-insensitive)
mode := control.Parse("must")      // → Must
mode = control.Parse("ANYOF")      // → AnyOf
mode = control.Parse("invalid")    // → Ignore (default)

// Format to string
str := control.Must.String()       // → "Must"

Marshaling Support

The Mode type implements multiple encoding interfaces:

// JSON
data, _ := json.Marshal(control.Must)  // → "Must"
var mode control.Mode
json.Unmarshal([]byte(`"must"`), &mode)

// YAML
data, _ := yaml.Marshal(control.Must)
yaml.Unmarshal([]byte("must"), &mode)

// TOML
data, _ := mode.MarshalTOML()

// CBOR (binary)
data, _ := mode.MarshalCBOR()

// Plain text
data, _ := mode.MarshalText()

See GoDoc for complete API.


mandatory Subpackage

Manages a single component group with an associated validation mode.

Features

  • Thread-safe key management
  • Atomic mode operations
  • Lock-free reads for high performance

API Example

import (
    "github.com/nabbar/golib/status/mandatory"
    "github.com/nabbar/golib/status/control"
)

// Create new group
m := mandatory.New()

// Set validation mode
m.SetMode(control.Must)

// Add component keys
m.KeyAdd("database", "cache", "queue")

// Check if key exists
if m.KeyHas("database") {
    fmt.Println("Database is in mandatory group")
}

// Get current mode
mode := m.GetMode()  // → Must

// List all keys
keys := m.KeyList()  // → ["database", "cache", "queue"]

// Remove keys
m.KeyDel("queue")

Thread Safety

// Safe for concurrent use
var wg sync.WaitGroup
m := mandatory.New()

for i := 0; i < 100; i++ {
    wg.Add(1)
    go func(id int) {
        defer wg.Done()
        m.KeyAdd(fmt.Sprintf("component-%d", id))
    }(i)
}
wg.Wait()

fmt.Println(m.KeyList())  // All 100 components added safely

See GoDoc for complete API.


listmandatory Subpackage

Manages multiple mandatory groups as a collection.

Features

  • Thread-safe collection operations
  • Iterator pattern with Walk method
  • Automatic cleanup of invalid entries

API Example

import (
    "github.com/nabbar/golib/status/listmandatory"
    "github.com/nabbar/golib/status/mandatory"
    "github.com/nabbar/golib/status/control"
)

// Create list
list := listmandatory.New()

// Create and add groups
coreGroup := mandatory.New()
coreGroup.SetMode(control.Must)
coreGroup.KeyAdd("database", "cache")
list.Add(coreGroup)

optionalGroup := mandatory.New()
optionalGroup.SetMode(control.Should)
optionalGroup.KeyAdd("email", "sms")
list.Add(optionalGroup)

redisCluster := mandatory.New()
redisCluster.SetMode(control.AnyOf)
redisCluster.KeyAdd("redis-1", "redis-2", "redis-3")
list.Add(redisCluster)

// Get count
count := list.Len()  // → 3

// Walk through groups
list.Walk(func(m mandatory.Mandatory) bool {
    fmt.Printf("Mode: %s, Keys: %v\n", m.GetMode(), m.KeyList())
    return true  // Continue iteration
})

// Remove a group
list.Del(optionalGroup)

See GoDoc for complete API.


Configuration

Config Structure
type Config struct {
    // HTTP status codes for each health state
    ReturnCode map[monsts.Status]int
    
    // Component groups with validation modes
    Component []Mandatory
}

type Mandatory struct {
    Mode       control.Mode  // Validation mode
    Keys       []string      // Component names
    ConfigKeys []string      // Config component keys (dynamic loading)
}
Default Values

If ReturnCode is empty, defaults are:

  • monsts.OK → 200 (http.StatusOK)
  • monsts.Warn → 207 (http.StatusMultiStatus)
  • monsts.KO → 500 (http.StatusInternalServerError)
Configuration Examples

Standard Web API

cfg := status.Config{
    ReturnCode: map[monsts.Status]int{
        monsts.OK:   200,  // OK
        monsts.Warn: 200,  // Treat warnings as OK
        monsts.KO:   503,  // Service Unavailable
    },
    Component: []status.Mandatory{
        {Mode: control.Must, Keys: []string{"database"}},
        {Mode: control.Should, Keys: []string{"cache"}},
    },
}

Dynamic Component Loading

// Load monitor names from component configuration
cfg := status.Config{
    Component: []status.Mandatory{
        {
            Mode: control.Must,
            ConfigKeys: []string{"database-component"}, // Resolves to monitor names
        },
    },
}

// Register resolver function
sts.RegisterGetConfigCpt(func(key string) Component {
    return myConfig.ComponentGet(key)
})

Kubernetes Health Probes

// Liveness: Strict (restart if any issue)
livenessCfg := status.Config{
    ReturnCode: map[monsts.Status]int{
        monsts.OK:   200,
        monsts.Warn: 500,  // Treat warnings as failure
        monsts.KO:   500,
    },
    Component: []status.Mandatory{
        {Mode: control.Must, Keys: []string{"core"}},
    },
}

// Readiness: Tolerant (accept warnings)
readinessCfg := status.Config{
    ReturnCode: map[monsts.Status]int{
        monsts.OK:   200,
        monsts.Warn: 200,  // Accept warnings
        monsts.KO:   503,
    },
}

Distributed System

cfg := status.Config{
    Component: []status.Mandatory{
        // Core database: must be healthy
        {Mode: control.Must, Keys: []string{"postgres"}},
        
        // Redis cluster: any node OK
        {Mode: control.AnyOf, Keys: []string{
            "redis-master", "redis-replica-1", "redis-replica-2",
        }},
        
        // Kafka cluster: quorum required
        {Mode: control.Quorum, Keys: []string{
            "kafka-1", "kafka-2", "kafka-3",
        }},
        
        // Optional services
        {Mode: control.Should, Keys: []string{"email", "sms"}},
    },
}

HTTP API

Endpoint Configuration
r := gin.Default()

// Simple endpoint
r.GET("/status", func(c *gin.Context) {
    sts.MiddleWare(c)
})

// With generic context
r.GET("/health", func(c *gin.Context) {
    sts.Expose(c)
})
Query Parameters
Parameter Values Description
short true, 1 Return only overall status (no component details)
format text, json Output format (default: JSON)
map true, 1 Output components as a map instead of a list
HTTP Headers
Header Values Description
X-Verbose false Return short output (same as short=true)
Accept text/plain, application/json Content negotiation
X-MapMode true Output components as a map instead of a list
Response Formats

JSON Response (default)

{
  "name": "my-api",
  "release": "v1.0.0",
  "hash": "abc123",
  "date_build": "2024-11-13T08:00:00Z",
  "status": "OK",
  "message": "",
  "component": {
    "database": {
      "status": "OK",
      "message": "Database connected",
      "options": {}
    },
    "cache": {
      "status": "OK",
      "message": "Redis operational",
      "options": {}
    }
  }
}

JSON Response (short)

{
  "name": "my-api",
  "release": "v1.0.0",
  "hash": "abc123",
  "date_build": "2024-11-13T08:00:00Z",
  "status": "OK",
  "message": ""
}

Text Response

OK: my-api (v1.0.0 - abc123)

Text Response (verbose)

OK: my-api (v1.0.0 - abc123)
  database: OK - Database connected
  cache: OK - Redis operational
HTTP Status Codes

The HTTP status code in the response depends on configuration:

Health Status Default Code Meaning
OK 200 All components healthy
Warn 207 Some warnings present
KO 500 One or more critical components failed

Customize codes via Config.ReturnCode (see Configuration).

Usage Examples

cURL

# JSON (default)
curl http://localhost:8080/status

# JSON short
curl http://localhost:8080/status?short=true

# Text format
curl http://localhost:8080/status?format=text

# Text format with details
curl -H "Accept: text/plain" http://localhost:8080/status

# Short via header
curl -H "X-Verbose: false" http://localhost:8080/status

# Map mode via header
curl -H "X-MapMode: true" http://localhost:8080/status

Go Client

resp, err := http.Get("http://localhost:8080/status")
if err != nil {
    log.Fatal(err)
}
defer resp.Body.Close()

if resp.StatusCode == http.StatusOK {
    log.Println("Service is healthy")
}

var status struct {
    Name    string `json:"name"`
    Release string `json:"release"`
    Status  string `json:"status"`
}
json.NewDecoder(resp.Body).Decode(&status)

Best Practices

Component Organization

Group by Criticality

cfg := status.Config{
    Component: []status.Mandatory{
        // Critical: Must be healthy for service to function
        {Mode: control.Must, Keys: []string{
            "database",
            "auth-service",
        }},
        
        // Important: Warnings acceptable, but not failures
        {Mode: control.Should, Keys: []string{
            "cache",
            "search-index",
        }},
        
        // Redundant: Any one instance is sufficient
        {Mode: control.AnyOf, Keys: []string{
            "worker-1", "worker-2", "worker-3",
        }},
    },
}
Health Check Implementation

✅ Good Practices

// 1. Fast health checks (<100ms)
func checkDatabase() (monsts.Status, string, error) {
    ctx, cancel := context.WithTimeout(context.Background(), 50*time.Millisecond)
    defer cancel()
    
    if err := db.PingContext(ctx); err != nil {
        return monsts.KO, "Database unreachable", err
    }
    return monsts.OK, "Database connected", nil
}

// 2. Use cached status for efficiency
if sts.IsCacheHealthy() {
    // Serve traffic (uses 3s cache)
}

// 3. Distinguish between warnings and failures
func checkCache() (monsts.Status, string, error) {
    if err := cache.Ping(); err != nil {
        // Cache down but not critical
        return monsts.Warn, "Cache unavailable", err
    }
    return monsts.OK, "Cache operational", nil
}

// 4. Provide meaningful messages
return monsts.KO, fmt.Sprintf("Connection pool exhausted: %d/%d", active, max), nil

❌ Bad Practices

// 1. Slow health checks (blocks thread)
func checkBad() (monsts.Status, string, error) {
    time.Sleep(5 * time.Second) // Too slow!
    return monsts.OK, "", nil
}

// 2. Ignoring cache benefits
for {
    if sts.IsHealthy() { // Recalculates every time
        // Heavy operation repeated unnecessarily
    }
    time.Sleep(100 * time.Millisecond)
}

// 3. Generic error messages
return monsts.KO, "Error", err // Not helpful for debugging

// 4. Silent failures
func checkSilent() (monsts.Status, string, error) {
    db.Ping() // Ignoring error
    return monsts.OK, "", nil
}
Error Handling

Always Handle Errors

// ✅ Good
if err := sts.MonitorAdd(monitor); err != nil {
    log.Printf("Failed to add monitor: %v", err)
    return err
}

// ❌ Bad
sts.MonitorAdd(monitor) // Ignoring error
Kubernetes Integration

Liveness Probe (Restart on failure)

livenessProbe:
  httpGet:
    path: /status?short=true
    port: 8080
  initialDelaySeconds: 30
  periodSeconds: 10
  timeoutSeconds: 5
  failureThreshold: 3

Readiness Probe (Remove from service on failure)

readinessProbe:
  httpGet:
    path: /status?short=true
    port: 8080
  initialDelaySeconds: 10
  periodSeconds: 5
  timeoutSeconds: 2
  failureThreshold: 2

Startup Probe (Initial health check)

startupProbe:
  httpGet:
    path: /status?short=true
    port: 8080
  initialDelaySeconds: 0
  periodSeconds: 5
  timeoutSeconds: 2
  failureThreshold: 30  # Allow 150s for startup
Monitoring Integration

Prometheus Metrics

import "github.com/prometheus/client_golang/prometheus"

var (
    healthStatus = prometheus.NewGaugeVec(
        prometheus.GaugeOpts{
            Name: "app_health_status",
            Help: "Health status (0=KO, 1=Warn, 2=OK)",
        },
        []string{"component"},
    )
)

func updateMetrics(sts status.Status) {
    sts.MonitorWalk(func(name string, mon montps.Monitor) bool {
        status := mon.Status()
        healthStatus.WithLabelValues(name).Set(float64(status.Int()))
        return true
    })
}
Performance Optimization

Use Cached Methods

// High-frequency checks (every request)
func middleware(c *gin.Context) {
    if !sts.IsCacheHealthy() {
        c.AbortWithStatus(503)
        return
    }
    c.Next()
}

// Background monitoring
go func() {
    ticker := time.NewTicker(10 * time.Second)
    for range ticker.C {
        if !sts.IsHealthy() {
            alerting.Trigger("service-unhealthy")
        }
    }
}()

Component Monitoring Best Practices

  • Keep checks lightweight (<100ms)
  • Use timeouts to prevent hanging
  • Cache expensive checks in the monitor itself
  • Return specific error messages
  • Use appropriate control modes (Must vs Should)

Testing

Test Suite: 307 specs across 4 packages with 84.82% overall coverage

# Run all tests
go test ./...

# With coverage
go test -cover ./...

# With race detection (recommended)
CGO_ENABLED=1 go test -race ./...

# Benchmarks
go test -bench=. -benchmem ./mandatory/
Test Results
status/                121 specs    82.20% coverage   10.7s
status/control/        102 specs    95.00% coverage   0.01s
status/listmandatory/   29 specs    86.00% coverage   0.5s
status/mandatory/       55 specs    76.10% coverage   0.1s
Quality Assurance
  • ✅ Zero data races (verified with -race)
  • ✅ Thread-safe concurrent operations
  • ✅ Comprehensive edge case coverage
  • ✅ Benchmark performance validation

See TESTING.md for detailed testing documentation.


Contributing

Contributions are welcome! Please follow these guidelines:

Code Contributions

  • Do not use AI to generate package implementation code
  • AI may assist with tests, documentation, and bug fixing
  • All contributions must pass go test -race
  • Maintain or improve test coverage (≥80%)
  • Follow existing code style and patterns

Documentation

  • Update README.md for new features
  • Add examples for common use cases
  • Keep TESTING.md synchronized with test changes

Testing

  • Write tests for all new features
  • Test edge cases and error conditions
  • Verify thread safety with race detector
  • Add comments explaining complex scenarios

Pull Requests

  • Provide clear description of changes
  • Reference related issues
  • Include test results
  • Update documentation

See CONTRIBUTING.md for detailed guidelines.


Future Enhancements

Potential improvements for future versions:

Control Modes

  • Custom validation logic via function callbacks
  • Weighted quorum (different components have different weights)
  • Time-based validation (require health for N consecutive checks)

Caching

  • Per-component cache duration
  • Cache invalidation hooks
  • Configurable cache strategies (LRU, TTL, etc.)

Monitoring

  • Built-in metrics export (Prometheus format)
  • Health check history and trends
  • Circuit breaker integration for failing components

Output Formats

  • XML output support
  • GraphQL endpoint support
  • Structured logging output

Advanced Features

  • Dependency graphs (component A depends on B)
  • Health check scheduling (different intervals per component)
  • Multi-region health aggregation
  • gRPC health check protocol support

Suggestions and contributions are welcome via GitHub issues.


AI Transparency Notice

In accordance with Article 50.4 of the EU AI Act, AI assistance has been used for testing, documentation, and bug fixing under human supervision.


License

MIT License - See LICENSE file for details.


Resources


Version: Go 1.25+ on Linux, macOS, Windows
Maintained By: Status Package Contributors

Documentation

Overview

Package status provides a comprehensive, thread-safe health check and status monitoring system designed for production-grade HTTP APIs.

It integrates seamlessly with the Gin web framework to expose status endpoints that aggregate health metrics from various application components (databases, caches, external services) using flexible and configurable validation strategies.

Overview

This package is built to address the needs of microservices and distributed systems where simple "up/down" checks are often insufficient. It offers:

  • **Flexible Validation**: Support for complex dependency rules via control modes (Must, Should, AnyOf, Quorum).
  • **High Performance**: Built-in caching mechanism using atomic operations to minimize lock contention and reduce load on downstream services during frequent health checks (e.g., Kubernetes probes).
  • **Thread-Safety**: All public API methods and internal state mutations are safe for concurrent access.
  • **Multi-Format Output**: Native support for JSON (default), plain text (for simple parsers), and structured map outputs.
  • **Dynamic Configuration**: Capability to load mandatory component definitions dynamically from configuration keys, allowing for runtime adaptability.
  • **Ecosystem Integration**: Designed to work hand-in-hand with `github.com/nabbar/golib/monitor` for the actual health checking logic.

Architecture

The package is structured into modular subpackages to separate concerns and improve maintainability:

status/
├── control/       # Defines the validation logic and enum types for control modes (Must, Should, etc.).
├── mandatory/     # Manages a single group of components that share a specific validation mode.
├── listmandatory/ # Manages a collection of mandatory groups, enabling complex, multi-layered validation rules.
├── interface.go   # Defines the main public interfaces (Status, Route, Info, Pool).
├── model.go       # Contains the core implementation of the Status interface and the aggregation logic.
├── config.go      # Defines configuration structures and validation logic.
├── cache.go       # Implements the caching layer using atomic values for lock-free reads.
└── route.go       # Provides the HTTP handler and middleware for Gin integration.

Component Overview

The following diagram illustrates the high-level components and their interactions:

┌──────────────────────────────────────────────────────┐
│                  Status Package                      │
│  HTTP Endpoint + Component Health Aggregation        │
└──────────────┬────────────┬──────────────┬───────────┘
               │            │              │
      ┌────────▼───┐  ┌────▼─────┐  ┌────▼────────┐
      │  control   │  │mandatory │  │listmandatory│
      │            │  │          │  │             │
      │ Validation │  │  Group   │  │  Group      │
      │   Modes    │  │ Manager  │  │ Collection  │
      └────────────┘  └──────────┘  └─────────────┘
               │            │              │
               └────────────┴──────────────┘
                            │
                  ┌─────────▼──────────┐
                  │  monitor/types     │
                  │  Component Health  │
                  └────────────────────┘

Data Flow & Logic

The following diagram details the request processing flow, from the incoming HTTP request to the final response, highlighting the caching and computation steps:

[HTTP Request] (GET /status)
      │
      ▼
[MiddleWare] (route.go)
      │
      ├─> Parse Query Params & Headers (short, format, map)
      │   Determines verbosity and output format.
      │
      ▼
[Status Computation] (model.go)
      │
      ├─> Check Cache (cache.go)
      │     │
      │     ├─> Valid? ───> Return Cached Status (Fast Path)
      │     │               (Atomic read, < 10ns)
      │     │
      │     └─> Invalid? ─┐ (Slow Path)
      │                   │
      │           [Walk Monitor Pool] (pool.go)
      │           Iterate over all registered monitors.
      │                   │
      │                   ▼
      │           [Apply Control Modes] (control/mandatory)
      │           Evaluate health based on configured rules.
      │                   │
      │             ┌─────┴─────┐
      │             │           │
      │        [Must/Should] [AnyOf/Quorum]
      │             │           │
      │             ▼           ▼
      │        Check Indiv.   Check Group
      │        Component      Logic (Thresholds)
      │             │           │
      │             └─────┬─────┘
      │                   │
      │                   ▼
      │           [Aggregate Status]
      │           Determine Global Status (OK / WARN / KO)
      │                   │
      │                   ▼
      └─<── Update Cache ─┘
            (Atomic write)
      │
      ▼
[Response Encoding] (encode.go)
      │
      ├─> Format: JSON / Text
      ├─> Verbosity: Full (details) / Short (status only)
      ├─> Structure: List / Map
      │
      ▼
[HTTP Response] (Status Code + Body)

Key Features Detail

## Component Monitoring The system aggregates health status from multiple sources. Each source is a "Monitor" (defined in `github.com/nabbar/golib/monitor`) that performs the actual check (e.g., pinging a DB).

## Control Modes Control modes dictate how the failure of a specific component affects the global application status:

  • **Ignore**: The component is monitored, but its status is completely ignored in the global calculation.
  • **Should**: Non-critical dependency. Failure results in a `WARN` global status, but not `KO`.
  • **Must**: Critical dependency. Failure results in a `KO` global status.
  • **AnyOf**: Redundancy check. The group is healthy if *at least one* component is healthy.
  • **Quorum**: Consensus check. The group is healthy if *more than 50%* of components are healthy.

## Caching To prevent "thundering herd" problems or excessive load on dependencies during high-frequency health checks (like Kubernetes liveness probes), the status is cached.

  • **Duration**: Configurable, defaults to 3 seconds.
  • **Mechanism**: Uses `atomic.Value` to store the result and timestamp, allowing for lock-free reads in the hot path.

## Output Formats

  • **JSON**: Standard format, easy to parse.
  • **Text**: Human-readable, useful for command-line tools (curl/grep).
  • **Map Mode**: Returns components as a JSON map (keyed by name) instead of a list, facilitating direct access by component name.

Usage

## Basic Setup

import (
	"github.com/gin-gonic/gin"
	"github.com/nabbar/golib/context"
	"github.com/nabbar/golib/status"
	"github.com/nabbar/golib/monitor/pool"
	"github.com/nabbar/golib/monitor/types"
)

func main() {
	// Initialize the global context
	ctx := context.NewGlobal()

	// Create the status manager
	sts := status.New(ctx)
	sts.SetInfo("my-app", "v1.0.0", "build-hash")

	// Create and register the monitor pool
	monPool := pool.New(ctx)
	sts.RegisterPool(func() montps.Pool { return monPool })

	// Setup Gin router
	r := gin.Default()

	// Register the status middleware
	r.GET("/status", func(c *gin.Context) {
		sts.MiddleWare(c)
	})

	r.Run(":8080")
}

## Configuration

You can configure HTTP return codes and mandatory components. This example shows how to mix static definitions with dynamic loading.

cfg := status.Config{
	// Map internal status to HTTP status codes
	ReturnCode: map[monsts.Status]int{
		monsts.OK:   200,
		monsts.Warn: 207, // Multi-Status
		monsts.KO:   503, // Service Unavailable
	},
	// Define mandatory component groups
	Component: []status.Mandatory{
		{
			Mode: control.Must,
			Keys: []string{"database"}, // Static definition: "database" must be up
		},
		{
			Mode:       control.Should,
			ConfigKeys: []string{"cache-component"}, // Dynamic: load keys from config "cache-component"
		},
	},
}
sts.SetConfig(cfg)

// Register a resolver for dynamic loading (ConfigKeys)
sts.RegisterGetConfigCpt(func(key string) cfgtypes.Component {
	// Logic to retrieve component by key from your config system
	return myConfig.ComponentGet(key)
})

Programmatic Health Checks

The package provides several methods to check the system's health programmatically, useful for internal logic or custom probes.

## Live vs. Cached Checks

  • **Live Checks** (`IsHealthy()`, `IsStrictlyHealthy()`): These methods force a re-evaluation of all monitors. They provide the most up-to-date state but are more expensive. Use them when you need immediate confirmation of a state change (e.g., during startup or shutdown).

  • **Cached Checks** (`IsCacheHealthy()`, `IsCacheStrictlyHealthy()`): These methods return the cached result if it is still valid (within the TTL). They are extremely fast (<10ns) and thread-safe. Use them for high-frequency endpoints like `/health` or `/status`.

## Strict vs. Tolerant Checks

  • **Tolerant Check** (`IsHealthy`, `IsCacheHealthy`): Returns `true` if the global status is `OK` or `WARN`. This is suitable for **Readiness Probes**, where a degraded service (WARN) might still be able to serve some traffic.

  • **Strict Check** (`IsStrictlyHealthy`, `IsCacheStrictlyHealthy`): Returns `true` *only* if the global status is `OK`. This is suitable for **Liveness Probes**, where you might want to restart the service if it's not fully healthy (depending on your restart policy).

## Checking Specific Components

You can also check the health of one or more specific components. The control logic (Must/Should/etc.) associated with these components is still applied during the check.

// Check if "database" and "cache" are healthy
if sts.IsHealthy("database", "cache") {
	// Proceed with logic requiring DB and Cache
}

HTTP API

The exposed endpoint supports several query parameters and headers for content negotiation:

  • `short=true` (query) or `X-Verbose: false` (header): Returns only the overall status without component details.
  • `format=text` (query) or `Accept: text/plain` (header): Returns plain text output.
  • `map=true` (query) or `X-MapMode: true` (header): Returns components as a map instead of a list.

Subpackages

The package is modularized to separate concerns:

  • `control`: Defines validation modes (e.g., Must, Should) and their encoding/decoding logic. It is the brain of the validation strategy.
  • `mandatory`: Manages a single group of components associated with a specific validation mode. It is a thread-safe container for a list of component keys and their control mode.
  • `listmandatory`: Manages a collection of `mandatory` groups, allowing for the creation of complex validation rules across different sets of components.

Control Modes & Logic

The health of the application is determined by aggregating the status of monitored components according to their assigned control mode.

## Available Modes

  • `Ignore`: The component's status is completely ignored and does not affect the global status.
  • `Should`: The component is important but not critical. A failure (`KO`) or warning (`WARN`) in this component will result in a global `WARN` status, but not `KO`.
  • `Must`: The component is critical. A failure (`KO`) will result in a global `KO` status. A warning (`WARN`) will result in a global `WARN`.
  • `AnyOf`: Used for redundant groups (e.g., a cluster of services). The global status will be `KO` only if all components in the group are `KO`. Otherwise, the group's status is determined by the best-status member.
  • `Quorum`: Used for distributed consensus groups. The global status will be `KO` if more than 50% of the components in the group are `KO`.

## Status Transitions

The global status is calculated dynamically based on the worst-case scenario allowed by the configuration.

  1. `OK`: All `Must` components are `OK`, `Quorum` is satisfied, and `AnyOf` has healthy candidates.
  2. `WARN`: A `Should` component is `KO`, or a `Must` component is `WARN`. The service is considered degraded but still operational.
  3. `KO`: A `Must` component is `KO`, `Quorum` is lost, or an `AnyOf` group has no healthy candidates. The service is considered unavailable.

Transitions occur automatically as component health changes. The `IsHealthy()` method returns `true` for both `OK` and `WARN` states, while `IsStrictlyHealthy()` returns `true` only for the `OK` state.

Index

Constants

View Source
const (
	// ErrorParamEmpty occurs when required parameters are missing. For example,
	// this can happen when attempting to marshal a status response without having
	// first set the application info (name, release, hash) via `SetInfo` or `SetVersion`.
	ErrorParamEmpty liberr.CodeError = iota + liberr.MinPkgStatus

	// ErrorValidatorError occurs when the configuration validation fails. This
	// error is returned by `Config.Validate()` when the provided configuration
	// does not meet the required constraints defined by the struct tags (e.g., a
	// required field is missing).
	ErrorValidatorError
)
View Source
const (
	// HeadVerbose is the HTTP header for controlling response verbosity.
	// A value of "false" enables short mode (equivalent to `short=true` query param).
	HeadVerbose = "X-Verbose"

	// HeadFormat is the standard "Accept" HTTP header used for content negotiation.
	// It supports "application/json" and "text/plain".
	HeadFormat = "Accept"

	// HeadMapMode is the HTTP header for enabling map mode for component output.
	// A value of "true" enables map mode.
	HeadMapMode = "X-MapMode"

	// QueryVerbose is the query parameter for enabling short output mode.
	// A value of "true" or "1" will omit component details from the response.
	QueryVerbose = "short"

	// QueryFormat is the query parameter for selecting the output format.
	// Supported values are "text" or "json".
	QueryFormat = "format"

	// QueryMapMode is the query parameter for enabling map mode for component output.
	// A value of "true" or "1" will format the component list as a map.
	QueryMapMode = "map"
)

Variables

This section is empty.

Functions

This section is empty.

Types

type Config added in v1.10.0

type Config struct {
	// ReturnCode maps internal health status levels (OK, Warn, KO) to HTTP status codes.
	//
	// Default values if not set:
	//   - monsts.OK:   200 (http.StatusOK)
	//   - monsts.Warn: 207 (http.StatusMultiStatus)
	//   - monsts.KO:   500 (http.StatusInternalServerError)
	ReturnCode map[monsts.Status]int `mapstructure:"return-code" json:"return-code" yaml:"return-code" toml:"return-code" validate:"required"`

	// Component defines the list of mandatory component groups. Each group specifies
	// a set of components and the control mode that applies to them.
	Component []Mandatory `mapstructure:"component" json:"component" yaml:"component" toml:"component" validate:""`
}

Config defines the complete configuration for the status system. It controls how the application's health is computed and how it is reported via HTTP.

func (Config) Validate added in v1.10.0

func (o Config) Validate() error

Validate checks if the configuration is valid using the `validator` package. It ensures that all required fields are present and meet the defined constraints.

Returns:

An error if validation fails, containing details about which fields failed
and why. Returns nil if the configuration is valid.

type Encode added in v1.10.0

type Encode interface {
	// String returns the status as a formatted string, suitable for logging or
	// plain text responses.
	String() string

	// Bytes returns the status as a byte slice, equivalent to `[]byte(String())`.
	Bytes() []byte

	// GinRender renders the status response to a Gin context. It handles content
	// negotiation (JSON vs. text) and verbosity (full vs. short).
	GinRender(c *ginsdk.Context, isText bool, isShort bool)

	// GinCode returns the appropriate HTTP status code for the response, based on
	// the overall health status and configuration.
	GinCode() int
}

Encode defines the interface for status response encoding. It provides methods for rendering the status in different formats (JSON, plain text) and for integration with the Gin framework.

type FuncGetCfgCpt added in v1.20.0

type FuncGetCfgCpt func(key string) cfgtps.ComponentMonitor

FuncGetCfgCpt defines a function type for retrieving a component by its key. This function acts as a bridge between the status package and an external component management system. It is used to dynamically load monitor names from component configurations when `ConfigKeys` is used in the `Mandatory` section of the status configuration.

The returned `cfgtps.ComponentMonitor` must provide the monitor names via its `GetMonitorNames()` method.

type Info added in v1.5.5

type Info interface {
	// SetInfo manually sets the application name, release version, and build hash.
	// This is a straightforward way to provide static version information.
	//
	// Parameters:
	//   - name: The name of the application (e.g., "my-api").
	//   - release: The release version (e.g., "v1.2.3").
	//   - hash: The build commit hash (e.g., "abcdef1").
	SetInfo(name, release, hash string)

	// SetVersion sets the application information from a `version.Version` object.
	// This is the recommended approach as it allows the version information to be
	// managed centrally and updated dynamically if needed.
	//
	// Parameters:
	//   - vers: An object that implements the `libver.Version` interface.
	SetVersion(vers libver.Version)
}

Info defines the interface for setting the application's version and build information. This information is included in the status response to help identify the exact version of the running service.

type Mandatory added in v1.11.3

type Mandatory struct {
	// Mode defines how this group of components affects the overall status.
	// Possible values include:
	//   - Ignore: The components are monitored but do not affect global status.
	//   - Should: Failure causes a warning but not a critical failure.
	//   - Must: Failure causes a critical global failure.
	//   - AnyOf: At least one component in the group must be healthy.
	//   - Quorum: A majority of components in the group must be healthy.
	Mode stsctr.Mode `mapstructure:"mode" json:"mode" yaml:"mode" toml:"mode" validate:"required"`

	// Keys is a list of static monitor names belonging to this group.
	// These names must match the names of the monitors registered in the monitor pool.
	// This field is used when the component names are known at configuration time.
	Keys []string `mapstructure:"keys" json:"keys" yaml:"keys" toml:"keys"`

	// ConfigKeys is used to specify the keys of config components. This allows for
	// dynamic resolution of monitor names. When `SetConfig` is called, the system
	// will look up the component configuration using these keys (via the function
	// registered with `RegisterGetConfigCpt`) and add the associated monitor names
	// to this mandatory group.
	ConfigKeys []string `mapstructure:"configKeys" json:"configKeys" yaml:"configKeys" toml:"configKeys"`
}

Mandatory defines a group of components that share a specific control mode. It allows grouping multiple components (e.g., "all databases") and defining how their collective health affects the overall application status.

This structure is typically used when loading configuration from a file (JSON, YAML, etc.).

See github.com/nabbar/golib/status/control for details on available control modes.

func ParseList added in v1.19.0

func ParseList(m ...stsmdt.Mandatory) []Mandatory

ParseList converts a slice of `mandatory.Mandatory` interfaces to a slice of `Mandatory` structs.

This is a utility function for bulk conversion, useful when exporting the current configuration state.

Parameters:

  • m: A variadic list of `mandatory.Mandatory` interfaces.

Returns:

A slice of `Mandatory` structs. Nil entries in the input are skipped.

func ParseMandatory added in v1.19.0

func ParseMandatory(m stsmdt.Mandatory) Mandatory

ParseMandatory converts a `mandatory.Mandatory` interface (from the internal logic) to a `Mandatory` struct (for configuration/export).

This is a utility function for converting between the runtime interface representation and the configuration struct representation.

Parameters:

  • m: The `mandatory.Mandatory` interface to convert.

Returns:

A `Mandatory` struct populated with the mode and keys from the interface.
Returns an empty struct if the input is nil.

type Pool added in v1.10.0

type Pool interface {
	montps.PoolStatus

	// RegisterPool registers a function that provides the monitor pool. This
	// dependency injection pattern is crucial, as it decouples the status
	// package from the monitor pool's lifecycle. The status package can then
	// retrieve the most up-to-date pool instance whenever it needs to perform
	// a health check.
	//
	// Parameters:
	//   - fct: A function that returns an instance of `montps.Pool`.
	RegisterPool(fct montps.FuncPool)
}

Pool defines the interface for managing the monitor components that contribute to the overall health status. It embeds `montps.PoolStatus` to provide basic monitor management (Add, Del, Get, etc.) and adds a method for registering the pool itself.

type Route added in v1.10.0

type Route interface {
	// Expose provides a generic handler for status requests that can be used
	// with any framework that uses `context.Context`. It acts as a wrapper
	// around the `MiddleWare` method, allowing for broader framework compatibility.
	// If the provided context is a `*gin.Context`, it will be handled; otherwise,
	// the call is a no-op.
	//
	// Parameters:
	//   - ctx: The context of the request, which may be a `*gin.Context`.
	Expose(ctx context.Context)

	// MiddleWare is a Gin middleware that processes status requests. It handles
	// content negotiation for format (JSON/text) and verbosity (full/short)
	// based on query parameters and HTTP headers. It calculates the current
	// health status and renders the appropriate response.
	//
	// Parameters:
	//   - c: The `*gin.Context` for the incoming HTTP request.
	MiddleWare(c *ginsdk.Context)

	// SetErrorReturn registers a custom factory function for creating error
	// formatters. This allows you to define how errors generated within the
	// status middleware are rendered in the HTTP response, ensuring consistent
	// error formatting across your application.
	//
	// Parameters:
	//   - f: A function that returns an instance of `liberr.ReturnGin`.
	SetErrorReturn(f func() liberr.ReturnGin)
}

Route defines the HTTP routing interface for exposing the status endpoint. This interface groups all methods related to handling HTTP requests, primarily for integration with the Gin web framework.

type Status

type Status interface {
	encoding.TextMarshaler
	json.Marshaler

	Route
	Info
	Pool

	// RegisterGetConfigCpt registers a function to retrieve component configurations
	// by key. This is essential for the dynamic `ConfigKeys` feature, allowing
	// the status system to query an external configuration manager for components
	// and their associated monitors.
	//
	// Parameters:
	//   - fct: The function that will be called to resolve a component key.
	RegisterGetConfigCpt(fct FuncGetCfgCpt)

	// SetConfig applies a configuration for status computation and HTTP response codes.
	// This is the primary method for defining your health check policies, including
	// which components are critical and how their failures should be handled.
	//
	// Parameters:
	//   - cfg: The `Config` object containing the desired settings.
	SetConfig(cfg Config)

	// GetConfig returns the current configuration used for status computation.
	// This can be useful for debugging or for services that need to inspect
	// the current health check policy.
	//
	// Returns:
	//   The current `Config` object.
	GetConfig() Config

	// IsHealthy performs a live (non-cached) health check to determine if the
	// overall system or a specific set of components are "healthy," meaning their
	// status is either `OK` or `WARN`. This is a "tolerant" check.
	//
	// Parameters:
	//   - name: An optional list of component names to check. If empty, checks all components.
	//
	// Returns:
	//   `true` if the aggregated status is `OK` or `WARN`, `false` otherwise.
	IsHealthy(name ...string) bool

	// IsStrictlyHealthy performs a live (non-cached) health check to determine if
	// the overall system or specific components are "strictly healthy," meaning
	// their status is `OK`. This is a "strict" check.
	//
	// Parameters:
	//   - name: An optional list of component names to check. If empty, checks all components.
	//
	// Returns:
	//   `true` only if the aggregated status is `OK`, `false` otherwise.
	IsStrictlyHealthy(name ...string) bool

	// IsCacheHealthy performs a "tolerant" health check using the cached status.
	// It returns `true` if the cached status is `OK` or `WARN`. This is a
	// high-performance check suitable for frequent calls (e.g., in a middleware).
	//
	// Returns:
	//   `true` if the cached status is `OK` or `WARN`, `false` otherwise.
	IsCacheHealthy() bool

	// IsCacheStrictlyHealthy performs a "strict" health check using the cached status.
	// It returns `true` only if the cached status is `OK`. This is also a
	// high-performance check.
	//
	// Returns:
	//   `true` only if the cached status is `OK`, `false` otherwise.
	IsCacheStrictlyHealthy() bool
}

Status is the main interface that combines all health status functionality. It provides a complete solution for application health monitoring and reporting, from configuration to HTTP response rendering.

func New added in v1.5.5

func New(ctx context.Context) Status

New creates a new, fully initialized `Status` instance.

The returned instance is thread-safe but requires further configuration before it can be used effectively. At a minimum, you must:

  1. Call `SetInfo` or `SetVersion` to provide application identity.
  2. Call `RegisterPool` to link a monitor pool for health checks.

You can also optionally call `SetConfig` to define custom health policies and `SetErrorReturn` to customize error formatting.

Parameters:

  • ctx: The root `context.Context` for the application.

Returns:

A new `Status` instance.

Directories

Path Synopsis
Package control provides the validation modes that govern how component health affects the overall application status.
Package control provides the validation modes that govern how component health affects the overall application status.
Package listmandatory provides management of multiple mandatory component groups.
Package listmandatory provides management of multiple mandatory component groups.
Package mandatory provides a thread-safe mechanism for managing groups of components that share a common validation mode.
Package mandatory provides a thread-safe mechanism for managing groups of components that share a common validation mode.

Jump to

Keyboard shortcuts

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