sainsburys-tech / next-logger

JSON logging patcher for Next.js
MIT License
144 stars 14 forks source link

[sharing] Azure Application Insights example #12

Closed devinrhode2 closed 2 years ago

devinrhode2 commented 2 years ago

Just wanted to share this example:

// next-logger.config.mjs
// This MUST be a .mjs file, so we can use top-level await.

import pinoms from 'pino-multi-stream'
// @ts-expect-error - no @types package for this (not necessary either)
import { createWriteStream } from 'pino-applicationinsights'

import nicerEnv from './src/env/nicerEnv'
import isDev from './src/env/isDev'

/** @type {Parameters<typeof createWriteStream>[0]} */
const createWriteStreamConfig = {
  // No other properties can be defined here, other than `key: process.env.APPINSIGHTS_INSTRUMENTATIONKEY` which would not be used.
  setup: (
    /**
     * Hoisted from pino-applicationinsights
     * @type {typeof import('applicationinsights')}
     */
    applicationInsights,
  ) => {
    const applicationInsightsChain = applicationInsights
      .setup(process.env.APPINSIGHTS_INSTRUMENTATIONKEY)
      // Copied from: https://docs.microsoft.com/en-us/azure/azure-monitor/app/nodejs#sdk-configuration
      .setAutoDependencyCorrelation(true)
      .setAutoCollectRequests(true)
      .setAutoCollectPerformance(true, true)
      .setAutoCollectExceptions(true)
      .setAutoCollectDependencies(true)
      .setAutoCollectConsole(
        true,
        true, // ALSO record console methods
      )
      .setUseDiskRetryCaching(true)
      .setSendLiveMetrics(false)
      .setDistributedTracingMode(
        applicationInsights.DistributedTracingModes.AI,
      )
      .start()

    if (process.env.NODE_ENV !== 'production') {
      applicationInsights.defaultClient.config.disableAppInsights = true
    }

    return applicationInsightsChain
  },
}

/** @type {import('pino-multi-stream').Streams[0]['stream']} */
// `logger` is not called with `await` - therefore - we'll top-level await here. (See next-logger/lib/logger.js)
const writeStream = await createWriteStream(
  createWriteStreamConfig,
)

/**
 * @type {(
 *   pinoConfig: Parameters<
 *     typeof import('pino').pino
 *   >[0]
 * ) => ReturnType<
 *   typeof import('pino').pino
 * >}
 */
// next-logger does not call this with `await`
export const logger = (_defaultConfig) => {
  return pinoms({
    level: 'debug',
    streams: [
      {
        stream: writeStream,
        level: 'debug',
      },
    ],
  })
}

I'm using this tsconfig, typescript 4.5.5

{
  "compilerOptions": {
    "allowUnreachableCode": false,
    "allowUnusedLabels": false,
    "alwaysStrict": true,
    "exactOptionalPropertyTypes": true,
    "noFallthroughCasesInSwitch": true,
    "noImplicitAny": true,
    "noImplicitOverride": true,
    "noImplicitReturns": true,
    "noImplicitThis": true,
    "noPropertyAccessFromIndexSignature": true,
    "noUncheckedIndexedAccess": true,
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    "strict": true,

    "module": "esnext",
    "target": "es2017",
    "lib": ["dom", "dom.iterable", "esnext"],
    "jsx": "preserve",
    "allowJs": true,
    "checkJs": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true,
    "noEmit": true,
    "esModuleInterop": true,
    "moduleResolution": "node",
    "resolveJsonModule": true,
    "isolatedModules": true,
    "incremental": true
  }
}

Versions:

Node 16.13.2
pino-applicationinsights@2.1.0
applicationinsights@1.8.10
pino-multi-stream@6.0.0
pino@7.6.5
pino-pretty@7.5.1

However, only these three need to appear in your package.json:

pino-applicationinsights
pino-multi-stream
pino-pretty

pino and application-insights are already included in these packages.

Lastly, my package.json scripts entry:

  "start": "cross-env NODE_OPTIONS='-r next-logger' next start | pino-pretty -c -l",
devinrhode2 commented 2 years ago

I will keep this open, in case someone wants to pull this into the readme

atkinchris commented 2 years ago

Great example; thanks for raising it!

The top-level await makes me wonder if this library should support async constructors - but I suspect that will cause unexpected timing issues with synchronous requires/patching.

Looking at https://github.com/ovhemert/pino-applicationinsights/blob/master/src/index.js specifically, I'm unsure why its marked as async - it doesn't appear to actually await or return a Promise.

devinrhode2 commented 2 years ago

Ok, turns out .mjs files are NOT supported by cosmic config https://github.com/davidtheclark/cosmiconfig/issues/224

CleanShot 2022-03-03 at 15 48 09@2x

devinrhode2 commented 2 years ago

@atkinchris I noticed that same exact thing and raised an issue here: https://github.com/ovhemert/pino-applicationinsights/issues/56

For now it was pretty easy to use patch-package and just delete the async keyword 😄 🎉

devinrhode2 commented 2 years ago

Side note - something I noticed, I think with next, is that all exceptions that happen in an api route end up being funneled into console.error in some way.