Skip to main content
Version: Next

Observability

The package provides a complete observability layer for applications, integrating metrics, tracing, and logs. The implementation encapsulates OpenTelemetry, offering features for monitoring transactions, segments, errors, and custom metrics.

Configuration

Configuration is automatically performed when starting colibri-sdk-go. To export data to an OpenTelemetry collector, use the following environment variables:

VariableRequiredDescription
OTEL_EXPORTER_OTLP_ENDPOINTYesOTLP collector endpoint — accepts host:port or full URL (e.g., http://localhost:4318)
OTEL_EXPORTER_OTLP_HEADERSNoComma-separated key=value headers (e.g., api-key=secret,x-env=prod)
OTEL_EXPORTER_OTLP_METRICS_ENDPOINTNoOverride endpoint for the metrics signal only. Defaults to OTEL_EXPORTER_OTLP_ENDPOINT
OTEL_SERVICE_NAMENoService name reported to the backend. Defaults to APP_NAME

Initialization

When initializing Colibri, the following components are automatically configured:

  • OpenTelemetry connection.
  • Default application metrics.
  • Go runtime metrics collection.

Automatic Metrics

When OTEL_EXPORTER_OTLP_ENDPOINT is configured, the SDK automatically emits the following metrics:

  • HTTP Server/Client: request duration via otelfiber and otelhttp
  • Database: operation duration via otelsql
  • Go Runtime: heap, GC, and goroutines

Main Components

1. Monitoring Transactions

// Starting a new monitoring transaction
txn, ctx := monitoring.StartTransaction(ctx, "MyOperation")
defer monitoring.EndTransaction(txn)

2. Segments

// Creating a transaction segment
segment := monitoring.StartTransactionSegment(ctx, "MyOperation", map[string]string{
"operation": "query",
"type": "database",
})
defer monitoring.EndTransactionSegment(segment)

Note: For the segment to be correctly related to the transaction, we must pass the ctx attribute created in the main transaction.

3. Error Tracking

// Notifying errors
if err != nil {
monitoring.NoticeError(txn, err)
return err
}

4. Custom Metrics

The SDK exposes three types of metric instruments via the monitoring package:

Counter

A monotonically increasing counter for measuring event occurrences.

import "github.com/colibriproject-dev/colibri-sdk-go/pkg/base/monitoring"

requests := monitoring.Counter("app.requests", "Total HTTP requests", "1")
requests.Add(ctx, 1, map[string]string{"route": "/api/users"})

Histogram

Records value distributions, ideal for measuring durations and sizes.

duration := monitoring.Histogram("app.request.duration", "Request duration", "ms")
duration.Record(ctx, elapsed.Milliseconds(), map[string]string{"status": "200"})

Gauge

Captures the current value of a measurement at a point in time.

activeConns := monitoring.Gauge("app.connections.active", "Active connections", "1")
activeConns.Record(ctx, float64(count), nil)

Usage Examples

1. External Service Monitoring

func (s *Service) CallExternalAPI(ctx context.Context) error {
segment := monitoring.StartTransactionSegment(ctx, "Payment_API")
defer monitoring.EndTransactionSegment(segment)

resp, err := http.Get("https://api.external.com/resource")
if err != nil {
monitoring.NoticeError(monitoring.GetTransactionInContext(ctx), err)
return err
}
defer resp.Body.Close()

return nil
}

2. Custom Metrics in a Service

var (
ordersProcessed = monitoring.Counter("orders.processed", "Processed orders", "1")
processingTime = monitoring.Histogram("orders.processing_time", "Processing time", "ms")
queueSize = monitoring.Gauge("orders.queue_size", "Queue size", "1")
)

func (s *Service) ProcessOrder(ctx context.Context, order Order) error {
start := time.Now()

if err := s.process(ctx, order); err != nil {
return err
}

ordersProcessed.Add(ctx, 1, map[string]string{"status": "success"})
processingTime.Record(ctx, float64(time.Since(start).Milliseconds()), nil)
queueSize.Record(ctx, float64(s.queue.Len()), nil)
return nil
}

Best Practices

  1. Transaction Naming:

    • Use descriptive and consistent names.
    • Follow a naming standard.
    • Avoid dynamic names.
  2. Context Management:

    • Always propagate the context.
    • Use defer to end transactions.
    • Maintain the correct lifecycle.
  3. Attributes:

    • Add relevant attributes.
    • Do not add sensitive data.
    • Use consistent keys.
  4. Errors:

    • Notify relevant errors.
    • Add context to errors.
    • Avoid notifying expected errors.
  5. Metrics:

    • Declare instruments as package-level variables (avoids recreation on every call).
    • Prefer names in domain.object.action format (e.g., app.requests.total).
    • Use standard units: 1 for dimensionless counters, ms for milliseconds, By for bytes.
  6. Performance:

    • Monitor response times.
    • Observe resource usage.
    • Identify bottlenecks.
  7. Security:

    • Do not log sensitive data in logs or attributes.
    • Respect data protection laws.