Tier: Platform · Status: Full · Java original:
firefly-otel-spring-boot-starter· .NET project:FireflyFramework.Observability
observability provides three orthogonal concerns:
- Structured logging — a
slog.Loggerbuilder that auto-enriches every record with the correlation id fromkernel.WithCorrelationID. - Health indicators — composable
Indicatorvalues with aCompositeaggregator producing the canonical UP / DEGRADED / DOWN / UNKNOWN rollup. - Startup banner — the ASCII Firefly banner + version + runtime identifying line.
OpenTelemetry SDK wiring (exporters, sampling, resource attributes)
is left to the application's main.go — this module exposes only
the building blocks that compose with go.opentelemetry.io/otel.
type LogConfig struct {
Level slog.Level
Output io.Writer
Service string
Format string // "json" | "text"
}
func DefaultLogConfig() LogConfig
func NewLogger(cfg LogConfig) *slog.LoggerThe returned logger wraps a CorrelationHandler so every record gets
a correlationId attribute when one is on the context.
type Status string // StatusUp | StatusDegraded | StatusDown | StatusUnknown
type HealthResult struct {
Status Status
Message string
Details map[string]any
Duration time.Duration
Time time.Time
}
type Indicator interface {
Name() string
Check(ctx context.Context) HealthResult
}
type IndicatorFunc struct {
NameValue string
Fn func(ctx context.Context) HealthResult
}
type Composite struct{ ... }
func NewComposite() *Composite
func (*Composite) Add(Indicator)
func (*Composite) CheckAll(ctx) (Status, map[string]HealthResult)The composite rollup is DOWN if any indicator is DOWN, else
DEGRADED if any is DEGRADED, else UP.
func PrintBanner(w io.Writer, starter, app string)Emits the ASCII art + framework version + runtime identifier. Called
by startercore.Core.PrintBanner() on startup.
import (
"context"
"log/slog"
"github.com/fireflyframework/fireflyframework-go/kernel"
"github.com/fireflyframework/fireflyframework-go/observability"
)
log := observability.NewLogger(observability.LogConfig{
Service: "orders",
Format: "json",
Level: slog.LevelInfo,
})
ctx := kernel.WithCorrelationID(context.Background(), "abc-123")
log.InfoContext(ctx, "placed order", "id", "42")
// {"time":"…","level":"INFO","msg":"placed order","service":"orders","correlationId":"abc-123","id":"42"}
health := observability.NewComposite()
health.Add(observability.IndicatorFunc{
NameValue: "db",
Fn: func(ctx context.Context) observability.HealthResult {
if err := db.PingContext(ctx); err != nil {
return observability.HealthResult{Status: observability.StatusDown, Message: err.Error()}
}
return observability.HealthResult{Status: observability.StatusUp}
},
})
overall, results := health.CheckAll(ctx)The actuator module mounts this Composite on /actuator/health.
cd observability
go test ./...Covers JSON-format correlation-id emission, the degraded ⊕ up
overall computation, and banner content.