@faapi/faapi 6.7.0 → 6.8.0

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/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { A as AppRegistries, R as RouteManifest, F as FaapiContext, C as CorsOptions, T as TaskClient, H as HelmetOptions, L as LoggerOptions, a as FaapiMiddleware, I as InjectorMap, b as TaskFailedHandler, c as TaskQueueDeps, d as TaskQueue, e as TaskDriver, f as TaskRegistry, g as TaskManifest, h as ToolMetadata, i as AgentCore, j as AgentMetadata, W as WsRouteManifest } from './routeTypes-CsL7xtqu.js';
2
- export { k as AgentHandleFactory, l as AgentHandleStore, m as AgentPathMeta, n as AgentRegistry, o as AgentToolDescriptor, p as FaapiContextConfig, q as FaapiTaskMeta, r as FailOptions, s as Injector, t as RouteInfo, u as RouteInputSchema, v as RouteOutputSchema, w as RouteParamSchema, S as SkillRegistry, x as SseEvent, y as SseWriter, z as TaskContext, B as TaskDriverJob, D as TaskDriverProcess, E as TaskDriverRecord, G as TaskFailedInfo, J as TaskJob, K as TaskJobStatus, M as TaskMetadata, N as TaskModule, O as TaskRegistriesSnapshot, P as TaskRegistriesView, Q as ToolCore, U as ToolPathMeta, V as ToolRegistry, X as cors, Y as createAppRegistries, Z as createTaskRegistriesView, _ as createTaskRegistry, $ as helmet, a0 as logger } from './routeTypes-CsL7xtqu.js';
1
+ import { A as AppRegistries, R as RouteManifest, F as FaapiContext, C as CorsOptions, T as TaskClient, H as HelmetOptions, L as LoggerOptions, a as LogConfig, b as FaapiMiddleware, I as InjectorMap, c as TaskFailedHandler, d as TaskQueueDeps, e as TaskQueue, f as TaskDriver, g as TaskRegistry, h as TaskManifest, i as ToolMetadata, j as AgentCore, k as AgentMetadata, l as CreateLoggerOptions, m as Logger, W as WsRouteManifest } from './routeTypes-J5pPbjwR.js';
2
+ export { n as AgentHandleFactory, o as AgentHandleStore, p as AgentPathMeta, q as AgentRegistry, r as AgentToolDescriptor, s as FaapiContextConfig, t as FaapiTaskMeta, u as FailOptions, v as Injector, w as LogEntry, x as LogLevel, y as LogSink, z as RouteInfo, B as RouteInputSchema, D as RouteOutputSchema, E as RouteParamSchema, S as SkillRegistry, G as SseEvent, J as SseWriter, K as TaskContext, M as TaskDriverJob, N as TaskDriverProcess, O as TaskDriverRecord, P as TaskFailedInfo, Q as TaskJob, U as TaskJobStatus, V as TaskMetadata, X as TaskModule, Y as TaskRegistriesSnapshot, Z as TaskRegistriesView, _ as ToolCore, $ as ToolPathMeta, a0 as ToolRegistry, a1 as cors, a2 as createAppRegistries, a3 as createTaskRegistriesView, a4 as createTaskRegistry, a5 as helmet, a6 as logger } from './routeTypes-J5pPbjwR.js';
3
3
  import * as node_http from 'node:http';
4
4
  import { Server, IncomingMessage, ServerResponse } from 'node:http';
5
5
  import { Socket } from 'node:net';
@@ -552,6 +552,26 @@ interface FaapiConfig {
552
552
  bodyLimit?: number;
553
553
  /** 日志中间件配置 */
554
554
  logger?: LoggerOptions | boolean;
555
+ /**
556
+ * 业务日志器全局配置(与 `logger` 请求日志中间件是两条独立管道,详见 logger/logger.md)
557
+ *
558
+ * - `false`:完全静默(含 error,测试降噪)
559
+ * - `true` / 缺省:默认级别(显式 level > LOG_LEVEL env > 'info')+ console 文本输出
560
+ * - `LogConfig`:精细配置 level 与 sink(sink 可整体接管接 pino/winston/文件)
561
+ *
562
+ * 生效范围:`ctx.log` / 参数注入 `log` / `createLogger` / `taskCtx.log`。
563
+ * 日志全局配置是进程级资源,多 app 同进程时后启动覆盖先启动。
564
+ *
565
+ * ```ts
566
+ * export default {
567
+ * log: {
568
+ * level: 'debug',
569
+ * sink: (entry) => pinoLogger[entry.level]({ scope: entry.scope, ...entry.fields }, entry.message),
570
+ * },
571
+ * } satisfies FaapiConfig;
572
+ * ```
573
+ */
574
+ log?: LogConfig | boolean;
555
575
  /** HTTP/2 配置,false 禁用(默认 http/1.1) */
556
576
  http2?: Http2Options | boolean;
557
577
  /**
@@ -1466,6 +1486,26 @@ declare function loadConfig(rootDir: string, dist: string): Promise<Partial<Faap
1466
1486
  */
1467
1487
  declare function loadEnv(rootDir: string): void;
1468
1488
 
1489
+ /**
1490
+ * 设置全局日志配置(进程级,`createAppBase` 启动时以 `config.log` 调用)
1491
+ *
1492
+ * - `false`:完全静默(含 error,测试降噪场景)
1493
+ * - `true` / `{}`:默认级别(显式 level > LOG_LEVEL env > 'info')+ 默认 console sink
1494
+ * - `{ level, sink }`:精细配置;非法 level 抛错(启动期 fail fast)
1495
+ * - `undefined`:重置为默认(编程式多 app 切换全局配置用)
1496
+ *
1497
+ * LOG_LEVEL env 仅在本函数读取——app 启动路径必经此处(env 非法在启动期报错);
1498
+ * 纯编程式不调本函数时 env 不生效(默认 info)。
1499
+ */
1500
+ declare function configureLogging(config?: LogConfig | boolean | undefined): void;
1501
+ /**
1502
+ * 创建日志器
1503
+ *
1504
+ * @param scope 分类标签(如 'db'、'http'),默认无分类
1505
+ * @param options 实例级覆盖(level/sink/构造 fields)
1506
+ */
1507
+ declare function createLogger(scope?: string, options?: CreateLoggerOptions): Logger;
1508
+
1469
1509
  declare const VALIDATION_ERROR = "VALIDATION_ERROR";
1470
1510
  declare const ROUTE_NOT_FOUND = "ROUTE_NOT_FOUND";
1471
1511
  declare const METHOD_NOT_ALLOWED = "METHOD_NOT_ALLOWED";
@@ -1666,4 +1706,4 @@ type ProdApp = AppBase;
1666
1706
  */
1667
1707
  declare function createProdApp(options?: CreateAppOptions): Promise<ProdApp>;
1668
1708
 
1669
- export { type AgentConfig, AgentCore, AgentMetadata, type AgentModule, type ProdApp as App, AppRegistries, CorsOptions, type CreateAppOptions, type CronScheduler, type DevApp, type FaapiConfig, FaapiContext, FaapiError, FaapiMiddleware, type FaapiPlugin, type HandlerTypeInfo, HelmetOptions, type InjectOptions, type InjectResponse, InjectorMap, InternalError, type LifecycleContext, type LifecycleHooks, type LlmConfig, type LlmModelConfig, LoggerOptions, MethodNotAllowedError, ModuleLoadError, type PluginContext, type PluginDeclaration, type ProdApp, type PropertyType, type RequestHandler, type ResponseConfig, RouteManifest, RouteNotFoundError, type RouteSchemaSource, type RuntimeType, SchemaExtractionError, TASK_PATTERNS, type TaskBullMqOptions, TaskClient, type TaskConfig, TaskDriver, TaskFailedHandler, TaskManifest, type TaskPgBossOptions, TaskQueue, TaskRegistry, ToolMetadata, type ToolModule, type ToolSchemaModule, type TypeConstraint, type UpgradeHandler, ValidationError, type ValidationErrorCode, type ValidationIssue, type WsContext, type WsEventHandlers, type WsHandler, type WsSocket, clearAgentHandleFactory, collectRouteSchemaSources, createProdApp as createApp, createCronScheduler, createDevApp, createProdApp, createProgram, createPrograms, createTaskQueue, extractTypeInfo, getAgent, getAgentEntry, getApp, getInputTypeForMethod, getSkill, getTool, getToolSchemaPath, hydrateSkillRegistry, invalidateProgramCache, listSkills, loadAgentModule, loadConfig, loadEnv, loadTaskDriver, loadToolModule, loadToolSchema, registerAgentHandleFactory, removeSkill, resolveAgentTools, resolveSubAgents, resolveTypeNode, scanTasks, upsertSkill };
1709
+ export { type AgentConfig, AgentCore, AgentMetadata, type AgentModule, type ProdApp as App, AppRegistries, CorsOptions, type CreateAppOptions, CreateLoggerOptions, type CronScheduler, type DevApp, type FaapiConfig, FaapiContext, FaapiError, FaapiMiddleware, type FaapiPlugin, type HandlerTypeInfo, HelmetOptions, type InjectOptions, type InjectResponse, InjectorMap, InternalError, type LifecycleContext, type LifecycleHooks, type LlmConfig, type LlmModelConfig, LogConfig, Logger, LoggerOptions, MethodNotAllowedError, ModuleLoadError, type PluginContext, type PluginDeclaration, type ProdApp, type PropertyType, type RequestHandler, type ResponseConfig, RouteManifest, RouteNotFoundError, type RouteSchemaSource, type RuntimeType, SchemaExtractionError, TASK_PATTERNS, type TaskBullMqOptions, TaskClient, type TaskConfig, TaskDriver, TaskFailedHandler, TaskManifest, type TaskPgBossOptions, TaskQueue, TaskRegistry, ToolMetadata, type ToolModule, type ToolSchemaModule, type TypeConstraint, type UpgradeHandler, ValidationError, type ValidationErrorCode, type ValidationIssue, type WsContext, type WsEventHandlers, type WsHandler, type WsSocket, clearAgentHandleFactory, collectRouteSchemaSources, configureLogging, createProdApp as createApp, createCronScheduler, createDevApp, createLogger, createProdApp, createProgram, createPrograms, createTaskQueue, extractTypeInfo, getAgent, getAgentEntry, getApp, getInputTypeForMethod, getSkill, getTool, getToolSchemaPath, hydrateSkillRegistry, invalidateProgramCache, listSkills, loadAgentModule, loadConfig, loadEnv, loadTaskDriver, loadToolModule, loadToolSchema, registerAgentHandleFactory, removeSkill, resolveAgentTools, resolveSubAgents, resolveTypeNode, scanTasks, upsertSkill };
package/dist/index.js CHANGED
@@ -1122,8 +1122,10 @@ var init_resolveInjection = __esm({
1122
1122
  // Phase 2.3
1123
1123
  agents: "agents",
1124
1124
  // Phase 2.3
1125
- tasks: "tasks"
1125
+ tasks: "tasks",
1126
1126
  // 任务子系统:TaskClient(入队/查询)
1127
+ log: "log"
1128
+ // 请求级日志器(ctx.log 同一实例:scope http,自动带 requestId 等字段)
1127
1129
  };
1128
1130
  injectionCache = /* @__PURE__ */ new WeakMap();
1129
1131
  }
@@ -2066,6 +2068,36 @@ function serializeError(err) {
2066
2068
  return { name: 'Error', message: String(err), stack: undefined, props: {} };
2067
2069
  }
2068
2070
  `;
2071
+ var BUILD_LOGGER_SOURCE = `
2072
+ var LOG_RANK = { debug: 0, info: 1, warn: 2, error: 3 };
2073
+ function createTaskLogger(level, scope, fields) {
2074
+ function make(suffix) {
2075
+ function write(lvl, message, callFields) {
2076
+ if (LOG_RANK[lvl] < LOG_RANK[level]) return;
2077
+ var entry = { level: lvl, message: message, time: new Date().toISOString() };
2078
+ var fullScope = suffix === undefined ? scope : scope === undefined ? suffix : scope + ':' + suffix;
2079
+ if (fullScope !== undefined) entry.scope = fullScope;
2080
+ if (fields !== undefined || callFields !== undefined) {
2081
+ entry.fields = Object.assign({}, fields, callFields);
2082
+ }
2083
+ try {
2084
+ parentPort.postMessage({ type: 'log', entry: entry });
2085
+ } catch (_) {
2086
+ entry.fields = { warning: 'log fields not cloneable across worker boundary, dropped' };
2087
+ parentPort.postMessage({ type: 'log', entry: entry });
2088
+ }
2089
+ }
2090
+ return {
2091
+ debug: function (m, f) { write('debug', m, f); },
2092
+ info: function (m, f) { write('info', m, f); },
2093
+ warn: function (m, f) { write('warn', m, f); },
2094
+ error: function (m, f) { write('error', m, f); },
2095
+ child: function (cs) { return make(suffix === undefined ? cs : suffix + ':' + cs); },
2096
+ };
2097
+ }
2098
+ return make(undefined);
2099
+ }
2100
+ `;
2069
2101
  function buildWrapperSource(moduleUrl) {
2070
2102
  return `
2071
2103
  import { parentPort } from 'node:worker_threads';
@@ -2075,6 +2107,8 @@ ${BUILD_VIEW_SOURCE}
2075
2107
 
2076
2108
  ${SERIALIZE_ERROR_SOURCE}
2077
2109
 
2110
+ ${BUILD_LOGGER_SOURCE}
2111
+
2078
2112
  const run = mod.run;
2079
2113
  if (typeof run !== 'function') {
2080
2114
  parentPort.postMessage({
@@ -2094,7 +2128,8 @@ if (typeof run !== 'function') {
2094
2128
  try {
2095
2129
  const registries = buildRegistriesView(msg.registries);
2096
2130
  const progress = (value) => parentPort.postMessage({ type: 'progress', value });
2097
- const result = await run(payload, { ...taskCtx, signal: controller.signal, registries, progress });
2131
+ const taskLog = msg.log ? createTaskLogger(msg.log.level, msg.log.scope, msg.log.fields) : undefined;
2132
+ const result = await run(payload, { ...taskCtx, signal: controller.signal, registries, progress, log: taskLog });
2098
2133
  parentPort.postMessage({ type: 'done', result });
2099
2134
  } catch (err) {
2100
2135
  const serialized = serializeError(err);
@@ -2202,6 +2237,8 @@ async function runTaskInWorker(options) {
2202
2237
  finish(() => reject(reviveError(msg.error)));
2203
2238
  } else if (msg?.type === "progress") {
2204
2239
  options.onProgress?.(msg.value);
2240
+ } else if (msg?.type === "log") {
2241
+ options.onLog?.(msg.entry);
2205
2242
  }
2206
2243
  }
2207
2244
  );
@@ -2225,7 +2262,8 @@ async function runTaskInWorker(options) {
2225
2262
  type: "run",
2226
2263
  payload,
2227
2264
  taskCtx: { config: safeConfig(taskCtx.config), job: taskCtx.job },
2228
- registries: options.registries
2265
+ registries: options.registries,
2266
+ log: options.log
2229
2267
  });
2230
2268
  } catch (err) {
2231
2269
  finish(
@@ -2237,6 +2275,118 @@ async function runTaskInWorker(options) {
2237
2275
  });
2238
2276
  }
2239
2277
 
2278
+ // src/logger/logger.ts
2279
+ var LEVEL_RANK = { debug: 0, info: 1, warn: 2, error: 3 };
2280
+ var VALID_LEVELS = ["debug", "info", "warn", "error"];
2281
+ function assertLogLevel(value, source) {
2282
+ if (typeof value !== "string" || !VALID_LEVELS.includes(value)) {
2283
+ throw new Error(
2284
+ `[faapi] Invalid log level from ${source}: ${String(value)}. Expected one of: ${VALID_LEVELS.join(", ")}.`
2285
+ );
2286
+ }
2287
+ }
2288
+ var globalState = {
2289
+ level: null,
2290
+ sink: null,
2291
+ disabled: false
2292
+ };
2293
+ function configureLogging(config) {
2294
+ if (config === void 0) {
2295
+ globalState.level = null;
2296
+ globalState.sink = null;
2297
+ globalState.disabled = false;
2298
+ return;
2299
+ }
2300
+ const objectConfig = typeof config === "object" ? config : {};
2301
+ globalState.disabled = config === false;
2302
+ globalState.sink = objectConfig.sink ?? null;
2303
+ if (objectConfig.level !== void 0) {
2304
+ assertLogLevel(objectConfig.level, "config.log.level");
2305
+ globalState.level = objectConfig.level;
2306
+ } else if (process.env.LOG_LEVEL !== void 0) {
2307
+ assertLogLevel(process.env.LOG_LEVEL, "LOG_LEVEL env");
2308
+ globalState.level = process.env.LOG_LEVEL;
2309
+ } else {
2310
+ globalState.level = null;
2311
+ }
2312
+ }
2313
+ function globalThreshold() {
2314
+ return globalState.level ?? "info";
2315
+ }
2316
+ function getEffectiveLogLevel() {
2317
+ return globalThreshold();
2318
+ }
2319
+ function serializeFieldValue(value) {
2320
+ if (value instanceof Error) {
2321
+ return { name: value.name, message: value.message, stack: value.stack };
2322
+ }
2323
+ return value;
2324
+ }
2325
+ function formatEntry(entry) {
2326
+ let line = `[${entry.time}] ${entry.level.toUpperCase()} `;
2327
+ if (entry.scope) line += `[${entry.scope}] `;
2328
+ line += entry.message;
2329
+ if (entry.fields !== void 0) {
2330
+ const plain = Object.fromEntries(
2331
+ Object.entries(entry.fields).map(([k, v]) => [k, serializeFieldValue(v)])
2332
+ );
2333
+ try {
2334
+ line += ` ${JSON.stringify(plain)}`;
2335
+ } catch (err) {
2336
+ line += ` [unserializable fields: ${err instanceof Error ? err.message : String(err)}]`;
2337
+ }
2338
+ }
2339
+ return line;
2340
+ }
2341
+ var CONSOLE_METHOD = {
2342
+ debug: "debug",
2343
+ info: "info",
2344
+ warn: "warn",
2345
+ error: "error"
2346
+ };
2347
+ var consoleSink = (entry) => {
2348
+ console[CONSOLE_METHOD[entry.level]](formatEntry(entry));
2349
+ };
2350
+ function globalDispatch(entry) {
2351
+ if (globalState.disabled) return;
2352
+ (globalState.sink ?? consoleSink)(entry);
2353
+ }
2354
+ function writeLogEntry(entry) {
2355
+ if (LEVEL_RANK[entry.level] < LEVEL_RANK[globalThreshold()]) return;
2356
+ globalDispatch(entry);
2357
+ }
2358
+ function createLogger(scope, options) {
2359
+ const baseScope = scope;
2360
+ const baseFields = options?.fields;
2361
+ const write = (level, message, callFields, scopeSuffix) => {
2362
+ const threshold = options?.level ?? globalThreshold();
2363
+ if (LEVEL_RANK[level] < LEVEL_RANK[threshold]) return;
2364
+ const entry = {
2365
+ level,
2366
+ message,
2367
+ time: (/* @__PURE__ */ new Date()).toISOString()
2368
+ };
2369
+ const fullScope = scopeSuffix === void 0 ? baseScope : baseScope === void 0 ? scopeSuffix : `${baseScope}:${scopeSuffix}`;
2370
+ if (fullScope !== void 0) entry.scope = fullScope;
2371
+ if (baseFields !== void 0 || callFields !== void 0) {
2372
+ entry.fields = { ...baseFields, ...callFields };
2373
+ }
2374
+ if (options?.sink) {
2375
+ options.sink(entry);
2376
+ } else {
2377
+ globalDispatch(entry);
2378
+ }
2379
+ };
2380
+ const makeLogger = (scopeSuffix) => ({
2381
+ debug: (message, fields) => write("debug", message, fields, scopeSuffix),
2382
+ info: (message, fields) => write("info", message, fields, scopeSuffix),
2383
+ warn: (message, fields) => write("warn", message, fields, scopeSuffix),
2384
+ error: (message, fields) => write("error", message, fields, scopeSuffix),
2385
+ child: (childScope) => makeLogger(scopeSuffix === void 0 ? childScope : `${scopeSuffix}:${childScope}`)
2386
+ });
2387
+ return makeLogger(void 0);
2388
+ }
2389
+
2240
2390
  // src/task/taskQueue.ts
2241
2391
  var TERMINAL_STATUSES = /* @__PURE__ */ new Set(["done", "failed", "cancelled"]);
2242
2392
  var MAX_FINISHED_RECORDS = 1e3;
@@ -2340,12 +2490,20 @@ function createTaskQueue(deps) {
2340
2490
  job: { id: job.id, name: job.name, attempt: job.attempt }
2341
2491
  },
2342
2492
  registries: snapshotRegistries(),
2493
+ // 日志配置随 workerData 下发(纯数据),worker 内联重建 taskCtx.log,
2494
+ // 条目回传宿主走统一管道(scope/fields 与进程内路径一致)
2495
+ log: {
2496
+ level: getEffectiveLogLevel(),
2497
+ scope: `task:${job.name}`,
2498
+ fields: { jobId: job.id, task: job.name, attempt: job.attempt }
2499
+ },
2343
2500
  timeoutMs: meta.timeoutMs,
2344
2501
  graceMs: meta.graceMs,
2345
2502
  externalSignal: job.signal,
2346
2503
  onProgress: (value) => {
2347
2504
  if (record.status === "running") record.progress = value;
2348
- }
2505
+ },
2506
+ onLog: writeLogEntry
2349
2507
  });
2350
2508
  } else {
2351
2509
  let mod = moduleCache2.get(job.name);
@@ -2361,6 +2519,9 @@ function createTaskQueue(deps) {
2361
2519
  config: deps.config,
2362
2520
  job: { id: job.id, name: job.name, attempt: job.attempt },
2363
2521
  registries: registriesView,
2522
+ log: createLogger(`task:${job.name}`, {
2523
+ fields: { jobId: job.id, task: job.name, attempt: job.attempt }
2524
+ }),
2364
2525
  progress: (value) => {
2365
2526
  if (record.status === "running") record.progress = value;
2366
2527
  }
@@ -3772,6 +3933,7 @@ function logger(options = {}) {
3772
3933
  const response = await next();
3773
3934
  const duration = Date.now() - start;
3774
3935
  const entry = {
3936
+ requestId: ctx.requestId,
3775
3937
  method: ctx.method,
3776
3938
  path: ctx.path,
3777
3939
  status: response.status,
@@ -3784,6 +3946,7 @@ function logger(options = {}) {
3784
3946
  const message = err instanceof Error ? err.message : String(err);
3785
3947
  const status = err?.statusCode ?? 500;
3786
3948
  const entry = {
3949
+ requestId: ctx.requestId,
3787
3950
  method: ctx.method,
3788
3951
  path: ctx.path,
3789
3952
  status,
@@ -4406,6 +4569,12 @@ function formatErrorResponse(error, config) {
4406
4569
  }
4407
4570
 
4408
4571
  // src/runtime/createContext.ts
4572
+ function resolveRequestId(request) {
4573
+ const header = request.headers.get("x-request-id");
4574
+ const first = header?.split(",")[0]?.trim();
4575
+ if (first) return first;
4576
+ return crypto.randomUUID();
4577
+ }
4409
4578
  function parseCookies(cookieHeader) {
4410
4579
  const cookies = /* @__PURE__ */ new Map();
4411
4580
  if (!cookieHeader) return cookies;
@@ -4439,7 +4608,13 @@ function createContextFromUrl(request, url, params, config = {}, ip = "", regist
4439
4608
  for (const [key, val] of parsedCookies) {
4440
4609
  cookiesObj[key] = val;
4441
4610
  }
4611
+ const requestId = resolveRequestId(request);
4612
+ const log = createLogger("http", {
4613
+ fields: { requestId, method: request.method, path: url.pathname }
4614
+ });
4442
4615
  const ctx = {
4616
+ requestId,
4617
+ log,
4443
4618
  request,
4444
4619
  params,
4445
4620
  query: url.searchParams,
@@ -4824,6 +4999,9 @@ function getBuiltinInjectionValue(type, ctx, body) {
4824
4999
  // 任务子系统:注入 TaskClient(入队/查询);未注册工厂(无 app 编排)时 undefined
4825
5000
  case "tasks":
4826
5001
  return ctx.registries ? ctx.registries.taskHandle.get(ctx) : void 0;
5002
+ // 请求级日志器:与 ctx.log 同一实例(scope http,自动带 requestId/method/path 字段)
5003
+ case "log":
5004
+ return ctx.log;
4827
5005
  default:
4828
5006
  return void 0;
4829
5007
  }
@@ -6659,6 +6837,7 @@ var FAAPI_CONFIG_KEYS = /* @__PURE__ */ new Set([
6659
6837
  "etag",
6660
6838
  "bodyLimit",
6661
6839
  "logger",
6840
+ "log",
6662
6841
  "http2",
6663
6842
  "trustedProxy",
6664
6843
  "response",
@@ -6677,6 +6856,7 @@ async function createAppBase(options) {
6677
6856
  );
6678
6857
  }
6679
6858
  const config = await loadConfig(rootDir, dist);
6859
+ configureLogging(config?.log);
6680
6860
  const serialized = await importWithCacheBust(routesPath);
6681
6861
  const hydrated = await hydrateRoutes(serialized);
6682
6862
  let sorted = sortRoutes(hydrated.routes);
@@ -7375,11 +7555,13 @@ export {
7375
7555
  ValidationError,
7376
7556
  clearAgentHandleFactory,
7377
7557
  collectRouteSchemaSources,
7558
+ configureLogging,
7378
7559
  cors,
7379
7560
  createProdApp as createApp,
7380
7561
  createAppRegistries,
7381
7562
  createCronScheduler,
7382
7563
  createDevApp,
7564
+ createLogger,
7383
7565
  createProdApp,
7384
7566
  createProgram,
7385
7567
  createPrograms,