mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-10-29 09:07:09 +08:00
feat(metrics): add response metrics (#1263)
This commit is contained in:
@ -25,13 +25,21 @@ where
|
|||||||
Q: Serialize + std::fmt::Debug + 'a,
|
Q: Serialize + std::fmt::Debug + 'a,
|
||||||
S: TryFrom<Q> + Serialize,
|
S: TryFrom<Q> + Serialize,
|
||||||
E: Serialize + error_stack::Context + actix_web::ResponseError + Clone,
|
E: Serialize + error_stack::Context + actix_web::ResponseError + Clone,
|
||||||
|
U: auth::AuthInfo,
|
||||||
error_stack::Report<E>: services::EmbedError,
|
error_stack::Report<E>: services::EmbedError,
|
||||||
errors::ApiErrorResponse: ErrorSwitch<E>,
|
errors::ApiErrorResponse: ErrorSwitch<E>,
|
||||||
T: std::fmt::Debug,
|
T: std::fmt::Debug,
|
||||||
A: AppStateInfo,
|
A: AppStateInfo,
|
||||||
{
|
{
|
||||||
let resp: common_utils::errors::CustomResult<_, E> =
|
let resp: common_utils::errors::CustomResult<_, E> = api::server_wrap_util(
|
||||||
api::server_wrap_util(state, request, payload, func, api_authentication).await;
|
&router_env::Flow::CompatibilityLayerRequest,
|
||||||
|
state,
|
||||||
|
request,
|
||||||
|
payload,
|
||||||
|
func,
|
||||||
|
api_authentication,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
match resp {
|
match resp {
|
||||||
Ok(api::ApplicationResponse::Json(router_resp)) => {
|
Ok(api::ApplicationResponse::Json(router_resp)) => {
|
||||||
let pg_resp = S::try_from(router_resp);
|
let pg_resp = S::try_from(router_resp);
|
||||||
|
|||||||
@ -12,6 +12,7 @@ use crate::{
|
|||||||
payments::helpers,
|
payments::helpers,
|
||||||
},
|
},
|
||||||
db::StorageInterface,
|
db::StorageInterface,
|
||||||
|
routes::metrics,
|
||||||
services::api as service_api,
|
services::api as service_api,
|
||||||
types::{
|
types::{
|
||||||
self, api,
|
self, api,
|
||||||
@ -378,10 +379,11 @@ pub async fn create_payment_connector(
|
|||||||
}
|
}
|
||||||
None => None,
|
None => None,
|
||||||
};
|
};
|
||||||
|
|
||||||
let merchant_connector_account = storage::MerchantConnectorAccountNew {
|
let merchant_connector_account = storage::MerchantConnectorAccountNew {
|
||||||
merchant_id: Some(merchant_id.to_string()),
|
merchant_id: Some(merchant_id.to_string()),
|
||||||
connector_type: Some(req.connector_type.foreign_into()),
|
connector_type: Some(req.connector_type.foreign_into()),
|
||||||
connector_name: Some(req.connector_name),
|
connector_name: Some(req.connector_name.to_owned()),
|
||||||
merchant_connector_id: utils::generate_id(consts::ID_LENGTH, "mca"),
|
merchant_connector_id: utils::generate_id(consts::ID_LENGTH, "mca"),
|
||||||
connector_account_details: req.connector_account_details,
|
connector_account_details: req.connector_account_details,
|
||||||
payment_methods_enabled,
|
payment_methods_enabled,
|
||||||
@ -406,6 +408,15 @@ pub async fn create_payment_connector(
|
|||||||
},
|
},
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
|
metrics::MCA_CREATE.add(
|
||||||
|
&metrics::CONTEXT,
|
||||||
|
1,
|
||||||
|
&[
|
||||||
|
metrics::request::add_attributes("connector", req.connector_name.to_string()),
|
||||||
|
metrics::request::add_attributes("merchant", merchant_id.to_string()),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
let mca_response = ForeignTryFrom::foreign_try_from(mca)?;
|
let mca_response = ForeignTryFrom::foreign_try_from(mca)?;
|
||||||
|
|
||||||
Ok(service_api::ApplicationResponse::Json(mca_response))
|
Ok(service_api::ApplicationResponse::Json(mca_response))
|
||||||
|
|||||||
@ -132,7 +132,7 @@ pub async fn create_api_key(
|
|||||||
let plaintext_api_key = PlaintextApiKey::new(consts::API_KEY_LENGTH);
|
let plaintext_api_key = PlaintextApiKey::new(consts::API_KEY_LENGTH);
|
||||||
let api_key = storage::ApiKeyNew {
|
let api_key = storage::ApiKeyNew {
|
||||||
key_id: PlaintextApiKey::new_key_id(),
|
key_id: PlaintextApiKey::new_key_id(),
|
||||||
merchant_id,
|
merchant_id: merchant_id.to_owned(),
|
||||||
name: api_key.name,
|
name: api_key.name,
|
||||||
description: api_key.description,
|
description: api_key.description,
|
||||||
hashed_api_key: plaintext_api_key.keyed_hash(hash_key.peek()).into(),
|
hashed_api_key: plaintext_api_key.keyed_hash(hash_key.peek()).into(),
|
||||||
@ -148,7 +148,11 @@ pub async fn create_api_key(
|
|||||||
.change_context(errors::ApiErrorResponse::InternalServerError)
|
.change_context(errors::ApiErrorResponse::InternalServerError)
|
||||||
.attach_printable("Failed to insert new API key")?;
|
.attach_printable("Failed to insert new API key")?;
|
||||||
|
|
||||||
metrics::API_KEY_CREATED.add(&metrics::CONTEXT, 1, &[]);
|
metrics::API_KEY_CREATED.add(
|
||||||
|
&metrics::CONTEXT,
|
||||||
|
1,
|
||||||
|
&[metrics::request::add_attributes("merchant", merchant_id)],
|
||||||
|
);
|
||||||
|
|
||||||
Ok(ApplicationResponse::Json(
|
Ok(ApplicationResponse::Json(
|
||||||
(api_key, plaintext_api_key).foreign_into(),
|
(api_key, plaintext_api_key).foreign_into(),
|
||||||
|
|||||||
@ -13,7 +13,7 @@ use crate::{
|
|||||||
errors::{self, RouterResponse, RouterResult},
|
errors::{self, RouterResponse, RouterResult},
|
||||||
payments::{self, helpers},
|
payments::{self, helpers},
|
||||||
},
|
},
|
||||||
routes::AppState,
|
routes::{metrics, AppState},
|
||||||
services::{self, RedirectForm},
|
services::{self, RedirectForm},
|
||||||
types::{
|
types::{
|
||||||
self, api,
|
self, api,
|
||||||
@ -165,7 +165,7 @@ where
|
|||||||
payment_data.address,
|
payment_data.address,
|
||||||
server,
|
server,
|
||||||
payment_data.connector_response.authentication_data,
|
payment_data.connector_response.authentication_data,
|
||||||
operation,
|
&operation,
|
||||||
payment_data.ephemeral_key,
|
payment_data.ephemeral_key,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -256,7 +256,7 @@ pub fn payments_to_payments_response<R, Op>(
|
|||||||
address: PaymentAddress,
|
address: PaymentAddress,
|
||||||
server: &Server,
|
server: &Server,
|
||||||
redirection_data: Option<serde_json::Value>,
|
redirection_data: Option<serde_json::Value>,
|
||||||
operation: Op,
|
operation: &Op,
|
||||||
ephemeral_key_option: Option<ephemeral_key::EphemeralKey>,
|
ephemeral_key_option: Option<ephemeral_key::EphemeralKey>,
|
||||||
) -> RouterResponse<api::PaymentsResponse>
|
) -> RouterResponse<api::PaymentsResponse>
|
||||||
where
|
where
|
||||||
@ -287,8 +287,19 @@ where
|
|||||||
.collect(),
|
.collect(),
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
let merchant_id = payment_attempt.merchant_id.to_owned();
|
||||||
|
let payment_method_type = payment_attempt
|
||||||
|
.payment_method_type
|
||||||
|
.as_ref()
|
||||||
|
.map(ToString::to_string)
|
||||||
|
.unwrap_or("".to_owned());
|
||||||
|
let payment_method = payment_attempt
|
||||||
|
.payment_method
|
||||||
|
.as_ref()
|
||||||
|
.map(ToString::to_string)
|
||||||
|
.unwrap_or("".to_owned());
|
||||||
|
|
||||||
Ok(match payment_request {
|
let output = Ok(match payment_request {
|
||||||
Some(_request) => {
|
Some(_request) => {
|
||||||
if payments::is_start_pay(&operation) && redirection_data.is_some() {
|
if payments::is_start_pay(&operation) && redirection_data.is_some() {
|
||||||
let redirection_data = redirection_data.get_required_value("redirection_data")?;
|
let redirection_data = redirection_data.get_required_value("redirection_data")?;
|
||||||
@ -479,7 +490,20 @@ where
|
|||||||
metadata: payment_intent.metadata,
|
metadata: payment_intent.metadata,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}),
|
}),
|
||||||
})
|
});
|
||||||
|
|
||||||
|
metrics::PAYMENT_OPS_COUNT.add(
|
||||||
|
&metrics::CONTEXT,
|
||||||
|
1,
|
||||||
|
&[
|
||||||
|
metrics::request::add_attributes("operation", format!("{:?}", operation)),
|
||||||
|
metrics::request::add_attributes("merchant", merchant_id),
|
||||||
|
metrics::request::add_attributes("payment_method_type", payment_method_type),
|
||||||
|
metrics::request::add_attributes("payment_method", payment_method),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
output
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ForeignFrom<(storage::PaymentIntent, storage::PaymentAttempt)> for api::PaymentsResponse {
|
impl ForeignFrom<(storage::PaymentIntent, storage::PaymentAttempt)> for api::PaymentsResponse {
|
||||||
|
|||||||
@ -10,10 +10,12 @@ counter_metric!(AWS_KMS_FAILURES, GLOBAL_METER); // No. of AWS KMS API failures
|
|||||||
|
|
||||||
// API Level Metrics
|
// API Level Metrics
|
||||||
counter_metric!(REQUESTS_RECEIVED, GLOBAL_METER);
|
counter_metric!(REQUESTS_RECEIVED, GLOBAL_METER);
|
||||||
counter_metric!(FAILED_REQUEST, GLOBAL_METER);
|
counter_metric!(REQUEST_STATUS, GLOBAL_METER);
|
||||||
histogram_metric!(REQUEST_TIME, GLOBAL_METER);
|
histogram_metric!(REQUEST_TIME, GLOBAL_METER);
|
||||||
|
|
||||||
// Operation Level Metrics
|
// Operation Level Metrics
|
||||||
|
counter_metric!(PAYMENT_OPS_COUNT, GLOBAL_METER);
|
||||||
|
|
||||||
counter_metric!(PAYMENT_COUNT, GLOBAL_METER);
|
counter_metric!(PAYMENT_COUNT, GLOBAL_METER);
|
||||||
counter_metric!(SUCCESSFUL_PAYMENT, GLOBAL_METER);
|
counter_metric!(SUCCESSFUL_PAYMENT, GLOBAL_METER);
|
||||||
|
|
||||||
@ -42,6 +44,8 @@ counter_metric!(CUSTOMER_REDACTED, GLOBAL_METER);
|
|||||||
counter_metric!(API_KEY_CREATED, GLOBAL_METER);
|
counter_metric!(API_KEY_CREATED, GLOBAL_METER);
|
||||||
counter_metric!(API_KEY_REVOKED, GLOBAL_METER);
|
counter_metric!(API_KEY_REVOKED, GLOBAL_METER);
|
||||||
|
|
||||||
|
counter_metric!(MCA_CREATE, GLOBAL_METER);
|
||||||
|
|
||||||
// Flow Specific Metrics
|
// Flow Specific Metrics
|
||||||
|
|
||||||
counter_metric!(ACCESS_TOKEN_CREATION, GLOBAL_METER);
|
counter_metric!(ACCESS_TOKEN_CREATION, GLOBAL_METER);
|
||||||
|
|||||||
@ -1,8 +1,9 @@
|
|||||||
use super::utils as metric_utils;
|
use super::utils as metric_utils;
|
||||||
|
use crate::services::ApplicationResponse;
|
||||||
|
|
||||||
pub async fn record_request_time_metric<F, R>(
|
pub async fn record_request_time_metric<F, R>(
|
||||||
future: F,
|
future: F,
|
||||||
flow: impl router_env::types::FlowMetric,
|
flow: &impl router_env::types::FlowMetric,
|
||||||
) -> R
|
) -> R
|
||||||
where
|
where
|
||||||
F: futures::Future<Output = R>,
|
F: futures::Future<Output = R>,
|
||||||
@ -37,3 +38,26 @@ pub fn add_attributes<T: Into<router_env::opentelemetry::Value>>(
|
|||||||
) -> router_env::opentelemetry::KeyValue {
|
) -> router_env::opentelemetry::KeyValue {
|
||||||
router_env::opentelemetry::KeyValue::new(key, value)
|
router_env::opentelemetry::KeyValue::new(key, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn status_code_metrics(status_code: i64, flow: String, merchant_id: String) {
|
||||||
|
super::REQUEST_STATUS.add(
|
||||||
|
&super::CONTEXT,
|
||||||
|
1,
|
||||||
|
&[
|
||||||
|
add_attributes("status_code", status_code),
|
||||||
|
add_attributes("flow", flow),
|
||||||
|
add_attributes("merchant_id", merchant_id),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn track_response_status_code<Q>(response: &ApplicationResponse<Q>) -> i64 {
|
||||||
|
match response {
|
||||||
|
ApplicationResponse::Json(_)
|
||||||
|
| ApplicationResponse::StatusOk
|
||||||
|
| ApplicationResponse::TextPlain(_)
|
||||||
|
| ApplicationResponse::Form(_)
|
||||||
|
| ApplicationResponse::FileData(_) => 200,
|
||||||
|
ApplicationResponse::JsonForRedirection(_) => 302,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -9,7 +9,7 @@ use std::{
|
|||||||
time::{Duration, Instant},
|
time::{Duration, Instant},
|
||||||
};
|
};
|
||||||
|
|
||||||
use actix_web::{body, HttpRequest, HttpResponse, Responder};
|
use actix_web::{body, HttpRequest, HttpResponse, Responder, ResponseError};
|
||||||
use common_utils::errors::ReportSwitchExt;
|
use common_utils::errors::ReportSwitchExt;
|
||||||
use error_stack::{report, IntoReport, Report, ResultExt};
|
use error_stack::{report, IntoReport, Report, ResultExt};
|
||||||
use masking::{ExposeOptionInterface, PeekInterface};
|
use masking::{ExposeOptionInterface, PeekInterface};
|
||||||
@ -528,6 +528,7 @@ pub enum AuthFlow {
|
|||||||
|
|
||||||
#[instrument(skip(request, payload, state, func, api_auth))]
|
#[instrument(skip(request, payload, state, func, api_auth))]
|
||||||
pub async fn server_wrap_util<'a, 'b, A, U, T, Q, F, Fut, E, OErr>(
|
pub async fn server_wrap_util<'a, 'b, A, U, T, Q, F, Fut, E, OErr>(
|
||||||
|
flow: &'a impl router_env::types::FlowMetric,
|
||||||
state: &'b A,
|
state: &'b A,
|
||||||
request: &'a HttpRequest,
|
request: &'a HttpRequest,
|
||||||
payload: T,
|
payload: T,
|
||||||
@ -536,18 +537,36 @@ pub async fn server_wrap_util<'a, 'b, A, U, T, Q, F, Fut, E, OErr>(
|
|||||||
) -> CustomResult<ApplicationResponse<Q>, OErr>
|
) -> CustomResult<ApplicationResponse<Q>, OErr>
|
||||||
where
|
where
|
||||||
F: Fn(&'b A, U, T) -> Fut,
|
F: Fn(&'b A, U, T) -> Fut,
|
||||||
|
'b: 'a,
|
||||||
Fut: Future<Output = CustomResult<ApplicationResponse<Q>, E>>,
|
Fut: Future<Output = CustomResult<ApplicationResponse<Q>, E>>,
|
||||||
Q: Serialize + Debug + 'a,
|
Q: Serialize + Debug + 'a,
|
||||||
T: Debug,
|
T: Debug,
|
||||||
A: AppStateInfo,
|
A: AppStateInfo,
|
||||||
|
U: auth::AuthInfo,
|
||||||
CustomResult<ApplicationResponse<Q>, E>: ReportSwitchExt<ApplicationResponse<Q>, OErr>,
|
CustomResult<ApplicationResponse<Q>, E>: ReportSwitchExt<ApplicationResponse<Q>, OErr>,
|
||||||
CustomResult<U, errors::ApiErrorResponse>: ReportSwitchExt<U, OErr>,
|
CustomResult<U, errors::ApiErrorResponse>: ReportSwitchExt<U, OErr>,
|
||||||
|
OErr: ResponseError + Sync + Send + 'static,
|
||||||
{
|
{
|
||||||
let auth_out = api_auth
|
let auth_out = api_auth
|
||||||
.authenticate_and_fetch(request.headers(), state)
|
.authenticate_and_fetch(request.headers(), state)
|
||||||
.await
|
.await
|
||||||
.switch()?;
|
.switch()?;
|
||||||
func(state, auth_out, payload).await.switch()
|
let metric_merchant_id = auth_out.get_merchant_id().unwrap_or("").to_string();
|
||||||
|
|
||||||
|
let output = func(state, auth_out, payload).await.switch();
|
||||||
|
|
||||||
|
let status_code = match output.as_ref() {
|
||||||
|
Ok(res) => metrics::request::track_response_status_code(res),
|
||||||
|
Err(err) => err.current_context().status_code().as_u16().into(),
|
||||||
|
};
|
||||||
|
|
||||||
|
metrics::request::status_code_metrics(
|
||||||
|
status_code,
|
||||||
|
flow.to_string(),
|
||||||
|
metric_merchant_id.to_string(),
|
||||||
|
);
|
||||||
|
|
||||||
|
output
|
||||||
}
|
}
|
||||||
|
|
||||||
#[instrument(
|
#[instrument(
|
||||||
@ -567,6 +586,7 @@ where
|
|||||||
Fut: Future<Output = CustomResult<ApplicationResponse<Q>, E>>,
|
Fut: Future<Output = CustomResult<ApplicationResponse<Q>, E>>,
|
||||||
Q: Serialize + Debug + 'a,
|
Q: Serialize + Debug + 'a,
|
||||||
T: Debug,
|
T: Debug,
|
||||||
|
U: auth::AuthInfo,
|
||||||
A: AppStateInfo,
|
A: AppStateInfo,
|
||||||
CustomResult<ApplicationResponse<Q>, E>:
|
CustomResult<ApplicationResponse<Q>, E>:
|
||||||
ReportSwitchExt<ApplicationResponse<Q>, api_models::errors::types::ApiErrorResponse>,
|
ReportSwitchExt<ApplicationResponse<Q>, api_models::errors::types::ApiErrorResponse>,
|
||||||
@ -579,8 +599,8 @@ where
|
|||||||
let start_instant = Instant::now();
|
let start_instant = Instant::now();
|
||||||
logger::info!(tag = ?Tag::BeginRequest);
|
logger::info!(tag = ?Tag::BeginRequest);
|
||||||
let res = match metrics::request::record_request_time_metric(
|
let res = match metrics::request::record_request_time_metric(
|
||||||
server_wrap_util(state, request, payload, func, api_auth),
|
server_wrap_util(&flow, state, request, payload, func, api_auth),
|
||||||
flow,
|
&flow,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
@ -636,7 +656,7 @@ where
|
|||||||
|
|
||||||
pub fn log_and_return_error_response<T>(error: Report<T>) -> HttpResponse
|
pub fn log_and_return_error_response<T>(error: Report<T>) -> HttpResponse
|
||||||
where
|
where
|
||||||
T: actix_web::ResponseError + error_stack::Context + Clone,
|
T: error_stack::Context + Clone + ResponseError,
|
||||||
Report<T>: EmbedError,
|
Report<T>: EmbedError,
|
||||||
{
|
{
|
||||||
logger::error!(?error);
|
logger::error!(?error);
|
||||||
|
|||||||
@ -20,10 +20,28 @@ use crate::{
|
|||||||
types::storage,
|
types::storage,
|
||||||
utils::OptionExt,
|
utils::OptionExt,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pub trait AuthInfo {
|
||||||
|
fn get_merchant_id(&self) -> Option<&str>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AuthInfo for () {
|
||||||
|
fn get_merchant_id(&self) -> Option<&str> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AuthInfo for storage::MerchantAccount {
|
||||||
|
fn get_merchant_id(&self) -> Option<&str> {
|
||||||
|
Some(&self.merchant_id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
pub trait AuthenticateAndFetch<T, A>
|
pub trait AuthenticateAndFetch<T, A>
|
||||||
where
|
where
|
||||||
A: AppStateInfo,
|
A: AppStateInfo,
|
||||||
|
T: AuthInfo,
|
||||||
{
|
{
|
||||||
async fn authenticate_and_fetch(
|
async fn authenticate_and_fetch(
|
||||||
&self,
|
&self,
|
||||||
@ -303,7 +321,7 @@ impl ClientSecretFetch for api_models::cards_info::CardsInfoRequest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn jwt_auth_or<'a, T, A: AppStateInfo>(
|
pub fn jwt_auth_or<'a, T: AuthInfo, A: AppStateInfo>(
|
||||||
default_auth: &'a dyn AuthenticateAndFetch<T, A>,
|
default_auth: &'a dyn AuthenticateAndFetch<T, A>,
|
||||||
headers: &HeaderMap,
|
headers: &HeaderMap,
|
||||||
) -> Box<&'a dyn AuthenticateAndFetch<T, A>>
|
) -> Box<&'a dyn AuthenticateAndFetch<T, A>>
|
||||||
|
|||||||
@ -184,12 +184,14 @@ pub enum Flow {
|
|||||||
AttachDisputeEvidence,
|
AttachDisputeEvidence,
|
||||||
/// Retrieve Dispute Evidence flow
|
/// Retrieve Dispute Evidence flow
|
||||||
RetrieveDisputeEvidence,
|
RetrieveDisputeEvidence,
|
||||||
|
/// Request to compatibility layer
|
||||||
|
CompatibilityLayerRequest,
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
/// Trait for providing generic behaviour to flow metric
|
/// Trait for providing generic behaviour to flow metric
|
||||||
///
|
///
|
||||||
pub trait FlowMetric: ToString + std::fmt::Debug {}
|
pub trait FlowMetric: ToString + std::fmt::Debug + Clone {}
|
||||||
impl FlowMetric for Flow {}
|
impl FlowMetric for Flow {}
|
||||||
|
|
||||||
/// Category of log event.
|
/// Category of log event.
|
||||||
|
|||||||
Reference in New Issue
Block a user