mirror of
https://github.com/denoland/deno.git
synced 2025-03-04 09:57:11 -05:00
feat(http): add otel metrics (#28034)
Signed-off-by: Leo Kettmeir <crowlkats@toaxl.com> Co-authored-by: Luca Casonato <hello@lcas.dev>
This commit is contained in:
parent
f62fc9e81f
commit
bf79971c95
13 changed files with 1579 additions and 245 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -2045,6 +2045,7 @@ dependencies = [
|
||||||
"deno_core",
|
"deno_core",
|
||||||
"deno_error",
|
"deno_error",
|
||||||
"deno_net",
|
"deno_net",
|
||||||
|
"deno_telemetry",
|
||||||
"deno_websocket",
|
"deno_websocket",
|
||||||
"flate2",
|
"flate2",
|
||||||
"http 0.2.12",
|
"http 0.2.12",
|
||||||
|
|
|
@ -13,6 +13,7 @@ import {
|
||||||
op_http_close_after_finish,
|
op_http_close_after_finish,
|
||||||
op_http_get_request_headers,
|
op_http_get_request_headers,
|
||||||
op_http_get_request_method_and_url,
|
op_http_get_request_method_and_url,
|
||||||
|
op_http_metric_handle_otel_error,
|
||||||
op_http_read_request_body,
|
op_http_read_request_body,
|
||||||
op_http_request_on_cancel,
|
op_http_request_on_cancel,
|
||||||
op_http_serve,
|
op_http_serve,
|
||||||
|
@ -89,6 +90,7 @@ import { SymbolAsyncDispose } from "ext:deno_web/00_infra.js";
|
||||||
import {
|
import {
|
||||||
builtinTracer,
|
builtinTracer,
|
||||||
enterSpan,
|
enterSpan,
|
||||||
|
METRICS_ENABLED,
|
||||||
TRACING_ENABLED,
|
TRACING_ENABLED,
|
||||||
} from "ext:deno_telemetry/telemetry.ts";
|
} from "ext:deno_telemetry/telemetry.ts";
|
||||||
import {
|
import {
|
||||||
|
@ -573,6 +575,9 @@ function mapToCallback(context, callback, onError) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
if (METRICS_ENABLED) {
|
||||||
|
op_http_metric_handle_otel_error(req);
|
||||||
|
}
|
||||||
// deno-lint-ignore no-console
|
// deno-lint-ignore no-console
|
||||||
console.error("Exception in onError while handling exception", error);
|
console.error("Exception in onError while handling exception", error);
|
||||||
response = internalServerError();
|
response = internalServerError();
|
||||||
|
|
|
@ -30,6 +30,7 @@ cache_control.workspace = true
|
||||||
deno_core.workspace = true
|
deno_core.workspace = true
|
||||||
deno_error.workspace = true
|
deno_error.workspace = true
|
||||||
deno_net.workspace = true
|
deno_net.workspace = true
|
||||||
|
deno_telemetry.workspace = true
|
||||||
deno_websocket.workspace = true
|
deno_websocket.workspace = true
|
||||||
flate2.workspace = true
|
flate2.workspace = true
|
||||||
http.workspace = true
|
http.workspace = true
|
||||||
|
|
|
@ -270,8 +270,12 @@ pub async fn op_http_upgrade_websocket_next(
|
||||||
// Stage 1: set the response to 101 Switching Protocols and send it
|
// Stage 1: set the response to 101 Switching Protocols and send it
|
||||||
let upgrade = http.upgrade()?;
|
let upgrade = http.upgrade()?;
|
||||||
{
|
{
|
||||||
|
{
|
||||||
|
http.otel_info_set_status(StatusCode::SWITCHING_PROTOCOLS.as_u16());
|
||||||
|
}
|
||||||
let mut response_parts = http.response_parts();
|
let mut response_parts = http.response_parts();
|
||||||
response_parts.status = StatusCode::SWITCHING_PROTOCOLS;
|
response_parts.status = StatusCode::SWITCHING_PROTOCOLS;
|
||||||
|
|
||||||
for (name, value) in headers {
|
for (name, value) in headers {
|
||||||
response_parts.headers.append(
|
response_parts.headers.append(
|
||||||
HeaderName::from_bytes(&name).unwrap(),
|
HeaderName::from_bytes(&name).unwrap(),
|
||||||
|
@ -305,8 +309,11 @@ fn set_promise_complete(http: Rc<HttpRecord>, status: u16) {
|
||||||
// The Javascript code should never provide a status that is invalid here (see 23_response.js), so we
|
// The Javascript code should never provide a status that is invalid here (see 23_response.js), so we
|
||||||
// will quietly ignore invalid values.
|
// will quietly ignore invalid values.
|
||||||
if let Ok(code) = StatusCode::from_u16(status) {
|
if let Ok(code) = StatusCode::from_u16(status) {
|
||||||
|
{
|
||||||
http.response_parts().status = code;
|
http.response_parts().status = code;
|
||||||
}
|
}
|
||||||
|
http.otel_info_set_status(status);
|
||||||
|
}
|
||||||
http.complete();
|
http.complete();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -713,8 +720,11 @@ fn set_response(
|
||||||
// The Javascript code should never provide a status that is invalid here (see 23_response.js), so we
|
// The Javascript code should never provide a status that is invalid here (see 23_response.js), so we
|
||||||
// will quietly ignore invalid values.
|
// will quietly ignore invalid values.
|
||||||
if let Ok(code) = StatusCode::from_u16(status) {
|
if let Ok(code) = StatusCode::from_u16(status) {
|
||||||
|
{
|
||||||
http.response_parts().status = code;
|
http.response_parts().status = code;
|
||||||
}
|
}
|
||||||
|
http.otel_info_set_status(status);
|
||||||
|
}
|
||||||
} else if force_instantiate_body {
|
} else if force_instantiate_body {
|
||||||
response_fn(Compression::None).abort();
|
response_fn(Compression::None).abort();
|
||||||
}
|
}
|
||||||
|
@ -1404,3 +1414,12 @@ pub async fn op_raw_write_vectored(
|
||||||
let nwritten = resource.write_vectored(&buf1, &buf2).await?;
|
let nwritten = resource.write_vectored(&buf1, &buf2).await?;
|
||||||
Ok(nwritten)
|
Ok(nwritten)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[op2(fast)]
|
||||||
|
pub fn op_http_metric_handle_otel_error(external: *const c_void) {
|
||||||
|
let http =
|
||||||
|
// SAFETY: external is deleted before calling this op.
|
||||||
|
unsafe { take_external!(external, "op_http_metric_handle_otel_error") };
|
||||||
|
|
||||||
|
http.otel_info_set_error("user");
|
||||||
|
}
|
||||||
|
|
369
ext/http/lib.rs
369
ext/http/lib.rs
|
@ -53,6 +53,10 @@ use deno_core::ResourceId;
|
||||||
use deno_core::StringOrBuffer;
|
use deno_core::StringOrBuffer;
|
||||||
use deno_error::JsErrorBox;
|
use deno_error::JsErrorBox;
|
||||||
use deno_net::raw::NetworkStream;
|
use deno_net::raw::NetworkStream;
|
||||||
|
use deno_telemetry::Histogram;
|
||||||
|
use deno_telemetry::MeterProvider;
|
||||||
|
use deno_telemetry::UpDownCounter;
|
||||||
|
use deno_telemetry::OTEL_GLOBALS;
|
||||||
use deno_websocket::ws_create_server_stream;
|
use deno_websocket::ws_create_server_stream;
|
||||||
use flate2::write::GzEncoder;
|
use flate2::write::GzEncoder;
|
||||||
use flate2::Compression;
|
use flate2::Compression;
|
||||||
|
@ -70,6 +74,7 @@ use hyper_v014::Body;
|
||||||
use hyper_v014::HeaderMap;
|
use hyper_v014::HeaderMap;
|
||||||
use hyper_v014::Request;
|
use hyper_v014::Request;
|
||||||
use hyper_v014::Response;
|
use hyper_v014::Response;
|
||||||
|
use once_cell::sync::OnceCell;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use tokio::io::AsyncRead;
|
use tokio::io::AsyncRead;
|
||||||
use tokio::io::AsyncWrite;
|
use tokio::io::AsyncWrite;
|
||||||
|
@ -100,6 +105,15 @@ pub use request_properties::HttpRequestProperties;
|
||||||
pub use service::UpgradeUnavailableError;
|
pub use service::UpgradeUnavailableError;
|
||||||
pub use websocket_upgrade::WebSocketUpgradeError;
|
pub use websocket_upgrade::WebSocketUpgradeError;
|
||||||
|
|
||||||
|
struct OtelCollectors {
|
||||||
|
duration: Histogram<f64>,
|
||||||
|
active_requests: UpDownCounter<i64>,
|
||||||
|
request_size: Histogram<u64>,
|
||||||
|
response_size: Histogram<u64>,
|
||||||
|
}
|
||||||
|
|
||||||
|
static OTEL_COLLECTORS: OnceCell<OtelCollectors> = OnceCell::new();
|
||||||
|
|
||||||
#[derive(Debug, Default, Clone, Copy)]
|
#[derive(Debug, Default, Clone, Copy)]
|
||||||
pub struct Options {
|
pub struct Options {
|
||||||
/// By passing a hook function, the caller can customize various configuration
|
/// By passing a hook function, the caller can customize various configuration
|
||||||
|
@ -156,6 +170,7 @@ deno_core::extension!(
|
||||||
http_next::op_http_wait,
|
http_next::op_http_wait,
|
||||||
http_next::op_http_close,
|
http_next::op_http_close,
|
||||||
http_next::op_http_cancel,
|
http_next::op_http_cancel,
|
||||||
|
http_next::op_http_metric_handle_otel_error,
|
||||||
],
|
],
|
||||||
esm = ["00_serve.ts", "01_http.js", "02_websocket.ts"],
|
esm = ["00_serve.ts", "01_http.js", "02_websocket.ts"],
|
||||||
options = {
|
options = {
|
||||||
|
@ -231,6 +246,287 @@ impl From<tokio::net::unix::SocketAddr> for HttpSocketAddr {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct OtelInfo {
|
||||||
|
attributes: OtelInfoAttributes,
|
||||||
|
duration: Option<std::time::Instant>,
|
||||||
|
request_size: Option<u64>,
|
||||||
|
response_size: Option<u64>,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct OtelInfoAttributes {
|
||||||
|
http_request_method: Cow<'static, str>,
|
||||||
|
network_protocol_version: &'static str,
|
||||||
|
url_scheme: Cow<'static, str>,
|
||||||
|
server_address: Option<String>,
|
||||||
|
server_port: Option<i64>,
|
||||||
|
error_type: Option<&'static str>,
|
||||||
|
http_response_status_code: Option<i64>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl OtelInfoAttributes {
|
||||||
|
fn method(method: &http::method::Method) -> Cow<'static, str> {
|
||||||
|
use http::method::Method;
|
||||||
|
|
||||||
|
match *method {
|
||||||
|
Method::GET => Cow::Borrowed("GET"),
|
||||||
|
Method::POST => Cow::Borrowed("POST"),
|
||||||
|
Method::PUT => Cow::Borrowed("PUT"),
|
||||||
|
Method::DELETE => Cow::Borrowed("DELETE"),
|
||||||
|
Method::HEAD => Cow::Borrowed("HEAD"),
|
||||||
|
Method::OPTIONS => Cow::Borrowed("OPTIONS"),
|
||||||
|
Method::CONNECT => Cow::Borrowed("CONNECT"),
|
||||||
|
Method::PATCH => Cow::Borrowed("PATCH"),
|
||||||
|
Method::TRACE => Cow::Borrowed("TRACE"),
|
||||||
|
_ => Cow::Owned(method.to_string()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn method_v02(method: &http_v02::method::Method) -> Cow<'static, str> {
|
||||||
|
use http_v02::method::Method;
|
||||||
|
|
||||||
|
match *method {
|
||||||
|
Method::GET => Cow::Borrowed("GET"),
|
||||||
|
Method::POST => Cow::Borrowed("POST"),
|
||||||
|
Method::PUT => Cow::Borrowed("PUT"),
|
||||||
|
Method::DELETE => Cow::Borrowed("DELETE"),
|
||||||
|
Method::HEAD => Cow::Borrowed("HEAD"),
|
||||||
|
Method::OPTIONS => Cow::Borrowed("OPTIONS"),
|
||||||
|
Method::CONNECT => Cow::Borrowed("CONNECT"),
|
||||||
|
Method::PATCH => Cow::Borrowed("PATCH"),
|
||||||
|
Method::TRACE => Cow::Borrowed("TRACE"),
|
||||||
|
_ => Cow::Owned(method.to_string()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn version(version: http::Version) -> &'static str {
|
||||||
|
use http::Version;
|
||||||
|
|
||||||
|
match version {
|
||||||
|
Version::HTTP_09 => "0.9",
|
||||||
|
Version::HTTP_10 => "1.0",
|
||||||
|
Version::HTTP_11 => "1.1",
|
||||||
|
Version::HTTP_2 => "2",
|
||||||
|
Version::HTTP_3 => "3",
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn version_v02(version: http_v02::Version) -> &'static str {
|
||||||
|
use http_v02::Version;
|
||||||
|
|
||||||
|
match version {
|
||||||
|
Version::HTTP_09 => "0.9",
|
||||||
|
Version::HTTP_10 => "1.0",
|
||||||
|
Version::HTTP_11 => "1.1",
|
||||||
|
Version::HTTP_2 => "2",
|
||||||
|
Version::HTTP_3 => "3",
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn for_counter(&self) -> Vec<deno_telemetry::KeyValue> {
|
||||||
|
let mut attributes = vec![
|
||||||
|
deno_telemetry::KeyValue::new(
|
||||||
|
"http.request.method",
|
||||||
|
self.http_request_method.clone(),
|
||||||
|
),
|
||||||
|
deno_telemetry::KeyValue::new("url.scheme", self.url_scheme.clone()),
|
||||||
|
];
|
||||||
|
|
||||||
|
if let Some(address) = self.server_address.clone() {
|
||||||
|
attributes.push(deno_telemetry::KeyValue::new("server.address", address));
|
||||||
|
}
|
||||||
|
if let Some(port) = self.server_port {
|
||||||
|
attributes.push(deno_telemetry::KeyValue::new("server.port", port));
|
||||||
|
}
|
||||||
|
|
||||||
|
attributes
|
||||||
|
}
|
||||||
|
|
||||||
|
fn for_histogram(&self) -> Vec<deno_telemetry::KeyValue> {
|
||||||
|
let mut histogram_attributes = vec![
|
||||||
|
deno_telemetry::KeyValue::new(
|
||||||
|
"http.request.method",
|
||||||
|
self.http_request_method.clone(),
|
||||||
|
),
|
||||||
|
deno_telemetry::KeyValue::new("url.scheme", self.url_scheme.clone()),
|
||||||
|
deno_telemetry::KeyValue::new(
|
||||||
|
"network.protocol.version",
|
||||||
|
self.network_protocol_version,
|
||||||
|
),
|
||||||
|
];
|
||||||
|
|
||||||
|
if let Some(address) = self.server_address.clone() {
|
||||||
|
histogram_attributes
|
||||||
|
.push(deno_telemetry::KeyValue::new("server.address", address));
|
||||||
|
}
|
||||||
|
if let Some(port) = self.server_port {
|
||||||
|
histogram_attributes
|
||||||
|
.push(deno_telemetry::KeyValue::new("server.port", port));
|
||||||
|
}
|
||||||
|
if let Some(status_code) = self.http_response_status_code {
|
||||||
|
histogram_attributes.push(deno_telemetry::KeyValue::new(
|
||||||
|
"http.response.status_code",
|
||||||
|
status_code,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(error) = self.error_type {
|
||||||
|
histogram_attributes
|
||||||
|
.push(deno_telemetry::KeyValue::new("error.type", error));
|
||||||
|
}
|
||||||
|
|
||||||
|
histogram_attributes
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl OtelInfo {
|
||||||
|
fn new(
|
||||||
|
instant: std::time::Instant,
|
||||||
|
request_size: u64,
|
||||||
|
attributes: OtelInfoAttributes,
|
||||||
|
) -> Self {
|
||||||
|
let otel = OTEL_GLOBALS.get().unwrap();
|
||||||
|
let collectors = OTEL_COLLECTORS.get_or_init(|| {
|
||||||
|
let meter = otel
|
||||||
|
.meter_provider
|
||||||
|
.meter_with_scope(otel.builtin_instrumentation_scope.clone());
|
||||||
|
|
||||||
|
let duration = meter
|
||||||
|
.f64_histogram("http.server.request.duration")
|
||||||
|
.with_unit("s")
|
||||||
|
.with_description("Duration of HTTP server requests.")
|
||||||
|
.with_boundaries(vec![
|
||||||
|
0.005, 0.01, 0.025, 0.05, 0.075, 0.1, 0.25, 0.5, 0.75, 1.0, 2.5, 5.0,
|
||||||
|
7.5, 10.0,
|
||||||
|
])
|
||||||
|
.build();
|
||||||
|
|
||||||
|
let active_requests = meter
|
||||||
|
.i64_up_down_counter("http.server.active_requests")
|
||||||
|
.with_unit("{request}")
|
||||||
|
.with_description("Number of active HTTP server requests.")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
let request_size = meter
|
||||||
|
.u64_histogram("http.server.request.body.size")
|
||||||
|
.with_unit("By")
|
||||||
|
.with_description("Size of HTTP server request bodies.")
|
||||||
|
.with_boundaries(vec![
|
||||||
|
0.0,
|
||||||
|
100.0,
|
||||||
|
1000.0,
|
||||||
|
10000.0,
|
||||||
|
100000.0,
|
||||||
|
1000000.0,
|
||||||
|
10000000.0,
|
||||||
|
100000000.0,
|
||||||
|
1000000000.0,
|
||||||
|
])
|
||||||
|
.build();
|
||||||
|
|
||||||
|
let response_size = meter
|
||||||
|
.u64_histogram("http.server.response.body.size")
|
||||||
|
.with_unit("By")
|
||||||
|
.with_description("Size of HTTP server response bodies.")
|
||||||
|
.with_boundaries(vec![
|
||||||
|
0.0,
|
||||||
|
100.0,
|
||||||
|
1000.0,
|
||||||
|
10000.0,
|
||||||
|
100000.0,
|
||||||
|
1000000.0,
|
||||||
|
10000000.0,
|
||||||
|
100000000.0,
|
||||||
|
1000000000.0,
|
||||||
|
])
|
||||||
|
.build();
|
||||||
|
|
||||||
|
OtelCollectors {
|
||||||
|
duration,
|
||||||
|
active_requests,
|
||||||
|
request_size,
|
||||||
|
response_size,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
collectors.active_requests.add(1, &attributes.for_counter());
|
||||||
|
|
||||||
|
Self {
|
||||||
|
attributes,
|
||||||
|
duration: Some(instant),
|
||||||
|
request_size: Some(request_size),
|
||||||
|
response_size: Some(0),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_duration_and_request_size(&mut self) {
|
||||||
|
let collectors = OTEL_COLLECTORS.get().unwrap();
|
||||||
|
let attributes = self.attributes.for_histogram();
|
||||||
|
|
||||||
|
if let Some(duration) = self.duration.take() {
|
||||||
|
let duration = duration.elapsed();
|
||||||
|
collectors
|
||||||
|
.duration
|
||||||
|
.record(duration.as_secs_f64(), &attributes);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(request_size) = self.request_size.take() {
|
||||||
|
let collectors = OTEL_COLLECTORS.get().unwrap();
|
||||||
|
collectors.request_size.record(request_size, &attributes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for OtelInfo {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
let collectors = OTEL_COLLECTORS.get().unwrap();
|
||||||
|
|
||||||
|
self.handle_duration_and_request_size();
|
||||||
|
|
||||||
|
collectors
|
||||||
|
.active_requests
|
||||||
|
.add(-1, &self.attributes.for_counter());
|
||||||
|
|
||||||
|
if let Some(response_size) = self.response_size {
|
||||||
|
collectors
|
||||||
|
.response_size
|
||||||
|
.record(response_size, &self.attributes.for_histogram());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_error_otel(
|
||||||
|
otel: &Option<Rc<RefCell<Option<OtelInfo>>>>,
|
||||||
|
error: &HttpError,
|
||||||
|
) {
|
||||||
|
if let Some(otel) = otel.as_ref() {
|
||||||
|
let mut maybe_otel_info = otel.borrow_mut();
|
||||||
|
if let Some(otel_info) = maybe_otel_info.as_mut() {
|
||||||
|
otel_info.attributes.error_type = Some(match error {
|
||||||
|
HttpError::Resource(_) => "resource",
|
||||||
|
HttpError::Canceled(_) => "canceled",
|
||||||
|
HttpError::HyperV014(_) => "hyper",
|
||||||
|
HttpError::InvalidHeaderName(_) => "invalid header name",
|
||||||
|
HttpError::InvalidHeaderValue(_) => "invalid header value",
|
||||||
|
HttpError::Http(_) => "http",
|
||||||
|
HttpError::ResponseHeadersAlreadySent => {
|
||||||
|
"response headers already sent"
|
||||||
|
}
|
||||||
|
HttpError::ConnectionClosedWhileSendingResponse => {
|
||||||
|
"connection closed while sending response"
|
||||||
|
}
|
||||||
|
HttpError::AlreadyInUse => "already in use",
|
||||||
|
HttpError::Io(_) => "io",
|
||||||
|
HttpError::NoResponseHeaders => "no response headers",
|
||||||
|
HttpError::ResponseAlreadyCompleted => "response already completed",
|
||||||
|
HttpError::UpgradeBodyUsed => "upgrade body used",
|
||||||
|
HttpError::Other(_) => "unknown",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
struct HttpConnResource {
|
struct HttpConnResource {
|
||||||
addr: HttpSocketAddr,
|
addr: HttpSocketAddr,
|
||||||
scheme: &'static str,
|
scheme: &'static str,
|
||||||
|
@ -300,6 +596,8 @@ impl HttpConnResource {
|
||||||
let (request_tx, request_rx) = oneshot::channel();
|
let (request_tx, request_rx) = oneshot::channel();
|
||||||
let (response_tx, response_rx) = oneshot::channel();
|
let (response_tx, response_rx) = oneshot::channel();
|
||||||
|
|
||||||
|
let otel_instant = OTEL_GLOBALS.get().map(|_| std::time::Instant::now());
|
||||||
|
|
||||||
let acceptor = HttpAcceptor::new(request_tx, response_rx);
|
let acceptor = HttpAcceptor::new(request_tx, response_rx);
|
||||||
self.acceptors_tx.unbounded_send(acceptor).ok()?;
|
self.acceptors_tx.unbounded_send(acceptor).ok()?;
|
||||||
|
|
||||||
|
@ -317,11 +615,37 @@ impl HttpConnResource {
|
||||||
.unwrap_or(Encoding::Identity)
|
.unwrap_or(Encoding::Identity)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let otel_info = OTEL_GLOBALS.get().map(|_| {
|
||||||
|
let size_hint = request.size_hint();
|
||||||
|
Rc::new(RefCell::new(Some(OtelInfo::new(
|
||||||
|
otel_instant.unwrap(),
|
||||||
|
size_hint.upper().unwrap_or(size_hint.lower()),
|
||||||
|
OtelInfoAttributes {
|
||||||
|
http_request_method: OtelInfoAttributes::method_v02(
|
||||||
|
request.method(),
|
||||||
|
),
|
||||||
|
url_scheme: Cow::Borrowed(self.scheme),
|
||||||
|
network_protocol_version: OtelInfoAttributes::version_v02(
|
||||||
|
request.version(),
|
||||||
|
),
|
||||||
|
server_address: request.uri().host().map(|host| host.to_string()),
|
||||||
|
server_port: request.uri().port_u16().map(|port| port as i64),
|
||||||
|
error_type: Default::default(),
|
||||||
|
http_response_status_code: Default::default(),
|
||||||
|
},
|
||||||
|
))))
|
||||||
|
});
|
||||||
|
|
||||||
let method = request.method().to_string();
|
let method = request.method().to_string();
|
||||||
let url = req_url(&request, self.scheme, &self.addr);
|
let url = req_url(&request, self.scheme, &self.addr);
|
||||||
let read_stream = HttpStreamReadResource::new(self, request);
|
let read_stream =
|
||||||
let write_stream =
|
HttpStreamReadResource::new(self, request, otel_info.clone());
|
||||||
HttpStreamWriteResource::new(self, response_tx, accept_encoding);
|
let write_stream = HttpStreamWriteResource::new(
|
||||||
|
self,
|
||||||
|
response_tx,
|
||||||
|
accept_encoding,
|
||||||
|
otel_info,
|
||||||
|
);
|
||||||
Some((read_stream, write_stream, method, url))
|
Some((read_stream, write_stream, method, url))
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -438,22 +762,29 @@ pub struct HttpStreamReadResource {
|
||||||
pub rd: AsyncRefCell<HttpRequestReader>,
|
pub rd: AsyncRefCell<HttpRequestReader>,
|
||||||
cancel_handle: CancelHandle,
|
cancel_handle: CancelHandle,
|
||||||
size: SizeHint,
|
size: SizeHint,
|
||||||
|
otel_info: Option<Rc<RefCell<Option<OtelInfo>>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct HttpStreamWriteResource {
|
pub struct HttpStreamWriteResource {
|
||||||
conn: Rc<HttpConnResource>,
|
conn: Rc<HttpConnResource>,
|
||||||
wr: AsyncRefCell<HttpResponseWriter>,
|
wr: AsyncRefCell<HttpResponseWriter>,
|
||||||
accept_encoding: Encoding,
|
accept_encoding: Encoding,
|
||||||
|
otel_info: Option<Rc<RefCell<Option<OtelInfo>>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl HttpStreamReadResource {
|
impl HttpStreamReadResource {
|
||||||
fn new(conn: &Rc<HttpConnResource>, request: Request<Body>) -> Self {
|
fn new(
|
||||||
|
conn: &Rc<HttpConnResource>,
|
||||||
|
request: Request<Body>,
|
||||||
|
otel_info: Option<Rc<RefCell<Option<OtelInfo>>>>,
|
||||||
|
) -> Self {
|
||||||
let size = request.body().size_hint();
|
let size = request.body().size_hint();
|
||||||
Self {
|
Self {
|
||||||
_conn: conn.clone(),
|
_conn: conn.clone(),
|
||||||
rd: HttpRequestReader::Headers(request).into(),
|
rd: HttpRequestReader::Headers(request).into(),
|
||||||
size,
|
size,
|
||||||
cancel_handle: CancelHandle::new(),
|
cancel_handle: CancelHandle::new(),
|
||||||
|
otel_info,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -530,11 +861,13 @@ impl HttpStreamWriteResource {
|
||||||
conn: &Rc<HttpConnResource>,
|
conn: &Rc<HttpConnResource>,
|
||||||
response_tx: oneshot::Sender<Response<Body>>,
|
response_tx: oneshot::Sender<Response<Body>>,
|
||||||
accept_encoding: Encoding,
|
accept_encoding: Encoding,
|
||||||
|
otel_info: Option<Rc<RefCell<Option<OtelInfo>>>>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
conn: conn.clone(),
|
conn: conn.clone(),
|
||||||
wr: HttpResponseWriter::Headers(response_tx).into(),
|
wr: HttpResponseWriter::Headers(response_tx).into(),
|
||||||
accept_encoding,
|
accept_encoding,
|
||||||
|
otel_info,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -786,6 +1119,14 @@ async fn op_http_write_headers(
|
||||||
let (new_wr, body) = http_response(data, compressing, encoding)?;
|
let (new_wr, body) = http_response(data, compressing, encoding)?;
|
||||||
let body = builder.status(status).body(body)?;
|
let body = builder.status(status).body(body)?;
|
||||||
|
|
||||||
|
if let Some(otel) = stream.otel_info.as_ref() {
|
||||||
|
let mut otel = otel.borrow_mut();
|
||||||
|
if let Some(otel_info) = otel.as_mut() {
|
||||||
|
otel_info.attributes.http_response_status_code = Some(status as _);
|
||||||
|
otel_info.handle_duration_and_request_size();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let mut old_wr = RcRef::map(&stream, |r| &r.wr).borrow_mut().await;
|
let mut old_wr = RcRef::map(&stream, |r| &r.wr).borrow_mut().await;
|
||||||
let response_tx = match replace(&mut *old_wr, new_wr) {
|
let response_tx = match replace(&mut *old_wr, new_wr) {
|
||||||
HttpResponseWriter::Headers(response_tx) => response_tx,
|
HttpResponseWriter::Headers(response_tx) => response_tx,
|
||||||
|
@ -810,7 +1151,8 @@ fn op_http_headers(
|
||||||
let stream = state.resource_table.get::<HttpStreamReadResource>(rid)?;
|
let stream = state.resource_table.get::<HttpStreamReadResource>(rid)?;
|
||||||
let rd = RcRef::map(&stream, |r| &r.rd)
|
let rd = RcRef::map(&stream, |r| &r.rd)
|
||||||
.try_borrow()
|
.try_borrow()
|
||||||
.ok_or(HttpError::AlreadyInUse)?;
|
.ok_or(HttpError::AlreadyInUse)
|
||||||
|
.inspect_err(|e| handle_error_otel(&stream.otel_info, e))?;
|
||||||
match &*rd {
|
match &*rd {
|
||||||
HttpRequestReader::Headers(request) => Ok(req_headers(request.headers())),
|
HttpRequestReader::Headers(request) => Ok(req_headers(request.headers())),
|
||||||
HttpRequestReader::Body(headers, _) => Ok(req_headers(headers)),
|
HttpRequestReader::Body(headers, _) => Ok(req_headers(headers)),
|
||||||
|
@ -1025,6 +1367,15 @@ async fn op_http_write(
|
||||||
.get::<HttpStreamWriteResource>(rid)?;
|
.get::<HttpStreamWriteResource>(rid)?;
|
||||||
let mut wr = RcRef::map(&stream, |r| &r.wr).borrow_mut().await;
|
let mut wr = RcRef::map(&stream, |r| &r.wr).borrow_mut().await;
|
||||||
|
|
||||||
|
if let Some(otel) = stream.otel_info.as_ref() {
|
||||||
|
let mut maybe_otel_info = otel.borrow_mut();
|
||||||
|
if let Some(otel_info) = maybe_otel_info.as_mut() {
|
||||||
|
if let Some(response_size) = otel_info.response_size.as_mut() {
|
||||||
|
*response_size += buf.len() as u64;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
match &mut *wr {
|
match &mut *wr {
|
||||||
HttpResponseWriter::Headers(_) => Err(HttpError::NoResponseHeaders),
|
HttpResponseWriter::Headers(_) => Err(HttpError::NoResponseHeaders),
|
||||||
HttpResponseWriter::Closed => Err(HttpError::ResponseAlreadyCompleted),
|
HttpResponseWriter::Closed => Err(HttpError::ResponseAlreadyCompleted),
|
||||||
|
@ -1125,13 +1476,17 @@ async fn op_http_upgrade_websocket(
|
||||||
|
|
||||||
let request = match &mut *rd {
|
let request = match &mut *rd {
|
||||||
HttpRequestReader::Headers(request) => request,
|
HttpRequestReader::Headers(request) => request,
|
||||||
_ => return Err(HttpError::UpgradeBodyUsed),
|
_ => {
|
||||||
|
return Err(HttpError::UpgradeBodyUsed)
|
||||||
|
.inspect_err(|e| handle_error_otel(&stream.otel_info, e))
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let (transport, bytes) = extract_network_stream(
|
let (transport, bytes) = extract_network_stream(
|
||||||
hyper_v014::upgrade::on(request)
|
hyper_v014::upgrade::on(request)
|
||||||
.await
|
.await
|
||||||
.map_err(|err| HttpError::HyperV014(Arc::new(err)))?,
|
.map_err(|err| HttpError::HyperV014(Arc::new(err)))
|
||||||
|
.inspect_err(|e| handle_error_otel(&stream.otel_info, e))?,
|
||||||
);
|
);
|
||||||
Ok(ws_create_server_stream(
|
Ok(ws_create_server_stream(
|
||||||
&mut state.borrow_mut(),
|
&mut state.borrow_mut(),
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
// Copyright 2018-2025 the Deno authors. MIT license.
|
// Copyright 2018-2025 the Deno authors. MIT license.
|
||||||
|
|
||||||
|
use std::borrow::Cow;
|
||||||
use std::cell::Cell;
|
use std::cell::Cell;
|
||||||
use std::cell::Ref;
|
use std::cell::Ref;
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
|
@ -30,6 +32,8 @@ use tokio::sync::oneshot;
|
||||||
use crate::request_properties::HttpConnectionProperties;
|
use crate::request_properties::HttpConnectionProperties;
|
||||||
use crate::response_body::ResponseBytesInner;
|
use crate::response_body::ResponseBytesInner;
|
||||||
use crate::response_body::ResponseStreamResult;
|
use crate::response_body::ResponseStreamResult;
|
||||||
|
use crate::OtelInfo;
|
||||||
|
use crate::OtelInfoAttributes;
|
||||||
|
|
||||||
pub type Request = hyper::Request<Incoming>;
|
pub type Request = hyper::Request<Incoming>;
|
||||||
pub type Response = hyper::Response<HttpRecordResponse>;
|
pub type Response = hyper::Response<HttpRecordResponse>;
|
||||||
|
@ -184,12 +188,38 @@ pub(crate) async fn handle_request(
|
||||||
server_state: SignallingRc<HttpServerState>, // Keep server alive for duration of this future.
|
server_state: SignallingRc<HttpServerState>, // Keep server alive for duration of this future.
|
||||||
tx: tokio::sync::mpsc::Sender<Rc<HttpRecord>>,
|
tx: tokio::sync::mpsc::Sender<Rc<HttpRecord>>,
|
||||||
) -> Result<Response, hyper_v014::Error> {
|
) -> Result<Response, hyper_v014::Error> {
|
||||||
|
let otel_info = if deno_telemetry::OTEL_GLOBALS.get().is_some() {
|
||||||
|
let instant = std::time::Instant::now();
|
||||||
|
let size_hint = request.size_hint();
|
||||||
|
Some(OtelInfo::new(
|
||||||
|
instant,
|
||||||
|
size_hint.upper().unwrap_or(size_hint.lower()),
|
||||||
|
OtelInfoAttributes {
|
||||||
|
http_request_method: OtelInfoAttributes::method(request.method()),
|
||||||
|
url_scheme: request
|
||||||
|
.uri()
|
||||||
|
.scheme_str()
|
||||||
|
.map(|s| Cow::Owned(s.to_string()))
|
||||||
|
.unwrap_or_else(|| Cow::Borrowed("http")),
|
||||||
|
network_protocol_version: OtelInfoAttributes::version(
|
||||||
|
request.version(),
|
||||||
|
),
|
||||||
|
server_address: request.uri().host().map(|host| host.to_string()),
|
||||||
|
server_port: request.uri().port_u16().map(|port| port as i64),
|
||||||
|
error_type: Default::default(),
|
||||||
|
http_response_status_code: Default::default(),
|
||||||
|
},
|
||||||
|
))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
// If the underlying TCP connection is closed, this future will be dropped
|
// If the underlying TCP connection is closed, this future will be dropped
|
||||||
// and execution could stop at any await point.
|
// and execution could stop at any await point.
|
||||||
// The HttpRecord must live until JavaScript is done processing so is wrapped
|
// The HttpRecord must live until JavaScript is done processing so is wrapped
|
||||||
// in an Rc. The guard ensures unneeded resources are freed at cancellation.
|
// in an Rc. The guard ensures unneeded resources are freed at cancellation.
|
||||||
let guarded_record = guard(
|
let guarded_record = guard(
|
||||||
HttpRecord::new(request, request_info, server_state),
|
HttpRecord::new(request, request_info, server_state, otel_info),
|
||||||
HttpRecord::cancel,
|
HttpRecord::cancel,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -228,6 +258,7 @@ struct HttpRecordInner {
|
||||||
been_dropped: bool,
|
been_dropped: bool,
|
||||||
finished: bool,
|
finished: bool,
|
||||||
needs_close_after_finish: bool,
|
needs_close_after_finish: bool,
|
||||||
|
otel_info: Option<OtelInfo>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct HttpRecord(RefCell<Option<HttpRecordInner>>);
|
pub struct HttpRecord(RefCell<Option<HttpRecordInner>>);
|
||||||
|
@ -248,6 +279,7 @@ impl HttpRecord {
|
||||||
request: Request,
|
request: Request,
|
||||||
request_info: HttpConnectionProperties,
|
request_info: HttpConnectionProperties,
|
||||||
server_state: SignallingRc<HttpServerState>,
|
server_state: SignallingRc<HttpServerState>,
|
||||||
|
otel_info: Option<OtelInfo>,
|
||||||
) -> Rc<Self> {
|
) -> Rc<Self> {
|
||||||
let (request_parts, request_body) = request.into_parts();
|
let (request_parts, request_body) = request.into_parts();
|
||||||
let request_body = Some(request_body.into());
|
let request_body = Some(request_body.into());
|
||||||
|
@ -284,6 +316,7 @@ impl HttpRecord {
|
||||||
been_dropped: false,
|
been_dropped: false,
|
||||||
finished: false,
|
finished: false,
|
||||||
needs_close_after_finish: false,
|
needs_close_after_finish: false,
|
||||||
|
otel_info,
|
||||||
});
|
});
|
||||||
record
|
record
|
||||||
}
|
}
|
||||||
|
@ -487,6 +520,7 @@ impl HttpRecord {
|
||||||
) -> Poll<Self::Output> {
|
) -> Poll<Self::Output> {
|
||||||
let mut mut_self = self.0.self_mut();
|
let mut mut_self = self.0.self_mut();
|
||||||
if mut_self.response_ready {
|
if mut_self.response_ready {
|
||||||
|
mut_self.otel_info.take();
|
||||||
return Poll::Ready(());
|
return Poll::Ready(());
|
||||||
}
|
}
|
||||||
mut_self.response_waker = Some(cx.waker().clone());
|
mut_self.response_waker = Some(cx.waker().clone());
|
||||||
|
@ -523,6 +557,22 @@ impl HttpRecord {
|
||||||
|
|
||||||
HttpRecordFinished(self)
|
HttpRecordFinished(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn otel_info_set_status(&self, status: u16) {
|
||||||
|
let mut inner = self.self_mut();
|
||||||
|
if let Some(info) = inner.otel_info.as_mut() {
|
||||||
|
info.attributes.http_response_status_code = Some(status as _);
|
||||||
|
info.handle_duration_and_request_size();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn otel_info_set_error(&self, error: &'static str) {
|
||||||
|
let mut inner = self.self_mut();
|
||||||
|
if let Some(info) = inner.otel_info.as_mut() {
|
||||||
|
info.attributes.error_type = Some(error);
|
||||||
|
info.handle_duration_and_request_size();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[repr(transparent)]
|
#[repr(transparent)]
|
||||||
|
@ -579,6 +629,16 @@ impl Body for HttpRecordResponse {
|
||||||
}
|
}
|
||||||
record.take_response_body();
|
record.take_response_body();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let ResponseStreamResult::NonEmptyBuf(buf) = &res {
|
||||||
|
let mut http = self.0 .0.borrow_mut();
|
||||||
|
if let Some(otel_info) = &mut http.as_mut().unwrap().otel_info {
|
||||||
|
if let Some(response_size) = &mut otel_info.response_size {
|
||||||
|
*response_size += buf.len() as u64;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Poll::Ready(res.into())
|
Poll::Ready(res.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -38,8 +38,10 @@ use opentelemetry::logs::AnyValue;
|
||||||
use opentelemetry::logs::LogRecord as LogRecordTrait;
|
use opentelemetry::logs::LogRecord as LogRecordTrait;
|
||||||
use opentelemetry::logs::Severity;
|
use opentelemetry::logs::Severity;
|
||||||
use opentelemetry::metrics::AsyncInstrumentBuilder;
|
use opentelemetry::metrics::AsyncInstrumentBuilder;
|
||||||
|
pub use opentelemetry::metrics::Histogram;
|
||||||
use opentelemetry::metrics::InstrumentBuilder;
|
use opentelemetry::metrics::InstrumentBuilder;
|
||||||
use opentelemetry::metrics::MeterProvider as _;
|
pub use opentelemetry::metrics::MeterProvider;
|
||||||
|
pub use opentelemetry::metrics::UpDownCounter;
|
||||||
use opentelemetry::otel_debug;
|
use opentelemetry::otel_debug;
|
||||||
use opentelemetry::otel_error;
|
use opentelemetry::otel_error;
|
||||||
use opentelemetry::trace::Link;
|
use opentelemetry::trace::Link;
|
||||||
|
@ -51,10 +53,10 @@ use opentelemetry::trace::TraceFlags;
|
||||||
use opentelemetry::trace::TraceId;
|
use opentelemetry::trace::TraceId;
|
||||||
use opentelemetry::trace::TraceState;
|
use opentelemetry::trace::TraceState;
|
||||||
use opentelemetry::InstrumentationScope;
|
use opentelemetry::InstrumentationScope;
|
||||||
use opentelemetry::Key;
|
pub use opentelemetry::Key;
|
||||||
use opentelemetry::KeyValue;
|
pub use opentelemetry::KeyValue;
|
||||||
use opentelemetry::StringValue;
|
pub use opentelemetry::StringValue;
|
||||||
use opentelemetry::Value;
|
pub use opentelemetry::Value;
|
||||||
use opentelemetry_otlp::HttpExporterBuilder;
|
use opentelemetry_otlp::HttpExporterBuilder;
|
||||||
use opentelemetry_otlp::Protocol;
|
use opentelemetry_otlp::Protocol;
|
||||||
use opentelemetry_otlp::WithExportConfig;
|
use opentelemetry_otlp::WithExportConfig;
|
||||||
|
@ -196,7 +198,7 @@ fn otel_create_shared_runtime() -> UnboundedSender<BoxFuture<'static, ()>> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
struct OtelSharedRuntime;
|
pub struct OtelSharedRuntime;
|
||||||
|
|
||||||
impl hyper::rt::Executor<BoxFuture<'static, ()>> for OtelSharedRuntime {
|
impl hyper::rt::Executor<BoxFuture<'static, ()>> for OtelSharedRuntime {
|
||||||
fn execute(&self, fut: BoxFuture<'static, ()>) {
|
fn execute(&self, fut: BoxFuture<'static, ()>) {
|
||||||
|
@ -586,15 +588,16 @@ mod hyper_client {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct OtelGlobals {
|
#[derive(Debug)]
|
||||||
span_processor: BatchSpanProcessor<OtelSharedRuntime>,
|
pub struct OtelGlobals {
|
||||||
log_processor: BatchLogProcessor<OtelSharedRuntime>,
|
pub span_processor: BatchSpanProcessor<OtelSharedRuntime>,
|
||||||
id_generator: DenoIdGenerator,
|
pub log_processor: BatchLogProcessor<OtelSharedRuntime>,
|
||||||
meter_provider: SdkMeterProvider,
|
pub id_generator: DenoIdGenerator,
|
||||||
builtin_instrumentation_scope: InstrumentationScope,
|
pub meter_provider: SdkMeterProvider,
|
||||||
|
pub builtin_instrumentation_scope: InstrumentationScope,
|
||||||
}
|
}
|
||||||
|
|
||||||
static OTEL_GLOBALS: OnceCell<OtelGlobals> = OnceCell::new();
|
pub static OTEL_GLOBALS: OnceCell<OtelGlobals> = OnceCell::new();
|
||||||
|
|
||||||
pub fn init(
|
pub fn init(
|
||||||
rt_config: OtelRuntimeConfig,
|
rt_config: OtelRuntimeConfig,
|
||||||
|
@ -808,7 +811,7 @@ pub fn handle_log(record: &log::Record) {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
enum DenoIdGenerator {
|
pub enum DenoIdGenerator {
|
||||||
Random(RandomIdGenerator),
|
Random(RandomIdGenerator),
|
||||||
Deterministic {
|
Deterministic {
|
||||||
next_trace_id: AtomicU64,
|
next_trace_id: AtomicU64,
|
||||||
|
@ -1708,9 +1711,9 @@ impl OtelMeter {
|
||||||
|
|
||||||
enum Instrument {
|
enum Instrument {
|
||||||
Counter(opentelemetry::metrics::Counter<f64>),
|
Counter(opentelemetry::metrics::Counter<f64>),
|
||||||
UpDownCounter(opentelemetry::metrics::UpDownCounter<f64>),
|
UpDownCounter(UpDownCounter<f64>),
|
||||||
Gauge(opentelemetry::metrics::Gauge<f64>),
|
Gauge(opentelemetry::metrics::Gauge<f64>),
|
||||||
Histogram(opentelemetry::metrics::Histogram<f64>),
|
Histogram(Histogram<f64>),
|
||||||
Observable(Arc<Mutex<HashMap<Vec<KeyValue>, f64>>>),
|
Observable(Arc<Mutex<HashMap<Vec<KeyValue>, f64>>>),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -23,6 +23,13 @@
|
||||||
"args": "run -A main.ts metric.ts",
|
"args": "run -A main.ts metric.ts",
|
||||||
"output": "metric.out"
|
"output": "metric.out"
|
||||||
},
|
},
|
||||||
|
"http_metric": {
|
||||||
|
"envs": {
|
||||||
|
"OTEL_METRIC_EXPORT_INTERVAL": "1000"
|
||||||
|
},
|
||||||
|
"args": "run -A main.ts http_metric.ts",
|
||||||
|
"output": "http_metric.out"
|
||||||
|
},
|
||||||
"links": {
|
"links": {
|
||||||
"args": "run -A main.ts links.ts",
|
"args": "run -A main.ts links.ts",
|
||||||
"output": "links.out"
|
"output": "links.out"
|
||||||
|
|
|
@ -189,5 +189,259 @@
|
||||||
"spanId": "0000000000000004"
|
"spanId": "0000000000000004"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"metrics": []
|
"metrics": [
|
||||||
|
{
|
||||||
|
"name": "http.server.active_requests",
|
||||||
|
"description": "Number of active HTTP server requests.",
|
||||||
|
"unit": "{request}",
|
||||||
|
"metadata": [],
|
||||||
|
"sum": {
|
||||||
|
"dataPoints": [
|
||||||
|
{
|
||||||
|
"attributes": [
|
||||||
|
{
|
||||||
|
"key": "http.request.method",
|
||||||
|
"value": {
|
||||||
|
"stringValue": "GET"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "url.scheme",
|
||||||
|
"value": {
|
||||||
|
"stringValue": "http"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"startTimeUnixNano": "[WILDCARD]",
|
||||||
|
"timeUnixNano": "[WILDCARD]",
|
||||||
|
"exemplars": [],
|
||||||
|
"flags": 0,
|
||||||
|
"asInt": 0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"aggregationTemporality": 2,
|
||||||
|
"isMonotonic": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "http.server.request.body.size",
|
||||||
|
"description": "Size of HTTP server request bodies.",
|
||||||
|
"unit": "By",
|
||||||
|
"metadata": [],
|
||||||
|
"histogram": {
|
||||||
|
"dataPoints": [
|
||||||
|
{
|
||||||
|
"attributes": [
|
||||||
|
{
|
||||||
|
"key": "http.request.method",
|
||||||
|
"value": {
|
||||||
|
"stringValue": "GET"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "http.response.status_code",
|
||||||
|
"value": {
|
||||||
|
"intValue": "200"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "network.protocol.version",
|
||||||
|
"value": {
|
||||||
|
"stringValue": "1.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "url.scheme",
|
||||||
|
"value": {
|
||||||
|
"stringValue": "http"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"startTimeUnixNano": "[WILDCARD]",
|
||||||
|
"timeUnixNano": "[WILDCARD]",
|
||||||
|
"count": 1,
|
||||||
|
"sum": 0,
|
||||||
|
"bucketCounts": [
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"explicitBounds": [
|
||||||
|
0,
|
||||||
|
100,
|
||||||
|
1000,
|
||||||
|
10000,
|
||||||
|
100000,
|
||||||
|
1000000,
|
||||||
|
10000000,
|
||||||
|
100000000,
|
||||||
|
1000000000
|
||||||
|
],
|
||||||
|
"exemplars": [],
|
||||||
|
"flags": 0,
|
||||||
|
"min": 0,
|
||||||
|
"max": 0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"aggregationTemporality": 2
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "http.server.request.duration",
|
||||||
|
"description": "Duration of HTTP server requests.",
|
||||||
|
"unit": "s",
|
||||||
|
"metadata": [],
|
||||||
|
"histogram": {
|
||||||
|
"dataPoints": [
|
||||||
|
{
|
||||||
|
"attributes": [
|
||||||
|
{
|
||||||
|
"key": "http.request.method",
|
||||||
|
"value": {
|
||||||
|
"stringValue": "GET"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "http.response.status_code",
|
||||||
|
"value": {
|
||||||
|
"intValue": "200"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "network.protocol.version",
|
||||||
|
"value": {
|
||||||
|
"stringValue": "1.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "url.scheme",
|
||||||
|
"value": {
|
||||||
|
"stringValue": "http"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"startTimeUnixNano": "[WILDCARD]",
|
||||||
|
"timeUnixNano": "[WILDCARD]",
|
||||||
|
"count": 1,
|
||||||
|
"sum": [WILDCARD],
|
||||||
|
"bucketCounts": [
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"explicitBounds": [
|
||||||
|
0.005,
|
||||||
|
0.01,
|
||||||
|
0.025,
|
||||||
|
0.05,
|
||||||
|
0.075,
|
||||||
|
0.1,
|
||||||
|
0.25,
|
||||||
|
0.5,
|
||||||
|
0.75,
|
||||||
|
1,
|
||||||
|
2.5,
|
||||||
|
5,
|
||||||
|
7.5,
|
||||||
|
10
|
||||||
|
],
|
||||||
|
"exemplars": [],
|
||||||
|
"flags": 0,
|
||||||
|
"min": [WILDCARD],
|
||||||
|
"max": [WILDCARD]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"aggregationTemporality": 2
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "http.server.response.body.size",
|
||||||
|
"description": "Size of HTTP server response bodies.",
|
||||||
|
"unit": "By",
|
||||||
|
"metadata": [],
|
||||||
|
"histogram": {
|
||||||
|
"dataPoints": [
|
||||||
|
{
|
||||||
|
"attributes": [
|
||||||
|
{
|
||||||
|
"key": "http.request.method",
|
||||||
|
"value": {
|
||||||
|
"stringValue": "GET"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "http.response.status_code",
|
||||||
|
"value": {
|
||||||
|
"intValue": "200"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "network.protocol.version",
|
||||||
|
"value": {
|
||||||
|
"stringValue": "1.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "url.scheme",
|
||||||
|
"value": {
|
||||||
|
"stringValue": "http"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"startTimeUnixNano": "[WILDCARD]",
|
||||||
|
"timeUnixNano": "[WILDCARD]",
|
||||||
|
"count": 1,
|
||||||
|
"sum": 0,
|
||||||
|
"bucketCounts": [
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"explicitBounds": [
|
||||||
|
0,
|
||||||
|
100,
|
||||||
|
1000,
|
||||||
|
10000,
|
||||||
|
100000,
|
||||||
|
1000000,
|
||||||
|
10000000,
|
||||||
|
100000000,
|
||||||
|
1000000000
|
||||||
|
],
|
||||||
|
"exemplars": [],
|
||||||
|
"flags": 0,
|
||||||
|
"min": 0,
|
||||||
|
"max": 0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"aggregationTemporality": 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|
608
tests/specs/cli/otel_basic/http_metric.out
Normal file
608
tests/specs/cli/otel_basic/http_metric.out
Normal file
|
@ -0,0 +1,608 @@
|
||||||
|
{
|
||||||
|
"spans": [
|
||||||
|
{
|
||||||
|
"traceId": "00000000000000000000000000000001",
|
||||||
|
"spanId": "0000000000000001",
|
||||||
|
"traceState": "",
|
||||||
|
"parentSpanId": "",
|
||||||
|
"flags": 1,
|
||||||
|
"name": "GET",
|
||||||
|
"kind": 3,
|
||||||
|
"startTimeUnixNano": "[WILDCARD]",
|
||||||
|
"endTimeUnixNano": "[WILDCARD]",
|
||||||
|
"attributes": [
|
||||||
|
{
|
||||||
|
"key": "http.request.method",
|
||||||
|
"value": {
|
||||||
|
"stringValue": "GET"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "url.full",
|
||||||
|
"value": {
|
||||||
|
"stringValue": "http://localhost:8080/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "url.scheme",
|
||||||
|
"value": {
|
||||||
|
"stringValue": "http"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "url.path",
|
||||||
|
"value": {
|
||||||
|
"stringValue": "/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "url.query",
|
||||||
|
"value": {
|
||||||
|
"stringValue": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "http.response.status_code",
|
||||||
|
"value": {
|
||||||
|
"stringValue": "200"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"droppedAttributesCount": 0,
|
||||||
|
"events": [],
|
||||||
|
"droppedEventsCount": 0,
|
||||||
|
"links": [],
|
||||||
|
"droppedLinksCount": 0,
|
||||||
|
"status": {
|
||||||
|
"message": "",
|
||||||
|
"code": 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"traceId": "00000000000000000000000000000002",
|
||||||
|
"spanId": "0000000000000002",
|
||||||
|
"traceState": "",
|
||||||
|
"parentSpanId": "",
|
||||||
|
"flags": 1,
|
||||||
|
"name": "GET",
|
||||||
|
"kind": 2,
|
||||||
|
"startTimeUnixNano": "[WILDCARD]",
|
||||||
|
"endTimeUnixNano": "[WILDCARD]",
|
||||||
|
"attributes": [
|
||||||
|
{
|
||||||
|
"key": "http.request.method",
|
||||||
|
"value": {
|
||||||
|
"stringValue": "GET"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "url.full",
|
||||||
|
"value": {
|
||||||
|
"stringValue": "http://localhost:8080/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "url.scheme",
|
||||||
|
"value": {
|
||||||
|
"stringValue": "http"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "url.path",
|
||||||
|
"value": {
|
||||||
|
"stringValue": "/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "url.query",
|
||||||
|
"value": {
|
||||||
|
"stringValue": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "http.response.status_code",
|
||||||
|
"value": {
|
||||||
|
"stringValue": "200"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"droppedAttributesCount": 0,
|
||||||
|
"events": [],
|
||||||
|
"droppedEventsCount": 0,
|
||||||
|
"links": [],
|
||||||
|
"droppedLinksCount": 0,
|
||||||
|
"status": {
|
||||||
|
"message": "",
|
||||||
|
"code": 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"traceId": "00000000000000000000000000000001",
|
||||||
|
"spanId": "0000000000000003",
|
||||||
|
"traceState": "",
|
||||||
|
"parentSpanId": "0000000000000001",
|
||||||
|
"flags": 1,
|
||||||
|
"name": "GET",
|
||||||
|
"kind": 3,
|
||||||
|
"startTimeUnixNano": "[WILDCARD]",
|
||||||
|
"endTimeUnixNano": "[WILDCARD]",
|
||||||
|
"attributes": [
|
||||||
|
{
|
||||||
|
"key": "http.request.method",
|
||||||
|
"value": {
|
||||||
|
"stringValue": "GET"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "url.full",
|
||||||
|
"value": {
|
||||||
|
"stringValue": "http://localhost:8080/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "url.scheme",
|
||||||
|
"value": {
|
||||||
|
"stringValue": "http"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "url.path",
|
||||||
|
"value": {
|
||||||
|
"stringValue": "/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "url.query",
|
||||||
|
"value": {
|
||||||
|
"stringValue": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "http.response.status_code",
|
||||||
|
"value": {
|
||||||
|
"stringValue": "200"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"droppedAttributesCount": 0,
|
||||||
|
"events": [],
|
||||||
|
"droppedEventsCount": 0,
|
||||||
|
"links": [],
|
||||||
|
"droppedLinksCount": 0,
|
||||||
|
"status": {
|
||||||
|
"message": "",
|
||||||
|
"code": 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"traceId": "00000000000000000000000000000003",
|
||||||
|
"spanId": "0000000000000004",
|
||||||
|
"traceState": "",
|
||||||
|
"parentSpanId": "",
|
||||||
|
"flags": 1,
|
||||||
|
"name": "GET",
|
||||||
|
"kind": 2,
|
||||||
|
"startTimeUnixNano": "[WILDCARD]",
|
||||||
|
"endTimeUnixNano": "[WILDCARD]",
|
||||||
|
"attributes": [
|
||||||
|
{
|
||||||
|
"key": "http.request.method",
|
||||||
|
"value": {
|
||||||
|
"stringValue": "GET"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "url.full",
|
||||||
|
"value": {
|
||||||
|
"stringValue": "http://localhost:8080/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "url.scheme",
|
||||||
|
"value": {
|
||||||
|
"stringValue": "http"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "url.path",
|
||||||
|
"value": {
|
||||||
|
"stringValue": "/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "url.query",
|
||||||
|
"value": {
|
||||||
|
"stringValue": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "http.response.status_code",
|
||||||
|
"value": {
|
||||||
|
"stringValue": "200"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"droppedAttributesCount": 0,
|
||||||
|
"events": [],
|
||||||
|
"droppedEventsCount": 0,
|
||||||
|
"links": [],
|
||||||
|
"droppedLinksCount": 0,
|
||||||
|
"status": {
|
||||||
|
"message": "",
|
||||||
|
"code": 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"traceId": "00000000000000000000000000000001",
|
||||||
|
"spanId": "0000000000000005",
|
||||||
|
"traceState": "",
|
||||||
|
"parentSpanId": "0000000000000001",
|
||||||
|
"flags": 1,
|
||||||
|
"name": "GET",
|
||||||
|
"kind": 3,
|
||||||
|
"startTimeUnixNano": "[WILDCARD]",
|
||||||
|
"endTimeUnixNano": "[WILDCARD]",
|
||||||
|
"attributes": [
|
||||||
|
{
|
||||||
|
"key": "http.request.method",
|
||||||
|
"value": {
|
||||||
|
"stringValue": "GET"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "url.full",
|
||||||
|
"value": {
|
||||||
|
"stringValue": "http://localhost:8080/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "url.scheme",
|
||||||
|
"value": {
|
||||||
|
"stringValue": "http"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "url.path",
|
||||||
|
"value": {
|
||||||
|
"stringValue": "/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "url.query",
|
||||||
|
"value": {
|
||||||
|
"stringValue": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "http.response.status_code",
|
||||||
|
"value": {
|
||||||
|
"stringValue": "200"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"droppedAttributesCount": 0,
|
||||||
|
"events": [],
|
||||||
|
"droppedEventsCount": 0,
|
||||||
|
"links": [],
|
||||||
|
"droppedLinksCount": 0,
|
||||||
|
"status": {
|
||||||
|
"message": "",
|
||||||
|
"code": 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"traceId": "00000000000000000000000000000004",
|
||||||
|
"spanId": "0000000000000006",
|
||||||
|
"traceState": "",
|
||||||
|
"parentSpanId": "",
|
||||||
|
"flags": 1,
|
||||||
|
"name": "GET",
|
||||||
|
"kind": 2,
|
||||||
|
"startTimeUnixNano": "[WILDCARD]",
|
||||||
|
"endTimeUnixNano": "[WILDCARD]",
|
||||||
|
"attributes": [
|
||||||
|
{
|
||||||
|
"key": "http.request.method",
|
||||||
|
"value": {
|
||||||
|
"stringValue": "GET"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "url.full",
|
||||||
|
"value": {
|
||||||
|
"stringValue": "http://localhost:8080/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "url.scheme",
|
||||||
|
"value": {
|
||||||
|
"stringValue": "http"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "url.path",
|
||||||
|
"value": {
|
||||||
|
"stringValue": "/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "url.query",
|
||||||
|
"value": {
|
||||||
|
"stringValue": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "http.response.status_code",
|
||||||
|
"value": {
|
||||||
|
"stringValue": "200"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"droppedAttributesCount": 0,
|
||||||
|
"events": [],
|
||||||
|
"droppedEventsCount": 0,
|
||||||
|
"links": [],
|
||||||
|
"droppedLinksCount": 0,
|
||||||
|
"status": {
|
||||||
|
"message": "",
|
||||||
|
"code": 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"logs": [],
|
||||||
|
"metrics": [
|
||||||
|
{
|
||||||
|
"name": "http.server.active_requests",
|
||||||
|
"description": "Number of active HTTP server requests.",
|
||||||
|
"unit": "{request}",
|
||||||
|
"metadata": [],
|
||||||
|
"sum": {
|
||||||
|
"dataPoints": [
|
||||||
|
{
|
||||||
|
"attributes": [
|
||||||
|
{
|
||||||
|
"key": "http.request.method",
|
||||||
|
"value": {
|
||||||
|
"stringValue": "GET"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "url.scheme",
|
||||||
|
"value": {
|
||||||
|
"stringValue": "http"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"startTimeUnixNano": "[WILDCARD]",
|
||||||
|
"timeUnixNano": "[WILDCARD]",
|
||||||
|
"exemplars": [],
|
||||||
|
"flags": 0,
|
||||||
|
"asInt": 0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"aggregationTemporality": 2,
|
||||||
|
"isMonotonic": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "http.server.request.body.size",
|
||||||
|
"description": "Size of HTTP server request bodies.",
|
||||||
|
"unit": "By",
|
||||||
|
"metadata": [],
|
||||||
|
"histogram": {
|
||||||
|
"dataPoints": [
|
||||||
|
{
|
||||||
|
"attributes": [
|
||||||
|
{
|
||||||
|
"key": "http.request.method",
|
||||||
|
"value": {
|
||||||
|
"stringValue": "GET"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "http.response.status_code",
|
||||||
|
"value": {
|
||||||
|
"intValue": "200"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "network.protocol.version",
|
||||||
|
"value": {
|
||||||
|
"stringValue": "1.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "url.scheme",
|
||||||
|
"value": {
|
||||||
|
"stringValue": "http"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"startTimeUnixNano": "[WILDCARD]",
|
||||||
|
"timeUnixNano": "[WILDCARD]",
|
||||||
|
"count": 3,
|
||||||
|
"sum": 0,
|
||||||
|
"bucketCounts": [
|
||||||
|
3,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"explicitBounds": [
|
||||||
|
0,
|
||||||
|
100,
|
||||||
|
1000,
|
||||||
|
10000,
|
||||||
|
100000,
|
||||||
|
1000000,
|
||||||
|
10000000,
|
||||||
|
100000000,
|
||||||
|
1000000000
|
||||||
|
],
|
||||||
|
"exemplars": [],
|
||||||
|
"flags": 0,
|
||||||
|
"min": 0,
|
||||||
|
"max": 0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"aggregationTemporality": 2
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "http.server.request.duration",
|
||||||
|
"description": "Duration of HTTP server requests.",
|
||||||
|
"unit": "s",
|
||||||
|
"metadata": [],
|
||||||
|
"histogram": {
|
||||||
|
"dataPoints": [
|
||||||
|
{
|
||||||
|
"attributes": [
|
||||||
|
{
|
||||||
|
"key": "http.request.method",
|
||||||
|
"value": {
|
||||||
|
"stringValue": "GET"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "http.response.status_code",
|
||||||
|
"value": {
|
||||||
|
"intValue": "200"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "network.protocol.version",
|
||||||
|
"value": {
|
||||||
|
"stringValue": "1.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "url.scheme",
|
||||||
|
"value": {
|
||||||
|
"stringValue": "http"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"startTimeUnixNano": "[WILDCARD]",
|
||||||
|
"timeUnixNano": "[WILDCARD]",
|
||||||
|
"count": 3,
|
||||||
|
"sum": [WILDCARD],
|
||||||
|
"bucketCounts": [
|
||||||
|
3,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"explicitBounds": [
|
||||||
|
0.005,
|
||||||
|
0.01,
|
||||||
|
0.025,
|
||||||
|
0.05,
|
||||||
|
0.075,
|
||||||
|
0.1,
|
||||||
|
0.25,
|
||||||
|
0.5,
|
||||||
|
0.75,
|
||||||
|
1,
|
||||||
|
2.5,
|
||||||
|
5,
|
||||||
|
7.5,
|
||||||
|
10
|
||||||
|
],
|
||||||
|
"exemplars": [],
|
||||||
|
"flags": 0,
|
||||||
|
"min": [WILDCARD],
|
||||||
|
"max": [WILDCARD]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"aggregationTemporality": 2
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "http.server.response.body.size",
|
||||||
|
"description": "Size of HTTP server response bodies.",
|
||||||
|
"unit": "By",
|
||||||
|
"metadata": [],
|
||||||
|
"histogram": {
|
||||||
|
"dataPoints": [
|
||||||
|
{
|
||||||
|
"attributes": [
|
||||||
|
{
|
||||||
|
"key": "http.request.method",
|
||||||
|
"value": {
|
||||||
|
"stringValue": "GET"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "http.response.status_code",
|
||||||
|
"value": {
|
||||||
|
"intValue": "200"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "network.protocol.version",
|
||||||
|
"value": {
|
||||||
|
"stringValue": "1.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "url.scheme",
|
||||||
|
"value": {
|
||||||
|
"stringValue": "http"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"startTimeUnixNano": "[WILDCARD]",
|
||||||
|
"timeUnixNano": "[WILDCARD]",
|
||||||
|
"count": 3,
|
||||||
|
"sum": 0,
|
||||||
|
"bucketCounts": [
|
||||||
|
3,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"explicitBounds": [
|
||||||
|
0,
|
||||||
|
100,
|
||||||
|
1000,
|
||||||
|
10000,
|
||||||
|
100000,
|
||||||
|
1000000,
|
||||||
|
10000000,
|
||||||
|
100000000,
|
||||||
|
1000000000
|
||||||
|
],
|
||||||
|
"exemplars": [],
|
||||||
|
"flags": 0,
|
||||||
|
"min": 0,
|
||||||
|
"max": 0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"aggregationTemporality": 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
10
tests/specs/cli/otel_basic/http_metric.ts
Normal file
10
tests/specs/cli/otel_basic/http_metric.ts
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
const server = Deno.serve(
|
||||||
|
{ port: 8080, onListen: () => {} },
|
||||||
|
() => new Response("foo"),
|
||||||
|
);
|
||||||
|
|
||||||
|
for (let i = 0; i < 3; i++) {
|
||||||
|
await fetch(`http://localhost:8080`);
|
||||||
|
}
|
||||||
|
|
||||||
|
await server.shutdown();
|
|
@ -40,6 +40,17 @@ const server = Deno.serve(
|
||||||
data.spans.sort((a, b) =>
|
data.spans.sort((a, b) =>
|
||||||
Number(BigInt(`0x${a.spanId}`) - BigInt(`0x${b.spanId}`))
|
Number(BigInt(`0x${a.spanId}`) - BigInt(`0x${b.spanId}`))
|
||||||
);
|
);
|
||||||
|
data.metrics.sort((a, b) => a.name.localeCompare(b.name));
|
||||||
|
for (const metric of data.metrics) {
|
||||||
|
if ("histogram" in metric) {
|
||||||
|
for (const dataPoint of metric.histogram.dataPoints) {
|
||||||
|
dataPoint.attributes.sort((a, b) => {
|
||||||
|
return a.key.localeCompare(b.key);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
console.log(JSON.stringify(data, null, 2));
|
console.log(JSON.stringify(data, null, 2));
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
|
@ -18,8 +18,8 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"startTimeUnixNano": "[WILDCARD]",
|
"startTimeUnixNano": [WILDCARD],
|
||||||
"timeUnixNano": "[WILDCARD]",
|
"timeUnixNano": [WILDCARD],
|
||||||
"exemplars": [],
|
"exemplars": [],
|
||||||
"flags": 0,
|
"flags": 0,
|
||||||
"asDouble": 1
|
"asDouble": 1
|
||||||
|
@ -29,180 +29,6 @@
|
||||||
"isMonotonic": true
|
"isMonotonic": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"name": "up_down_counter",
|
|
||||||
"description": "Example of a UpDownCounter",
|
|
||||||
"unit": "",
|
|
||||||
"metadata": [],
|
|
||||||
"sum": {
|
|
||||||
"dataPoints": [
|
|
||||||
{
|
|
||||||
"attributes": [
|
|
||||||
{
|
|
||||||
"key": "attribute",
|
|
||||||
"value": {
|
|
||||||
"doubleValue": 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"startTimeUnixNano": "[WILDCARD]",
|
|
||||||
"timeUnixNano": "[WILDCARD]",
|
|
||||||
"exemplars": [],
|
|
||||||
"flags": 0,
|
|
||||||
"asDouble": -1
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"aggregationTemporality": 2,
|
|
||||||
"isMonotonic": false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "gauge",
|
|
||||||
"description": "Example of a Gauge",
|
|
||||||
"unit": "",
|
|
||||||
"metadata": [],
|
|
||||||
"gauge": {
|
|
||||||
"dataPoints": [
|
|
||||||
{
|
|
||||||
"attributes": [
|
|
||||||
{
|
|
||||||
"key": "attribute",
|
|
||||||
"value": {
|
|
||||||
"doubleValue": 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"startTimeUnixNano": "[WILDCARD]",
|
|
||||||
"timeUnixNano": "[WILDCARD]",
|
|
||||||
"exemplars": [],
|
|
||||||
"flags": 0,
|
|
||||||
"asDouble": 1
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "histogram",
|
|
||||||
"description": "Example of a Histogram",
|
|
||||||
"unit": "",
|
|
||||||
"metadata": [],
|
|
||||||
"histogram": {
|
|
||||||
"dataPoints": [
|
|
||||||
{
|
|
||||||
"attributes": [
|
|
||||||
{
|
|
||||||
"key": "attribute",
|
|
||||||
"value": {
|
|
||||||
"doubleValue": 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"startTimeUnixNano": "[WILDCARD]",
|
|
||||||
"timeUnixNano": "[WILDCARD]",
|
|
||||||
"count": 1,
|
|
||||||
"sum": 1,
|
|
||||||
"bucketCounts": [
|
|
||||||
0,
|
|
||||||
1,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
"explicitBounds": [
|
|
||||||
0,
|
|
||||||
5,
|
|
||||||
10,
|
|
||||||
25,
|
|
||||||
50,
|
|
||||||
75,
|
|
||||||
100,
|
|
||||||
250,
|
|
||||||
500,
|
|
||||||
750,
|
|
||||||
1000,
|
|
||||||
2500,
|
|
||||||
5000,
|
|
||||||
7500,
|
|
||||||
10000
|
|
||||||
],
|
|
||||||
"exemplars": [],
|
|
||||||
"flags": 0,
|
|
||||||
"min": 1,
|
|
||||||
"max": 1
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"aggregationTemporality": 2
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "observable_counter",
|
|
||||||
"description": "Example of a ObservableCounter",
|
|
||||||
"unit": "",
|
|
||||||
"metadata": [],
|
|
||||||
"sum": {
|
|
||||||
"dataPoints": [
|
|
||||||
{
|
|
||||||
"attributes": [],
|
|
||||||
"startTimeUnixNano": "[WILDCARD]",
|
|
||||||
"timeUnixNano": "[WILDCARD]",
|
|
||||||
"exemplars": [],
|
|
||||||
"flags": 0,
|
|
||||||
"asDouble": 1
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"aggregationTemporality": 2,
|
|
||||||
"isMonotonic": true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "observable_up_down_counter",
|
|
||||||
"description": "Example of a ObservableUpDownCounter",
|
|
||||||
"unit": "",
|
|
||||||
"metadata": [],
|
|
||||||
"sum": {
|
|
||||||
"dataPoints": [
|
|
||||||
{
|
|
||||||
"attributes": [],
|
|
||||||
"startTimeUnixNano": "[WILDCARD]",
|
|
||||||
"timeUnixNano": "[WILDCARD]",
|
|
||||||
"exemplars": [],
|
|
||||||
"flags": 0,
|
|
||||||
"asDouble": 1
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"aggregationTemporality": 2,
|
|
||||||
"isMonotonic": false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "observable_gauge",
|
|
||||||
"description": "Example of a ObservableGauge",
|
|
||||||
"unit": "",
|
|
||||||
"metadata": [],
|
|
||||||
"gauge": {
|
|
||||||
"dataPoints": [
|
|
||||||
{
|
|
||||||
"attributes": [],
|
|
||||||
"startTimeUnixNano": "[WILDCARD]",
|
|
||||||
"timeUnixNano": "[WILDCARD]",
|
|
||||||
"exemplars": [],
|
|
||||||
"flags": 0,
|
|
||||||
"asDouble": 1
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"name": "counter",
|
"name": "counter",
|
||||||
"description": "Example of a Counter",
|
"description": "Example of a Counter",
|
||||||
|
@ -219,8 +45,8 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"startTimeUnixNano": "[WILDCARD]",
|
"startTimeUnixNano": [WILDCARD],
|
||||||
"timeUnixNano": "[WILDCARD]",
|
"timeUnixNano": [WILDCARD],
|
||||||
"exemplars": [],
|
"exemplars": [],
|
||||||
"flags": 0,
|
"flags": 0,
|
||||||
"asDouble": 1
|
"asDouble": 1
|
||||||
|
@ -230,33 +56,6 @@
|
||||||
"isMonotonic": true
|
"isMonotonic": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"name": "up_down_counter",
|
|
||||||
"description": "Example of a UpDownCounter",
|
|
||||||
"unit": "",
|
|
||||||
"metadata": [],
|
|
||||||
"sum": {
|
|
||||||
"dataPoints": [
|
|
||||||
{
|
|
||||||
"attributes": [
|
|
||||||
{
|
|
||||||
"key": "attribute",
|
|
||||||
"value": {
|
|
||||||
"doubleValue": 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"startTimeUnixNano": "[WILDCARD]",
|
|
||||||
"timeUnixNano": "[WILDCARD]",
|
|
||||||
"exemplars": [],
|
|
||||||
"flags": 0,
|
|
||||||
"asDouble": -1
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"aggregationTemporality": 2,
|
|
||||||
"isMonotonic": false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"name": "gauge",
|
"name": "gauge",
|
||||||
"description": "Example of a Gauge",
|
"description": "Example of a Gauge",
|
||||||
|
@ -273,8 +72,33 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"startTimeUnixNano": "[WILDCARD]",
|
"startTimeUnixNano": [WILDCARD],
|
||||||
"timeUnixNano": "[WILDCARD]",
|
"timeUnixNano": [WILDCARD],
|
||||||
|
"exemplars": [],
|
||||||
|
"flags": 0,
|
||||||
|
"asDouble": 1
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "gauge",
|
||||||
|
"description": "Example of a Gauge",
|
||||||
|
"unit": "",
|
||||||
|
"metadata": [],
|
||||||
|
"gauge": {
|
||||||
|
"dataPoints": [
|
||||||
|
{
|
||||||
|
"attributes": [
|
||||||
|
{
|
||||||
|
"key": "attribute",
|
||||||
|
"value": {
|
||||||
|
"doubleValue": 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"startTimeUnixNano": [WILDCARD],
|
||||||
|
"timeUnixNano": [WILDCARD],
|
||||||
"exemplars": [],
|
"exemplars": [],
|
||||||
"flags": 0,
|
"flags": 0,
|
||||||
"asDouble": 1
|
"asDouble": 1
|
||||||
|
@ -298,8 +122,72 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"startTimeUnixNano": "[WILDCARD]",
|
"startTimeUnixNano": [WILDCARD],
|
||||||
"timeUnixNano": "[WILDCARD]",
|
"timeUnixNano": [WILDCARD],
|
||||||
|
"count": 1,
|
||||||
|
"sum": 1,
|
||||||
|
"bucketCounts": [
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"explicitBounds": [
|
||||||
|
0,
|
||||||
|
5,
|
||||||
|
10,
|
||||||
|
25,
|
||||||
|
50,
|
||||||
|
75,
|
||||||
|
100,
|
||||||
|
250,
|
||||||
|
500,
|
||||||
|
750,
|
||||||
|
1000,
|
||||||
|
2500,
|
||||||
|
5000,
|
||||||
|
7500,
|
||||||
|
10000
|
||||||
|
],
|
||||||
|
"exemplars": [],
|
||||||
|
"flags": 0,
|
||||||
|
"min": 1,
|
||||||
|
"max": 1
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"aggregationTemporality": 2
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "histogram",
|
||||||
|
"description": "Example of a Histogram",
|
||||||
|
"unit": "",
|
||||||
|
"metadata": [],
|
||||||
|
"histogram": {
|
||||||
|
"dataPoints": [
|
||||||
|
{
|
||||||
|
"attributes": [
|
||||||
|
{
|
||||||
|
"key": "attribute",
|
||||||
|
"value": {
|
||||||
|
"doubleValue": 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"startTimeUnixNano": [WILDCARD],
|
||||||
|
"timeUnixNano": [WILDCARD],
|
||||||
"count": 1,
|
"count": 1,
|
||||||
"sum": 1,
|
"sum": 1,
|
||||||
"bucketCounts": [
|
"bucketCounts": [
|
||||||
|
@ -355,8 +243,8 @@
|
||||||
"dataPoints": [
|
"dataPoints": [
|
||||||
{
|
{
|
||||||
"attributes": [],
|
"attributes": [],
|
||||||
"startTimeUnixNano": "[WILDCARD]",
|
"startTimeUnixNano": [WILDCARD],
|
||||||
"timeUnixNano": "[WILDCARD]",
|
"timeUnixNano": [WILDCARD],
|
||||||
"exemplars": [],
|
"exemplars": [],
|
||||||
"flags": 0,
|
"flags": 0,
|
||||||
"asDouble": 1
|
"asDouble": 1
|
||||||
|
@ -367,23 +255,23 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "observable_up_down_counter",
|
"name": "observable_counter",
|
||||||
"description": "Example of a ObservableUpDownCounter",
|
"description": "Example of a ObservableCounter",
|
||||||
"unit": "",
|
"unit": "",
|
||||||
"metadata": [],
|
"metadata": [],
|
||||||
"sum": {
|
"sum": {
|
||||||
"dataPoints": [
|
"dataPoints": [
|
||||||
{
|
{
|
||||||
"attributes": [],
|
"attributes": [],
|
||||||
"startTimeUnixNano": "[WILDCARD]",
|
"startTimeUnixNano": [WILDCARD],
|
||||||
"timeUnixNano": "[WILDCARD]",
|
"timeUnixNano": [WILDCARD],
|
||||||
"exemplars": [],
|
"exemplars": [],
|
||||||
"flags": 0,
|
"flags": 0,
|
||||||
"asDouble": 1
|
"asDouble": 1
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"aggregationTemporality": 2,
|
"aggregationTemporality": 2,
|
||||||
"isMonotonic": false
|
"isMonotonic": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -395,14 +283,126 @@
|
||||||
"dataPoints": [
|
"dataPoints": [
|
||||||
{
|
{
|
||||||
"attributes": [],
|
"attributes": [],
|
||||||
"startTimeUnixNano": "[WILDCARD]",
|
"startTimeUnixNano": [WILDCARD],
|
||||||
"timeUnixNano": "[WILDCARD]",
|
"timeUnixNano": [WILDCARD],
|
||||||
"exemplars": [],
|
"exemplars": [],
|
||||||
"flags": 0,
|
"flags": 0,
|
||||||
"asDouble": 1
|
"asDouble": 1
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "observable_gauge",
|
||||||
|
"description": "Example of a ObservableGauge",
|
||||||
|
"unit": "",
|
||||||
|
"metadata": [],
|
||||||
|
"gauge": {
|
||||||
|
"dataPoints": [
|
||||||
|
{
|
||||||
|
"attributes": [],
|
||||||
|
"startTimeUnixNano": [WILDCARD],
|
||||||
|
"timeUnixNano": [WILDCARD],
|
||||||
|
"exemplars": [],
|
||||||
|
"flags": 0,
|
||||||
|
"asDouble": 1
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "observable_up_down_counter",
|
||||||
|
"description": "Example of a ObservableUpDownCounter",
|
||||||
|
"unit": "",
|
||||||
|
"metadata": [],
|
||||||
|
"sum": {
|
||||||
|
"dataPoints": [
|
||||||
|
{
|
||||||
|
"attributes": [],
|
||||||
|
"startTimeUnixNano": [WILDCARD],
|
||||||
|
"timeUnixNano": [WILDCARD],
|
||||||
|
"exemplars": [],
|
||||||
|
"flags": 0,
|
||||||
|
"asDouble": 1
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"aggregationTemporality": 2,
|
||||||
|
"isMonotonic": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "observable_up_down_counter",
|
||||||
|
"description": "Example of a ObservableUpDownCounter",
|
||||||
|
"unit": "",
|
||||||
|
"metadata": [],
|
||||||
|
"sum": {
|
||||||
|
"dataPoints": [
|
||||||
|
{
|
||||||
|
"attributes": [],
|
||||||
|
"startTimeUnixNano": [WILDCARD],
|
||||||
|
"timeUnixNano": [WILDCARD],
|
||||||
|
"exemplars": [],
|
||||||
|
"flags": 0,
|
||||||
|
"asDouble": 1
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"aggregationTemporality": 2,
|
||||||
|
"isMonotonic": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "up_down_counter",
|
||||||
|
"description": "Example of a UpDownCounter",
|
||||||
|
"unit": "",
|
||||||
|
"metadata": [],
|
||||||
|
"sum": {
|
||||||
|
"dataPoints": [
|
||||||
|
{
|
||||||
|
"attributes": [
|
||||||
|
{
|
||||||
|
"key": "attribute",
|
||||||
|
"value": {
|
||||||
|
"doubleValue": 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"startTimeUnixNano": [WILDCARD],
|
||||||
|
"timeUnixNano": [WILDCARD],
|
||||||
|
"exemplars": [],
|
||||||
|
"flags": 0,
|
||||||
|
"asDouble": -1
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"aggregationTemporality": 2,
|
||||||
|
"isMonotonic": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "up_down_counter",
|
||||||
|
"description": "Example of a UpDownCounter",
|
||||||
|
"unit": "",
|
||||||
|
"metadata": [],
|
||||||
|
"sum": {
|
||||||
|
"dataPoints": [
|
||||||
|
{
|
||||||
|
"attributes": [
|
||||||
|
{
|
||||||
|
"key": "attribute",
|
||||||
|
"value": {
|
||||||
|
"doubleValue": 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"startTimeUnixNano": [WILDCARD],
|
||||||
|
"timeUnixNano": [WILDCARD],
|
||||||
|
"exemplars": [],
|
||||||
|
"flags": 0,
|
||||||
|
"asDouble": -1
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"aggregationTemporality": 2,
|
||||||
|
"isMonotonic": false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue