Skip to content

Commit

Permalink
remove high cardinality labels and fix bug for operationName (#3179)
Browse files Browse the repository at this point in the history
* fix: operationName issue when label is not present

* remove high cardinality labels

* fix: format

* upgrade prometheus

* add changeset

* use major version
  • Loading branch information
darren-west authored Feb 2, 2024
1 parent 266488a commit 7dc37e6
Show file tree
Hide file tree
Showing 5 changed files with 19 additions and 80 deletions.
5 changes: 5 additions & 0 deletions .changeset/dull-numbers-work.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@graphql-yoga/plugin-prometheus': major
---

Removed labels that cause high cardinality
2 changes: 1 addition & 1 deletion packages/plugins/prometheus/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@
"prom-client": "^15.0.0"
},
"dependencies": {
"@envelop/prometheus": "^9.2.0"
"@envelop/prometheus": "^9.3.1"
},
"devDependencies": {
"prom-client": "15.0.0"
Expand Down
25 changes: 2 additions & 23 deletions packages/plugins/prometheus/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,7 @@ export { createCounter, createHistogram, createSummary, FillLabelsFnParams };

export interface PrometheusTracingPluginConfig extends EnvelopPrometheusTracingPluginConfig {
http?: boolean | ReturnType<typeof createHistogram>;
httpRequestHeaders?: boolean;
httpResponseHeaders?: boolean;

/**
* The endpoint to serve metrics exposed by this plugin.
* Defaults to "/metrics".
Expand All @@ -26,7 +25,6 @@ export interface PrometheusTracingPluginConfig extends EnvelopPrometheusTracingP
export function usePrometheus(options: PrometheusTracingPluginConfig): Plugin {
const endpoint = options.endpoint || '/metrics';
const registry = options.registry || defaultRegistry;

let httpHistogram: ReturnType<typeof createHistogram> | undefined;

function labelExists(label: string) {
Expand All @@ -37,19 +35,13 @@ export function usePrometheus(options: PrometheusTracingPluginConfig): Plugin {
}

if (options.http) {
const labelNames = ['url', 'method', 'statusCode', 'statusText'];
const labelNames = ['method', 'statusCode'];
if (labelExists('operationName')) {
labelNames.push('operationName');
}
if (labelExists('operationType')) {
labelNames.push('operationType');
}
if (options.httpRequestHeaders) {
labelNames.push('requestHeaders');
}
if (options.httpResponseHeaders) {
labelNames.push('responseHeaders');
}
httpHistogram =
typeof options.http === 'object'
? options.http
Expand All @@ -62,28 +54,15 @@ export function usePrometheus(options: PrometheusTracingPluginConfig): Plugin {
}),
fillLabelsFn(params, { request, response }) {
const labels: Record<string, string> = {
operationName: params.operationName || 'Anonymous',
url: request.url,
method: request.method,
statusCode: response.status,
statusText: response.statusText,
};
if (labelExists('operationType') && params.operationType) {
labels.operationType = params.operationType;
}
if (labelExists('operationName')) {
labels.operationName = params.operationName || 'Anonymous';
}
if (options.httpRequestHeaders) {
labels.requestHeaders = JSON.stringify(
Object.fromEntries(request.headers.entries()),
);
}
if (options.httpResponseHeaders) {
labels.responseHeaders = JSON.stringify(
Object.fromEntries(response.headers.entries()),
);
}
return labels;
},
});
Expand Down
63 changes: 9 additions & 54 deletions packages/plugins/prometheus/tests/prometheus.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ describe('Prometheus', () => {
afterEach(() => {
registry.clear();
});
it('http flag should work and do not send headers by default', async () => {
it('http flag should work', async () => {
const yoga = createYoga({
schema,
plugins: [
Expand Down Expand Up @@ -47,21 +47,20 @@ describe('Prometheus', () => {
expect(metrics).toContain('graphql_yoga_http_duration_bucket');
expect(metrics).toContain('operationName="TestProm"');
expect(metrics).toContain('operationType="query"');
expect(metrics).toContain('url="http://localhost:4000/graphql"');
expect(metrics).toContain('method="POST"');
expect(metrics).toContain('statusCode="200"');
expect(metrics).toContain('statusText="OK"');
expect(metrics).not.toContain('requestHeaders');
expect(metrics).not.toContain('x-test=test');
});
it('httpRequestHeaders should work', async () => {
it('labels should be excluded', async () => {
const yoga = createYoga({
schema,
plugins: [
usePrometheus({
http: true,
httpRequestHeaders: true,
registry,
labels: {
operationName: false,
operationType: false,
},
}),
],
});
Expand All @@ -82,54 +81,12 @@ describe('Prometheus', () => {
await result.text();
const metrics = await registry.metrics();
expect(metrics).toContain('graphql_yoga_http_duration_bucket');
expect(metrics).toContain('operationName="TestProm"');
expect(metrics).toContain('operationType="query"');
expect(metrics).toContain('url="http://localhost:4000/graphql"');
expect(metrics).toContain('method="POST"');
expect(metrics).toContain('statusCode="200"');
expect(metrics).toContain('statusText="OK"');
expect(metrics).toContain(
'requestHeaders="{\\"content-type\\":\\"application/json\\",\\"x-test\\":\\"test\\",\\"content-length\\":\\"82\\"}"}',
);
});
it('httpResponseHeaders should work', async () => {
const yoga = createYoga({
schema,
plugins: [
usePrometheus({
http: true,
httpResponseHeaders: true,
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();
expect(metrics).toContain('graphql_yoga_http_duration_bucket');
expect(metrics).toContain('operationName="TestProm"');
expect(metrics).toContain('operationType="query"');
expect(metrics).toContain('url="http://localhost:4000/graphql"');
expect(metrics).not.toContain('operationName="TestProm"');
expect(metrics).not.toContain('operationType="query"');
expect(metrics).toContain('method="POST"');
expect(metrics).toContain('statusCode="200"');
expect(metrics).toContain('statusText="OK"');
expect(metrics).toContain(
`responseHeaders="{\\"content-type\\":\\"application/json; charset=utf-8\\",\\"content-length\\":\\"33\\"}"}`,
);
});

it('endpoint should work', async () => {
const yoga = createYoga({
schema,
Expand Down Expand Up @@ -162,9 +119,7 @@ describe('Prometheus', () => {
expect(metrics).toContain('graphql_yoga_http_duration_bucket');
expect(metrics).toContain('operationName="TestProm"');
expect(metrics).toContain('operationType="query"');
expect(metrics).toContain('url="http://localhost:4000/graphql"');
expect(metrics).toContain('method="POST"');
expect(metrics).toContain('statusCode="200"');
expect(metrics).toContain('statusText="OK"');
});
});
4 changes: 2 additions & 2 deletions website/src/pages/docs/integrations/integration-with-bun.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,8 @@ const yoga = createYoga({
})

const server = Bun.serve({
fetch: yoga,
});
fetch: yoga
})

console.info(
`Server is running on ${new URL(
Expand Down

0 comments on commit 7dc37e6

Please sign in to comment.