summaryrefslogtreecommitdiff
path: root/runtime/ops
diff options
context:
space:
mode:
Diffstat (limited to 'runtime/ops')
-rw-r--r--runtime/ops/otel.rs297
1 files changed, 204 insertions, 93 deletions
diff --git a/runtime/ops/otel.rs b/runtime/ops/otel.rs
index b32764d7f..61a7b0ef0 100644
--- a/runtime/ops/otel.rs
+++ b/runtime/ops/otel.rs
@@ -1,8 +1,8 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
use crate::tokio_util::create_basic_runtime;
+use deno_core::anyhow;
use deno_core::anyhow::anyhow;
-use deno_core::anyhow::{self};
use deno_core::futures::channel::mpsc;
use deno_core::futures::channel::mpsc::UnboundedSender;
use deno_core::futures::future::BoxFuture;
@@ -23,7 +23,6 @@ use opentelemetry::trace::SpanKind;
use opentelemetry::trace::Status as SpanStatus;
use opentelemetry::trace::TraceFlags;
use opentelemetry::trace::TraceId;
-use opentelemetry::InstrumentationScope;
use opentelemetry::Key;
use opentelemetry::KeyValue;
use opentelemetry::StringValue;
@@ -63,11 +62,15 @@ deno_core::extension!(
deno_otel,
ops = [
op_otel_log,
+ op_otel_instrumentation_scope_create_and_enter,
+ op_otel_instrumentation_scope_enter,
+ op_otel_instrumentation_scope_enter_builtin,
op_otel_span_start,
op_otel_span_continue,
op_otel_span_attribute,
op_otel_span_attribute2,
op_otel_span_attribute3,
+ op_otel_span_set_dropped,
op_otel_span_flush,
],
);
@@ -303,6 +306,10 @@ mod hyper_client {
static OTEL_PROCESSORS: OnceCell<(SpanProcessor, LogProcessor)> =
OnceCell::new();
+static BUILT_IN_INSTRUMENTATION_SCOPE: OnceCell<
+ opentelemetry::InstrumentationScope,
+> = OnceCell::new();
+
pub fn init(config: OtelConfig) -> anyhow::Result<()> {
// Parse the `OTEL_EXPORTER_OTLP_PROTOCOL` variable. The opentelemetry_*
// crates don't do this automatically.
@@ -390,6 +397,14 @@ pub fn init(config: OtelConfig) -> anyhow::Result<()> {
.set((span_processor, log_processor))
.map_err(|_| anyhow!("failed to init otel"))?;
+ let builtin_instrumentation_scope =
+ opentelemetry::InstrumentationScope::builder("deno")
+ .with_version(config.runtime_version.clone())
+ .build();
+ BUILT_IN_INSTRUMENTATION_SCOPE
+ .set(builtin_instrumentation_scope)
+ .map_err(|_| anyhow!("failed to init otel"))?;
+
Ok(())
}
@@ -458,16 +473,160 @@ pub fn handle_log(record: &log::Record) {
log_processor.emit(
&mut log_record,
- &InstrumentationScope::builder("deno").build(),
+ BUILT_IN_INSTRUMENTATION_SCOPE.get().unwrap(),
);
}
+fn parse_trace_id(
+ scope: &mut v8::HandleScope<'_>,
+ trace_id: v8::Local<'_, v8::Value>,
+) -> TraceId {
+ if let Ok(string) = trace_id.try_cast() {
+ let value_view = v8::ValueView::new(scope, string);
+ match value_view.data() {
+ v8::ValueViewData::OneByte(bytes) => {
+ TraceId::from_hex(&String::from_utf8_lossy(bytes))
+ .unwrap_or(TraceId::INVALID)
+ }
+
+ _ => TraceId::INVALID,
+ }
+ } else if let Ok(uint8array) = trace_id.try_cast::<v8::Uint8Array>() {
+ let data = uint8array.data();
+ let byte_length = uint8array.byte_length();
+ if byte_length != 16 {
+ return TraceId::INVALID;
+ }
+ // SAFETY: We have ensured that the byte length is 16, so it is safe to
+ // cast the data to an array of 16 bytes.
+ let bytes = unsafe { &*(data as *const u8 as *const [u8; 16]) };
+ TraceId::from_bytes(*bytes)
+ } else {
+ TraceId::INVALID
+ }
+}
+
+fn parse_span_id(
+ scope: &mut v8::HandleScope<'_>,
+ span_id: v8::Local<'_, v8::Value>,
+) -> SpanId {
+ if let Ok(string) = span_id.try_cast() {
+ let value_view = v8::ValueView::new(scope, string);
+ match value_view.data() {
+ v8::ValueViewData::OneByte(bytes) => {
+ SpanId::from_hex(&String::from_utf8_lossy(bytes))
+ .unwrap_or(SpanId::INVALID)
+ }
+ _ => SpanId::INVALID,
+ }
+ } else if let Ok(uint8array) = span_id.try_cast::<v8::Uint8Array>() {
+ let data = uint8array.data();
+ let byte_length = uint8array.byte_length();
+ if byte_length != 8 {
+ return SpanId::INVALID;
+ }
+ // SAFETY: We have ensured that the byte length is 8, so it is safe to
+ // cast the data to an array of 8 bytes.
+ let bytes = unsafe { &*(data as *const u8 as *const [u8; 8]) };
+ SpanId::from_bytes(*bytes)
+ } else {
+ SpanId::INVALID
+ }
+}
+
+macro_rules! attr {
+ ($scope:ident, $attributes:expr $(=> $dropped_attributes_count:expr)?, $name:expr, $value:expr) => {
+ let name = if let Ok(name) = $name.try_cast() {
+ let view = v8::ValueView::new($scope, name);
+ match view.data() {
+ v8::ValueViewData::OneByte(bytes) => {
+ Some(String::from_utf8_lossy(bytes).into_owned())
+ }
+ v8::ValueViewData::TwoByte(bytes) => {
+ Some(String::from_utf16_lossy(bytes))
+ }
+ }
+ } else {
+ None
+ };
+ let value = if let Ok(string) = $value.try_cast::<v8::String>() {
+ Some(Value::String(StringValue::from({
+ let x = v8::ValueView::new($scope, string);
+ match x.data() {
+ v8::ValueViewData::OneByte(bytes) => {
+ String::from_utf8_lossy(bytes).into_owned()
+ }
+ v8::ValueViewData::TwoByte(bytes) => String::from_utf16_lossy(bytes),
+ }
+ })))
+ } else if let Ok(number) = $value.try_cast::<v8::Number>() {
+ Some(Value::F64(number.value()))
+ } else if let Ok(boolean) = $value.try_cast::<v8::Boolean>() {
+ Some(Value::Bool(boolean.is_true()))
+ } else if let Ok(bigint) = $value.try_cast::<v8::BigInt>() {
+ let (i64_value, _lossless) = bigint.i64_value();
+ Some(Value::I64(i64_value))
+ } else {
+ None
+ };
+ if let (Some(name), Some(value)) = (name, value) {
+ $attributes.push(KeyValue::new(name, value));
+ }
+ $(
+ else {
+ $dropped_attributes_count += 1;
+ }
+ )?
+ };
+}
+
+#[derive(Debug, Clone)]
+struct InstrumentationScope(opentelemetry::InstrumentationScope);
+
+impl deno_core::GarbageCollected for InstrumentationScope {}
+
+#[op2]
+#[cppgc]
+fn op_otel_instrumentation_scope_create_and_enter(
+ state: &mut OpState,
+ #[string] name: String,
+ #[string] version: Option<String>,
+ #[string] schema_url: Option<String>,
+) -> InstrumentationScope {
+ let mut builder = opentelemetry::InstrumentationScope::builder(name);
+ if let Some(version) = version {
+ builder = builder.with_version(version);
+ }
+ if let Some(schema_url) = schema_url {
+ builder = builder.with_schema_url(schema_url);
+ }
+ let scope = InstrumentationScope(builder.build());
+ state.put(scope.clone());
+ scope
+}
+
+#[op2(fast)]
+fn op_otel_instrumentation_scope_enter(
+ state: &mut OpState,
+ #[cppgc] scope: &InstrumentationScope,
+) {
+ state.put(scope.clone());
+}
+
+#[op2(fast)]
+fn op_otel_instrumentation_scope_enter_builtin(state: &mut OpState) {
+ state.put(InstrumentationScope(
+ BUILT_IN_INSTRUMENTATION_SCOPE.get().unwrap().clone(),
+ ));
+}
+
#[op2(fast)]
fn op_otel_log(
+ scope: &mut v8::HandleScope<'_>,
#[string] message: String,
#[smi] level: i32,
- #[string] trace_id: &str,
- #[string] span_id: &str,
+ trace_id: v8::Local<'_, v8::Value>,
+ span_id: v8::Local<'_, v8::Value>,
#[smi] trace_flags: u8,
) {
let Some((_, log_processor)) = OTEL_PROCESSORS.get() else {
@@ -483,15 +642,16 @@ fn op_otel_log(
3.. => Severity::Error,
};
+ let trace_id = parse_trace_id(scope, trace_id);
+ let span_id = parse_span_id(scope, span_id);
+
let mut log_record = LogRecord::default();
log_record.set_observed_timestamp(SystemTime::now());
log_record.set_body(message.into());
log_record.set_severity_number(severity);
log_record.set_severity_text(severity.name());
- if let (Ok(trace_id), Ok(span_id)) =
- (TraceId::from_hex(trace_id), SpanId::from_hex(span_id))
- {
+ if trace_id != TraceId::INVALID && span_id != SpanId::INVALID {
log_record.set_trace_context(
trace_id,
span_id,
@@ -501,7 +661,7 @@ fn op_otel_log(
log_processor.emit(
&mut log_record,
- &InstrumentationScope::builder("deno").build(),
+ BUILT_IN_INSTRUMENTATION_SCOPE.get().unwrap(),
);
}
@@ -527,40 +687,23 @@ fn op_otel_span_start<'s>(
span_processor.on_end(temporary_span.0);
};
- let trace_id = {
- let x = v8::ValueView::new(scope, trace_id.try_cast()?);
- match x.data() {
- v8::ValueViewData::OneByte(bytes) => {
- TraceId::from_hex(&String::from_utf8_lossy(bytes))?
- }
- _ => return Err(anyhow!("invalid trace_id")),
- }
+ let Some(InstrumentationScope(instrumentation_scope)) =
+ state.try_borrow::<InstrumentationScope>()
+ else {
+ return Err(anyhow!("instrumentation scope not available"));
};
- let span_id = {
- let x = v8::ValueView::new(scope, span_id.try_cast()?);
- match x.data() {
- v8::ValueViewData::OneByte(bytes) => {
- SpanId::from_hex(&String::from_utf8_lossy(bytes))?
- }
- _ => return Err(anyhow!("invalid span_id")),
- }
- };
+ let trace_id = parse_trace_id(scope, trace_id);
+ if trace_id == TraceId::INVALID {
+ return Err(anyhow!("invalid trace_id"));
+ }
- let parent_span_id = {
- let x = v8::ValueView::new(scope, parent_span_id.try_cast()?);
- match x.data() {
- v8::ValueViewData::OneByte(bytes) => {
- let s = String::from_utf8_lossy(bytes);
- if s.is_empty() {
- SpanId::INVALID
- } else {
- SpanId::from_hex(&s)?
- }
- }
- _ => return Err(anyhow!("invalid parent_span_id")),
- }
- };
+ let span_id = parse_span_id(scope, span_id);
+ if span_id == SpanId::INVALID {
+ return Err(anyhow!("invalid span_id"));
+ }
+
+ let parent_span_id = parse_span_id(scope, parent_span_id);
let name = {
let x = v8::ValueView::new(scope, name.try_cast()?);
@@ -601,7 +744,7 @@ fn op_otel_span_start<'s>(
events: Default::default(),
links: Default::default(),
status: SpanStatus::Unset,
- instrumentation_scope: InstrumentationScope::builder("deno").build(),
+ instrumentation_scope: instrumentation_scope.clone(),
});
state.put(temporary_span);
@@ -626,52 +769,6 @@ fn op_otel_span_continue(
}
}
-macro_rules! attr {
- ($scope:ident, $temporary_span:ident, $name:ident, $value:ident) => {
- let name = if let Ok(name) = $name.try_cast() {
- let view = v8::ValueView::new($scope, name);
- match view.data() {
- v8::ValueViewData::OneByte(bytes) => {
- Some(String::from_utf8_lossy(bytes).into_owned())
- }
- v8::ValueViewData::TwoByte(bytes) => {
- Some(String::from_utf16_lossy(bytes))
- }
- }
- } else {
- None
- };
- let value = if let Ok(string) = $value.try_cast::<v8::String>() {
- Some(Value::String(StringValue::from({
- let x = v8::ValueView::new($scope, string);
- match x.data() {
- v8::ValueViewData::OneByte(bytes) => {
- String::from_utf8_lossy(bytes).into_owned()
- }
- v8::ValueViewData::TwoByte(bytes) => String::from_utf16_lossy(bytes),
- }
- })))
- } else if let Ok(number) = $value.try_cast::<v8::Number>() {
- Some(Value::F64(number.value()))
- } else if let Ok(boolean) = $value.try_cast::<v8::Boolean>() {
- Some(Value::Bool(boolean.is_true()))
- } else if let Ok(bigint) = $value.try_cast::<v8::BigInt>() {
- let (i64_value, _lossless) = bigint.i64_value();
- Some(Value::I64(i64_value))
- } else {
- None
- };
- if let (Some(name), Some(value)) = (name, value) {
- $temporary_span
- .0
- .attributes
- .push(KeyValue::new(name, value));
- } else {
- $temporary_span.0.dropped_attributes_count += 1;
- }
- };
-}
-
#[op2(fast)]
fn op_otel_span_attribute<'s>(
scope: &mut v8::HandleScope<'s>,
@@ -684,7 +781,7 @@ fn op_otel_span_attribute<'s>(
temporary_span.0.attributes.reserve_exact(
(capacity as usize) - temporary_span.0.attributes.capacity(),
);
- attr!(scope, temporary_span, key, value);
+ attr!(scope, temporary_span.0.attributes => temporary_span.0.dropped_attributes_count, key, value);
}
}
@@ -702,8 +799,8 @@ fn op_otel_span_attribute2<'s>(
temporary_span.0.attributes.reserve_exact(
(capacity as usize) - temporary_span.0.attributes.capacity(),
);
- attr!(scope, temporary_span, key1, value1);
- attr!(scope, temporary_span, key2, value2);
+ attr!(scope, temporary_span.0.attributes => temporary_span.0.dropped_attributes_count, key1, value1);
+ attr!(scope, temporary_span.0.attributes => temporary_span.0.dropped_attributes_count, key2, value2);
}
}
@@ -724,9 +821,23 @@ fn op_otel_span_attribute3<'s>(
temporary_span.0.attributes.reserve_exact(
(capacity as usize) - temporary_span.0.attributes.capacity(),
);
- attr!(scope, temporary_span, key1, value1);
- attr!(scope, temporary_span, key2, value2);
- attr!(scope, temporary_span, key3, value3);
+ attr!(scope, temporary_span.0.attributes => temporary_span.0.dropped_attributes_count, key1, value1);
+ attr!(scope, temporary_span.0.attributes => temporary_span.0.dropped_attributes_count, key2, value2);
+ attr!(scope, temporary_span.0.attributes => temporary_span.0.dropped_attributes_count, key3, value3);
+ }
+}
+
+#[op2(fast)]
+fn op_otel_span_set_dropped(
+ state: &mut OpState,
+ #[smi] dropped_attributes_count: u32,
+ #[smi] dropped_links_count: u32,
+ #[smi] dropped_events_count: u32,
+) {
+ if let Some(temporary_span) = state.try_borrow_mut::<TemporarySpan>() {
+ temporary_span.0.dropped_attributes_count = dropped_attributes_count;
+ temporary_span.0.links.dropped_count = dropped_links_count;
+ temporary_span.0.events.dropped_count = dropped_events_count;
}
}