Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions .changeset/rude-eggs-pull.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
'@eth-optimism/common-ts': minor
'@eth-optimism/drippie-mon': minor
'@eth-optimism/fault-detector': minor
'@eth-optimism/message-relayer': minor
'@eth-optimism/replica-healthcheck': minor
---

BaseServiceV2 exposes service name and version as standard synthetic metric
68 changes: 50 additions & 18 deletions packages/common-ts/src/base-service/base-service-v2.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import bodyParser from 'body-parser'
import morgan from 'morgan'

import { Logger } from '../common/logger'
import { Metric } from './metrics'
import { Metric, Gauge, Counter } from './metrics'
import { validators } from './validators'

export type Options = {
Expand All @@ -31,13 +31,19 @@ export type OptionsSpec<TOptions extends Options> = {
validator: (spec?: Spec<TOptions[P]>) => ValidatorSpec<TOptions[P]>
desc: string
default?: TOptions[P]
secret?: boolean
}
}

export type MetricsV2 = {
[key: string]: Metric
}

export type StandardMetrics = {
metadata: Gauge
unhandledErrors: Counter
}

export type MetricsSpec<TMetrics extends MetricsV2> = {
[P in keyof Required<TMetrics>]: {
type: new (configuration: any) => TMetrics[P]
Expand Down Expand Up @@ -99,7 +105,7 @@ export abstract class BaseServiceV2<
/**
* Metrics.
*/
protected readonly metrics: TMetrics
protected readonly metrics: TMetrics & StandardMetrics

/**
* Registry for prometheus metrics.
Expand Down Expand Up @@ -137,6 +143,7 @@ export abstract class BaseServiceV2<
*/
constructor(params: {
name: string
version: string
optionsSpec: OptionsSpec<TOptions>
metricsSpec: MetricsSpec<TMetrics>
options?: Partial<TOptions>
Expand Down Expand Up @@ -170,6 +177,31 @@ export abstract class BaseServiceV2<
},
}

// List of options that can safely be logged.
const publicOptionNames = Object.entries(params.optionsSpec)
.filter(([, spec]) => {
return spec.secret !== true
})
.map(([key]) => {
return key
})

// Add default metrics to metrics spec.
;(params.metricsSpec as any) = {
...(params.metricsSpec || {}),

// Users cannot set these options.
metadata: {
type: Gauge,
desc: 'Service metadata',
labels: ['name', 'version'].concat(publicOptionNames),
},
unhandledErrors: {
type: Counter,
desc: 'Unhandled errors',
},
}

/**
* Special snake_case function which accounts for the common strings "L1" and "L2" which would
* normally be split into "L_1" and "L_2" by the snake_case function.
Expand Down Expand Up @@ -274,7 +306,7 @@ export abstract class BaseServiceV2<
labelNames: spec.labels || [],
})
return acc
}, {}) as TMetrics
}, {}) as TMetrics & StandardMetrics

// Create the metrics server.
this.metricsRegistry = prometheus.register
Expand Down Expand Up @@ -309,6 +341,19 @@ export abstract class BaseServiceV2<
// Handle stop signals.
process.on('SIGTERM', stop)
process.on('SIGINT', stop)

// Set metadata synthetic metric.
this.metrics.metadata.set(
{
name: params.name,
version: params.version,
...publicOptionNames.reduce((acc, key) => {
acc[key] = config.str(key)
return acc
}, {}),
},
1
)
}

/**
Expand All @@ -330,21 +375,7 @@ export abstract class BaseServiceV2<
app.use(bodyParser.urlencoded({ extended: true }))

// Logging.
app.use(
morgan((tokens, req, res) => {
return [
tokens.method(req, res),
tokens.url(req, res),
tokens.status(req, res),
JSON.stringify(req.body),
'\n',
tokens.res(req, res, 'content-length'),
'-',
tokens['response-time'](req, res),
'ms',
].join(' ')
})
)
app.use(morgan('short'))

// Metrics.
// Will expose a /metrics endpoint by default.
Expand Down Expand Up @@ -397,6 +428,7 @@ export abstract class BaseServiceV2<
try {
await this.main()
} catch (err) {
this.metrics.unhandledErrors.inc()
this.logger.error('caught an unhandled exception', {
message: err.message,
stack: err.stack,
Expand Down
18 changes: 3 additions & 15 deletions packages/drippie-mon/src/service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ type DrippieMonOptions = {
}

type DrippieMonMetrics = {
metadata: Gauge
isExecutable: Gauge
executedDripCount: Gauge
unexpectedRpcErrors: Counter
Expand All @@ -31,6 +30,8 @@ export class DrippieMonService extends BaseServiceV2<
> {
constructor(options?: Partial<DrippieMonOptions>) {
super({
// eslint-disable-next-line @typescript-eslint/no-var-requires
version: require('../package.json').version,
name: 'drippie-mon',
loop: true,
loopIntervalMs: 60_000,
Expand All @@ -39,18 +40,14 @@ export class DrippieMonService extends BaseServiceV2<
rpc: {
validator: validators.provider,
desc: 'Provider for network where Drippie is deployed',
secret: true,
},
drippieAddress: {
validator: validators.str,
desc: 'Address of Drippie contract',
},
},
metricsSpec: {
metadata: {
type: Gauge,
desc: 'Drippie Monitor metadata',
labels: ['version', 'address'],
},
isExecutable: {
type: Gauge,
desc: 'Whether or not the drip is currently executable',
Expand All @@ -76,15 +73,6 @@ export class DrippieMonService extends BaseServiceV2<
DrippieArtifact.abi,
this.options.rpc
)

this.metrics.metadata.set(
{
// eslint-disable-next-line @typescript-eslint/no-var-requires
version: require('../package.json').version,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice, definitely way more readable to do it this way

address: this.options.drippieAddress,
},
1
)
}

protected async main(): Promise<void> {
Expand Down
4 changes: 4 additions & 0 deletions packages/fault-detector/src/service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ type State = {
export class FaultDetector extends BaseServiceV2<Options, Metrics, State> {
constructor(options?: Partial<Options>) {
super({
// eslint-disable-next-line @typescript-eslint/no-var-requires
version: require('../package.json').version,
name: 'fault-detector',
loop: true,
loopIntervalMs: 1000,
Expand All @@ -40,10 +42,12 @@ export class FaultDetector extends BaseServiceV2<Options, Metrics, State> {
l1RpcProvider: {
validator: validators.provider,
desc: 'Provider for interacting with L1',
secret: true,
},
l2RpcProvider: {
validator: validators.provider,
desc: 'Provider for interacting with L2',
secret: true,
},
startBatchIndex: {
validator: validators.num,
Expand Down
7 changes: 6 additions & 1 deletion packages/message-relayer/src/service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,20 +37,25 @@ export class MessageRelayerService extends BaseServiceV2<
> {
constructor(options?: Partial<MessageRelayerOptions>) {
super({
name: 'Message_Relayer',
// eslint-disable-next-line @typescript-eslint/no-var-requires
version: require('../package.json').version,
name: 'message-relayer',
options,
optionsSpec: {
l1RpcProvider: {
validator: validators.provider,
desc: 'Provider for interacting with L1.',
secret: true,
},
l2RpcProvider: {
validator: validators.provider,
desc: 'Provider for interacting with L2.',
secret: true,
},
l1Wallet: {
validator: validators.wallet,
desc: 'Wallet used to interact with L1.',
secret: true,
},
fromL2TransactionIndex: {
validator: validators.num,
Expand Down
6 changes: 5 additions & 1 deletion packages/replica-healthcheck/src/service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,17 +32,21 @@ export class HealthcheckService extends BaseServiceV2<
> {
constructor(options?: Partial<HealthcheckOptions>) {
super({
name: 'Healthcheck',
// eslint-disable-next-line @typescript-eslint/no-var-requires
version: require('../package.json').version,
name: 'healthcheck',
loopIntervalMs: 5000,
options,
optionsSpec: {
referenceRpcProvider: {
validator: validators.provider,
desc: 'Provider for interacting with L1',
secret: true,
},
targetRpcProvider: {
validator: validators.provider,
desc: 'Provider for interacting with L2',
secret: true,
},
onDivergenceWaitMs: {
validator: validators.num,
Expand Down