Skip to content

Commit e922ff2

Browse files
authored
perf: make evlog zero-dependency (#54)
1 parent a865706 commit e922ff2

File tree

10 files changed

+40
-87
lines changed

10 files changed

+40
-87
lines changed

.agents/skills/create-adapter/SKILL.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ Read [references/adapter-template.md](references/adapter-template.md) for the fu
5151
Key architecture rules:
5252

5353
1. **Config interface** -- service-specific fields (API key, endpoint, etc.) plus optional `timeout?: number`
54-
2. **`getRuntimeConfig()` helper** -- dynamic `require('nitropack/runtime')` wrapped in try/catch
54+
2. **`getRuntimeConfig()`** -- import from `./_utils` (shared helper, do NOT redefine locally)
5555
3. **Config priority** (highest to lowest):
5656
- Overrides passed to `create{Name}Drain()`
5757
- `runtimeConfig.evlog.{name}`

.agents/skills/create-adapter/references/adapter-template.md

Lines changed: 2 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ Replace `{Name}`, `{name}`, and `{NAME}` with the actual service name.
66

77
```typescript
88
import type { DrainContext, WideEvent } from '../types'
9+
import { getRuntimeConfig } from './_utils'
910

1011
// --- 1. Config Interface ---
1112
// Define all service-specific configuration fields.
@@ -45,23 +46,7 @@ export function to{Name}Event(event: WideEvent): {Name}Event {
4546
}
4647
}
4748

48-
// --- 3. Runtime Config Helper ---
49-
// Dynamic require to avoid bundling issues outside Nitro.
50-
// Returns undefined when not in a Nitro context.
51-
function getRuntimeConfig(): {
52-
evlog?: { {name}?: Partial<{Name}Config> }
53-
{name}?: Partial<{Name}Config>
54-
} | undefined {
55-
try {
56-
// eslint-disable-next-line @typescript-eslint/no-require-imports
57-
const { useRuntimeConfig } = require('nitropack/runtime')
58-
return useRuntimeConfig()
59-
} catch {
60-
return undefined
61-
}
62-
}
63-
64-
// --- 4. Factory Function ---
49+
// --- 3. Factory Function ---
6550
// Returns a drain function that resolves config at call time.
6651
// Config priority: overrides > runtimeConfig.evlog.{name} > runtimeConfig.{name} > env vars
6752

bun.lock

Lines changed: 0 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/evlog/package.json

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -101,9 +101,6 @@
101101
"test:coverage": "vitest run --coverage",
102102
"typecheck": "echo 'Typecheck handled by build'"
103103
},
104-
"dependencies": {
105-
"defu": "^6.1.4"
106-
},
107104
"devDependencies": {
108105
"@nuxt/devtools": "^3.1.1",
109106
"@nuxt/schema": "^4.3.1",
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
/**
2+
* Try to get runtime config from Nitro/Nuxt environment.
3+
* Returns undefined if not in a Nitro context.
4+
*/
5+
export function getRuntimeConfig(): Record<string, unknown> | undefined {
6+
try {
7+
// eslint-disable-next-line @typescript-eslint/no-require-imports
8+
const { useRuntimeConfig } = require('nitropack/runtime')
9+
return useRuntimeConfig()
10+
} catch {
11+
return undefined
12+
}
13+
}

packages/evlog/src/adapters/axiom.ts

Lines changed: 1 addition & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import type { DrainContext, WideEvent } from '../types'
2+
import { getRuntimeConfig } from './_utils'
23

34
export interface AxiomConfig {
45
/** Axiom dataset name */
@@ -13,21 +14,6 @@ export interface AxiomConfig {
1314
timeout?: number
1415
}
1516

16-
/**
17-
* Try to get runtime config from Nitro/Nuxt environment.
18-
* Returns undefined if not in a Nitro context.
19-
*/
20-
function getRuntimeConfig(): { evlog?: { axiom?: Partial<AxiomConfig> }, axiom?: Partial<AxiomConfig> } | undefined {
21-
try {
22-
// Dynamic import to avoid bundling issues when not in Nitro
23-
// eslint-disable-next-line @typescript-eslint/no-require-imports
24-
const { useRuntimeConfig } = require('nitropack/runtime')
25-
return useRuntimeConfig()
26-
} catch {
27-
return undefined
28-
}
29-
}
30-
3117
/**
3218
* Create a drain function for sending logs to Axiom.
3319
*

packages/evlog/src/adapters/otlp.ts

Lines changed: 1 addition & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import type { DrainContext, LogLevel, WideEvent } from '../types'
2+
import { getRuntimeConfig } from './_utils'
23

34
export interface OTLPConfig {
45
/** OTLP HTTP endpoint (e.g., http://localhost:4318) */
@@ -70,21 +71,6 @@ const SEVERITY_TEXT_MAP: Record<LogLevel, string> = {
7071
error: 'ERROR',
7172
}
7273

73-
/**
74-
* Try to get runtime config from Nitro/Nuxt environment.
75-
* Returns undefined if not in a Nitro context.
76-
*/
77-
function getRuntimeConfig(): { evlog?: { otlp?: Partial<OTLPConfig> }, otlp?: Partial<OTLPConfig> } | undefined {
78-
try {
79-
// Dynamic import to avoid bundling issues when not in Nitro
80-
// eslint-disable-next-line @typescript-eslint/no-require-imports
81-
const { useRuntimeConfig } = require('nitropack/runtime')
82-
return useRuntimeConfig()
83-
} catch {
84-
return undefined
85-
}
86-
}
87-
8874
/**
8975
* Convert a value to OTLP attribute value format.
9076
*/

packages/evlog/src/adapters/posthog.ts

Lines changed: 1 addition & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import type { DrainContext, WideEvent } from '../types'
2+
import { getRuntimeConfig } from './_utils'
23

34
export interface PostHogConfig {
45
/** PostHog project API key */
@@ -21,21 +22,6 @@ export interface PostHogEvent {
2122
properties: Record<string, unknown>
2223
}
2324

24-
/**
25-
* Try to get runtime config from Nitro/Nuxt environment.
26-
* Returns undefined if not in a Nitro context.
27-
*/
28-
function getRuntimeConfig(): { evlog?: { posthog?: Partial<PostHogConfig> }, posthog?: Partial<PostHogConfig> } | undefined {
29-
try {
30-
// Dynamic import to avoid bundling issues when not in Nitro
31-
// eslint-disable-next-line @typescript-eslint/no-require-imports
32-
const { useRuntimeConfig } = require('nitropack/runtime')
33-
return useRuntimeConfig()
34-
} catch {
35-
return undefined
36-
}
37-
}
38-
3925
/**
4026
* Convert a WideEvent to a PostHog event format.
4127
*/

packages/evlog/src/adapters/sentry.ts

Lines changed: 1 addition & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import type { DrainContext, LogLevel, WideEvent } from '../types'
2+
import { getRuntimeConfig } from './_utils'
23

34
export interface SentryConfig {
45
/** Sentry DSN */
@@ -45,21 +46,6 @@ const SEVERITY_MAP: Record<LogLevel, number> = {
4546
error: 17,
4647
}
4748

48-
/**
49-
* Try to get runtime config from Nitro/Nuxt environment.
50-
* Returns undefined if not in a Nitro context.
51-
*/
52-
function getRuntimeConfig(): { evlog?: { sentry?: Partial<SentryConfig> }, sentry?: Partial<SentryConfig> } | undefined {
53-
try {
54-
// Dynamic import to avoid bundling issues when not in Nitro
55-
// eslint-disable-next-line @typescript-eslint/no-require-imports
56-
const { useRuntimeConfig } = require('nitropack/runtime')
57-
return useRuntimeConfig()
58-
} catch {
59-
return undefined
60-
}
61-
}
62-
6349
function parseSentryDsn(dsn: string): SentryDsnParts {
6450
const url = new URL(dsn)
6551
const publicKey = url.username

packages/evlog/src/logger.ts

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,24 @@
1-
import { defu } from 'defu'
21
import type { EnvironmentContext, Log, LogLevel, LoggerConfig, RequestLogger, RequestLoggerOptions, SamplingConfig, TailSamplingContext, WideEvent } from './types'
32
import { colors, detectEnvironment, formatDuration, getConsoleMethod, getLevelColor, isDev, matchesPattern } from './utils'
43

4+
function isPlainObject(val: unknown): val is Record<string, unknown> {
5+
return val !== null && typeof val === 'object' && !Array.isArray(val)
6+
}
7+
8+
function deepDefaults(base: Record<string, unknown>, defaults: Record<string, unknown>): Record<string, unknown> {
9+
const result = { ...base }
10+
for (const key in defaults) {
11+
const baseVal = result[key]
12+
const defaultVal = defaults[key]
13+
if (baseVal === undefined || baseVal === null) {
14+
result[key] = defaultVal
15+
} else if (isPlainObject(baseVal) && isPlainObject(defaultVal)) {
16+
result[key] = deepDefaults(baseVal, defaultVal)
17+
}
18+
}
19+
return result
20+
}
21+
522
let globalEnv: EnvironmentContext = {
623
service: 'app',
724
environment: 'development',
@@ -221,7 +238,7 @@ export function createRequestLogger(options: RequestLoggerOptions = {}): Request
221238

222239
return {
223240
set<T extends Record<string, unknown>>(data: T): void {
224-
context = defu(data, context) as Record<string, unknown>
241+
context = deepDefaults(data, context) as Record<string, unknown>
225242
},
226243

227244
error(error: Error | string, errorContext?: Record<string, unknown>): void {
@@ -236,7 +253,7 @@ export function createRequestLogger(options: RequestLoggerOptions = {}): Request
236253
stack: err.stack,
237254
},
238255
}
239-
context = defu(errorData, context) as Record<string, unknown>
256+
context = deepDefaults(errorData, context) as Record<string, unknown>
240257
},
241258

242259
emit(overrides?: Record<string, unknown> & { _forceKeep?: boolean }): WideEvent | null {

0 commit comments

Comments
 (0)