optimize(querying) ∧ ¬optimize(writing)
annotate(span, context) where context = {
business ∪ user ∪ technical ∪ outcome
}
effect-api := Effect.annotateCurrentSpan ∨ Effect.annotateLogs
queryable-via := annotations ⊄ log-message-args
// extra log-message args → message body, not auto-indexed dimensions
execution-plan := Effect.withExecutionPlan ∨ Stream.withExecutionPlan
attempt-events := onEvent(AttemptStart | AttemptSuccess | AttemptFailure)
// AttemptFailure.cause retains typed failures, defects, and interruption
// every AttemptStart has exactly one terminal event; observer defects are isolated
high-dimensionality → better-queryability
high-cardinality(userId) → acceptable
∀ span → attach(identity ∪ user ∪ business ∪ performance ∪ outcome)
¬queryable → ¬enough-context
Reference: See Article.md for full article by Boris Tane (loggingsucks.com)
1---2name: effect-wide-events3description: Conceptual guide to wide events (canonical log lines) for observability. Use when thinking about instrumentation strategy, span annotations, or designing what context to capture.4---56<wide-events>78<philosophy>9traditional := many(log-lines) → grep(services) → hope10wide := one(event) → query(structured) → answer1112optimize(querying) ∧ ¬optimize(writing)13</philosophy>1415<current-practice>16implementation := OTel spans + annotations17wide-events := mental-model for annotation strategy1819annotate(span, context) where context = {20business ∪ user ∪ technical ∪ outcome21}2223effect-api := Effect.annotateCurrentSpan ∨ Effect.annotateLogs24queryable-via := annotations ⊄ log-message-args25// extra log-message args → message body, not auto-indexed dimensions2627execution-plan := Effect.withExecutionPlan ∨ Stream.withExecutionPlan28attempt-events := onEvent(AttemptStart | AttemptSuccess | AttemptFailure)29// AttemptFailure.cause retains typed failures, defects, and interruption30// every AttemptStart has exactly one terminal event; observer defects are isolated31</current-practice>3233<dimensionality>34wide-event.fields := {35 identity: {traceId, spanId, service, operation}36 user: {userId, accountTier, accountAge, lifetimeValue}37 business: {featureFlags, experimentGroup, cartValue}38 performance: {durationMs, dbQueryCount, cacheHitRate, retryCount}39 outcome: {success, errorCode, httpStatus}40}4142high-dimensionality → better-queryability43high-cardinality(userId) → acceptable44</dimensionality>4546<anti-patterns>47scattered-logs := console.log("step1") >> console.log("step2") >> ...48low-dimensionality := span.set("success", true) ∧ |fields| < 549technical-only := {http.status, db.queries} ∧ ¬{user, business}50</anti-patterns>5152<correct-pattern>53span.setAttributes({54 "request.operation", "user.id", "user.tier",55 "cart.items", "cart.value", "feature.*",56 "db.query_count", "cache.hit_rate",57 "request.success"58})5960∀ span → attach(identity ∪ user ∪ business ∪ performance ∪ outcome)61</correct-pattern>6263<tail-sampling>64retain(100%) := errors ∨ slow(>p99) ∨ vip65retain(1-5%) := success ∧ fast66</tail-sampling>6768<queryability-test>69before(instrument) → verify(answerable({70 "failures where tier=premium ∧ feature.new_flow=true"71 "p99(latency) group by tier"72 "errors group by featureFlags"73 "full context for user X incident"74}))7576¬queryable → ¬enough-context77</queryability-test>7879<terminology>80cardinality := |unique values| (userId=high, httpMethod=low)81dimensionality := |fields per event| (more → better)82wide-event := canonical-log-line := one comprehensive record83</terminology>8485<when-to-apply>86deciding(span-annotations)87reviewing(instrumentation-coverage)88debugging(incidents) → "what context was missing?"89planning(new-service-observability)90choosing(fields-to-index)91</when-to-apply>9293</wide-events>9495Reference: See `Article.md` for full article by Boris Tane (loggingsucks.com)