Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: runtime logger formatting and debug verbose option #3067

Merged
merged 5 commits into from
Aug 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions docs/content/docs/5.v9/3.options/10.misc.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,10 +47,10 @@ If it can not detect Nuxt configuration changing, you may need to run `nuxi prep

## `debug`

- type: `boolean`
- type: `boolean | 'verbose'`
- default: `false`

Whether to use `@nuxtjs/i18n` debug mode. If `true`, logs will be output to the console.
Whether to use `@nuxtjs/i18n` debug mode. If `true` or `'verbose'`, logs will be output to the console, setting this to `'verbose'` will also log loaded messages objects.

::callout{icon="i-heroicons-exclamation-triangle" color="amber"}
The purpose of this option is to help identify any problems with `@nuxtjs/i18n`.
Expand Down
72 changes: 0 additions & 72 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 3 additions & 3 deletions src/bundler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ export async function extendBundler(nuxt: Nuxt, nuxtOptions: Required<NuxtI18nOp
dropMessageCompiler: nuxtOptions.bundle.dropMessageCompiler
}),
{
__DEBUG__: String(nuxtOptions.debug)
__DEBUG__: String(!!nuxtOptions.debug)
}
)
)
Expand Down Expand Up @@ -125,10 +125,10 @@ export async function extendBundler(nuxt: Nuxt, nuxtOptions: Required<NuxtI18nOp

extendViteConfig(config => {
if (config.define) {
config.define['__DEBUG__'] = JSON.stringify(nuxtOptions.debug)
config.define['__DEBUG__'] = JSON.stringify(!!nuxtOptions.debug)
} else {
config.define = {
__DEBUG__: JSON.stringify(nuxtOptions.debug)
__DEBUG__: JSON.stringify(!!nuxtOptions.debug)
}
}
debug('vite.config.define', config.define)
Expand Down
5 changes: 5 additions & 0 deletions src/logger.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
declare module 'virtual:nuxt-i18n-logger' {
import type { ConsolaInstance } from 'consola'

export function createLogger(label: string): ConsolaInstance
}
12 changes: 12 additions & 0 deletions src/module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ import {
import { distDir, runtimeDir } from './dirs'
import { applyLayerOptions, checkLayerOptions, resolveLayerVueI18nConfigInfo } from './layers'
import { generateTemplateNuxtI18nOptions } from './template'
import { i18nVirtualLoggerPlugin, RESOLVED_VIRTUAL_NUXT_I18N_LOGGER, VIRTUAL_NUXT_I18N_LOGGER } from './virtual-logger'

import type { HookResult } from '@nuxt/schema'
import type { LocaleObject, NuxtI18nOptions } from './types'
Expand Down Expand Up @@ -220,6 +221,7 @@ export default defineNuxtModule<NuxtI18nOptions>({
// for composables
nuxt.options.alias['#i18n'] = resolve(distDir, 'runtime/composables/index.mjs')
nuxt.options.build.transpile.push('#i18n')
nuxt.options.build.transpile.push(VIRTUAL_NUXT_I18N_LOGGER)

const genTemplate = (isServer: boolean, lazy?: boolean) => {
const nuxtI18nOptions = defu({}, options)
Expand Down Expand Up @@ -251,6 +253,16 @@ export default defineNuxtModule<NuxtI18nOptions>({
getContents: () => genTemplate(false)
})

nuxt.options.imports.transform ??= {}
nuxt.options.imports.transform.include ??= []
nuxt.options.imports.transform.include.push(new RegExp(`${RESOLVED_VIRTUAL_NUXT_I18N_LOGGER}$`))

nuxt.hook('vite:extendConfig', cfg => {
// eslint-disable-next-line @typescript-eslint/no-floating-promises
cfg.plugins ||= []
cfg.plugins.push(i18nVirtualLoggerPlugin(options.debug))
})

/**
* `PageMeta` augmentation to add `nuxtI18n` property
* TODO: Remove in v9, `useSetI18nParams` should be used instead
Expand Down
6 changes: 5 additions & 1 deletion src/nitro.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import {
import type { Nuxt } from '@nuxt/schema'
import type { NuxtI18nOptions, LocaleInfo } from './types'
import { resolveI18nDir } from './layers'
import { i18nVirtualLoggerPlugin } from './virtual-logger'

const debug = createDebug('@nuxtjs/i18n:nitro')

Expand Down Expand Up @@ -52,6 +53,9 @@ export async function setupNitro(
nitroConfig.rollupConfig.plugins = isArray(nitroConfig.rollupConfig.plugins)
? nitroConfig.rollupConfig.plugins
: [nitroConfig.rollupConfig.plugins]

nitroConfig.rollupConfig.plugins.push(i18nVirtualLoggerPlugin(nuxtOptions.debug))

const yamlPaths = getResourcePaths(additionalParams.localeInfo, /\.ya?ml$/)
if (yamlPaths.length > 0) {
// @ts-ignore NOTE: A type error occurs due to a mismatch between the version of rollup on the nitro side (v4.x) and the version of rollup that `@rollup/plugin-yaml` depends on (v3.x). We ignore this type error because `@rollup/plugin-yaml` is rollup version compatible.
Expand Down Expand Up @@ -120,7 +124,7 @@ export { localeDetector }
}

// setup debug flag
nitroConfig.replace['__DEBUG__'] = String(nuxtOptions.debug)
nitroConfig.replace['__DEBUG__'] = String(!!nuxtOptions.debug)
debug('nitro.replace', nitroConfig.replace)
})

Expand Down
59 changes: 23 additions & 36 deletions src/runtime/internal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ import isHTTPS from 'is-https'
import { useRequestHeaders, useRequestEvent, useCookie as useNuxtCookie, useRuntimeConfig, useNuxtApp } from '#imports'
import { NUXT_I18N_MODULE_ID, DEFAULT_COOKIE_KEY, isSSG, localeCodes, normalizedLocales } from '#build/i18n.options.mjs'
import { findBrowserLocale, getLocalesRegex } from './routing/utils'
import { createLogger, initCommonComposableOptions, type CommonComposableOptions } from './utils'
import { initCommonComposableOptions, type CommonComposableOptions } from './utils'
import { createLogger } from 'virtual:nuxt-i18n-logger'

import type { Locale } from 'vue-i18n'
import type { DetectBrowserLanguageOptions, LocaleObject } from '#build/i18n.options.mjs'
Expand Down Expand Up @@ -49,20 +50,21 @@ export function parseAcceptLanguage(input: string): string[] {

export function getBrowserLocale(): string | undefined {
let ret: string | undefined
const logger = /*#__PURE__*/ createLogger('getBrowserLocale')
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We tag the logs with createLogger for each function, do we need to go that far? πŸ˜…

I think it would be better to have a logger tagged per js/ts module.
I believe we can make debugging more efficient from currently by filtering the logs by module.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree it seems like a bit much, but most of the logs originally already had the function name in the log itself in each log statement (for example https://github.com/nuxt-modules/i18n/pull/3067/files/a44ce5dbcd40696e2cfd3db47b18464476a5cb45#diff-7755b40274cbd4ca5d224c6ed6953280e589110e9289595aeb246ff4a1c07448L57).

The primary reason for tagging them is so the actual code/logic is easier to read and understand (fewer multiline debug log statements, less word repetition). With these refactors I'm hoping to find patterns for simplification like in #3063.

Logging per module/file would definitely make more sense, and I think the fact that we had the function name in the logs probably means that internal.ts and utils.ts` are not descriptive enough, which we could solve by splitting these up further.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The primary reason for tagging them is so the actual code/logic is easier to read and understand (fewer multiline debug log statements, less word repetition). With these refactors I'm hoping to find patterns for simplification like in #3063.

Okay, I could understand what you are trying to improve for logging. Let's continue to refactor and improve! :)


if (import.meta.client) {
if (navigator.languages) {
// get browser language either from navigator if running on client side, or from the headers
ret = findBrowserLocale(normalizedLocales, navigator.languages as string[])
__DEBUG__ && console.log('getBrowserLocale (navigator.languages, ret) -', navigator.languages, ret)
__DEBUG__ && logger.log('(navigator.languages, ret) -', navigator.languages, ret)
}
} else if (import.meta.server) {
const header = useRequestHeaders(['accept-language'])
__DEBUG__ && console.log('getBrowserLocale accept-language', header)
__DEBUG__ && logger.log('accept-language', header)
const accept = header['accept-language']
if (accept) {
ret = findBrowserLocale(normalizedLocales, parseAcceptLanguage(accept))
__DEBUG__ && console.log('getBrowserLocale ret', ret)
__DEBUG__ && logger.log('ret', ret)
}
}

Expand Down Expand Up @@ -92,8 +94,10 @@ export function getLocaleCookie(
detect: false | DetectBrowserLanguageOptions,
defaultLocale: string
): string | undefined {
const env = import.meta.client ? 'client' : 'server'
const logger = /*#__PURE__*/ createLogger(`getLocaleCookie:${env}`)
__DEBUG__ &&
console.log('getLocaleCookie', {
logger.log({
useCookie: detect && detect.useCookie,
cookieKey: detect && detect.cookieKey,
localeCodes
Expand All @@ -104,27 +108,23 @@ export function getLocaleCookie(
}

const localeCode: string | undefined = cookieRef.value ?? undefined
const env = import.meta.client ? 'client' : 'server'
if (localeCode == null) {
__DEBUG__ && console.log(`getLocaleCookie (${env}) - none`)
__DEBUG__ && logger.log(`none`)
return
}

if (localeCodes.includes(localeCode)) {
__DEBUG__ && console.log(`getLocaleCookie (${env}) - locale from cookie: `, localeCode)
__DEBUG__ && logger.log(`locale from cookie: `, localeCode)
return localeCode
}

if (defaultLocale) {
__DEBUG__ &&
console.log(
`getLocaleCookie (${env}) - unknown locale cookie (${localeCode}), setting to defaultLocale (${defaultLocale})`
)
__DEBUG__ && logger.log(`unknown locale cookie (${localeCode}), setting to defaultLocale (${defaultLocale})`)
cookieRef.value = defaultLocale
return defaultLocale
}

__DEBUG__ && console.log(`getLocaleCookie (${env}) - unknown locale cookie (${localeCode}), unsetting cookie`)
__DEBUG__ && logger.log(`unknown locale cookie (${localeCode}), unsetting cookie`)
cookieRef.value = undefined
return
}
Expand Down Expand Up @@ -176,7 +176,7 @@ export function detectBrowserLanguage(
detectLocaleContext: DetectLocaleContext,
locale: Locale = ''
): DetectBrowserLanguageFromResult {
const logger = createLogger('detectBrowserLanguage')
const logger = /*#__PURE__*/ createLogger('detectBrowserLanguage')
const _detect = runtimeDetectBrowserLanguage()

// feature is disabled
Expand Down Expand Up @@ -271,15 +271,13 @@ export function getLocaleDomain(
strategy: string,
route: string | RouteLocationNormalized | RouteLocationNormalizedLoaded
): string {
const logger = /*#__PURE__*/ createLogger(`getLocaleDomain`)
let host = getHost() || ''
const routePath = isObject(route) ? route.path : isString(route) ? route : ''

if (host) {
__DEBUG__ &&
console.log(
`MultiDomainsMultiLocales: locating domain for host: `,
host,
strategy,
isObject(route) ? route.path : route
)
__DEBUG__ && logger.log(`locating domain for host`, { host, strategy, path: routePath })

let matchingLocale: LocaleObject | undefined
const matchingLocales = locales.filter(locale => {
if (locale && locale.domain) {
Expand All @@ -296,8 +294,7 @@ export function getLocaleDomain(

if (matchingLocales.length === 1) {
matchingLocale = matchingLocales[0]
__DEBUG__ &&
console.log(`MultiDomainsMultiLocales: found only one matching domain: `, host, matchingLocales[0].code)
__DEBUG__ && logger.log(`found one matching domain`, { host, matchedLocale: matchingLocales[0].code })
} else if (matchingLocales.length > 1) {
if (strategy === 'no_prefix') {
console.warn(
Expand All @@ -310,20 +307,13 @@ export function getLocaleDomain(
} else {
// get prefix from route
if (route) {
const routePath = isObject(route) ? route.path : isString(route) ? route : ''

__DEBUG__ &&
console.log(`MultiDomainsMultiLocales: Check in matched domain for locale match in path: `, routePath, host)
__DEBUG__ && logger.log(`check matched domain for locale match`, { path: routePath, host })

if (routePath && routePath !== '') {
const matches = routePath.match(getLocalesRegex(matchingLocales.map(l => l.code)))
if (matches && matches.length > 1) {
matchingLocale = matchingLocales.find(l => l.code === matches[1])
__DEBUG__ &&
console.log(
`MultiDomainsMultiLocales: Found matching locale from path. MatchingLocale is now`,
matchingLocale?.code
)
__DEBUG__ && logger.log(`matched locale from path`, { matchedLocale: matchingLocale?.code })
}
}
}
Expand All @@ -334,10 +324,7 @@ export function getLocaleDomain(
Array.isArray(l.defaultForDomains) ? l.defaultForDomains.includes(host) : l.domainDefault
)
__DEBUG__ &&
console.log(
`MultiDomainsMultiLocales: matching locale not found - trying to get default for this domain. MatchingLocale is now`,
matchingLocale?.code
)
logger.log(`no locale matched - using default for this domain`, { matchedLocale: matchingLocale?.code })
}
}
}
Expand Down
16 changes: 10 additions & 6 deletions src/runtime/messages.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { deepCopy, isFunction, isArray, isObject, isString } from '@intlify/shared'
import { createLogger } from 'virtual:nuxt-i18n-logger'

import type { I18nOptions, Locale, FallbackLocale, LocaleMessages, DefineLocaleMessage } from 'vue-i18n'
import type { NuxtApp } from '#app'
Expand All @@ -14,6 +15,7 @@ export type LocaleLoader<T = LocaleMessages<DefineLocaleMessage>> = {
load: () => Promise<MessageLoaderResult<T>>
cache: boolean
}

const cacheMessages = new Map<string, LocaleMessages<DefineLocaleMessage>>()

export async function loadVueI18nOptions(
Expand Down Expand Up @@ -75,19 +77,20 @@ export async function loadInitialMessages<Context extends NuxtApp = NuxtApp>(
}

async function loadMessage(locale: Locale, { key, load }: LocaleLoader) {
const logger = /*#__PURE__*/ createLogger('loadMessage')
let message: LocaleMessages<DefineLocaleMessage> | null = null
try {
__DEBUG__ && console.log('loadMessage: (locale) -', locale)
__DEBUG__ && logger.log({ locale })
const getter = await load().then(r => ('default' in r ? r.default : r))
if (isFunction(getter)) {
message = await getter(locale)
__DEBUG__ && console.log('loadMessage: dynamic load', message)
__DEBUG__ && logger.log('dynamic load', logger.level >= 999 ? message : '')
} else {
message = getter
if (message != null && cacheMessages) {
cacheMessages.set(key, message)
}
__DEBUG__ && console.log('loadMessage: load', message)
__DEBUG__ && logger.log('loaded', logger.level >= 999 ? message : '')
}
} catch (e: unknown) {
console.error('Failed locale loading: ' + (e as Error).message)
Expand All @@ -100,6 +103,7 @@ export async function loadLocale(
localeLoaders: Record<Locale, LocaleLoader[]>,
setter: (locale: Locale, message: LocaleMessages<DefineLocaleMessage>) => void
) {
const logger = /*#__PURE__*/ createLogger('loadLocale')
const loaders = localeLoaders[locale]

if (loaders == null) {
Expand All @@ -112,11 +116,11 @@ export async function loadLocale(
let message: LocaleMessages<DefineLocaleMessage> | undefined | null = null

if (cacheMessages && cacheMessages.has(loader.key) && loader.cache) {
__DEBUG__ && console.log(loader.key + ' is already loaded')
__DEBUG__ && logger.log(loader.key + ' is already loaded')
message = cacheMessages.get(loader.key)
} else {
__DEBUG__ && !loader.cache && console.log(loader.key + ' bypassing cache!')
__DEBUG__ && console.log(loader.key + ' is loading ...')
__DEBUG__ && !loader.cache && logger.log(loader.key + ' bypassing cache!')
__DEBUG__ && logger.log(loader.key + ' is loading ...')
message = await loadMessage(locale, loader)
}

Expand Down
Loading