Skip to content

Commit

Permalink
refactor: metrics module (#140)
Browse files Browse the repository at this point in the history
# Description

This refactors the metrics to make it more explicit when used within
consuming code.

# Changes

- [x] Metrics now scoped to a module

## How to test
1. Run in testing environment and observe metrics continue to work.
  • Loading branch information
mfw78 authored Jan 25, 2024
1 parent 279f720 commit b9bf2a9
Show file tree
Hide file tree
Showing 7 changed files with 47 additions and 70 deletions.
3 changes: 1 addition & 2 deletions src/commands/run.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { RunOptions } from "../types";
import { getLogger, DBService } from "../utils";
import { getLogger, DBService, ApiService } from "../utils";
import { ChainContext } from "../domain";
import { ApiService } from "../utils/api";

/**
* Run the watch-tower 👀🐮
Expand Down
38 changes: 15 additions & 23 deletions src/domain/addContract.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {
getLogger,
handleExecutionError,
isComposableCowCompatible,
metrics,
} from "../utils";
import { BytesLike, ethers } from "ethers";

Expand All @@ -20,17 +21,6 @@ import {

import { ChainContext } from "./chainContext";
import { ConditionalOrderParams } from "@cowprotocol/cow-sdk";
import {
addContractRunsTotal,
addContractsErrorsTotal,
addContractsRunDurationSeconds,
measureTime,
merkleRootTotal,
ownersTotal,
singleOrdersTotal,
activeOrdersTotal,
activeOwnersTotal,
} from "../utils/metrics";

/**
* Listens to these events on the `ComposableCoW` contract:
Expand All @@ -44,13 +34,13 @@ export async function addContract(
event: ConditionalOrderCreatedEvent
) {
const { chainId } = context;
await measureTime({
await metrics.measureTime({
action: () => _addContract(context, event),
labelValues: [chainId.toString()],
durationMetric: addContractsRunDurationSeconds,
totalRunsMetric: addContractRunsTotal,
durationMetric: metrics.addContractsRunDurationSeconds,
totalRunsMetric: metrics.addContractRunsTotal,
errorHandler: handleExecutionError,
errorMetric: addContractsErrorsTotal,
errorMetric: metrics.addContractsErrorsTotal,
});
}

Expand Down Expand Up @@ -79,7 +69,7 @@ async function _addContract(
registry
);
if (added) {
ownersTotal.labels(context.chainId.toString()).inc();
metrics.ownersTotal.labels(context.chainId.toString()).inc();
numContractsAdded++;
} else {
log.error(
Expand Down Expand Up @@ -110,6 +100,7 @@ export async function _registerNewOrder(
): Promise<{ error: boolean; added: boolean }> {
const log = getLogger("addContract:_registerNewOrder");
const { transactionHash: tx } = event;
const { network } = registry;
let added = false;
try {
// Check if the log is a ConditionalOrderCreated event
Expand All @@ -134,7 +125,7 @@ export async function _registerNewOrder(
registry
);
added = true;
singleOrdersTotal.labels(registry.network).inc();
metrics.singleOrdersTotal.labels(network).inc();
} else if (
event.topics[0] == composableCow.getEventTopic("MerkleRootSet")
) {
Expand Down Expand Up @@ -175,7 +166,7 @@ export async function _registerNewOrder(
registry
);
added = true;
merkleRootTotal.labels(registry.network).inc();
metrics.merkleRootTotal.labels(network).inc();
}
}
}
Expand Down Expand Up @@ -209,8 +200,9 @@ export function add(
) {
const log = getLogger("addContract:add");
const { handler, salt, staticInput } = params;
if (registry.ownerOrders.has(owner)) {
const conditionalOrders = registry.ownerOrders.get(owner);
const { network, ownerOrders } = registry;
if (ownerOrders.has(owner)) {
const conditionalOrders = ownerOrders.get(owner);
log.info(
`Adding conditional order to already existing owner contract ${owner}`,
{ tx, handler, salt, staticInput }
Expand All @@ -234,7 +226,7 @@ export function add(
orders: new Map(),
composableCow,
});
activeOrdersTotal.labels(registry.network).inc();
metrics.activeOrdersTotal.labels(network).inc();
}
} else {
log.info(`Adding conditional order to new owner contract ${owner}:`, {
Expand All @@ -247,8 +239,8 @@ export function add(
owner,
new Set([{ tx, params, proof, orders: new Map(), composableCow }])
);
activeOwnersTotal.labels(registry.network).inc();
activeOrdersTotal.labels(registry.network).inc();
metrics.activeOwnersTotal.labels(network).inc();
metrics.activeOrdersTotal.labels(network).inc();
}
}

Expand Down
29 changes: 13 additions & 16 deletions src/domain/chainContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,8 @@ import {
DBService,
getLogger,
isRunningInKubernetesPod,
metrics,
} from "../utils";
import {
blockHeight,
blockProducingRate,
eventsProcessedTotal,
processBlockDurationSeconds,
reorgDepth,
reorgsTotal,
} from "../utils/metrics";
import { hexZeroPad } from "ethers/lib/utils";
import { FilterPolicy } from "../utils/filterPolicy";

Expand Down Expand Up @@ -183,7 +176,9 @@ export class ChainContext {
const { pageSize } = this;

// Set the block height metric
blockHeight.labels(chainId.toString()).set(lastProcessedBlock?.number ?? 0);
metrics.blockHeight
.labels(chainId.toString())
.set(lastProcessedBlock?.number ?? 0);

// Start watching from (not including) the last processed block (if any)
let fromBlock = lastProcessedBlock
Expand Down Expand Up @@ -269,7 +264,9 @@ export class ChainContext {
await this.registry.write();

// Set the block height metric
blockHeight.labels(chainId.toString()).set(Number(blockNumber));
metrics.blockHeight
.labels(chainId.toString())
.set(Number(blockNumber));
} catch (err) {
log.error(`Error processing block ${blockNumber}`, err);
}
Expand Down Expand Up @@ -334,16 +331,16 @@ export class ChainContext {

// Set the block time metric
const _blockTime = block.timestamp - lastBlockReceived.timestamp;
blockProducingRate.labels(chainId.toString()).set(_blockTime);
metrics.blockProducingRate.labels(chainId.toString()).set(_blockTime);

if (
blockNumber <= lastBlockReceived.number &&
block.hash !== lastBlockReceived.hash
) {
// This is a re-org, so process the block again
reorgsTotal.labels(chainId.toString()).inc();
metrics.reorgsTotal.labels(chainId.toString()).inc();
log.info(`Re-org detected, re-processing block ${blockNumber}`);
reorgDepth
metrics.reorgDepth
.labels(chainId.toString())
.set(lastBlockReceived.number - blockNumber + 1);
}
Expand All @@ -361,7 +358,7 @@ export class ChainContext {
// Block height metric
this.registry.lastProcessedBlock = blockToRegistryBlock(block);
this.registry.write();
blockHeight.labels(chainId.toString()).set(Number(blockNumber));
metrics.blockHeight.labels(chainId.toString()).set(blockNumber);
} catch {
log.error(`Error processing block ${blockNumber}`);
}
Expand Down Expand Up @@ -457,7 +454,7 @@ async function processBlock(
blockTimestampOverride?: number
) {
const { provider, chainId } = context;
const timer = processBlockDurationSeconds
const timer = metrics.processBlockDurationSeconds
.labels(context.chainId.toString())
.startTimer();
const log = getLogger(
Expand All @@ -481,7 +478,7 @@ async function processBlock(
return false;
});
log.info(`Result of "addContract": ${_formatResult(result)}`);
eventsProcessedTotal.labels(chainId.toString()).inc();
metrics.eventsProcessedTotal.labels(chainId.toString()).inc();
}
}

Expand Down
33 changes: 11 additions & 22 deletions src/domain/checkForAndPlaceOrder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
getLogger,
pollConditionalOrder,
handleOnChainCustomError,
metrics,
} from "../utils";
import {
ConditionalOrder as ConditionalOrderSDK,
Expand All @@ -30,18 +31,6 @@ import {
formatEpoch,
} from "@cowprotocol/cow-sdk";
import { ChainContext, SDK_BACKOFF_NUM_OF_ATTEMPTS } from "./chainContext";
import {
pollingOnChainDurationSeconds,
activeOrdersTotal,
activeOwnersTotal,
orderBookDiscreteOrdersTotal,
orderBookErrorsTotal,
pollingOnChainChecksTotal,
pollingRunsTotal,
pollingUnexpectedErrorsTotal,
pollingOnChainEthersErrorsTotal,
measureTime,
} from "../utils/metrics";
import { FilterAction } from "../utils/filterPolicy";
import { validateOrder } from "../utils/filterOrder";

Expand Down Expand Up @@ -230,7 +219,7 @@ export async function checkForAndPlaceOrder(
const action = deleted ? "Stop Watching" : "Failed to stop watching";

log.debug(`${action} conditional order from TX ${conditionalOrder.tx}`);
activeOrdersTotal.labels(chainId.toString()).dec();
metrics.activeOrdersTotal.labels(chainId.toString()).dec();
}
}

Expand All @@ -239,7 +228,7 @@ export async function checkForAndPlaceOrder(
for (const [owner, conditionalOrders] of Array.from(ownerOrders.entries())) {
if (conditionalOrders.size === 0) {
ownerOrders.delete(owner);
activeOwnersTotal.labels(chainId.toString()).dec();
metrics.activeOwnersTotal.labels(chainId.toString()).dec();
}
}

Expand Down Expand Up @@ -274,7 +263,7 @@ async function _processConditionalOrder(
const id = ConditionalOrderSDK.leafToId(conditionalOrder.params);
const metricLabels = [chainId.toString(), handler, owner, id];
try {
pollingRunsTotal.labels(...metricLabels).inc();
metrics.pollingRunsTotal.labels(...metricLabels).inc();

const proof = conditionalOrder.proof
? conditionalOrder.proof.path.map((c) => c.toString())
Expand Down Expand Up @@ -310,7 +299,7 @@ async function _processConditionalOrder(
// Unsupported Order Type (unknown handler)
// For now, fallback to legacy behavior
// TODO: Decide in the future what to do. Probably, move the error handling to the SDK and kill the poll Legacy
pollResult = await measureTime({
pollResult = await metrics.measureTime({
action: () =>
_pollLegacy(
context,
Expand All @@ -321,8 +310,8 @@ async function _processConditionalOrder(
orderRef
),
labelValues: metricLabels,
durationMetric: pollingOnChainDurationSeconds,
totalRunsMetric: pollingOnChainChecksTotal,
durationMetric: metrics.pollingOnChainDurationSeconds,
totalRunsMetric: metrics.pollingOnChainChecksTotal,
});
}

Expand Down Expand Up @@ -389,7 +378,7 @@ async function _processConditionalOrder(
signature,
};
} catch (e: any) {
pollingUnexpectedErrorsTotal.labels(...metricLabels).inc();
metrics.pollingUnexpectedErrorsTotal.labels(...metricLabels).inc();
return {
result: PollResultCode.UNEXPECTED_ERROR,
error: e,
Expand Down Expand Up @@ -488,7 +477,7 @@ async function _placeOrder(params: {
log.debug(`Post order details`, postOrder);
if (!dryRun) {
const orderUid = await orderBook.sendOrder(postOrder);
orderBookDiscreteOrdersTotal.labels(...metricLabels).inc();
metrics.orderBookDiscreteOrdersTotal.labels(...metricLabels).inc();
log.info(`API response`, { orderUid });
}
} catch (error: any) {
Expand Down Expand Up @@ -544,7 +533,7 @@ function _handleOrderBookError(
metricLabels: string[]
): Omit<PollResultSuccess, "order" | "signature"> | PollResultErrors {
const apiError = body?.errorType as OrderPostError.errorType;
orderBookErrorsTotal
metrics.orderBookErrorsTotal
.labels(...metricLabels, status.toString(), apiError)
.inc();
if (status === 400) {
Expand Down Expand Up @@ -649,7 +638,7 @@ async function _pollLegacy(
// We can only get here from some provider / ethers failure. As the contract hasn't had it's say
// we will defer to try again.
log.error(`ethers/call Unexpected error`, error);
pollingOnChainEthersErrorsTotal.labels(...metricLabels).inc();
metrics.pollingOnChainEthersErrorsTotal.labels(...metricLabels).inc();
return {
result: PollResultCode.TRY_NEXT_BLOCK,
reason:
Expand Down
7 changes: 3 additions & 4 deletions src/types/model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,7 @@ import { BytesLike, ethers } from "ethers";

import type { ConditionalOrderCreatedEvent } from "./generated/ComposableCoW";
import { ConditionalOrderParams, PollResult } from "@cowprotocol/cow-sdk";
import { DBService } from "../utils";
import { activeOrdersTotal, activeOwnersTotal } from "../utils/metrics";
import { DBService, metrics } from "../utils";

// Standardise the storage key
const LAST_NOTIFIED_ERROR_STORAGE_KEY = "LAST_NOTIFIED_ERROR";
Expand Down Expand Up @@ -167,12 +166,12 @@ export class Registry {
.catch(() => null);

// Return registry (on its latest version)
activeOwnersTotal.labels(network).set(ownerOrders.size);
metrics.activeOwnersTotal.labels(network).set(ownerOrders.size);
const numOrders = Array.from(ownerOrders.values()).reduce(
(acc, o) => acc + o.size,
0
);
activeOrdersTotal.labels(network).set(numOrders);
metrics.activeOrdersTotal.labels(network).set(numOrders);

return new Registry(
ownerOrders,
Expand Down
5 changes: 2 additions & 3 deletions src/utils/contracts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {
SupportedChainId,
} from "@cowprotocol/cow-sdk";
import { getLogger } from "./logging";
import { pollingOnChainInvalidInterfacesTotal } from "./metrics";
import { metrics } from ".";

// Selectors that are required to be part of the contract's bytecode in order to be considered compatible
const REQUIRED_SELECTORS = [
Expand Down Expand Up @@ -293,7 +293,6 @@ export function handleOnChainCustomError(params: {
`Order for safe ${owner} where the Safe has swap guard enabled. Deleting order...`
);
return dropOrder("The conditional order didn't pass the swap guard");
// TODO: Add metrics to track this
case CustomErrorSelectors.ORDER_NOT_VALID:
const reason = msgWithSelector(parsedCustomError.message);
log.info(
Expand Down Expand Up @@ -333,7 +332,7 @@ export function handleOnChainCustomError(params: {
log.debug(
`Contract returned a non-compliant interface revert via getTradeableOrderWithSignature. Simulate: https://dashboard.tenderly.co/gp-v2/watch-tower-prod/simulator/new?network=${chainId}&contractAddress=${target}&rawFunctionInput=${callData}`
);
pollingOnChainInvalidInterfacesTotal.labels(...metricLabels).inc();
metrics.pollingOnChainInvalidInterfacesTotal.labels(...metricLabels).inc();
return {
result: PollResultCode.DONT_TRY_AGAIN,
reason: "Order returned a non-compliant (invalid/erroneous) revert hint",
Expand Down
2 changes: 2 additions & 0 deletions src/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,5 @@ export * from "./poll";
export * from "./misc";
export * from "./db";
export * from "./logging";
export * from "./api";
export * as metrics from "./metrics";

0 comments on commit b9bf2a9

Please sign in to comment.