feat(router): adding metrics for tracking behavior throughout the router crate (#768)

This commit is contained in:
Nishant Joshi
2023-03-21 11:03:03 +05:30
committed by GitHub
parent 1d2166cf96
commit d302b286b8
28 changed files with 487 additions and 77 deletions

View File

@ -10,6 +10,7 @@ use crate::{
consts,
core::errors::{self, RouterResponse, StorageErrorExt},
db::StorageInterface,
routes::metrics,
services::ApplicationResponse,
types::{api, storage, transformers::ForeignInto},
utils,
@ -147,6 +148,8 @@ 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, &[]);
Ok(ApplicationResponse::Json(
(api_key, plaintext_api_key).foreign_into(),
))
@ -191,6 +194,8 @@ pub async fn revoke_api_key(
.await
.map_err(|err| err.to_not_found_response(errors::ApiErrorResponse::ApiKeyNotFound))?;
metrics::API_KEY_REVOKED.add(&metrics::CONTEXT, 1, &[]);
Ok(ApplicationResponse::Json(api::RevokeApiKeyResponse {
key_id: key_id.to_owned(),
revoked,

View File

@ -10,7 +10,7 @@ use crate::{
},
db::StorageInterface,
pii::PeekInterface,
routes::AppState,
routes::{metrics, AppState},
services,
types::{
api::customers::{self, CustomerRequestExt},
@ -222,6 +222,7 @@ pub async fn delete_customer(
address_deleted: true,
payment_methods_deleted: true,
};
metrics::CUSTOMER_REDACTED.add(&metrics::CONTEXT, 1, &[]);
Ok(services::ApplicationResponse::Json(response))
}

View File

@ -6,7 +6,7 @@ use super::payments::helpers;
use crate::{
core::errors::{self, RouterResponse, StorageErrorExt},
db::StorageInterface,
routes::AppState,
routes::{metrics, AppState},
services,
types::{
self,
@ -135,7 +135,14 @@ where
.await
.change_context(errors::ApiErrorResponse::MandateNotFound),
}?;
metrics::SUBSEQUENT_MANDATE_PAYMENT.add(
&metrics::CONTEXT,
1,
&[metrics::request::add_attributes(
"connector",
mandate.connector,
)],
);
resp.payment_method_id = Some(mandate.payment_method_id);
}
None => {
@ -167,6 +174,7 @@ where
payment_method_id,
mandate_reference,
) {
let connector = new_mandate_data.connector.clone();
logger::debug!("{:?}", new_mandate_data);
resp.request
.set_mandate_id(api_models::payments::MandateIds {
@ -182,6 +190,11 @@ where
errors::ApiErrorResponse::DuplicateRefundRequest,
)
})?;
metrics::MANDATE_COUNT.add(
&metrics::CONTEXT,
1,
&[metrics::request::add_attributes("connector", connector)],
);
};
} else if resp.request.get_setup_future_usage().is_some() {
helpers::call_payment_method(

View File

@ -19,7 +19,7 @@ use error_stack::{report, ResultExt};
use router_env::{instrument, tracing};
#[cfg(feature = "basilisk")]
use crate::scheduler::metrics;
use crate::scheduler::metrics as scheduler_metrics;
use crate::{
configs::settings,
core::{
@ -29,7 +29,8 @@ use crate::{
},
db, logger,
pii::prelude::*,
routes, services,
routes::{self, metrics},
services,
types::{
api::{self, PaymentMethodCreateExt},
storage::{self, enums},
@ -159,14 +160,25 @@ pub async fn add_card_to_locker(
customer_id: String,
merchant_account: &storage::MerchantAccount,
) -> errors::CustomResult<api::PaymentMethodResponse, errors::VaultError> {
match state.conf.locker.locker_setup {
settings::LockerSetup::BasiliskLocker => {
add_card_hs(state, req, card, customer_id, merchant_account).await
}
settings::LockerSetup::LegacyLocker => {
add_card(state, req, card, customer_id, merchant_account).await
}
}
metrics::STORED_TO_LOCKER.add(&metrics::CONTEXT, 1, &[]);
metrics::request::record_card_operation_time(
async {
match state.conf.locker.locker_setup {
settings::LockerSetup::BasiliskLocker => {
add_card_hs(state, req, card, customer_id, merchant_account).await
}
settings::LockerSetup::LegacyLocker => {
add_card(state, req, card, customer_id, merchant_account).await
}
}
.map_err(|error| {
metrics::CARD_LOCKER_FAILURES.add(&metrics::CONTEXT, 1, &[]);
error
})
},
&metrics::CARD_ADD_TIME,
)
.await
}
pub async fn get_card_from_locker(
@ -176,22 +188,34 @@ pub async fn get_card_from_locker(
card_reference: &str,
locker_id: Option<String>,
) -> errors::RouterResult<payment_methods::Card> {
match state.conf.locker.locker_setup {
settings::LockerSetup::LegacyLocker => {
get_card_from_legacy_locker(
state,
&locker_id.get_required_value("locker_id")?,
card_reference,
)
.await
}
settings::LockerSetup::BasiliskLocker => {
get_card_from_hs_locker(state, customer_id, merchant_id, card_reference)
.await
.change_context(errors::ApiErrorResponse::InternalServerError)
.attach_printable("Failed while getting card from basilisk_hs")
}
}
metrics::GET_FROM_LOCKER.add(&metrics::CONTEXT, 1, &[]);
metrics::request::record_card_operation_time(
async {
match state.conf.locker.locker_setup {
settings::LockerSetup::LegacyLocker => {
get_card_from_legacy_locker(
state,
&locker_id.get_required_value("locker_id")?,
card_reference,
)
.await
}
settings::LockerSetup::BasiliskLocker => {
get_card_from_hs_locker(state, customer_id, merchant_id, card_reference)
.await
.change_context(errors::ApiErrorResponse::InternalServerError)
.attach_printable("Failed while getting card from basilisk_hs")
}
}
.map_err(|error| {
metrics::CARD_LOCKER_FAILURES.add(&metrics::CONTEXT, 1, &[]);
error
})
},
&metrics::CARD_GET_TIME,
)
.await
}
pub async fn delete_card_from_locker(
@ -200,14 +224,27 @@ pub async fn delete_card_from_locker(
merchant_id: &str,
card_reference: &str,
) -> errors::RouterResult<payment_methods::DeleteCardResp> {
match state.conf.locker.locker_setup {
settings::LockerSetup::LegacyLocker => {
delete_card(state, merchant_id, card_reference).await
}
settings::LockerSetup::BasiliskLocker => {
delete_card_from_hs_locker(state, customer_id, merchant_id, card_reference).await
}
}
metrics::DELETE_FROM_LOCKER.add(&metrics::CONTEXT, 1, &[]);
metrics::request::record_card_operation_time(
async {
match state.conf.locker.locker_setup {
settings::LockerSetup::LegacyLocker => {
delete_card(state, merchant_id, card_reference).await
}
settings::LockerSetup::BasiliskLocker => {
delete_card_from_hs_locker(state, customer_id, merchant_id, card_reference)
.await
}
}
.map_err(|error| {
metrics::CARD_LOCKER_FAILURES.add(&metrics::CONTEXT, 1, &[]);
error
})
},
&metrics::CARD_DELETE_TIME,
)
.await
}
#[instrument(skip_all)]
@ -1659,7 +1696,7 @@ impl BasiliskCardSupport {
enums::PaymentMethod::Card,
)
.await?;
metrics::TOKENIZED_DATA_COUNT.add(&metrics::CONTEXT, 1, &[]);
scheduler_metrics::TOKENIZED_DATA_COUNT.add(&metrics::CONTEXT, 1, &[]);
Ok(card)
}
}

View File

@ -5,6 +5,8 @@ use josekit::jwe;
use masking::PeekInterface;
use router_env::{instrument, tracing};
#[cfg(feature = "basilisk")]
use crate::routes::metrics;
use crate::{
configs::settings,
core::errors::{self, CustomResult, RouterResult},
@ -24,7 +26,7 @@ use crate::{
#[cfg(feature = "basilisk")]
use crate::{
db,
scheduler::{metrics, process_data, utils as process_tracker_utils},
scheduler::{metrics as scheduler_metrics, process_data, utils as process_tracker_utils},
types::storage::ProcessTrackerExt,
};
#[cfg(feature = "basilisk")]
@ -367,7 +369,7 @@ impl Vault {
let lookup_key = create_tokenize(state, value1, Some(value2), lookup_key).await?;
add_delete_tokenized_data_task(&*state.store, &lookup_key, pm).await?;
metrics::TOKENIZED_DATA_COUNT.add(&metrics::CONTEXT, 1, &[]);
scheduler_metrics::TOKENIZED_DATA_COUNT.add(&metrics::CONTEXT, 1, &[]);
Ok(lookup_key)
}
@ -437,6 +439,7 @@ pub async fn create_tokenize(
value2: Option<String>,
lookup_key: String,
) -> RouterResult<String> {
metrics::CREATED_TOKENIZED_CARD.add(&metrics::CONTEXT, 1, &[]);
let payload_to_be_encrypted = api::TokenizePayloadRequest {
value1,
value2: value2.unwrap_or_default(),
@ -499,9 +502,12 @@ pub async fn create_tokenize(
)?;
Ok(get_response.lookup_key)
}
Err(err) => Err(errors::ApiErrorResponse::InternalServerError)
.into_report()
.attach_printable(format!("Got 4xx from the basilisk locker: {err:?}")),
Err(err) => {
metrics::TEMP_LOCKER_FAILURES.add(&metrics::CONTEXT, 1, &[]);
Err(errors::ApiErrorResponse::InternalServerError)
.into_report()
.attach_printable(format!("Got 4xx from the basilisk locker: {err:?}"))
}
}
}
@ -511,6 +517,7 @@ pub async fn get_tokenized_data(
lookup_key: &str,
should_get_value2: bool,
) -> RouterResult<api::TokenizePayloadRequest> {
metrics::GET_TOKENIZED_CARD.add(&metrics::CONTEXT, 1, &[]);
let payload_to_be_encrypted = api::GetTokenizePayloadRequest {
lookup_key: lookup_key.to_string(),
get_value2: should_get_value2,
@ -566,9 +573,12 @@ pub async fn get_tokenized_data(
.attach_printable("Error getting TokenizePayloadRequest from tokenize response")?;
Ok(get_response)
}
Err(err) => Err(errors::ApiErrorResponse::InternalServerError)
.into_report()
.attach_printable(format!("Got 4xx from the basilisk locker: {err:?}")),
Err(err) => {
metrics::TEMP_LOCKER_FAILURES.add(&metrics::CONTEXT, 1, &[]);
Err(errors::ApiErrorResponse::InternalServerError)
.into_report()
.attach_printable(format!("Got 4xx from the basilisk locker: {err:?}"))
}
}
}
@ -577,6 +587,7 @@ pub async fn delete_tokenized_data(
state: &routes::AppState,
lookup_key: &str,
) -> RouterResult<String> {
metrics::DELETED_TOKENIZED_CARD.add(&metrics::CONTEXT, 1, &[]);
let payload_to_be_encrypted = api::DeleteTokenizeByTokenRequest {
lookup_key: lookup_key.to_string(),
service_name: VAULT_SERVICE_NAME.to_string(),
@ -635,9 +646,12 @@ pub async fn delete_tokenized_data(
)?;
Ok(delete_response)
}
Err(err) => Err(errors::ApiErrorResponse::InternalServerError)
.into_report()
.attach_printable(format!("Got 4xx from the basilisk locker: {err:?}")),
Err(err) => {
metrics::TEMP_LOCKER_FAILURES.add(&metrics::CONTEXT, 1, &[]);
Err(errors::ApiErrorResponse::InternalServerError)
.into_report()
.attach_printable(format!("Got 4xx from the basilisk locker: {err:?}"))
}
}
}
@ -721,14 +735,14 @@ pub async fn start_tokenize_data_workflow(
logger::error!("Error: Deleting Card From Locker : {}", resp);
retry_delete_tokenize(db, &delete_tokenize_data.pm, tokenize_tracker.to_owned())
.await?;
metrics::RETRIED_DELETE_DATA_COUNT.add(&metrics::CONTEXT, 1, &[]);
scheduler_metrics::RETRIED_DELETE_DATA_COUNT.add(&metrics::CONTEXT, 1, &[]);
}
}
Err(err) => {
logger::error!("Err: Deleting Card From Locker : {}", err);
retry_delete_tokenize(db, &delete_tokenize_data.pm, tokenize_tracker.to_owned())
.await?;
metrics::RETRIED_DELETE_DATA_COUNT.add(&metrics::CONTEXT, 1, &[]);
scheduler_metrics::RETRIED_DELETE_DATA_COUNT.add(&metrics::CONTEXT, 1, &[]);
}
}
Ok(())

View File

@ -8,7 +8,7 @@ use crate::{
errors::{self, RouterResult},
payments,
},
routes::AppState,
routes::{metrics, AppState},
services,
types::{self, api as api_types, storage, transformers::ForeignInto},
};
@ -180,6 +180,13 @@ pub async fn refresh_connector_auth(
.await
.change_context(errors::ApiErrorResponse::InternalServerError)
.attach_printable("Could not refresh access token")?;
metrics::ACCESS_TOKEN_CREATION.add(
&metrics::CONTEXT,
1,
&[metrics::request::add_attributes(
"connector",
connector.connector_name.to_string(),
)],
);
Ok(access_token_router_data.response)
}

View File

@ -7,8 +7,7 @@ use crate::{
mandate,
payments::{self, access_token, transformers, PaymentData},
},
routes::AppState,
scheduler::metrics,
routes::{metrics, AppState},
services,
types::{self, api, storage},
};

View File

@ -6,7 +6,7 @@ use crate::{
errors::{ConnectorErrorExt, RouterResult},
payments::{self, access_token, transformers, PaymentData},
},
routes::AppState,
routes::{metrics, AppState},
services,
types::{self, api, storage},
};
@ -43,6 +43,14 @@ impl Feature<api::Void, types::PaymentsCancelData>
call_connector_action: payments::CallConnectorAction,
_merchant_account: &storage::MerchantAccount,
) -> RouterResult<Self> {
metrics::PAYMENT_CANCEL_COUNT.add(
&metrics::CONTEXT,
1,
&[metrics::request::add_attributes(
"connector",
connector.connector_name.to_string(),
)],
);
self.decide_flow(
state,
connector,

View File

@ -8,7 +8,8 @@ use crate::{
errors::{self, ConnectorErrorExt, RouterResult},
payments::{self, access_token, transformers, PaymentData},
},
routes, services,
routes::{self, metrics},
services,
types::{self, api, storage},
utils::OptionExt,
};
@ -44,6 +45,14 @@ impl Feature<api::Session, types::PaymentsSessionData> for types::PaymentsSessio
call_connector_action: payments::CallConnectorAction,
_merchant_account: &storage::MerchantAccount,
) -> RouterResult<Self> {
metrics::SESSION_TOKEN_CREATED.add(
&metrics::CONTEXT,
1,
&[metrics::request::add_attributes(
"connector",
connector.connector_name.to_string(),
)],
);
self.decide_flow(
state,
connector,

View File

@ -20,8 +20,8 @@ use crate::{
payment_methods::{cards, vault},
},
db::StorageInterface,
routes::AppState,
scheduler::{metrics, workflows::payment_sync},
routes::{metrics, AppState},
scheduler::{metrics as scheduler_metrics, workflows::payment_sync},
services,
types::{
api::{self, enums as api_enums, CustomerAcceptanceExt, MandateValidationFieldsExt},
@ -483,7 +483,7 @@ where
match schedule_time {
Some(stime) => {
metrics::TASKS_ADDED_COUNT.add(&metrics::CONTEXT, 1, &[]); // Metrics
scheduler_metrics::TASKS_ADDED_COUNT.add(&metrics::CONTEXT, 1, &[]); // Metrics
super::add_process_sync_task(&*state.store, payment_attempt, stime)
.await
.into_report()
@ -663,6 +663,7 @@ pub async fn create_customer_if_not_exist<'a, F: Clone, R>(
..storage::CustomerNew::default()
};
metrics::CUSTOMER_CREATED.add(&metrics::CONTEXT, 1, &[]);
db.insert_customer(new_customer).await
}
})

View File

@ -10,6 +10,7 @@ use crate::{
payments::PaymentData,
},
db::StorageInterface,
routes::metrics,
services::RedirectForm,
types::{
self, api,
@ -323,6 +324,10 @@ async fn payment_response_update_tracker<F: Clone, T>(
.change_context(errors::ApiErrorResponse::InternalServerError)
.attach_printable("Could not parse the connector response")?;
if router_data.status == enums::AttemptStatus::Charged {
metrics::SUCCESSFUL_PAYMENT.add(&metrics::CONTEXT, 1, &[]);
}
let payment_attempt_update = storage::PaymentAttemptUpdate::ResponseUpdate {
status: router_data.status,
connector: Some(router_data.connector),

View File

@ -11,7 +11,7 @@ use crate::{
utils as core_utils,
},
db, logger,
routes::AppState,
routes::{metrics, AppState},
scheduler::{process_data, utils as process_tracker_utils, workflows::payment_sync},
services,
types::{
@ -98,6 +98,16 @@ pub async fn trigger_refund_to_gateway(
.clone()
.ok_or(errors::ApiErrorResponse::InternalServerError)?;
let connector_id = connector.to_string();
metrics::REFUND_COUNT.add(
&metrics::CONTEXT,
1,
&[metrics::request::add_attributes(
"connector",
connector.to_string(),
)],
);
let connector: api::ConnectorData = api::ConnectorData::get_connector_by_name(
&state.conf.connectors,
&connector_id,
@ -164,13 +174,25 @@ pub async fn trigger_refund_to_gateway(
refund_error_message: Some(err.message),
refund_error_code: Some(err.code),
},
Ok(response) => storage::RefundUpdate::Update {
connector_refund_id: response.connector_refund_id,
refund_status: response.refund_status,
sent_to_gateway: true,
refund_error_message: None,
refund_arn: "".to_string(),
},
Ok(response) => {
if response.refund_status == storage_models::enums::RefundStatus::Success {
metrics::SUCCESSFUL_REFUND.add(
&metrics::CONTEXT,
1,
&[metrics::request::add_attributes(
"connector",
connector.connector_name.to_string(),
)],
)
}
storage::RefundUpdate::Update {
connector_refund_id: response.connector_refund_id,
refund_status: response.refund_status,
sent_to_gateway: true,
refund_error_message: None,
refund_arn: "".to_string(),
}
}
};
let response = state

View File

@ -29,7 +29,9 @@ pub async fn merchant_account_create(
req: HttpRequest,
json_payload: web::Json<admin::MerchantAccountCreate>,
) -> HttpResponse {
let flow = Flow::MerchantsAccountCreate;
api::server_wrap(
flow,
state.get_ref(),
&req,
json_payload.into_inner(),
@ -60,11 +62,13 @@ pub async fn retrieve_merchant_account(
req: HttpRequest,
mid: web::Path<String>,
) -> HttpResponse {
let flow = Flow::MerchantsAccountRetrieve;
let payload = web::Json(admin::MerchantId {
merchant_id: mid.into_inner(),
})
.into_inner();
api::server_wrap(
flow,
state.get_ref(),
&req,
payload,
@ -97,8 +101,10 @@ pub async fn update_merchant_account(
mid: web::Path<String>,
json_payload: web::Json<admin::MerchantAccountUpdate>,
) -> HttpResponse {
let flow = Flow::MerchantsAccountUpdate;
let merchant_id = mid.into_inner();
api::server_wrap(
flow,
state.get_ref(),
&req,
json_payload.into_inner(),
@ -130,11 +136,13 @@ pub async fn delete_merchant_account(
req: HttpRequest,
mid: web::Path<String>,
) -> HttpResponse {
let flow = Flow::MerchantsAccountDelete;
let payload = web::Json(admin::MerchantId {
merchant_id: mid.into_inner(),
})
.into_inner();
api::server_wrap(
flow,
state.get_ref(),
&req,
payload,
@ -166,8 +174,10 @@ pub async fn payment_connector_create(
path: web::Path<String>,
json_payload: web::Json<admin::MerchantConnector>,
) -> HttpResponse {
let flow = Flow::MerchantConnectorsCreate;
let merchant_id = path.into_inner();
api::server_wrap(
flow,
state.get_ref(),
&req,
json_payload.into_inner(),
@ -202,6 +212,7 @@ pub async fn payment_connector_retrieve(
req: HttpRequest,
path: web::Path<(String, String)>,
) -> HttpResponse {
let flow = Flow::MerchantConnectorsRetrieve;
let (merchant_id, merchant_connector_id) = path.into_inner();
let payload = web::Json(admin::MerchantConnectorId {
merchant_id,
@ -209,6 +220,7 @@ pub async fn payment_connector_retrieve(
})
.into_inner();
api::server_wrap(
flow,
state.get_ref(),
&req,
payload,
@ -244,8 +256,10 @@ pub async fn payment_connector_list(
req: HttpRequest,
path: web::Path<String>,
) -> HttpResponse {
let flow = Flow::MerchantConnectorsList;
let merchant_id = path.into_inner();
api::server_wrap(
flow,
state.get_ref(),
&req,
merchant_id,
@ -282,8 +296,10 @@ pub async fn payment_connector_update(
path: web::Path<(String, String)>,
json_payload: web::Json<admin::MerchantConnector>,
) -> HttpResponse {
let flow = Flow::MerchantConnectorsUpdate;
let (merchant_id, merchant_connector_id) = path.into_inner();
api::server_wrap(
flow,
state.get_ref(),
&req,
json_payload.into_inner(),
@ -320,6 +336,7 @@ pub async fn payment_connector_delete(
req: HttpRequest,
path: web::Path<(String, String)>,
) -> HttpResponse {
let flow = Flow::MerchantConnectorsDelete;
let (merchant_id, merchant_connector_id) = path.into_inner();
let payload = web::Json(admin::MerchantConnectorId {
merchant_id,
@ -327,6 +344,7 @@ pub async fn payment_connector_delete(
})
.into_inner();
api::server_wrap(
flow,
state.get_ref(),
&req,
payload,
@ -348,9 +366,11 @@ pub async fn merchant_account_toggle_kv(
path: web::Path<String>,
json_payload: web::Json<admin::ToggleKVRequest>,
) -> HttpResponse {
let flow = Flow::ConfigKeyUpdate;
let payload = json_payload.into_inner();
let merchant_id = path.into_inner();
api::server_wrap(
flow,
state.get_ref(),
&req,
(merchant_id, payload),
@ -371,8 +391,10 @@ pub async fn merchant_account_kv_status(
req: HttpRequest,
path: web::Path<String>,
) -> HttpResponse {
let flow = Flow::ConfigKeyFetch;
let merchant_id = path.into_inner();
api::server_wrap(
flow,
state.get_ref(),
&req,
merchant_id,

View File

@ -32,10 +32,12 @@ pub async fn api_key_create(
path: web::Path<String>,
json_payload: web::Json<api_types::CreateApiKeyRequest>,
) -> impl Responder {
let flow = Flow::ApiKeyCreate;
let payload = json_payload.into_inner();
let merchant_id = path.into_inner();
api::server_wrap(
flow,
state.get_ref(),
&req,
payload,
@ -79,9 +81,11 @@ pub async fn api_key_retrieve(
req: HttpRequest,
path: web::Path<(String, String)>,
) -> impl Responder {
let flow = Flow::ApiKeyRetrieve;
let (_merchant_id, key_id) = path.into_inner();
api::server_wrap(
flow,
state.get_ref(),
&req,
&key_id,
@ -117,10 +121,12 @@ pub async fn api_key_update(
path: web::Path<(String, String)>,
json_payload: web::Json<api_types::UpdateApiKeyRequest>,
) -> impl Responder {
let flow = Flow::ApiKeyUpdate;
let (_merchant_id, key_id) = path.into_inner();
let payload = json_payload.into_inner();
api::server_wrap(
flow,
state.get_ref(),
&req,
(&key_id, payload),
@ -155,9 +161,11 @@ pub async fn api_key_revoke(
req: HttpRequest,
path: web::Path<(String, String)>,
) -> impl Responder {
let flow = Flow::ApiKeyRevoke;
let (_merchant_id, key_id) = path.into_inner();
api::server_wrap(
flow,
state.get_ref(),
&req,
&key_id,
@ -192,12 +200,14 @@ pub async fn api_key_list(
path: web::Path<String>,
query: web::Query<api_types::ListApiKeyConstraints>,
) -> impl Responder {
let flow = Flow::ApiKeyList;
let list_api_key_constraints = query.into_inner();
let limit = list_api_key_constraints.limit;
let offset = list_api_key_constraints.skip;
let merchant_id = path.into_inner();
api::server_wrap(
flow,
state.get_ref(),
&req,
(limit, offset, merchant_id),

View File

@ -14,9 +14,11 @@ pub async fn config_key_retrieve(
req: HttpRequest,
path: web::Path<String>,
) -> impl Responder {
let flow = Flow::ConfigKeyFetch;
let key = path.into_inner();
api::server_wrap(
flow,
state.get_ref(),
&req,
&key,
@ -33,11 +35,13 @@ pub async fn config_key_update(
path: web::Path<String>,
json_payload: web::Json<api_types::ConfigUpdate>,
) -> impl Responder {
let flow = Flow::ConfigKeyUpdate;
let mut payload = json_payload.into_inner();
let key = path.into_inner();
payload.key = key;
api::server_wrap(
flow,
state.get_ref(),
&req,
&payload,

View File

@ -29,7 +29,9 @@ pub async fn customers_create(
req: HttpRequest,
json_payload: web::Json<customers::CustomerRequest>,
) -> HttpResponse {
let flow = Flow::CustomersCreate;
api::server_wrap(
flow,
state.get_ref(),
&req,
json_payload.into_inner(),
@ -60,6 +62,7 @@ pub async fn customers_retrieve(
req: HttpRequest,
path: web::Path<String>,
) -> HttpResponse {
let flow = Flow::CustomersRetrieve;
let payload = web::Json(customers::CustomerId {
customer_id: path.into_inner(),
})
@ -72,6 +75,7 @@ pub async fn customers_retrieve(
};
api::server_wrap(
flow,
state.get_ref(),
&req,
payload,
@ -104,9 +108,11 @@ pub async fn customers_update(
path: web::Path<String>,
mut json_payload: web::Json<customers::CustomerRequest>,
) -> HttpResponse {
let flow = Flow::CustomersUpdate;
let customer_id = path.into_inner();
json_payload.customer_id = customer_id;
api::server_wrap(
flow,
state.get_ref(),
&req,
json_payload.into_inner(),
@ -137,11 +143,13 @@ pub async fn customers_delete(
req: HttpRequest,
path: web::Path<String>,
) -> impl Responder {
let flow = Flow::CustomersCreate;
let payload = web::Json(customers::CustomerId {
customer_id: path.into_inner(),
})
.into_inner();
api::server_wrap(
flow,
state.get_ref(),
&req,
payload,
@ -157,11 +165,13 @@ pub async fn get_customer_mandates(
req: HttpRequest,
path: web::Path<String>,
) -> impl Responder {
let flow = Flow::CustomersGetMandates;
let customer_id = customers::CustomerId {
customer_id: path.into_inner(),
};
api::server_wrap(
flow,
state.get_ref(),
&req,
customer_id,

View File

@ -14,8 +14,10 @@ pub async fn ephemeral_key_create(
req: HttpRequest,
json_payload: web::Json<customers::CustomerId>,
) -> HttpResponse {
let flow = Flow::EphemeralKeyCreate;
let payload = json_payload.into_inner();
api::server_wrap(
flow,
state.get_ref(),
&req,
payload,
@ -33,8 +35,10 @@ pub async fn ephemeral_key_delete(
req: HttpRequest,
path: web::Path<String>,
) -> HttpResponse {
let flow = Flow::EphemeralKeyDelete;
let payload = path.into_inner();
api::server_wrap(
flow,
state.get_ref(),
&req,
payload,

View File

@ -32,10 +32,12 @@ pub async fn get_mandate(
req: HttpRequest,
path: web::Path<String>,
) -> HttpResponse {
let flow = Flow::MandatesRetrieve;
let mandate_id = mandates::MandateId {
mandate_id: path.into_inner(),
};
api::server_wrap(
flow,
state.get_ref(),
&req,
mandate_id,
@ -69,10 +71,12 @@ pub async fn revoke_mandate(
req: HttpRequest,
path: web::Path<String>,
) -> HttpResponse {
let flow = Flow::MandatesRevoke;
let mandate_id = mandates::MandateId {
mandate_id: path.into_inner(),
};
api::server_wrap(
flow,
state.get_ref(),
&req,
mandate_id,

View File

@ -1,4 +1,4 @@
use router_env::{counter_metric, global_meter, metrics_context};
use router_env::{counter_metric, global_meter, histogram_metric, metrics_context};
metrics_context!(CONTEXT);
global_meter!(GLOBAL_METER, "ROUTER_API");
@ -7,3 +7,66 @@ counter_metric!(HEALTH_METRIC, GLOBAL_METER); // No. of health API hits
counter_metric!(KV_MISS, GLOBAL_METER); // No. of KV misses
#[cfg(feature = "kms")]
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);
histogram_metric!(REQUEST_TIME, GLOBAL_METER);
// Operation Level Metrics
counter_metric!(PAYMENT_COUNT, GLOBAL_METER);
counter_metric!(SUCCESSFUL_PAYMENT, GLOBAL_METER);
counter_metric!(REFUND_COUNT, GLOBAL_METER);
counter_metric!(SUCCESSFUL_REFUND, GLOBAL_METER);
counter_metric!(PAYMENT_CANCEL_COUNT, GLOBAL_METER);
counter_metric!(SUCCESSFUL_CANCEL, GLOBAL_METER);
counter_metric!(MANDATE_COUNT, GLOBAL_METER);
counter_metric!(SUBSEQUENT_MANDATE_PAYMENT, GLOBAL_METER);
counter_metric!(RETRY_COUNT, GLOBAL_METER);
counter_metric!(STORED_TO_LOCKER, GLOBAL_METER);
counter_metric!(GET_FROM_LOCKER, GLOBAL_METER);
counter_metric!(DELETE_FROM_LOCKER, GLOBAL_METER);
counter_metric!(CREATED_TOKENIZED_CARD, GLOBAL_METER);
counter_metric!(DELETED_TOKENIZED_CARD, GLOBAL_METER);
counter_metric!(GET_TOKENIZED_CARD, GLOBAL_METER);
counter_metric!(CUSTOMER_CREATED, GLOBAL_METER);
counter_metric!(CUSTOMER_REDACTED, GLOBAL_METER);
counter_metric!(API_KEY_CREATED, GLOBAL_METER);
counter_metric!(API_KEY_REVOKED, GLOBAL_METER);
// Flow Specific Metrics
counter_metric!(ACCESS_TOKEN_CREATION, GLOBAL_METER);
histogram_metric!(CONNECTOR_REQUEST_TIME, GLOBAL_METER);
counter_metric!(SESSION_TOKEN_CREATED, GLOBAL_METER);
counter_metric!(CONNECTOR_CALL_COUNT, GLOBAL_METER); // Attributes needed
counter_metric!(THREE_DS_PAYMENT_COUNT, GLOBAL_METER);
counter_metric!(THREE_DS_DOWNGRADE_COUNT, GLOBAL_METER);
counter_metric!(RESPONSE_DESERIALIZATION_FAILURE, GLOBAL_METER);
counter_metric!(CONNECTOR_ERROR_RESPONSE_COUNT, GLOBAL_METER);
counter_metric!(REQUEST_TIMEOUT_COUNT, GLOBAL_METER);
// Connector Level Metric
counter_metric!(REQUEST_BUILD_FAILURE, GLOBAL_METER);
counter_metric!(UNIMPLEMENTED_FLOW, GLOBAL_METER);
// Service Level
counter_metric!(CARD_LOCKER_FAILURES, GLOBAL_METER);
counter_metric!(TEMP_LOCKER_FAILURES, GLOBAL_METER);
histogram_metric!(CARD_ADD_TIME, GLOBAL_METER);
histogram_metric!(CARD_GET_TIME, GLOBAL_METER);
histogram_metric!(CARD_DELETE_TIME, GLOBAL_METER);
pub mod request;
pub mod utils;

View File

@ -0,0 +1,36 @@
use super::utils as metric_utils;
pub async fn record_request_time_metric<F, R>(future: F, flow: router_env::Flow) -> R
where
F: futures::Future<Output = R>,
{
let key = "request_type";
super::REQUESTS_RECEIVED.add(&super::CONTEXT, 1, &[add_attributes(key, flow.to_string())]);
let (result, time) = metric_utils::time_future(future).await;
super::REQUEST_TIME.record(
&super::CONTEXT,
time.as_secs_f64(),
&[add_attributes(key, flow.to_string())],
);
result
}
#[inline]
pub async fn record_card_operation_time<F, R>(
future: F,
metric: &once_cell::sync::Lazy<router_env::opentelemetry::metrics::Histogram<f64>>,
) -> R
where
F: futures::Future<Output = R>,
{
let (result, time) = metric_utils::time_future(future).await;
metric.record(&super::CONTEXT, time.as_secs_f64(), &[]);
result
}
pub fn add_attributes<T: Into<router_env::opentelemetry::Value>>(
key: &'static str,
value: T,
) -> router_env::opentelemetry::KeyValue {
router_env::opentelemetry::KeyValue::new(key, value)
}

View File

@ -0,0 +1,12 @@
use std::time;
#[inline]
pub async fn time_future<F, R>(future: F) -> (R, time::Duration)
where
F: futures::Future<Output = R>,
{
let start = time::Instant::now();
let result = future.await;
let time_spent = start.elapsed();
(result, time_spent)
}

View File

@ -29,7 +29,9 @@ pub async fn create_payment_method_api(
req: HttpRequest,
json_payload: web::Json<payment_methods::PaymentMethodCreate>,
) -> HttpResponse {
let flow = Flow::PaymentMethodsCreate;
api::server_wrap(
flow,
state.get_ref(),
&req,
json_payload.into_inner(),
@ -71,6 +73,7 @@ pub async fn list_payment_method_api(
req: HttpRequest,
json_payload: web::Query<payment_methods::PaymentMethodListRequest>,
) -> HttpResponse {
let flow = Flow::PaymentMethodsList;
let payload = json_payload.into_inner();
let (auth, _) = match auth::check_client_secret_and_get_auth(req.headers(), &payload) {
@ -79,6 +82,7 @@ pub async fn list_payment_method_api(
};
api::server_wrap(
flow,
state.get_ref(),
&req,
payload,
@ -119,6 +123,7 @@ pub async fn list_customer_payment_method_api(
req: HttpRequest,
json_payload: web::Query<payment_methods::PaymentMethodListRequest>,
) -> HttpResponse {
let flow = Flow::CustomerPaymentMethodsList;
let customer_id = customer_id.into_inner().0;
let auth_type = match auth::is_ephemeral_auth(req.headers(), &*state.store, &customer_id).await
@ -128,6 +133,7 @@ pub async fn list_customer_payment_method_api(
};
api::server_wrap(
flow,
state.get_ref(),
&req,
json_payload.into_inner(),
@ -162,12 +168,14 @@ pub async fn payment_method_retrieve_api(
req: HttpRequest,
path: web::Path<String>,
) -> HttpResponse {
let flow = Flow::PaymentMethodsRetrieve;
let payload = web::Json(PaymentMethodId {
payment_method_id: path.into_inner(),
})
.into_inner();
api::server_wrap(
flow,
state.get_ref(),
&req,
payload,
@ -202,9 +210,11 @@ pub async fn payment_method_update_api(
path: web::Path<String>,
json_payload: web::Json<payment_methods::PaymentMethodUpdate>,
) -> HttpResponse {
let flow = Flow::PaymentMethodsUpdate;
let payment_method_id = path.into_inner();
api::server_wrap(
flow,
state.get_ref(),
&req,
json_payload.into_inner(),
@ -244,10 +254,12 @@ pub async fn payment_method_delete_api(
req: HttpRequest,
payment_method_id: web::Path<(String,)>,
) -> HttpResponse {
let flow = Flow::PaymentMethodsDelete;
let pm = PaymentMethodId {
payment_method_id: payment_method_id.into_inner().0,
};
api::server_wrap(
flow,
state.get_ref(),
&req,
pm,

View File

@ -34,6 +34,7 @@ pub async fn payments_create(
req: actix_web::HttpRequest,
json_payload: web::Json<payment_types::PaymentsRequest>,
) -> impl Responder {
let flow = Flow::PaymentsCreate;
let payload = json_payload.into_inner();
if let Some(api_enums::CaptureMethod::Scheduled) = payload.capture_method {
@ -41,6 +42,7 @@ pub async fn payments_create(
};
api::server_wrap(
flow,
state.get_ref(),
&req,
payload,
@ -82,13 +84,14 @@ pub async fn payments_start(
req: actix_web::HttpRequest,
path: web::Path<(String, String, String)>,
) -> impl Responder {
let flow = Flow::PaymentsStart;
let (payment_id, merchant_id, attempt_id) = path.into_inner();
let payload = payment_types::PaymentsStartRequest {
payment_id: payment_id.clone(),
merchant_id: merchant_id.clone(),
attempt_id: attempt_id.clone(),
};
api::server_wrap(
api::server_wrap(flow,
state.get_ref(),
&req,
payload,
@ -133,6 +136,7 @@ pub async fn payments_retrieve(
path: web::Path<String>,
json_payload: web::Query<payment_types::PaymentRetrieveBody>,
) -> impl Responder {
let flow = Flow::PaymentsRetrieve;
let payload = payment_types::PaymentsRetrieveRequest {
resource_id: payment_types::PaymentIdType::PaymentIntentId(path.to_string()),
merchant_id: json_payload.merchant_id.clone(),
@ -146,6 +150,7 @@ pub async fn payments_retrieve(
};
api::server_wrap(
flow,
state.get_ref(),
&req,
payload,
@ -190,6 +195,7 @@ pub async fn payments_update(
json_payload: web::Json<payment_types::PaymentsRequest>,
path: web::Path<String>,
) -> impl Responder {
let flow = Flow::PaymentsUpdate;
let mut payload = json_payload.into_inner();
if let Some(api_enums::CaptureMethod::Scheduled) = payload.capture_method {
@ -206,6 +212,7 @@ pub async fn payments_update(
};
api::server_wrap(
flow,
state.get_ref(),
&req,
payload,
@ -249,6 +256,7 @@ pub async fn payments_confirm(
json_payload: web::Json<payment_types::PaymentsRequest>,
path: web::Path<String>,
) -> impl Responder {
let flow = Flow::PaymentsConfirm;
let mut payload = json_payload.into_inner();
if let Some(api_enums::CaptureMethod::Scheduled) = payload.capture_method {
@ -266,6 +274,7 @@ pub async fn payments_confirm(
};
api::server_wrap(
flow,
state.get_ref(),
&req,
payload,
@ -309,12 +318,14 @@ pub async fn payments_capture(
json_payload: web::Json<payment_types::PaymentsCaptureRequest>,
path: web::Path<String>,
) -> impl Responder {
let flow = Flow::PaymentsCapture;
let capture_payload = payment_types::PaymentsCaptureRequest {
payment_id: Some(path.into_inner()),
..json_payload.into_inner()
};
api::server_wrap(
flow,
state.get_ref(),
&req,
capture_payload,
@ -354,9 +365,11 @@ pub async fn payments_connector_session(
req: actix_web::HttpRequest,
json_payload: web::Json<payment_types::PaymentsSessionRequest>,
) -> impl Responder {
let flow = Flow::PaymentsSessionToken;
let sessions_payload = json_payload.into_inner();
api::server_wrap(
flow,
state.get_ref(),
&req,
sessions_payload,
@ -405,6 +418,7 @@ pub async fn payments_redirect_response(
req: actix_web::HttpRequest,
path: web::Path<(String, String, String)>,
) -> impl Responder {
let flow = Flow::PaymentsRedirect;
let (payment_id, merchant_id, connector) = path.into_inner();
let param_string = req.query_string();
@ -417,6 +431,7 @@ pub async fn payments_redirect_response(
connector: Some(connector),
};
api::server_wrap(
flow,
state.get_ref(),
&req,
payload,
@ -439,6 +454,7 @@ pub async fn payments_complete_authorize(
json_payload: web::Form<serde_json::Value>,
path: web::Path<(String, String, String)>,
) -> impl Responder {
let flow = Flow::PaymentsRedirect;
let (payment_id, merchant_id, connector) = path.into_inner();
let param_string = req.query_string();
@ -451,6 +467,7 @@ pub async fn payments_complete_authorize(
connector: Some(connector),
};
api::server_wrap(
flow,
state.get_ref(),
&req,
payload,
@ -492,11 +509,13 @@ pub async fn payments_cancel(
json_payload: web::Json<payment_types::PaymentsCancelRequest>,
path: web::Path<String>,
) -> impl Responder {
let flow = Flow::PaymentsCancel;
let mut payload = json_payload.into_inner();
let payment_id = path.into_inner();
payload.payment_id = payment_id;
api::server_wrap(
flow,
state.get_ref(),
&req,
payload,
@ -548,8 +567,10 @@ pub async fn payments_list(
req: actix_web::HttpRequest,
payload: web::Query<payment_types::PaymentListConstraints>,
) -> impl Responder {
let flow = Flow::PaymentsList;
let payload = payload.into_inner();
api::server_wrap(
flow,
state.get_ref(),
&req,
payload,

View File

@ -7,36 +7,42 @@ use router_env::{instrument, tracing, Flow};
#[instrument(skip_all, fields(flow = ?Flow::PayoutsCreate))]
// #[post("/create")]
pub async fn payouts_create() -> impl Responder {
let _flow = Flow::PayoutsCreate;
http_response("create")
}
#[instrument(skip_all, fields(flow = ?Flow::PayoutsRetrieve))]
// #[get("/retrieve")]
pub async fn payouts_retrieve() -> impl Responder {
let _flow = Flow::PayoutsRetrieve;
http_response("retrieve")
}
#[instrument(skip_all, fields(flow = ?Flow::PayoutsUpdate))]
// #[post("/update")]
pub async fn payouts_update() -> impl Responder {
let _flow = Flow::PayoutsUpdate;
http_response("update")
}
#[instrument(skip_all, fields(flow = ?Flow::PayoutsReverse))]
// #[post("/reverse")]
pub async fn payouts_reverse() -> impl Responder {
let _flow = Flow::PayoutsReverse;
http_response("reverse")
}
#[instrument(skip_all, fields(flow = ?Flow::PayoutsCancel))]
// #[post("/cancel")]
pub async fn payouts_cancel() -> impl Responder {
let _flow = Flow::PayoutsCancel;
http_response("cancel")
}
#[instrument(skip_all, fields(flow = ?Flow::PayoutsAccounts))]
// #[get("/accounts")]
pub async fn payouts_accounts() -> impl Responder {
let _flow = Flow::PayoutsAccounts;
http_response("accounts")
}

View File

@ -30,7 +30,9 @@ pub async fn refunds_create(
req: HttpRequest,
json_payload: web::Json<refunds::RefundRequest>,
) -> HttpResponse {
let flow = Flow::RefundsCreate;
api::server_wrap(
flow,
state.get_ref(),
&req,
json_payload.into_inner(),
@ -64,9 +66,11 @@ pub async fn refunds_retrieve(
req: HttpRequest,
path: web::Path<String>,
) -> HttpResponse {
let flow = Flow::RefundsRetrieve;
let refund_id = path.into_inner();
api::server_wrap(
flow,
state.get_ref(),
&req,
refund_id,
@ -104,8 +108,10 @@ pub async fn refunds_update(
json_payload: web::Json<refunds::RefundUpdateRequest>,
path: web::Path<String>,
) -> HttpResponse {
let flow = Flow::RefundsUpdate;
let refund_id = path.into_inner();
api::server_wrap(
flow,
state.get_ref(),
&req,
json_payload.into_inner(),
@ -148,7 +154,9 @@ pub async fn refunds_list(
req: HttpRequest,
payload: web::Query<api_models::refunds::RefundListRequest>,
) -> HttpResponse {
let flow = Flow::RefundsList;
api::server_wrap(
flow,
state.get_ref(),
&req,
payload.into_inner(),

View File

@ -15,9 +15,11 @@ pub async fn receive_incoming_webhook<W: api_types::OutgoingWebhookType>(
body: web::Bytes,
path: web::Path<(String, String)>,
) -> impl Responder {
let flow = Flow::IncomingWebhookReceive;
let (merchant_id, connector_name) = path.into_inner();
api::server_wrap(
flow,
state.get_ref(),
&req,
body,

View File

@ -26,7 +26,7 @@ use crate::{
payments,
},
logger,
routes::{app::AppStateInfo, AppState},
routes::{app::AppStateInfo, metrics, AppState},
services::authentication as auth,
types::{self, api, storage, ErrorResponse},
};
@ -103,9 +103,17 @@ pub trait ConnectorIntegration<T, Req, Resp>: ConnectorIntegrationAny<T, Req, Re
fn build_request(
&self,
_req: &types::RouterData<T, Req, Resp>,
req: &types::RouterData<T, Req, Resp>,
_connectors: &Connectors,
) -> CustomResult<Option<Request>, errors::ConnectorError> {
metrics::UNIMPLEMENTED_FLOW.add(
&metrics::CONTEXT,
1,
&[metrics::request::add_attributes(
"connector",
req.connector.clone(),
)],
);
Ok(None)
}
@ -179,7 +187,40 @@ where
Ok(router_data)
}
payments::CallConnectorAction::Trigger => {
match connector_integration.build_request(req, &state.conf.connectors)? {
metrics::CONNECTOR_CALL_COUNT.add(
&metrics::CONTEXT,
1,
&[
metrics::request::add_attributes("connector", req.connector.to_string()),
metrics::request::add_attributes(
"flow",
std::any::type_name::<T>()
.split("::")
.last()
.unwrap_or_default()
.to_string(),
),
],
);
match connector_integration
.build_request(req, &state.conf.connectors)
.map_err(|error| {
if matches!(
error.current_context(),
&errors::ConnectorError::RequestEncodingFailed
| &errors::ConnectorError::RequestEncodingFailedWithReason(_)
) {
metrics::RESPONSE_DESERIALIZATION_FAILURE.add(
&metrics::CONTEXT,
1,
&[metrics::request::add_attributes(
"connector",
req.connector.to_string(),
)],
)
}
error
})? {
Some(request) => {
logger::debug!(connector_request=?request);
let response = call_connector_api(state, request).await;
@ -187,8 +228,32 @@ where
match response {
Ok(body) => {
let response = match body {
Ok(body) => connector_integration.handle_response(req, body)?,
Ok(body) => connector_integration
.handle_response(req, body)
.map_err(|error| {
if error.current_context()
== &errors::ConnectorError::ResponseDeserializationFailed
{
metrics::RESPONSE_DESERIALIZATION_FAILURE.add(
&metrics::CONTEXT,
1,
&[metrics::request::add_attributes(
"connector",
req.connector.to_string(),
)],
)
}
error
})?,
Err(body) => {
metrics::CONNECTOR_ERROR_RESPONSE_COUNT.add(
&metrics::CONTEXT,
1,
&[metrics::request::add_attributes(
"connector",
req.connector.clone(),
)],
);
let error = connector_integration.get_error_response(body)?;
router_data.response = Err(error);
@ -277,7 +342,10 @@ async fn send_request(
.send()
.await
.map_err(|error| match error {
error if error.is_timeout() => errors::ApiClientError::RequestTimeoutReceived,
error if error.is_timeout() => {
metrics::REQUEST_BUILD_FAILURE.add(&metrics::CONTEXT, 1, &[]);
errors::ApiClientError::RequestTimeoutReceived
}
_ => errors::ApiClientError::RequestNotSent(error.to_string()),
})
.into_report()
@ -454,6 +522,7 @@ where
fields(request_method, request_url_path)
)]
pub async fn server_wrap<'a, 'b, A, T, U, Q, F, Fut, E>(
flow: router_env::Flow,
state: &'b A,
request: &'a HttpRequest,
payload: T,
@ -476,8 +545,12 @@ where
let start_instant = Instant::now();
logger::info!(tag = ?Tag::BeginRequest);
let res = match server_wrap_util(state, request, payload, func, api_auth).await {
let res = match metrics::request::record_request_time_metric(
server_wrap_util(state, request, payload, func, api_auth),
flow,
)
.await
{
Ok(ApplicationResponse::Json(response)) => match serde_json::to_string(&response) {
Ok(res) => http_response_json(res),
Err(_) => http_response_err(

View File

@ -138,6 +138,8 @@ pub enum Flow {
PayoutsCancel,
/// Payouts accounts flow.
PayoutsAccounts,
/// Payments Redirect flow.
PaymentsRedirect,
/// Refunds create flow.
RefundsCreate,
/// Refunds retrieve flow.