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:
| Variable | Required | Description |
|---|---|---|
OTEL_EXPORTER_OTLP_ENDPOINT | Yes | OTLP collector endpoint — accepts host:port or full URL (e.g., http://localhost:4318) |
OTEL_EXPORTER_OTLP_HEADERS | No | Comma-separated key=value headers (e.g., api-key=secret,x-env=prod) |
OTEL_EXPORTER_OTLP_METRICS_ENDPOINT | No | Override endpoint for the metrics signal only. Defaults to OTEL_EXPORTER_OTLP_ENDPOINT |
OTEL_SERVICE_NAME | No | Service 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
otelfiberandotelhttp - 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
ctxattribute 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
-
Transaction Naming:
- Use descriptive and consistent names.
- Follow a naming standard.
- Avoid dynamic names.
-
Context Management:
- Always propagate the context.
- Use defer to end transactions.
- Maintain the correct lifecycle.
-
Attributes:
- Add relevant attributes.
- Do not add sensitive data.
- Use consistent keys.
-
Errors:
- Notify relevant errors.
- Add context to errors.
- Avoid notifying expected errors.
-
Metrics:
- Declare instruments as package-level variables (avoids recreation on every call).
- Prefer names in
domain.object.actionformat (e.g.,app.requests.total). - Use standard units:
1for dimensionless counters,msfor milliseconds,Byfor bytes.
-
Performance:
- Monitor response times.
- Observe resource usage.
- Identify bottlenecks.
-
Security:
- Do not log sensitive data in logs or attributes.
- Respect data protection laws.