Skip to content

Commit

Permalink
fix(client): enable debug logging in edge and fix process.env access (#…
Browse files Browse the repository at this point in the history
…14537)

- Forward `process.env.DEBUG` or `global.DEBUG` from the entry point of
  the edge client to the runtime bundle as `process.env.DEBUG`. This
  makes it possible to enable the debug logs during development using an
  environment variable.

- Patch the debug logger to make it work in the edge environment and use
  `console.debug` or `console.log` instead of `process.stderr.write`.

- Use uniform way of accessing the environment variables in the entry
  point both for `DATABASE_URL` and `DEBUG` and fix incorrect
  unconditional access to the `process` object which might not be
  present.

Fixes #12681
Fixes #14536
Fixes #13771
  • Loading branch information
aqrln committed Aug 3, 2022
1 parent 3fae834 commit 8298195
Show file tree
Hide file tree
Showing 6 changed files with 50 additions and 6 deletions.
2 changes: 2 additions & 0 deletions packages/client/src/generation/TSClient/TSClient.ts
Expand Up @@ -9,6 +9,7 @@ import { DMMFHelper } from '../../runtime/dmmf'
import type { DMMF } from '../../runtime/dmmf-types'
import type { GetPrismaClientConfig } from '../../runtime/getPrismaClient'
import type { InternalDatasource } from '../../runtime/utils/printDatasources'
import { buildDebugInitialization } from '../utils/buildDebugInitialization'
import { buildDirname } from '../utils/buildDirname'
import { buildDMMF } from '../utils/buildDMMF'
import { buildInjectableEdgeEnv } from '../utils/buildInjectableEdgeEnv'
Expand Down Expand Up @@ -126,6 +127,7 @@ ${await buildInlineSchema(dataProxy, schemaPath)}
${buildInlineDatasource(dataProxy, datasources)}
${buildInjectableEdgeEnv(edge, datasources)}
${buildWarnEnvConflicts(edge, runtimeDir, runtimeName)}
${buildDebugInitialization(edge)}
const PrismaClient = getPrismaClient(config)
exports.PrismaClient = PrismaClient
Object.assign(exports, Prisma)
Expand Down
1 change: 1 addition & 0 deletions packages/client/src/generation/TSClient/common.ts
Expand Up @@ -35,6 +35,7 @@ const {
join,
raw,
Decimal,
Debug,
objectEnumValues,
makeStrictEnum
} = require('${runtimeDir}/${runtimeName}')
Expand Down
26 changes: 26 additions & 0 deletions packages/client/src/generation/utils/buildDebugInitialization.ts
@@ -0,0 +1,26 @@
import { getRuntimeEdgeEnvVar } from './buildInjectableEdgeEnv'

/**
* Builds the code to initialize the `debug` package.
*
* The code running in the Edge Client entry point has access to `process.env`
* if it's defined, but the code in the runtime bundle doesn't. Furthermore, in
* some environments `DEBUG` may be defined as a global variable rather than
* available in `process.env`. The entry point fetches the value of `DEBUG` and
* passes in to the `debug` package.
*
* @param edge Whether the edge runtime is used
*/
export function buildDebugInitialization(edge: boolean) {
if (!edge) {
return ''
}

const debugVar = getRuntimeEdgeEnvVar('DEBUG')

return `\
if (${debugVar}) {
Debug.enable(${debugVar})
}
`
}
20 changes: 14 additions & 6 deletions packages/client/src/generation/utils/buildInjectableEdgeEnv.ts
Expand Up @@ -38,12 +38,7 @@ function declareInjectableEdgeEnv(datasources: InternalDatasource[]) {
const envVarNames = getSelectedEnvVarNames(datasources)

for (const envVarName of envVarNames) {
// for cloudflare workers, an env var is a global js variable
const cfwEnv = `typeof global !== 'undefined' && global['${envVarName}']`
// for vercel edge functions, it's injected statically at build
const vercelEnv = `process.env.${envVarName}`

injectableEdgeEnv.parsed[envVarName] = `${cfwEnv} || ${vercelEnv} || undefined`
injectableEdgeEnv.parsed[envVarName] = getRuntimeEdgeEnvVar(envVarName)
}

// we make it json then remove the quotes to turn it into "code"
Expand All @@ -69,3 +64,16 @@ function getSelectedEnvVarNames(datasources: InternalDatasource[]) {
return acc
}, [] as string[])
}

/**
* Builds the expression to get the value of an environment variable at run time.
* @param envVarName Name of the environment variable
*/
export function getRuntimeEdgeEnvVar(envVarName: string) {
// for cloudflare workers, an env var is a global js variable
const cfwEnv = `typeof global !== 'undefined' && global['${envVarName}']`
// for vercel edge functions, it's injected statically at build
const nodeOrVercelEnv = `typeof process !== 'undefined' && process.env && process.env.${envVarName}`

return `${cfwEnv} || ${nodeOrVercelEnv} || undefined`
}
1 change: 1 addition & 0 deletions packages/client/src/runtime/index.ts
Expand Up @@ -18,6 +18,7 @@ export type { DecimalJsLike } from './utils/decimalJsLike'
export { findSync } from './utils/find'
export { NotFoundError } from './utils/rejectOnNotFound'
export { warnEnvConflicts } from './warnEnvConflicts'
export { Debug } from '@prisma/debug'
export {
Engine,
PrismaClientInitializationError,
Expand Down
6 changes: 6 additions & 0 deletions packages/debug/src/index.ts
Expand Up @@ -4,6 +4,12 @@ const MAX_LOGS = 100

const debugArgsHistory: any[] = []

// Patch the Node.js logger to use `console.debug` or `console.log` (similar to
// the browser logger) in the Edge Client.
if (typeof process !== 'undefined' && typeof process.stderr?.write !== 'function') {
debug.log = console.debug ?? console.log
}

/**
* Wrapper on top of the original `Debug` to keep a history of the all last
* {@link MAX_LOGS}. This is then used by {@link getLogs} to generate an error
Expand Down

0 comments on commit 8298195

Please sign in to comment.