feat(injecta): logger support for easier debugging

This commit is contained in:
Neko Ayaka
2025-10-07 23:43:39 +08:00
parent 35259b5d29
commit df508fc047
7 changed files with 223 additions and 132 deletions
+3
View File
@@ -23,5 +23,8 @@
"scripts": {
"typecheck": "tsc --noEmit",
"build": "tsdown"
},
"dependencies": {
"@guiiai/logg": "catalog:"
}
}
+5 -1
View File
@@ -1,9 +1,13 @@
import type { DependencyMap, InvokeOption, ProvideOption } from '.'
import type { DependencyMap, InvokeOption, Logger, ProvideOption } from '.'
import { createContainer, provide as indexProvide, start as indexStart, stop as indexStop } from '.'
const globalContainer = createContainer()
export function setLogger(logger: Logger) {
globalContainer.logger = logger
}
export function provide<D extends DependencyMap | undefined, T = any>(
name: string,
option: ProvideOption<T, D>,
+10 -1
View File
@@ -1,12 +1,21 @@
import {
invoke as globalInvoke,
provide as globalProvide,
setLogger as globalSetLogger,
start as globalStart,
stop as globalStop,
} from './global'
export type { Lifecycle } from './builtin'
export type { Logger, LoggerOptions } from './logger'
export {
createDefaultLogger,
createLoggLogger,
createNoopLogger,
} from './logger'
export {
createContainer,
invoke,
@@ -14,7 +23,6 @@ export {
start,
stop,
} from './scoped'
export type {
BuildContext,
Container,
@@ -32,4 +40,5 @@ export const injecta = {
invoke: globalInvoke,
start: globalStart,
stop: globalStop,
setLogger: globalSetLogger,
}
+139
View File
@@ -0,0 +1,139 @@
import { useLogg } from '@guiiai/logg'
import { name } from '../package.json'
export interface Logger {
provide: (name: string, dependencies: string[]) => void
invoke: (dependencies: string[]) => void
beforeRun: (name: string) => void
run: (name: string, duration: number) => void
hookOnStart: (name: string) => void
hookOnStartComplete: (name: string, duration: number) => void
hookOnStop: (name: string) => void
hookOnStopComplete: (name: string, duration: number) => void
running: () => void
}
export interface LoggerOptions {
enabled?: boolean
logger?: Logger
}
function formatDuration(ms: number): string {
if (ms < 1) {
return `${(ms * 1000).toFixed(3)}µs`
}
if (ms < 1000) {
return `${ms.toFixed(3)}ms`
}
return `${(ms / 1000).toFixed(3)}s`
}
export function createDefaultLogger(): Logger {
return {
provide: (name: string, dependencies: string[]) => {
const depsStr = dependencies.length > 0 ? ` (depends on: ${dependencies.join(', ')})` : ''
// eslint-disable-next-line no-console
console.log(`[${name}] PROVIDE ${name}${depsStr}`)
},
invoke: (dependencies: string[]) => {
const depsStr = dependencies.length > 0 ? ` (depends on: ${dependencies.join(', ')})` : ''
// eslint-disable-next-line no-console
console.log(`[${name}] INVOKE${depsStr}`)
},
beforeRun: (name: string) => {
// eslint-disable-next-line no-console
console.log(`[${name}] BEFORE RUN provide: ${name}()`)
},
run: (name: string, duration: number) => {
// eslint-disable-next-line no-console
console.log(`[${name}] RUN provide: ${name}() in ${formatDuration(duration)}`)
},
hookOnStart: (name: string) => {
// eslint-disable-next-line no-console
console.log(`[${name}] HOOK OnStart ${name}() executing`)
},
hookOnStartComplete: (name: string, duration: number) => {
// eslint-disable-next-line no-console
console.log(`[${name}] HOOK OnStart ${name}() ran successfully in ${formatDuration(duration)}`)
},
hookOnStop: (name: string) => {
// eslint-disable-next-line no-console
console.log(`[${name}] HOOK OnStop ${name}() executing`)
},
hookOnStopComplete: (name: string, duration: number) => {
// eslint-disable-next-line no-console
console.log(`[${name}] HOOK OnStop ${name}() ran successfully in ${formatDuration(duration)}`)
},
running: () => {
// eslint-disable-next-line no-console
console.log(`[${name}] RUNNING`)
},
}
}
export function createLoggLogger(): Logger {
const logg = useLogg(name).useGlobalConfig()
return {
provide: (name: string, dependencies: string[]) => {
const depsStr = dependencies.length > 0 ? ` (depends on: ${dependencies.join(', ')})` : ''
logg.log(`PROVIDE ${name}${depsStr}`)
},
invoke: (dependencies: string[]) => {
const depsStr = dependencies.length > 0 ? ` (depends on: ${dependencies.join(', ')})` : ''
logg.log(`INVOKE${depsStr}`)
},
beforeRun: (name: string) => {
logg.log(`BEFORE RUN provide: ${name}()`)
},
run: (name: string, duration: number) => {
logg.log(`RUN provide: ${name}() in ${formatDuration(duration)}`)
},
hookOnStart: (name: string) => {
logg.log(`HOOK OnStart ${name}() executing`)
},
hookOnStartComplete: (name: string, duration: number) => {
logg.log(`HOOK OnStart ${name}() ran successfully in ${formatDuration(duration)}`)
},
hookOnStop: (name: string) => {
logg.log(`HOOK OnStop ${name}() executing`)
},
hookOnStopComplete: (name: string, duration: number) => {
logg.log(`HOOK OnStop ${name}() ran successfully in ${formatDuration(duration)}`)
},
running: () => {
logg.log(`RUNNING`)
},
}
}
export function createNoopLogger(): Logger {
return {
provide: () => {},
invoke: () => {},
beforeRun: () => {},
run: () => {},
hookOnStart: () => {},
hookOnStartComplete: () => {},
hookOnStop: () => {},
hookOnStopComplete: () => {},
running: () => {},
}
}
+41 -7
View File
@@ -1,4 +1,7 @@
import type { LifecycleTriggerable } from './builtin'
import type { Logger, LoggerOptions } from './logger'
import { createDefaultLogger, createNoopLogger } from './logger'
export type DependencyMap = Record<string, any>
@@ -8,6 +11,7 @@ export interface Container {
lifecycleHooks: Map<string, LifecycleTriggerable>
dependencyGraph: Map<string, string[]>
invocations: InvokeOptionObject<any>[]
logger: Logger
}
export type BuildContext<D extends DependencyMap | undefined = undefined> = {
@@ -23,13 +27,18 @@ export type InvokeOptionObject<D extends DependencyMap | undefined = undefined>
export type InvokeOptionFunc<D extends DependencyMap | undefined = undefined> = (dependencies: D) => void | Promise<void>
export type InvokeOption<D extends DependencyMap | undefined = undefined> = InvokeOptionObject<D> | InvokeOptionFunc<D>
export function createContainer(): Container {
export function createContainer(options?: LoggerOptions): Container {
const logger = options?.enabled === false
? createNoopLogger()
: (options?.logger ?? createDefaultLogger())
return {
providers: new Map(),
instances: new Map(),
lifecycleHooks: new Map(),
dependencyGraph: new Map(),
invocations: [],
logger,
}
}
@@ -46,16 +55,20 @@ export function provide<D extends DependencyMap | undefined, T = any>(
// Track dependencies for lifecycle ordering
const dependencies = providerObject.dependsOn ? Object.values(providerObject.dependsOn) : []
container.logger.provide(name, dependencies)
container.dependencyGraph.set(name, dependencies)
}
export function invoke<D extends DependencyMap>(container: Container, option: InvokeOption<D>): void {
if (typeof option === 'function') {
container.invocations.push({ callback: option } as InvokeOptionObject<any>)
}
else {
container.invocations.push(option as InvokeOptionObject<any>)
}
const invocationObject = typeof option === 'function'
? { callback: option } as InvokeOptionObject<any>
: option as InvokeOptionObject<any>
const dependencies = invocationObject.dependsOn ? Object.values(invocationObject.dependsOn) : []
container.logger.invoke(dependencies)
container.invocations.push(invocationObject)
}
async function resolveInstance<T>(container: Container, name: string): Promise<T> {
@@ -93,7 +106,14 @@ async function resolveInstance<T>(container: Container, name: string): Promise<T
name,
}
container.logger.beforeRun(name)
const startTime = performance.now()
const instance = await provider.build(context)
const duration = performance.now() - startTime
container.logger.run(name, duration)
container.instances.set(name, instance)
return instance as T
@@ -137,7 +157,13 @@ export async function startLifecycleHooks(container: Container): Promise<void> {
for (const serviceName of sortedServices) {
const lifecycle = container.lifecycleHooks.get(serviceName)
if (lifecycle?.emitOnStart) {
container.logger.hookOnStart(serviceName)
const startTime = performance.now()
await lifecycle.emitOnStart()
const duration = performance.now() - startTime
container.logger.hookOnStartComplete(serviceName, duration)
}
}
}
@@ -149,7 +175,13 @@ export async function stopLifecycleHooks(container: Container): Promise<void> {
for (const serviceName of sortedServices.reverse()) {
const lifecycle = container.lifecycleHooks.get(serviceName)
if (lifecycle?.emitOnStop) {
container.logger.hookOnStop(serviceName)
const startTime = performance.now()
await lifecycle.emitOnStop()
const duration = performance.now() - startTime
container.logger.hookOnStopComplete(serviceName, duration)
}
}
}
@@ -168,6 +200,8 @@ export async function start(container: Container): Promise<void> {
await invocation.callback(resolvedDependencies)
}
container.logger.running()
}
export async function stop(container: Container): Promise<void> {
+24 -122
View File
@@ -7,7 +7,7 @@ settings:
catalogs:
default:
'@guiiai/logg':
specifier: ^1.1.0
specifier: 1.1.0
version: 1.1.0
'@moeru/std':
specifier: 0.1.0-beta.7
@@ -1133,9 +1133,13 @@ importers:
devDependencies:
unplugin-yaml:
specifier: ^3.0.6
version: 3.0.6(@nuxt/kit@4.0.3(magicast@0.3.5))(@nuxt/schema@4.0.3)(astro@5.10.1(@netlify/blobs@9.1.2)(@types/node@24.6.2)(db0@0.3.2(drizzle-orm@0.44.6(@opentelemetry/api@1.9.0)(@types/pg@8.15.5)(gel@2.0.0)(pg@8.16.3)(postgres@3.4.7)))(ioredis@5.7.0)(jiti@2.5.1)(less@4.4.1)(lightningcss@1.30.2)(rollup@4.52.4)(terser@5.43.1)(tsx@4.20.6)(typescript@5.9.3)(yaml@2.8.1))(esbuild@0.25.9)(rolldown@1.0.0-beta.42)(rollup@4.52.4)(vite@7.1.9(@types/node@24.6.2)(jiti@2.5.1)(less@4.4.1)(lightningcss@1.30.2)(terser@5.43.1)(tsx@4.20.6)(yaml@2.8.1))
version: 3.0.6(@nuxt/kit@4.0.3(magicast@0.3.5))(@nuxt/schema@4.0.3)(astro@5.10.1(@netlify/blobs@9.1.2)(@types/node@24.6.2)(db0@0.3.2(drizzle-orm@0.44.6(@opentelemetry/api@1.9.0)(@types/pg@8.15.5)(gel@2.0.0)(pg@8.16.3)(postgres@3.4.7)))(encoding@0.1.13)(ioredis@5.7.0)(jiti@2.5.1)(less@4.4.1)(lightningcss@1.30.2)(rollup@4.52.4)(terser@5.43.1)(tsx@4.20.6)(typescript@5.9.3)(yaml@2.8.1))(esbuild@0.25.9)(rolldown@1.0.0-beta.42)(rollup@4.52.4)(vite@7.1.9(@types/node@24.6.2)(jiti@2.5.1)(less@4.4.1)(lightningcss@1.30.2)(terser@5.43.1)(tsx@4.20.6)(yaml@2.8.1))
packages/injecta: {}
packages/injecta:
dependencies:
'@guiiai/logg':
specifier: 'catalog:'
version: 1.1.0
packages/memory-pgvector:
dependencies:
@@ -1773,7 +1777,7 @@ importers:
version: rolldown-vite@7.1.15(@types/node@24.6.2)(esbuild@0.25.9)(jiti@2.5.1)(less@4.4.1)(terser@5.43.1)(tsx@4.20.6)(yaml@2.8.1)
vite-plugin-vue-devtools:
specifier: ^8.0.2
version: 8.0.2(@nuxt/kit@4.0.3(magicast@0.3.5))(rolldown-vite@7.1.15(@types/node@24.6.2)(esbuild@0.25.9)(jiti@2.5.1)(less@4.4.1)(terser@5.43.1)(tsx@4.20.6)(yaml@2.8.1))(vue@3.5.22(typescript@5.9.3))
version: 8.0.2(rolldown-vite@7.1.15(@types/node@24.6.2)(esbuild@0.25.9)(jiti@2.5.1)(less@4.4.1)(terser@5.43.1)(tsx@4.20.6)(yaml@2.8.1))(vue@3.5.22(typescript@5.9.3))
vue-router:
specifier: ^4.5.1
version: 4.5.1(vue@3.5.22(typescript@5.9.3))
@@ -1807,7 +1811,7 @@ importers:
version: rolldown-vite@7.1.15(@types/node@24.6.2)(esbuild@0.25.9)(jiti@2.5.1)(less@4.4.1)(terser@5.43.1)(tsx@4.20.6)(yaml@2.8.1)
vite-plugin-vue-devtools:
specifier: ^8.0.2
version: 8.0.2(@nuxt/kit@4.0.3(magicast@0.3.5))(rolldown-vite@7.1.15(@types/node@24.6.2)(esbuild@0.25.9)(jiti@2.5.1)(less@4.4.1)(terser@5.43.1)(tsx@4.20.6)(yaml@2.8.1))(vue@3.5.22(typescript@5.9.3))
version: 8.0.2(rolldown-vite@7.1.15(@types/node@24.6.2)(esbuild@0.25.9)(jiti@2.5.1)(less@4.4.1)(terser@5.43.1)(tsx@4.20.6)(yaml@2.8.1))(vue@3.5.22(typescript@5.9.3))
vue-router:
specifier: ^4.5.1
version: 4.5.1(vue@3.5.22(typescript@5.9.3))
@@ -23457,108 +23461,6 @@ snapshots:
- yaml
optional: true
astro@5.10.1(@netlify/blobs@9.1.2)(@types/node@24.6.2)(db0@0.3.2(drizzle-orm@0.44.6(@opentelemetry/api@1.9.0)(@types/pg@8.15.5)(gel@2.0.0)(pg@8.16.3)(postgres@3.4.7)))(ioredis@5.7.0)(jiti@2.5.1)(less@4.4.1)(lightningcss@1.30.2)(rollup@4.52.4)(terser@5.43.1)(tsx@4.20.6)(typescript@5.9.3)(yaml@2.8.1):
dependencies:
'@astrojs/compiler': 2.12.2
'@astrojs/internal-helpers': 0.6.1
'@astrojs/markdown-remark': 6.3.2
'@astrojs/telemetry': 3.3.0
'@capsizecss/unpack': 2.4.0(encoding@0.1.13)
'@oslojs/encoding': 1.1.0
'@rollup/pluginutils': 5.3.0(rollup@4.52.4)
acorn: 8.15.0
aria-query: 5.3.2
axobject-query: 4.1.0
boxen: 8.0.1
ci-info: 4.3.0
clsx: 2.1.1
common-ancestor-path: 1.0.1
cookie: 1.0.2
cssesc: 3.0.0
debug: 4.4.3
deterministic-object-hash: 2.0.2
devalue: 5.3.2
diff: 5.2.0
dlv: 1.1.3
dset: 3.1.4
es-module-lexer: 1.7.0
esbuild: 0.25.9
estree-walker: 3.0.3
flattie: 1.1.1
fontace: 0.3.0
github-slugger: 2.0.0
html-escaper: 3.0.3
http-cache-semantics: 4.2.0
import-meta-resolve: 4.2.0
js-yaml: 4.1.0
kleur: 4.1.5
magic-string: 0.30.19
magicast: 0.3.5
mrmime: 2.0.1
neotraverse: 0.6.18
p-limit: 6.2.0
p-queue: 8.1.0
package-manager-detector: 1.3.0
picomatch: 4.0.3
prompts: 2.4.2
rehype: 13.0.2
semver: 7.7.2
shiki: 3.13.0
tinyexec: 0.3.2
tinyglobby: 0.2.15
tsconfck: 3.1.6(typescript@5.9.3)
ultrahtml: 1.6.0
unifont: 0.5.2
unist-util-visit: 5.0.0
unstorage: 1.17.0(@netlify/blobs@9.1.2)(db0@0.3.2(drizzle-orm@0.44.6(@opentelemetry/api@1.9.0)(@types/pg@8.15.5)(gel@2.0.0)(pg@8.16.3)(postgres@3.4.7)))(ioredis@5.7.0)
vfile: 6.0.3
vite: 6.3.6(@types/node@24.6.2)(jiti@2.5.1)(less@4.4.1)(lightningcss@1.30.2)(terser@5.43.1)(tsx@4.20.6)(yaml@2.8.1)
vitefu: 1.1.1(vite@6.3.6(@types/node@24.6.2)(jiti@2.5.1)(less@4.4.1)(lightningcss@1.30.2)(terser@5.43.1)(tsx@4.20.6)(yaml@2.8.1))
xxhash-wasm: 1.1.0
yargs-parser: 21.1.1
yocto-spinner: 0.2.3
zod: 3.25.76
zod-to-json-schema: 3.24.6(zod@3.25.76)
zod-to-ts: 1.2.0(typescript@5.9.3)(zod@3.25.76)
optionalDependencies:
sharp: 0.33.5
transitivePeerDependencies:
- '@azure/app-configuration'
- '@azure/cosmos'
- '@azure/data-tables'
- '@azure/identity'
- '@azure/keyvault-secrets'
- '@azure/storage-blob'
- '@capacitor/preferences'
- '@deno/kv'
- '@netlify/blobs'
- '@planetscale/database'
- '@types/node'
- '@upstash/redis'
- '@vercel/blob'
- '@vercel/functions'
- '@vercel/kv'
- aws4fetch
- db0
- encoding
- idb-keyval
- ioredis
- jiti
- less
- lightningcss
- rollup
- sass
- sass-embedded
- stylus
- sugarss
- supports-color
- terser
- tsx
- typescript
- uploadthing
- yaml
optional: true
async-exit-hook@2.0.1: {}
async-mutex@0.3.2:
@@ -32512,20 +32414,6 @@ snapshots:
rollup: 4.52.4
vite: 7.1.9(@types/node@24.6.2)(jiti@2.5.1)(less@4.4.1)(lightningcss@1.30.2)(terser@5.43.1)(tsx@4.20.6)(yaml@2.8.1)
unplugin-yaml@3.0.6(@nuxt/kit@4.0.3(magicast@0.3.5))(@nuxt/schema@4.0.3)(astro@5.10.1(@netlify/blobs@9.1.2)(@types/node@24.6.2)(db0@0.3.2(drizzle-orm@0.44.6(@opentelemetry/api@1.9.0)(@types/pg@8.15.5)(gel@2.0.0)(pg@8.16.3)(postgres@3.4.7)))(ioredis@5.7.0)(jiti@2.5.1)(less@4.4.1)(lightningcss@1.30.2)(rollup@4.52.4)(terser@5.43.1)(tsx@4.20.6)(typescript@5.9.3)(yaml@2.8.1))(esbuild@0.25.9)(rolldown@1.0.0-beta.42)(rollup@4.52.4)(vite@7.1.9(@types/node@24.6.2)(jiti@2.5.1)(less@4.4.1)(lightningcss@1.30.2)(terser@5.43.1)(tsx@4.20.6)(yaml@2.8.1)):
dependencies:
'@rollup/pluginutils': 5.3.0(rollup@4.52.4)
unplugin: 2.3.10
yaml: 2.8.1
optionalDependencies:
'@nuxt/kit': 4.0.3(magicast@0.3.5)
'@nuxt/schema': 4.0.3
astro: 5.10.1(@netlify/blobs@9.1.2)(@types/node@24.6.2)(db0@0.3.2(drizzle-orm@0.44.6(@opentelemetry/api@1.9.0)(@types/pg@8.15.5)(gel@2.0.0)(pg@8.16.3)(postgres@3.4.7)))(ioredis@5.7.0)(jiti@2.5.1)(less@4.4.1)(lightningcss@1.30.2)(rollup@4.52.4)(terser@5.43.1)(tsx@4.20.6)(typescript@5.9.3)(yaml@2.8.1)
esbuild: 0.25.9
rolldown: 1.0.0-beta.42
rollup: 4.52.4
vite: 7.1.9(@types/node@24.6.2)(jiti@2.5.1)(less@4.4.1)(lightningcss@1.30.2)(terser@5.43.1)(tsx@4.20.6)(yaml@2.8.1)
unplugin@1.16.1:
dependencies:
acorn: 8.15.0
@@ -32836,7 +32724,6 @@ snapshots:
'@nuxt/kit': 3.18.1(magicast@0.3.5)
transitivePeerDependencies:
- supports-color
optional: true
vite-plugin-inspect@11.3.3(@nuxt/kit@3.18.1(magicast@0.3.5))(vite@6.3.6(@types/node@24.6.2)(jiti@2.5.1)(less@4.4.1)(lightningcss@1.30.2)(terser@5.43.1)(tsx@4.20.6)(yaml@2.8.1)):
dependencies:
@@ -32916,6 +32803,21 @@ snapshots:
- supports-color
- vue
vite-plugin-vue-devtools@8.0.2(rolldown-vite@7.1.15(@types/node@24.6.2)(esbuild@0.25.9)(jiti@2.5.1)(less@4.4.1)(terser@5.43.1)(tsx@4.20.6)(yaml@2.8.1))(vue@3.5.22(typescript@5.9.3)):
dependencies:
'@vue/devtools-core': 8.0.2(rolldown-vite@7.1.15(@types/node@24.6.2)(esbuild@0.25.9)(jiti@2.5.1)(less@4.4.1)(terser@5.43.1)(tsx@4.20.6)(yaml@2.8.1))(vue@3.5.22(typescript@5.9.3))
'@vue/devtools-kit': 8.0.2
'@vue/devtools-shared': 8.0.2
execa: 9.6.0
sirv: 3.0.2
vite: rolldown-vite@7.1.15(@types/node@24.6.2)(esbuild@0.25.9)(jiti@2.5.1)(less@4.4.1)(terser@5.43.1)(tsx@4.20.6)(yaml@2.8.1)
vite-plugin-inspect: 11.3.3(@nuxt/kit@3.18.1(magicast@0.3.5))(rolldown-vite@7.1.15(@types/node@24.6.2)(esbuild@0.25.9)(jiti@2.5.1)(less@4.4.1)(terser@5.43.1)(tsx@4.20.6)(yaml@2.8.1))
vite-plugin-vue-inspector: 5.3.2(rolldown-vite@7.1.15(@types/node@24.6.2)(esbuild@0.25.9)(jiti@2.5.1)(less@4.4.1)(terser@5.43.1)(tsx@4.20.6)(yaml@2.8.1))
transitivePeerDependencies:
- '@nuxt/kit'
- supports-color
- vue
vite-plugin-vue-inspector@5.3.2(rolldown-vite@7.1.15(@types/node@24.6.2)(esbuild@0.25.9)(jiti@2.5.1)(less@4.4.1)(terser@5.43.1)(tsx@4.20.6)(yaml@2.8.1)):
dependencies:
'@babel/core': 7.28.3
+1 -1
View File
@@ -8,7 +8,7 @@ packages:
- '!**/dist/**'
catalog:
'@guiiai/logg': ^1.1.0
'@guiiai/logg': 1.1.0
'@moeru/std': 0.1.0-beta.7
'@proj-airi/drizzle-duckdb-wasm': ^0.4.28
'@xsai-ext/providers-cloud': ^0.4.0-beta.4