diff --git a/android/src/main/java/com/opentelemetry/OpenTelemetryModule.kt b/android/src/main/java/com/opentelemetry/OpenTelemetryModule.kt index 29d2b03..01c596b 100644 --- a/android/src/main/java/com/opentelemetry/OpenTelemetryModule.kt +++ b/android/src/main/java/com/opentelemetry/OpenTelemetryModule.kt @@ -1,32 +1,7 @@ package com.opentelemetry -import android.util.Log -import com.facebook.react.bridge.ReadableArray import com.facebook.react.bridge.ReactApplicationContext -import com.facebook.react.bridge.ReadableMap -import com.facebook.react.bridge.ReadableType import com.facebook.react.module.annotations.ReactModule -import io.opentelemetry.api.common.AttributeKey -import io.opentelemetry.api.common.Attributes -import io.opentelemetry.api.trace.SpanContext -import io.opentelemetry.api.trace.SpanKind -import io.opentelemetry.api.trace.StatusCode -import io.opentelemetry.api.trace.TraceFlags -import io.opentelemetry.api.trace.TraceState -import io.opentelemetry.sdk.common.InstrumentationLibraryInfo -import io.opentelemetry.sdk.common.InstrumentationScopeInfo -import io.opentelemetry.sdk.metrics.data.AggregationTemporality -import io.opentelemetry.sdk.metrics.data.Data -import io.opentelemetry.sdk.metrics.data.SumData -import io.opentelemetry.sdk.metrics.data.LongExemplarData -import io.opentelemetry.sdk.metrics.data.LongPointData -import io.opentelemetry.sdk.metrics.data.MetricData -import io.opentelemetry.sdk.metrics.data.MetricDataType -import io.opentelemetry.sdk.trace.ReadableSpan -import io.opentelemetry.sdk.trace.data.EventData -import io.opentelemetry.sdk.trace.data.LinkData -import io.opentelemetry.sdk.trace.data.SpanData -import io.opentelemetry.sdk.trace.data.StatusData @ReactModule(name = OpenTelemetryModule.NAME) class OpenTelemetryModule(reactContext: ReactApplicationContext) : @@ -36,288 +11,7 @@ class OpenTelemetryModule(reactContext: ReactApplicationContext) : return NAME } - override fun exportTraces(spans: ReadableArray) { - Log.d(NAME, "Spans size: ${spans.size()}") - - for (i in 0 until spans.size()) { - val spanMap = spans.getMap(i) ?: continue - val span = createReadableSpan(spanMap) - OpenTelemetry.spanProcessor?.onEnd(span) - } - } - - private fun createReadableSpan(rawSpan: ReadableMap): ReadableSpan { - val spanData = createSpanData(rawSpan) - - return object : ReadableSpan { - override fun getSpanContext() = spanData.spanContext - - override fun getParentSpanContext() = spanData.parentSpanContext - - override fun getName() = spanData.name - - override fun toSpanData() = spanData - - override fun getInstrumentationLibraryInfo() = spanData.instrumentationLibraryInfo - - override fun hasEnded() = spanData.hasEnded() - - override fun getKind() = spanData.kind - - override fun getAttribute(key: AttributeKey) = spanData.attributes.get(key) - - override fun getLatencyNanos() = spanData.endEpochNanos - spanData.startEpochNanos - } - } - - private fun createSpanData(rawSpan: ReadableMap): SpanData { - return object : SpanData { - override fun getName() = rawSpan.getString("name") ?: "unknown" - - override fun getResource() = OpenTelemetry.sdkResource - - override fun getKind(): SpanKind { - return rawSpan.getDouble("kind").let { SpanKind.entries.getOrNull(it.toInt()) } - ?: SpanKind.INTERNAL - } - - override fun getSpanContext(): SpanContext { - val ctx = rawSpan.getMap("spanContext") - return SpanContext.create( - ctx?.getString("traceId"), - ctx?.getString("spanId"), - TraceFlags.fromByte(ctx?.getInt("traceFlags")!!.toByte()), - TraceState.getDefault() - ) - } - - override fun getParentSpanContext(): SpanContext { - val ctx = rawSpan.getMap("spanContext") - return SpanContext.create( - ctx?.getString("traceId"), - rawSpan.getString("parentSpanId"), - TraceFlags.fromByte(ctx?.getInt("traceFlags")!!.toByte()), - TraceState.getDefault() - ) - } - - override fun getParentSpanId() = rawSpan.getString("parentSpanId") ?: "" - - override fun getStatus(): StatusData { - val status = rawSpan.getMap("status") - val code = - status?.getDouble("code")?.let { StatusCode.entries.getOrNull(it.toInt()) } - ?: StatusCode.UNSET - val description = status?.getString("description") - return StatusData.create(code, description) - } - - override fun getStartEpochNanos(): Long { - val startTime = rawSpan.getArray("startTime") ?: return 0 - return startTime.hrTimeToNanoseconds() - } - - override fun getAttributes(): Attributes { - val attributes = rawSpan.getMap("attributes") ?: return Attributes.empty() - return attributes.toOpenTelemetryAttributes() - } - - // TODO - override fun getEvents(): MutableList { - return mutableListOf() - } - - // TODO - override fun getLinks(): MutableList { - return mutableListOf() - } - - override fun getEndEpochNanos(): Long { - val endTime = rawSpan.getArray("endTime") ?: return 0 - return endTime.hrTimeToNanoseconds() - } - - override fun hasEnded() = rawSpan.getBoolean("ended") - - // TODO - override fun getTotalRecordedEvents() = 0 - - // TODO - override fun getTotalRecordedLinks() = 0 - - override fun getTotalAttributeCount() = getAttributes().size() - - override fun getInstrumentationScopeInfo(): InstrumentationScopeInfo { - val instrumentation = rawSpan.getMap("instrumentationScope") - val name = instrumentation?.getString("name") ?: "unknown" - val builder = InstrumentationScopeInfo.builder(name) - instrumentation?.getString("version")?.let { builder.setSchemaUrl(it) } - instrumentation?.getString("schemaUrl")?.let { builder.setSchemaUrl(it) } - return builder.build() - } - - override fun getInstrumentationLibraryInfo(): InstrumentationLibraryInfo { - val instrumentation = rawSpan.getMap("instrumentationScope") - val name = instrumentation?.getString("name") ?: "unknown" - val version = instrumentation?.getString("version") ?: "unknown" - return InstrumentationLibraryInfo.create(name, version) - } - } - } - - override fun exportMetrics(metrics: ReadableArray) { - Log.d(NAME, "Metrics size: ${metrics.size()}") - - val metricDataList = ArrayList() - - for (i in 0 until metrics.size()) { - val metricMap = metrics.getMap(i) ?: continue - val rawScope = metricMap.getMap("scope") ?: continue - val rawMetrics = metricMap.getArray("metrics") ?: continue - - for (y in 0 until rawMetrics.size()) { - val rawMetric = rawMetrics.getMap(i) ?: continue - val metricData = createMetricData(rawMetric, rawScope) - metricDataList.add(metricData) - } - } - - OpenTelemetry.logMetricExporter?.export(metricDataList) - OpenTelemetry.otlpMetricExporter?.export(metricDataList) - } - - private fun createMetricData(rawMetric: ReadableMap, rawScope: ReadableMap): MetricData { - return object : MetricData { - override fun getResource() = OpenTelemetry.sdkResource - - override fun getInstrumentationScopeInfo(): InstrumentationScopeInfo { - val name = rawScope.getString("name") ?: "unknown" - val builder = InstrumentationScopeInfo.builder(name) - rawScope.getString("version")?.let { builder.setVersion(it) } - return builder.build() - } - - override fun getName(): String { - val descriptor = rawMetric.getMap("descriptor") - return descriptor?.getString("name") ?: "unknown" - } - - override fun getDescription(): String { - val descriptor = rawMetric.getMap("descriptor") - return descriptor?.getString("description") ?: "" - } - - override fun getUnit(): String { - val descriptor = rawMetric.getMap("descriptor") - return descriptor?.getString("unit") ?: "" - } - - override fun getType(): MetricDataType { - // TODO: look into the `valueType` on the descriptor, we might need to support long/double types properly - return when (rawMetric.getInt("dataPointType")) { - 0 -> MetricDataType.HISTOGRAM - 1 -> MetricDataType.EXPONENTIAL_HISTOGRAM - 2 -> MetricDataType.LONG_GAUGE - 3 -> MetricDataType.LONG_SUM - else -> MetricDataType.SUMMARY - } - } - - override fun getData(): Data<*> { - val dataPoints = rawMetric.getArray("dataPoints") - val isMonotonic = rawMetric.getBoolean("isMonotonic") - val aggregationTemporality = when (rawMetric.getInt("aggregationTemporality")) { - 0 -> AggregationTemporality.DELTA - 1 -> AggregationTemporality.CUMULATIVE - else -> AggregationTemporality.CUMULATIVE - } - - when (type) { - MetricDataType.HISTOGRAM -> TODO() - MetricDataType.LONG_GAUGE -> TODO() - MetricDataType.DOUBLE_GAUGE -> TODO() - MetricDataType.LONG_SUM -> { - return object : SumData { - override fun getPoints(): MutableCollection { - val points = mutableListOf() - if (dataPoints == null) return points - - for (i in 0 until dataPoints.size()) { - val point = dataPoints.getMap(i) ?: continue - points.add(createLongPointData(point)) - } - - return points - } - - override fun isMonotonic() = isMonotonic - - override fun getAggregationTemporality() = aggregationTemporality - } - } - MetricDataType.DOUBLE_SUM -> TODO() - MetricDataType.SUMMARY -> TODO() - MetricDataType.EXPONENTIAL_HISTOGRAM -> TODO() - } - } - } - } - - private fun createLongPointData(point: ReadableMap): LongPointData { - return object : LongPointData { - override fun getValue(): Long { - return point.getDouble("value").toLong() - } - - override fun getAttributes(): Attributes { - val attributes = point.getMap("attributes") - return attributes?.toOpenTelemetryAttributes() ?: Attributes.empty() - } - - override fun getStartEpochNanos(): Long { - val startTime = point.getArray("startTime") ?: return 0 - return startTime.hrTimeToNanoseconds() - } - - override fun getEpochNanos(): Long { - val endTime = point.getArray("endTime") ?: return 0 - return endTime.hrTimeToNanoseconds() - } - - // Implement any other required methods from LongPointData - override fun getExemplars(): List { - // Return empty list if you don't have exemplars - return emptyList() - } - } - } - companion object { const val NAME = "OpenTelemetry" } } - -fun ReadableMap.toOpenTelemetryAttributes(): Attributes { - val builder = Attributes.builder() - val iterator = this.keySetIterator() - - while (iterator.hasNextKey()) { - val key = iterator.nextKey() - when (val type = this.getType(key)) { - ReadableType.String -> builder.put(key, this.getString(key) ?: "") - ReadableType.Number -> builder.put(key, this.getDouble(key)) - else -> TODO("Unsupported attribute type: $type") - } - } - - return builder.build() -} - -// Converts HrTime [seconds, nanoseconds] to total nanoseconds -fun ReadableArray.hrTimeToNanoseconds(): Long { - if (this.size() != 2) return 0L - - val seconds = this.getDouble(0).toLong() - val nanos = this.getDouble(1).toLong() - return seconds * 1_000_000_000 + nanos -} diff --git a/ios/OpenTelemetry.mm b/ios/OpenTelemetry.mm index 5e326b3..0446202 100644 --- a/ios/OpenTelemetry.mm +++ b/ios/OpenTelemetry.mm @@ -9,12 +9,4 @@ @implementation OpenTelemetry return std::make_shared(params); } -- (void)exportMetrics:(nonnull NSArray *)metrics { - // TODO -} - -- (void)exportTraces:(nonnull NSArray *)spans { - // TODO -} - @end diff --git a/src/NativeOpenTelemetry.ts b/src/NativeOpenTelemetry.ts index 4ac66ee..ad37dcb 100644 --- a/src/NativeOpenTelemetry.ts +++ b/src/NativeOpenTelemetry.ts @@ -1,24 +1,5 @@ -import type { Attributes, HrTime, SpanContext, SpanKind, SpanStatus } from "@opentelemetry/api"; -import type { InstrumentationScope } from '@opentelemetry/core'; -import type { ScopeMetrics } from "@opentelemetry/sdk-metrics"; import { TurboModuleRegistry, type TurboModule } from "react-native"; -export interface Spec extends TurboModule { - exportTraces( - spans: { - name: string; - spanContext: SpanContext; - status: SpanStatus; - startTime: HrTime; - endTime: HrTime; - kind: SpanKind; - attributes: Attributes; - parentSpanId?: string; - ended: boolean; - instrumentationScope: InstrumentationScope; - }[], - ): void; - exportMetrics(metrics: ScopeMetrics[]): void; -} +export interface Spec extends TurboModule {} export default TurboModuleRegistry.getEnforcing("OpenTelemetry"); diff --git a/src/index.tsx b/src/index.tsx index 99b67a8..55427ae 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -28,8 +28,6 @@ import { ATTR_SERVICE_VERSION, } from "@opentelemetry/semantic-conventions"; import type { Options } from "./types"; -import { NativeTraceExporter } from "./native-trace-exporter"; -import { NativeMetricExporter } from "./native-metric-exporter"; export function openTelemetrySDK(options: Options = {}) { console.log("SDK", { options }); @@ -52,9 +50,6 @@ export function openTelemetrySDK(options: Options = {}) { const logSpanProcessor = options.debug ? new BatchSpanProcessor(new ConsoleSpanExporter()) : null; - const nativeSpanProcessor = options.native - ? new BatchSpanProcessor(new NativeTraceExporter()) - : null; const otlpSpanProcessor = options.url ? new BatchSpanProcessor( new OTLPTraceExporter({ @@ -71,7 +66,6 @@ export function openTelemetrySDK(options: Options = {}) { spanProcessors: [ logSpanProcessor, otlpSpanProcessor, - nativeSpanProcessor, ].filter((processor) => processor !== null), }); @@ -100,11 +94,6 @@ export function openTelemetrySDK(options: Options = {}) { exporter: new ConsoleMetricExporter(), }) : null; - const nativeMetricReader = options.native - ? new PeriodicExportingMetricReader({ - exporter: new NativeMetricExporter(), - }) - : null; const otlpMetricReader = options.url ? new PeriodicExportingMetricReader({ exporter: new OTLPMetricExporter({ @@ -118,7 +107,7 @@ export function openTelemetrySDK(options: Options = {}) { const meterProvider = new MeterProvider({ resource, - readers: [logMetricReader, nativeMetricReader, otlpMetricReader].filter( + readers: [logMetricReader, otlpMetricReader].filter( (reader) => reader !== null ), }); diff --git a/src/native-metric-exporter.native.ts b/src/native-metric-exporter.native.ts deleted file mode 100644 index 592beea..0000000 --- a/src/native-metric-exporter.native.ts +++ /dev/null @@ -1,22 +0,0 @@ -import type { ExportResult } from "@opentelemetry/core"; -import type { - PushMetricExporter, - ResourceMetrics, -} from "@opentelemetry/sdk-metrics"; -import NATIVE from "./NativeOpenTelemetry"; - -export class NativeMetricExporter implements PushMetricExporter { - export(entry: ResourceMetrics, callback: (result: ExportResult) => void) { - console.log("Offloading metrics to the native SDK"); - NATIVE.exportMetrics(entry.scopeMetrics); - callback({ code: 0 }); - } - - forceFlush() { - return Promise.resolve(); - } - - shutdown() { - return Promise.resolve(); - } -} diff --git a/src/native-metric-exporter.ts b/src/native-metric-exporter.ts deleted file mode 100644 index dd4c504..0000000 --- a/src/native-metric-exporter.ts +++ /dev/null @@ -1,14 +0,0 @@ -import type { PushMetricExporter } from "@opentelemetry/sdk-metrics"; - -/* Noop implementation for Web */ -export class NativeMetricExporter implements PushMetricExporter { - export() {} - - forceFlush() { - return Promise.resolve(); - } - - shutdown() { - return Promise.resolve(); - } -} diff --git a/src/native-trace-exporter.native.ts b/src/native-trace-exporter.native.ts deleted file mode 100644 index 5769c3f..0000000 --- a/src/native-trace-exporter.native.ts +++ /dev/null @@ -1,32 +0,0 @@ -import type { ExportResult } from "@opentelemetry/core"; -import type { ReadableSpan, SpanExporter } from "@opentelemetry/sdk-trace-base"; -import NATIVE from "./NativeOpenTelemetry"; - -export class NativeTraceExporter implements SpanExporter { - export(spans: ReadableSpan[], callback: (result: ExportResult) => void) { - console.log("Offloading traces to the native SDK"); - NATIVE.exportTraces( - spans.map(span => ({ - name: span.name, - kind: span.kind, - status: span.status, - attributes: span.attributes, - spanContext: span.spanContext(), - parentSpanId: span.parentSpanContext?.spanId, - startTime: span.startTime, - endTime: span.endTime, - ended: span.ended, - instrumentationScope: span.instrumentationScope, - })) - ); - callback({ code: 0 }); - } - - forceFlush() { - return Promise.resolve(); - } - - shutdown() { - return Promise.resolve(); - } -} diff --git a/src/native-trace-exporter.ts b/src/native-trace-exporter.ts deleted file mode 100644 index 83cbc14..0000000 --- a/src/native-trace-exporter.ts +++ /dev/null @@ -1,14 +0,0 @@ -import type { SpanExporter } from "@opentelemetry/sdk-trace-base"; - -/* Noop implementation for Web */ -export class NativeTraceExporter implements SpanExporter { - export() {} - - forceFlush() { - return Promise.resolve(); - } - - shutdown() { - return Promise.resolve(); - } -}