-
Notifications
You must be signed in to change notification settings - Fork 518
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(instrumentation-graphql): change execute and parse span names and attributes #747
Changes from all commits
be7a970
f78ec52
ccc0354
f5bd5d9
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -39,12 +39,12 @@ import { | |
GraphQLInstrumentationParsedConfig, | ||
OtelExecutionArgs, | ||
ObjectWithGraphQLData, | ||
OPERATION_NOT_SUPPORTED, | ||
Maybe, | ||
} from './types'; | ||
import { | ||
addInputVariableAttributes, | ||
addSpanSource, | ||
createExecuteSpanName, | ||
endSpan, | ||
getOperation, | ||
wrapFieldResolver, | ||
|
@@ -390,29 +390,46 @@ export class GraphQLInstrumentation extends InstrumentationBase { | |
}); | ||
} | ||
|
||
private getOperationType( | ||
operation: graphqlTypes.DefinitionNode | undefined | ||
): string | undefined { | ||
if (operation?.kind === 'OperationDefinition') { | ||
return operation.operation; | ||
} | ||
|
||
return undefined; | ||
} | ||
|
||
private getOperationName( | ||
operation: graphqlTypes.DefinitionNode | undefined, | ||
processedArgs: graphqlTypes.ExecutionArgs | ||
): string | undefined { | ||
if (operation?.kind === 'OperationDefinition') { | ||
if (operation.name) { | ||
return operation.name.value; | ||
} | ||
} | ||
|
||
return processedArgs.operationName ?? undefined; | ||
} | ||
|
||
private _createExecuteSpan( | ||
operation: graphqlTypes.DefinitionNode | undefined, | ||
processedArgs: graphqlTypes.ExecutionArgs | ||
): api.Span { | ||
const config = this._getConfig(); | ||
|
||
const span = this.tracer.startSpan(SpanNames.EXECUTE, {}); | ||
if (operation) { | ||
const name = (operation as graphqlTypes.OperationDefinitionNode) | ||
.operation; | ||
if (name) { | ||
span.setAttribute(AttributeNames.OPERATION, name); | ||
} | ||
} else { | ||
let operationName = ' '; | ||
if (processedArgs.operationName) { | ||
operationName = ` "${processedArgs.operationName}" `; | ||
} | ||
operationName = OPERATION_NOT_SUPPORTED.replace( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I have removed this, as i don't believe it's needed to be presented in such a way. Maybe we should rather add an attribute that would represent that the operation is "not supported"? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. why do you think so ? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That is true, but making I would stick with "opertationName" to represent the name so the value is more predictable. Especially if you want to use it with some automation, where with current implementation you would have to parse the string to get out the actual operation name. Maybe adding |
||
'$operationName$', | ||
operationName | ||
); | ||
span.setAttribute(AttributeNames.OPERATION, operationName); | ||
const operationType = this.getOperationType(operation); | ||
const operationName = this.getOperationName(operation, processedArgs); | ||
|
||
const span = this.tracer.startSpan(createExecuteSpanName(operationType)); | ||
|
||
if (operationType) { | ||
span.setAttribute(AttributeNames.OPERATION_TYPE, operationType); | ||
} | ||
|
||
if (operationName) { | ||
span.setAttribute(AttributeNames.OPERATION_NAME, operationName); | ||
} | ||
|
||
if (processedArgs.document?.loc) { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -120,7 +120,7 @@ function createResolverSpan( | |
}; | ||
|
||
const span = tracer.startSpan( | ||
SpanNames.RESOLVE, | ||
createResolveSpanName(info.fieldName), | ||
{ | ||
attributes, | ||
}, | ||
|
@@ -433,3 +433,16 @@ async function safeExecuteInTheMiddleAsync<T>( | |
return result as T; | ||
} | ||
} | ||
|
||
export const createResolveSpanName = (fieldName: string): string => { | ||
return `${SpanNames.RESOLVE}.${fieldName}`; | ||
}; | ||
|
||
export const createExecuteSpanName = ( | ||
operationType: string | undefined | ||
): string => { | ||
if (operationType) { | ||
return `${SpanNames.EXECUTE}.${operationType}`; | ||
} | ||
return `${SpanNames.EXECUTE}`; | ||
}; | ||
Comment on lines
+437
to
+448
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What's your motivation in adding low-cardinality (field name) and unbounded-cardinality (operation name) data to these spans' names? I'm concerned that this change will make queries on these span names harder, since these names would now have two jobs: convey the span "type" + convey another aspect of the span. E.g., it's harder to refer to all the "resolver" spans. The added data is present in other attributes; is that not sufficient? Each attribute describing a single characteristic of the span? If one wished to do a query akin to how "GROUP BY name" would be under these changes, one would do "GROUP BY name, field_name". There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Apologies; just saw the linked issue. Commented there. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I believe it's adding more visibility (without looking at attributes) at what part of "graphql resolving" does the span belongs to. Same as for HTTP there isn't just "http.resolving" but it includes method (GET, POST etc.) and also the "route" such as https://opentelemetry.io/docs/reference/specification/trace/semantic_conventions/http/#name |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this specified?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't understand what you mean @dyladan.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@mentos1386 I suspect @dyladan is asking whether there are OpenTelemetry semantic conventions for GraphQL trace data. (see here for what that means if you're unsure: #705 (comment))
If so, the answer is "not yet"; the specification effort is underway here: open-telemetry/opentelemetry-specification#1670
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes @spencerwilson is correct that's exactly what I meant. In general we hesitate to add/change attributes without semantic conventions, but in this case it seems like the spec is moving quite slowly.