mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-11-02 21:07:58 +08:00
chore: add metrics for external api call (#2021)
This commit is contained in:
@ -39,3 +39,5 @@ pub(crate) const APPLEPAY_VALIDATION_URL: &str =
|
||||
// Qr Image data source starts with this string
|
||||
// The base64 image data will be appended to it to image data source
|
||||
pub(crate) const QR_IMAGE_DATA_SOURCE_STRING: &str = "data:image/png;base64";
|
||||
|
||||
pub(crate) const METRICS_HOST_TAG_NAME: &str = "host";
|
||||
|
||||
@ -197,6 +197,7 @@ pub async fn add_card_to_locker(
|
||||
})
|
||||
},
|
||||
&metrics::CARD_ADD_TIME,
|
||||
&[],
|
||||
)
|
||||
.await
|
||||
}
|
||||
@ -221,6 +222,7 @@ pub async fn get_card_from_locker(
|
||||
})
|
||||
},
|
||||
&metrics::CARD_GET_TIME,
|
||||
&[],
|
||||
)
|
||||
.await
|
||||
}
|
||||
@ -243,6 +245,7 @@ pub async fn delete_card_from_locker(
|
||||
})
|
||||
},
|
||||
&metrics::CARD_DELETE_TIME,
|
||||
&[],
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
@ -12,6 +12,7 @@ counter_metric!(AWS_KMS_FAILURES, GLOBAL_METER); // No. of AWS KMS API failures
|
||||
counter_metric!(REQUESTS_RECEIVED, GLOBAL_METER);
|
||||
counter_metric!(REQUEST_STATUS, GLOBAL_METER);
|
||||
histogram_metric!(REQUEST_TIME, GLOBAL_METER);
|
||||
histogram_metric!(EXTERNAL_REQUEST_TIME, GLOBAL_METER);
|
||||
|
||||
// Operation Level Metrics
|
||||
counter_metric!(PAYMENT_OPS_COUNT, GLOBAL_METER);
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
use router_env::opentelemetry;
|
||||
|
||||
use super::utils as metric_utils;
|
||||
use crate::services::ApplicationResponse;
|
||||
|
||||
@ -23,12 +25,13 @@ where
|
||||
pub async fn record_operation_time<F, R>(
|
||||
future: F,
|
||||
metric: &once_cell::sync::Lazy<router_env::opentelemetry::metrics::Histogram<f64>>,
|
||||
key_value: &[opentelemetry::KeyValue],
|
||||
) -> R
|
||||
where
|
||||
F: futures::Future<Output = R>,
|
||||
{
|
||||
let (result, time) = metric_utils::time_future(future).await;
|
||||
metric.record(&super::CONTEXT, time.as_secs_f64(), &[]);
|
||||
metric.record(&super::CONTEXT, time.as_secs_f64(), key_value);
|
||||
result
|
||||
}
|
||||
|
||||
|
||||
@ -28,7 +28,11 @@ use crate::{
|
||||
payments,
|
||||
},
|
||||
logger,
|
||||
routes::{app::AppStateInfo, metrics, AppState},
|
||||
routes::{
|
||||
app::AppStateInfo,
|
||||
metrics::{self, request as metrics_request},
|
||||
AppState,
|
||||
},
|
||||
services::authentication as auth,
|
||||
types::{
|
||||
self,
|
||||
@ -407,12 +411,19 @@ pub async fn send_request(
|
||||
option_timeout_secs: Option<u64>,
|
||||
) -> CustomResult<reqwest::Response, errors::ApiClientError> {
|
||||
logger::debug!(method=?request.method, headers=?request.headers, payload=?request.payload, ?request);
|
||||
let url = &request.url;
|
||||
|
||||
let url = reqwest::Url::parse(&request.url)
|
||||
.into_report()
|
||||
.change_context(errors::ApiClientError::UrlEncodingFailed)?;
|
||||
|
||||
#[cfg(feature = "dummy_connector")]
|
||||
let should_bypass_proxy = url.starts_with(&state.conf.connectors.dummyconnector.base_url)
|
||||
|| client::proxy_bypass_urls(&state.conf.locker).contains(url);
|
||||
let should_bypass_proxy = url
|
||||
.as_str()
|
||||
.starts_with(&state.conf.connectors.dummyconnector.base_url)
|
||||
|| client::proxy_bypass_urls(&state.conf.locker).contains(&url.to_string());
|
||||
#[cfg(not(feature = "dummy_connector"))]
|
||||
let should_bypass_proxy = client::proxy_bypass_urls(&state.conf.locker).contains(url);
|
||||
let should_bypass_proxy =
|
||||
client::proxy_bypass_urls(&state.conf.locker).contains(&url.to_string());
|
||||
let client = client::create_client(
|
||||
&state.conf.proxy,
|
||||
should_bypass_proxy,
|
||||
@ -420,66 +431,81 @@ pub async fn send_request(
|
||||
request.certificate_key,
|
||||
)?;
|
||||
let headers = request.headers.construct_header_map()?;
|
||||
match request.method {
|
||||
Method::Get => client.get(url),
|
||||
Method::Post => {
|
||||
let client = client.post(url);
|
||||
match request.content_type {
|
||||
Some(ContentType::Json) => client.json(&request.payload),
|
||||
|
||||
Some(ContentType::FormData) => client.multipart(
|
||||
request
|
||||
.form_data
|
||||
.unwrap_or_else(reqwest::multipart::Form::new),
|
||||
),
|
||||
let metrics_tag = router_env::opentelemetry::KeyValue {
|
||||
key: consts::METRICS_HOST_TAG_NAME.into(),
|
||||
value: url.host_str().unwrap_or_default().to_string().into(),
|
||||
};
|
||||
|
||||
// Currently this is not used remove this if not required
|
||||
// If using this then handle the serde_part
|
||||
Some(ContentType::FormUrlEncoded) => {
|
||||
let payload = match request.payload.clone() {
|
||||
Some(req) => serde_json::from_str(req.peek())
|
||||
let send_request = async {
|
||||
match request.method {
|
||||
Method::Get => client.get(url),
|
||||
Method::Post => {
|
||||
let client = client.post(url);
|
||||
match request.content_type {
|
||||
Some(ContentType::Json) => client.json(&request.payload),
|
||||
|
||||
Some(ContentType::FormData) => client.multipart(
|
||||
request
|
||||
.form_data
|
||||
.unwrap_or_else(reqwest::multipart::Form::new),
|
||||
),
|
||||
|
||||
// Currently this is not used remove this if not required
|
||||
// If using this then handle the serde_part
|
||||
Some(ContentType::FormUrlEncoded) => {
|
||||
let payload = match request.payload.clone() {
|
||||
Some(req) => serde_json::from_str(req.peek())
|
||||
.into_report()
|
||||
.change_context(errors::ApiClientError::UrlEncodingFailed)?,
|
||||
_ => json!(r#""#),
|
||||
};
|
||||
let url_encoded_payload = serde_urlencoded::to_string(&payload)
|
||||
.into_report()
|
||||
.change_context(errors::ApiClientError::UrlEncodingFailed)?,
|
||||
_ => json!(r#""#),
|
||||
};
|
||||
let url_encoded_payload = serde_urlencoded::to_string(&payload)
|
||||
.into_report()
|
||||
.change_context(errors::ApiClientError::UrlEncodingFailed)
|
||||
.attach_printable_lazy(|| {
|
||||
format!(
|
||||
"Unable to do url encoding on request: {:?}",
|
||||
&request.payload
|
||||
)
|
||||
})?;
|
||||
.change_context(errors::ApiClientError::UrlEncodingFailed)
|
||||
.attach_printable_lazy(|| {
|
||||
format!(
|
||||
"Unable to do url encoding on request: {:?}",
|
||||
&request.payload
|
||||
)
|
||||
})?;
|
||||
|
||||
logger::debug!(?url_encoded_payload);
|
||||
client.body(url_encoded_payload)
|
||||
logger::debug!(?url_encoded_payload);
|
||||
client.body(url_encoded_payload)
|
||||
}
|
||||
// If payload needs processing the body cannot have default
|
||||
None => client.body(request.payload.expose_option().unwrap_or_default()),
|
||||
}
|
||||
// If payload needs processing the body cannot have default
|
||||
None => client.body(request.payload.expose_option().unwrap_or_default()),
|
||||
}
|
||||
}
|
||||
|
||||
Method::Put => client
|
||||
.put(url)
|
||||
.body(request.payload.expose_option().unwrap_or_default()), // If payload needs processing the body cannot have default
|
||||
Method::Delete => client.delete(url),
|
||||
}
|
||||
.add_headers(headers)
|
||||
.timeout(Duration::from_secs(
|
||||
option_timeout_secs.unwrap_or(crate::consts::REQUEST_TIME_OUT),
|
||||
))
|
||||
.send()
|
||||
.await
|
||||
.map_err(|error| match error {
|
||||
error if error.is_timeout() => {
|
||||
metrics::REQUEST_BUILD_FAILURE.add(&metrics::CONTEXT, 1, &[]);
|
||||
errors::ApiClientError::RequestTimeoutReceived
|
||||
Method::Put => client
|
||||
.put(url)
|
||||
.body(request.payload.expose_option().unwrap_or_default()), // If payload needs processing the body cannot have default
|
||||
Method::Delete => client.delete(url),
|
||||
}
|
||||
_ => errors::ApiClientError::RequestNotSent(error.to_string()),
|
||||
})
|
||||
.into_report()
|
||||
.attach_printable("Unable to send request to connector")
|
||||
.add_headers(headers)
|
||||
.timeout(Duration::from_secs(
|
||||
option_timeout_secs.unwrap_or(crate::consts::REQUEST_TIME_OUT),
|
||||
))
|
||||
.send()
|
||||
.await
|
||||
.map_err(|error| match error {
|
||||
error if error.is_timeout() => {
|
||||
metrics::REQUEST_BUILD_FAILURE.add(&metrics::CONTEXT, 1, &[]);
|
||||
errors::ApiClientError::RequestTimeoutReceived
|
||||
}
|
||||
_ => errors::ApiClientError::RequestNotSent(error.to_string()),
|
||||
})
|
||||
.into_report()
|
||||
.attach_printable("Unable to send request to connector")
|
||||
};
|
||||
|
||||
metrics_request::record_operation_time(
|
||||
send_request,
|
||||
&metrics::EXTERNAL_REQUEST_TIME,
|
||||
&[metrics_tag],
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
#[instrument(skip_all)]
|
||||
|
||||
@ -192,6 +192,7 @@ where
|
||||
request::record_operation_time(
|
||||
crypto::Encryptable::encrypt(inner, key, crypto::GcmAes256),
|
||||
&ENCRYPTION_TIME,
|
||||
&[],
|
||||
)
|
||||
.await
|
||||
}
|
||||
@ -220,6 +221,7 @@ where
|
||||
request::record_operation_time(
|
||||
inner.async_map(|item| crypto::Encryptable::decrypt(item, key, crypto::GcmAes256)),
|
||||
&DECRYPTION_TIME,
|
||||
&[],
|
||||
)
|
||||
.await
|
||||
.transpose()
|
||||
|
||||
Reference in New Issue
Block a user