Skip to content

Commit

Permalink
fix: nuxt bridge support via nitro
Browse files Browse the repository at this point in the history
  • Loading branch information
aldarund committed Oct 18, 2022
1 parent 1b69f4a commit 82d4d92
Show file tree
Hide file tree
Showing 7 changed files with 3,368 additions and 147 deletions.
184 changes: 104 additions & 80 deletions lib/module.js
Original file line number Diff line number Diff line change
@@ -1,96 +1,120 @@
const path = require('path')
const deepMerge = require('deepmerge')
const { fileURLToPath } = require('url')
const AppInsights = require('applicationinsights')
const logger = require('consola').withScope('nuxt:appInsights')
const { requestHandler, errorHandler } = require('./serverHandlers')

module.exports = function appInsights (moduleOptions) {
const defaults = {
instrumentationKey: process.env.APPINSIGHTS_INSTRUMENTATION_KEY || false,
serverConnectionString: process.env.APPINSIGHTS_CONNECTION_STRING || false,
disabled: process.env.APPINSIGHTS_DISABLED || false,
initialize: process.env.APPINSIGHTS_INITIALIZE || true,
disableClientSide: process.env.APPINSIGHTS_DISABLE_CLIENT_SIDE || false,
disableServerSide: process.env.APPINSIGHTS_DISABLE_SERVER_SIDE || false,
trackPageView: process.env.APPINSIGHTS_TRACK_PAGE_VIEW || true,
serverConfig: {},
clientConfig: {}
}
const { nuxt } = this
const deepMerge = require('deepmerge')

const publicRuntimeConfig = (nuxt.options.publicRuntimeConfig || {}).appInsights || {}
const { defineNuxtModule, getNuxtVersion, logger, addTemplate, addPluginTemplate, hasNuxtCompatibility } = require('@nuxt/kit')
const { requestHandler, errorHandler } = require('./serverHandlers')

const topLevelOptions = this.options.appInsights || {}
const options = deepMerge.all([defaults, topLevelOptions, moduleOptions, publicRuntimeConfig])
const DEFAULTS = {
instrumentationKey: process.env.APPINSIGHTS_INSTRUMENTATION_KEY || false,
serverConnectionString: process.env.APPINSIGHTS_CONNECTION_STRING || false,
disabled: process.env.APPINSIGHTS_DISABLED || false,
initialize: process.env.APPINSIGHTS_INITIALIZE || true,
disableClientSide: process.env.APPINSIGHTS_DISABLE_CLIENT_SIDE || false,
disableServerSide: process.env.APPINSIGHTS_DISABLE_SERVER_SIDE || false,
trackPageView: process.env.APPINSIGHTS_TRACK_PAGE_VIEW || true,
serverConfig: {},
clientConfig: {}
}
module.exports = defineNuxtModule({
meta: {
name: '@nuxtjs/applicationinsights',
configKey: 'appInsights',
compatibility: {
nuxt: '^2.0.0 || ^3.0.0-rc.7'
}
},
defaults: DEFAULTS,
async setup (_options, nuxt) {
const options = deepMerge.all([_options, (nuxt.options.runtimeConfig ? nuxt.options.runtimeConfig.public.appInsights : (nuxt.options.publicRuntimeConfig || {}).appInsights) || {}, (nuxt.options.runtimeConfig ? nuxt.options.runtimeConfig.appInsights : (nuxt.options.privateRuntimeConfig || {}).appInsights) || {}])
if (options.disabled) {
logger.info('Errors will not be logged because the disable option has been set')
return
}

if (options.disabled) {
logger.info('Errors will not be logged because the disable option has been set')
return
}
if (!options.instrumentationKey) {
logger.info('Errors will not be logged because no instrumentationKey has been provided')
return
}

if (!options.instrumentationKey) {
logger.info('Errors will not be logged because no instrumentationKey has been provided')
return
}
// Register the client plugin
if (!options.disableClientSide) {
addTemplate({
src: path.resolve(__dirname, 'appinsights-vue.js'),
fileName: 'appinsights-vue.js'
})
addPluginTemplate({
src: path.resolve(__dirname, 'appinsights.client.js'),
fileName: 'appinsights.client.js',
mode: 'client',
options: {
config: {
instrumentationKey: options.instrumentationKey,
...options.clientConfig
},
initialize: options.initialize,
trackPageView: options.trackPageView
}
})
}

// Register the client plugin
if (!options.disableClientSide) {
this.addTemplate({
src: path.resolve(__dirname, 'appinsights-vue.js'),
fileName: 'appinsights-vue.js'
})
this.addPlugin({
src: path.resolve(__dirname, 'appinsights.client.js'),
fileName: 'appinsights.client.js',
mode: 'client',
options: {
config: {
instrumentationKey: options.instrumentationKey,
...options.clientConfig
},
initialize: options.initialize,
trackPageView: options.trackPageView
// Register the server plugin
if (!options.disableServerSide) {
if (!options.serverConnectionString) {
logger.info('Server errors will not be logged because no serverConnectionString provided')
return
}
})
}

// Register the server plugin
if (!options.disableServerSide) {
const privateRuntimeConfig = (nuxt.options.privateRuntimeConfig || {}).appInsights || {}
if (await hasNuxtCompatibility({ bridge: true })) {
addTemplate({
src: path.resolve(__dirname, 'runtime/appinsights.init.js'),
fileName: 'appinsights.init.js'
})
addPluginTemplate({
src: path.resolve(__dirname, 'runtime/appinsights.server.js'),
fileName: 'appinsights.server.js',
mode: 'server',
options
})
nuxt.hook('nitro:config', (config) => {
// Add a nitro plugin that will run the validator for us on each request
config.plugins = config.plugins || []
config.plugins.push(fileURLToPath(new URL('./runtime/nitro', import.meta.url)))
config.virtual = config.virtual || {}
config.virtual['#appinsights-config'] = `export default ${JSON.stringify(options)}`
config.virtual['#existing-error-handler'] = `import errorHandler from '${config.errorHandler}';export default errorHandler`
config.errorHandler = fileURLToPath(new URL('./runtime/errorHandler', import.meta.url))
})
} else {
const appInsightsServer = AppInsights.setup(options.serverConnectionString)

const optionsServer = deepMerge.all([options, privateRuntimeConfig])
// Initialize AppInsights
for (const [key, value] of Object.entries(options.serverConfig)) {
AppInsights.defaultClient.config[key] = value
}

if (!optionsServer.serverConnectionString) {
logger.info('Server errors will not be logged because no serverConnectionString provided')
return
}
if (options.initialize) {
appInsightsServer.start()
}
const appInsightsClient = AppInsights.defaultClient

const appInsightsServer = AppInsights.setup(optionsServer.serverConnectionString)
logger.success('Started logging errors to AppInsights')

// Initialize AppInsights
for (const [key, value] of Object.entries(optionsServer.serverConfig)) {
AppInsights.defaultClient.config[key] = value
}
addPluginTemplate({
src: path.resolve(__dirname, 'appinsights.server.js'),
fileName: 'appinsights.server.js',
mode: 'server'
})

if (optionsServer.initialize) {
appInsightsServer.start()
nuxt.hook('render:setupMiddleware', app => app.use(requestHandler(appInsightsClient)))
nuxt.hook('render:errorMiddleware', app => app.use(errorHandler(appInsightsClient)))
nuxt.hook('generate:routeFailed', ({ errors }) => {
errors.forEach(({ error }) => {
appInsightsClient.trackException({ exception: error })
})
})
}
}
const appInsightsClient = AppInsights.defaultClient

logger.success('Started logging errors to AppInsights')

this.addPlugin({
src: path.resolve(__dirname, 'appinsights.server.js'),
fileName: 'appinsights.server.js',
mode: 'server'
})

this.nuxt.hook('render:setupMiddleware', app => app.use(requestHandler(appInsightsClient)))
this.nuxt.hook('render:errorMiddleware', app => app.use(errorHandler(appInsightsClient)))
this.nuxt.hook('generate:routeFailed', ({ errors }) => {
errors.forEach(({ error }) => {
appInsightsClient.trackException({ exception: error })
})
})
}
}
})
20 changes: 20 additions & 0 deletions lib/runtime/appinsights.init.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import * as AppInsights from 'applicationinsights'

let initialized = false

const getClient = (options, runtimeConfig) => {
if (!initialized) {
const runtimeConfigLocal = runtimeConfig.public.appInsights || {}
const mergedConfig = { ...options, ...runtimeConfigLocal }
const appInsightsServer = AppInsights.setup(mergedConfig.serverConnectionString)
for (const [key, value] of Object.entries(mergedConfig.serverConfig)) {
AppInsights.defaultClient.config[key] = value
}
if (mergedConfig.initialize) {
appInsightsServer.start()
}
initialized = true
}
return AppInsights.default ? AppInsights.default.defaultClient : AppInsights.defaultClient
}
export default getClient
8 changes: 8 additions & 0 deletions lib/runtime/appinsights.server.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { useRuntimeConfig } from '#imports'
import getClient from './appinsights.init'

export default defineNuxtPlugin(nuxtApp => {
const client = getClient(<%= serialize(options) %>, useRuntimeConfig())
nuxtApp.provide('injected', () => 'my injected function')
// now available on `nuxtApp.$injected`
})
15 changes: 15 additions & 0 deletions lib/runtime/errorHandler.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import getClient from './appinsights.init'
import defaultErrorHandler from '#existing-error-handler'
import { useRuntimeConfig } 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
}
})
defaultErrorHandler(error, event)
}
14 changes: 14 additions & 0 deletions lib/runtime/nitro.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import getClient from './appinsights.init'
import { useRuntimeConfig } from '#imports'
import options from '#appinsights-config'

export default function (nitro) {
const client = getClient(options, useRuntimeConfig())
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 })
}
})
}
7 changes: 4 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,16 +22,17 @@
"nuxt": "node --inspect=0.0.0.0 node_modules/nuxt-edge/bin/nuxt test/fixture"
},
"dependencies": {
"applicationinsights": "^2.3.3",
"@microsoft/applicationinsights-web": "^2.8.5",
"consola": "^2.15.0",
"@nuxt/kit": "^3.0.0-rc.10",
"applicationinsights": "^2.3.5",
"@microsoft/applicationinsights-web": "^2.8.7",
"deepmerge": "^4.2.2"
},
"devDependencies": {
"@nuxtjs/eslint-config": "^6.0.1",
"codecov": "^3.8.2",
"eslint": "^7.28.0",
"jest": "^26.6.3",
"nuxt": "npm:[email protected]",
"nuxt-edge": "^2.16.0-27064814.35c6ac41",
"standard-version": "^9.3.0"
},
Expand Down
Loading

0 comments on commit 82d4d92

Please sign in to comment.