Skip to main content
Version: v0.2.1

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.