Skip to content

Commit

Permalink
feat(opentelemetry-node): add @opentelemetry/instrumentation-graphql (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
david-luna authored Jan 24, 2025
1 parent 0f835fe commit afa684a
Show file tree
Hide file tree
Showing 8 changed files with 208 additions and 2 deletions.
1 change: 1 addition & 0 deletions packages/opentelemetry-node/docs/supported-technologies.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ requires:
| `@opentelemetry/instrumentation-express` | `express` version range `^4.0.0` | [README](https://github.com/open-telemetry/opentelemetry-js-contrib/tree/main/plugins/node/opentelemetry-instrumentation-express#readme) |
| `@opentelemetry/instrumentation-fastify` | `fastify` version range `>=3 <5` | [README](https://github.com/open-telemetry/opentelemetry-js-contrib/tree/main/plugins/node/opentelemetry-instrumentation-fastify#readme) |
| `@opentelemetry/instrumentation-generic-pool` | `generic-pool` version range `2 - 2.3, ^2.4, >=3` | [README](https://github.com/open-telemetry/opentelemetry-js-contrib/tree/main/plugins/node/opentelemetry-instrumentation-generic-pool#readme) |
| `@opentelemetry/instrumentation-graphql` | `graphql` version range `>=14.0.0 <17` | [README](https://github.com/open-telemetry/opentelemetry-js-contrib/tree/main/plugins/node/opentelemetry-instrumentation-graphql#readme) |
| `@opentelemetry/instrumentation-grpc` | `@grpc/grpc-js` version range `^1.0.0` | [README](https://github.com/open-telemetry/opentelemetry-js/tree/main/experimental/packages/opentelemetry-instrumentation-grpc#readme) |
| `@opentelemetry/instrumentation-hapi` | `@hapi/hapi >=17.0.0 <21` | [README](https://github.com/open-telemetry/opentelemetry-js-contrib/tree/main/plugins/node/opentelemetry-instrumentation-hapi#readme) |
| `@opentelemetry/instrumentation-http` | `http` module for Node.js `>=14` | [README](https://github.com/open-telemetry/opentelemetry-js/tree/main/experimental/packages/opentelemetry-instrumentation-http#readme) |
Expand Down
3 changes: 3 additions & 0 deletions packages/opentelemetry-node/lib/instrumentations.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
* "@opentelemetry/instrumentation-express": import('@opentelemetry/instrumentation-express').ExpressInstrumentationConfig | InstrumentationFactory,
* "@opentelemetry/instrumentation-fastify": import('@opentelemetry/instrumentation-fastify').FastifyInstrumentationConfig | InstrumentationFactory,
* "@opentelemetry/instrumentation-generic-pool": import('@opentelemetry/instrumentation').InstrumentationConfig | InstrumentationFactory,
* "@opentelemetry/instrumentation-graphql": import('@opentelemetry/instrumentation-graphql').GraphQLInstrumentation | InstrumentationFactory,
* "@opentelemetry/instrumentation-grpc": import('@opentelemetry/instrumentation-grpc').GrpcInstrumentationConfig | InstrumentationFactory,
* "@opentelemetry/instrumentation-hapi": import('@opentelemetry/instrumentation').InstrumentationConfig | InstrumentationFactory,
* "@opentelemetry/instrumentation-http": import('@opentelemetry/instrumentation-http').HttpInstrumentationConfig | InstrumentationFactory,
Expand Down Expand Up @@ -73,6 +74,7 @@ const {DnsInstrumentation} = require('@opentelemetry/instrumentation-dns');
const {ExpressInstrumentation} = require('@opentelemetry/instrumentation-express');
const {FastifyInstrumentation} = require('@opentelemetry/instrumentation-fastify');
const {GenericPoolInstrumentation} = require('@opentelemetry/instrumentation-generic-pool');
const {GraphQLInstrumentation} = require('@opentelemetry/instrumentation-graphql');
const {GrpcInstrumentation} = require('@opentelemetry/instrumentation-grpc');
const {HapiInstrumentation} = require('@opentelemetry/instrumentation-hapi');
const {HttpInstrumentation} = require('@opentelemetry/instrumentation-http');
Expand Down Expand Up @@ -121,6 +123,7 @@ const INSTRUMENTATIONS = {
'@opentelemetry/instrumentation-express': (cfg) => new ExpressInstrumentation(cfg),
'@opentelemetry/instrumentation-fastify': (cfg) => new FastifyInstrumentation(cfg),
'@opentelemetry/instrumentation-generic-pool': (cfg) => new GenericPoolInstrumentation(cfg),
'@opentelemetry/instrumentation-graphql': (cfg) => new GraphQLInstrumentation(cfg),
'@opentelemetry/instrumentation-grpc': (cfg) => new GrpcInstrumentation(cfg),
'@opentelemetry/instrumentation-hapi': (cfg) => new HapiInstrumentation(cfg),
'@opentelemetry/instrumentation-http': (cfg) => new HttpInstrumentation(cfg),
Expand Down
41 changes: 41 additions & 0 deletions packages/opentelemetry-node/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions packages/opentelemetry-node/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@
"@opentelemetry/instrumentation-express": "^0.47.0",
"@opentelemetry/instrumentation-fastify": "^0.44.0",
"@opentelemetry/instrumentation-generic-pool": "^0.43.0",
"@opentelemetry/instrumentation-graphql": "^0.47.0",
"@opentelemetry/instrumentation-grpc": "^0.57.1",
"@opentelemetry/instrumentation-hapi": "^0.45.1",
"@opentelemetry/instrumentation-http": "^0.57.1",
Expand Down Expand Up @@ -137,6 +138,7 @@
"dotenv": "^16.4.5",
"express": "^4.21.0",
"fastify": "^5.1.0",
"graphql": "^16.10.0",
"ioredis": "^5.4.1",
"module-details-from-path": "^1.0.3",
"mongodb": "^6.9.0",
Expand Down
79 changes: 79 additions & 0 deletions packages/opentelemetry-node/test/fixtures/use-graphql.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

// Usage: node -r @elastic/opentelemetry-node use-graphql.js
//
const otel = require('@opentelemetry/api');
const graphql = require('graphql');

async function main() {
const {schema, source} = setup();
const result = await graphql.graphql({schema, source});
console.dir(result, {depth: 9});
}

const tracer = otel.trace.getTracer('test');
tracer.startActiveSpan('manual-parent-span', async (span) => {
await main();
span.end();
});

// hrlper functions
function setup() {
const Todo = new graphql.GraphQLObjectType({
name: 'Todo',
fields: {
id: {
type: graphql.GraphQLString,
resolve(obj, args) {
return obj.id;
},
},
desc: {
type: graphql.GraphQLString,
resolve(obj, args) {
return obj.desc;
},
},
},
});
const query = new graphql.GraphQLObjectType({
name: 'Query',
fields: {
todo: {
type: Todo,
args: {
id: {type: graphql.GraphQLInt},
},
resolve(obj, args, context) {
return Promise.resolve({id: args.id, desc: 'todo desc'});
},
},
},
});
const schema = new graphql.GraphQLSchema({query});
const source = `
query {
todo (id: 0) {
desc
}
}
`;
return {schema, source};
}
65 changes: 65 additions & 0 deletions packages/opentelemetry-node/test/instr-graphql.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

// Test that instrumentation-graphql generates the telemetry we expect.

const test = require('tape');
const {runTestFixtures} = require('./testutils');

/** @type {import('./testutils').TestFixture[]} */
const testFixtures = [
{
name: 'use-graphql',
args: ['./fixtures/use-graphql.js'],
cwd: __dirname,
env: {
NODE_OPTIONS: '--require=@elastic/opentelemetry-node',
},
// verbose: true,
checkTelemetry: (t, col) => {
// // We expect spans like this
// ------ trace 41f7ac (6 spans) ------
// span 49cb5c "manual-parent-span" (17.9ms, SPAN_KIND_INTERNAL)
// +3ms `- span 9428d5 "graphql.parse" (1.3ms, SPAN_KIND_INTERNAL)
// +2ms `- span 3e6ca5 "graphql.validate" (3.2ms, SPAN_KIND_INTERNAL)
// +3ms `- span 7f1bb6 "query" (9.5ms, SPAN_KIND_INTERNAL)
// +1ms `- span 95a0e1 "graphql.resolve todo" (3.8ms, SPAN_KIND_INTERNAL)
// +4ms `- span a4ec19 "graphql.resolve todo.desc" (0.0ms, SPAN_KIND_INTERNAL)
const spans = col.sortedSpans.slice(1);
t.equal(spans.length, 5);

t.ok(
spans.every(
(s) => s.scope.name,
'@opentelemetry/instrumentation-graphql'
)
);
t.equal(spans[0].name, 'graphql.parse');
t.equal(spans[1].name, 'graphql.validate');
t.equal(spans[2].name, 'query');
t.equal(spans[3].name, 'graphql.resolve todo');
t.equal(spans[4].name, 'graphql.resolve todo.desc');
},
},
];

test('graphql instrumentation', (suite) => {
runTestFixtures(suite, testFixtures);
suite.end();
});
18 changes: 16 additions & 2 deletions packages/opentelemetry-node/test/testutils.js
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,9 @@ function assertDeepMatch(t, actual, expected, msgPrefix = 'obj') {
* Filter out instr-dns and instr-net spans for testing.
* Eventually it would be preferable to have each test run with instr-dns
* and instr-net turned off, if that is what they want to test.
*
* @param {CollectedSpan[]} spans
* @returns {CollectedSpan[]}
*/
function filterOutDnsNetSpans(spans) {
// Filter out instr-dns and instr-net spans for testing.
Expand Down Expand Up @@ -311,7 +314,18 @@ function quoteEnv(env) {
*/

/**
* @typedef {import('@opentelemetry/api').Span} Span
* @typedef {Object} CollectedSpan
* @property {string} traceId
* @property {string} spanId
* @property {string} parentSpanId
* @property {string} name
* @property {string} kind
* @property {string} startTimeUnixNano
* @property {string} endTimeUnixNano
* @property {Record<string, any>} attributes
* @property {{ code: string }} status // TODO: use a type instead of a string
* @property {{ attributes: Record<string, any> }} resource
* @property {{ name: string; version: string }} scope
*/

/**
Expand All @@ -326,7 +340,7 @@ function quoteEnv(env) {
* TODO: Likewise for this Span type.
*
* @typedef {Object} CollectorStore
* @property {Span[]} sortedSpans
* @property {CollectedSpan[]} sortedSpans
* @property {CollectedMetric[]} metrics
* @property {import('@opentelemetry/api-logs').LogRecord[]} logs
*/
Expand Down
1 change: 1 addition & 0 deletions packages/opentelemetry-node/types/instrumentations.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export type InstrumentaionsMap = {
"@opentelemetry/instrumentation-express": import('@opentelemetry/instrumentation-express').ExpressInstrumentationConfig | InstrumentationFactory;
"@opentelemetry/instrumentation-fastify": import('@opentelemetry/instrumentation-fastify').FastifyInstrumentationConfig | InstrumentationFactory;
"@opentelemetry/instrumentation-generic-pool": import('@opentelemetry/instrumentation').InstrumentationConfig | InstrumentationFactory;
"@opentelemetry/instrumentation-graphql": import('@opentelemetry/instrumentation-graphql').GraphQLInstrumentation | InstrumentationFactory;
"@opentelemetry/instrumentation-grpc": import('@opentelemetry/instrumentation-grpc').GrpcInstrumentationConfig | InstrumentationFactory;
"@opentelemetry/instrumentation-hapi": import('@opentelemetry/instrumentation').InstrumentationConfig | InstrumentationFactory;
"@opentelemetry/instrumentation-http": import('@opentelemetry/instrumentation-http').HttpInstrumentationConfig | InstrumentationFactory;
Expand Down

0 comments on commit afa684a

Please sign in to comment.