Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add IP metrics to REST #2174

Merged
merged 17 commits into from
Jun 28, 2021
3 changes: 3 additions & 0 deletions hedera-mirror-rest/accounts.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ const utils = require('./utils');
const transactions = require('./transactions');
const {NotFoundError} = require('./errors/notFoundError');
const {DbError} = require('./errors/dbError');
const {recordIpAndEndpoint} = require('./middleware/metricsHandler');

/**
* Processes one row of the results of the SQL query and format into API return format
Expand Down Expand Up @@ -145,6 +146,7 @@ const toQueryObject = (queryAndParams) => {
* @return {Promise} Promise for PostgreSQL query
*/
const getAccounts = async (req, res) => {
recordIpAndEndpoint('/accounts/{id}', req.ip);
steven-sheehy marked this conversation as resolved.
Show resolved Hide resolved
// Validate query parameters first
await utils.validateReq(req);

Expand Down Expand Up @@ -208,6 +210,7 @@ const getAccounts = async (req, res) => {
* @return {} None.
*/
const getOneAccount = async (req, res) => {
recordIpAndEndpoint('/accounts/{id}', req.ip);
// Validate query parameters first
await utils.validateReq(req);

Expand Down
2 changes: 2 additions & 0 deletions hedera-mirror-rest/balances.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ const config = require('./config');
const constants = require('./constants');
const EntityId = require('./entityId');
const utils = require('./utils');
const {recordIpAndEndpoint} = require('./middleware/metricsHandler');

const formatBalancesResult = (req, result, limit, order) => {
const {rows, sqlQuery} = result;
Expand Down Expand Up @@ -73,6 +74,7 @@ const formatBalancesResult = (req, result, limit, order) => {
* @return {Promise} Promise for PostgreSQL query
*/
const getBalances = async (req, res) => {
recordIpAndEndpoint('/balances', req.ip);
await utils.validateReq(req);

// Parse the filter parameters for credit/debit, account-numbers, timestamp and pagination
Expand Down
3 changes: 2 additions & 1 deletion hedera-mirror-rest/config/application.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,11 @@ hedera:
metrics:
enabled: true
config:
authentication: true
authentication: false
password: password
username: metrics
uriPath: '/swagger'
elasticsearch: 'http://127.0.0.1:9200'
openapi:
specFileName: 'openapi'
swaggerUIPath: 'docs'
Expand Down
15 changes: 15 additions & 0 deletions hedera-mirror-rest/middleware/metricsHandler.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ const swStats = require('swagger-stats');
const config = require('../config');
const oasHandler = require('./openapiHandler');

const client = require('prom-client');

const metricsHandler = () => {
const defaultMetricsConfig = {
name: process.env.npm_package_name,
Expand All @@ -52,6 +54,19 @@ const onMetricsAuthenticate = async (req, username, password) => {
});
};

const ipEndpointHistogram = new client.Histogram({
steven-sheehy marked this conversation as resolved.
Show resolved Hide resolved
name: 'ip_count',
help: 'test',
ijungmann marked this conversation as resolved.
Show resolved Hide resolved
buckets: [0.1, 5, 15, 50, 100, 500],
labelNames: ['endpoint', 'ip'],
});

const recordIpAndEndpoint = (endpoint, ip) => {
ipEndpointHistogram.labels(endpoint, ip).observe(1);
};

module.exports = {
metricsHandler,
ipEndpointHistogram,
recordIpAndEndpoint,
};
4 changes: 4 additions & 0 deletions hedera-mirror-rest/schedules.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ const constants = require('./constants');
const EntityId = require('./entityId');
const utils = require('./utils');
const {NotFoundError} = require('./errors/notFoundError');
const {recordIpAndEndpoint} = require('./middleware/metricsHandler');

const scheduleSelectFields = [
'e.key',
Expand Down Expand Up @@ -122,6 +123,8 @@ const formatScheduleRow = (row) => {
* @returns {Promise<void>}
*/
const getScheduleById = async (req, res) => {
recordIpAndEndpoint('/schedules/{id}', req.ip);

const scheduleId = EntityId.fromString(req.params.id, constants.filterKeys.SCHEDULEID).getEncodedId();
if (logger.isTraceEnabled()) {
logger.trace(`getScheduleById query: ${getScheduleByIdQuery}, params: ${scheduleId}`);
Expand Down Expand Up @@ -216,6 +219,7 @@ const getScheduleEntities = async (pgSqlQuery, pgSqlParams) => {
* @returns {Promise<void>}
*/
const getSchedules = async (req, res) => {
recordIpAndEndpoint('/schedules/{id}', req.ip);
// extract filters from query param
const filters = utils.buildFilterObject(req.query);

Expand Down
2 changes: 2 additions & 0 deletions hedera-mirror-rest/stateproof.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ const utils = require('./utils');
const {DbError} = require('./errors/dbError');
const {NotFoundError} = require('./errors/notFoundError');
const {FileDownloadError} = require('./errors/fileDownloadError');
const {recordIpAndEndpoint} = require('./middleware/metricsHandler');

/**
* Get the consensus_ns of the transaction. Throws exception if no such successful transaction found or multiple such
Expand Down Expand Up @@ -276,6 +277,7 @@ const formatRecordFile = (data, transactionId, scheduled) => {
* @returns none
*/
const getStateProofForTransaction = async (req, res) => {
recordIpAndEndpoint('/transactions/:id/stateproof', req.ip);
const filters = utils.buildFilterObject(req.query);
await utils.validateAndParseFilters(filters);

Expand Down
4 changes: 4 additions & 0 deletions hedera-mirror-rest/tokens.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ const EntityId = require('./entityId');
const utils = require('./utils');
const {InvalidArgumentError} = require('./errors/invalidArgumentError');
const {NotFoundError} = require('./errors/notFoundError');
const {recordIpAndEndpoint} = require('./middleware/metricsHandler');

// select columns
const sqlQueryColumns = {
Expand Down Expand Up @@ -226,6 +227,7 @@ const validateTokensFilters = (filters) => {
};

const getTokensRequest = async (req, res) => {
recordIpAndEndpoint('/tokens', req.ip);
// extract filters from query param
const filters = utils.buildFilterObject(req.query);

Expand Down Expand Up @@ -278,6 +280,7 @@ const getTokensRequest = async (req, res) => {
};

const getTokenInfoRequest = async (req, res) => {
recordIpAndEndpoint('/tokens/{id}', req.ip);
const tokenId = EntityId.fromString(req.params.id, constants.filterKeys.TOKENID).getEncodedId();

// concatenate queries to produce final sql query
Expand Down Expand Up @@ -393,6 +396,7 @@ const formatTokenBalanceRow = (row) => {
* @param {Response} res HTTP response object
*/
const getTokenBalances = async (req, res) => {
recordIpAndEndpoint('/tokens/{id}/balances', req.ip);
const tokenId = EntityId.fromString(req.params.id, constants.filterKeys.TOKENID).getEncodedId();
const filters = utils.buildFilterObject(req.query);
await utils.validateAndParseFilters(filters);
Expand Down
4 changes: 4 additions & 0 deletions hedera-mirror-rest/topicmessage.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ const EntityId = require('./entityId');
const utils = require('./utils');
const {NotFoundError} = require('./errors/notFoundError');
const {InvalidArgumentError} = require('./errors/invalidArgumentError');
const {ipEndpointHistogram} = require('./middleware/metricsHandler');

const topicMessageColumns = {
CONSENSUS_TIMESTAMP: 'consensus_timestamp',
Expand Down Expand Up @@ -131,6 +132,7 @@ const formatTopicMessageRow = (row, messageEncoding) => {
* @return {Promise} Promise for PostgreSQL query
*/
const getMessageByConsensusTimestamp = async (req, res) => {
recordIpAndEndpoint('/topics/messages/{consensusTimestamp', req.ip);
const consensusTimestampParam = req.params.consensusTimestamp;
validateConsensusTimestampParam(consensusTimestampParam);

Expand All @@ -152,6 +154,7 @@ const getMessageByConsensusTimestamp = async (req, res) => {
* @return {Promise} Promise for PostgreSQL query
*/
const getMessageByTopicAndSequenceRequest = async (req, res) => {
recordIpAndEndpoint('/topics/{id}/messages/{sequenceNumber}', req.ip);
const topicIdStr = req.params.id;
const seqNum = req.params.sequencenumber;
validateGetSequenceMessageParams(topicIdStr, seqNum);
Expand All @@ -175,6 +178,7 @@ const getMessageByTopicAndSequenceRequest = async (req, res) => {
* @returns {Promise} Promise for PostgreSQL query
*/
const getTopicMessages = async (req, res) => {
recordIpAndEndpoint('/topics/{id}/messages', req.ip);
// retrieve param and filters from request
const topicIdStr = req.params.id;
const filters = utils.buildFilterObject(req.query);
Expand Down
7 changes: 5 additions & 2 deletions hedera-mirror-rest/transactions.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ const constants = require('./constants');
const EntityId = require('./entityId');
const TransactionId = require('./transactionId');
const {NotFoundError} = require('./errors/notFoundError');

const {recordIpAndEndpoint} = require('./middleware/metricsHandler');
/**
* Gets the select clause with token transfers sorted by token_id and account_id in the specified order
*
Expand Down Expand Up @@ -433,6 +433,8 @@ const reqToSql = async function (req) {
* @return {Promise} Promise for PostgreSQL query
*/
const getTransactions = async (req, res) => {
recordIpAndEndpoint('transactions', req.ip);

// Validate query parameters first
await utils.validateReq(req);

Expand Down Expand Up @@ -465,7 +467,6 @@ const getTransactions = async (req, res) => {
logger.debug(`getTransactions returning ${ret.transactions.length} entries`);
res.locals[constants.responseDataLabel] = ret;
};

steven-sheehy marked this conversation as resolved.
Show resolved Hide resolved
/**
* Gets the scheduled db query from the scheduled param in the HTTP request query. The last scheduled value is honored.
* If not present, returns empty string.
Expand Down Expand Up @@ -493,6 +494,8 @@ const getScheduledQuery = (query) => {
* @return {} None.
*/
const getOneTransaction = async (req, res) => {
recordIpAndEndpoint('transactions/{id}', req.ip);
// ipEndpointHistogram.labels("transactions/{id}", req.ip).observe(1);
await utils.validateReq(req);

const transactionId = TransactionId.fromString(req.params.id);
Expand Down