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,
|
||||
S: TryFrom<Q> + Serialize,
|
||||
E: Serialize + error_stack::Context + actix_web::ResponseError + Clone,
|
||||
U: auth::AuthInfo,
|
||||
error_stack::Report<E>: services::EmbedError,
|
||||
errors::ApiErrorResponse: ErrorSwitch<E>,
|
||||
T: std::fmt::Debug,
|
||||
A: AppStateInfo,
|
||||
{
|
||||
let resp: common_utils::errors::CustomResult<_, E> =
|
||||
api::server_wrap_util(state, request, payload, func, api_authentication).await;
|
||||
let resp: common_utils::errors::CustomResult<_, E> = api::server_wrap_util(
|
||||
&router_env::Flow::CompatibilityLayerRequest,
|
||||
state,
|
||||
request,
|
||||
payload,
|
||||
func,
|
||||
api_authentication,
|
||||
)
|
||||
.await;
|
||||
match resp {
|
||||
Ok(api::ApplicationResponse::Json(router_resp)) => {
|
||||
let pg_resp = S::try_from(router_resp);
|
||||
|
||||
@ -12,6 +12,7 @@ use crate::{
|
||||
payments::helpers,
|
||||
},
|
||||
db::StorageInterface,
|
||||
routes::metrics,
|
||||
services::api as service_api,
|
||||
types::{
|
||||
self, api,
|
||||
@ -378,10 +379,11 @@ pub async fn create_payment_connector(
|
||||
}
|
||||
None => None,
|
||||
};
|
||||
|
||||
let merchant_connector_account = storage::MerchantConnectorAccountNew {
|
||||
merchant_id: Some(merchant_id.to_string()),
|
||||
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"),
|
||||
connector_account_details: req.connector_account_details,
|
||||
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)?;
|
||||
|
||||
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 api_key = storage::ApiKeyNew {
|
||||
key_id: PlaintextApiKey::new_key_id(),
|
||||
merchant_id,
|
||||
merchant_id: merchant_id.to_owned(),
|
||||
name: api_key.name,
|
||||
description: api_key.description,
|
||||
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)
|
||||
.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(
|
||||
(api_key, plaintext_api_key).foreign_into(),
|
||||
|
||||
@ -13,7 +13,7 @@ use crate::{
|
||||
errors::{self, RouterResponse, RouterResult},
|
||||
payments::{self, helpers},
|
||||
},
|
||||
routes::AppState,
|
||||
routes::{metrics, AppState},
|
||||
services::{self, RedirectForm},
|
||||
types::{
|
||||
self, api,
|
||||
@ -165,7 +165,7 @@ where
|
||||
payment_data.address,
|
||||
server,
|
||||
payment_data.connector_response.authentication_data,
|
||||
operation,
|
||||
&operation,
|
||||
payment_data.ephemeral_key,
|
||||
)
|
||||
}
|
||||
@ -256,7 +256,7 @@ pub fn payments_to_payments_response<R, Op>(
|
||||
address: PaymentAddress,
|
||||
server: &Server,
|
||||
redirection_data: Option<serde_json::Value>,
|
||||
operation: Op,
|
||||
operation: &Op,
|
||||
ephemeral_key_option: Option<ephemeral_key::EphemeralKey>,
|
||||
) -> RouterResponse<api::PaymentsResponse>
|
||||
where
|
||||
@ -287,8 +287,19 @@ where
|
||||
.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) => {
|
||||
if payments::is_start_pay(&operation) && redirection_data.is_some() {
|
||||
let redirection_data = redirection_data.get_required_value("redirection_data")?;
|
||||
@ -479,7 +490,20 @@ where
|
||||
metadata: payment_intent.metadata,
|
||||
..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 {
|
||||
|
||||
@ -10,10 +10,12 @@ counter_metric!(AWS_KMS_FAILURES, GLOBAL_METER); // No. of AWS KMS API failures
|
||||
|
||||
// API Level Metrics
|
||||
counter_metric!(REQUESTS_RECEIVED, GLOBAL_METER);
|
||||
counter_metric!(FAILED_REQUEST, GLOBAL_METER);
|
||||
counter_metric!(REQUEST_STATUS, GLOBAL_METER);
|
||||
histogram_metric!(REQUEST_TIME, GLOBAL_METER);
|
||||
|
||||
// Operation Level Metrics
|
||||
counter_metric!(PAYMENT_OPS_COUNT, GLOBAL_METER);
|
||||
|
||||
counter_metric!(PAYMENT_COUNT, 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_REVOKED, GLOBAL_METER);
|
||||
|
||||
counter_metric!(MCA_CREATE, GLOBAL_METER);
|
||||
|
||||
// Flow Specific Metrics
|
||||
|
||||
counter_metric!(ACCESS_TOKEN_CREATION, GLOBAL_METER);
|
||||
|
||||
@ -1,8 +1,9 @@
|
||||
use super::utils as metric_utils;
|
||||
use crate::services::ApplicationResponse;
|
||||
|
||||
pub async fn record_request_time_metric<F, R>(
|
||||
future: F,
|
||||
flow: impl router_env::types::FlowMetric,
|
||||
flow: &impl router_env::types::FlowMetric,
|
||||
) -> R
|
||||
where
|
||||
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::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},
|
||||
};
|
||||
|
||||
use actix_web::{body, HttpRequest, HttpResponse, Responder};
|
||||
use actix_web::{body, HttpRequest, HttpResponse, Responder, ResponseError};
|
||||
use common_utils::errors::ReportSwitchExt;
|
||||
use error_stack::{report, IntoReport, Report, ResultExt};
|
||||
use masking::{ExposeOptionInterface, PeekInterface};
|
||||
@ -528,6 +528,7 @@ pub enum AuthFlow {
|
||||
|
||||
#[instrument(skip(request, payload, state, func, api_auth))]
|
||||
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,
|
||||
request: &'a HttpRequest,
|
||||
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>
|
||||
where
|
||||
F: Fn(&'b A, U, T) -> Fut,
|
||||
'b: 'a,
|
||||
Fut: Future<Output = CustomResult<ApplicationResponse<Q>, E>>,
|
||||
Q: Serialize + Debug + 'a,
|
||||
T: Debug,
|
||||
A: AppStateInfo,
|
||||
U: auth::AuthInfo,
|
||||
CustomResult<ApplicationResponse<Q>, E>: ReportSwitchExt<ApplicationResponse<Q>, OErr>,
|
||||
CustomResult<U, errors::ApiErrorResponse>: ReportSwitchExt<U, OErr>,
|
||||
OErr: ResponseError + Sync + Send + 'static,
|
||||
{
|
||||
let auth_out = api_auth
|
||||
.authenticate_and_fetch(request.headers(), state)
|
||||
.await
|
||||
.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(
|
||||
@ -567,6 +586,7 @@ where
|
||||
Fut: Future<Output = CustomResult<ApplicationResponse<Q>, E>>,
|
||||
Q: Serialize + Debug + 'a,
|
||||
T: Debug,
|
||||
U: auth::AuthInfo,
|
||||
A: AppStateInfo,
|
||||
CustomResult<ApplicationResponse<Q>, E>:
|
||||
ReportSwitchExt<ApplicationResponse<Q>, api_models::errors::types::ApiErrorResponse>,
|
||||
@ -579,8 +599,8 @@ where
|
||||
let start_instant = Instant::now();
|
||||
logger::info!(tag = ?Tag::BeginRequest);
|
||||
let res = match metrics::request::record_request_time_metric(
|
||||
server_wrap_util(state, request, payload, func, api_auth),
|
||||
flow,
|
||||
server_wrap_util(&flow, state, request, payload, func, api_auth),
|
||||
&flow,
|
||||
)
|
||||
.await
|
||||
{
|
||||
@ -636,7 +656,7 @@ where
|
||||
|
||||
pub fn log_and_return_error_response<T>(error: Report<T>) -> HttpResponse
|
||||
where
|
||||
T: actix_web::ResponseError + error_stack::Context + Clone,
|
||||
T: error_stack::Context + Clone + ResponseError,
|
||||
Report<T>: EmbedError,
|
||||
{
|
||||
logger::error!(?error);
|
||||
|
||||
@ -20,10 +20,28 @@ use crate::{
|
||||
types::storage,
|
||||
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]
|
||||
pub trait AuthenticateAndFetch<T, A>
|
||||
where
|
||||
A: AppStateInfo,
|
||||
T: AuthInfo,
|
||||
{
|
||||
async fn authenticate_and_fetch(
|
||||
&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>,
|
||||
headers: &HeaderMap,
|
||||
) -> Box<&'a dyn AuthenticateAndFetch<T, A>>
|
||||
|
||||
@ -184,12 +184,14 @@ pub enum Flow {
|
||||
AttachDisputeEvidence,
|
||||
/// Retrieve Dispute Evidence flow
|
||||
RetrieveDisputeEvidence,
|
||||
/// Request to compatibility layer
|
||||
CompatibilityLayerRequest,
|
||||
}
|
||||
|
||||
///
|
||||
/// 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 {}
|
||||
|
||||
/// Category of log event.
|
||||
|
||||
Reference in New Issue
Block a user