Skip to content

Commit

Permalink
add dd trace (#851)
Browse files Browse the repository at this point in the history
* add dd trace

* trace second level fns
  • Loading branch information
shunjizhan authored Oct 4, 2023
1 parent 6caf38b commit 9c1ecd7
Show file tree
Hide file tree
Showing 14 changed files with 364 additions and 263 deletions.
81 changes: 81 additions & 0 deletions packages/eth-providers/src/base-provider-dd.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import { BaseProvider } from './base-provider';
import tracer from 'dd-trace';

const TRACE_METHODS = [
'_onNewHead',
'_onNewFinalizedHead',
'_notifySubscribers',
'queryStorage',
'getNetwork',
'getBlockNumber',
'getBlockData',
'getBlockDataForHeader',
'getBalance',
'getTransactionCount',
'getEvmTransactionCount',
'getSubstrateNonce',
'getCode',
'call',
'_ethCall',
'getStorageAt',
'getGasPrice',
'getFeeData',
'estimateGas',
'_estimateGasCost',
'getEthResources',
'_getEthGas',
'estimateResources',
'getSubstrateAddress',
'getEvmAddress',
'queryAccountInfo',
'queryContractInfo',
'prepareTransaction',
'sendRawTransaction',
'sendTransaction',
'_wrapTransaction',
'_getBlockNumber',
'_getBlockHash',
'_isBlockCanonical',
'_isBlockFinalized',
'_isTransactionFinalized',
'_ensureSafeModeBlockTagFinalization',
'_getBlockHeader',
'getReceiptAtBlockFromChain',
'getReceiptAtBlock',
'_getReceiptAtBlockByHash',
'_getReceiptAtBlockByIndex',
'_getPendingTX',
'getTransactionByHash',
'getReceipt',
'_getReceipt',
'_sanitizeRawFilter',
'getLogs',
'_waitForSubql',
'getIndexerMetadata',
'healthCheck',
'addEventListener',
'removeEventListener',
'addPollFilter',
'_pollLogs',
'_pollBlocks',
'poll',
'removePollFilter',
];

export class BaseProviderWithTrace extends BaseProvider {
constructor(...args: any[]) {
super(...args);

for (const methodName of TRACE_METHODS) {
if (typeof this[methodName] !== 'function' || methodName === 'constructor') {
throw new Error(`cannot trace method ${methodName}`);
}

this[methodName] = tracer.wrap(
`provider.${methodName}`,
{ resource: methodName },
this[methodName].bind(this)
);
}
}
}
26 changes: 13 additions & 13 deletions packages/eth-providers/src/base-provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -303,19 +303,6 @@ export abstract class BaseProvider extends AbstractProvider {
readonly #headTasks: Map<string, Subscription> = new Map();
readonly #finalizedHeadTasks: Map<string, Subscription> = new Map();

get bestBlockHash() {
return firstValueFrom(this.best$).then(({ hash }) => hash);
}
get bestBlockNumber() {
return firstValueFrom(this.best$).then(({ number }) => number);
}
get finalizedBlockHash() {
return firstValueFrom(this.finalized$).then(({ hash }) => hash);
}
get finalizedBlockNumber() {
return firstValueFrom(this.finalized$).then(({ number }) => number);
}

constructor({
safeMode = false,
localMode = false,
Expand Down Expand Up @@ -356,6 +343,19 @@ export abstract class BaseProvider extends AbstractProvider {
}
}

get bestBlockHash() {
return firstValueFrom(this.best$).then(({ hash }) => hash);
}
get bestBlockNumber() {
return firstValueFrom(this.best$).then(({ number }) => number);
}
get finalizedBlockHash() {
return firstValueFrom(this.finalized$).then(({ hash }) => hash);
}
get finalizedBlockNumber() {
return firstValueFrom(this.finalized$).then(({ number }) => number);
}

static isProvider(value: any): value is Provider {
return !!(value && value._isProvider);
}
Expand Down
17 changes: 16 additions & 1 deletion packages/eth-providers/src/rpc-provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ import { options } from '@acala-network/api';

import { ApiPromise, WsProvider } from '@polkadot/api';
import { BaseProvider, BaseProviderOptions } from './base-provider';
import { BaseProviderWithTrace } from './base-provider-dd';

export class EvmRpcProvider extends BaseProvider {

constructor(endpoint: string | string[], opts?: BaseProviderOptions) {
super(opts);

Expand All @@ -18,3 +18,18 @@ export class EvmRpcProvider extends BaseProvider {
return new EvmRpcProvider(endpoint, opt);
}
}

export class EvmRpcProviderWithTrace extends BaseProviderWithTrace {
constructor(endpoint: string | string[], opts?: BaseProviderOptions) {
super(opts);

const provider = new WsProvider(endpoint);
const api = new ApiPromise(options({ provider }));

this.setApi(api);
}

static from(endpoint: string | string[], opt?: BaseProviderOptions): EvmRpcProviderWithTrace {
return new EvmRpcProviderWithTrace(endpoint, opt);
}
}
3 changes: 2 additions & 1 deletion packages/eth-rpc-adapter/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
"body-parser": "~1.19.0",
"connect": "~3.7.0",
"cors": "~2.8.5",
"dd-trace": "~2.6.0",
"dd-trace": "~4.16.0",
"ethers": "~5.7.0",
"pino": "~7.0.0-rc.3",
"ts-node-dev": "^2.0.0",
Expand All @@ -35,6 +35,7 @@
"@types/body-parser": "~1.19.1",
"@types/connect": "~3.4.35",
"@types/cors": "~2.8.12",
"@types/dd-trace": "^0.9.0",
"@types/node": "~20.1.0",
"@types/ws": "~8.2.0",
"@types/yargs": "^16.0.4",
Expand Down
24 changes: 16 additions & 8 deletions packages/eth-rpc-adapter/src/eip1193-bridge.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,28 @@
import { EvmRpcProvider, PROVIDER_ERRORS, PollFilterType, TX, hexlifyRpcResult } from '@acala-network/eth-providers';
import { InvalidParams, MethodNotFound } from './errors';
import {
EvmRpcProvider,
EvmRpcProviderWithTrace,
PROVIDER_ERRORS,
PollFilterType,
TX,
hexlifyRpcResult,
} from '@acala-network/eth-providers';
import { Log, TransactionReceipt } from '@ethersproject/abstract-provider';
import { Signer } from '@ethersproject/abstract-signer';
import { getAddress } from '@ethersproject/address';
import { hexValue } from '@ethersproject/bytes';
import { validate } from './validate';
import { version } from './_version';
import EventEmitter from 'events';
import WebSocket from 'ws';

import { InvalidParams, MethodNotFound } from './errors';
import { validate } from './validate';
import { version } from './_version';

const HEX_ZERO = '0x0';
export class Eip1193Bridge extends EventEmitter {
readonly provider: EvmRpcProvider;
readonly provider: EvmRpcProvider | EvmRpcProviderWithTrace;
readonly #impl: Eip1193BridgeImpl;

constructor(provider: EvmRpcProvider, signer?: Signer) {
constructor(provider: EvmRpcProvider | EvmRpcProviderWithTrace, signer?: Signer) {
super();
this.provider = provider;
this.#impl = new Eip1193BridgeImpl(provider, signer);
Expand Down Expand Up @@ -43,10 +51,10 @@ export class Eip1193Bridge extends EventEmitter {
}

class Eip1193BridgeImpl {
readonly #provider: EvmRpcProvider;
readonly #provider: EvmRpcProvider | EvmRpcProviderWithTrace;
readonly #signer?: Signer;

constructor(provider: EvmRpcProvider, signer?: Signer) {
constructor(provider: EvmRpcProvider | EvmRpcProviderWithTrace, signer?: Signer) {
this.#provider = provider;
this.#signer = signer;
}
Expand Down
9 changes: 5 additions & 4 deletions packages/eth-rpc-adapter/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
import 'dd-trace/init';
import './utils/tracer';

import { Eip1193Bridge } from './eip1193-bridge';
import { EvmRpcProvider } from '@acala-network/eth-providers';
import { EvmRpcProviderWithTrace } from '@acala-network/eth-providers';
import { Router } from './router';
import { monitorRuntime } from './monitor-runtime';
import { monitorRuntime } from './utils/monitor-runtime';
import { yargsOptions as opts } from './utils';
import { version } from './_version';
import EthRpcServer from './server';

export async function start(): Promise<void> {
console.log('starting server ...');

const provider = EvmRpcProvider.from(opts.endpoint.split(','), {
const provider = EvmRpcProviderWithTrace.from(opts.endpoint.split(','), {
safeMode: opts.safeMode,
localMode: opts.localMode,
verbose: opts.verbose,
Expand Down
14 changes: 11 additions & 3 deletions packages/eth-rpc-adapter/src/router.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import { ERROR_PATTERN } from '@acala-network/eth-providers';
import { Eip1193Bridge } from './eip1193-bridge';
import { Logger as EthLogger } from '@ethersproject/logger';
import WebSocket from 'ws';
import tracer from 'dd-trace';

import { Eip1193Bridge } from './eip1193-bridge';
import { InternalError, InvalidParams, JSONRPCError, MethodNotFound } from './errors';
import { JsonRpcResponse } from './server';
import WebSocket from 'ws';
export class Router {
readonly #bridge: Eip1193Bridge;

Expand All @@ -14,7 +16,13 @@ export class Router {
public async call(methodName: string, params: unknown[], ws?: WebSocket): Promise<Partial<JsonRpcResponse>> {
if (this.#bridge.isMethodImplemented(methodName)) {
try {
return { result: await this.#bridge.send(methodName, params, ws) };
const result = await tracer.trace(
`router_call.<${methodName}>`,
{ resource: methodName },
() => this.#bridge.send(methodName, params, ws)
);

return { result };
} catch (err: any) {
if (JSONRPCError.isJSONRPCError(err)) {
return { error: err.json() };
Expand Down
40 changes: 14 additions & 26 deletions packages/eth-rpc-adapter/src/server.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import { BatchSizeError, InvalidRequest, MethodNotFound } from './errors';
import { DataDogUtil } from './utils';
import { Router } from './router';
import { errorHandler } from './middlewares';
import { json as jsonParser } from 'body-parser';
import { logger } from './logger';
import WebSocket from 'ws';
import connect, { HandleFunction } from 'connect';
import cors from 'cors';
import http, { ServerOptions } from 'http';

import { BatchSizeError, InvalidRequest, MethodNotFound } from './errors';
import { Router } from './router';
import { errorHandler } from './middlewares';
import { logger } from './utils/logger';

export interface EthRpcServerOptions extends ServerOptions {
port: number;
batchSize: number;
Expand All @@ -21,7 +21,7 @@ export interface JsonRpcRequest {
jsonrpc: string;
id: string;
method: string;
params: any[] | Record<string, unknown>;
params: any[];
}

export interface JsonRpcError {
Expand Down Expand Up @@ -117,14 +117,14 @@ export default class EthRpcServer {
}

protected async baseHandler({ id, method, params }: JsonRpcRequest, ws?: WebSocket): Promise<JsonRpcResponse> {
let res: JsonRpcResponse = {
let response: JsonRpcResponse = {
id: id ?? null,
jsonrpc: '2.0',
};

if (id === null || id === undefined || !method) {
return {
...res,
...response,
error: new InvalidRequest({
id: id || null,
method: method || null,
Expand All @@ -138,35 +138,23 @@ export default class EthRpcServer {
throw new Error('No router configured');
}

// Initialize datadog span and get spanTags from the context
const spanTags = DataDogUtil.buildTracerSpan();

const routerForMethod = this.routers.find((r) => r.isMethodImplemented(method));

if (routerForMethod === undefined) {
res.error = new MethodNotFound(
response.error = new MethodNotFound(
'Method not found',
`The method ${method} does not exist / is not available.`
).json();
} else {
res = {
const res = await routerForMethod.call(method, params, ws);

response = {
...response,
...res,
...(await routerForMethod.call(method, params as any, ws)),
};
}

// Add span tags to the datadog span
DataDogUtil.assignTracerSpan(spanTags, {
id,
method,
...(Array.isArray(params)
? params.reduce((c, v, i) => {
return { ...c, [`param_${i}`]: v };
}, {})
: params),
});

return res;
return response;
}

private async httpHandler(req: any, res: http.ServerResponse, next: (err?: any) => void): Promise<void> {
Expand Down
Loading

0 comments on commit 9c1ecd7

Please sign in to comment.