Skip to content

Commit

Permalink
Change default config for Prom metrics (#3408)
Browse files Browse the repository at this point in the history
  • Loading branch information
dotansimha authored Aug 14, 2024
1 parent df689a5 commit 88393b3
Show file tree
Hide file tree
Showing 5 changed files with 179 additions and 45 deletions.
18 changes: 18 additions & 0 deletions .changeset/cuddly-needles-cry.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
---
'@graphql-yoga/plugin-prometheus': major
---

By default, the following metrics are now enabled:

- `graphql_envelop_deprecated_field`
- `graphql_envelop_request`
- `graphql_envelop_request_duration`
- `graphql_envelop_request_time_summary`
- `graphql_envelop_phase_parse`
- `graphql_envelop_phase_validate`
- `graphql_envelop_phase_context`
- `graphql_envelop_error_result`
- `graphql_envelop_phase_execute`
- `graphql_envelop_phase_subscribe`
- `graphql_envelop_schema_change`
- `graphql_yoga_http_duration`
57 changes: 52 additions & 5 deletions packages/plugins/prometheus/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,30 @@ export {
getSummaryFromConfig,
};

export type PrometheusTracingPluginConfig = EnvelopPrometheusTracingPluginConfig & {
metrics: {
export type PrometheusTracingPluginConfig = Omit<
EnvelopPrometheusTracingPluginConfig,
'metrics'
> & {
/**
* The Prometheus metrics to report.
*
* By default, the following metrics are enabled:
*
* - `graphql_envelop_deprecated_field`
* - `graphql_envelop_request`
* - `graphql_envelop_request_duration`
* - `graphql_envelop_request_time_summary`
* - `graphql_envelop_phase_parse`
* - `graphql_envelop_phase_validate`
* - `graphql_envelop_phase_context`
* - `graphql_envelop_error_result`
* - `graphql_envelop_phase_execute`
* - `graphql_envelop_phase_subscribe`
* - `graphql_envelop_schema_change`
* - `graphql_yoga_http_duration`
*
*/
metrics?: EnvelopPrometheusTracingPluginConfig['metrics'] & {
/**
* Tracks the duration of HTTP requests. It reports the time spent to
* process each incoming request as an histogram.
Expand Down Expand Up @@ -66,12 +88,37 @@ export type PrometheusTracingPluginConfig = EnvelopPrometheusTracingPluginConfig
endpoint?: string | boolean;
};

const DEFAULT_METRICS_CONFIG: PrometheusTracingPluginConfig['metrics'] = {
graphql_envelop_deprecated_field: true,
graphql_envelop_request: true,
graphql_envelop_request_duration: true,
graphql_envelop_request_time_summary: true,
graphql_envelop_phase_parse: true,
graphql_envelop_phase_validate: true,
graphql_envelop_phase_context: true,
graphql_envelop_error_result: true,
graphql_envelop_execute_resolver: false,
graphql_envelop_phase_execute: true,
graphql_envelop_phase_subscribe: true,
graphql_envelop_schema_change: true,
graphql_yoga_http_duration: true,
};

export function usePrometheus(options: PrometheusTracingPluginConfig): Plugin {
const endpoint = options.endpoint || '/metrics';
const registry = options.registry || defaultRegistry;
const resolvedOptions: EnvelopPrometheusTracingPluginConfig = {
...options,
metrics: {
...DEFAULT_METRICS_CONFIG,
...options.metrics,
},
};

const httpHistogram = getHistogramFromConfig<PrometheusTracingPluginConfig['metrics']>(
options,
const httpHistogram = getHistogramFromConfig<
NonNullable<PrometheusTracingPluginConfig['metrics']>
>(
resolvedOptions,
'graphql_yoga_http_duration',
{
help: 'Time spent on HTTP connection',
Expand All @@ -91,7 +138,7 @@ export function usePrometheus(options: PrometheusTracingPluginConfig): Plugin {

return {
onPluginInit({ addPlugin }) {
addPlugin(useEnvelopPrometheus({ ...options, registry }) as Plugin);
addPlugin(useEnvelopPrometheus({ ...resolvedOptions, registry }) as Plugin);
},
onRequest({ request, url, fetchAPI, endResponse }) {
startByRequest.set(request, Date.now());
Expand Down
45 changes: 45 additions & 0 deletions packages/plugins/prometheus/tests/prometheus.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,51 @@ describe('Prometheus', () => {
afterEach(() => {
registry.clear();
});

it('should have default configs for the plugin metrics', async () => {
const yoga = createYoga({
schema,
plugins: [
usePrometheus({
registry,
}),
],
});
const result = await yoga.fetch('http://localhost:4000/graphql', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'x-test': 'test',
},
body: JSON.stringify({
query: /* GraphQL */ `
query TestProm {
hello
}
`,
}),
});
await result.text();
const metrics = await registry.metrics();

// enabled by default
expect(metrics).toContain('# TYPE graphql_yoga_http_duration histogram');
expect(metrics).toContain('# TYPE graphql_envelop_phase_parse histogram');
expect(metrics).toContain('# TYPE graphql_envelop_phase_validate histogram');
expect(metrics).toContain('# TYPE graphql_envelop_phase_context histogram');
expect(metrics).toContain('# TYPE graphql_envelop_phase_execute histogram');
expect(metrics).toContain('# TYPE graphql_envelop_phase_subscribe histogram');
expect(metrics).toContain('# TYPE graphql_envelop_request_duration histogram');
expect(metrics).toContain('# TYPE graphql_envelop_request_time_summary summary');
expect(metrics).toContain('# TYPE graphql_envelop_error_result counter');
expect(metrics).toContain('# TYPE graphql_envelop_request counter');
expect(metrics).toContain('# TYPE graphql_envelop_deprecated_field counter');
expect(metrics).toContain('# TYPE graphql_envelop_schema_change counter');

// disabled by default
expect(metrics).not.toContain('graphql_envelop_execute_resolver');
});

it('http flag should work', async () => {
const yoga = createYoga({
schema,
Expand Down
2 changes: 1 addition & 1 deletion website/src/pages/docs/features/_meta.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,9 @@ export default {
'sofa-api': 'REST API',
cookies: 'Cookies',
'apollo-federation': 'Apollo Federation',
'envelop-plugins': 'Plugins',
testing: 'Testing',
jwt: 'JWT',
'landing-page': 'Landing Page',
'request-customization': 'Request Customization',
'envelop-plugins': 'Custom Plugins',
};
102 changes: 63 additions & 39 deletions website/src/pages/docs/features/monitoring.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -35,27 +35,26 @@ const getEnveloped = envelop({
// ... other plugins ...
usePrometheus({
endpoint: '/metrics', // optional, default is `/metrics`, you can disable it by setting it to `false` if registry is configured in "push" mode
usePrometheus({
// all metrics are disabled by default, please opt-in to the metrics you wish to get
metrics: {
graphql_envelop_request_time_summary: true,
graphql_envelop_phase_parse: true,
graphql_envelop_phase_validate: true,
graphql_envelop_phase_context: true,
graphql_envelop_phase_execute: true,
graphql_envelop_phase_subscribe: true,
graphql_envelop_error_result: true,
graphql_envelop_deprecated_field: true,
graphql_envelop_request_duration: true,
graphql_envelop_schema_change: true,
graphql_envelop_request: true,

// Warning: enabling resolvers level metrics will introduce significant overhead
graphql_envelop_execute_resolver: true
},

resolversWhitelist: ['Mutation.*', 'Query.user'] // reports metrics for these resolvers, leave `undefined` to report all fields
}),
// Optional, see default values below
metrics: {
// By default, these are the metrics that are enabled:
graphql_envelop_request_time_summary: true,
graphql_envelop_phase_parse: true,
graphql_envelop_phase_validate: true,
graphql_envelop_phase_context: true,
graphql_envelop_phase_execute: true,
graphql_envelop_phase_subscribe: true,
graphql_envelop_error_result: true,
graphql_envelop_deprecated_field: true,
graphql_envelop_request_duration: true,
graphql_envelop_schema_change: true,
graphql_envelop_request: true,
graphql_yoga_http_duration: true,

// This metric is disabled by default.
// Warning: enabling resolvers level metrics will introduce significant overhead
graphql_envelop_execute_resolver: false
}
})
]
})
Expand Down Expand Up @@ -84,6 +83,8 @@ disabled by setting the corresponding key in `labels` option object to `false`.

### `graphql_yoga_http_duration`

> ✅ This metric is enabled by default.
This metric tracks the duration of incoming (downstream) HTTP requests. It reports the time spent to
process each incoming request as a
[histogram](https://prometheus.io/docs/concepts/metric_types/#histogram).
Expand All @@ -98,8 +99,6 @@ filter is to include only `statusCode` with `200` value and `method` with `POST`
for GraphQL requests, but it can also be `GET` depending on your client setup) value to get
execution time of successful GraphQL requests only.

To enable this metric, set the `http` option to `true`.

#### Labels

This metric includes some useful labels to help you identify requests and group them together.
Expand Down Expand Up @@ -135,6 +134,8 @@ graphql_yoga_http_duration_count{method="GET",statusCode="200",operationName="An

### `graphql_envelop_phase_parse`

> ✅ This metric is enabled by default.
This metric tracks the duration of the `parse` phase of the GraphQL execution. It reports the time
spent parsing the incoming GraphQL operation.

Expand Down Expand Up @@ -174,6 +175,8 @@ graphql_envelop_phase_parse_count{operationName="Anonymous",operationType="query

### `graphql_envelop_phase_validate`

> ✅ This metric is enabled by default.
This metric tracks the duration of the `validate` phase of the GraphQL execution. It reports the
time spent validating the incoming GraphQL operation.

Expand Down Expand Up @@ -209,6 +212,8 @@ graphql_envelop_phase_validate_count{operationName="Anonymous",operationType="qu

### `graphql_envelop_phase_context`

> ✅ This metric is enabled by default.
This metric tracks the duration of the `context` phase of the GraphQL execution. It reports the time
spent building the context object that will be passed to the executors.

Expand Down Expand Up @@ -244,6 +249,8 @@ graphql_envelop_phase_context_count{operationName="Anonymous",operationType="que

### `graphql_envelop_phase_execute`

> ✅ This metric is enabled by default.
This metric tracks the duration of the `execute` phase of the GraphQL execution. It reports the time
spent actually resolving the response of the incoming operation. This includes the gathering of all
the data from all sources required to construct the final response. It is reported as a
Expand Down Expand Up @@ -282,6 +289,8 @@ graphql_envelop_phase_execute_count{operationName="Anonymous",operationType="que

### `graphql_envelop_phase_subscribe`

> ✅ This metric is enabled by default.
This metric tracks the duration of the `subscribe` phase of the GraphQL execution. It reports the
time spent initiating a subscription (which doesn't include actually sending the first response).

Expand Down Expand Up @@ -317,6 +326,8 @@ graphql_envelop_phase_subscribe_count{operationName="Anonymous",operationType="s

### `graphql_envelop_request_duration`

> ✅ This metric is enabled by default.
This metric tracks the duration of the complete GraphQL operation execution.

It is reported as a [histogram](https://prometheus.io/docs/concepts/metric_types/#histogram).
Expand Down Expand Up @@ -351,6 +362,8 @@ graphql_envelop_request_duration_count{operationName="Anonymous",operationType="

### `graphql_envelop_request_time_summary`

> ✅ This metric is enabled by default.
This metric provides a summary of the time spent on the GraphQL operation execution.

It reports the same timing than
Expand Down Expand Up @@ -382,6 +395,8 @@ graphql_envelop_request_time_summary_count{operationName="Anonymous",operationTy

### `graphql_envelop_error_result`

> ✅ This metric is enabled by default.
This metric tracks the number of errors that occurred returned by the GraphQL execution. It counts
all errors found in the final response, but it also includes errors from other GraphQL processing
phases (parsing, validation and context building).
Expand Down Expand Up @@ -410,6 +425,8 @@ graphql_envelop_error_result{operationName="Anonymous",operationType="query",pat

### `graphql_envelop_request`

> ✅ This metric is enabled by default.
This metric tracks the number of GraphQL operations executed. It counts all operations, either
failed or successful, including subscriptions.

Expand All @@ -432,6 +449,8 @@ graphql_envelop_request{operationName="Anonymous",operationType="query"} 1

### `graphql_envelop_deprecated_field`

> ✅ This metric is enabled by default.
This metric tracks the number of deprecated fields used in the GraphQL operation.

#### Labels
Expand All @@ -453,6 +472,8 @@ graphql_envelop_deprecated_field{operationName="Anonymous",operationType="query"

### `graphql_envelop_schema_change`

> ✅ This metric is enabled by default.
This metric tracks the number of schema changes that have occurred since the gateway started.

If you are using a plugin that modifies the schema on the fly, be aware that this metric will also
Expand All @@ -473,6 +494,8 @@ graphql_envelop_schema_change 1

### `graphql_envelop_execute_resolver`

> ❌ This metric is disabled by default.
> **Caution**: Enabling resolvers level metrics will introduce significant overhead.
>
> We highly recommend to enable this for debugging purpose only.
Expand Down Expand Up @@ -573,21 +596,22 @@ const getEnveloped = envelop({
useEngine({ parse, validate, specifiedRules, execute, subscribe }),
// ... other plugins ...
usePrometheus({
// all optional, and by default, all set to false, please opt-in to the metrics you wish to get
parse: createHistogram({
registry: registry // make sure to add your custom registry, if you are not using the default one
histogram: new Histogram({
name: 'my_custom_name',
help: 'HELP ME',
labelNames: ['opText'] as const,
}),
fillLabelsFn: params => {
// if you wish to fill your `labels` with metadata, you can use the params in order to get access to things like DocumentNode, operationName, operationType, `error` (for error metrics) and `info` (for resolvers metrics)
return {
opText: print(params.document)
metrics: {
graphql_envelop_phase_parse: createHistogram({
registry: registry // make sure to add your custom registry, if you are not using the default one
histogram: new Histogram({
name: 'my_custom_name',
help: 'HELP ME',
labelNames: ['opText'] as const,
}),
fillLabelsFn: params => {
// if you wish to fill your `labels` with metadata, you can use the params in order to get access to things like DocumentNode, operationName, operationType, `error` (for error metrics) and `info` (for resolvers metrics)
return {
opText: print(params.document)
}
}
}
})
})
}
})
]
})
Expand All @@ -610,8 +634,8 @@ function usePrometheusWithRegistry() {
registry.clear()

return usePrometheus({
registry,
...
registry
// ...
})
}
```
Expand Down

0 comments on commit 88393b3

Please sign in to comment.