@gaonjs/core 0.1.3 → 0.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/env.d.ts +60 -0
- package/dist/env.js +125 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +5 -0
- package/dist/logging.d.ts +41 -0
- package/dist/logging.js +112 -0
- package/package.json +7 -1
package/dist/env.d.ts
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* env 누락·형변환 실패를 알리는 에러. 메시지가 곧 수리 안내서다
|
|
3
|
+
* (§7.5.3 — "무엇이 잘못됐다"가 아니라 "→ 어디에 무엇을 추가하라"까지).
|
|
4
|
+
*/
|
|
5
|
+
export declare class EnvError extends Error {
|
|
6
|
+
constructor(message: string);
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* `.env` 파일을 로드한다. NODE_ENV 별 오버레이(`.env.<env>`)를 나중에 얹어
|
|
10
|
+
* 우선하게 한다(이미 정의된 process.env 는 loadEnvFile 이 덮지 않는다 —
|
|
11
|
+
* 실제 환경변수가 파일보다 우선). 파일이 없으면 조용히 지나간다.
|
|
12
|
+
*
|
|
13
|
+
* `gaon dev`·`gaon serve`·`gaon work` 부팅 진입점이 가장 먼저 호출한다.
|
|
14
|
+
*/
|
|
15
|
+
export declare function loadDotEnv(root?: string): void;
|
|
16
|
+
/** 옵션 없는 문자열 env — 필수. 누락 시 EnvError(수리 안내). */
|
|
17
|
+
export interface RequiredOpts {
|
|
18
|
+
readonly default?: undefined;
|
|
19
|
+
}
|
|
20
|
+
/** 기본값이 있는 문자열 env — 누락 시 default. */
|
|
21
|
+
export interface DefaultedOpts {
|
|
22
|
+
readonly default: string;
|
|
23
|
+
}
|
|
24
|
+
export interface EnvHelper {
|
|
25
|
+
/** 필수 문자열. 누락 시 부팅 에러. */
|
|
26
|
+
(key: string): string;
|
|
27
|
+
/** 기본값이 있는 문자열. 누락 시 default. */
|
|
28
|
+
(key: string, opts: DefaultedOpts): string;
|
|
29
|
+
/** 선택 문자열 — 누락 시 undefined(EnvError 없음). */
|
|
30
|
+
optional(key: string): string | undefined;
|
|
31
|
+
/** 정수. 파싱 실패 시 EnvError. default 로 누락 허용. */
|
|
32
|
+
int(key: string, opts?: {
|
|
33
|
+
default?: number;
|
|
34
|
+
}): number;
|
|
35
|
+
/** 불리언. `1·true·yes·on`(대소문자 무시) = true. default 로 누락 허용. */
|
|
36
|
+
bool(key: string, opts?: {
|
|
37
|
+
default?: boolean;
|
|
38
|
+
}): boolean;
|
|
39
|
+
/** 콤마 구분 문자열 목록. 누락 시 default(생략 시 []). */
|
|
40
|
+
list(key: string, opts?: {
|
|
41
|
+
default?: readonly string[];
|
|
42
|
+
}): string[];
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* 타입 안전 env 헬퍼. `gaon.config.ts` 등 부팅 경로에서 호출한다 —
|
|
46
|
+
* 최상위에서 호출하면 선언된 env 누락이 **부팅 시점**에 드러난다(§7).
|
|
47
|
+
*
|
|
48
|
+
* env('DATABASE_URL') // 필수 문자열
|
|
49
|
+
* env('PORT', { default: '3000' }) // 기본값
|
|
50
|
+
* env.int('WEB_CONCURRENCY', { default: 1 })
|
|
51
|
+
* env.bool('DEBUG', { default: false })
|
|
52
|
+
* env.optional('SENTRY_DSN') // string | undefined
|
|
53
|
+
*/
|
|
54
|
+
export declare const env: EnvHelper;
|
|
55
|
+
/**
|
|
56
|
+
* 여러 필수 env 를 한 번에 검증한다 — 누락을 **모아서** 한 에러로 보고해
|
|
57
|
+
* "고치고 재시작 → 또 다른 누락" 반복을 없앤다. 부팅 진입점에서 선언 목록을
|
|
58
|
+
* 넘겨 호출한다.
|
|
59
|
+
*/
|
|
60
|
+
export declare function requireEnv(keys: readonly string[]): void;
|
package/dist/env.js
ADDED
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
// @gaonjs/core · 환경변수/시크릿 배터리 (§7 v0.14 편입)
|
|
2
|
+
//
|
|
3
|
+
// `.env` 관례로 값을 싣고, `env('KEY')` 타입 안전 헬퍼로 읽는다. 선언된
|
|
4
|
+
// env 가 누락되면 **부팅 시점**에 수리 안내를 포함한 에러로 즉시 실패한다
|
|
5
|
+
// (런타임 한복판에서 터지지 않게). 시크릿은 env 로만 주입한다 — 코드·설정
|
|
6
|
+
// 파일 하드코딩은 doctor 가 검사한다.
|
|
7
|
+
//
|
|
8
|
+
// dotenv 대신 Node 22+ 내장 `process.loadEnvFile()` 를 쓴다(engines ≥22).
|
|
9
|
+
// 신규 의존을 늘리지 않는다는 원칙(§0)과, "만들지 않고 접착한다"(§2)에 맞다.
|
|
10
|
+
import { existsSync } from 'node:fs';
|
|
11
|
+
import { join } from 'node:path';
|
|
12
|
+
/**
|
|
13
|
+
* env 누락·형변환 실패를 알리는 에러. 메시지가 곧 수리 안내서다
|
|
14
|
+
* (§7.5.3 — "무엇이 잘못됐다"가 아니라 "→ 어디에 무엇을 추가하라"까지).
|
|
15
|
+
*/
|
|
16
|
+
export class EnvError extends Error {
|
|
17
|
+
constructor(message) {
|
|
18
|
+
super(message);
|
|
19
|
+
this.name = 'EnvError';
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
function repairHint(key, reason) {
|
|
23
|
+
return (`환경변수 ${key} ${reason}\n` +
|
|
24
|
+
` → 프로젝트 루트 .env 에 다음 줄을 추가하고 프로세스를 재시작하세요:\n` +
|
|
25
|
+
` ${key}=<값>\n` +
|
|
26
|
+
` (예시는 .env.example 을 참고. 시크릿은 코드·설정 파일에 하드코딩하지 마세요.)`);
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* `.env` 파일을 로드한다. NODE_ENV 별 오버레이(`.env.<env>`)를 나중에 얹어
|
|
30
|
+
* 우선하게 한다(이미 정의된 process.env 는 loadEnvFile 이 덮지 않는다 —
|
|
31
|
+
* 실제 환경변수가 파일보다 우선). 파일이 없으면 조용히 지나간다.
|
|
32
|
+
*
|
|
33
|
+
* `gaon dev`·`gaon serve`·`gaon work` 부팅 진입점이 가장 먼저 호출한다.
|
|
34
|
+
*/
|
|
35
|
+
export function loadDotEnv(root = process.cwd()) {
|
|
36
|
+
const nodeEnv = process.env.NODE_ENV;
|
|
37
|
+
// 환경별 오버레이를 먼저 로드해 우선순위를 준다: loadEnvFile 은 이미
|
|
38
|
+
// 설정된 키를 덮지 않으므로, 더 구체적인 파일을 앞서 로드한다.
|
|
39
|
+
if (nodeEnv) {
|
|
40
|
+
const envFile = join(root, `.env.${nodeEnv}`);
|
|
41
|
+
if (existsSync(envFile))
|
|
42
|
+
process.loadEnvFile(envFile);
|
|
43
|
+
}
|
|
44
|
+
const base = join(root, '.env');
|
|
45
|
+
if (existsSync(base))
|
|
46
|
+
process.loadEnvFile(base);
|
|
47
|
+
}
|
|
48
|
+
function raw(key) {
|
|
49
|
+
const v = process.env[key];
|
|
50
|
+
// 빈 문자열은 "미설정"으로 취급한다 — `.env` 에 `KEY=` 만 적힌 흔한 실수가
|
|
51
|
+
// 조용히 통과해 런타임에서 터지는 것을 부팅 시점 에러로 앞당긴다.
|
|
52
|
+
return v === undefined || v === '' ? undefined : v;
|
|
53
|
+
}
|
|
54
|
+
function required(key) {
|
|
55
|
+
const v = raw(key);
|
|
56
|
+
if (v === undefined)
|
|
57
|
+
throw new EnvError(repairHint(key, '가 설정되지 않았습니다.'));
|
|
58
|
+
return v;
|
|
59
|
+
}
|
|
60
|
+
const base = ((key, opts) => {
|
|
61
|
+
const v = raw(key);
|
|
62
|
+
if (v !== undefined)
|
|
63
|
+
return v;
|
|
64
|
+
if (opts && 'default' in opts && opts.default !== undefined)
|
|
65
|
+
return opts.default;
|
|
66
|
+
return required(key);
|
|
67
|
+
});
|
|
68
|
+
base.optional = (key) => raw(key);
|
|
69
|
+
base.int = (key, opts) => {
|
|
70
|
+
const v = raw(key);
|
|
71
|
+
if (v === undefined) {
|
|
72
|
+
if (opts && opts.default !== undefined)
|
|
73
|
+
return opts.default;
|
|
74
|
+
throw new EnvError(repairHint(key, '가 설정되지 않았습니다(정수 필요).'));
|
|
75
|
+
}
|
|
76
|
+
const n = Number(v);
|
|
77
|
+
if (!Number.isInteger(n)) {
|
|
78
|
+
throw new EnvError(repairHint(key, `= "${v}" 는 정수가 아닙니다.`));
|
|
79
|
+
}
|
|
80
|
+
return n;
|
|
81
|
+
};
|
|
82
|
+
base.bool = (key, opts) => {
|
|
83
|
+
const v = raw(key);
|
|
84
|
+
if (v === undefined) {
|
|
85
|
+
if (opts && opts.default !== undefined)
|
|
86
|
+
return opts.default;
|
|
87
|
+
throw new EnvError(repairHint(key, '가 설정되지 않았습니다(불리언 필요).'));
|
|
88
|
+
}
|
|
89
|
+
return ['1', 'true', 'yes', 'on'].includes(v.toLowerCase());
|
|
90
|
+
};
|
|
91
|
+
base.list = (key, opts) => {
|
|
92
|
+
const v = raw(key);
|
|
93
|
+
if (v === undefined)
|
|
94
|
+
return opts?.default ? [...opts.default] : [];
|
|
95
|
+
return v
|
|
96
|
+
.split(',')
|
|
97
|
+
.map((s) => s.trim())
|
|
98
|
+
.filter((s) => s.length > 0);
|
|
99
|
+
};
|
|
100
|
+
/**
|
|
101
|
+
* 타입 안전 env 헬퍼. `gaon.config.ts` 등 부팅 경로에서 호출한다 —
|
|
102
|
+
* 최상위에서 호출하면 선언된 env 누락이 **부팅 시점**에 드러난다(§7).
|
|
103
|
+
*
|
|
104
|
+
* env('DATABASE_URL') // 필수 문자열
|
|
105
|
+
* env('PORT', { default: '3000' }) // 기본값
|
|
106
|
+
* env.int('WEB_CONCURRENCY', { default: 1 })
|
|
107
|
+
* env.bool('DEBUG', { default: false })
|
|
108
|
+
* env.optional('SENTRY_DSN') // string | undefined
|
|
109
|
+
*/
|
|
110
|
+
export const env = base;
|
|
111
|
+
/**
|
|
112
|
+
* 여러 필수 env 를 한 번에 검증한다 — 누락을 **모아서** 한 에러로 보고해
|
|
113
|
+
* "고치고 재시작 → 또 다른 누락" 반복을 없앤다. 부팅 진입점에서 선언 목록을
|
|
114
|
+
* 넘겨 호출한다.
|
|
115
|
+
*/
|
|
116
|
+
export function requireEnv(keys) {
|
|
117
|
+
const missing = keys.filter((k) => raw(k) === undefined);
|
|
118
|
+
if (missing.length === 0)
|
|
119
|
+
return;
|
|
120
|
+
const lines = missing.map((k) => ` ${k}=<값>`).join('\n');
|
|
121
|
+
throw new EnvError(`필수 환경변수 ${missing.length}개가 누락되었습니다: ${missing.join(', ')}\n` +
|
|
122
|
+
` → 프로젝트 루트 .env 에 다음 줄을 추가하고 프로세스를 재시작하세요:\n` +
|
|
123
|
+
lines +
|
|
124
|
+
`\n (.env.example 참고. 시크릿은 코드·설정 파일에 하드코딩하지 마세요.)`);
|
|
125
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -27,3 +27,5 @@ export type IsHidden<V> = "__gaon_hidden__" extends keyof V ? true : false;
|
|
|
27
27
|
* 전역 심볼 레지스트리(`Symbol.for`)라 패키지·번들 경계를 넘어도 동일하다.
|
|
28
28
|
*/
|
|
29
29
|
export declare const HIDDEN_COLUMNS: unique symbol;
|
|
30
|
+
export { env, loadDotEnv, requireEnv, EnvError, type EnvHelper, } from "./env.js";
|
|
31
|
+
export { log, rootLogger, setRootLogger, runWithLogContext, enterLogContext, currentLogContext, type Logger, type LogContext, } from "./logging.js";
|
package/dist/index.js
CHANGED
|
@@ -26,3 +26,8 @@ export const MILESTONES = [
|
|
|
26
26
|
* 전역 심볼 레지스트리(`Symbol.for`)라 패키지·번들 경계를 넘어도 동일하다.
|
|
27
27
|
*/
|
|
28
28
|
export const HIDDEN_COLUMNS = Symbol.for("gaon.hiddenColumns");
|
|
29
|
+
// ── 배터리 (§7 M8) ──────────────────────────────────────────────
|
|
30
|
+
// env·구조화 로깅은 코어에 내장한다 — 어느 프로세스(웹·워커·허브)에서든
|
|
31
|
+
// 같은 헬퍼를 쓴다. 시드/메일/스토리지/i18n 은 각자 패키지로 분리한다.
|
|
32
|
+
export { env, loadDotEnv, requireEnv, EnvError, } from "./env.js";
|
|
33
|
+
export { log, rootLogger, setRootLogger, runWithLogContext, enterLogContext, currentLogContext, } from "./logging.js";
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { type Logger as PinoLogger } from 'pino';
|
|
2
|
+
/** 로그 컨텍스트 — 요청/잡/이벤트 경계에서 심어 하위 로그에 자동 전파된다. */
|
|
3
|
+
export interface LogContext {
|
|
4
|
+
/** 요청/잡/이벤트 상관관계 ID. HTTP 요청·잡 실행·이벤트 처리마다 부여. */
|
|
5
|
+
readonly requestId?: string;
|
|
6
|
+
/** 추가 상관 필드(잡 이름·이벤트명 등). 로그에 그대로 병합된다. */
|
|
7
|
+
readonly [key: string]: unknown;
|
|
8
|
+
}
|
|
9
|
+
/** 현재 로그 컨텍스트를 읽는다(없으면 undefined). */
|
|
10
|
+
export declare function currentLogContext(): LogContext | undefined;
|
|
11
|
+
/**
|
|
12
|
+
* 주어진 컨텍스트로 fn 을 실행한다 — 그 안의 모든 로그에 컨텍스트 필드가
|
|
13
|
+
* 자동으로 붙는다(요청 ID 전파). 중첩 시 바깥 컨텍스트에 병합한다.
|
|
14
|
+
*/
|
|
15
|
+
export declare function runWithLogContext<T>(context: LogContext, fn: () => T): T;
|
|
16
|
+
/**
|
|
17
|
+
* 콜백 없이 현재 비동기 컨텍스트에 로그 컨텍스트를 심는다(enterWith). Fastify
|
|
18
|
+
* onRequest 훅처럼 "이 지점 이후"의 모든 후속 처리에 요청 ID 를 전파해야 할
|
|
19
|
+
* 때 쓴다 — 이후 같은 요청의 모든 log.* 호출에 자동으로 붙는다.
|
|
20
|
+
*/
|
|
21
|
+
export declare function enterLogContext(context: LogContext): void;
|
|
22
|
+
/** log.info(msg, fields) 형태의 어디서나 쓰는 로그 헬퍼(§7). */
|
|
23
|
+
export interface Logger {
|
|
24
|
+
info(message: string, fields?: Record<string, unknown>): void;
|
|
25
|
+
warn(message: string, fields?: Record<string, unknown>): void;
|
|
26
|
+
error(message: string, fields?: Record<string, unknown>): void;
|
|
27
|
+
debug(message: string, fields?: Record<string, unknown>): void;
|
|
28
|
+
/** 고정 필드가 붙은 하위 로거(모듈·앱 스코프 등). */
|
|
29
|
+
child(fields: Record<string, unknown>): Logger;
|
|
30
|
+
/** 하위 통합(Fastify 등)이 요구하는 원시 pino 인스턴스. */
|
|
31
|
+
readonly raw: PinoLogger;
|
|
32
|
+
}
|
|
33
|
+
/** 프로세스 루트 로거(지연 초기화 — NODE_ENV·GAON_LOG_LEVEL 를 부팅 후 읽게). */
|
|
34
|
+
export declare function rootLogger(): PinoLogger;
|
|
35
|
+
/**
|
|
36
|
+
* 테스트/부팅에서 루트 로거를 명시 주입한다(요청별 재구성 방지·조용한 모드).
|
|
37
|
+
* 웹 앱은 Fastify 의 pino 인스턴스를 여기 심어 로거를 하나로 통일한다.
|
|
38
|
+
*/
|
|
39
|
+
export declare function setRootLogger(p: PinoLogger): void;
|
|
40
|
+
/** 어디서나 쓰는 전역 로거. 컨텍스트(요청 ID)는 자동 전파된다(§7). */
|
|
41
|
+
export declare const log: Logger;
|
package/dist/logging.js
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
// @gaonjs/core · 구조화 로깅 배터리 (§7 v0.14 편입)
|
|
2
|
+
//
|
|
3
|
+
// JSON 구조화 로그를 코어에 내장한다. 요청(HTTP)·잡·이벤트 처리는 자동으로
|
|
4
|
+
// 로그가 남고(요청 ID 전파 포함), 개발 모드는 사람이 읽는 포맷, 운영은 JSON.
|
|
5
|
+
// `log.info(msg, fields)` 헬퍼를 어디서든 쓴다.
|
|
6
|
+
//
|
|
7
|
+
// pino 를 접착한다(§2 — 만들지 않고 접착) — Fastify 가 이미 pino 10 을 쓰므로
|
|
8
|
+
// 웹·워커 전 프로세스에서 로거가 하나로 통일된다. 요청 ID 는
|
|
9
|
+
// AsyncLocalStorage 로 전파하고, pino mixin 이 매 로그에 자동으로 얹는다.
|
|
10
|
+
import { AsyncLocalStorage } from 'node:async_hooks';
|
|
11
|
+
import { createRequire } from 'node:module';
|
|
12
|
+
import pino from 'pino';
|
|
13
|
+
const contextStore = new AsyncLocalStorage();
|
|
14
|
+
/** 현재 로그 컨텍스트를 읽는다(없으면 undefined). */
|
|
15
|
+
export function currentLogContext() {
|
|
16
|
+
return contextStore.getStore();
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* 주어진 컨텍스트로 fn 을 실행한다 — 그 안의 모든 로그에 컨텍스트 필드가
|
|
20
|
+
* 자동으로 붙는다(요청 ID 전파). 중첩 시 바깥 컨텍스트에 병합한다.
|
|
21
|
+
*/
|
|
22
|
+
export function runWithLogContext(context, fn) {
|
|
23
|
+
const parent = contextStore.getStore();
|
|
24
|
+
return contextStore.run({ ...parent, ...context }, fn);
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* 콜백 없이 현재 비동기 컨텍스트에 로그 컨텍스트를 심는다(enterWith). Fastify
|
|
28
|
+
* onRequest 훅처럼 "이 지점 이후"의 모든 후속 처리에 요청 ID 를 전파해야 할
|
|
29
|
+
* 때 쓴다 — 이후 같은 요청의 모든 log.* 호출에 자동으로 붙는다.
|
|
30
|
+
*/
|
|
31
|
+
export function enterLogContext(context) {
|
|
32
|
+
const parent = contextStore.getStore();
|
|
33
|
+
contextStore.enterWith({ ...parent, ...context });
|
|
34
|
+
}
|
|
35
|
+
function isProd() {
|
|
36
|
+
return process.env.NODE_ENV === 'production';
|
|
37
|
+
}
|
|
38
|
+
/** pino-pretty 가 이 프로세스에서 해석 가능한지(설치 여부). 없으면 JSON 폴백. */
|
|
39
|
+
function prettyAvailable() {
|
|
40
|
+
try {
|
|
41
|
+
createRequire(import.meta.url).resolve('pino-pretty');
|
|
42
|
+
return true;
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
return false;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
function defaultLevel() {
|
|
49
|
+
if (process.env.GAON_LOG_LEVEL)
|
|
50
|
+
return process.env.GAON_LOG_LEVEL;
|
|
51
|
+
// 테스트는 조용히(로깅 동작은 캡처 로거로 별도 검증) — 운영 info, 그 외 debug.
|
|
52
|
+
if (process.env.NODE_ENV === 'test')
|
|
53
|
+
return 'silent';
|
|
54
|
+
return isProd() ? 'info' : 'debug';
|
|
55
|
+
}
|
|
56
|
+
function createPino() {
|
|
57
|
+
const level = defaultLevel();
|
|
58
|
+
// 컨텍스트(요청 ID 등)를 매 로그에 병합한다 — pino mixin 은 로그 시점에
|
|
59
|
+
// 호출되므로 AsyncLocalStorage 의 현재 값을 그대로 실을 수 있다.
|
|
60
|
+
const mixin = () => contextStore.getStore() ?? {};
|
|
61
|
+
// 개발 모드는 사람이 읽는 포맷(pino-pretty), 그 외(운영·테스트)는 JSON 한
|
|
62
|
+
// 줄. pretty 는 워커 스레드 transport 라 (1)명시적 development 이고
|
|
63
|
+
// (2)pino-pretty 가 해석될 때만 켠다 — 테스트(NODE_ENV=test)·미설치
|
|
64
|
+
// 환경에서 코어가 죽지 않게 JSON 으로 폴백한다.
|
|
65
|
+
if (process.env.NODE_ENV === 'development' && prettyAvailable()) {
|
|
66
|
+
return pino({
|
|
67
|
+
level,
|
|
68
|
+
mixin,
|
|
69
|
+
transport: {
|
|
70
|
+
target: 'pino-pretty',
|
|
71
|
+
options: { colorize: true, translateTime: 'HH:MM:ss.l', ignore: 'pid,hostname' },
|
|
72
|
+
},
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
return pino({ level, mixin });
|
|
76
|
+
}
|
|
77
|
+
function wrap(p) {
|
|
78
|
+
return {
|
|
79
|
+
// pino 네이티브는 (mergingObject, message) 순서이므로 (msg, fields)를 뒤집는다.
|
|
80
|
+
info: (message, fields) => p.info(fields ?? {}, message),
|
|
81
|
+
warn: (message, fields) => p.warn(fields ?? {}, message),
|
|
82
|
+
error: (message, fields) => p.error(fields ?? {}, message),
|
|
83
|
+
debug: (message, fields) => p.debug(fields ?? {}, message),
|
|
84
|
+
child: (fields) => wrap(p.child(fields)),
|
|
85
|
+
raw: p,
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
let rootPino;
|
|
89
|
+
/** 프로세스 루트 로거(지연 초기화 — NODE_ENV·GAON_LOG_LEVEL 를 부팅 후 읽게). */
|
|
90
|
+
export function rootLogger() {
|
|
91
|
+
if (!rootPino)
|
|
92
|
+
rootPino = createPino();
|
|
93
|
+
return rootPino;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* 테스트/부팅에서 루트 로거를 명시 주입한다(요청별 재구성 방지·조용한 모드).
|
|
97
|
+
* 웹 앱은 Fastify 의 pino 인스턴스를 여기 심어 로거를 하나로 통일한다.
|
|
98
|
+
*/
|
|
99
|
+
export function setRootLogger(p) {
|
|
100
|
+
rootPino = p;
|
|
101
|
+
}
|
|
102
|
+
/** 어디서나 쓰는 전역 로거. 컨텍스트(요청 ID)는 자동 전파된다(§7). */
|
|
103
|
+
export const log = {
|
|
104
|
+
info: (m, f) => wrap(rootLogger()).info(m, f),
|
|
105
|
+
warn: (m, f) => wrap(rootLogger()).warn(m, f),
|
|
106
|
+
error: (m, f) => wrap(rootLogger()).error(m, f),
|
|
107
|
+
debug: (m, f) => wrap(rootLogger()).debug(m, f),
|
|
108
|
+
child: (f) => wrap(rootLogger().child(f)),
|
|
109
|
+
get raw() {
|
|
110
|
+
return rootLogger();
|
|
111
|
+
},
|
|
112
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gaonjs/core",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.4",
|
|
4
4
|
"description": "Gaon 커널: 설정·생명주기·플러그인 시스템·doctor (M2+ 구현 예정)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -23,6 +23,12 @@
|
|
|
23
23
|
"dist",
|
|
24
24
|
"README.md"
|
|
25
25
|
],
|
|
26
|
+
"dependencies": {
|
|
27
|
+
"pino": "^10.3.1"
|
|
28
|
+
},
|
|
29
|
+
"devDependencies": {
|
|
30
|
+
"pino-pretty": "^13.1.3"
|
|
31
|
+
},
|
|
26
32
|
"scripts": {
|
|
27
33
|
"build": "node ../../node_modules/typescript/bin/tsc -p tsconfig.json"
|
|
28
34
|
}
|