Files
speckle-server/packages/server/logging/apolloPlugin.js
T
Kristaps Fabians Geikins 83d8035dc2 chore: upgrade to eslint 9 (#2348)
* root + server

* frontend

* frontend-2

* dui3

* dui3

* tailwind theme

* ui-components

* preview service

* viewer

* viewer-sandbox

* fileimport-service

* webhook service

* objectloader

* shared

* ui-components-nuxt

* WIP full config

* WIP full linter

* eslint projectwide util

* minor fix

* removing redundant ci

* clean up test errors

* fixed prettier formatting

* CI improvements

* TSC lint fix

* 'buildBatch' needs to be async since some batch types (like Text) require it. Removed a disabled liniting rule from ObjLoader

* removed unnecessary void

---------

Co-authored-by: AlexandruPopovici <alexandrupopoviciioan@gmail.com>
2024-06-12 14:38:02 +03:00

121 lines
3.9 KiB
JavaScript

/* eslint-disable camelcase */
/* istanbul ignore file */
const Sentry = require('@sentry/node')
const { ApolloError } = require('apollo-server-express')
const prometheusClient = require('prom-client')
const { graphqlLogger } = require('@/logging/logging')
const { redactSensitiveVariables } = require('@/logging/loggingHelper')
const { GraphQLError } = require('graphql')
const metricCallCount = new prometheusClient.Counter({
name: 'speckle_server_apollo_calls',
help: 'Number of calls',
labelNames: ['actionName']
})
/** @type {import('apollo-server-core').PluginDefinition} */
module.exports = {
// eslint-disable-next-line no-unused-vars
requestDidStart(ctx) {
const apolloRequestStart = Date.now()
return {
didResolveOperation(ctx) {
let logger = ctx.context.log || graphqlLogger
const auth = ctx.context
const userId = auth?.userId
const op = `GQL ${ctx.operation.operation} ${ctx.operation.selectionSet.selections[0].name.value}`
const name = `GQL ${ctx.operation.selectionSet.selections[0].name.value}`
const kind = ctx.operation.operation
const query = ctx.request.query
const variables = ctx.request.variables
logger = logger.child({
graphql_operation_kind: kind,
graphql_query: query,
graphql_variables: redactSensitiveVariables(variables),
graphql_operation_value: op,
graphql_operation_name: name,
userId
})
const transaction = Sentry.startTransaction({
op,
name
})
try {
const actionName = `${ctx.operation.operation} ${ctx.operation.selectionSet.selections[0].name.value}`
logger = logger.child({ actionName })
metricCallCount.labels(actionName).inc()
// logger.debug(actionName)
} catch (e) {
Sentry.captureException(e)
}
Sentry.configureScope((scope) => scope.setSpan(transaction))
ctx.request.transaction = transaction
ctx.context.log = logger
},
didEncounterErrors(ctx) {
let logger = ctx.context.log || graphqlLogger
logger = logger.child({
apollo_query_duration_ms: Date.now() - apolloRequestStart
})
for (const err of ctx.errors) {
const operationName = ctx.request.operationName || null
const query = ctx.request.query
const variables = ctx.request.variables
if (err.path) {
logger = logger.child({ 'query-path': err.path.join(' > ') })
}
if (
(err instanceof GraphQLError && err.extensions?.code === 'FORBIDDEN') ||
err instanceof ApolloError
) {
logger.info(
{ err },
'{graphql_operation_value} failed after {apollo_query_duration_ms} ms'
)
} else {
logger.error(
err,
'{graphql_operation_value} failed after {apollo_query_duration_ms} ms'
)
}
Sentry.withScope((scope) => {
scope.setTag('operationName', operationName)
scope.setExtra('query', query)
scope.setExtra('variables', variables)
if (err.path) {
// We can also add the path as breadcrumb
scope.addBreadcrumb({
category: 'query-path',
message: err.path.join(' > '),
level: Sentry.Severity.Debug
})
}
Sentry.captureException(err)
})
}
},
willSendResponse(ctx) {
const logger = ctx.context.log || graphqlLogger
if (ctx.request.transaction) {
ctx.request.transaction.finish()
}
logger.info(
{
apollo_query_duration_ms: Date.now() - apolloRequestStart
},
'{graphql_operation_value} finished after {apollo_query_duration_ms} ms'
)
}
}
}
}