From 14fd03d097b33e8399819b40361145dd523e595f Mon Sep 17 00:00:00 2001 From: prathamesh0 <42446521+prathamesh0@users.noreply.github.com> Date: Fri, 7 Jun 2024 15:47:00 +0530 Subject: [PATCH] Regenerate watchers for updated GQL query metrics (#47) * Regenerate azimuth watcher * Regenerate censures watcher * Regenerate claims watcher * Regenerate conditional-star-release watcher * Regenerate delegated-sending watcher * Regenerate ecliptic watcher * Regenerate linear-star-release watcher * Regenerate polls watcher * Update README * Update package versions * Update watcher repo URLs --- README.md | 2 +- lerna.json | 2 +- packages/azimuth-watcher/.gitignore | 2 + packages/azimuth-watcher/README.md | 2 +- .../azimuth-watcher/environments/local.toml | 30 +- packages/azimuth-watcher/package.json | 16 +- packages/azimuth-watcher/src/indexer.ts | 2 +- packages/azimuth-watcher/src/resolvers.ts | 699 ++++++++++++------ packages/censures-watcher/.gitignore | 2 + packages/censures-watcher/README.md | 2 +- .../censures-watcher/environments/local.toml | 30 +- packages/censures-watcher/package.json | 16 +- packages/censures-watcher/src/indexer.ts | 2 +- packages/censures-watcher/src/resolvers.ts | 219 ++++-- packages/claims-watcher/.gitignore | 2 + packages/claims-watcher/README.md | 2 +- .../claims-watcher/environments/local.toml | 30 +- packages/claims-watcher/package.json | 16 +- packages/claims-watcher/src/indexer.ts | 2 +- packages/claims-watcher/src/resolvers.ts | 183 ++++- .../.gitignore | 2 + .../README.md | 2 +- .../environments/local.toml | 30 +- .../package.json | 16 +- .../src/indexer.ts | 2 +- .../src/resolvers.ts | 291 ++++++-- packages/delegated-sending-watcher/.gitignore | 2 + packages/delegated-sending-watcher/README.md | 2 +- .../environments/local.toml | 30 +- .../delegated-sending-watcher/package.json | 16 +- .../delegated-sending-watcher/src/indexer.ts | 2 +- .../src/resolvers.ts | 195 +++-- packages/ecliptic-watcher/.gitignore | 2 + packages/ecliptic-watcher/README.md | 2 +- .../ecliptic-watcher/environments/local.toml | 30 +- packages/ecliptic-watcher/package.json | 16 +- packages/ecliptic-watcher/src/indexer.ts | 2 +- packages/ecliptic-watcher/src/resolvers.ts | 303 ++++++-- packages/gateway-server/package.json | 2 +- .../linear-star-release-watcher/.gitignore | 2 + .../linear-star-release-watcher/README.md | 2 +- .../environments/local.toml | 30 +- .../linear-star-release-watcher/package.json | 16 +- .../src/indexer.ts | 2 +- .../src/resolvers.ts | 207 ++++-- packages/polls-watcher/.gitignore | 2 + packages/polls-watcher/README.md | 2 +- .../polls-watcher/environments/local.toml | 30 +- packages/polls-watcher/package.json | 16 +- packages/polls-watcher/src/indexer.ts | 2 +- packages/polls-watcher/src/resolvers.ts | 255 +++++-- yarn.lock | 225 ++++-- 52 files changed, 2162 insertions(+), 837 deletions(-) diff --git a/README.md b/README.md index cf2fcb5..18b2d25 100644 --- a/README.md +++ b/README.md @@ -142,7 +142,7 @@ Steps to generate Azimuth watchers using the code generator ([`@cerc-io/codegen` flatten: true ``` - Note: Create `.sol` files with the contract code from Etherscan for [`DelegatedSending`](https://etherscan.io/address/0xf6b461fe1ad4bd2ce25b23fe0aff2ac19b3dfa76#code) and [`Ecliptic`](https://etherscan.io/address/ecliptic.eth#code) contracts and use the file path for `contracts.path` + Note: Create `.sol` files with the contract code from Etherscan for [`ConditionalStarRelease`](https://etherscan.io/address/0x8C241098C3D3498Fe1261421633FD57986D74AeA#code), [`DelegatedSending`](https://etherscan.io/address/0xf6b461fe1ad4bd2ce25b23fe0aff2ac19b3dfa76#code), [`Ecliptic`](https://etherscan.io/address/ecliptic.eth#code) and [`LinearStarRelease`](https://etherscan.io/address/0x86cd9cd0992F04231751E3761De45cEceA5d1801#code) contracts and use the file path for `contracts.path` * Run codegen command to generate the watcher: diff --git a/lerna.json b/lerna.json index ac49bb3..c2a71bb 100644 --- a/lerna.json +++ b/lerna.json @@ -3,6 +3,6 @@ "packages/*" ], "useWorkspaces": true, - "version": "0.1.5", + "version": "0.1.6", "npmClient": "yarn" } diff --git a/packages/azimuth-watcher/.gitignore b/packages/azimuth-watcher/.gitignore index 0b4cc3f..549d70b 100644 --- a/packages/azimuth-watcher/.gitignore +++ b/packages/azimuth-watcher/.gitignore @@ -4,3 +4,5 @@ out/ .vscode .idea + +gql-logs/ diff --git a/packages/azimuth-watcher/README.md b/packages/azimuth-watcher/README.md index ba07311..a1f7ec5 100644 --- a/packages/azimuth-watcher/README.md +++ b/packages/azimuth-watcher/README.md @@ -63,7 +63,7 @@ To enable GQL requests caching: -* Update the `server.gqlCache` config with required settings. +* Update the `server.gql.cache` config with required settings. * In the GQL [schema file](./src/schema.gql), use the `cacheControl` directive to apply cache hints at schema level. diff --git a/packages/azimuth-watcher/environments/local.toml b/packages/azimuth-watcher/environments/local.toml index d5cbbc2..396b554 100644 --- a/packages/azimuth-watcher/environments/local.toml +++ b/packages/azimuth-watcher/environments/local.toml @@ -2,7 +2,6 @@ host = "127.0.0.1" port = 3001 kind = "active" - gqlPath = "/graphql" # Checkpointing state. checkpointing = true @@ -14,22 +13,29 @@ # CAUTION: Disable only if state creation is not desired or can be filled subsequently enableState = false - # Max block range for which to return events in eventsInRange GQL query. - # Use -1 for skipping check on block range. - maxEventsBlockRange = 1000 - # Flag to specify whether RPC endpoint supports block hash as block tag parameter rpcSupportsBlockHashParam = false - # GQL cache settings - [server.gqlCache] - enabled = true + # Server GQL config + [server.gql] + path = "/graphql" - # Max in-memory cache size (in bytes) (default 8 MB) - # maxCacheSize + # Max block range for which to return events in eventsInRange GQL query. + # Use -1 for skipping check on block range. + maxEventsBlockRange = 1000 - # GQL cache-control max-age settings (in seconds) - maxAge = 15 + # Log directory for GQL requests + logDir = "./gql-logs" + + # GQL cache settings + [server.gql.cache] + enabled = true + + # Max in-memory cache size (in bytes) (default 8 MB) + # maxCacheSize + + # GQL cache-control max-age settings (in seconds) + maxAge = 15 [metrics] host = "127.0.0.1" diff --git a/packages/azimuth-watcher/package.json b/packages/azimuth-watcher/package.json index f45a13f..2c3b241 100644 --- a/packages/azimuth-watcher/package.json +++ b/packages/azimuth-watcher/package.json @@ -1,6 +1,6 @@ { "name": "@cerc-io/azimuth-watcher", - "version": "0.1.5", + "version": "0.1.6", "description": "azimuth-watcher", "private": true, "main": "dist/index.js", @@ -28,7 +28,8 @@ }, "repository": { "type": "git", - "url": "git+https://github.com/cerc-io/watcher-ts.git" + "url": "https://github.com/cerc-io/azimuth-watcher-ts", + "directory": "packages/azimuth-watcher" }, "author": "", "license": "AGPL-3.0", @@ -38,10 +39,10 @@ "homepage": "https://github.com/cerc-io/watcher-ts#readme", "dependencies": { "@apollo/client": "^3.3.19", - "@cerc-io/cli": "^0.2.92", - "@cerc-io/ipld-eth-client": "^0.2.92", - "@cerc-io/solidity-mapper": "^0.2.92", - "@cerc-io/util": "^0.2.92", + "@cerc-io/cli": "^0.2.94", + "@cerc-io/ipld-eth-client": "^0.2.94", + "@cerc-io/solidity-mapper": "^0.2.94", + "@cerc-io/util": "^0.2.94", "@ethersproject/providers": "^5.4.4", "debug": "^4.3.1", "decimal.js": "^10.3.1", @@ -69,6 +70,7 @@ "eslint-plugin-standard": "^5.0.0", "husky": "^7.0.2", "ts-node": "^10.2.1", - "typescript": "^5.0.2" + "typescript": "^5.0.2", + "winston": "^3.13.0" } } diff --git a/packages/azimuth-watcher/src/indexer.ts b/packages/azimuth-watcher/src/indexer.ts index aa9381b..3faa4a1 100644 --- a/packages/azimuth-watcher/src/indexer.ts +++ b/packages/azimuth-watcher/src/indexer.ts @@ -1734,7 +1734,7 @@ export class Indexer implements IndexerInterface { } async getEventsInRange (fromBlockNumber: number, toBlockNumber: number): Promise> { - return this._baseIndexer.getEventsInRange(fromBlockNumber, toBlockNumber, this._serverConfig.maxEventsBlockRange); + return this._baseIndexer.getEventsInRange(fromBlockNumber, toBlockNumber, this._serverConfig.gql.maxEventsBlockRange); } async getSyncStatus (): Promise { diff --git a/packages/azimuth-watcher/src/resolvers.ts b/packages/azimuth-watcher/src/resolvers.ts index b1f7a85..9dccbef 100644 --- a/packages/azimuth-watcher/src/resolvers.ts +++ b/packages/azimuth-watcher/src/resolvers.ts @@ -5,11 +5,14 @@ import assert from 'assert'; import debug from 'debug'; import { GraphQLResolveInfo } from 'graphql'; +import { ExpressContext } from 'apollo-server-express'; +import winston from 'winston'; import { ValueResult, gqlTotalQueryCount, gqlQueryCount, + gqlQueryDuration, getResultState, IndexerInterface, GraphQLBigInt, @@ -23,11 +26,57 @@ import { Indexer } from './indexer'; const log = debug('vulcanize:resolver'); -export const createResolvers = async (indexerArg: IndexerInterface, eventWatcher: EventWatcher): Promise => { +const executeAndRecordMetrics = async ( + indexer: Indexer, + gqlLogger: winston.Logger, + opName: string, + expressContext: ExpressContext, + operation: () => Promise +) => { + gqlTotalQueryCount.inc(1); + gqlQueryCount.labels(opName).inc(1); + const endTimer = gqlQueryDuration.labels(opName).startTimer(); + + try { + const [result, syncStatus] = await Promise.all([ + operation(), + indexer.getSyncStatus() + ]); + + gqlLogger.info({ + opName, + query: expressContext.req.body.query, + variables: expressContext.req.body.variables, + latestIndexedBlockNumber: syncStatus?.latestIndexedBlockNumber, + urlPath: expressContext.req.path, + apiKey: expressContext.req.header('x-api-key'), + origin: expressContext.req.headers.origin + }); + return result; + } catch (error) { + gqlLogger.error({ + opName, + error, + query: expressContext.req.body.query, + variables: expressContext.req.body.variables, + urlPath: expressContext.req.path, + apiKey: expressContext.req.header('x-api-key'), + origin: expressContext.req.headers.origin + }); + } finally { + endTimer(); + } +}; + +export const createResolvers = async ( + indexerArg: IndexerInterface, + eventWatcher: EventWatcher, + gqlLogger: winston.Logger +): Promise => { const indexer = indexerArg as Indexer; // eslint-disable-next-line @typescript-eslint/no-unused-vars - const gqlCacheConfig = indexer.serverConfig.gqlCache; + const gqlCacheConfig = indexer.serverConfig.gql.cache; return { BigInt: GraphQLBigInt, @@ -62,853 +111,1077 @@ export const createResolvers = async (indexerArg: IndexerInterface, eventWatcher _: any, { blockHash, contractAddress, _point }: { blockHash: string, contractAddress: string, _point: bigint }, // eslint-disable-next-line @typescript-eslint/no-unused-vars - __: any, + expressContext: ExpressContext, // eslint-disable-next-line @typescript-eslint/no-unused-vars info: GraphQLResolveInfo ): Promise => { log('isActive', blockHash, contractAddress, _point); - gqlTotalQueryCount.inc(1); - gqlQueryCount.labels('isActive').inc(1); // Set cache-control hints // setGQLCacheHints(info, {}, gqlCacheConfig); - return indexer.isActive(blockHash, contractAddress, _point); + return executeAndRecordMetrics( + indexer, + gqlLogger, + 'isActive', + expressContext, + async () => indexer.isActive(blockHash, contractAddress, _point) + ); }, getKeys: ( _: any, { blockHash, contractAddress, _point }: { blockHash: string, contractAddress: string, _point: bigint }, // eslint-disable-next-line @typescript-eslint/no-unused-vars - __: any, + expressContext: ExpressContext, // eslint-disable-next-line @typescript-eslint/no-unused-vars info: GraphQLResolveInfo ): Promise => { log('getKeys', blockHash, contractAddress, _point); - gqlTotalQueryCount.inc(1); - gqlQueryCount.labels('getKeys').inc(1); // Set cache-control hints // setGQLCacheHints(info, {}, gqlCacheConfig); - return indexer.getKeys(blockHash, contractAddress, _point); + return executeAndRecordMetrics( + indexer, + gqlLogger, + 'getKeys', + expressContext, + async () => indexer.getKeys(blockHash, contractAddress, _point) + ); }, getKeyRevisionNumber: ( _: any, { blockHash, contractAddress, _point }: { blockHash: string, contractAddress: string, _point: bigint }, // eslint-disable-next-line @typescript-eslint/no-unused-vars - __: any, + expressContext: ExpressContext, // eslint-disable-next-line @typescript-eslint/no-unused-vars info: GraphQLResolveInfo ): Promise => { log('getKeyRevisionNumber', blockHash, contractAddress, _point); - gqlTotalQueryCount.inc(1); - gqlQueryCount.labels('getKeyRevisionNumber').inc(1); // Set cache-control hints // setGQLCacheHints(info, {}, gqlCacheConfig); - return indexer.getKeyRevisionNumber(blockHash, contractAddress, _point); + return executeAndRecordMetrics( + indexer, + gqlLogger, + 'getKeyRevisionNumber', + expressContext, + async () => indexer.getKeyRevisionNumber(blockHash, contractAddress, _point) + ); }, hasBeenLinked: ( _: any, { blockHash, contractAddress, _point }: { blockHash: string, contractAddress: string, _point: bigint }, // eslint-disable-next-line @typescript-eslint/no-unused-vars - __: any, + expressContext: ExpressContext, // eslint-disable-next-line @typescript-eslint/no-unused-vars info: GraphQLResolveInfo ): Promise => { log('hasBeenLinked', blockHash, contractAddress, _point); - gqlTotalQueryCount.inc(1); - gqlQueryCount.labels('hasBeenLinked').inc(1); // Set cache-control hints // setGQLCacheHints(info, {}, gqlCacheConfig); - return indexer.hasBeenLinked(blockHash, contractAddress, _point); + return executeAndRecordMetrics( + indexer, + gqlLogger, + 'hasBeenLinked', + expressContext, + async () => indexer.hasBeenLinked(blockHash, contractAddress, _point) + ); }, isLive: ( _: any, { blockHash, contractAddress, _point }: { blockHash: string, contractAddress: string, _point: bigint }, // eslint-disable-next-line @typescript-eslint/no-unused-vars - __: any, + expressContext: ExpressContext, // eslint-disable-next-line @typescript-eslint/no-unused-vars info: GraphQLResolveInfo ): Promise => { log('isLive', blockHash, contractAddress, _point); - gqlTotalQueryCount.inc(1); - gqlQueryCount.labels('isLive').inc(1); // Set cache-control hints // setGQLCacheHints(info, {}, gqlCacheConfig); - return indexer.isLive(blockHash, contractAddress, _point); + return executeAndRecordMetrics( + indexer, + gqlLogger, + 'isLive', + expressContext, + async () => indexer.isLive(blockHash, contractAddress, _point) + ); }, getContinuityNumber: ( _: any, { blockHash, contractAddress, _point }: { blockHash: string, contractAddress: string, _point: bigint }, // eslint-disable-next-line @typescript-eslint/no-unused-vars - __: any, + expressContext: ExpressContext, // eslint-disable-next-line @typescript-eslint/no-unused-vars info: GraphQLResolveInfo ): Promise => { log('getContinuityNumber', blockHash, contractAddress, _point); - gqlTotalQueryCount.inc(1); - gqlQueryCount.labels('getContinuityNumber').inc(1); // Set cache-control hints // setGQLCacheHints(info, {}, gqlCacheConfig); - return indexer.getContinuityNumber(blockHash, contractAddress, _point); + return executeAndRecordMetrics( + indexer, + gqlLogger, + 'getContinuityNumber', + expressContext, + async () => indexer.getContinuityNumber(blockHash, contractAddress, _point) + ); }, getSpawnCount: ( _: any, { blockHash, contractAddress, _point }: { blockHash: string, contractAddress: string, _point: bigint }, // eslint-disable-next-line @typescript-eslint/no-unused-vars - __: any, + expressContext: ExpressContext, // eslint-disable-next-line @typescript-eslint/no-unused-vars info: GraphQLResolveInfo ): Promise => { log('getSpawnCount', blockHash, contractAddress, _point); - gqlTotalQueryCount.inc(1); - gqlQueryCount.labels('getSpawnCount').inc(1); // Set cache-control hints // setGQLCacheHints(info, {}, gqlCacheConfig); - return indexer.getSpawnCount(blockHash, contractAddress, _point); + return executeAndRecordMetrics( + indexer, + gqlLogger, + 'getSpawnCount', + expressContext, + async () => indexer.getSpawnCount(blockHash, contractAddress, _point) + ); }, getSpawned: ( _: any, { blockHash, contractAddress, _point }: { blockHash: string, contractAddress: string, _point: bigint }, // eslint-disable-next-line @typescript-eslint/no-unused-vars - __: any, + expressContext: ExpressContext, // eslint-disable-next-line @typescript-eslint/no-unused-vars info: GraphQLResolveInfo ): Promise => { log('getSpawned', blockHash, contractAddress, _point); - gqlTotalQueryCount.inc(1); - gqlQueryCount.labels('getSpawned').inc(1); // Set cache-control hints // setGQLCacheHints(info, {}, gqlCacheConfig); - return indexer.getSpawned(blockHash, contractAddress, _point); + return executeAndRecordMetrics( + indexer, + gqlLogger, + 'getSpawned', + expressContext, + async () => indexer.getSpawned(blockHash, contractAddress, _point) + ); }, hasSponsor: ( _: any, { blockHash, contractAddress, _point }: { blockHash: string, contractAddress: string, _point: bigint }, // eslint-disable-next-line @typescript-eslint/no-unused-vars - __: any, + expressContext: ExpressContext, // eslint-disable-next-line @typescript-eslint/no-unused-vars info: GraphQLResolveInfo ): Promise => { log('hasSponsor', blockHash, contractAddress, _point); - gqlTotalQueryCount.inc(1); - gqlQueryCount.labels('hasSponsor').inc(1); // Set cache-control hints // setGQLCacheHints(info, {}, gqlCacheConfig); - return indexer.hasSponsor(blockHash, contractAddress, _point); + return executeAndRecordMetrics( + indexer, + gqlLogger, + 'hasSponsor', + expressContext, + async () => indexer.hasSponsor(blockHash, contractAddress, _point) + ); }, getSponsor: ( _: any, { blockHash, contractAddress, _point }: { blockHash: string, contractAddress: string, _point: bigint }, // eslint-disable-next-line @typescript-eslint/no-unused-vars - __: any, + expressContext: ExpressContext, // eslint-disable-next-line @typescript-eslint/no-unused-vars info: GraphQLResolveInfo ): Promise => { log('getSponsor', blockHash, contractAddress, _point); - gqlTotalQueryCount.inc(1); - gqlQueryCount.labels('getSponsor').inc(1); // Set cache-control hints // setGQLCacheHints(info, {}, gqlCacheConfig); - return indexer.getSponsor(blockHash, contractAddress, _point); + return executeAndRecordMetrics( + indexer, + gqlLogger, + 'getSponsor', + expressContext, + async () => indexer.getSponsor(blockHash, contractAddress, _point) + ); }, isSponsor: ( _: any, { blockHash, contractAddress, _point, _sponsor }: { blockHash: string, contractAddress: string, _point: bigint, _sponsor: bigint }, // eslint-disable-next-line @typescript-eslint/no-unused-vars - __: any, + expressContext: ExpressContext, // eslint-disable-next-line @typescript-eslint/no-unused-vars info: GraphQLResolveInfo ): Promise => { log('isSponsor', blockHash, contractAddress, _point, _sponsor); - gqlTotalQueryCount.inc(1); - gqlQueryCount.labels('isSponsor').inc(1); // Set cache-control hints // setGQLCacheHints(info, {}, gqlCacheConfig); - return indexer.isSponsor(blockHash, contractAddress, _point, _sponsor); + return executeAndRecordMetrics( + indexer, + gqlLogger, + 'isSponsor', + expressContext, + async () => indexer.isSponsor(blockHash, contractAddress, _point, _sponsor) + ); }, getSponsoringCount: ( _: any, { blockHash, contractAddress, _sponsor }: { blockHash: string, contractAddress: string, _sponsor: bigint }, // eslint-disable-next-line @typescript-eslint/no-unused-vars - __: any, + expressContext: ExpressContext, // eslint-disable-next-line @typescript-eslint/no-unused-vars info: GraphQLResolveInfo ): Promise => { log('getSponsoringCount', blockHash, contractAddress, _sponsor); - gqlTotalQueryCount.inc(1); - gqlQueryCount.labels('getSponsoringCount').inc(1); // Set cache-control hints // setGQLCacheHints(info, {}, gqlCacheConfig); - return indexer.getSponsoringCount(blockHash, contractAddress, _sponsor); + return executeAndRecordMetrics( + indexer, + gqlLogger, + 'getSponsoringCount', + expressContext, + async () => indexer.getSponsoringCount(blockHash, contractAddress, _sponsor) + ); }, getSponsoring: ( _: any, { blockHash, contractAddress, _sponsor }: { blockHash: string, contractAddress: string, _sponsor: bigint }, // eslint-disable-next-line @typescript-eslint/no-unused-vars - __: any, + expressContext: ExpressContext, // eslint-disable-next-line @typescript-eslint/no-unused-vars info: GraphQLResolveInfo ): Promise => { log('getSponsoring', blockHash, contractAddress, _sponsor); - gqlTotalQueryCount.inc(1); - gqlQueryCount.labels('getSponsoring').inc(1); // Set cache-control hints // setGQLCacheHints(info, {}, gqlCacheConfig); - return indexer.getSponsoring(blockHash, contractAddress, _sponsor); + return executeAndRecordMetrics( + indexer, + gqlLogger, + 'getSponsoring', + expressContext, + async () => indexer.getSponsoring(blockHash, contractAddress, _sponsor) + ); }, isEscaping: ( _: any, { blockHash, contractAddress, _point }: { blockHash: string, contractAddress: string, _point: bigint }, // eslint-disable-next-line @typescript-eslint/no-unused-vars - __: any, + expressContext: ExpressContext, // eslint-disable-next-line @typescript-eslint/no-unused-vars info: GraphQLResolveInfo ): Promise => { log('isEscaping', blockHash, contractAddress, _point); - gqlTotalQueryCount.inc(1); - gqlQueryCount.labels('isEscaping').inc(1); // Set cache-control hints // setGQLCacheHints(info, {}, gqlCacheConfig); - return indexer.isEscaping(blockHash, contractAddress, _point); + return executeAndRecordMetrics( + indexer, + gqlLogger, + 'isEscaping', + expressContext, + async () => indexer.isEscaping(blockHash, contractAddress, _point) + ); }, getEscapeRequest: ( _: any, { blockHash, contractAddress, _point }: { blockHash: string, contractAddress: string, _point: bigint }, // eslint-disable-next-line @typescript-eslint/no-unused-vars - __: any, + expressContext: ExpressContext, // eslint-disable-next-line @typescript-eslint/no-unused-vars info: GraphQLResolveInfo ): Promise => { log('getEscapeRequest', blockHash, contractAddress, _point); - gqlTotalQueryCount.inc(1); - gqlQueryCount.labels('getEscapeRequest').inc(1); // Set cache-control hints // setGQLCacheHints(info, {}, gqlCacheConfig); - return indexer.getEscapeRequest(blockHash, contractAddress, _point); + return executeAndRecordMetrics( + indexer, + gqlLogger, + 'getEscapeRequest', + expressContext, + async () => indexer.getEscapeRequest(blockHash, contractAddress, _point) + ); }, isRequestingEscapeTo: ( _: any, { blockHash, contractAddress, _point, _sponsor }: { blockHash: string, contractAddress: string, _point: bigint, _sponsor: bigint }, // eslint-disable-next-line @typescript-eslint/no-unused-vars - __: any, + expressContext: ExpressContext, // eslint-disable-next-line @typescript-eslint/no-unused-vars info: GraphQLResolveInfo ): Promise => { log('isRequestingEscapeTo', blockHash, contractAddress, _point, _sponsor); - gqlTotalQueryCount.inc(1); - gqlQueryCount.labels('isRequestingEscapeTo').inc(1); // Set cache-control hints // setGQLCacheHints(info, {}, gqlCacheConfig); - return indexer.isRequestingEscapeTo(blockHash, contractAddress, _point, _sponsor); + return executeAndRecordMetrics( + indexer, + gqlLogger, + 'isRequestingEscapeTo', + expressContext, + async () => indexer.isRequestingEscapeTo(blockHash, contractAddress, _point, _sponsor) + ); }, getEscapeRequestsCount: ( _: any, { blockHash, contractAddress, _sponsor }: { blockHash: string, contractAddress: string, _sponsor: bigint }, // eslint-disable-next-line @typescript-eslint/no-unused-vars - __: any, + expressContext: ExpressContext, // eslint-disable-next-line @typescript-eslint/no-unused-vars info: GraphQLResolveInfo ): Promise => { log('getEscapeRequestsCount', blockHash, contractAddress, _sponsor); - gqlTotalQueryCount.inc(1); - gqlQueryCount.labels('getEscapeRequestsCount').inc(1); // Set cache-control hints // setGQLCacheHints(info, {}, gqlCacheConfig); - return indexer.getEscapeRequestsCount(blockHash, contractAddress, _sponsor); + return executeAndRecordMetrics( + indexer, + gqlLogger, + 'getEscapeRequestsCount', + expressContext, + async () => indexer.getEscapeRequestsCount(blockHash, contractAddress, _sponsor) + ); }, getEscapeRequests: ( _: any, { blockHash, contractAddress, _sponsor }: { blockHash: string, contractAddress: string, _sponsor: bigint }, // eslint-disable-next-line @typescript-eslint/no-unused-vars - __: any, + expressContext: ExpressContext, // eslint-disable-next-line @typescript-eslint/no-unused-vars info: GraphQLResolveInfo ): Promise => { log('getEscapeRequests', blockHash, contractAddress, _sponsor); - gqlTotalQueryCount.inc(1); - gqlQueryCount.labels('getEscapeRequests').inc(1); // Set cache-control hints // setGQLCacheHints(info, {}, gqlCacheConfig); - return indexer.getEscapeRequests(blockHash, contractAddress, _sponsor); + return executeAndRecordMetrics( + indexer, + gqlLogger, + 'getEscapeRequests', + expressContext, + async () => indexer.getEscapeRequests(blockHash, contractAddress, _sponsor) + ); }, getOwner: ( _: any, { blockHash, contractAddress, _point }: { blockHash: string, contractAddress: string, _point: bigint }, // eslint-disable-next-line @typescript-eslint/no-unused-vars - __: any, + expressContext: ExpressContext, // eslint-disable-next-line @typescript-eslint/no-unused-vars info: GraphQLResolveInfo ): Promise => { log('getOwner', blockHash, contractAddress, _point); - gqlTotalQueryCount.inc(1); - gqlQueryCount.labels('getOwner').inc(1); // Set cache-control hints // setGQLCacheHints(info, {}, gqlCacheConfig); - return indexer.getOwner(blockHash, contractAddress, _point); + return executeAndRecordMetrics( + indexer, + gqlLogger, + 'getOwner', + expressContext, + async () => indexer.getOwner(blockHash, contractAddress, _point) + ); }, isOwner: ( _: any, { blockHash, contractAddress, _point, _address }: { blockHash: string, contractAddress: string, _point: bigint, _address: string }, // eslint-disable-next-line @typescript-eslint/no-unused-vars - __: any, + expressContext: ExpressContext, // eslint-disable-next-line @typescript-eslint/no-unused-vars info: GraphQLResolveInfo ): Promise => { log('isOwner', blockHash, contractAddress, _point, _address); - gqlTotalQueryCount.inc(1); - gqlQueryCount.labels('isOwner').inc(1); // Set cache-control hints // setGQLCacheHints(info, {}, gqlCacheConfig); - return indexer.isOwner(blockHash, contractAddress, _point, _address); + return executeAndRecordMetrics( + indexer, + gqlLogger, + 'isOwner', + expressContext, + async () => indexer.isOwner(blockHash, contractAddress, _point, _address) + ); }, getOwnedPointCount: ( _: any, { blockHash, contractAddress, _whose }: { blockHash: string, contractAddress: string, _whose: string }, // eslint-disable-next-line @typescript-eslint/no-unused-vars - __: any, + expressContext: ExpressContext, // eslint-disable-next-line @typescript-eslint/no-unused-vars info: GraphQLResolveInfo ): Promise => { log('getOwnedPointCount', blockHash, contractAddress, _whose); - gqlTotalQueryCount.inc(1); - gqlQueryCount.labels('getOwnedPointCount').inc(1); // Set cache-control hints // setGQLCacheHints(info, {}, gqlCacheConfig); - return indexer.getOwnedPointCount(blockHash, contractAddress, _whose); + return executeAndRecordMetrics( + indexer, + gqlLogger, + 'getOwnedPointCount', + expressContext, + async () => indexer.getOwnedPointCount(blockHash, contractAddress, _whose) + ); }, getOwnedPoints: ( _: any, { blockHash, contractAddress, _whose }: { blockHash: string, contractAddress: string, _whose: string }, // eslint-disable-next-line @typescript-eslint/no-unused-vars - __: any, + expressContext: ExpressContext, // eslint-disable-next-line @typescript-eslint/no-unused-vars info: GraphQLResolveInfo ): Promise => { log('getOwnedPoints', blockHash, contractAddress, _whose); - gqlTotalQueryCount.inc(1); - gqlQueryCount.labels('getOwnedPoints').inc(1); // Set cache-control hints // setGQLCacheHints(info, {}, gqlCacheConfig); - return indexer.getOwnedPoints(blockHash, contractAddress, _whose); + return executeAndRecordMetrics( + indexer, + gqlLogger, + 'getOwnedPoints', + expressContext, + async () => indexer.getOwnedPoints(blockHash, contractAddress, _whose) + ); }, getOwnedPointAtIndex: ( _: any, { blockHash, contractAddress, _whose, _index }: { blockHash: string, contractAddress: string, _whose: string, _index: bigint }, // eslint-disable-next-line @typescript-eslint/no-unused-vars - __: any, + expressContext: ExpressContext, // eslint-disable-next-line @typescript-eslint/no-unused-vars info: GraphQLResolveInfo ): Promise => { log('getOwnedPointAtIndex', blockHash, contractAddress, _whose, _index); - gqlTotalQueryCount.inc(1); - gqlQueryCount.labels('getOwnedPointAtIndex').inc(1); // Set cache-control hints // setGQLCacheHints(info, {}, gqlCacheConfig); - return indexer.getOwnedPointAtIndex(blockHash, contractAddress, _whose, _index); + return executeAndRecordMetrics( + indexer, + gqlLogger, + 'getOwnedPointAtIndex', + expressContext, + async () => indexer.getOwnedPointAtIndex(blockHash, contractAddress, _whose, _index) + ); }, getManagementProxy: ( _: any, { blockHash, contractAddress, _point }: { blockHash: string, contractAddress: string, _point: bigint }, // eslint-disable-next-line @typescript-eslint/no-unused-vars - __: any, + expressContext: ExpressContext, // eslint-disable-next-line @typescript-eslint/no-unused-vars info: GraphQLResolveInfo ): Promise => { log('getManagementProxy', blockHash, contractAddress, _point); - gqlTotalQueryCount.inc(1); - gqlQueryCount.labels('getManagementProxy').inc(1); // Set cache-control hints // setGQLCacheHints(info, {}, gqlCacheConfig); - return indexer.getManagementProxy(blockHash, contractAddress, _point); + return executeAndRecordMetrics( + indexer, + gqlLogger, + 'getManagementProxy', + expressContext, + async () => indexer.getManagementProxy(blockHash, contractAddress, _point) + ); }, isManagementProxy: ( _: any, { blockHash, contractAddress, _point, _proxy }: { blockHash: string, contractAddress: string, _point: bigint, _proxy: string }, // eslint-disable-next-line @typescript-eslint/no-unused-vars - __: any, + expressContext: ExpressContext, // eslint-disable-next-line @typescript-eslint/no-unused-vars info: GraphQLResolveInfo ): Promise => { log('isManagementProxy', blockHash, contractAddress, _point, _proxy); - gqlTotalQueryCount.inc(1); - gqlQueryCount.labels('isManagementProxy').inc(1); // Set cache-control hints // setGQLCacheHints(info, {}, gqlCacheConfig); - return indexer.isManagementProxy(blockHash, contractAddress, _point, _proxy); + return executeAndRecordMetrics( + indexer, + gqlLogger, + 'isManagementProxy', + expressContext, + async () => indexer.isManagementProxy(blockHash, contractAddress, _point, _proxy) + ); }, canManage: ( _: any, { blockHash, contractAddress, _point, _who }: { blockHash: string, contractAddress: string, _point: bigint, _who: string }, // eslint-disable-next-line @typescript-eslint/no-unused-vars - __: any, + expressContext: ExpressContext, // eslint-disable-next-line @typescript-eslint/no-unused-vars info: GraphQLResolveInfo ): Promise => { log('canManage', blockHash, contractAddress, _point, _who); - gqlTotalQueryCount.inc(1); - gqlQueryCount.labels('canManage').inc(1); // Set cache-control hints // setGQLCacheHints(info, {}, gqlCacheConfig); - return indexer.canManage(blockHash, contractAddress, _point, _who); + return executeAndRecordMetrics( + indexer, + gqlLogger, + 'canManage', + expressContext, + async () => indexer.canManage(blockHash, contractAddress, _point, _who) + ); }, getManagerForCount: ( _: any, { blockHash, contractAddress, _proxy }: { blockHash: string, contractAddress: string, _proxy: string }, // eslint-disable-next-line @typescript-eslint/no-unused-vars - __: any, + expressContext: ExpressContext, // eslint-disable-next-line @typescript-eslint/no-unused-vars info: GraphQLResolveInfo ): Promise => { log('getManagerForCount', blockHash, contractAddress, _proxy); - gqlTotalQueryCount.inc(1); - gqlQueryCount.labels('getManagerForCount').inc(1); // Set cache-control hints // setGQLCacheHints(info, {}, gqlCacheConfig); - return indexer.getManagerForCount(blockHash, contractAddress, _proxy); + return executeAndRecordMetrics( + indexer, + gqlLogger, + 'getManagerForCount', + expressContext, + async () => indexer.getManagerForCount(blockHash, contractAddress, _proxy) + ); }, getManagerFor: ( _: any, { blockHash, contractAddress, _proxy }: { blockHash: string, contractAddress: string, _proxy: string }, // eslint-disable-next-line @typescript-eslint/no-unused-vars - __: any, + expressContext: ExpressContext, // eslint-disable-next-line @typescript-eslint/no-unused-vars info: GraphQLResolveInfo ): Promise => { log('getManagerFor', blockHash, contractAddress, _proxy); - gqlTotalQueryCount.inc(1); - gqlQueryCount.labels('getManagerFor').inc(1); // Set cache-control hints // setGQLCacheHints(info, {}, gqlCacheConfig); - return indexer.getManagerFor(blockHash, contractAddress, _proxy); + return executeAndRecordMetrics( + indexer, + gqlLogger, + 'getManagerFor', + expressContext, + async () => indexer.getManagerFor(blockHash, contractAddress, _proxy) + ); }, getSpawnProxy: ( _: any, { blockHash, contractAddress, _point }: { blockHash: string, contractAddress: string, _point: bigint }, // eslint-disable-next-line @typescript-eslint/no-unused-vars - __: any, + expressContext: ExpressContext, // eslint-disable-next-line @typescript-eslint/no-unused-vars info: GraphQLResolveInfo ): Promise => { log('getSpawnProxy', blockHash, contractAddress, _point); - gqlTotalQueryCount.inc(1); - gqlQueryCount.labels('getSpawnProxy').inc(1); // Set cache-control hints // setGQLCacheHints(info, {}, gqlCacheConfig); - return indexer.getSpawnProxy(blockHash, contractAddress, _point); + return executeAndRecordMetrics( + indexer, + gqlLogger, + 'getSpawnProxy', + expressContext, + async () => indexer.getSpawnProxy(blockHash, contractAddress, _point) + ); }, isSpawnProxy: ( _: any, { blockHash, contractAddress, _point, _proxy }: { blockHash: string, contractAddress: string, _point: bigint, _proxy: string }, // eslint-disable-next-line @typescript-eslint/no-unused-vars - __: any, + expressContext: ExpressContext, // eslint-disable-next-line @typescript-eslint/no-unused-vars info: GraphQLResolveInfo ): Promise => { log('isSpawnProxy', blockHash, contractAddress, _point, _proxy); - gqlTotalQueryCount.inc(1); - gqlQueryCount.labels('isSpawnProxy').inc(1); // Set cache-control hints // setGQLCacheHints(info, {}, gqlCacheConfig); - return indexer.isSpawnProxy(blockHash, contractAddress, _point, _proxy); + return executeAndRecordMetrics( + indexer, + gqlLogger, + 'isSpawnProxy', + expressContext, + async () => indexer.isSpawnProxy(blockHash, contractAddress, _point, _proxy) + ); }, canSpawnAs: ( _: any, { blockHash, contractAddress, _point, _who }: { blockHash: string, contractAddress: string, _point: bigint, _who: string }, // eslint-disable-next-line @typescript-eslint/no-unused-vars - __: any, + expressContext: ExpressContext, // eslint-disable-next-line @typescript-eslint/no-unused-vars info: GraphQLResolveInfo ): Promise => { log('canSpawnAs', blockHash, contractAddress, _point, _who); - gqlTotalQueryCount.inc(1); - gqlQueryCount.labels('canSpawnAs').inc(1); // Set cache-control hints // setGQLCacheHints(info, {}, gqlCacheConfig); - return indexer.canSpawnAs(blockHash, contractAddress, _point, _who); + return executeAndRecordMetrics( + indexer, + gqlLogger, + 'canSpawnAs', + expressContext, + async () => indexer.canSpawnAs(blockHash, contractAddress, _point, _who) + ); }, getSpawningForCount: ( _: any, { blockHash, contractAddress, _proxy }: { blockHash: string, contractAddress: string, _proxy: string }, // eslint-disable-next-line @typescript-eslint/no-unused-vars - __: any, + expressContext: ExpressContext, // eslint-disable-next-line @typescript-eslint/no-unused-vars info: GraphQLResolveInfo ): Promise => { log('getSpawningForCount', blockHash, contractAddress, _proxy); - gqlTotalQueryCount.inc(1); - gqlQueryCount.labels('getSpawningForCount').inc(1); // Set cache-control hints // setGQLCacheHints(info, {}, gqlCacheConfig); - return indexer.getSpawningForCount(blockHash, contractAddress, _proxy); + return executeAndRecordMetrics( + indexer, + gqlLogger, + 'getSpawningForCount', + expressContext, + async () => indexer.getSpawningForCount(blockHash, contractAddress, _proxy) + ); }, getSpawningFor: ( _: any, { blockHash, contractAddress, _proxy }: { blockHash: string, contractAddress: string, _proxy: string }, // eslint-disable-next-line @typescript-eslint/no-unused-vars - __: any, + expressContext: ExpressContext, // eslint-disable-next-line @typescript-eslint/no-unused-vars info: GraphQLResolveInfo ): Promise => { log('getSpawningFor', blockHash, contractAddress, _proxy); - gqlTotalQueryCount.inc(1); - gqlQueryCount.labels('getSpawningFor').inc(1); // Set cache-control hints // setGQLCacheHints(info, {}, gqlCacheConfig); - return indexer.getSpawningFor(blockHash, contractAddress, _proxy); + return executeAndRecordMetrics( + indexer, + gqlLogger, + 'getSpawningFor', + expressContext, + async () => indexer.getSpawningFor(blockHash, contractAddress, _proxy) + ); }, getVotingProxy: ( _: any, { blockHash, contractAddress, _point }: { blockHash: string, contractAddress: string, _point: bigint }, // eslint-disable-next-line @typescript-eslint/no-unused-vars - __: any, + expressContext: ExpressContext, // eslint-disable-next-line @typescript-eslint/no-unused-vars info: GraphQLResolveInfo ): Promise => { log('getVotingProxy', blockHash, contractAddress, _point); - gqlTotalQueryCount.inc(1); - gqlQueryCount.labels('getVotingProxy').inc(1); // Set cache-control hints // setGQLCacheHints(info, {}, gqlCacheConfig); - return indexer.getVotingProxy(blockHash, contractAddress, _point); + return executeAndRecordMetrics( + indexer, + gqlLogger, + 'getVotingProxy', + expressContext, + async () => indexer.getVotingProxy(blockHash, contractAddress, _point) + ); }, isVotingProxy: ( _: any, { blockHash, contractAddress, _point, _proxy }: { blockHash: string, contractAddress: string, _point: bigint, _proxy: string }, // eslint-disable-next-line @typescript-eslint/no-unused-vars - __: any, + expressContext: ExpressContext, // eslint-disable-next-line @typescript-eslint/no-unused-vars info: GraphQLResolveInfo ): Promise => { log('isVotingProxy', blockHash, contractAddress, _point, _proxy); - gqlTotalQueryCount.inc(1); - gqlQueryCount.labels('isVotingProxy').inc(1); // Set cache-control hints // setGQLCacheHints(info, {}, gqlCacheConfig); - return indexer.isVotingProxy(blockHash, contractAddress, _point, _proxy); + return executeAndRecordMetrics( + indexer, + gqlLogger, + 'isVotingProxy', + expressContext, + async () => indexer.isVotingProxy(blockHash, contractAddress, _point, _proxy) + ); }, canVoteAs: ( _: any, { blockHash, contractAddress, _point, _who }: { blockHash: string, contractAddress: string, _point: bigint, _who: string }, // eslint-disable-next-line @typescript-eslint/no-unused-vars - __: any, + expressContext: ExpressContext, // eslint-disable-next-line @typescript-eslint/no-unused-vars info: GraphQLResolveInfo ): Promise => { log('canVoteAs', blockHash, contractAddress, _point, _who); - gqlTotalQueryCount.inc(1); - gqlQueryCount.labels('canVoteAs').inc(1); // Set cache-control hints // setGQLCacheHints(info, {}, gqlCacheConfig); - return indexer.canVoteAs(blockHash, contractAddress, _point, _who); + return executeAndRecordMetrics( + indexer, + gqlLogger, + 'canVoteAs', + expressContext, + async () => indexer.canVoteAs(blockHash, contractAddress, _point, _who) + ); }, getVotingForCount: ( _: any, { blockHash, contractAddress, _proxy }: { blockHash: string, contractAddress: string, _proxy: string }, // eslint-disable-next-line @typescript-eslint/no-unused-vars - __: any, + expressContext: ExpressContext, // eslint-disable-next-line @typescript-eslint/no-unused-vars info: GraphQLResolveInfo ): Promise => { log('getVotingForCount', blockHash, contractAddress, _proxy); - gqlTotalQueryCount.inc(1); - gqlQueryCount.labels('getVotingForCount').inc(1); // Set cache-control hints // setGQLCacheHints(info, {}, gqlCacheConfig); - return indexer.getVotingForCount(blockHash, contractAddress, _proxy); + return executeAndRecordMetrics( + indexer, + gqlLogger, + 'getVotingForCount', + expressContext, + async () => indexer.getVotingForCount(blockHash, contractAddress, _proxy) + ); }, getVotingFor: ( _: any, { blockHash, contractAddress, _proxy }: { blockHash: string, contractAddress: string, _proxy: string }, // eslint-disable-next-line @typescript-eslint/no-unused-vars - __: any, + expressContext: ExpressContext, // eslint-disable-next-line @typescript-eslint/no-unused-vars info: GraphQLResolveInfo ): Promise => { log('getVotingFor', blockHash, contractAddress, _proxy); - gqlTotalQueryCount.inc(1); - gqlQueryCount.labels('getVotingFor').inc(1); // Set cache-control hints // setGQLCacheHints(info, {}, gqlCacheConfig); - return indexer.getVotingFor(blockHash, contractAddress, _proxy); + return executeAndRecordMetrics( + indexer, + gqlLogger, + 'getVotingFor', + expressContext, + async () => indexer.getVotingFor(blockHash, contractAddress, _proxy) + ); }, getTransferProxy: ( _: any, { blockHash, contractAddress, _point }: { blockHash: string, contractAddress: string, _point: bigint }, // eslint-disable-next-line @typescript-eslint/no-unused-vars - __: any, + expressContext: ExpressContext, // eslint-disable-next-line @typescript-eslint/no-unused-vars info: GraphQLResolveInfo ): Promise => { log('getTransferProxy', blockHash, contractAddress, _point); - gqlTotalQueryCount.inc(1); - gqlQueryCount.labels('getTransferProxy').inc(1); // Set cache-control hints // setGQLCacheHints(info, {}, gqlCacheConfig); - return indexer.getTransferProxy(blockHash, contractAddress, _point); + return executeAndRecordMetrics( + indexer, + gqlLogger, + 'getTransferProxy', + expressContext, + async () => indexer.getTransferProxy(blockHash, contractAddress, _point) + ); }, isTransferProxy: ( _: any, { blockHash, contractAddress, _point, _proxy }: { blockHash: string, contractAddress: string, _point: bigint, _proxy: string }, // eslint-disable-next-line @typescript-eslint/no-unused-vars - __: any, + expressContext: ExpressContext, // eslint-disable-next-line @typescript-eslint/no-unused-vars info: GraphQLResolveInfo ): Promise => { log('isTransferProxy', blockHash, contractAddress, _point, _proxy); - gqlTotalQueryCount.inc(1); - gqlQueryCount.labels('isTransferProxy').inc(1); // Set cache-control hints // setGQLCacheHints(info, {}, gqlCacheConfig); - return indexer.isTransferProxy(blockHash, contractAddress, _point, _proxy); + return executeAndRecordMetrics( + indexer, + gqlLogger, + 'isTransferProxy', + expressContext, + async () => indexer.isTransferProxy(blockHash, contractAddress, _point, _proxy) + ); }, canTransfer: ( _: any, { blockHash, contractAddress, _point, _who }: { blockHash: string, contractAddress: string, _point: bigint, _who: string }, // eslint-disable-next-line @typescript-eslint/no-unused-vars - __: any, + expressContext: ExpressContext, // eslint-disable-next-line @typescript-eslint/no-unused-vars info: GraphQLResolveInfo ): Promise => { log('canTransfer', blockHash, contractAddress, _point, _who); - gqlTotalQueryCount.inc(1); - gqlQueryCount.labels('canTransfer').inc(1); // Set cache-control hints // setGQLCacheHints(info, {}, gqlCacheConfig); - return indexer.canTransfer(blockHash, contractAddress, _point, _who); + return executeAndRecordMetrics( + indexer, + gqlLogger, + 'canTransfer', + expressContext, + async () => indexer.canTransfer(blockHash, contractAddress, _point, _who) + ); }, getTransferringForCount: ( _: any, { blockHash, contractAddress, _proxy }: { blockHash: string, contractAddress: string, _proxy: string }, // eslint-disable-next-line @typescript-eslint/no-unused-vars - __: any, + expressContext: ExpressContext, // eslint-disable-next-line @typescript-eslint/no-unused-vars info: GraphQLResolveInfo ): Promise => { log('getTransferringForCount', blockHash, contractAddress, _proxy); - gqlTotalQueryCount.inc(1); - gqlQueryCount.labels('getTransferringForCount').inc(1); // Set cache-control hints // setGQLCacheHints(info, {}, gqlCacheConfig); - return indexer.getTransferringForCount(blockHash, contractAddress, _proxy); + return executeAndRecordMetrics( + indexer, + gqlLogger, + 'getTransferringForCount', + expressContext, + async () => indexer.getTransferringForCount(blockHash, contractAddress, _proxy) + ); }, getTransferringFor: ( _: any, { blockHash, contractAddress, _proxy }: { blockHash: string, contractAddress: string, _proxy: string }, // eslint-disable-next-line @typescript-eslint/no-unused-vars - __: any, + expressContext: ExpressContext, // eslint-disable-next-line @typescript-eslint/no-unused-vars info: GraphQLResolveInfo ): Promise => { log('getTransferringFor', blockHash, contractAddress, _proxy); - gqlTotalQueryCount.inc(1); - gqlQueryCount.labels('getTransferringFor').inc(1); // Set cache-control hints // setGQLCacheHints(info, {}, gqlCacheConfig); - return indexer.getTransferringFor(blockHash, contractAddress, _proxy); + return executeAndRecordMetrics( + indexer, + gqlLogger, + 'getTransferringFor', + expressContext, + async () => indexer.getTransferringFor(blockHash, contractAddress, _proxy) + ); }, isOperator: ( _: any, { blockHash, contractAddress, _owner, _operator }: { blockHash: string, contractAddress: string, _owner: string, _operator: string }, // eslint-disable-next-line @typescript-eslint/no-unused-vars - __: any, + expressContext: ExpressContext, // eslint-disable-next-line @typescript-eslint/no-unused-vars info: GraphQLResolveInfo ): Promise => { log('isOperator', blockHash, contractAddress, _owner, _operator); - gqlTotalQueryCount.inc(1); - gqlQueryCount.labels('isOperator').inc(1); // Set cache-control hints // setGQLCacheHints(info, {}, gqlCacheConfig); - return indexer.isOperator(blockHash, contractAddress, _owner, _operator); + return executeAndRecordMetrics( + indexer, + gqlLogger, + 'isOperator', + expressContext, + async () => indexer.isOperator(blockHash, contractAddress, _owner, _operator) + ); }, - events: async (_: any, { blockHash, contractAddress, name }: { blockHash: string, contractAddress: string, name?: string }) => { + events: async ( + _: any, + { blockHash, contractAddress, name }: { blockHash: string, contractAddress: string, name?: string }, + expressContext: ExpressContext + ) => { log('events', blockHash, contractAddress, name); - gqlTotalQueryCount.inc(1); - gqlQueryCount.labels('events').inc(1); - const block = await indexer.getBlockProgress(blockHash); - if (!block || !block.isComplete) { - throw new Error(`Block hash ${blockHash} number ${block?.blockNumber} not processed yet`); - } + return executeAndRecordMetrics( + indexer, + gqlLogger, + 'events', + expressContext, + async () => { + const block = await indexer.getBlockProgress(blockHash); + if (!block || !block.isComplete) { + throw new Error(`Block hash ${blockHash} number ${block?.blockNumber} not processed yet`); + } - const events = await indexer.getEventsByFilter(blockHash, contractAddress, name); - return events.map(event => indexer.getResultEvent(event)); + const events = await indexer.getEventsByFilter(blockHash, contractAddress, name); + return events.map(event => indexer.getResultEvent(event)); + } + ); }, - eventsInRange: async (_: any, { fromBlockNumber, toBlockNumber }: { fromBlockNumber: number, toBlockNumber: number }) => { + eventsInRange: async ( + _: any, + { fromBlockNumber, toBlockNumber }: { fromBlockNumber: number, toBlockNumber: number }, + expressContext: ExpressContext + ) => { log('eventsInRange', fromBlockNumber, toBlockNumber); - gqlTotalQueryCount.inc(1); - gqlQueryCount.labels('eventsInRange').inc(1); - const syncStatus = await indexer.getSyncStatus(); + return executeAndRecordMetrics( + indexer, + gqlLogger, + 'eventsInRange', + expressContext, + async () => { + const syncStatus = await indexer.getSyncStatus(); - if (!syncStatus) { - throw new Error('No blocks processed yet'); - } + if (!syncStatus) { + throw new Error('No blocks processed yet'); + } - if ((fromBlockNumber < syncStatus.initialIndexedBlockNumber) || (toBlockNumber > syncStatus.latestProcessedBlockNumber)) { - throw new Error(`Block range should be between ${syncStatus.initialIndexedBlockNumber} and ${syncStatus.latestProcessedBlockNumber}`); - } + if ((fromBlockNumber < syncStatus.initialIndexedBlockNumber) || (toBlockNumber > syncStatus.latestProcessedBlockNumber)) { + throw new Error(`Block range should be between ${syncStatus.initialIndexedBlockNumber} and ${syncStatus.latestProcessedBlockNumber}`); + } - const events = await indexer.getEventsInRange(fromBlockNumber, toBlockNumber); - return events.map(event => indexer.getResultEvent(event)); + const events = await indexer.getEventsInRange(fromBlockNumber, toBlockNumber); + return events.map(event => indexer.getResultEvent(event)); + } + ); }, - getStateByCID: async (_: any, { cid }: { cid: string }) => { + getStateByCID: async ( + _: any, + { cid }: { cid: string }, + expressContext: ExpressContext + ) => { log('getStateByCID', cid); - gqlTotalQueryCount.inc(1); - gqlQueryCount.labels('getStateByCID').inc(1); - const state = await indexer.getStateByCID(cid); + return executeAndRecordMetrics( + indexer, + gqlLogger, + 'getStateByCID', + expressContext, + async () => { + const state = await indexer.getStateByCID(cid); - return state && state.block.isComplete ? getResultState(state) : undefined; + return state && state.block.isComplete ? getResultState(state) : undefined; + } + ); }, - getState: async (_: any, { blockHash, contractAddress, kind }: { blockHash: string, contractAddress: string, kind: string }) => { + getState: async ( + _: any, + { blockHash, contractAddress, kind }: { blockHash: string, contractAddress: string, kind: string }, + expressContext: ExpressContext + ) => { log('getState', blockHash, contractAddress, kind); - gqlTotalQueryCount.inc(1); - gqlQueryCount.labels('getState').inc(1); - const state = await indexer.getPrevState(blockHash, contractAddress, kind); + return executeAndRecordMetrics( + indexer, + gqlLogger, + 'getState', + expressContext, + async () => { + const state = await indexer.getPrevState(blockHash, contractAddress, kind); - return state && state.block.isComplete ? getResultState(state) : undefined; + return state && state.block.isComplete ? getResultState(state) : undefined; + } + ); }, - getSyncStatus: async () => { + getSyncStatus: async ( + _: any, + __: Record, + expressContext: ExpressContext + ) => { log('getSyncStatus'); - gqlTotalQueryCount.inc(1); - gqlQueryCount.labels('getSyncStatus').inc(1); - return indexer.getSyncStatus(); + return executeAndRecordMetrics( + indexer, + gqlLogger, + 'getSyncStatus', + expressContext, + async () => indexer.getSyncStatus() + ); } } }; diff --git a/packages/censures-watcher/.gitignore b/packages/censures-watcher/.gitignore index 0b4cc3f..549d70b 100644 --- a/packages/censures-watcher/.gitignore +++ b/packages/censures-watcher/.gitignore @@ -4,3 +4,5 @@ out/ .vscode .idea + +gql-logs/ diff --git a/packages/censures-watcher/README.md b/packages/censures-watcher/README.md index b464749..118879e 100644 --- a/packages/censures-watcher/README.md +++ b/packages/censures-watcher/README.md @@ -63,7 +63,7 @@ To enable GQL requests caching: -* Update the `server.gqlCache` config with required settings. +* Update the `server.gql.cache` config with required settings. * In the GQL [schema file](./src/schema.gql), use the `cacheControl` directive to apply cache hints at schema level. diff --git a/packages/censures-watcher/environments/local.toml b/packages/censures-watcher/environments/local.toml index 87732b1..146e25c 100644 --- a/packages/censures-watcher/environments/local.toml +++ b/packages/censures-watcher/environments/local.toml @@ -2,7 +2,6 @@ host = "127.0.0.1" port = 3002 kind = "active" - gqlPath = "/graphql" # Checkpointing state. checkpointing = true @@ -14,22 +13,29 @@ # CAUTION: Disable only if state creation is not desired or can be filled subsequently enableState = false - # Max block range for which to return events in eventsInRange GQL query. - # Use -1 for skipping check on block range. - maxEventsBlockRange = 1000 - # Flag to specify whether RPC endpoint supports block hash as block tag parameter rpcSupportsBlockHashParam = false - # GQL cache settings - [server.gqlCache] - enabled = true + # Server GQL config + [server.gql] + path = "/graphql" - # Max in-memory cache size (in bytes) (default 8 MB) - # maxCacheSize + # Max block range for which to return events in eventsInRange GQL query. + # Use -1 for skipping check on block range. + maxEventsBlockRange = 1000 - # GQL cache-control max-age settings (in seconds) - maxAge = 15 + # Log directory for GQL requests + logDir = "./gql-logs" + + # GQL cache settings + [server.gql.cache] + enabled = true + + # Max in-memory cache size (in bytes) (default 8 MB) + # maxCacheSize + + # GQL cache-control max-age settings (in seconds) + maxAge = 15 [metrics] host = "127.0.0.1" diff --git a/packages/censures-watcher/package.json b/packages/censures-watcher/package.json index 6d0ff1e..6c28e7d 100644 --- a/packages/censures-watcher/package.json +++ b/packages/censures-watcher/package.json @@ -1,6 +1,6 @@ { "name": "@cerc-io/censures-watcher", - "version": "0.1.5", + "version": "0.1.6", "description": "censures-watcher", "private": true, "main": "dist/index.js", @@ -28,7 +28,8 @@ }, "repository": { "type": "git", - "url": "git+https://github.com/cerc-io/watcher-ts.git" + "url": "https://github.com/cerc-io/azimuth-watcher-ts", + "directory": "packages/censures-watcher" }, "author": "", "license": "AGPL-3.0", @@ -38,10 +39,10 @@ "homepage": "https://github.com/cerc-io/watcher-ts#readme", "dependencies": { "@apollo/client": "^3.3.19", - "@cerc-io/cli": "^0.2.92", - "@cerc-io/ipld-eth-client": "^0.2.92", - "@cerc-io/solidity-mapper": "^0.2.92", - "@cerc-io/util": "^0.2.92", + "@cerc-io/cli": "^0.2.94", + "@cerc-io/ipld-eth-client": "^0.2.94", + "@cerc-io/solidity-mapper": "^0.2.94", + "@cerc-io/util": "^0.2.94", "@ethersproject/providers": "^5.4.4", "debug": "^4.3.1", "decimal.js": "^10.3.1", @@ -69,6 +70,7 @@ "eslint-plugin-standard": "^5.0.0", "husky": "^7.0.2", "ts-node": "^10.2.1", - "typescript": "^5.0.2" + "typescript": "^5.0.2", + "winston": "^3.13.0" } } diff --git a/packages/censures-watcher/src/indexer.ts b/packages/censures-watcher/src/indexer.ts index 7589554..e7c0281 100644 --- a/packages/censures-watcher/src/indexer.ts +++ b/packages/censures-watcher/src/indexer.ts @@ -512,7 +512,7 @@ export class Indexer implements IndexerInterface { } async getEventsInRange (fromBlockNumber: number, toBlockNumber: number): Promise> { - return this._baseIndexer.getEventsInRange(fromBlockNumber, toBlockNumber, this._serverConfig.maxEventsBlockRange); + return this._baseIndexer.getEventsInRange(fromBlockNumber, toBlockNumber, this._serverConfig.gql.maxEventsBlockRange); } async getSyncStatus (): Promise { diff --git a/packages/censures-watcher/src/resolvers.ts b/packages/censures-watcher/src/resolvers.ts index 7da58d0..e21763b 100644 --- a/packages/censures-watcher/src/resolvers.ts +++ b/packages/censures-watcher/src/resolvers.ts @@ -5,11 +5,14 @@ import assert from 'assert'; import debug from 'debug'; import { GraphQLResolveInfo } from 'graphql'; +import { ExpressContext } from 'apollo-server-express'; +import winston from 'winston'; import { ValueResult, gqlTotalQueryCount, gqlQueryCount, + gqlQueryDuration, getResultState, IndexerInterface, GraphQLBigInt, @@ -23,11 +26,57 @@ import { Indexer } from './indexer'; const log = debug('vulcanize:resolver'); -export const createResolvers = async (indexerArg: IndexerInterface, eventWatcher: EventWatcher): Promise => { +const executeAndRecordMetrics = async ( + indexer: Indexer, + gqlLogger: winston.Logger, + opName: string, + expressContext: ExpressContext, + operation: () => Promise +) => { + gqlTotalQueryCount.inc(1); + gqlQueryCount.labels(opName).inc(1); + const endTimer = gqlQueryDuration.labels(opName).startTimer(); + + try { + const [result, syncStatus] = await Promise.all([ + operation(), + indexer.getSyncStatus() + ]); + + gqlLogger.info({ + opName, + query: expressContext.req.body.query, + variables: expressContext.req.body.variables, + latestIndexedBlockNumber: syncStatus?.latestIndexedBlockNumber, + urlPath: expressContext.req.path, + apiKey: expressContext.req.header('x-api-key'), + origin: expressContext.req.headers.origin + }); + return result; + } catch (error) { + gqlLogger.error({ + opName, + error, + query: expressContext.req.body.query, + variables: expressContext.req.body.variables, + urlPath: expressContext.req.path, + apiKey: expressContext.req.header('x-api-key'), + origin: expressContext.req.headers.origin + }); + } finally { + endTimer(); + } +}; + +export const createResolvers = async ( + indexerArg: IndexerInterface, + eventWatcher: EventWatcher, + gqlLogger: winston.Logger +): Promise => { const indexer = indexerArg as Indexer; // eslint-disable-next-line @typescript-eslint/no-unused-vars - const gqlCacheConfig = indexer.serverConfig.gqlCache; + const gqlCacheConfig = indexer.serverConfig.gql.cache; return { BigInt: GraphQLBigInt, @@ -62,133 +111,197 @@ export const createResolvers = async (indexerArg: IndexerInterface, eventWatcher _: any, { blockHash, contractAddress, _whose }: { blockHash: string, contractAddress: string, _whose: number }, // eslint-disable-next-line @typescript-eslint/no-unused-vars - __: any, + expressContext: ExpressContext, // eslint-disable-next-line @typescript-eslint/no-unused-vars info: GraphQLResolveInfo ): Promise => { log('getCensuringCount', blockHash, contractAddress, _whose); - gqlTotalQueryCount.inc(1); - gqlQueryCount.labels('getCensuringCount').inc(1); // Set cache-control hints // setGQLCacheHints(info, {}, gqlCacheConfig); - return indexer.getCensuringCount(blockHash, contractAddress, _whose); + return executeAndRecordMetrics( + indexer, + gqlLogger, + 'getCensuringCount', + expressContext, + async () => indexer.getCensuringCount(blockHash, contractAddress, _whose) + ); }, getCensuring: ( _: any, { blockHash, contractAddress, _whose }: { blockHash: string, contractAddress: string, _whose: number }, // eslint-disable-next-line @typescript-eslint/no-unused-vars - __: any, + expressContext: ExpressContext, // eslint-disable-next-line @typescript-eslint/no-unused-vars info: GraphQLResolveInfo ): Promise => { log('getCensuring', blockHash, contractAddress, _whose); - gqlTotalQueryCount.inc(1); - gqlQueryCount.labels('getCensuring').inc(1); // Set cache-control hints // setGQLCacheHints(info, {}, gqlCacheConfig); - return indexer.getCensuring(blockHash, contractAddress, _whose); + return executeAndRecordMetrics( + indexer, + gqlLogger, + 'getCensuring', + expressContext, + async () => indexer.getCensuring(blockHash, contractAddress, _whose) + ); }, getCensuredByCount: ( _: any, { blockHash, contractAddress, _who }: { blockHash: string, contractAddress: string, _who: number }, // eslint-disable-next-line @typescript-eslint/no-unused-vars - __: any, + expressContext: ExpressContext, // eslint-disable-next-line @typescript-eslint/no-unused-vars info: GraphQLResolveInfo ): Promise => { log('getCensuredByCount', blockHash, contractAddress, _who); - gqlTotalQueryCount.inc(1); - gqlQueryCount.labels('getCensuredByCount').inc(1); // Set cache-control hints // setGQLCacheHints(info, {}, gqlCacheConfig); - return indexer.getCensuredByCount(blockHash, contractAddress, _who); + return executeAndRecordMetrics( + indexer, + gqlLogger, + 'getCensuredByCount', + expressContext, + async () => indexer.getCensuredByCount(blockHash, contractAddress, _who) + ); }, getCensuredBy: ( _: any, { blockHash, contractAddress, _who }: { blockHash: string, contractAddress: string, _who: number }, // eslint-disable-next-line @typescript-eslint/no-unused-vars - __: any, + expressContext: ExpressContext, // eslint-disable-next-line @typescript-eslint/no-unused-vars info: GraphQLResolveInfo ): Promise => { log('getCensuredBy', blockHash, contractAddress, _who); - gqlTotalQueryCount.inc(1); - gqlQueryCount.labels('getCensuredBy').inc(1); // Set cache-control hints // setGQLCacheHints(info, {}, gqlCacheConfig); - return indexer.getCensuredBy(blockHash, contractAddress, _who); + return executeAndRecordMetrics( + indexer, + gqlLogger, + 'getCensuredBy', + expressContext, + async () => indexer.getCensuredBy(blockHash, contractAddress, _who) + ); }, - events: async (_: any, { blockHash, contractAddress, name }: { blockHash: string, contractAddress: string, name?: string }) => { + events: async ( + _: any, + { blockHash, contractAddress, name }: { blockHash: string, contractAddress: string, name?: string }, + expressContext: ExpressContext + ) => { log('events', blockHash, contractAddress, name); - gqlTotalQueryCount.inc(1); - gqlQueryCount.labels('events').inc(1); - const block = await indexer.getBlockProgress(blockHash); - if (!block || !block.isComplete) { - throw new Error(`Block hash ${blockHash} number ${block?.blockNumber} not processed yet`); - } + return executeAndRecordMetrics( + indexer, + gqlLogger, + 'events', + expressContext, + async () => { + const block = await indexer.getBlockProgress(blockHash); + if (!block || !block.isComplete) { + throw new Error(`Block hash ${blockHash} number ${block?.blockNumber} not processed yet`); + } - const events = await indexer.getEventsByFilter(blockHash, contractAddress, name); - return events.map(event => indexer.getResultEvent(event)); + const events = await indexer.getEventsByFilter(blockHash, contractAddress, name); + return events.map(event => indexer.getResultEvent(event)); + } + ); }, - eventsInRange: async (_: any, { fromBlockNumber, toBlockNumber }: { fromBlockNumber: number, toBlockNumber: number }) => { + eventsInRange: async ( + _: any, + { fromBlockNumber, toBlockNumber }: { fromBlockNumber: number, toBlockNumber: number }, + expressContext: ExpressContext + ) => { log('eventsInRange', fromBlockNumber, toBlockNumber); - gqlTotalQueryCount.inc(1); - gqlQueryCount.labels('eventsInRange').inc(1); - const syncStatus = await indexer.getSyncStatus(); + return executeAndRecordMetrics( + indexer, + gqlLogger, + 'eventsInRange', + expressContext, + async () => { + const syncStatus = await indexer.getSyncStatus(); - if (!syncStatus) { - throw new Error('No blocks processed yet'); - } + if (!syncStatus) { + throw new Error('No blocks processed yet'); + } - if ((fromBlockNumber < syncStatus.initialIndexedBlockNumber) || (toBlockNumber > syncStatus.latestProcessedBlockNumber)) { - throw new Error(`Block range should be between ${syncStatus.initialIndexedBlockNumber} and ${syncStatus.latestProcessedBlockNumber}`); - } + if ((fromBlockNumber < syncStatus.initialIndexedBlockNumber) || (toBlockNumber > syncStatus.latestProcessedBlockNumber)) { + throw new Error(`Block range should be between ${syncStatus.initialIndexedBlockNumber} and ${syncStatus.latestProcessedBlockNumber}`); + } - const events = await indexer.getEventsInRange(fromBlockNumber, toBlockNumber); - return events.map(event => indexer.getResultEvent(event)); + const events = await indexer.getEventsInRange(fromBlockNumber, toBlockNumber); + return events.map(event => indexer.getResultEvent(event)); + } + ); }, - getStateByCID: async (_: any, { cid }: { cid: string }) => { + getStateByCID: async ( + _: any, + { cid }: { cid: string }, + expressContext: ExpressContext + ) => { log('getStateByCID', cid); - gqlTotalQueryCount.inc(1); - gqlQueryCount.labels('getStateByCID').inc(1); - const state = await indexer.getStateByCID(cid); + return executeAndRecordMetrics( + indexer, + gqlLogger, + 'getStateByCID', + expressContext, + async () => { + const state = await indexer.getStateByCID(cid); - return state && state.block.isComplete ? getResultState(state) : undefined; + return state && state.block.isComplete ? getResultState(state) : undefined; + } + ); }, - getState: async (_: any, { blockHash, contractAddress, kind }: { blockHash: string, contractAddress: string, kind: string }) => { + getState: async ( + _: any, + { blockHash, contractAddress, kind }: { blockHash: string, contractAddress: string, kind: string }, + expressContext: ExpressContext + ) => { log('getState', blockHash, contractAddress, kind); - gqlTotalQueryCount.inc(1); - gqlQueryCount.labels('getState').inc(1); - const state = await indexer.getPrevState(blockHash, contractAddress, kind); + return executeAndRecordMetrics( + indexer, + gqlLogger, + 'getState', + expressContext, + async () => { + const state = await indexer.getPrevState(blockHash, contractAddress, kind); - return state && state.block.isComplete ? getResultState(state) : undefined; + return state && state.block.isComplete ? getResultState(state) : undefined; + } + ); }, - getSyncStatus: async () => { + getSyncStatus: async ( + _: any, + __: Record, + expressContext: ExpressContext + ) => { log('getSyncStatus'); - gqlTotalQueryCount.inc(1); - gqlQueryCount.labels('getSyncStatus').inc(1); - return indexer.getSyncStatus(); + return executeAndRecordMetrics( + indexer, + gqlLogger, + 'getSyncStatus', + expressContext, + async () => indexer.getSyncStatus() + ); } } }; diff --git a/packages/claims-watcher/.gitignore b/packages/claims-watcher/.gitignore index 0b4cc3f..549d70b 100644 --- a/packages/claims-watcher/.gitignore +++ b/packages/claims-watcher/.gitignore @@ -4,3 +4,5 @@ out/ .vscode .idea + +gql-logs/ diff --git a/packages/claims-watcher/README.md b/packages/claims-watcher/README.md index 855024a..0ee1715 100644 --- a/packages/claims-watcher/README.md +++ b/packages/claims-watcher/README.md @@ -63,7 +63,7 @@ To enable GQL requests caching: -* Update the `server.gqlCache` config with required settings. +* Update the `server.gql.cache` config with required settings. * In the GQL [schema file](./src/schema.gql), use the `cacheControl` directive to apply cache hints at schema level. diff --git a/packages/claims-watcher/environments/local.toml b/packages/claims-watcher/environments/local.toml index fd2b169..a74fd77 100644 --- a/packages/claims-watcher/environments/local.toml +++ b/packages/claims-watcher/environments/local.toml @@ -2,7 +2,6 @@ host = "127.0.0.1" port = 3003 kind = "active" - gqlPath = "/graphql" # Checkpointing state. checkpointing = true @@ -14,22 +13,29 @@ # CAUTION: Disable only if state creation is not desired or can be filled subsequently enableState = false - # Max block range for which to return events in eventsInRange GQL query. - # Use -1 for skipping check on block range. - maxEventsBlockRange = 1000 - # Flag to specify whether RPC endpoint supports block hash as block tag parameter rpcSupportsBlockHashParam = false - # GQL cache settings - [server.gqlCache] - enabled = true + # Server GQL config + [server.gql] + path = "/graphql" - # Max in-memory cache size (in bytes) (default 8 MB) - # maxCacheSize + # Max block range for which to return events in eventsInRange GQL query. + # Use -1 for skipping check on block range. + maxEventsBlockRange = 1000 - # GQL cache-control max-age settings (in seconds) - maxAge = 15 + # Log directory for GQL requests + logDir = "./gql-logs" + + # GQL cache settings + [server.gql.cache] + enabled = true + + # Max in-memory cache size (in bytes) (default 8 MB) + # maxCacheSize + + # GQL cache-control max-age settings (in seconds) + maxAge = 15 [metrics] host = "127.0.0.1" diff --git a/packages/claims-watcher/package.json b/packages/claims-watcher/package.json index f7075f6..29f8d02 100644 --- a/packages/claims-watcher/package.json +++ b/packages/claims-watcher/package.json @@ -1,6 +1,6 @@ { "name": "@cerc-io/claims-watcher", - "version": "0.1.5", + "version": "0.1.6", "description": "claims-watcher", "private": true, "main": "dist/index.js", @@ -28,7 +28,8 @@ }, "repository": { "type": "git", - "url": "git+https://github.com/cerc-io/watcher-ts.git" + "url": "https://github.com/cerc-io/azimuth-watcher-ts", + "directory": "packages/claims-watcher" }, "author": "", "license": "AGPL-3.0", @@ -38,10 +39,10 @@ "homepage": "https://github.com/cerc-io/watcher-ts#readme", "dependencies": { "@apollo/client": "^3.3.19", - "@cerc-io/cli": "^0.2.92", - "@cerc-io/ipld-eth-client": "^0.2.92", - "@cerc-io/solidity-mapper": "^0.2.92", - "@cerc-io/util": "^0.2.92", + "@cerc-io/cli": "^0.2.94", + "@cerc-io/ipld-eth-client": "^0.2.94", + "@cerc-io/solidity-mapper": "^0.2.94", + "@cerc-io/util": "^0.2.94", "@ethersproject/providers": "^5.4.4", "debug": "^4.3.1", "decimal.js": "^10.3.1", @@ -69,6 +70,7 @@ "eslint-plugin-standard": "^5.0.0", "husky": "^7.0.2", "ts-node": "^10.2.1", - "typescript": "^5.0.2" + "typescript": "^5.0.2", + "winston": "^3.13.0" } } diff --git a/packages/claims-watcher/src/indexer.ts b/packages/claims-watcher/src/indexer.ts index 46e3e9b..1bb2807 100644 --- a/packages/claims-watcher/src/indexer.ts +++ b/packages/claims-watcher/src/indexer.ts @@ -420,7 +420,7 @@ export class Indexer implements IndexerInterface { } async getEventsInRange (fromBlockNumber: number, toBlockNumber: number): Promise> { - return this._baseIndexer.getEventsInRange(fromBlockNumber, toBlockNumber, this._serverConfig.maxEventsBlockRange); + return this._baseIndexer.getEventsInRange(fromBlockNumber, toBlockNumber, this._serverConfig.gql.maxEventsBlockRange); } async getSyncStatus (): Promise { diff --git a/packages/claims-watcher/src/resolvers.ts b/packages/claims-watcher/src/resolvers.ts index 2e02f06..b7e82c9 100644 --- a/packages/claims-watcher/src/resolvers.ts +++ b/packages/claims-watcher/src/resolvers.ts @@ -5,11 +5,14 @@ import assert from 'assert'; import debug from 'debug'; import { GraphQLResolveInfo } from 'graphql'; +import { ExpressContext } from 'apollo-server-express'; +import winston from 'winston'; import { ValueResult, gqlTotalQueryCount, gqlQueryCount, + gqlQueryDuration, getResultState, IndexerInterface, GraphQLBigInt, @@ -23,11 +26,57 @@ import { Indexer } from './indexer'; const log = debug('vulcanize:resolver'); -export const createResolvers = async (indexerArg: IndexerInterface, eventWatcher: EventWatcher): Promise => { +const executeAndRecordMetrics = async ( + indexer: Indexer, + gqlLogger: winston.Logger, + opName: string, + expressContext: ExpressContext, + operation: () => Promise +) => { + gqlTotalQueryCount.inc(1); + gqlQueryCount.labels(opName).inc(1); + const endTimer = gqlQueryDuration.labels(opName).startTimer(); + + try { + const [result, syncStatus] = await Promise.all([ + operation(), + indexer.getSyncStatus() + ]); + + gqlLogger.info({ + opName, + query: expressContext.req.body.query, + variables: expressContext.req.body.variables, + latestIndexedBlockNumber: syncStatus?.latestIndexedBlockNumber, + urlPath: expressContext.req.path, + apiKey: expressContext.req.header('x-api-key'), + origin: expressContext.req.headers.origin + }); + return result; + } catch (error) { + gqlLogger.error({ + opName, + error, + query: expressContext.req.body.query, + variables: expressContext.req.body.variables, + urlPath: expressContext.req.path, + apiKey: expressContext.req.header('x-api-key'), + origin: expressContext.req.headers.origin + }); + } finally { + endTimer(); + } +}; + +export const createResolvers = async ( + indexerArg: IndexerInterface, + eventWatcher: EventWatcher, + gqlLogger: winston.Logger +): Promise => { const indexer = indexerArg as Indexer; // eslint-disable-next-line @typescript-eslint/no-unused-vars - const gqlCacheConfig = indexer.serverConfig.gqlCache; + const gqlCacheConfig = indexer.serverConfig.gql.cache; return { BigInt: GraphQLBigInt, @@ -62,79 +111,131 @@ export const createResolvers = async (indexerArg: IndexerInterface, eventWatcher _: any, { blockHash, contractAddress, _whose, _protocol, _claim }: { blockHash: string, contractAddress: string, _whose: bigint, _protocol: string, _claim: string }, // eslint-disable-next-line @typescript-eslint/no-unused-vars - __: any, + expressContext: ExpressContext, // eslint-disable-next-line @typescript-eslint/no-unused-vars info: GraphQLResolveInfo ): Promise => { log('findClaim', blockHash, contractAddress, _whose, _protocol, _claim); - gqlTotalQueryCount.inc(1); - gqlQueryCount.labels('findClaim').inc(1); // Set cache-control hints // setGQLCacheHints(info, {}, gqlCacheConfig); - return indexer.findClaim(blockHash, contractAddress, _whose, _protocol, _claim); + return executeAndRecordMetrics( + indexer, + gqlLogger, + 'findClaim', + expressContext, + async () => indexer.findClaim(blockHash, contractAddress, _whose, _protocol, _claim) + ); }, - events: async (_: any, { blockHash, contractAddress, name }: { blockHash: string, contractAddress: string, name?: string }) => { + events: async ( + _: any, + { blockHash, contractAddress, name }: { blockHash: string, contractAddress: string, name?: string }, + expressContext: ExpressContext + ) => { log('events', blockHash, contractAddress, name); - gqlTotalQueryCount.inc(1); - gqlQueryCount.labels('events').inc(1); - const block = await indexer.getBlockProgress(blockHash); - if (!block || !block.isComplete) { - throw new Error(`Block hash ${blockHash} number ${block?.blockNumber} not processed yet`); - } + return executeAndRecordMetrics( + indexer, + gqlLogger, + 'events', + expressContext, + async () => { + const block = await indexer.getBlockProgress(blockHash); + if (!block || !block.isComplete) { + throw new Error(`Block hash ${blockHash} number ${block?.blockNumber} not processed yet`); + } - const events = await indexer.getEventsByFilter(blockHash, contractAddress, name); - return events.map(event => indexer.getResultEvent(event)); + const events = await indexer.getEventsByFilter(blockHash, contractAddress, name); + return events.map(event => indexer.getResultEvent(event)); + } + ); }, - eventsInRange: async (_: any, { fromBlockNumber, toBlockNumber }: { fromBlockNumber: number, toBlockNumber: number }) => { + eventsInRange: async ( + _: any, + { fromBlockNumber, toBlockNumber }: { fromBlockNumber: number, toBlockNumber: number }, + expressContext: ExpressContext + ) => { log('eventsInRange', fromBlockNumber, toBlockNumber); - gqlTotalQueryCount.inc(1); - gqlQueryCount.labels('eventsInRange').inc(1); - const syncStatus = await indexer.getSyncStatus(); + return executeAndRecordMetrics( + indexer, + gqlLogger, + 'eventsInRange', + expressContext, + async () => { + const syncStatus = await indexer.getSyncStatus(); - if (!syncStatus) { - throw new Error('No blocks processed yet'); - } + if (!syncStatus) { + throw new Error('No blocks processed yet'); + } - if ((fromBlockNumber < syncStatus.initialIndexedBlockNumber) || (toBlockNumber > syncStatus.latestProcessedBlockNumber)) { - throw new Error(`Block range should be between ${syncStatus.initialIndexedBlockNumber} and ${syncStatus.latestProcessedBlockNumber}`); - } + if ((fromBlockNumber < syncStatus.initialIndexedBlockNumber) || (toBlockNumber > syncStatus.latestProcessedBlockNumber)) { + throw new Error(`Block range should be between ${syncStatus.initialIndexedBlockNumber} and ${syncStatus.latestProcessedBlockNumber}`); + } - const events = await indexer.getEventsInRange(fromBlockNumber, toBlockNumber); - return events.map(event => indexer.getResultEvent(event)); + const events = await indexer.getEventsInRange(fromBlockNumber, toBlockNumber); + return events.map(event => indexer.getResultEvent(event)); + } + ); }, - getStateByCID: async (_: any, { cid }: { cid: string }) => { + getStateByCID: async ( + _: any, + { cid }: { cid: string }, + expressContext: ExpressContext + ) => { log('getStateByCID', cid); - gqlTotalQueryCount.inc(1); - gqlQueryCount.labels('getStateByCID').inc(1); - const state = await indexer.getStateByCID(cid); + return executeAndRecordMetrics( + indexer, + gqlLogger, + 'getStateByCID', + expressContext, + async () => { + const state = await indexer.getStateByCID(cid); - return state && state.block.isComplete ? getResultState(state) : undefined; + return state && state.block.isComplete ? getResultState(state) : undefined; + } + ); }, - getState: async (_: any, { blockHash, contractAddress, kind }: { blockHash: string, contractAddress: string, kind: string }) => { + getState: async ( + _: any, + { blockHash, contractAddress, kind }: { blockHash: string, contractAddress: string, kind: string }, + expressContext: ExpressContext + ) => { log('getState', blockHash, contractAddress, kind); - gqlTotalQueryCount.inc(1); - gqlQueryCount.labels('getState').inc(1); - const state = await indexer.getPrevState(blockHash, contractAddress, kind); + return executeAndRecordMetrics( + indexer, + gqlLogger, + 'getState', + expressContext, + async () => { + const state = await indexer.getPrevState(blockHash, contractAddress, kind); - return state && state.block.isComplete ? getResultState(state) : undefined; + return state && state.block.isComplete ? getResultState(state) : undefined; + } + ); }, - getSyncStatus: async () => { + getSyncStatus: async ( + _: any, + __: Record, + expressContext: ExpressContext + ) => { log('getSyncStatus'); - gqlTotalQueryCount.inc(1); - gqlQueryCount.labels('getSyncStatus').inc(1); - return indexer.getSyncStatus(); + return executeAndRecordMetrics( + indexer, + gqlLogger, + 'getSyncStatus', + expressContext, + async () => indexer.getSyncStatus() + ); } } }; diff --git a/packages/conditional-star-release-watcher/.gitignore b/packages/conditional-star-release-watcher/.gitignore index 0b4cc3f..549d70b 100644 --- a/packages/conditional-star-release-watcher/.gitignore +++ b/packages/conditional-star-release-watcher/.gitignore @@ -4,3 +4,5 @@ out/ .vscode .idea + +gql-logs/ diff --git a/packages/conditional-star-release-watcher/README.md b/packages/conditional-star-release-watcher/README.md index d09c478..db64c87 100644 --- a/packages/conditional-star-release-watcher/README.md +++ b/packages/conditional-star-release-watcher/README.md @@ -63,7 +63,7 @@ To enable GQL requests caching: -* Update the `server.gqlCache` config with required settings. +* Update the `server.gql.cache` config with required settings. * In the GQL [schema file](./src/schema.gql), use the `cacheControl` directive to apply cache hints at schema level. diff --git a/packages/conditional-star-release-watcher/environments/local.toml b/packages/conditional-star-release-watcher/environments/local.toml index 8f573e9..397d021 100644 --- a/packages/conditional-star-release-watcher/environments/local.toml +++ b/packages/conditional-star-release-watcher/environments/local.toml @@ -2,7 +2,6 @@ host = "127.0.0.1" port = 3004 kind = "active" - gqlPath = "/graphql" # Checkpointing state. checkpointing = true @@ -14,22 +13,29 @@ # CAUTION: Disable only if state creation is not desired or can be filled subsequently enableState = false - # Max block range for which to return events in eventsInRange GQL query. - # Use -1 for skipping check on block range. - maxEventsBlockRange = 1000 - # Flag to specify whether RPC endpoint supports block hash as block tag parameter rpcSupportsBlockHashParam = false - # GQL cache settings - [server.gqlCache] - enabled = true + # Server GQL config + [server.gql] + path = "/graphql" - # Max in-memory cache size (in bytes) (default 8 MB) - # maxCacheSize + # Max block range for which to return events in eventsInRange GQL query. + # Use -1 for skipping check on block range. + maxEventsBlockRange = 1000 - # GQL cache-control max-age settings (in seconds) - maxAge = 15 + # Log directory for GQL requests + logDir = "./gql-logs" + + # GQL cache settings + [server.gql.cache] + enabled = true + + # Max in-memory cache size (in bytes) (default 8 MB) + # maxCacheSize + + # GQL cache-control max-age settings (in seconds) + maxAge = 15 [metrics] host = "127.0.0.1" diff --git a/packages/conditional-star-release-watcher/package.json b/packages/conditional-star-release-watcher/package.json index 29bfaab..999e3da 100644 --- a/packages/conditional-star-release-watcher/package.json +++ b/packages/conditional-star-release-watcher/package.json @@ -1,6 +1,6 @@ { "name": "@cerc-io/conditional-star-release-watcher", - "version": "0.1.5", + "version": "0.1.6", "description": "conditional-star-release-watcher", "private": true, "main": "dist/index.js", @@ -28,7 +28,8 @@ }, "repository": { "type": "git", - "url": "git+https://github.com/cerc-io/watcher-ts.git" + "url": "https://github.com/cerc-io/azimuth-watcher-ts", + "directory": "packages/conditional-star-release-watcher" }, "author": "", "license": "AGPL-3.0", @@ -38,10 +39,10 @@ "homepage": "https://github.com/cerc-io/watcher-ts#readme", "dependencies": { "@apollo/client": "^3.3.19", - "@cerc-io/cli": "^0.2.92", - "@cerc-io/ipld-eth-client": "^0.2.92", - "@cerc-io/solidity-mapper": "^0.2.92", - "@cerc-io/util": "^0.2.92", + "@cerc-io/cli": "^0.2.94", + "@cerc-io/ipld-eth-client": "^0.2.94", + "@cerc-io/solidity-mapper": "^0.2.94", + "@cerc-io/util": "^0.2.94", "@ethersproject/providers": "^5.4.4", "debug": "^4.3.1", "decimal.js": "^10.3.1", @@ -69,6 +70,7 @@ "eslint-plugin-standard": "^5.0.0", "husky": "^7.0.2", "ts-node": "^10.2.1", - "typescript": "^5.0.2" + "typescript": "^5.0.2", + "winston": "^3.13.0" } } diff --git a/packages/conditional-star-release-watcher/src/indexer.ts b/packages/conditional-star-release-watcher/src/indexer.ts index ef1850b..81b6c38 100644 --- a/packages/conditional-star-release-watcher/src/indexer.ts +++ b/packages/conditional-star-release-watcher/src/indexer.ts @@ -701,7 +701,7 @@ export class Indexer implements IndexerInterface { } async getEventsInRange (fromBlockNumber: number, toBlockNumber: number): Promise> { - return this._baseIndexer.getEventsInRange(fromBlockNumber, toBlockNumber, this._serverConfig.maxEventsBlockRange); + return this._baseIndexer.getEventsInRange(fromBlockNumber, toBlockNumber, this._serverConfig.gql.maxEventsBlockRange); } async getSyncStatus (): Promise { diff --git a/packages/conditional-star-release-watcher/src/resolvers.ts b/packages/conditional-star-release-watcher/src/resolvers.ts index a1a93ef..c1e79f3 100644 --- a/packages/conditional-star-release-watcher/src/resolvers.ts +++ b/packages/conditional-star-release-watcher/src/resolvers.ts @@ -5,11 +5,14 @@ import assert from 'assert'; import debug from 'debug'; import { GraphQLResolveInfo } from 'graphql'; +import { ExpressContext } from 'apollo-server-express'; +import winston from 'winston'; import { ValueResult, gqlTotalQueryCount, gqlQueryCount, + gqlQueryDuration, getResultState, IndexerInterface, GraphQLBigInt, @@ -23,11 +26,57 @@ import { Indexer } from './indexer'; const log = debug('vulcanize:resolver'); -export const createResolvers = async (indexerArg: IndexerInterface, eventWatcher: EventWatcher): Promise => { +const executeAndRecordMetrics = async ( + indexer: Indexer, + gqlLogger: winston.Logger, + opName: string, + expressContext: ExpressContext, + operation: () => Promise +) => { + gqlTotalQueryCount.inc(1); + gqlQueryCount.labels(opName).inc(1); + const endTimer = gqlQueryDuration.labels(opName).startTimer(); + + try { + const [result, syncStatus] = await Promise.all([ + operation(), + indexer.getSyncStatus() + ]); + + gqlLogger.info({ + opName, + query: expressContext.req.body.query, + variables: expressContext.req.body.variables, + latestIndexedBlockNumber: syncStatus?.latestIndexedBlockNumber, + urlPath: expressContext.req.path, + apiKey: expressContext.req.header('x-api-key'), + origin: expressContext.req.headers.origin + }); + return result; + } catch (error) { + gqlLogger.error({ + opName, + error, + query: expressContext.req.body.query, + variables: expressContext.req.body.variables, + urlPath: expressContext.req.path, + apiKey: expressContext.req.header('x-api-key'), + origin: expressContext.req.headers.origin + }); + } finally { + endTimer(); + } +}; + +export const createResolvers = async ( + indexerArg: IndexerInterface, + eventWatcher: EventWatcher, + gqlLogger: winston.Logger +): Promise => { const indexer = indexerArg as Indexer; // eslint-disable-next-line @typescript-eslint/no-unused-vars - const gqlCacheConfig = indexer.serverConfig.gqlCache; + const gqlCacheConfig = indexer.serverConfig.gql.cache; return { BigInt: GraphQLBigInt, @@ -62,241 +111,329 @@ export const createResolvers = async (indexerArg: IndexerInterface, eventWatcher _: any, { blockHash, contractAddress, _participant, _batch }: { blockHash: string, contractAddress: string, _participant: string, _batch: number }, // eslint-disable-next-line @typescript-eslint/no-unused-vars - __: any, + expressContext: ExpressContext, // eslint-disable-next-line @typescript-eslint/no-unused-vars info: GraphQLResolveInfo ): Promise => { log('withdrawLimit', blockHash, contractAddress, _participant, _batch); - gqlTotalQueryCount.inc(1); - gqlQueryCount.labels('withdrawLimit').inc(1); // Set cache-control hints // setGQLCacheHints(info, {}, gqlCacheConfig); - return indexer.withdrawLimit(blockHash, contractAddress, _participant, _batch); + return executeAndRecordMetrics( + indexer, + gqlLogger, + 'withdrawLimit', + expressContext, + async () => indexer.withdrawLimit(blockHash, contractAddress, _participant, _batch) + ); }, verifyBalance: ( _: any, { blockHash, contractAddress, _participant }: { blockHash: string, contractAddress: string, _participant: string }, // eslint-disable-next-line @typescript-eslint/no-unused-vars - __: any, + expressContext: ExpressContext, // eslint-disable-next-line @typescript-eslint/no-unused-vars info: GraphQLResolveInfo ): Promise => { log('verifyBalance', blockHash, contractAddress, _participant); - gqlTotalQueryCount.inc(1); - gqlQueryCount.labels('verifyBalance').inc(1); // Set cache-control hints // setGQLCacheHints(info, {}, gqlCacheConfig); - return indexer.verifyBalance(blockHash, contractAddress, _participant); + return executeAndRecordMetrics( + indexer, + gqlLogger, + 'verifyBalance', + expressContext, + async () => indexer.verifyBalance(blockHash, contractAddress, _participant) + ); }, getBatches: ( _: any, { blockHash, contractAddress, _participant }: { blockHash: string, contractAddress: string, _participant: string }, // eslint-disable-next-line @typescript-eslint/no-unused-vars - __: any, + expressContext: ExpressContext, // eslint-disable-next-line @typescript-eslint/no-unused-vars info: GraphQLResolveInfo ): Promise => { log('getBatches', blockHash, contractAddress, _participant); - gqlTotalQueryCount.inc(1); - gqlQueryCount.labels('getBatches').inc(1); // Set cache-control hints // setGQLCacheHints(info, {}, gqlCacheConfig); - return indexer.getBatches(blockHash, contractAddress, _participant); + return executeAndRecordMetrics( + indexer, + gqlLogger, + 'getBatches', + expressContext, + async () => indexer.getBatches(blockHash, contractAddress, _participant) + ); }, getBatch: ( _: any, { blockHash, contractAddress, _participant, _batch }: { blockHash: string, contractAddress: string, _participant: string, _batch: number }, // eslint-disable-next-line @typescript-eslint/no-unused-vars - __: any, + expressContext: ExpressContext, // eslint-disable-next-line @typescript-eslint/no-unused-vars info: GraphQLResolveInfo ): Promise => { log('getBatch', blockHash, contractAddress, _participant, _batch); - gqlTotalQueryCount.inc(1); - gqlQueryCount.labels('getBatch').inc(1); // Set cache-control hints // setGQLCacheHints(info, {}, gqlCacheConfig); - return indexer.getBatch(blockHash, contractAddress, _participant, _batch); + return executeAndRecordMetrics( + indexer, + gqlLogger, + 'getBatch', + expressContext, + async () => indexer.getBatch(blockHash, contractAddress, _participant, _batch) + ); }, getWithdrawn: ( _: any, { blockHash, contractAddress, _participant }: { blockHash: string, contractAddress: string, _participant: string }, // eslint-disable-next-line @typescript-eslint/no-unused-vars - __: any, + expressContext: ExpressContext, // eslint-disable-next-line @typescript-eslint/no-unused-vars info: GraphQLResolveInfo ): Promise => { log('getWithdrawn', blockHash, contractAddress, _participant); - gqlTotalQueryCount.inc(1); - gqlQueryCount.labels('getWithdrawn').inc(1); // Set cache-control hints // setGQLCacheHints(info, {}, gqlCacheConfig); - return indexer.getWithdrawn(blockHash, contractAddress, _participant); + return executeAndRecordMetrics( + indexer, + gqlLogger, + 'getWithdrawn', + expressContext, + async () => indexer.getWithdrawn(blockHash, contractAddress, _participant) + ); }, getWithdrawnFromBatch: ( _: any, { blockHash, contractAddress, _participant, _batch }: { blockHash: string, contractAddress: string, _participant: string, _batch: number }, // eslint-disable-next-line @typescript-eslint/no-unused-vars - __: any, + expressContext: ExpressContext, // eslint-disable-next-line @typescript-eslint/no-unused-vars info: GraphQLResolveInfo ): Promise => { log('getWithdrawnFromBatch', blockHash, contractAddress, _participant, _batch); - gqlTotalQueryCount.inc(1); - gqlQueryCount.labels('getWithdrawnFromBatch').inc(1); // Set cache-control hints // setGQLCacheHints(info, {}, gqlCacheConfig); - return indexer.getWithdrawnFromBatch(blockHash, contractAddress, _participant, _batch); + return executeAndRecordMetrics( + indexer, + gqlLogger, + 'getWithdrawnFromBatch', + expressContext, + async () => indexer.getWithdrawnFromBatch(blockHash, contractAddress, _participant, _batch) + ); }, getForfeited: ( _: any, { blockHash, contractAddress, _participant }: { blockHash: string, contractAddress: string, _participant: string }, // eslint-disable-next-line @typescript-eslint/no-unused-vars - __: any, + expressContext: ExpressContext, // eslint-disable-next-line @typescript-eslint/no-unused-vars info: GraphQLResolveInfo ): Promise => { log('getForfeited', blockHash, contractAddress, _participant); - gqlTotalQueryCount.inc(1); - gqlQueryCount.labels('getForfeited').inc(1); // Set cache-control hints // setGQLCacheHints(info, {}, gqlCacheConfig); - return indexer.getForfeited(blockHash, contractAddress, _participant); + return executeAndRecordMetrics( + indexer, + gqlLogger, + 'getForfeited', + expressContext, + async () => indexer.getForfeited(blockHash, contractAddress, _participant) + ); }, hasForfeitedBatch: ( _: any, { blockHash, contractAddress, _participant, _batch }: { blockHash: string, contractAddress: string, _participant: string, _batch: number }, // eslint-disable-next-line @typescript-eslint/no-unused-vars - __: any, + expressContext: ExpressContext, // eslint-disable-next-line @typescript-eslint/no-unused-vars info: GraphQLResolveInfo ): Promise => { log('hasForfeitedBatch', blockHash, contractAddress, _participant, _batch); - gqlTotalQueryCount.inc(1); - gqlQueryCount.labels('hasForfeitedBatch').inc(1); // Set cache-control hints // setGQLCacheHints(info, {}, gqlCacheConfig); - return indexer.hasForfeitedBatch(blockHash, contractAddress, _participant, _batch); + return executeAndRecordMetrics( + indexer, + gqlLogger, + 'hasForfeitedBatch', + expressContext, + async () => indexer.hasForfeitedBatch(blockHash, contractAddress, _participant, _batch) + ); }, getRemainingStars: ( _: any, { blockHash, contractAddress, _participant }: { blockHash: string, contractAddress: string, _participant: string }, // eslint-disable-next-line @typescript-eslint/no-unused-vars - __: any, + expressContext: ExpressContext, // eslint-disable-next-line @typescript-eslint/no-unused-vars info: GraphQLResolveInfo ): Promise => { log('getRemainingStars', blockHash, contractAddress, _participant); - gqlTotalQueryCount.inc(1); - gqlQueryCount.labels('getRemainingStars').inc(1); // Set cache-control hints // setGQLCacheHints(info, {}, gqlCacheConfig); - return indexer.getRemainingStars(blockHash, contractAddress, _participant); + return executeAndRecordMetrics( + indexer, + gqlLogger, + 'getRemainingStars', + expressContext, + async () => indexer.getRemainingStars(blockHash, contractAddress, _participant) + ); }, getConditionsState: ( _: any, { blockHash, contractAddress }: { blockHash: string, contractAddress: string }, // eslint-disable-next-line @typescript-eslint/no-unused-vars - __: any, + expressContext: ExpressContext, // eslint-disable-next-line @typescript-eslint/no-unused-vars info: GraphQLResolveInfo ): Promise => { log('getConditionsState', blockHash, contractAddress); - gqlTotalQueryCount.inc(1); - gqlQueryCount.labels('getConditionsState').inc(1); // Set cache-control hints // setGQLCacheHints(info, {}, gqlCacheConfig); - return indexer.getConditionsState(blockHash, contractAddress); + return executeAndRecordMetrics( + indexer, + gqlLogger, + 'getConditionsState', + expressContext, + async () => indexer.getConditionsState(blockHash, contractAddress) + ); }, - events: async (_: any, { blockHash, contractAddress, name }: { blockHash: string, contractAddress: string, name?: string }) => { + events: async ( + _: any, + { blockHash, contractAddress, name }: { blockHash: string, contractAddress: string, name?: string }, + expressContext: ExpressContext + ) => { log('events', blockHash, contractAddress, name); - gqlTotalQueryCount.inc(1); - gqlQueryCount.labels('events').inc(1); - const block = await indexer.getBlockProgress(blockHash); - if (!block || !block.isComplete) { - throw new Error(`Block hash ${blockHash} number ${block?.blockNumber} not processed yet`); - } + return executeAndRecordMetrics( + indexer, + gqlLogger, + 'events', + expressContext, + async () => { + const block = await indexer.getBlockProgress(blockHash); + if (!block || !block.isComplete) { + throw new Error(`Block hash ${blockHash} number ${block?.blockNumber} not processed yet`); + } - const events = await indexer.getEventsByFilter(blockHash, contractAddress, name); - return events.map(event => indexer.getResultEvent(event)); + const events = await indexer.getEventsByFilter(blockHash, contractAddress, name); + return events.map(event => indexer.getResultEvent(event)); + } + ); }, - eventsInRange: async (_: any, { fromBlockNumber, toBlockNumber }: { fromBlockNumber: number, toBlockNumber: number }) => { + eventsInRange: async ( + _: any, + { fromBlockNumber, toBlockNumber }: { fromBlockNumber: number, toBlockNumber: number }, + expressContext: ExpressContext + ) => { log('eventsInRange', fromBlockNumber, toBlockNumber); - gqlTotalQueryCount.inc(1); - gqlQueryCount.labels('eventsInRange').inc(1); - const syncStatus = await indexer.getSyncStatus(); + return executeAndRecordMetrics( + indexer, + gqlLogger, + 'eventsInRange', + expressContext, + async () => { + const syncStatus = await indexer.getSyncStatus(); - if (!syncStatus) { - throw new Error('No blocks processed yet'); - } + if (!syncStatus) { + throw new Error('No blocks processed yet'); + } - if ((fromBlockNumber < syncStatus.initialIndexedBlockNumber) || (toBlockNumber > syncStatus.latestProcessedBlockNumber)) { - throw new Error(`Block range should be between ${syncStatus.initialIndexedBlockNumber} and ${syncStatus.latestProcessedBlockNumber}`); - } + if ((fromBlockNumber < syncStatus.initialIndexedBlockNumber) || (toBlockNumber > syncStatus.latestProcessedBlockNumber)) { + throw new Error(`Block range should be between ${syncStatus.initialIndexedBlockNumber} and ${syncStatus.latestProcessedBlockNumber}`); + } - const events = await indexer.getEventsInRange(fromBlockNumber, toBlockNumber); - return events.map(event => indexer.getResultEvent(event)); + const events = await indexer.getEventsInRange(fromBlockNumber, toBlockNumber); + return events.map(event => indexer.getResultEvent(event)); + } + ); }, - getStateByCID: async (_: any, { cid }: { cid: string }) => { + getStateByCID: async ( + _: any, + { cid }: { cid: string }, + expressContext: ExpressContext + ) => { log('getStateByCID', cid); - gqlTotalQueryCount.inc(1); - gqlQueryCount.labels('getStateByCID').inc(1); - const state = await indexer.getStateByCID(cid); + return executeAndRecordMetrics( + indexer, + gqlLogger, + 'getStateByCID', + expressContext, + async () => { + const state = await indexer.getStateByCID(cid); - return state && state.block.isComplete ? getResultState(state) : undefined; + return state && state.block.isComplete ? getResultState(state) : undefined; + } + ); }, - getState: async (_: any, { blockHash, contractAddress, kind }: { blockHash: string, contractAddress: string, kind: string }) => { + getState: async ( + _: any, + { blockHash, contractAddress, kind }: { blockHash: string, contractAddress: string, kind: string }, + expressContext: ExpressContext + ) => { log('getState', blockHash, contractAddress, kind); - gqlTotalQueryCount.inc(1); - gqlQueryCount.labels('getState').inc(1); - const state = await indexer.getPrevState(blockHash, contractAddress, kind); + return executeAndRecordMetrics( + indexer, + gqlLogger, + 'getState', + expressContext, + async () => { + const state = await indexer.getPrevState(blockHash, contractAddress, kind); - return state && state.block.isComplete ? getResultState(state) : undefined; + return state && state.block.isComplete ? getResultState(state) : undefined; + } + ); }, - getSyncStatus: async () => { + getSyncStatus: async ( + _: any, + __: Record, + expressContext: ExpressContext + ) => { log('getSyncStatus'); - gqlTotalQueryCount.inc(1); - gqlQueryCount.labels('getSyncStatus').inc(1); - return indexer.getSyncStatus(); + return executeAndRecordMetrics( + indexer, + gqlLogger, + 'getSyncStatus', + expressContext, + async () => indexer.getSyncStatus() + ); } } }; diff --git a/packages/delegated-sending-watcher/.gitignore b/packages/delegated-sending-watcher/.gitignore index 0b4cc3f..549d70b 100644 --- a/packages/delegated-sending-watcher/.gitignore +++ b/packages/delegated-sending-watcher/.gitignore @@ -4,3 +4,5 @@ out/ .vscode .idea + +gql-logs/ diff --git a/packages/delegated-sending-watcher/README.md b/packages/delegated-sending-watcher/README.md index bdb7240..7b9d2d5 100644 --- a/packages/delegated-sending-watcher/README.md +++ b/packages/delegated-sending-watcher/README.md @@ -65,7 +65,7 @@ This watcher has been generated based on the `DelegatedSending` contract deploye To enable GQL requests caching: -* Update the `server.gqlCache` config with required settings. +* Update the `server.gql.cache` config with required settings. * In the GQL [schema file](./src/schema.gql), use the `cacheControl` directive to apply cache hints at schema level. diff --git a/packages/delegated-sending-watcher/environments/local.toml b/packages/delegated-sending-watcher/environments/local.toml index c6de7a8..0f5b8f1 100644 --- a/packages/delegated-sending-watcher/environments/local.toml +++ b/packages/delegated-sending-watcher/environments/local.toml @@ -2,7 +2,6 @@ host = "127.0.0.1" port = 3005 kind = "active" - gqlPath = "/graphql" # Checkpointing state. checkpointing = true @@ -14,22 +13,29 @@ # CAUTION: Disable only if state creation is not desired or can be filled subsequently enableState = false - # Max block range for which to return events in eventsInRange GQL query. - # Use -1 for skipping check on block range. - maxEventsBlockRange = 1000 - # Flag to specify whether RPC endpoint supports block hash as block tag parameter rpcSupportsBlockHashParam = false - # GQL cache settings - [server.gqlCache] - enabled = true + # Server GQL config + [server.gql] + path = "/graphql" - # Max in-memory cache size (in bytes) (default 8 MB) - # maxCacheSize + # Max block range for which to return events in eventsInRange GQL query. + # Use -1 for skipping check on block range. + maxEventsBlockRange = 1000 - # GQL cache-control max-age settings (in seconds) - maxAge = 15 + # Log directory for GQL requests + logDir = "./gql-logs" + + # GQL cache settings + [server.gql.cache] + enabled = true + + # Max in-memory cache size (in bytes) (default 8 MB) + # maxCacheSize + + # GQL cache-control max-age settings (in seconds) + maxAge = 15 [metrics] host = "127.0.0.1" diff --git a/packages/delegated-sending-watcher/package.json b/packages/delegated-sending-watcher/package.json index f816077..3e2c15f 100644 --- a/packages/delegated-sending-watcher/package.json +++ b/packages/delegated-sending-watcher/package.json @@ -1,6 +1,6 @@ { "name": "@cerc-io/delegated-sending-watcher", - "version": "0.1.5", + "version": "0.1.6", "description": "delegated-sending-watcher", "private": true, "main": "dist/index.js", @@ -28,7 +28,8 @@ }, "repository": { "type": "git", - "url": "git+https://github.com/cerc-io/watcher-ts.git" + "url": "https://github.com/cerc-io/azimuth-watcher-ts", + "directory": "packages/delegated-sending-watcher" }, "author": "", "license": "AGPL-3.0", @@ -38,10 +39,10 @@ "homepage": "https://github.com/cerc-io/watcher-ts#readme", "dependencies": { "@apollo/client": "^3.3.19", - "@cerc-io/cli": "^0.2.92", - "@cerc-io/ipld-eth-client": "^0.2.92", - "@cerc-io/solidity-mapper": "^0.2.92", - "@cerc-io/util": "^0.2.92", + "@cerc-io/cli": "^0.2.94", + "@cerc-io/ipld-eth-client": "^0.2.94", + "@cerc-io/solidity-mapper": "^0.2.94", + "@cerc-io/util": "^0.2.94", "@ethersproject/providers": "^5.4.4", "debug": "^4.3.1", "decimal.js": "^10.3.1", @@ -69,6 +70,7 @@ "eslint-plugin-standard": "^5.0.0", "husky": "^7.0.2", "ts-node": "^10.2.1", - "typescript": "^5.0.2" + "typescript": "^5.0.2", + "winston": "^3.13.0" } } diff --git a/packages/delegated-sending-watcher/src/indexer.ts b/packages/delegated-sending-watcher/src/indexer.ts index d1f43ca..7fc87fb 100644 --- a/packages/delegated-sending-watcher/src/indexer.ts +++ b/packages/delegated-sending-watcher/src/indexer.ts @@ -450,7 +450,7 @@ export class Indexer implements IndexerInterface { } async getEventsInRange (fromBlockNumber: number, toBlockNumber: number): Promise> { - return this._baseIndexer.getEventsInRange(fromBlockNumber, toBlockNumber, this._serverConfig.maxEventsBlockRange); + return this._baseIndexer.getEventsInRange(fromBlockNumber, toBlockNumber, this._serverConfig.gql.maxEventsBlockRange); } async getSyncStatus (): Promise { diff --git a/packages/delegated-sending-watcher/src/resolvers.ts b/packages/delegated-sending-watcher/src/resolvers.ts index e0bbe06..4053d05 100644 --- a/packages/delegated-sending-watcher/src/resolvers.ts +++ b/packages/delegated-sending-watcher/src/resolvers.ts @@ -5,11 +5,14 @@ import assert from 'assert'; import debug from 'debug'; import { GraphQLResolveInfo } from 'graphql'; +import { ExpressContext } from 'apollo-server-express'; +import winston from 'winston'; import { ValueResult, gqlTotalQueryCount, gqlQueryCount, + gqlQueryDuration, getResultState, IndexerInterface, GraphQLBigInt, @@ -23,11 +26,57 @@ import { Indexer } from './indexer'; const log = debug('vulcanize:resolver'); -export const createResolvers = async (indexerArg: IndexerInterface, eventWatcher: EventWatcher): Promise => { +const executeAndRecordMetrics = async ( + indexer: Indexer, + gqlLogger: winston.Logger, + opName: string, + expressContext: ExpressContext, + operation: () => Promise +) => { + gqlTotalQueryCount.inc(1); + gqlQueryCount.labels(opName).inc(1); + const endTimer = gqlQueryDuration.labels(opName).startTimer(); + + try { + const [result, syncStatus] = await Promise.all([ + operation(), + indexer.getSyncStatus() + ]); + + gqlLogger.info({ + opName, + query: expressContext.req.body.query, + variables: expressContext.req.body.variables, + latestIndexedBlockNumber: syncStatus?.latestIndexedBlockNumber, + urlPath: expressContext.req.path, + apiKey: expressContext.req.header('x-api-key'), + origin: expressContext.req.headers.origin + }); + return result; + } catch (error) { + gqlLogger.error({ + opName, + error, + query: expressContext.req.body.query, + variables: expressContext.req.body.variables, + urlPath: expressContext.req.path, + apiKey: expressContext.req.header('x-api-key'), + origin: expressContext.req.headers.origin + }); + } finally { + endTimer(); + } +}; + +export const createResolvers = async ( + indexerArg: IndexerInterface, + eventWatcher: EventWatcher, + gqlLogger: winston.Logger +): Promise => { const indexer = indexerArg as Indexer; // eslint-disable-next-line @typescript-eslint/no-unused-vars - const gqlCacheConfig = indexer.serverConfig.gqlCache; + const gqlCacheConfig = indexer.serverConfig.gql.cache; return { BigInt: GraphQLBigInt, @@ -62,97 +111,153 @@ export const createResolvers = async (indexerArg: IndexerInterface, eventWatcher _: any, { blockHash, contractAddress, _as, _point }: { blockHash: string, contractAddress: string, _as: bigint, _point: bigint }, // eslint-disable-next-line @typescript-eslint/no-unused-vars - __: any, + expressContext: ExpressContext, // eslint-disable-next-line @typescript-eslint/no-unused-vars info: GraphQLResolveInfo ): Promise => { log('canSend', blockHash, contractAddress, _as, _point); - gqlTotalQueryCount.inc(1); - gqlQueryCount.labels('canSend').inc(1); // Set cache-control hints // setGQLCacheHints(info, {}, gqlCacheConfig); - return indexer.canSend(blockHash, contractAddress, _as, _point); + return executeAndRecordMetrics( + indexer, + gqlLogger, + 'canSend', + expressContext, + async () => indexer.canSend(blockHash, contractAddress, _as, _point) + ); }, canReceive: ( _: any, { blockHash, contractAddress, _recipient }: { blockHash: string, contractAddress: string, _recipient: string }, // eslint-disable-next-line @typescript-eslint/no-unused-vars - __: any, + expressContext: ExpressContext, // eslint-disable-next-line @typescript-eslint/no-unused-vars info: GraphQLResolveInfo ): Promise => { log('canReceive', blockHash, contractAddress, _recipient); - gqlTotalQueryCount.inc(1); - gqlQueryCount.labels('canReceive').inc(1); // Set cache-control hints // setGQLCacheHints(info, {}, gqlCacheConfig); - return indexer.canReceive(blockHash, contractAddress, _recipient); + return executeAndRecordMetrics( + indexer, + gqlLogger, + 'canReceive', + expressContext, + async () => indexer.canReceive(blockHash, contractAddress, _recipient) + ); }, - events: async (_: any, { blockHash, contractAddress, name }: { blockHash: string, contractAddress: string, name?: string }) => { + events: async ( + _: any, + { blockHash, contractAddress, name }: { blockHash: string, contractAddress: string, name?: string }, + expressContext: ExpressContext + ) => { log('events', blockHash, contractAddress, name); - gqlTotalQueryCount.inc(1); - gqlQueryCount.labels('events').inc(1); - const block = await indexer.getBlockProgress(blockHash); - if (!block || !block.isComplete) { - throw new Error(`Block hash ${blockHash} number ${block?.blockNumber} not processed yet`); - } + return executeAndRecordMetrics( + indexer, + gqlLogger, + 'events', + expressContext, + async () => { + const block = await indexer.getBlockProgress(blockHash); + if (!block || !block.isComplete) { + throw new Error(`Block hash ${blockHash} number ${block?.blockNumber} not processed yet`); + } - const events = await indexer.getEventsByFilter(blockHash, contractAddress, name); - return events.map(event => indexer.getResultEvent(event)); + const events = await indexer.getEventsByFilter(blockHash, contractAddress, name); + return events.map(event => indexer.getResultEvent(event)); + } + ); }, - eventsInRange: async (_: any, { fromBlockNumber, toBlockNumber }: { fromBlockNumber: number, toBlockNumber: number }) => { + eventsInRange: async ( + _: any, + { fromBlockNumber, toBlockNumber }: { fromBlockNumber: number, toBlockNumber: number }, + expressContext: ExpressContext + ) => { log('eventsInRange', fromBlockNumber, toBlockNumber); - gqlTotalQueryCount.inc(1); - gqlQueryCount.labels('eventsInRange').inc(1); - const syncStatus = await indexer.getSyncStatus(); + return executeAndRecordMetrics( + indexer, + gqlLogger, + 'eventsInRange', + expressContext, + async () => { + const syncStatus = await indexer.getSyncStatus(); - if (!syncStatus) { - throw new Error('No blocks processed yet'); - } + if (!syncStatus) { + throw new Error('No blocks processed yet'); + } - if ((fromBlockNumber < syncStatus.initialIndexedBlockNumber) || (toBlockNumber > syncStatus.latestProcessedBlockNumber)) { - throw new Error(`Block range should be between ${syncStatus.initialIndexedBlockNumber} and ${syncStatus.latestProcessedBlockNumber}`); - } + if ((fromBlockNumber < syncStatus.initialIndexedBlockNumber) || (toBlockNumber > syncStatus.latestProcessedBlockNumber)) { + throw new Error(`Block range should be between ${syncStatus.initialIndexedBlockNumber} and ${syncStatus.latestProcessedBlockNumber}`); + } - const events = await indexer.getEventsInRange(fromBlockNumber, toBlockNumber); - return events.map(event => indexer.getResultEvent(event)); + const events = await indexer.getEventsInRange(fromBlockNumber, toBlockNumber); + return events.map(event => indexer.getResultEvent(event)); + } + ); }, - getStateByCID: async (_: any, { cid }: { cid: string }) => { + getStateByCID: async ( + _: any, + { cid }: { cid: string }, + expressContext: ExpressContext + ) => { log('getStateByCID', cid); - gqlTotalQueryCount.inc(1); - gqlQueryCount.labels('getStateByCID').inc(1); - const state = await indexer.getStateByCID(cid); + return executeAndRecordMetrics( + indexer, + gqlLogger, + 'getStateByCID', + expressContext, + async () => { + const state = await indexer.getStateByCID(cid); - return state && state.block.isComplete ? getResultState(state) : undefined; + return state && state.block.isComplete ? getResultState(state) : undefined; + } + ); }, - getState: async (_: any, { blockHash, contractAddress, kind }: { blockHash: string, contractAddress: string, kind: string }) => { + getState: async ( + _: any, + { blockHash, contractAddress, kind }: { blockHash: string, contractAddress: string, kind: string }, + expressContext: ExpressContext + ) => { log('getState', blockHash, contractAddress, kind); - gqlTotalQueryCount.inc(1); - gqlQueryCount.labels('getState').inc(1); - const state = await indexer.getPrevState(blockHash, contractAddress, kind); + return executeAndRecordMetrics( + indexer, + gqlLogger, + 'getState', + expressContext, + async () => { + const state = await indexer.getPrevState(blockHash, contractAddress, kind); - return state && state.block.isComplete ? getResultState(state) : undefined; + return state && state.block.isComplete ? getResultState(state) : undefined; + } + ); }, - getSyncStatus: async () => { + getSyncStatus: async ( + _: any, + __: Record, + expressContext: ExpressContext + ) => { log('getSyncStatus'); - gqlTotalQueryCount.inc(1); - gqlQueryCount.labels('getSyncStatus').inc(1); - return indexer.getSyncStatus(); + return executeAndRecordMetrics( + indexer, + gqlLogger, + 'getSyncStatus', + expressContext, + async () => indexer.getSyncStatus() + ); } } }; diff --git a/packages/ecliptic-watcher/.gitignore b/packages/ecliptic-watcher/.gitignore index 0b4cc3f..549d70b 100644 --- a/packages/ecliptic-watcher/.gitignore +++ b/packages/ecliptic-watcher/.gitignore @@ -4,3 +4,5 @@ out/ .vscode .idea + +gql-logs/ diff --git a/packages/ecliptic-watcher/README.md b/packages/ecliptic-watcher/README.md index 8b4bad1..9444691 100644 --- a/packages/ecliptic-watcher/README.md +++ b/packages/ecliptic-watcher/README.md @@ -63,7 +63,7 @@ To enable GQL requests caching: -* Update the `server.gqlCache` config with required settings. +* Update the `server.gql.cache` config with required settings. * In the GQL [schema file](./src/schema.gql), use the `cacheControl` directive to apply cache hints at schema level. diff --git a/packages/ecliptic-watcher/environments/local.toml b/packages/ecliptic-watcher/environments/local.toml index 801d889..7a7e952 100644 --- a/packages/ecliptic-watcher/environments/local.toml +++ b/packages/ecliptic-watcher/environments/local.toml @@ -2,7 +2,6 @@ host = "127.0.0.1" port = 3006 kind = "active" - gqlPath = "/graphql" # Checkpointing state. checkpointing = true @@ -14,22 +13,29 @@ # CAUTION: Disable only if state creation is not desired or can be filled subsequently enableState = false - # Max block range for which to return events in eventsInRange GQL query. - # Use -1 for skipping check on block range. - maxEventsBlockRange = 1000 - # Flag to specify whether RPC endpoint supports block hash as block tag parameter rpcSupportsBlockHashParam = false - # GQL cache settings - [server.gqlCache] - enabled = true + # Server GQL config + [server.gql] + path = "/graphql" - # Max in-memory cache size (in bytes) (default 8 MB) - # maxCacheSize + # Max block range for which to return events in eventsInRange GQL query. + # Use -1 for skipping check on block range. + maxEventsBlockRange = 1000 - # GQL cache-control max-age settings (in seconds) - maxAge = 15 + # Log directory for GQL requests + logDir = "./gql-logs" + + # GQL cache settings + [server.gql.cache] + enabled = true + + # Max in-memory cache size (in bytes) (default 8 MB) + # maxCacheSize + + # GQL cache-control max-age settings (in seconds) + maxAge = 15 [metrics] host = "127.0.0.1" diff --git a/packages/ecliptic-watcher/package.json b/packages/ecliptic-watcher/package.json index 751e77c..926d6bf 100644 --- a/packages/ecliptic-watcher/package.json +++ b/packages/ecliptic-watcher/package.json @@ -1,6 +1,6 @@ { "name": "@cerc-io/ecliptic-watcher", - "version": "0.1.5", + "version": "0.1.6", "description": "ecliptic-watcher", "private": true, "main": "dist/index.js", @@ -28,7 +28,8 @@ }, "repository": { "type": "git", - "url": "git+https://github.com/cerc-io/watcher-ts.git" + "url": "https://github.com/cerc-io/azimuth-watcher-ts", + "directory": "packages/ecliptic-watcher" }, "author": "", "license": "AGPL-3.0", @@ -38,10 +39,10 @@ "homepage": "https://github.com/cerc-io/watcher-ts#readme", "dependencies": { "@apollo/client": "^3.3.19", - "@cerc-io/cli": "^0.2.92", - "@cerc-io/ipld-eth-client": "^0.2.92", - "@cerc-io/solidity-mapper": "^0.2.92", - "@cerc-io/util": "^0.2.92", + "@cerc-io/cli": "^0.2.94", + "@cerc-io/ipld-eth-client": "^0.2.94", + "@cerc-io/solidity-mapper": "^0.2.94", + "@cerc-io/util": "^0.2.94", "@ethersproject/providers": "^5.4.4", "debug": "^4.3.1", "decimal.js": "^10.3.1", @@ -69,6 +70,7 @@ "eslint-plugin-standard": "^5.0.0", "husky": "^7.0.2", "ts-node": "^10.2.1", - "typescript": "^5.0.2" + "typescript": "^5.0.2", + "winston": "^3.13.0" } } diff --git a/packages/ecliptic-watcher/src/indexer.ts b/packages/ecliptic-watcher/src/indexer.ts index 8c8d1cd..86244cf 100644 --- a/packages/ecliptic-watcher/src/indexer.ts +++ b/packages/ecliptic-watcher/src/indexer.ts @@ -722,7 +722,7 @@ export class Indexer implements IndexerInterface { } async getEventsInRange (fromBlockNumber: number, toBlockNumber: number): Promise> { - return this._baseIndexer.getEventsInRange(fromBlockNumber, toBlockNumber, this._serverConfig.maxEventsBlockRange); + return this._baseIndexer.getEventsInRange(fromBlockNumber, toBlockNumber, this._serverConfig.gql.maxEventsBlockRange); } async getSyncStatus (): Promise { diff --git a/packages/ecliptic-watcher/src/resolvers.ts b/packages/ecliptic-watcher/src/resolvers.ts index ec4d9cf..07ef8fe 100644 --- a/packages/ecliptic-watcher/src/resolvers.ts +++ b/packages/ecliptic-watcher/src/resolvers.ts @@ -5,11 +5,14 @@ import assert from 'assert'; import debug from 'debug'; import { GraphQLResolveInfo } from 'graphql'; +import { ExpressContext } from 'apollo-server-express'; +import winston from 'winston'; import { ValueResult, gqlTotalQueryCount, gqlQueryCount, + gqlQueryDuration, getResultState, IndexerInterface, GraphQLBigInt, @@ -23,11 +26,57 @@ import { Indexer } from './indexer'; const log = debug('vulcanize:resolver'); -export const createResolvers = async (indexerArg: IndexerInterface, eventWatcher: EventWatcher): Promise => { +const executeAndRecordMetrics = async ( + indexer: Indexer, + gqlLogger: winston.Logger, + opName: string, + expressContext: ExpressContext, + operation: () => Promise +) => { + gqlTotalQueryCount.inc(1); + gqlQueryCount.labels(opName).inc(1); + const endTimer = gqlQueryDuration.labels(opName).startTimer(); + + try { + const [result, syncStatus] = await Promise.all([ + operation(), + indexer.getSyncStatus() + ]); + + gqlLogger.info({ + opName, + query: expressContext.req.body.query, + variables: expressContext.req.body.variables, + latestIndexedBlockNumber: syncStatus?.latestIndexedBlockNumber, + urlPath: expressContext.req.path, + apiKey: expressContext.req.header('x-api-key'), + origin: expressContext.req.headers.origin + }); + return result; + } catch (error) { + gqlLogger.error({ + opName, + error, + query: expressContext.req.body.query, + variables: expressContext.req.body.variables, + urlPath: expressContext.req.path, + apiKey: expressContext.req.header('x-api-key'), + origin: expressContext.req.headers.origin + }); + } finally { + endTimer(); + } +}; + +export const createResolvers = async ( + indexerArg: IndexerInterface, + eventWatcher: EventWatcher, + gqlLogger: winston.Logger +): Promise => { const indexer = indexerArg as Indexer; // eslint-disable-next-line @typescript-eslint/no-unused-vars - const gqlCacheConfig = indexer.serverConfig.gqlCache; + const gqlCacheConfig = indexer.serverConfig.gql.cache; return { BigInt: GraphQLBigInt, @@ -62,259 +111,351 @@ export const createResolvers = async (indexerArg: IndexerInterface, eventWatcher _: any, { blockHash, contractAddress, _interfaceId }: { blockHash: string, contractAddress: string, _interfaceId: string }, // eslint-disable-next-line @typescript-eslint/no-unused-vars - __: any, + expressContext: ExpressContext, // eslint-disable-next-line @typescript-eslint/no-unused-vars info: GraphQLResolveInfo ): Promise => { log('supportsInterface', blockHash, contractAddress, _interfaceId); - gqlTotalQueryCount.inc(1); - gqlQueryCount.labels('supportsInterface').inc(1); // Set cache-control hints // setGQLCacheHints(info, {}, gqlCacheConfig); - return indexer.supportsInterface(blockHash, contractAddress, _interfaceId); + return executeAndRecordMetrics( + indexer, + gqlLogger, + 'supportsInterface', + expressContext, + async () => indexer.supportsInterface(blockHash, contractAddress, _interfaceId) + ); }, name: ( _: any, { blockHash, contractAddress }: { blockHash: string, contractAddress: string }, // eslint-disable-next-line @typescript-eslint/no-unused-vars - __: any, + expressContext: ExpressContext, // eslint-disable-next-line @typescript-eslint/no-unused-vars info: GraphQLResolveInfo ): Promise => { log('name', blockHash, contractAddress); - gqlTotalQueryCount.inc(1); - gqlQueryCount.labels('name').inc(1); // Set cache-control hints // setGQLCacheHints(info, {}, gqlCacheConfig); - return indexer.name(blockHash, contractAddress); + return executeAndRecordMetrics( + indexer, + gqlLogger, + 'name', + expressContext, + async () => indexer.name(blockHash, contractAddress) + ); }, symbol: ( _: any, { blockHash, contractAddress }: { blockHash: string, contractAddress: string }, // eslint-disable-next-line @typescript-eslint/no-unused-vars - __: any, + expressContext: ExpressContext, // eslint-disable-next-line @typescript-eslint/no-unused-vars info: GraphQLResolveInfo ): Promise => { log('symbol', blockHash, contractAddress); - gqlTotalQueryCount.inc(1); - gqlQueryCount.labels('symbol').inc(1); // Set cache-control hints // setGQLCacheHints(info, {}, gqlCacheConfig); - return indexer.symbol(blockHash, contractAddress); + return executeAndRecordMetrics( + indexer, + gqlLogger, + 'symbol', + expressContext, + async () => indexer.symbol(blockHash, contractAddress) + ); }, tokenURI: ( _: any, { blockHash, contractAddress, _tokenId }: { blockHash: string, contractAddress: string, _tokenId: bigint }, // eslint-disable-next-line @typescript-eslint/no-unused-vars - __: any, + expressContext: ExpressContext, // eslint-disable-next-line @typescript-eslint/no-unused-vars info: GraphQLResolveInfo ): Promise => { log('tokenURI', blockHash, contractAddress, _tokenId); - gqlTotalQueryCount.inc(1); - gqlQueryCount.labels('tokenURI').inc(1); // Set cache-control hints // setGQLCacheHints(info, {}, gqlCacheConfig); - return indexer.tokenURI(blockHash, contractAddress, _tokenId); + return executeAndRecordMetrics( + indexer, + gqlLogger, + 'tokenURI', + expressContext, + async () => indexer.tokenURI(blockHash, contractAddress, _tokenId) + ); }, balanceOf: ( _: any, { blockHash, contractAddress, _owner }: { blockHash: string, contractAddress: string, _owner: string }, // eslint-disable-next-line @typescript-eslint/no-unused-vars - __: any, + expressContext: ExpressContext, // eslint-disable-next-line @typescript-eslint/no-unused-vars info: GraphQLResolveInfo ): Promise => { log('balanceOf', blockHash, contractAddress, _owner); - gqlTotalQueryCount.inc(1); - gqlQueryCount.labels('balanceOf').inc(1); // Set cache-control hints // setGQLCacheHints(info, {}, gqlCacheConfig); - return indexer.balanceOf(blockHash, contractAddress, _owner); + return executeAndRecordMetrics( + indexer, + gqlLogger, + 'balanceOf', + expressContext, + async () => indexer.balanceOf(blockHash, contractAddress, _owner) + ); }, ownerOf: ( _: any, { blockHash, contractAddress, _tokenId }: { blockHash: string, contractAddress: string, _tokenId: bigint }, // eslint-disable-next-line @typescript-eslint/no-unused-vars - __: any, + expressContext: ExpressContext, // eslint-disable-next-line @typescript-eslint/no-unused-vars info: GraphQLResolveInfo ): Promise => { log('ownerOf', blockHash, contractAddress, _tokenId); - gqlTotalQueryCount.inc(1); - gqlQueryCount.labels('ownerOf').inc(1); // Set cache-control hints // setGQLCacheHints(info, {}, gqlCacheConfig); - return indexer.ownerOf(blockHash, contractAddress, _tokenId); + return executeAndRecordMetrics( + indexer, + gqlLogger, + 'ownerOf', + expressContext, + async () => indexer.ownerOf(blockHash, contractAddress, _tokenId) + ); }, exists: ( _: any, { blockHash, contractAddress, _tokenId }: { blockHash: string, contractAddress: string, _tokenId: bigint }, // eslint-disable-next-line @typescript-eslint/no-unused-vars - __: any, + expressContext: ExpressContext, // eslint-disable-next-line @typescript-eslint/no-unused-vars info: GraphQLResolveInfo ): Promise => { log('exists', blockHash, contractAddress, _tokenId); - gqlTotalQueryCount.inc(1); - gqlQueryCount.labels('exists').inc(1); // Set cache-control hints // setGQLCacheHints(info, {}, gqlCacheConfig); - return indexer.exists(blockHash, contractAddress, _tokenId); + return executeAndRecordMetrics( + indexer, + gqlLogger, + 'exists', + expressContext, + async () => indexer.exists(blockHash, contractAddress, _tokenId) + ); }, getApproved: ( _: any, { blockHash, contractAddress, _tokenId }: { blockHash: string, contractAddress: string, _tokenId: bigint }, // eslint-disable-next-line @typescript-eslint/no-unused-vars - __: any, + expressContext: ExpressContext, // eslint-disable-next-line @typescript-eslint/no-unused-vars info: GraphQLResolveInfo ): Promise => { log('getApproved', blockHash, contractAddress, _tokenId); - gqlTotalQueryCount.inc(1); - gqlQueryCount.labels('getApproved').inc(1); // Set cache-control hints // setGQLCacheHints(info, {}, gqlCacheConfig); - return indexer.getApproved(blockHash, contractAddress, _tokenId); + return executeAndRecordMetrics( + indexer, + gqlLogger, + 'getApproved', + expressContext, + async () => indexer.getApproved(blockHash, contractAddress, _tokenId) + ); }, isApprovedForAll: ( _: any, { blockHash, contractAddress, _owner, _operator }: { blockHash: string, contractAddress: string, _owner: string, _operator: string }, // eslint-disable-next-line @typescript-eslint/no-unused-vars - __: any, + expressContext: ExpressContext, // eslint-disable-next-line @typescript-eslint/no-unused-vars info: GraphQLResolveInfo ): Promise => { log('isApprovedForAll', blockHash, contractAddress, _owner, _operator); - gqlTotalQueryCount.inc(1); - gqlQueryCount.labels('isApprovedForAll').inc(1); // Set cache-control hints // setGQLCacheHints(info, {}, gqlCacheConfig); - return indexer.isApprovedForAll(blockHash, contractAddress, _owner, _operator); + return executeAndRecordMetrics( + indexer, + gqlLogger, + 'isApprovedForAll', + expressContext, + async () => indexer.isApprovedForAll(blockHash, contractAddress, _owner, _operator) + ); }, getSpawnLimit: ( _: any, { blockHash, contractAddress, _point, _time }: { blockHash: string, contractAddress: string, _point: bigint, _time: bigint }, // eslint-disable-next-line @typescript-eslint/no-unused-vars - __: any, + expressContext: ExpressContext, // eslint-disable-next-line @typescript-eslint/no-unused-vars info: GraphQLResolveInfo ): Promise => { log('getSpawnLimit', blockHash, contractAddress, _point, _time); - gqlTotalQueryCount.inc(1); - gqlQueryCount.labels('getSpawnLimit').inc(1); // Set cache-control hints // setGQLCacheHints(info, {}, gqlCacheConfig); - return indexer.getSpawnLimit(blockHash, contractAddress, _point, _time); + return executeAndRecordMetrics( + indexer, + gqlLogger, + 'getSpawnLimit', + expressContext, + async () => indexer.getSpawnLimit(blockHash, contractAddress, _point, _time) + ); }, canEscapeTo: ( _: any, { blockHash, contractAddress, _point, _sponsor }: { blockHash: string, contractAddress: string, _point: bigint, _sponsor: bigint }, // eslint-disable-next-line @typescript-eslint/no-unused-vars - __: any, + expressContext: ExpressContext, // eslint-disable-next-line @typescript-eslint/no-unused-vars info: GraphQLResolveInfo ): Promise => { log('canEscapeTo', blockHash, contractAddress, _point, _sponsor); - gqlTotalQueryCount.inc(1); - gqlQueryCount.labels('canEscapeTo').inc(1); // Set cache-control hints // setGQLCacheHints(info, {}, gqlCacheConfig); - return indexer.canEscapeTo(blockHash, contractAddress, _point, _sponsor); + return executeAndRecordMetrics( + indexer, + gqlLogger, + 'canEscapeTo', + expressContext, + async () => indexer.canEscapeTo(blockHash, contractAddress, _point, _sponsor) + ); }, - events: async (_: any, { blockHash, contractAddress, name }: { blockHash: string, contractAddress: string, name?: string }) => { + events: async ( + _: any, + { blockHash, contractAddress, name }: { blockHash: string, contractAddress: string, name?: string }, + expressContext: ExpressContext + ) => { log('events', blockHash, contractAddress, name); - gqlTotalQueryCount.inc(1); - gqlQueryCount.labels('events').inc(1); - const block = await indexer.getBlockProgress(blockHash); - if (!block || !block.isComplete) { - throw new Error(`Block hash ${blockHash} number ${block?.blockNumber} not processed yet`); - } + return executeAndRecordMetrics( + indexer, + gqlLogger, + 'events', + expressContext, + async () => { + const block = await indexer.getBlockProgress(blockHash); + if (!block || !block.isComplete) { + throw new Error(`Block hash ${blockHash} number ${block?.blockNumber} not processed yet`); + } - const events = await indexer.getEventsByFilter(blockHash, contractAddress, name); - return events.map(event => indexer.getResultEvent(event)); + const events = await indexer.getEventsByFilter(blockHash, contractAddress, name); + return events.map(event => indexer.getResultEvent(event)); + } + ); }, - eventsInRange: async (_: any, { fromBlockNumber, toBlockNumber }: { fromBlockNumber: number, toBlockNumber: number }) => { + eventsInRange: async ( + _: any, + { fromBlockNumber, toBlockNumber }: { fromBlockNumber: number, toBlockNumber: number }, + expressContext: ExpressContext + ) => { log('eventsInRange', fromBlockNumber, toBlockNumber); - gqlTotalQueryCount.inc(1); - gqlQueryCount.labels('eventsInRange').inc(1); - const syncStatus = await indexer.getSyncStatus(); + return executeAndRecordMetrics( + indexer, + gqlLogger, + 'eventsInRange', + expressContext, + async () => { + const syncStatus = await indexer.getSyncStatus(); - if (!syncStatus) { - throw new Error('No blocks processed yet'); - } + if (!syncStatus) { + throw new Error('No blocks processed yet'); + } - if ((fromBlockNumber < syncStatus.initialIndexedBlockNumber) || (toBlockNumber > syncStatus.latestProcessedBlockNumber)) { - throw new Error(`Block range should be between ${syncStatus.initialIndexedBlockNumber} and ${syncStatus.latestProcessedBlockNumber}`); - } + if ((fromBlockNumber < syncStatus.initialIndexedBlockNumber) || (toBlockNumber > syncStatus.latestProcessedBlockNumber)) { + throw new Error(`Block range should be between ${syncStatus.initialIndexedBlockNumber} and ${syncStatus.latestProcessedBlockNumber}`); + } - const events = await indexer.getEventsInRange(fromBlockNumber, toBlockNumber); - return events.map(event => indexer.getResultEvent(event)); + const events = await indexer.getEventsInRange(fromBlockNumber, toBlockNumber); + return events.map(event => indexer.getResultEvent(event)); + } + ); }, - getStateByCID: async (_: any, { cid }: { cid: string }) => { + getStateByCID: async ( + _: any, + { cid }: { cid: string }, + expressContext: ExpressContext + ) => { log('getStateByCID', cid); - gqlTotalQueryCount.inc(1); - gqlQueryCount.labels('getStateByCID').inc(1); - const state = await indexer.getStateByCID(cid); + return executeAndRecordMetrics( + indexer, + gqlLogger, + 'getStateByCID', + expressContext, + async () => { + const state = await indexer.getStateByCID(cid); - return state && state.block.isComplete ? getResultState(state) : undefined; + return state && state.block.isComplete ? getResultState(state) : undefined; + } + ); }, - getState: async (_: any, { blockHash, contractAddress, kind }: { blockHash: string, contractAddress: string, kind: string }) => { + getState: async ( + _: any, + { blockHash, contractAddress, kind }: { blockHash: string, contractAddress: string, kind: string }, + expressContext: ExpressContext + ) => { log('getState', blockHash, contractAddress, kind); - gqlTotalQueryCount.inc(1); - gqlQueryCount.labels('getState').inc(1); - const state = await indexer.getPrevState(blockHash, contractAddress, kind); + return executeAndRecordMetrics( + indexer, + gqlLogger, + 'getState', + expressContext, + async () => { + const state = await indexer.getPrevState(blockHash, contractAddress, kind); - return state && state.block.isComplete ? getResultState(state) : undefined; + return state && state.block.isComplete ? getResultState(state) : undefined; + } + ); }, - getSyncStatus: async () => { + getSyncStatus: async ( + _: any, + __: Record, + expressContext: ExpressContext + ) => { log('getSyncStatus'); - gqlTotalQueryCount.inc(1); - gqlQueryCount.labels('getSyncStatus').inc(1); - return indexer.getSyncStatus(); + return executeAndRecordMetrics( + indexer, + gqlLogger, + 'getSyncStatus', + expressContext, + async () => indexer.getSyncStatus() + ); } } }; diff --git a/packages/gateway-server/package.json b/packages/gateway-server/package.json index f44a564..3fbbbaf 100644 --- a/packages/gateway-server/package.json +++ b/packages/gateway-server/package.json @@ -1,6 +1,6 @@ { "name": "@cerc-io/gateway-server", - "version": "0.1.5", + "version": "0.1.6", "main": "index.js", "license": "AGPL-3.0", "private": true, diff --git a/packages/linear-star-release-watcher/.gitignore b/packages/linear-star-release-watcher/.gitignore index 0b4cc3f..549d70b 100644 --- a/packages/linear-star-release-watcher/.gitignore +++ b/packages/linear-star-release-watcher/.gitignore @@ -4,3 +4,5 @@ out/ .vscode .idea + +gql-logs/ diff --git a/packages/linear-star-release-watcher/README.md b/packages/linear-star-release-watcher/README.md index 89d162c..3521c2b 100644 --- a/packages/linear-star-release-watcher/README.md +++ b/packages/linear-star-release-watcher/README.md @@ -63,7 +63,7 @@ To enable GQL requests caching: -* Update the `server.gqlCache` config with required settings. +* Update the `server.gql.cache` config with required settings. * In the GQL [schema file](./src/schema.gql), use the `cacheControl` directive to apply cache hints at schema level. diff --git a/packages/linear-star-release-watcher/environments/local.toml b/packages/linear-star-release-watcher/environments/local.toml index c104bed..d52f618 100644 --- a/packages/linear-star-release-watcher/environments/local.toml +++ b/packages/linear-star-release-watcher/environments/local.toml @@ -2,7 +2,6 @@ host = "127.0.0.1" port = 3007 kind = "active" - gqlPath = "/graphql" # Checkpointing state. checkpointing = true @@ -14,22 +13,29 @@ # CAUTION: Disable only if state creation is not desired or can be filled subsequently enableState = false - # Max block range for which to return events in eventsInRange GQL query. - # Use -1 for skipping check on block range. - maxEventsBlockRange = 1000 - # Flag to specify whether RPC endpoint supports block hash as block tag parameter rpcSupportsBlockHashParam = false - # GQL cache settings - [server.gqlCache] - enabled = true + # Server GQL config + [server.gql] + path = "/graphql" - # Max in-memory cache size (in bytes) (default 8 MB) - # maxCacheSize + # Max block range for which to return events in eventsInRange GQL query. + # Use -1 for skipping check on block range. + maxEventsBlockRange = 1000 - # GQL cache-control max-age settings (in seconds) - maxAge = 15 + # Log directory for GQL requests + logDir = "./gql-logs" + + # GQL cache settings + [server.gql.cache] + enabled = true + + # Max in-memory cache size (in bytes) (default 8 MB) + # maxCacheSize + + # GQL cache-control max-age settings (in seconds) + maxAge = 15 [metrics] host = "127.0.0.1" diff --git a/packages/linear-star-release-watcher/package.json b/packages/linear-star-release-watcher/package.json index f8a1f68..6c2e257 100644 --- a/packages/linear-star-release-watcher/package.json +++ b/packages/linear-star-release-watcher/package.json @@ -1,6 +1,6 @@ { "name": "@cerc-io/linear-star-release-watcher", - "version": "0.1.5", + "version": "0.1.6", "description": "linear-star-release-watcher", "private": true, "main": "dist/index.js", @@ -28,7 +28,8 @@ }, "repository": { "type": "git", - "url": "git+https://github.com/cerc-io/watcher-ts.git" + "url": "https://github.com/cerc-io/azimuth-watcher-ts", + "directory": "packages/linear-star-release-watcher" }, "author": "", "license": "AGPL-3.0", @@ -38,10 +39,10 @@ "homepage": "https://github.com/cerc-io/watcher-ts#readme", "dependencies": { "@apollo/client": "^3.3.19", - "@cerc-io/cli": "^0.2.92", - "@cerc-io/ipld-eth-client": "^0.2.92", - "@cerc-io/solidity-mapper": "^0.2.92", - "@cerc-io/util": "^0.2.92", + "@cerc-io/cli": "^0.2.94", + "@cerc-io/ipld-eth-client": "^0.2.94", + "@cerc-io/solidity-mapper": "^0.2.94", + "@cerc-io/util": "^0.2.94", "@ethersproject/providers": "^5.4.4", "debug": "^4.3.1", "decimal.js": "^10.3.1", @@ -69,6 +70,7 @@ "eslint-plugin-standard": "^5.0.0", "husky": "^7.0.2", "ts-node": "^10.2.1", - "typescript": "^5.0.2" + "typescript": "^5.0.2", + "winston": "^3.13.0" } } diff --git a/packages/linear-star-release-watcher/src/indexer.ts b/packages/linear-star-release-watcher/src/indexer.ts index 06151a2..934dc5c 100644 --- a/packages/linear-star-release-watcher/src/indexer.ts +++ b/packages/linear-star-release-watcher/src/indexer.ts @@ -480,7 +480,7 @@ export class Indexer implements IndexerInterface { } async getEventsInRange (fromBlockNumber: number, toBlockNumber: number): Promise> { - return this._baseIndexer.getEventsInRange(fromBlockNumber, toBlockNumber, this._serverConfig.maxEventsBlockRange); + return this._baseIndexer.getEventsInRange(fromBlockNumber, toBlockNumber, this._serverConfig.gql.maxEventsBlockRange); } async getSyncStatus (): Promise { diff --git a/packages/linear-star-release-watcher/src/resolvers.ts b/packages/linear-star-release-watcher/src/resolvers.ts index 7ae209c..5e6c76d 100644 --- a/packages/linear-star-release-watcher/src/resolvers.ts +++ b/packages/linear-star-release-watcher/src/resolvers.ts @@ -5,11 +5,14 @@ import assert from 'assert'; import debug from 'debug'; import { GraphQLResolveInfo } from 'graphql'; +import { ExpressContext } from 'apollo-server-express'; +import winston from 'winston'; import { ValueResult, gqlTotalQueryCount, gqlQueryCount, + gqlQueryDuration, getResultState, IndexerInterface, GraphQLBigInt, @@ -23,11 +26,57 @@ import { Indexer } from './indexer'; const log = debug('vulcanize:resolver'); -export const createResolvers = async (indexerArg: IndexerInterface, eventWatcher: EventWatcher): Promise => { +const executeAndRecordMetrics = async ( + indexer: Indexer, + gqlLogger: winston.Logger, + opName: string, + expressContext: ExpressContext, + operation: () => Promise +) => { + gqlTotalQueryCount.inc(1); + gqlQueryCount.labels(opName).inc(1); + const endTimer = gqlQueryDuration.labels(opName).startTimer(); + + try { + const [result, syncStatus] = await Promise.all([ + operation(), + indexer.getSyncStatus() + ]); + + gqlLogger.info({ + opName, + query: expressContext.req.body.query, + variables: expressContext.req.body.variables, + latestIndexedBlockNumber: syncStatus?.latestIndexedBlockNumber, + urlPath: expressContext.req.path, + apiKey: expressContext.req.header('x-api-key'), + origin: expressContext.req.headers.origin + }); + return result; + } catch (error) { + gqlLogger.error({ + opName, + error, + query: expressContext.req.body.query, + variables: expressContext.req.body.variables, + urlPath: expressContext.req.path, + apiKey: expressContext.req.header('x-api-key'), + origin: expressContext.req.headers.origin + }); + } finally { + endTimer(); + } +}; + +export const createResolvers = async ( + indexerArg: IndexerInterface, + eventWatcher: EventWatcher, + gqlLogger: winston.Logger +): Promise => { const indexer = indexerArg as Indexer; // eslint-disable-next-line @typescript-eslint/no-unused-vars - const gqlCacheConfig = indexer.serverConfig.gqlCache; + const gqlCacheConfig = indexer.serverConfig.gql.cache; return { BigInt: GraphQLBigInt, @@ -62,115 +111,175 @@ export const createResolvers = async (indexerArg: IndexerInterface, eventWatcher _: any, { blockHash, contractAddress, _participant }: { blockHash: string, contractAddress: string, _participant: string }, // eslint-disable-next-line @typescript-eslint/no-unused-vars - __: any, + expressContext: ExpressContext, // eslint-disable-next-line @typescript-eslint/no-unused-vars info: GraphQLResolveInfo ): Promise => { log('withdrawLimit', blockHash, contractAddress, _participant); - gqlTotalQueryCount.inc(1); - gqlQueryCount.labels('withdrawLimit').inc(1); // Set cache-control hints // setGQLCacheHints(info, {}, gqlCacheConfig); - return indexer.withdrawLimit(blockHash, contractAddress, _participant); + return executeAndRecordMetrics( + indexer, + gqlLogger, + 'withdrawLimit', + expressContext, + async () => indexer.withdrawLimit(blockHash, contractAddress, _participant) + ); }, verifyBalance: ( _: any, { blockHash, contractAddress, _participant }: { blockHash: string, contractAddress: string, _participant: string }, // eslint-disable-next-line @typescript-eslint/no-unused-vars - __: any, + expressContext: ExpressContext, // eslint-disable-next-line @typescript-eslint/no-unused-vars info: GraphQLResolveInfo ): Promise => { log('verifyBalance', blockHash, contractAddress, _participant); - gqlTotalQueryCount.inc(1); - gqlQueryCount.labels('verifyBalance').inc(1); // Set cache-control hints // setGQLCacheHints(info, {}, gqlCacheConfig); - return indexer.verifyBalance(blockHash, contractAddress, _participant); + return executeAndRecordMetrics( + indexer, + gqlLogger, + 'verifyBalance', + expressContext, + async () => indexer.verifyBalance(blockHash, contractAddress, _participant) + ); }, getRemainingStars: ( _: any, { blockHash, contractAddress, _participant }: { blockHash: string, contractAddress: string, _participant: string }, // eslint-disable-next-line @typescript-eslint/no-unused-vars - __: any, + expressContext: ExpressContext, // eslint-disable-next-line @typescript-eslint/no-unused-vars info: GraphQLResolveInfo ): Promise => { log('getRemainingStars', blockHash, contractAddress, _participant); - gqlTotalQueryCount.inc(1); - gqlQueryCount.labels('getRemainingStars').inc(1); // Set cache-control hints // setGQLCacheHints(info, {}, gqlCacheConfig); - return indexer.getRemainingStars(blockHash, contractAddress, _participant); + return executeAndRecordMetrics( + indexer, + gqlLogger, + 'getRemainingStars', + expressContext, + async () => indexer.getRemainingStars(blockHash, contractAddress, _participant) + ); }, - events: async (_: any, { blockHash, contractAddress, name }: { blockHash: string, contractAddress: string, name?: string }) => { + events: async ( + _: any, + { blockHash, contractAddress, name }: { blockHash: string, contractAddress: string, name?: string }, + expressContext: ExpressContext + ) => { log('events', blockHash, contractAddress, name); - gqlTotalQueryCount.inc(1); - gqlQueryCount.labels('events').inc(1); - const block = await indexer.getBlockProgress(blockHash); - if (!block || !block.isComplete) { - throw new Error(`Block hash ${blockHash} number ${block?.blockNumber} not processed yet`); - } + return executeAndRecordMetrics( + indexer, + gqlLogger, + 'events', + expressContext, + async () => { + const block = await indexer.getBlockProgress(blockHash); + if (!block || !block.isComplete) { + throw new Error(`Block hash ${blockHash} number ${block?.blockNumber} not processed yet`); + } - const events = await indexer.getEventsByFilter(blockHash, contractAddress, name); - return events.map(event => indexer.getResultEvent(event)); + const events = await indexer.getEventsByFilter(blockHash, contractAddress, name); + return events.map(event => indexer.getResultEvent(event)); + } + ); }, - eventsInRange: async (_: any, { fromBlockNumber, toBlockNumber }: { fromBlockNumber: number, toBlockNumber: number }) => { + eventsInRange: async ( + _: any, + { fromBlockNumber, toBlockNumber }: { fromBlockNumber: number, toBlockNumber: number }, + expressContext: ExpressContext + ) => { log('eventsInRange', fromBlockNumber, toBlockNumber); - gqlTotalQueryCount.inc(1); - gqlQueryCount.labels('eventsInRange').inc(1); - const syncStatus = await indexer.getSyncStatus(); + return executeAndRecordMetrics( + indexer, + gqlLogger, + 'eventsInRange', + expressContext, + async () => { + const syncStatus = await indexer.getSyncStatus(); - if (!syncStatus) { - throw new Error('No blocks processed yet'); - } + if (!syncStatus) { + throw new Error('No blocks processed yet'); + } - if ((fromBlockNumber < syncStatus.initialIndexedBlockNumber) || (toBlockNumber > syncStatus.latestProcessedBlockNumber)) { - throw new Error(`Block range should be between ${syncStatus.initialIndexedBlockNumber} and ${syncStatus.latestProcessedBlockNumber}`); - } + if ((fromBlockNumber < syncStatus.initialIndexedBlockNumber) || (toBlockNumber > syncStatus.latestProcessedBlockNumber)) { + throw new Error(`Block range should be between ${syncStatus.initialIndexedBlockNumber} and ${syncStatus.latestProcessedBlockNumber}`); + } - const events = await indexer.getEventsInRange(fromBlockNumber, toBlockNumber); - return events.map(event => indexer.getResultEvent(event)); + const events = await indexer.getEventsInRange(fromBlockNumber, toBlockNumber); + return events.map(event => indexer.getResultEvent(event)); + } + ); }, - getStateByCID: async (_: any, { cid }: { cid: string }) => { + getStateByCID: async ( + _: any, + { cid }: { cid: string }, + expressContext: ExpressContext + ) => { log('getStateByCID', cid); - gqlTotalQueryCount.inc(1); - gqlQueryCount.labels('getStateByCID').inc(1); - const state = await indexer.getStateByCID(cid); + return executeAndRecordMetrics( + indexer, + gqlLogger, + 'getStateByCID', + expressContext, + async () => { + const state = await indexer.getStateByCID(cid); - return state && state.block.isComplete ? getResultState(state) : undefined; + return state && state.block.isComplete ? getResultState(state) : undefined; + } + ); }, - getState: async (_: any, { blockHash, contractAddress, kind }: { blockHash: string, contractAddress: string, kind: string }) => { + getState: async ( + _: any, + { blockHash, contractAddress, kind }: { blockHash: string, contractAddress: string, kind: string }, + expressContext: ExpressContext + ) => { log('getState', blockHash, contractAddress, kind); - gqlTotalQueryCount.inc(1); - gqlQueryCount.labels('getState').inc(1); - const state = await indexer.getPrevState(blockHash, contractAddress, kind); + return executeAndRecordMetrics( + indexer, + gqlLogger, + 'getState', + expressContext, + async () => { + const state = await indexer.getPrevState(blockHash, contractAddress, kind); - return state && state.block.isComplete ? getResultState(state) : undefined; + return state && state.block.isComplete ? getResultState(state) : undefined; + } + ); }, - getSyncStatus: async () => { + getSyncStatus: async ( + _: any, + __: Record, + expressContext: ExpressContext + ) => { log('getSyncStatus'); - gqlTotalQueryCount.inc(1); - gqlQueryCount.labels('getSyncStatus').inc(1); - return indexer.getSyncStatus(); + return executeAndRecordMetrics( + indexer, + gqlLogger, + 'getSyncStatus', + expressContext, + async () => indexer.getSyncStatus() + ); } } }; diff --git a/packages/polls-watcher/.gitignore b/packages/polls-watcher/.gitignore index 0b4cc3f..549d70b 100644 --- a/packages/polls-watcher/.gitignore +++ b/packages/polls-watcher/.gitignore @@ -4,3 +4,5 @@ out/ .vscode .idea + +gql-logs/ diff --git a/packages/polls-watcher/README.md b/packages/polls-watcher/README.md index 8b06932..39e8d1d 100644 --- a/packages/polls-watcher/README.md +++ b/packages/polls-watcher/README.md @@ -63,7 +63,7 @@ To enable GQL requests caching: -* Update the `server.gqlCache` config with required settings. +* Update the `server.gql.cache` config with required settings. * In the GQL [schema file](./src/schema.gql), use the `cacheControl` directive to apply cache hints at schema level. diff --git a/packages/polls-watcher/environments/local.toml b/packages/polls-watcher/environments/local.toml index 4c43448..ccc484d 100644 --- a/packages/polls-watcher/environments/local.toml +++ b/packages/polls-watcher/environments/local.toml @@ -2,7 +2,6 @@ host = "127.0.0.1" port = 3008 kind = "active" - gqlPath = "/graphql" # Checkpointing state. checkpointing = true @@ -14,22 +13,29 @@ # CAUTION: Disable only if state creation is not desired or can be filled subsequently enableState = false - # Max block range for which to return events in eventsInRange GQL query. - # Use -1 for skipping check on block range. - maxEventsBlockRange = 1000 - # Flag to specify whether RPC endpoint supports block hash as block tag parameter rpcSupportsBlockHashParam = false - # GQL cache settings - [server.gqlCache] - enabled = true + # Server GQL config + [server.gql] + path = "/graphql" - # Max in-memory cache size (in bytes) (default 8 MB) - # maxCacheSize + # Max block range for which to return events in eventsInRange GQL query. + # Use -1 for skipping check on block range. + maxEventsBlockRange = 1000 - # GQL cache-control max-age settings (in seconds) - maxAge = 15 + # Log directory for GQL requests + logDir = "./gql-logs" + + # GQL cache settings + [server.gql.cache] + enabled = true + + # Max in-memory cache size (in bytes) (default 8 MB) + # maxCacheSize + + # GQL cache-control max-age settings (in seconds) + maxAge = 15 [metrics] host = "127.0.0.1" diff --git a/packages/polls-watcher/package.json b/packages/polls-watcher/package.json index b265caf..d25fc02 100644 --- a/packages/polls-watcher/package.json +++ b/packages/polls-watcher/package.json @@ -1,6 +1,6 @@ { "name": "@cerc-io/polls-watcher", - "version": "0.1.5", + "version": "0.1.6", "description": "polls-watcher", "private": true, "main": "dist/index.js", @@ -28,7 +28,8 @@ }, "repository": { "type": "git", - "url": "git+https://github.com/cerc-io/watcher-ts.git" + "url": "https://github.com/cerc-io/azimuth-watcher-ts", + "directory": "packages/polls-watcher" }, "author": "", "license": "AGPL-3.0", @@ -38,10 +39,10 @@ "homepage": "https://github.com/cerc-io/watcher-ts#readme", "dependencies": { "@apollo/client": "^3.3.19", - "@cerc-io/cli": "^0.2.92", - "@cerc-io/ipld-eth-client": "^0.2.92", - "@cerc-io/solidity-mapper": "^0.2.92", - "@cerc-io/util": "^0.2.92", + "@cerc-io/cli": "^0.2.94", + "@cerc-io/ipld-eth-client": "^0.2.94", + "@cerc-io/solidity-mapper": "^0.2.94", + "@cerc-io/util": "^0.2.94", "@ethersproject/providers": "^5.4.4", "debug": "^4.3.1", "decimal.js": "^10.3.1", @@ -69,6 +70,7 @@ "eslint-plugin-standard": "^5.0.0", "husky": "^7.0.2", "ts-node": "^10.2.1", - "typescript": "^5.0.2" + "typescript": "^5.0.2", + "winston": "^3.13.0" } } diff --git a/packages/polls-watcher/src/indexer.ts b/packages/polls-watcher/src/indexer.ts index 81ab658..d40c837 100644 --- a/packages/polls-watcher/src/indexer.ts +++ b/packages/polls-watcher/src/indexer.ts @@ -602,7 +602,7 @@ export class Indexer implements IndexerInterface { } async getEventsInRange (fromBlockNumber: number, toBlockNumber: number): Promise> { - return this._baseIndexer.getEventsInRange(fromBlockNumber, toBlockNumber, this._serverConfig.maxEventsBlockRange); + return this._baseIndexer.getEventsInRange(fromBlockNumber, toBlockNumber, this._serverConfig.gql.maxEventsBlockRange); } async getSyncStatus (): Promise { diff --git a/packages/polls-watcher/src/resolvers.ts b/packages/polls-watcher/src/resolvers.ts index 62884b7..d86c7ae 100644 --- a/packages/polls-watcher/src/resolvers.ts +++ b/packages/polls-watcher/src/resolvers.ts @@ -5,11 +5,14 @@ import assert from 'assert'; import debug from 'debug'; import { GraphQLResolveInfo } from 'graphql'; +import { ExpressContext } from 'apollo-server-express'; +import winston from 'winston'; import { ValueResult, gqlTotalQueryCount, gqlQueryCount, + gqlQueryDuration, getResultState, IndexerInterface, GraphQLBigInt, @@ -23,11 +26,57 @@ import { Indexer } from './indexer'; const log = debug('vulcanize:resolver'); -export const createResolvers = async (indexerArg: IndexerInterface, eventWatcher: EventWatcher): Promise => { +const executeAndRecordMetrics = async ( + indexer: Indexer, + gqlLogger: winston.Logger, + opName: string, + expressContext: ExpressContext, + operation: () => Promise +) => { + gqlTotalQueryCount.inc(1); + gqlQueryCount.labels(opName).inc(1); + const endTimer = gqlQueryDuration.labels(opName).startTimer(); + + try { + const [result, syncStatus] = await Promise.all([ + operation(), + indexer.getSyncStatus() + ]); + + gqlLogger.info({ + opName, + query: expressContext.req.body.query, + variables: expressContext.req.body.variables, + latestIndexedBlockNumber: syncStatus?.latestIndexedBlockNumber, + urlPath: expressContext.req.path, + apiKey: expressContext.req.header('x-api-key'), + origin: expressContext.req.headers.origin + }); + return result; + } catch (error) { + gqlLogger.error({ + opName, + error, + query: expressContext.req.body.query, + variables: expressContext.req.body.variables, + urlPath: expressContext.req.path, + apiKey: expressContext.req.header('x-api-key'), + origin: expressContext.req.headers.origin + }); + } finally { + endTimer(); + } +}; + +export const createResolvers = async ( + indexerArg: IndexerInterface, + eventWatcher: EventWatcher, + gqlLogger: winston.Logger +): Promise => { const indexer = indexerArg as Indexer; // eslint-disable-next-line @typescript-eslint/no-unused-vars - const gqlCacheConfig = indexer.serverConfig.gqlCache; + const gqlCacheConfig = indexer.serverConfig.gql.cache; return { BigInt: GraphQLBigInt, @@ -62,187 +111,263 @@ export const createResolvers = async (indexerArg: IndexerInterface, eventWatcher _: any, { blockHash, contractAddress }: { blockHash: string, contractAddress: string }, // eslint-disable-next-line @typescript-eslint/no-unused-vars - __: any, + expressContext: ExpressContext, // eslint-disable-next-line @typescript-eslint/no-unused-vars info: GraphQLResolveInfo ): Promise => { log('getUpgradeProposals', blockHash, contractAddress); - gqlTotalQueryCount.inc(1); - gqlQueryCount.labels('getUpgradeProposals').inc(1); // Set cache-control hints // setGQLCacheHints(info, {}, gqlCacheConfig); - return indexer.getUpgradeProposals(blockHash, contractAddress); + return executeAndRecordMetrics( + indexer, + gqlLogger, + 'getUpgradeProposals', + expressContext, + async () => indexer.getUpgradeProposals(blockHash, contractAddress) + ); }, getUpgradeProposalCount: ( _: any, { blockHash, contractAddress }: { blockHash: string, contractAddress: string }, // eslint-disable-next-line @typescript-eslint/no-unused-vars - __: any, + expressContext: ExpressContext, // eslint-disable-next-line @typescript-eslint/no-unused-vars info: GraphQLResolveInfo ): Promise => { log('getUpgradeProposalCount', blockHash, contractAddress); - gqlTotalQueryCount.inc(1); - gqlQueryCount.labels('getUpgradeProposalCount').inc(1); // Set cache-control hints // setGQLCacheHints(info, {}, gqlCacheConfig); - return indexer.getUpgradeProposalCount(blockHash, contractAddress); + return executeAndRecordMetrics( + indexer, + gqlLogger, + 'getUpgradeProposalCount', + expressContext, + async () => indexer.getUpgradeProposalCount(blockHash, contractAddress) + ); }, getDocumentProposals: ( _: any, { blockHash, contractAddress }: { blockHash: string, contractAddress: string }, // eslint-disable-next-line @typescript-eslint/no-unused-vars - __: any, + expressContext: ExpressContext, // eslint-disable-next-line @typescript-eslint/no-unused-vars info: GraphQLResolveInfo ): Promise => { log('getDocumentProposals', blockHash, contractAddress); - gqlTotalQueryCount.inc(1); - gqlQueryCount.labels('getDocumentProposals').inc(1); // Set cache-control hints // setGQLCacheHints(info, {}, gqlCacheConfig); - return indexer.getDocumentProposals(blockHash, contractAddress); + return executeAndRecordMetrics( + indexer, + gqlLogger, + 'getDocumentProposals', + expressContext, + async () => indexer.getDocumentProposals(blockHash, contractAddress) + ); }, getDocumentProposalCount: ( _: any, { blockHash, contractAddress }: { blockHash: string, contractAddress: string }, // eslint-disable-next-line @typescript-eslint/no-unused-vars - __: any, + expressContext: ExpressContext, // eslint-disable-next-line @typescript-eslint/no-unused-vars info: GraphQLResolveInfo ): Promise => { log('getDocumentProposalCount', blockHash, contractAddress); - gqlTotalQueryCount.inc(1); - gqlQueryCount.labels('getDocumentProposalCount').inc(1); // Set cache-control hints // setGQLCacheHints(info, {}, gqlCacheConfig); - return indexer.getDocumentProposalCount(blockHash, contractAddress); + return executeAndRecordMetrics( + indexer, + gqlLogger, + 'getDocumentProposalCount', + expressContext, + async () => indexer.getDocumentProposalCount(blockHash, contractAddress) + ); }, getDocumentMajorities: ( _: any, { blockHash, contractAddress }: { blockHash: string, contractAddress: string }, // eslint-disable-next-line @typescript-eslint/no-unused-vars - __: any, + expressContext: ExpressContext, // eslint-disable-next-line @typescript-eslint/no-unused-vars info: GraphQLResolveInfo ): Promise => { log('getDocumentMajorities', blockHash, contractAddress); - gqlTotalQueryCount.inc(1); - gqlQueryCount.labels('getDocumentMajorities').inc(1); // Set cache-control hints // setGQLCacheHints(info, {}, gqlCacheConfig); - return indexer.getDocumentMajorities(blockHash, contractAddress); + return executeAndRecordMetrics( + indexer, + gqlLogger, + 'getDocumentMajorities', + expressContext, + async () => indexer.getDocumentMajorities(blockHash, contractAddress) + ); }, hasVotedOnUpgradePoll: ( _: any, { blockHash, contractAddress, _galaxy, _proposal }: { blockHash: string, contractAddress: string, _galaxy: number, _proposal: string }, // eslint-disable-next-line @typescript-eslint/no-unused-vars - __: any, + expressContext: ExpressContext, // eslint-disable-next-line @typescript-eslint/no-unused-vars info: GraphQLResolveInfo ): Promise => { log('hasVotedOnUpgradePoll', blockHash, contractAddress, _galaxy, _proposal); - gqlTotalQueryCount.inc(1); - gqlQueryCount.labels('hasVotedOnUpgradePoll').inc(1); // Set cache-control hints // setGQLCacheHints(info, {}, gqlCacheConfig); - return indexer.hasVotedOnUpgradePoll(blockHash, contractAddress, _galaxy, _proposal); + return executeAndRecordMetrics( + indexer, + gqlLogger, + 'hasVotedOnUpgradePoll', + expressContext, + async () => indexer.hasVotedOnUpgradePoll(blockHash, contractAddress, _galaxy, _proposal) + ); }, hasVotedOnDocumentPoll: ( _: any, { blockHash, contractAddress, _galaxy, _proposal }: { blockHash: string, contractAddress: string, _galaxy: number, _proposal: string }, // eslint-disable-next-line @typescript-eslint/no-unused-vars - __: any, + expressContext: ExpressContext, // eslint-disable-next-line @typescript-eslint/no-unused-vars info: GraphQLResolveInfo ): Promise => { log('hasVotedOnDocumentPoll', blockHash, contractAddress, _galaxy, _proposal); - gqlTotalQueryCount.inc(1); - gqlQueryCount.labels('hasVotedOnDocumentPoll').inc(1); // Set cache-control hints // setGQLCacheHints(info, {}, gqlCacheConfig); - return indexer.hasVotedOnDocumentPoll(blockHash, contractAddress, _galaxy, _proposal); + return executeAndRecordMetrics( + indexer, + gqlLogger, + 'hasVotedOnDocumentPoll', + expressContext, + async () => indexer.hasVotedOnDocumentPoll(blockHash, contractAddress, _galaxy, _proposal) + ); }, - events: async (_: any, { blockHash, contractAddress, name }: { blockHash: string, contractAddress: string, name?: string }) => { + events: async ( + _: any, + { blockHash, contractAddress, name }: { blockHash: string, contractAddress: string, name?: string }, + expressContext: ExpressContext + ) => { log('events', blockHash, contractAddress, name); - gqlTotalQueryCount.inc(1); - gqlQueryCount.labels('events').inc(1); - const block = await indexer.getBlockProgress(blockHash); - if (!block || !block.isComplete) { - throw new Error(`Block hash ${blockHash} number ${block?.blockNumber} not processed yet`); - } + return executeAndRecordMetrics( + indexer, + gqlLogger, + 'events', + expressContext, + async () => { + const block = await indexer.getBlockProgress(blockHash); + if (!block || !block.isComplete) { + throw new Error(`Block hash ${blockHash} number ${block?.blockNumber} not processed yet`); + } - const events = await indexer.getEventsByFilter(blockHash, contractAddress, name); - return events.map(event => indexer.getResultEvent(event)); + const events = await indexer.getEventsByFilter(blockHash, contractAddress, name); + return events.map(event => indexer.getResultEvent(event)); + } + ); }, - eventsInRange: async (_: any, { fromBlockNumber, toBlockNumber }: { fromBlockNumber: number, toBlockNumber: number }) => { + eventsInRange: async ( + _: any, + { fromBlockNumber, toBlockNumber }: { fromBlockNumber: number, toBlockNumber: number }, + expressContext: ExpressContext + ) => { log('eventsInRange', fromBlockNumber, toBlockNumber); - gqlTotalQueryCount.inc(1); - gqlQueryCount.labels('eventsInRange').inc(1); - const syncStatus = await indexer.getSyncStatus(); + return executeAndRecordMetrics( + indexer, + gqlLogger, + 'eventsInRange', + expressContext, + async () => { + const syncStatus = await indexer.getSyncStatus(); - if (!syncStatus) { - throw new Error('No blocks processed yet'); - } + if (!syncStatus) { + throw new Error('No blocks processed yet'); + } - if ((fromBlockNumber < syncStatus.initialIndexedBlockNumber) || (toBlockNumber > syncStatus.latestProcessedBlockNumber)) { - throw new Error(`Block range should be between ${syncStatus.initialIndexedBlockNumber} and ${syncStatus.latestProcessedBlockNumber}`); - } + if ((fromBlockNumber < syncStatus.initialIndexedBlockNumber) || (toBlockNumber > syncStatus.latestProcessedBlockNumber)) { + throw new Error(`Block range should be between ${syncStatus.initialIndexedBlockNumber} and ${syncStatus.latestProcessedBlockNumber}`); + } - const events = await indexer.getEventsInRange(fromBlockNumber, toBlockNumber); - return events.map(event => indexer.getResultEvent(event)); + const events = await indexer.getEventsInRange(fromBlockNumber, toBlockNumber); + return events.map(event => indexer.getResultEvent(event)); + } + ); }, - getStateByCID: async (_: any, { cid }: { cid: string }) => { + getStateByCID: async ( + _: any, + { cid }: { cid: string }, + expressContext: ExpressContext + ) => { log('getStateByCID', cid); - gqlTotalQueryCount.inc(1); - gqlQueryCount.labels('getStateByCID').inc(1); - const state = await indexer.getStateByCID(cid); + return executeAndRecordMetrics( + indexer, + gqlLogger, + 'getStateByCID', + expressContext, + async () => { + const state = await indexer.getStateByCID(cid); - return state && state.block.isComplete ? getResultState(state) : undefined; + return state && state.block.isComplete ? getResultState(state) : undefined; + } + ); }, - getState: async (_: any, { blockHash, contractAddress, kind }: { blockHash: string, contractAddress: string, kind: string }) => { + getState: async ( + _: any, + { blockHash, contractAddress, kind }: { blockHash: string, contractAddress: string, kind: string }, + expressContext: ExpressContext + ) => { log('getState', blockHash, contractAddress, kind); - gqlTotalQueryCount.inc(1); - gqlQueryCount.labels('getState').inc(1); - const state = await indexer.getPrevState(blockHash, contractAddress, kind); + return executeAndRecordMetrics( + indexer, + gqlLogger, + 'getState', + expressContext, + async () => { + const state = await indexer.getPrevState(blockHash, contractAddress, kind); - return state && state.block.isComplete ? getResultState(state) : undefined; + return state && state.block.isComplete ? getResultState(state) : undefined; + } + ); }, - getSyncStatus: async () => { + getSyncStatus: async ( + _: any, + __: Record, + expressContext: ExpressContext + ) => { log('getSyncStatus'); - gqlTotalQueryCount.inc(1); - gqlQueryCount.labels('getSyncStatus').inc(1); - return indexer.getSyncStatus(); + return executeAndRecordMetrics( + indexer, + gqlLogger, + 'getSyncStatus', + expressContext, + async () => indexer.getSyncStatus() + ); } } }; diff --git a/yarn.lock b/yarn.lock index a66da00..04aa71c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -206,10 +206,10 @@ js-tokens "^4.0.0" picocolors "^1.0.0" -"@cerc-io/cache@^0.2.92": - version "0.2.92" - resolved "https://git.vdb.to/api/packages/cerc-io/npm/%40cerc-io%2Fcache/-/0.2.92/cache-0.2.92.tgz#c30afc9469af94a2ddf0b63aa86be8ca1e71f1a0" - integrity sha512-pOindGekxLR77yNtjuRErF6LlPR1MnPCF7jl6S+IePi9gvt+wD9ZqR7RcXhTUHBzkcDN6DHHZFuseGRZylwU9g== +"@cerc-io/cache@^0.2.94": + version "0.2.94" + resolved "https://git.vdb.to/api/packages/cerc-io/npm/%40cerc-io%2Fcache/-/0.2.94/cache-0.2.94.tgz#e1557bfc3c85ebe3b83f24c79b55c7324797e7ed" + integrity sha512-9NBMd+19aBs3lgiie0X6TJizcn7yBCfEXXlbvMvac9SgYDPcloU+ghauQephAxayRjaWkrgjjfEWR/I0x6FwqA== dependencies: canonical-json "^0.0.4" debug "^4.3.1" @@ -217,19 +217,19 @@ fs-extra "^10.0.0" level "^7.0.0" -"@cerc-io/cli@^0.2.92": - version "0.2.92" - resolved "https://git.vdb.to/api/packages/cerc-io/npm/%40cerc-io%2Fcli/-/0.2.92/cli-0.2.92.tgz#a8fcd9995e0adbc2b995105416d4d2754dbcc4f9" - integrity sha512-GnurQbJEiyuatR36lShlUMZsUqjYuDYmRrIzO/BvPW9Sbhz0D0WcXKvRnVF98ClBLw832Kp7tnEIpgtONjWqAw== +"@cerc-io/cli@^0.2.94": + version "0.2.94" + resolved "https://git.vdb.to/api/packages/cerc-io/npm/%40cerc-io%2Fcli/-/0.2.94/cli-0.2.94.tgz#f193165a4b85174c48150051ea4b0e3df57fdf9e" + integrity sha512-aJGzH630bhYca74efVEU37sCjxRLmERDObzNpKIjbmYjKPg6urMbYI8gs33R2HIS/FUOMS39F5B9NMwgCQd/Vw== dependencies: "@apollo/client" "^3.7.1" - "@cerc-io/cache" "^0.2.92" - "@cerc-io/ipld-eth-client" "^0.2.92" + "@cerc-io/cache" "^0.2.94" + "@cerc-io/ipld-eth-client" "^0.2.94" "@cerc-io/libp2p" "^0.42.2-laconic-0.1.4" "@cerc-io/nitro-node" "^0.1.15" - "@cerc-io/peer" "^0.2.92" - "@cerc-io/rpc-eth-client" "^0.2.92" - "@cerc-io/util" "^0.2.92" + "@cerc-io/peer" "^0.2.94" + "@cerc-io/rpc-eth-client" "^0.2.94" + "@cerc-io/util" "^0.2.94" "@ethersproject/providers" "^5.4.4" "@graphql-tools/utils" "^9.1.1" "@ipld/dag-cbor" "^8.0.0" @@ -250,14 +250,14 @@ typeorm "0.2.37" yargs "^17.0.1" -"@cerc-io/ipld-eth-client@^0.2.92": - version "0.2.92" - resolved "https://git.vdb.to/api/packages/cerc-io/npm/%40cerc-io%2Fipld-eth-client/-/0.2.92/ipld-eth-client-0.2.92.tgz#72d1698e80813e9e4dc44b7b49c039e1617787d7" - integrity sha512-5BXnBGi/BIC+WILuxEpKRvDxBNwftRKIgt/CGPT+RlD/MODEztteODWVismjpTfGGqyTK0rnqCEpHwRp1a04dg== +"@cerc-io/ipld-eth-client@^0.2.94": + version "0.2.94" + resolved "https://git.vdb.to/api/packages/cerc-io/npm/%40cerc-io%2Fipld-eth-client/-/0.2.94/ipld-eth-client-0.2.94.tgz#22f79eaf82da60a25d8f89f23cf45581c012d6ff" + integrity sha512-IOOZrryZ3M7ndhk8N9NeRaXvzC8eU2OS0z7fVL0xZ/h3Ei4D+dz8fVJbCvgGRB6mJadrMXYyhahOtEAJYxjNHg== dependencies: "@apollo/client" "^3.7.1" - "@cerc-io/cache" "^0.2.92" - "@cerc-io/util" "^0.2.92" + "@cerc-io/cache" "^0.2.94" + "@cerc-io/util" "^0.2.94" cross-fetch "^3.1.4" debug "^4.3.1" ethers "^5.4.4" @@ -410,10 +410,10 @@ unique-names-generator "^4.7.1" yargs "^17.0.1" -"@cerc-io/peer@^0.2.92": - version "0.2.92" - resolved "https://git.vdb.to/api/packages/cerc-io/npm/%40cerc-io%2Fpeer/-/0.2.92/peer-0.2.92.tgz#7a0b590b3d6bc59a6c5c0e92fa6914fa16d9d75d" - integrity sha512-TqrAqELOPasYq9xDctnoPpQM+RvFkWeQvr+/AJ3h7jhPL7paAH6YFNS+okpOX9+1RGcDjmAzveM7BTRJ5QA2Ew== +"@cerc-io/peer@^0.2.94": + version "0.2.94" + resolved "https://git.vdb.to/api/packages/cerc-io/npm/%40cerc-io%2Fpeer/-/0.2.94/peer-0.2.94.tgz#c8776a9968b32d96fb91ed2a2c6d4b62b365e4a0" + integrity sha512-QQANziPnT08guAD/Nwy/HoqgIIX+w1+GS2bOUE4hfS+LJmTiZ+Zquiqcj69qGQeMQxfiUHmsBPttioF+B9rp/A== dependencies: "@cerc-io/libp2p" "^0.42.2-laconic-0.1.4" "@cerc-io/prometheus-metrics" "1.1.4" @@ -452,23 +452,23 @@ it-stream-types "^1.0.4" promjs "^0.4.2" -"@cerc-io/rpc-eth-client@^0.2.92": - version "0.2.92" - resolved "https://git.vdb.to/api/packages/cerc-io/npm/%40cerc-io%2Frpc-eth-client/-/0.2.92/rpc-eth-client-0.2.92.tgz#a8e596c1d80008c70b6a5fe39169ebcb6620b8c2" - integrity sha512-n142iIueVdEFSr5phyHApuaNZ0ESKAoZPT78bEV4j8wsGKYiMcho2be7q/5Wn9ZUhg7rws24pvmEGye66juWpg== +"@cerc-io/rpc-eth-client@^0.2.94": + version "0.2.94" + resolved "https://git.vdb.to/api/packages/cerc-io/npm/%40cerc-io%2Frpc-eth-client/-/0.2.94/rpc-eth-client-0.2.94.tgz#0c4f679abacf0aed831cbd295d21780f2a3dd17a" + integrity sha512-S3UvHO7Qb4Z43+fArMbrs78CP8l81D5Ootd225MQ2//TGmu9QsIK9+oqdI1CIVAQCbxI7QAStzdz76Y0px2iFA== dependencies: - "@cerc-io/cache" "^0.2.92" - "@cerc-io/ipld-eth-client" "^0.2.92" - "@cerc-io/util" "^0.2.92" + "@cerc-io/cache" "^0.2.94" + "@cerc-io/ipld-eth-client" "^0.2.94" + "@cerc-io/util" "^0.2.94" chai "^4.3.4" ethers "^5.4.4" left-pad "^1.3.0" mocha "^8.4.0" -"@cerc-io/solidity-mapper@^0.2.92": - version "0.2.92" - resolved "https://git.vdb.to/api/packages/cerc-io/npm/%40cerc-io%2Fsolidity-mapper/-/0.2.92/solidity-mapper-0.2.92.tgz#ba8c45caee3cbe5d6f1b40e2b4aaa017de12a810" - integrity sha512-8fvxbR+3VeRZRtaoj4HhP2z4dbpdLZZAiVmvpzoRiHuRE9VpNsPpzUEbr9DU2ina/wo0Cs5qm7Qdfz3mANSF/g== +"@cerc-io/solidity-mapper@^0.2.94": + version "0.2.94" + resolved "https://git.vdb.to/api/packages/cerc-io/npm/%40cerc-io%2Fsolidity-mapper/-/0.2.94/solidity-mapper-0.2.94.tgz#a414653bf20fcbedd91d4a544c6a019c8a02d958" + integrity sha512-kO2xqyAeq2+bbxBcItsT6i8qK1F0BhaYPzV1tTNXgGbkp3j1do86mc6IeTEeV6J/8e9EaeRBsOSQOdGOHUjcDA== dependencies: dotenv "^10.0.0" @@ -477,15 +477,15 @@ resolved "https://git.vdb.to/api/packages/cerc-io/npm/%40cerc-io%2Fts-channel/-/1.0.3-ts-nitro-0.1.1/ts-channel-1.0.3-ts-nitro-0.1.1.tgz#0768781313a167295c0bf21307f47e02dc17e936" integrity sha512-2jFICUSyffuZ+8+qRhXuLSJq4GJ6Y02wxiXoubH0Kzv2lIKkJtWICY1ZQQhtXAvP0ncAQB85WJHqtqwH8l7J3Q== -"@cerc-io/util@^0.2.92": - version "0.2.92" - resolved "https://git.vdb.to/api/packages/cerc-io/npm/%40cerc-io%2Futil/-/0.2.92/util-0.2.92.tgz#e4fe2858cb76984ca4f0e1047ec53a606f5d1169" - integrity sha512-QldZqgIDBioqNLyY9YtOEvmCiw10HbJrRZIgtqu3xj5uAPP89C3VtHyKpS/Ri8eYdb3ZBbezCuOON8cycDTPAA== +"@cerc-io/util@^0.2.94": + version "0.2.94" + resolved "https://git.vdb.to/api/packages/cerc-io/npm/%40cerc-io%2Futil/-/0.2.94/util-0.2.94.tgz#5d4c8046cf44641eac39a6f6ee11f2eb8b298c1f" + integrity sha512-4B1RkUKZoRMVVxWK5u+5d3GEmn0IN1FoAbkfCsBXMTH+OGI+VVGg6bRozaluy7pegpwDh1KtiH0A7NFjUup4OQ== dependencies: "@apollo/utils.keyvaluecache" "^1.0.1" "@cerc-io/nitro-node" "^0.1.15" - "@cerc-io/peer" "^0.2.92" - "@cerc-io/solidity-mapper" "^0.2.92" + "@cerc-io/peer" "^0.2.94" + "@cerc-io/solidity-mapper" "^0.2.94" "@cerc-io/ts-channel" "1.0.3-ts-nitro-0.1.1" "@ethersproject/properties" "^5.7.0" "@ethersproject/providers" "^5.4.4" @@ -527,6 +527,7 @@ toml "^3.0.0" typeorm "0.2.37" typeorm-naming-strategies "^2.0.0" + winston "^3.13.0" ws "^8.11.0" yargs "^17.0.1" @@ -612,6 +613,11 @@ dependencies: "@chainsafe/is-ip" "^2.0.1" +"@colors/colors@1.6.0", "@colors/colors@^1.6.0": + version "1.6.0" + resolved "https://registry.yarnpkg.com/@colors/colors/-/colors-1.6.0.tgz#ec6cd237440700bc23ca23087f513c75508958b0" + integrity sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA== + "@cspotcode/source-map-support@^0.8.0": version "0.8.1" resolved "https://registry.yarnpkg.com/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz#00629c35a688e05a88b1cda684fb9d5e73f000a1" @@ -619,6 +625,15 @@ dependencies: "@jridgewell/trace-mapping" "0.3.9" +"@dabh/diagnostics@^2.0.2": + version "2.0.3" + resolved "https://registry.yarnpkg.com/@dabh/diagnostics/-/diagnostics-2.0.3.tgz#7f7e97ee9a725dffc7808d93668cc984e1dc477a" + integrity sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA== + dependencies: + colorspace "1.1.x" + enabled "2.0.x" + kuler "^2.0.0" + "@envelop/core@^3.0.4": version "3.0.6" resolved "https://registry.yarnpkg.com/@envelop/core/-/core-3.0.6.tgz#e55c3564d05d648b0356a1c465aa90b0c51f485d" @@ -2936,6 +2951,11 @@ resolved "https://registry.yarnpkg.com/@types/strip-json-comments/-/strip-json-comments-0.0.30.tgz#9aa30c04db212a9a0649d6ae6fd50accc40748a1" integrity sha512-7NQmHra/JILCd1QqpSzl8+mJRc8ZHz3uDm8YV1Ks9IhK0epEiTw8aIErbvH9PI+6XbqhyIQy3462nEsn7UVzjQ== +"@types/triple-beam@^1.3.2": + version "1.3.5" + resolved "https://registry.yarnpkg.com/@types/triple-beam/-/triple-beam-1.3.5.tgz#74fef9ffbaa198eb8b588be029f38b00299caa2c" + integrity sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw== + "@types/yargs-parser@*": version "21.0.0" resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-21.0.0.tgz#0c60e537fa790f5f9472ed2776c2b71ec117351b" @@ -4154,7 +4174,7 @@ cmd-shim@^6.0.0: resolved "https://registry.yarnpkg.com/cmd-shim/-/cmd-shim-6.0.1.tgz#a65878080548e1dca760b3aea1e21ed05194da9d" integrity sha512-S9iI9y0nKR4hwEQsVWpyxld/6kRfGepGfzff83FcaiEBpmvlbA2nnGe7Cylgrx2f/p1P5S5wpRm9oL8z1PbS3Q== -color-convert@^1.9.0: +color-convert@^1.9.0, color-convert@^1.9.3: version "1.9.3" resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== @@ -4173,16 +4193,40 @@ color-name@1.1.3: resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" integrity sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw== -color-name@~1.1.4: +color-name@^1.0.0, color-name@~1.1.4: version "1.1.4" resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== +color-string@^1.6.0: + version "1.9.1" + resolved "https://registry.yarnpkg.com/color-string/-/color-string-1.9.1.tgz#4467f9146f036f855b764dfb5bf8582bf342c7a4" + integrity sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg== + dependencies: + color-name "^1.0.0" + simple-swizzle "^0.2.2" + color-support@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/color-support/-/color-support-1.1.3.tgz#93834379a1cc9a0c61f82f52f0d04322251bd5a2" integrity sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg== +color@^3.1.3: + version "3.2.1" + resolved "https://registry.yarnpkg.com/color/-/color-3.2.1.tgz#3544dc198caf4490c3ecc9a790b54fe9ff45e164" + integrity sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA== + dependencies: + color-convert "^1.9.3" + color-string "^1.6.0" + +colorspace@1.1.x: + version "1.1.4" + resolved "https://registry.yarnpkg.com/colorspace/-/colorspace-1.1.4.tgz#8d442d1186152f60453bf8070cd66eb364e59243" + integrity sha512-BgvKJiuVu1igBUF2kEjRCZXol6wiiGbY5ipL/oVPwm0BL9sIpMIzM8IK7vwuxIIzOXMV3Ey5w+vxhm0rR/TN8w== + dependencies: + color "^3.1.3" + text-hex "1.0.x" + columnify@1.6.0: version "1.6.0" resolved "https://registry.yarnpkg.com/columnify/-/columnify-1.6.0.tgz#6989531713c9008bb29735e61e37acf5bd553cf3" @@ -4794,6 +4838,11 @@ emoji-regex@^8.0.0: resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== +enabled@2.0.x: + version "2.0.0" + resolved "https://registry.yarnpkg.com/enabled/-/enabled-2.0.0.tgz#f9dd92ec2d6f4bbc0d5d1e64e21d61cd4665e7c2" + integrity sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ== + encodeurl@~1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" @@ -5377,6 +5426,11 @@ fastq@^1.6.0: dependencies: reusify "^1.0.4" +fecha@^4.2.0: + version "4.2.3" + resolved "https://registry.yarnpkg.com/fecha/-/fecha-4.2.3.tgz#4d9ccdbc61e8629b259fdca67e65891448d569fd" + integrity sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw== + figlet@^1.1.1: version "1.7.0" resolved "https://registry.yarnpkg.com/figlet/-/figlet-1.7.0.tgz#46903a04603fd19c3e380358418bb2703587a72e" @@ -5469,6 +5523,11 @@ flatted@^3.1.0: resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.7.tgz#609f39207cb614b89d0765b477cb2d437fbf9787" integrity sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ== +fn.name@1.x.x: + version "1.1.0" + resolved "https://registry.yarnpkg.com/fn.name/-/fn.name-1.1.0.tgz#26cad8017967aea8731bc42961d04a3d5988accc" + integrity sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw== + follow-redirects@^1.0.0: version "1.15.3" resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.3.tgz#fe2f3ef2690afce7e82ed0b44db08165b207123a" @@ -6508,6 +6567,11 @@ is-arrayish@^0.2.1: resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" integrity sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg== +is-arrayish@^0.3.1: + version "0.3.2" + resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.3.2.tgz#4574a2ae56f7ab206896fb431eaeed066fdf8f03" + integrity sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ== + is-bigint@^1.0.1: version "1.0.4" resolved "https://registry.yarnpkg.com/is-bigint/-/is-bigint-1.0.4.tgz#08147a1875bc2b32005d41ccd8291dffc6691df3" @@ -7200,6 +7264,11 @@ kind-of@^6.0.2, kind-of@^6.0.3: resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd" integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw== +kuler@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/kuler/-/kuler-2.0.0.tgz#e2c570a3800388fb44407e851531c1d670b061b3" + integrity sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A== + left-pad@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/left-pad/-/left-pad-1.3.0.tgz#5b8a3a7765dfe001261dde915589e782f8c94d1e" @@ -7510,6 +7579,18 @@ log-symbols@^4.1.0: chalk "^4.1.0" is-unicode-supported "^0.1.0" +logform@^2.3.2, logform@^2.4.0: + version "2.6.0" + resolved "https://registry.yarnpkg.com/logform/-/logform-2.6.0.tgz#8c82a983f05d6eaeb2d75e3decae7a768b2bf9b5" + integrity sha512-1ulHeNPp6k/LD8H91o7VYFBng5i1BDE7HoKxVbZiGFidS1Rj65qcywLxX+pVfAPoQJEjRdvKcusKwOupHCVOVQ== + dependencies: + "@colors/colors" "1.6.0" + "@types/triple-beam" "^1.3.2" + fecha "^4.2.0" + ms "^2.1.1" + safe-stable-stringify "^2.3.1" + triple-beam "^1.3.0" + loglevel@^1.6.8: version "1.8.1" resolved "https://registry.yarnpkg.com/loglevel/-/loglevel-1.8.1.tgz#5c621f83d5b48c54ae93b6156353f555963377b4" @@ -8575,6 +8656,13 @@ once@^1.3.0, once@^1.3.1, once@^1.4.0: dependencies: wrappy "1" +one-time@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/one-time/-/one-time-1.0.0.tgz#e06bc174aed214ed58edede573b433bbf827cb45" + integrity sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g== + dependencies: + fn.name "1.x.x" + onetime@^5.1.0, onetime@^5.1.2: version "5.1.2" resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e" @@ -9845,6 +9933,11 @@ safe-regex-test@^1.0.0: get-intrinsic "^1.1.3" is-regex "^1.1.4" +safe-stable-stringify@^2.3.1: + version "2.4.3" + resolved "https://registry.yarnpkg.com/safe-stable-stringify/-/safe-stable-stringify-2.4.3.tgz#138c84b6f6edb3db5f8ef3ef7115b8f55ccbf886" + integrity sha512-e2bDA2WJT0wxseVd4lsDP4+3ONX6HpMXQa1ZhFQ7SU+GjvORCmShbCMltrtIDfkYhVHrOcPtj+KhmDBdPdZD1g== + "safer-buffer@>= 2.1.2 < 3", "safer-buffer@>= 2.1.2 < 3.0.0": version "2.1.2" resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" @@ -9992,6 +10085,13 @@ sigstore@^1.0.0: make-fetch-happen "^11.0.1" tuf-js "^1.0.0" +simple-swizzle@^0.2.2: + version "0.2.2" + resolved "https://registry.yarnpkg.com/simple-swizzle/-/simple-swizzle-0.2.2.tgz#a4da6b635ffcccca33f70d17cb92592de95e557a" + integrity sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg== + dependencies: + is-arrayish "^0.3.1" + slash@3.0.0, slash@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" @@ -10108,6 +10208,11 @@ ssri@^10.0.0, ssri@^10.0.1: dependencies: minipass "^4.0.0" +stack-trace@0.0.x: + version "0.0.10" + resolved "https://registry.yarnpkg.com/stack-trace/-/stack-trace-0.0.10.tgz#547c70b347e8d32b4e108ea1a2a159e5fdde19c0" + integrity sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg== + statuses@2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/statuses/-/statuses-2.0.1.tgz#55cb000ccf1d48728bd23c685a063998cf1a1b63" @@ -10354,6 +10459,11 @@ text-extensions@^1.0.0: resolved "https://registry.yarnpkg.com/text-extensions/-/text-extensions-1.9.0.tgz#1853e45fee39c945ce6f6c36b2d659b5aabc2a26" integrity sha512-wiBrwC1EhBelW12Zy26JeOUkQ5mRu+5o8rpsJk5+2t+Y5vE7e842qtZDQ2g1NpX/29HdyFeJ4nSIhI47ENSxlQ== +text-hex@1.0.x: + version "1.0.0" + resolved "https://registry.yarnpkg.com/text-hex/-/text-hex-1.0.0.tgz#69dc9c1b17446ee79a92bf5b884bb4b9127506f5" + integrity sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg== + text-table@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" @@ -10451,6 +10561,11 @@ trim-newlines@^3.0.0: resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-3.0.1.tgz#260a5d962d8b752425b32f3a7db0dcacd176c144" integrity sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw== +triple-beam@^1.3.0: + version "1.4.1" + resolved "https://registry.yarnpkg.com/triple-beam/-/triple-beam-1.4.1.tgz#6fde70271dc6e5d73ca0c3b24e2d92afb7441984" + integrity sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg== + truncate-utf8-bytes@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/truncate-utf8-bytes/-/truncate-utf8-bytes-1.0.2.tgz#405923909592d56f78a5818434b0b78489ca5f2b" @@ -11013,6 +11128,32 @@ wide-align@^1.1.5: dependencies: string-width "^1.0.2 || 2 || 3 || 4" +winston-transport@^4.7.0: + version "4.7.0" + resolved "https://registry.yarnpkg.com/winston-transport/-/winston-transport-4.7.0.tgz#e302e6889e6ccb7f383b926df6936a5b781bd1f0" + integrity sha512-ajBj65K5I7denzer2IYW6+2bNIVqLGDHqDw3Ow8Ohh+vdW+rv4MZ6eiDvHoKhfJFZ2auyN8byXieDDJ96ViONg== + dependencies: + logform "^2.3.2" + readable-stream "^3.6.0" + triple-beam "^1.3.0" + +winston@^3.13.0: + version "3.13.0" + resolved "https://registry.yarnpkg.com/winston/-/winston-3.13.0.tgz#e76c0d722f78e04838158c61adc1287201de7ce3" + integrity sha512-rwidmA1w3SE4j0E5MuIufFhyJPBDG7Nu71RkZor1p2+qHvJSZ9GYDA81AyleQcZbh/+V6HjeBdfnTZJm9rSeQQ== + dependencies: + "@colors/colors" "^1.6.0" + "@dabh/diagnostics" "^2.0.2" + async "^3.2.3" + is-stream "^2.0.0" + logform "^2.4.0" + one-time "^1.0.0" + readable-stream "^3.4.0" + safe-stable-stringify "^2.3.1" + stack-trace "0.0.x" + triple-beam "^1.3.0" + winston-transport "^4.7.0" + word-wrap@^1.2.3: version "1.2.3" resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c"