наблюдаемость: ввести безопасный контракт метрик
CI / Rust Checks (pull_request) Successful in 6m15s
CI / UI Checks (pull_request) Successful in 5s
CI / Community Image Smoke (pull_request) Successful in 4m25s
CI / Frontend E2E (pull_request) Successful in 5m17s
CI / Deploy (pull_request) Has been skipped
CI / Rust Checks (push) Successful in 6m9s
CI / UI Checks (push) Successful in 5s
CI / Community Image Smoke (push) Successful in 1m3s
CI / Frontend E2E (push) Successful in 3m47s
CI / Deploy (push) Failing after 3s

This commit is contained in:
2026-07-31 05:04:01 +03:00
parent ec2453c00f
commit 9b1a739e39
50 changed files with 3066 additions and 433 deletions
+1 -1
View File
@@ -9,9 +9,9 @@ version.workspace = true
[dependencies]
async-trait = "0.1"
crank-core = { path = "../crank-core" }
crank-metrics = { path = "../crank-metrics" }
crank-trace = { path = "../crank-trace" }
futures-util = "0.3"
metrics.workspace = true
opentelemetry.workspace = true
reqwest = { workspace = true, features = ["stream"] }
serde.workspace = true
+16 -26
View File
@@ -7,6 +7,7 @@ use std::{
};
use crank_core::{HttpMethod, RestTarget};
use crank_metrics::{UpstreamOperationKind, UpstreamOutcome, UpstreamRequestMetrics};
use crank_trace::{ErrorCategory, Stage, StageOutcome};
use futures_util::StreamExt;
use opentelemetry::{global, propagation::Injector, trace::TraceContextExt};
@@ -71,24 +72,13 @@ impl RestAdapter {
target: &RestTarget,
request: &RestRequest,
) -> Result<RestResponse, RestAdapterError> {
let started_at = std::time::Instant::now();
let request_metrics = UpstreamRequestMetrics::start(UpstreamOperationKind::Rest);
let result = self.execute_inner(target, request).await;
let outcome = match &result {
Ok(_) => "success",
Ok(_) => UpstreamOutcome::Success,
Err(error) => upstream_outcome(error),
};
metrics::counter!(
"crank_upstream_requests_total",
"operation_kind" => "rest",
"outcome" => outcome
)
.increment(1);
metrics::histogram!(
"crank_upstream_request_duration_seconds",
"operation_kind" => "rest",
"outcome" => outcome
)
.record(started_at.elapsed().as_secs_f64());
request_metrics.complete(outcome);
result
}
@@ -149,27 +139,27 @@ impl RestAdapter {
}
}
fn upstream_outcome(error: &RestAdapterError) -> &'static str {
fn upstream_outcome(error: &RestAdapterError) -> UpstreamOutcome {
match error {
RestAdapterError::UnexpectedStatus { status, .. } if (400..500).contains(status) => {
"client_error"
UpstreamOutcome::ClientError
}
RestAdapterError::UnexpectedStatus { status, .. } if (500..600).contains(status) => {
"server_error"
UpstreamOutcome::ServerError
}
RestAdapterError::UnexpectedStatus { .. } => "unexpected_status",
RestAdapterError::Transport(error) if error.is_timeout() => "timeout",
RestAdapterError::Transport(_) => "transport_error",
RestAdapterError::ResponseTooLarge { .. } => "response_too_large",
RestAdapterError::TargetNotAllowed { .. } => "rejected",
RestAdapterError::WindowExpired => "window_expired",
RestAdapterError::InvalidSseEvent => "invalid_response",
RestAdapterError::UnexpectedStatus { .. } => UpstreamOutcome::UnexpectedStatus,
RestAdapterError::Transport(error) if error.is_timeout() => UpstreamOutcome::Timeout,
RestAdapterError::Transport(_) => UpstreamOutcome::TransportError,
RestAdapterError::ResponseTooLarge { .. } => UpstreamOutcome::ResponseTooLarge,
RestAdapterError::TargetNotAllowed { .. } => UpstreamOutcome::Rejected,
RestAdapterError::WindowExpired => UpstreamOutcome::WindowExpired,
RestAdapterError::InvalidSseEvent => UpstreamOutcome::InvalidResponse,
RestAdapterError::InvalidBaseUrl { .. }
| RestAdapterError::InvalidPathParameter { .. }
| RestAdapterError::InvalidQueryParameter { .. }
| RestAdapterError::InvalidHeaderName { .. }
| RestAdapterError::InvalidHeaderValue { .. } => "invalid_request",
RestAdapterError::InvalidConfiguration { .. } => "configuration",
| RestAdapterError::InvalidHeaderValue { .. } => UpstreamOutcome::InvalidRequest,
RestAdapterError::InvalidConfiguration { .. } => UpstreamOutcome::Configuration,
}
}