Skip to content

Commit

Permalink
Merge pull request #58 from nuxt-community/request-tracking
Browse files Browse the repository at this point in the history
fix: properly track ssr request and errors with  nitro
  • Loading branch information
aldarund authored Oct 27, 2022
2 parents d65656b + 899d935 commit 11c9220
Show file tree
Hide file tree
Showing 5 changed files with 94 additions and 16 deletions.
11 changes: 10 additions & 1 deletion lib/module.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@ const { fileURLToPath } = require('url')
const AppInsights = require('applicationinsights')
const deepMerge = require('deepmerge')

const { defineNuxtModule, getNuxtVersion, logger, addTemplate, addPluginTemplate, hasNuxtCompatibility } = require('@nuxt/kit')
const {
defineNuxtModule, logger, addTemplate, addPluginTemplate, hasNuxtCompatibility,
addServerHandler, createResolver
} = require('@nuxt/kit')
const { requestHandler, errorHandler } = require('./serverHandlers')

const DEFAULTS = {
Expand Down Expand Up @@ -62,6 +65,8 @@ module.exports = defineNuxtModule({
// Register the server plugin
if (!options.disableServerSide) {
if (await hasNuxtCompatibility({ bridge: true })) {
const runtimeDir = fileURLToPath(new URL('./runtime', import.meta.url))
const { resolve } = createResolver(runtimeDir)
addTemplate({
src: path.resolve(__dirname, 'runtime/appinsights.init.js'),
fileName: 'appinsights.init.js'
Expand All @@ -72,6 +77,10 @@ module.exports = defineNuxtModule({
mode: 'server',
options
})
addServerHandler({
middleware: true,
handler: resolve('./middleware.js')
})
nuxt.hook('nitro:config', (config) => {
// Add a nitro plugin that will run the validator for us on each request
config.plugins = config.plugins || []
Expand Down
36 changes: 33 additions & 3 deletions lib/runtime/appinsights.init.js
Original file line number Diff line number Diff line change
@@ -1,20 +1,50 @@
import * as AppInsights from 'applicationinsights'

let initialized = false
const appInsights = AppInsights.default ? AppInsights.default : AppInsights

const getClient = (options, runtimeConfig) => {
if (!initialized) {
const runtimeConfigLocal = { ...(runtimeConfig.public.appInsights || {}), ...(runtimeConfig.appInsights || {}) }
const mergedConfig = { ...options, ...runtimeConfigLocal }
const appInsightsServer = AppInsights.setup(mergedConfig.serverConnectionString)
const appInsightsServer = appInsights.setup(mergedConfig.serverConnectionString)
for (const [key, value] of Object.entries(mergedConfig.serverConfig)) {
AppInsights.defaultClient.config[key] = value
appInsights.defaultClient.config[key] = value
}
if (mergedConfig.initialize) {
appInsightsServer.start()
}
initialized = true
}
return AppInsights.default ? AppInsights.default.defaultClient : AppInsights.defaultClient
return appInsights.defaultClient
}

const addContextTelemetry = () => {
appInsights.defaultClient.addTelemetryProcessor(
(envelope, context) => {
const setContextKey = (key, value) => {
envelope.tags[key] = value
}
const getContextKey = (key) => {
return appInsights.defaultClient.context.keys[key]
}

// custom context that I set on per-request basis
const requestContext = context.authData || {}

for (const [key, value] of Object.entries(requestContext)) {
if (value) {
setContextKey(
getContextKey(key),
value
)
}
}

return true
}
)
}

export default getClient
export { appInsights, addContextTelemetry }
28 changes: 20 additions & 8 deletions lib/runtime/errorHandler.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,27 @@
import getClient from './appinsights.init'
import getClient, { appInsights } from './appinsights.init'
import defaultErrorHandler from '#existing-error-handler'
import { useRuntimeConfig } from '#imports'
import { useRuntimeConfig, useCookies } from '#imports'
import options from '#appinsights-config'

export default function (error, event) {
const client = getClient(options, useRuntimeConfig())
client.trackException({
exception: error,
properties: {
headers: event.req.headers
}
})
const correlationContext = event.req.context.correlationContext
const cookies = useCookies(event)
appInsights.wrapWithCorrelationContext(() => {
client.trackException({
exception: error,
properties: {
headers: event.req.headers,
url: event.req.headers.host + event.req.url
},
contextObjects: {
authData: {
userId: cookies.ai_user ? cookies.ai_user.split('|')[0] : undefined,
sessionId: cookies.ai_session,
userAuthUserId: event.req.context.userAuthUserId
}
}
})
}, correlationContext)()
defaultErrorHandler(error, event)
}
8 changes: 8 additions & 0 deletions lib/runtime/middleware.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { defineEventHandler } from 'h3'
import { appInsights } from './appinsights.init'

export default defineEventHandler((event) => {
const correlationContext = appInsights.startOperation(event.req)
event.req.context.startTime = Date.now()
event.req.context.correlationContext = correlationContext
})
27 changes: 23 additions & 4 deletions lib/runtime/nitro.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,33 @@
import getClient from './appinsights.init'
import { useRuntimeConfig } from '#imports'
import getClient, { appInsights, addContextTelemetry } from './appinsights.init'
import { useRuntimeConfig, useCookies } from '#imports'
import options from '#appinsights-config'

export default function (nitro) {
const client = getClient(options, useRuntimeConfig())
addContextTelemetry()
nitro.hooks.hook('render:response', (response, { event }) => {
if (typeof response.body === 'string' && (response.headers['Content-Type'] || response.headers['content-type'])?.includes('html')) {
// We deliberately do not await so as not to block the response
// appInsightsClient.trackRequest({ name: `${event.req.method} ${event.req.url}`, url: event.req.url, resultCode: response.statusCode, success: true, duration: 0 })
client.trackNodeHttpRequest({ request: event.req, response })
const cookies = useCookies(event)
const correlationContext = event.req.context.correlationContext
appInsights.wrapWithCorrelationContext(() => {
// console.log('ssss', appInsights.getCorrelationContext())
client.trackRequest({
name: `${event.req.method} ${event.req.url}`,
url: event.req.url,
resultCode: response.statusCode,
success: response.statusCode === 200,
duration: Date.now() - event.req.context.startTime,
id: correlationContext.operation.parentId,
contextObjects: {
authData: {
userId: cookies.ai_user ? cookies.ai_user.split('|')[0] : undefined,
userAuthUserId: event.req.context.userAuthUserId,
sessionId: cookies.ai_session
}
}
})
}, correlationContext)()
}
})
}

0 comments on commit 11c9220

Please sign in to comment.