Quickstart
Installation
go get github.com/zoobz-io/aperture
Requires Go 1.24+ and capitan.
Minimal Setup
package main
import (
"context"
"time"
"github.com/zoobz-io/aperture"
"github.com/zoobz-io/capitan"
"go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp"
"go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp"
"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp"
"go.opentelemetry.io/otel/sdk/log"
"go.opentelemetry.io/otel/sdk/metric"
"go.opentelemetry.io/otel/sdk/trace"
)
func main() {
ctx := context.Background()
// 1. Create OTEL providers
logExporter, _ := otlploghttp.New(ctx, otlploghttp.WithEndpoint("localhost:4318"), otlploghttp.WithInsecure())
logProvider := log.NewLoggerProvider(log.WithProcessor(log.NewBatchProcessor(logExporter)))
defer logProvider.Shutdown(ctx)
metricExporter, _ := otlpmetrichttp.New(ctx, otlpmetrichttp.WithEndpoint("localhost:4318"), otlpmetrichttp.WithInsecure())
meterProvider := metric.NewMeterProvider(metric.WithReader(
metric.NewPeriodicReader(metricExporter, metric.WithInterval(60*time.Second)),
))
defer meterProvider.Shutdown(ctx)
traceExporter, _ := otlptracehttp.New(ctx, otlptracehttp.WithEndpoint("localhost:4318"), otlptracehttp.WithInsecure())
traceProvider := trace.NewTracerProvider(trace.WithSpanProcessor(trace.NewBatchSpanProcessor(traceExporter)))
defer traceProvider.Shutdown(ctx)
// 2. Create aperture (no config = log all events)
cap := capitan.Default()
ap, err := aperture.New(cap, logProvider, meterProvider, traceProvider)
if err != nil {
panic(err)
}
defer ap.Close()
// 3. Emit events - they automatically become OTEL logs
sig := capitan.NewSignal("app.started", "Application started")
cap.Emit(ctx, sig)
// 4. Graceful shutdown
cap.Shutdown()
}
What's Happening
- OTEL providers - You configure how telemetry reaches your backend (exporters, batching, endpoints)
- Aperture bridge - Registers as a capitan observer, receives all events
- Automatic transformation - Events become OTEL logs with signal name and fields as attributes
- Provider shutdown - Your responsibility to flush and close providers
The key insight: aperture handles event-to-signal transformation, you handle OTEL configuration.
With Configuration
Add metrics and trace correlation:
// Define signals
orderCreated := capitan.NewSignal("order.created", "Order created")
orderCompleted := capitan.NewSignal("order.completed", "Order completed")
orderID := capitan.NewStringKey("order_id")
total := capitan.NewFloat64Key("total")
// Create aperture
ap, _ := aperture.New(cap, logProvider, meterProvider, traceProvider)
defer ap.Close()
// Configure transformations with schema
schema := aperture.Schema{
// Count order creations
Metrics: []aperture.MetricSchema{
{
Signal: "order.created",
Name: "orders_created_total",
Type: "counter",
},
},
// Correlate order start/end into spans
Traces: []aperture.TraceSchema{
{
Start: "order.created",
End: "order.completed",
CorrelationKey: "order_id",
SpanName: "order_processing",
},
},
// Only log order events
Logs: &aperture.LogSchema{
Whitelist: []string{"order.created", "order.completed"},
},
}
ap.Apply(schema)
// Emit order flow
cap.Emit(ctx, orderCreated, orderID.Field("ORD-123"), total.Field(99.99))
// ^ Counter incremented, log recorded, span started
time.Sleep(100 * time.Millisecond)
cap.Emit(ctx, orderCompleted, orderID.Field("ORD-123"))
// ^ Log recorded, span completed
Using OTEL Directly
Aperture exposes standard OTEL interfaces:
// Get OTEL primitives
logger := ap.Logger("orders")
meter := ap.Meter("orders")
tracer := ap.Tracer("orders")
// Create custom metrics
counter, _ := meter.Int64Counter("custom_counter")
counter.Add(ctx, 1)
// Create custom spans
ctx, span := tracer.Start(ctx, "custom-operation")
defer span.End()
Next Steps
- Concepts - Understand the core model
- Metrics Guide - Configure metric transformations
- Traces Guide - Set up trace correlation
- Testing Guide - Test your aperture configuration