Skip to content

feat(otel): add custom OpenTelemetry module with tracing, metrics, and structured logging#1226

Open
987Nabil wants to merge 4 commits intozio:mainfrom
987Nabil:feat/otel
Open

feat(otel): add custom OpenTelemetry module with tracing, metrics, and structured logging#1226
987Nabil wants to merge 4 commits intozio:mainfrom
987Nabil:feat/otel

Conversation

@987Nabil
Copy link
Contributor

Summary

Custom OpenTelemetry implementation for zio-blocks — zero Java SDK dependency, implementing the OTel standard from scratch in blocks style.

  • Tracing: Span lifecycle with scoped block pattern (tracer.span("op") { span => ... }), automatic parent-child linking via ContextStorage, AlwaysOn/AlwaysOff/ParentBased sampling
  • Metrics: All sync instruments (Counter, UpDownCounter, Histogram, Gauge) + async/observable instruments (ObservableCounter, ObservableUpDownCounter, ObservableGauge) with LongAdder-based concurrent accumulation
  • Structured Logging: Logger with automatic trace correlation (traceId/spanId injected from active span), 24 severity levels, LogRecordProcessor pipeline
  • Context Propagation: Loom-aware ContextStorage (ScopedValue on JDK 21+ via reflection, ThreadLocal fallback), W3C TraceContext + B3 (single + multi-header) propagation formats
  • Export: OTLP/JSON encoding (custom JSON writer, no external deps), BatchProcessor with bounded queue + retry, JdkHttpSender (java.net.http.HttpClient)
  • SDK: OtelSdk builder entry point wiring all three signals, JVM shutdown hook, PlatformExecutor with virtual threads on JDK 21+ / daemon thread fallback

Key Design Decisions

  • No Java OTel SDK dependency — implements OTel data model and OTLP protocol directly
  • No Scope dependency — spans use simple scoped block pattern, not $[Span] wrapper
  • JVM-only module (like schema-avro) — depends on context.jvm + chunk.jvm
  • Cross-Scala: 572 tests passing on both Scala 3.7.4 and 2.13.18
  • Loom detection at runtime via Class.forName reflection — single JAR, no Multi-Release JAR
  • OTLP/JSON encoding only (protobuf module planned separately)

Files

  • 43 source files in otel/src/main/scala/zio/blocks/otel/
  • 27 test files in otel/src/test/scala/zio/blocks/otel/
  • 572 tests total
  • build.sbt updated with otel project definition + aliases

Testing

sbt "++3.7.4; otel/test"     # 572 tests pass
sbt "++2.13.18; otel/test"   # 572 tests pass

Copilot AI review requested due to automatic review settings March 14, 2026 16:47
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a new otel subproject implementing OpenTelemetry-style tracing, metrics, and structured logging (with OTLP/JSON export) directly in zio-blocks.

Changes:

  • Introduces core OTel data model + APIs (Tracer/Span, Meter/instruments, Logger/log records) with context propagation (W3C + B3) and runtime Loom-aware context storage.
  • Adds OTLP/JSON exporters with batching, retries, and a platform scheduler.
  • Adds extensive test suite for IDs, context storage, samplers, spans, instruments, exporters, and SDK wiring; updates build.sbt to include the new module.

Reviewed changes

Copilot reviewed 68 out of 69 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
build.sbt Adds otel project, dependencies, coverage thresholds, and test alias integration.
otel/src/main/scala/zio/blocks/otel/AttributeKey.scala Defines typed attribute keys used across all signals.
otel/src/main/scala/zio/blocks/otel/AttributeType.scala Adds attribute type discriminator used for key/value conversion.
otel/src/main/scala/zio/blocks/otel/AttributeValue.scala Adds attribute value ADT (scalar + seq) used by Attributes and signal payloads.
otel/src/main/scala/zio/blocks/otel/Attributes.scala Implements immutable Attributes and builder used across spans/logs/metrics.
otel/src/main/scala/zio/blocks/otel/B3Propagator.scala Implements B3 single and multi-header context propagation.
otel/src/main/scala/zio/blocks/otel/BatchProcessor.scala Adds batching/retry processor used by OTLP exporters.
otel/src/main/scala/zio/blocks/otel/ContextStorage.scala Adds Loom-aware context storage (ScopedValue via reflection; ThreadLocal fallback).
otel/src/main/scala/zio/blocks/otel/HttpSender.scala Adds HTTP transport abstraction + JDK HttpClient sender for OTLP exports.
otel/src/main/scala/zio/blocks/otel/InstrumentationScope.scala Defines instrumentation scope metadata for produced telemetry.
otel/src/main/scala/zio/blocks/otel/Logger.scala Implements structured logging with optional trace correlation from ContextStorage.
otel/src/main/scala/zio/blocks/otel/LoggerProvider.scala Provides Logger instances and configures processors + context storage.
otel/src/main/scala/zio/blocks/otel/LogRecord.scala Defines LogRecord data model and a builder for log emission.
otel/src/main/scala/zio/blocks/otel/LogRecordProcessor.scala Defines log processor interface + noop implementation.
otel/src/main/scala/zio/blocks/otel/Meter.scala Implements Meter + builders and ties instruments into collection registry.
otel/src/main/scala/zio/blocks/otel/MeterProvider.scala Provides meters and reader backed by a shared registry.
otel/src/main/scala/zio/blocks/otel/MetricData.scala Defines metric point types and MetricData ADT.
otel/src/main/scala/zio/blocks/otel/MetricReader.scala Defines metric reader API + default impl.
otel/src/main/scala/zio/blocks/otel/ObservableInstruments.scala Adds observable instruments and callback API.
otel/src/main/scala/zio/blocks/otel/OtelContext.scala Bridges ContextStorage with zio-blocks Context[R].
otel/src/main/scala/zio/blocks/otel/OtelExample.scala Adds example main program demonstrating tracing/metrics/logging usage.
otel/src/main/scala/zio/blocks/otel/OtelSdk.scala Adds SDK builder wiring exporters, providers, scheduler, and shutdown hook.
otel/src/main/scala/zio/blocks/otel/OtlpJsonExporter.scala Adds OTLP/JSON exporter config and trace/log/metric exporters.
otel/src/main/scala/zio/blocks/otel/PlatformExecutor.scala Provides scheduled executor using virtual threads when available.
otel/src/main/scala/zio/blocks/otel/Propagator.scala Defines propagation abstraction (extract/inject + fields).
otel/src/main/scala/zio/blocks/otel/Resource.scala Defines Resource model and default resource attributes.
otel/src/main/scala/zio/blocks/otel/Sampler.scala Defines sampler API and implementations (AlwaysOn/Off/ParentBased).
otel/src/main/scala/zio/blocks/otel/Severity.scala Defines 24-level severity model and parsing helpers.
otel/src/main/scala/zio/blocks/otel/Span.scala Defines Span API + RecordingSpan implementation and NoOp span.
otel/src/main/scala/zio/blocks/otel/SpanBuilder.scala Adds fluent span builder to create RecordingSpan instances.
otel/src/main/scala/zio/blocks/otel/SpanContext.scala Adds SpanContext model (TraceId/SpanId/flags/state/remote).
otel/src/main/scala/zio/blocks/otel/SpanData.scala Defines immutable span snapshot model (events/links/status/resource/scope).
otel/src/main/scala/zio/blocks/otel/SpanId.scala Adds SpanId model + parsing/formatting + random generation.
otel/src/main/scala/zio/blocks/otel/SpanKind.scala Adds span kind ADT (internal/server/client/producer/consumer).
otel/src/main/scala/zio/blocks/otel/SpanProcessor.scala Defines span processor interface + noop implementation.
otel/src/main/scala/zio/blocks/otel/SpanStatus.scala Adds span status ADT (Unset/Ok/Error).
otel/src/main/scala/zio/blocks/otel/SyncInstruments.scala Implements sync instruments (Counter, UpDownCounter, Histogram, Gauge).
otel/src/main/scala/zio/blocks/otel/TraceFlags.scala Adds trace flags model + hex parsing/formatting.
otel/src/main/scala/zio/blocks/otel/TraceId.scala Adds TraceId model + hex parsing/formatting + random generation.
otel/src/main/scala/zio/blocks/otel/Tracer.scala Adds Tracer API with scoped span block pattern and processor callbacks.
otel/src/main/scala/zio/blocks/otel/TracerProvider.scala Adds TracerProvider builder and lifecycle methods.
otel/src/main/scala/zio/blocks/otel/W3CTraceContextPropagator.scala Implements W3C traceparent/tracestate propagator.
otel/src/test/scala/zio/blocks/otel/BatchProcessorSpec.scala Tests batching, drops, retries, periodic flush behavior.
otel/src/test/scala/zio/blocks/otel/ContextStorageSpec.scala Tests ScopedValue/ThreadLocal behavior and scoped restoration semantics.
otel/src/test/scala/zio/blocks/otel/HttpSenderSpec.scala Tests HttpResponse and JdkHttpSender construction/shutdown behavior.
otel/src/test/scala/zio/blocks/otel/InstrumentationScopeSpec.scala Tests InstrumentationScope constructors, accessors, and equality behavior.
otel/src/test/scala/zio/blocks/otel/LogRecordSpec.scala Tests LogRecord model, builder, severity mapping/parsing, and trace fields.
otel/src/test/scala/zio/blocks/otel/LoggerSpec.scala Tests logging pipeline, convenience APIs, and trace correlation behavior.
otel/src/test/scala/zio/blocks/otel/MeterSpec.scala Tests sync + observable instruments and reader collection across meters.
otel/src/test/scala/zio/blocks/otel/OtelContextSpec.scala Tests OtelContext snapshot/withSpan and integration with zio-blocks Context.
otel/src/test/scala/zio/blocks/otel/OtelSdkSpec.scala Tests SDK builder wiring, providers, resource/sampler customization, shutdown.
otel/src/test/scala/zio/blocks/otel/PlatformExecutorSpec.scala Tests scheduling behavior and cancellation with sequential aspect.
otel/src/test/scala/zio/blocks/otel/ResourceSpec.scala Tests Resource default attributes and merge semantics.
otel/src/test/scala/zio/blocks/otel/SamplerSpec.scala Tests sampler decisions, descriptions, and parent-based behavior.
otel/src/test/scala/zio/blocks/otel/SpanContextSpec.scala Tests SpanContext validity, sampled flag, and equality/immutability intent.
otel/src/test/scala/zio/blocks/otel/SpanIdSpec.scala Tests SpanId parsing/formatting, validity rules, random generation.
otel/src/test/scala/zio/blocks/otel/SpanKindSpec.scala Tests SpanKind sealing/exhaustiveness and equality behavior.
otel/src/test/scala/zio/blocks/otel/SpanSpec.scala Tests RecordingSpan lifecycle, NoOp behavior, builder behavior, concurrency.
otel/src/test/scala/zio/blocks/otel/SpanStatusSpec.scala Tests SpanStatus ADT correctness and pattern matching behavior.
otel/src/test/scala/zio/blocks/otel/TraceFlagsSpec.scala Tests TraceFlags parsing/formatting and bit semantics.
otel/src/test/scala/zio/blocks/otel/TraceIdSpec.scala Tests TraceId parsing/formatting, validity rules, random generation.
otel/src/test/scala/zio/blocks/otel/TracerSpec.scala Tests tracer provider builder, scoped span behavior, nesting, sampling.
Comments suppressed due to low confidence (2)

otel/src/main/scala/zio/blocks/otel/Tracer.scala:1

  • SamplingResult is computed but not fully applied: (1) SamplingDecision.RecordOnly is treated the same as RecordAndSample, so spans that should be “record only” still look sampled/exportable; (2) SamplingResult.attributes and SamplingResult.traceState are ignored. This will break sampler behavior (especially ParentBased + RecordOnly) and any sampler-provided attributes/state. Apply the sampler outcome by setting the resulting trace flags/trace state on the created SpanContext, merging result.attributes into the span’s initial attributes, and ensuring RecordOnly results in TraceFlags.none (and/or suppresses export if that’s the intended behavior).
    otel/src/main/scala/zio/blocks/otel/SyncInstruments.scala:1
  • The Counter docs say it “only allows non-negative additions,” but add does not enforce value >= 0. Either enforce monotonicity (e.g., ignore/throw on negative values) or update the documentation to match the actual behavior; otherwise callers can record invalid counter semantics.

Comment on lines +74 to +114
val contextStorage = ContextStorage.create[Option[SpanContext]](None)
val sdkScope = InstrumentationScope("otel-sdk")

// Trace exporter as a SpanProcessor
val traceExporter = new OtlpJsonTraceExporter(exporterConfig, resource, sdkScope, httpSender)
val tracerProvider = TracerProvider.builder
.setResource(resource)
.setSampler(sampler)
.addSpanProcessor(traceExporter)
.build()

// Meter provider
val meterProvider = MeterProvider.builder
.setResource(resource)
.build()

// Schedule periodic metric export
val metricExporter = new OtlpJsonMetricExporter(
exporterConfig,
resource,
sdkScope,
httpSender,
() =>
meterProvider.reader.collectAllMetrics().map { data =>
NamedMetric("metric", "", "", data)
}
)

PlatformExecutor.schedule(
exporterConfig.flushIntervalMillis,
exporterConfig.flushIntervalMillis,
TimeUnit.MILLISECONDS
)(new Runnable { def run(): Unit = { metricExporter.exportMetrics(); () } })

// Logger provider with log exporter
val logExporter = new OtlpJsonLogExporter(exporterConfig, resource, sdkScope, httpSender)
val loggerProvider = LoggerProvider.builder
.setResource(resource)
.addLogRecordProcessor(logExporter)
.setContextStorage(contextStorage)
.build()
Comment on lines +191 to +192
resource = Resource.empty,
instrumentationScope = InstrumentationScope("default")
* This builder for method chaining
*/
def setAttribute[A](key: AttributeKey[A], value: A): LogRecordBuilder =
copy(attributes = Attributes.builder.put(key, value).build)
Comment on lines +102 to +106
PlatformExecutor.schedule(
exporterConfig.flushIntervalMillis,
exporterConfig.flushIntervalMillis,
TimeUnit.MILLISECONDS
)(new Runnable { def run(): Unit = { metricExporter.exportMetrics(); () } })
Comment on lines +1 to +5
package zio.blocks.otel

import java.net.URI
import java.net.http.{HttpClient, HttpRequest, HttpResponse => JdkHttpResponse}
import java.time.Duration
@987Nabil 987Nabil force-pushed the feat/otel branch 3 times, most recently from 159251f to 43b0bfe Compare March 14, 2026 17:35
…d structured logging

Custom OTel implementation for zio-blocks — no Java SDK dependency.

Tracing: TraceId, SpanId, Span, SpanContext, Tracer with scoped block pattern,
TracerProvider, SpanProcessor, Sampler (AlwaysOn/AlwaysOff/ParentBased)

Metrics: Counter (monotonic, enforces non-negative), UpDownCounter, Histogram,
Gauge (sync), ObservableCounter, ObservableUpDownCounter, ObservableGauge (async),
Meter, MeterProvider

Logging: Severity (24 levels), LogRecord, Logger with trace correlation,
LoggerProvider, LogRecordProcessor

Context: Loom-aware ContextStorage (ScopedValue + ThreadLocal fallback via
reflection), shared between TracerProvider and LoggerProvider for trace
correlation, PlatformExecutor (virtual threads on JDK 21+, daemon fallback),
OtelContext integration with zio-blocks Context[R]

Propagation: W3C TraceContext (traceparent/tracestate), B3 (single + multi-header)

Export: OTLP/JSON encoding, HttpSender + JdkHttpSender, BatchProcessor with
bounded queue and retry, OtlpJsonExporter for all three signals

SDK: OtelSdk builder entry point with proper lifecycle management

580 tests, 87.6% statement / 72.6% branch coverage
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a new JVM-only otel module to zio-blocks that implements core OpenTelemetry signals (tracing, metrics, structured logging), context propagation, and OTLP/JSON export without relying on the Java OTel SDK.

Changes:

  • Introduces tracing primitives (IDs, span context/kind/status, span builder/impl), sampling, and propagators (W3C + B3).
  • Adds metrics API (sync + observable instruments), collection/reader infrastructure, and periodic export wiring.
  • Adds structured logging with trace correlation plus batching/export plumbing; wires everything through an OtelSdk builder and updates build.sbt to include the new project and tests.

Reviewed changes

Copilot reviewed 74 out of 79 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
build.sbt Adds otel project definition, test alias integration, and coverage configuration.
otel/src/main/scala/zio/blocks/otel/AttributeKey.scala Defines typed attribute keys for telemetry attributes.
otel/src/main/scala/zio/blocks/otel/AttributeType.scala Defines attribute type discriminators.
otel/src/main/scala/zio/blocks/otel/AttributeValue.scala Defines the attribute value ADT.
otel/src/main/scala/zio/blocks/otel/Attributes.scala Implements immutable attributes collection + builder utilities.
otel/src/main/scala/zio/blocks/otel/B3Propagator.scala Implements B3 single/multi-header propagation.
otel/src/main/scala/zio/blocks/otel/BatchProcessor.scala Adds batching, queueing, flushing, and retry logic for exports.
otel/src/main/scala/zio/blocks/otel/ContextStorage.scala Adds Loom-aware scoped context storage (ScopedValue/ThreadLocal).
otel/src/main/scala/zio/blocks/otel/HttpSender.scala Provides HTTP sender abstraction and JDK HttpClient implementation.
otel/src/main/scala/zio/blocks/otel/InstrumentationScope.scala Defines instrumentation scope model.
otel/src/main/scala/zio/blocks/otel/LogRecord.scala Adds log record model + builder.
otel/src/main/scala/zio/blocks/otel/LogRecordProcessor.scala Adds log processor API + noop implementation.
otel/src/main/scala/zio/blocks/otel/Logger.scala Implements structured logger with optional trace correlation.
otel/src/main/scala/zio/blocks/otel/LoggerProvider.scala Provides loggers by instrumentation scope and manages processors.
otel/src/main/scala/zio/blocks/otel/Meter.scala Implements meter/instrument builders and internal registry collection.
otel/src/main/scala/zio/blocks/otel/MeterProvider.scala Provides meters and a metric reader backed by a registry.
otel/src/main/scala/zio/blocks/otel/MetricData.scala Defines metric data models (sum/histogram/gauge).
otel/src/main/scala/zio/blocks/otel/MetricReader.scala Defines metric reader API and default implementation.
otel/src/main/scala/zio/blocks/otel/ObservableInstruments.scala Adds observable instrument types and callback collection.
otel/src/main/scala/zio/blocks/otel/OtelContext.scala Bridges otel context storage with zio-blocks Context[R].
otel/src/main/scala/zio/blocks/otel/OtelExample.scala Demonstrates end-to-end usage of tracing/metrics/logging.
otel/src/main/scala/zio/blocks/otel/OtelSdk.scala Adds SDK builder wiring exporters/providers and scheduling metric export.
otel/src/main/scala/zio/blocks/otel/OtlpJsonExporter.scala Adds OTLP/JSON exporter config and exporters for traces/logs/metrics.
otel/src/main/scala/zio/blocks/otel/PlatformExecutor.scala Provides platform executor (virtual threads when available).
otel/src/main/scala/zio/blocks/otel/Propagator.scala Defines the propagation abstraction (extract/inject + fields).
otel/src/main/scala/zio/blocks/otel/Resource.scala Defines OTel Resource model + default attributes and merge support.
otel/src/main/scala/zio/blocks/otel/Sampler.scala Adds sampling decisions, results, and built-in samplers.
otel/src/main/scala/zio/blocks/otel/Severity.scala Adds log severity model and parsing helpers.
otel/src/main/scala/zio/blocks/otel/Span.scala Defines Span API + noop span + recording span implementation.
otel/src/main/scala/zio/blocks/otel/SpanBuilder.scala Implements fluent span builder.
otel/src/main/scala/zio/blocks/otel/SpanContext.scala Defines propagatable span context model and constructors.
otel/src/main/scala/zio/blocks/otel/SpanData.scala Defines export snapshot types (span data, events, links).
otel/src/main/scala/zio/blocks/otel/SpanId.scala Implements span ID generation/validation and hex/bytes conversion.
otel/src/main/scala/zio/blocks/otel/SpanKind.scala Defines span kind ADT.
otel/src/main/scala/zio/blocks/otel/SpanProcessor.scala Defines span processor API + noop implementation.
otel/src/main/scala/zio/blocks/otel/SpanStatus.scala Defines span status ADT.
otel/src/main/scala/zio/blocks/otel/SyncInstruments.scala Implements sync metric instruments (counter/updown/histogram/gauge).
otel/src/main/scala/zio/blocks/otel/TraceFlags.scala Implements trace flags model and hex parsing/formatting.
otel/src/main/scala/zio/blocks/otel/TraceId.scala Implements trace ID generation/validation and hex/bytes conversion.
otel/src/main/scala/zio/blocks/otel/Tracer.scala Implements scoped span lifecycle API with sampler integration.
otel/src/main/scala/zio/blocks/otel/TracerProvider.scala Provides tracers, builder configuration, and processor lifecycle.
otel/src/main/scala/zio/blocks/otel/W3CTraceContextPropagator.scala Implements W3C TraceContext propagation (traceparent/tracestate).
otel/src/test/scala/zio/blocks/otel/BatchProcessorSpec.scala Tests batching, queue bounds, retries, and periodic flush behavior.
otel/src/test/scala/zio/blocks/otel/ContextStorageSpec.scala Tests ScopedValue/ThreadLocal behavior and scoped restoration.
otel/src/test/scala/zio/blocks/otel/HttpSenderSpec.scala Tests HTTP sender/response construction and shutdown behavior.
otel/src/test/scala/zio/blocks/otel/InstrumentationScopeSpec.scala Tests scope constructors/accessors and basic properties.
otel/src/test/scala/zio/blocks/otel/LoggerSpec.scala Tests logging pipeline, severity helpers, and trace correlation.
otel/src/test/scala/zio/blocks/otel/MeterSpec.scala Tests sync + observable instrument collection and provider behavior.
otel/src/test/scala/zio/blocks/otel/OtelContextSpec.scala Tests integration between ContextStorage and zio-blocks Context.
otel/src/test/scala/zio/blocks/otel/OtelSdkSpec.scala Tests SDK builder defaults, provider integration, and shutdown.
otel/src/test/scala/zio/blocks/otel/PlatformExecutorSpec.scala Tests scheduling behavior and cancellation.
otel/src/test/scala/zio/blocks/otel/ResourceSpec.scala Tests resource defaults and merge semantics.
otel/src/test/scala/zio/blocks/otel/SamplerSpec.scala Tests built-in samplers and sampling decisions/results.
otel/src/test/scala/zio/blocks/otel/SpanContextSpec.scala Tests span context validity, sampling flag, and fields.
otel/src/test/scala/zio/blocks/otel/SpanIdSpec.scala Tests span ID parsing/formatting/randomness and bytes conversion.
otel/src/test/scala/zio/blocks/otel/SpanKindSpec.scala Tests span kind accessibility and match exhaustiveness.
otel/src/test/scala/zio/blocks/otel/SpanSpec.scala Tests span lifecycle, data snapshotting, and basic thread-safety.
otel/src/test/scala/zio/blocks/otel/SpanStatusSpec.scala Tests span status variants and pattern matching.
otel/src/test/scala/zio/blocks/otel/TraceFlagsSpec.scala Tests trace flags parsing/formatting and sampled bit operations.
otel/src/test/scala/zio/blocks/otel/TraceIdSpec.scala Tests trace ID parsing/formatting/randomness and bytes conversion.

Comment on lines +142 to +146
val span = builder.startSpan()
// Apply traceState from SamplingResult if present
val finalSpan = if (result.traceState.nonEmpty) {
val correctedCtx = span.spanContext.copy(traceState = result.traceState)
new RecordingSpan(
Comment on lines +57 to +61
val cb = new ObservableCallback {
def record(value: Double, attributes: Attributes): Unit = {
measurements.add(SumDataPoint(attributes, 0L, now, value.toLong))
()
}
Comment on lines +19 to +33
final class TracerProvider(
resource: Resource,
sampler: Sampler,
processors: Seq[SpanProcessor],
private[otel] val contextStorage: ContextStorage[Option[SpanContext]]
) {

def get(name: String, version: String = ""): Tracer = {
val scope = InstrumentationScope(
name = name,
version = if (version.isEmpty) None else Some(version)
)
new Tracer(scope, resource, sampler, processors, contextStorage)
}

Comment on lines +198 to +203
seq.headOption match {
case Some(_: String) => AttributeValue.StringSeqValue(seq.asInstanceOf[Seq[String]])
case Some(_: Long) => AttributeValue.LongSeqValue(seq.asInstanceOf[Seq[Long]])
case Some(_: Double) => AttributeValue.DoubleSeqValue(seq.asInstanceOf[Seq[Double]])
case Some(_: Boolean) => AttributeValue.BooleanSeqValue(seq.asInstanceOf[Seq[Boolean]])
case _ => AttributeValue.StringSeqValue(Seq.empty)
Comment on lines +96 to +100
parentSpanContext = span.toSpanData.parentSpanContext,
startTimeNanos = span.toSpanData.startTimeNanos,
initialAttributes = span.toSpanData.attributes,
initialLinks = span.toSpanData.links,
resource = resource,
Comment on lines +101 to +105
val cb = new ObservableCallback {
def record(value: Double, attributes: Attributes): Unit = {
measurements.add(SumDataPoint(attributes, 0L, now, value.toLong))
()
}
Comment on lines +112 to +116
)
} else {
val delayMs = math.min(retryBaseMillis * (1L << attempt), 30000L)
try Thread.sleep(delayMs)
catch { case _: InterruptedException => Thread.currentThread().interrupt() }
- Delete OtelSdk.scala, OtelSdkBuilder, OtelExample.scala (anti-blocks pattern)
- Refactor PlatformExecutor from singleton to class with create() factory
- Add macro-based log object with GlobalLogState for zero-cost logging
  - Scala 3: inline + scala.quoted macros
  - Scala 2: blackbox.Context macros
  - Captures file, class, method, line at compile time
  - Level check before argument evaluation
- Fix Oracle review issues:
  - OtlpJsonEncoder: surrogate pair escaping, log flags field emission
  - TracerProvider/LoggerProvider: null -> Option for contextStorage
  - BatchProcessor: accepts ScheduledExecutorService, retry delay capped at 30s
  - BranchCoverageSpec: fix span.isRecording ZIO test laziness
- Coverage: 93.83% stmt, 90.21% branch (exceeds 80/70 thresholds)
… cross-compile JVM+JS

- LogEnrichment[A] type class with compile-time macro summoning
  - Scala 3: inline varargs + Varargs extractor + Expr.summon (zero allocation)
  - Scala 2: blackbox.Context + c.inferImplicitValue
  - Built-in instances: String, Throwable, tuple attrs, Attributes, Severity
  - Zero cost: all enrichment code inside level guard
  - API: log.info("msg", "key" -> 42L, exception)
- Hierarchical per-class log levels via prefix-matching Map in GlobalLogState
  - setLevel/clearLevel/clearAllLevels with thread-safe CAS
  - Most-specific prefix wins
- log.annotated for scoped contextual annotations
  - ThreadLocal-backed on JVM, global var on JS
  - Scoped-only API, impossible to leak
- Cross-compiled otel module for JVM + JS
  - Converted from project to crossProject(JSPlatform, JVMPlatform)
  - JS ContextStorage + LogAnnotations implementations
  - SpanId/TraceId: ThreadLocalRandom -> scala.util.Random for cross-platform
  - 625 JVM tests + 448 JS tests pass
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants