Yuki Takei 16 часов назад
Родитель
Сommit
ef61b9da48

+ 23 - 149
.kiro/specs/migrate-logger-to-pino/design.md → .kiro/specs/growi-logger/design.md

@@ -1,43 +1,43 @@
-# Design Document: migrate-logger-to-pino
+# Design Document: growi-logger
 
 ## Overview
 
-**Purpose**: This feature migrates GROWI's logging infrastructure from bunyan (with the custom `universal-bunyan` wrapper) to pino, delivering faster structured logging with a smaller dependency footprint.
+**Purpose**: `@growi/logger` is the shared logging infrastructure for the GROWI monorepo, providing namespace-based level control, platform detection (Node.js/browser), and Express HTTP middleware — built on pino.
 
-**Users**: All GROWI developers (logger consumers), operators (log level configuration), and the CI/CD pipeline (dependency management).
+**Users**: All GROWI developers (logger consumers), operators (log level configuration), and the CI/CD pipeline.
 
-**Impact**: Replaces 7 logging-related packages (`bunyan`, `universal-bunyan`, `bunyan-format`, `express-bunyan-logger`, `morgan`, `browser-bunyan`, `@browser-bunyan/console-formatted-stream`) with 3 (`pino`, `pino-pretty`, `pino-http`) plus a new shared package `@growi/logger`. Consumer applications import only `@growi/logger`; `pino-http` is encapsulated within the package.
+**Scope**: All GROWI applications (`apps/app`, `apps/slackbot-proxy`) and packages (`packages/slack`, `packages/remark-attachment-refs`, `packages/remark-lsx`) import from `@growi/logger` as the single logging entry point. Consumer applications do not import pino or pino-http directly.
 
 ### Goals
-- Replace bunyan with pino across all apps and packages without functional degradation
-- Preserve namespace-based log level control (config files + env var overrides)
-- Eliminate morgan by consolidating HTTP logging into pino-http
+- Provide namespace-based log level control via config objects and environment variable overrides
+- Consolidate HTTP request logging under `createHttpLoggerMiddleware()` (pino-http encapsulated)
 - Maintain OpenTelemetry diagnostic logger integration
-- Provide a shared `@growi/logger` package as the single logging entry point
+- Serve as the single `@growi/logger` entry point for all monorepo consumers
+- Preserve pino's worker-thread performance model (single Worker thread, child loggers)
 
 ### Non-Goals
-- Changing log output semantics (field names, message format) beyond what pino naturally produces
 - Adding new logging capabilities (structured context propagation, remote log shipping)
-- Migrating to pino v10 (deferred until OTel instrumentation supports it)
 - Changing the namespace naming convention (e.g., `growi:service:page`)
+- Publishing `@growi/logger` to npm (private package, monorepo-internal only)
+- Migrating to pino v10 (blocked on `@opentelemetry/instrumentation-pino` v10 support)
 
 ## Architecture
 
-### Existing Architecture Analysis
+### Architecture Overview
 
-The current logging stack has these layers:
+`@growi/logger` is organized into these layers:
 
-1. **universal-bunyan** — custom wrapper providing: namespace-based level control via config + env vars, platform detection (Node.js/browser), stream selection (bunyan-format for Node.js, ConsoleFormattedStream for browser), logger caching
-2. **Per-app loggerFactory** — thin wrapper that loads dev/prod config and delegates to universal-bunyan
-3. **bunyan / browser-bunyan** — underlying logger implementations
-4. **express-bunyan-logger / morgan** — HTTP request logging middleware
+1. **LoggerFactory** — creates and caches namespace-bound pino child loggers; `initializeLoggerFactory` spawns one Worker thread; `loggerFactory(name)` returns `rootLogger.child({ name })` with resolved level
+2. **LevelResolver + EnvVarParser** — resolve log level from config patterns and env var overrides via minimatch glob matching
+3. **TransportFactory** — produces pino transport config for Node.js (dev: bunyan-format, prod+FORMAT_NODE_LOG: pino-pretty singleLine, prod: raw JSON) and browser (console)
+4. **HttpLoggerFactory** — encapsulates pino-http as `createHttpLoggerMiddleware()`; dev-mode morgan-like formatting dynamically imported from `src/dev/`
 
-Key patterns to preserve:
-- `loggerFactory(name: string): Logger` as the sole logger creation API
+Key invariants:
+- `loggerFactory(name: string): Logger<string>` as the sole logger creation API
 - Hierarchical colon-delimited namespaces with glob pattern matching
-- Environment variables (`DEBUG`, `TRACE`, etc.) overriding config file levels
-- Dev: human-readable output; Prod: JSON output (toggleable via `FORMAT_NODE_LOG`)
-- Browser: console output with error-level default in production
+- `pino.transport()` called **once** in `initializeLoggerFactory`; all namespace loggers share the Worker thread
+- Dev-only modules (`src/dev/`) are never statically imported in production paths
+- Browser-unsafe modules (pino-http) are imported lazily inside function bodies
 
 ### Architecture Pattern & Boundary Map
 
@@ -88,7 +88,7 @@ graph TB
 ```
 
 **Architecture Integration**:
-- Selected pattern: Wrapper package (`@growi/logger`) encapsulating pino configuration — mirrors universal-bunyan's role
+- `@growi/logger` wraps pino with namespace-level control, transport setup, and HTTP middleware — the single logging entry point for all monorepo consumers
 - Domain boundary: `@growi/logger` owns all logger creation, level resolution, and transport setup; consumer apps only call `loggerFactory(name)`
 - Existing patterns preserved: factory function signature, namespace conventions, config file structure
 - New components: `LevelResolver` (namespace-to-level matching), `TransportFactory` (dev/prod stream setup), `EnvVarParser` (env variable parsing)
@@ -173,8 +173,7 @@ flowchart TD
 | 6.1–6.4 | HTTP request logging | HttpLoggerMiddleware | `createHttpLogger()` | — |
 | 7.1–7.3 | OpenTelemetry integration | DiagLoggerPinoAdapter | `DiagLogger` interface | — |
 | 8.1–8.5 | Multi-app consistency | @growi/logger package | Package exports | — |
-| 9.1–9.3 | Dependency cleanup | — (removal task) | — | — |
-| 10.1–10.3 | Backward-compatible API | LoggerFactory | `Logger` type export | — |
+| 10.1–10.3 | Pino logger type export | LoggerFactory | `Logger<string>` export | — |
 | 11.1–11.4 | Pino performance preservation | LoggerFactory | `initializeLoggerFactory`, shared root logger | Logger Creation |
 | 12.1–12.6 | Bunyan-like output format | BunyanFormatTransport, TransportFactory | Custom transport target | Logger Creation |
 | 13.1–13.5 | HTTP logger encapsulation | HttpLoggerFactory | `createHttpLoggerMiddleware()` | — |
@@ -543,128 +542,3 @@ Logging infrastructure must be resilient — a logger failure must never crash t
 - Logger initialization errors are written to `process.stderr` directly (cannot use the logger itself)
 - No additional monitoring infrastructure required — this is the monitoring infrastructure
 
-## Addendum: Formatting Improvements (Post-Migration)
-
-> Added 2026-03-30. The core migration is complete. This section covers log output readability improvements based on operator feedback.
-
-### Background
-
-- Morgan was used in dev because bunyan's express logging was too verbose
-- Morgan's one-liner format (`GET /path 200 12ms`) was valued for readability
-- `FORMAT_NODE_LOG=true` should produce concise one-liner logs suitable for quick-glance monitoring
-- Production default should remain structured JSON (already working via `.env.production`)
-
-### Gap Summary
-
-| Gap | Issue | Resolution |
-|-----|-------|------------|
-| A | `singleLine: false` in prod FORMAT_NODE_LOG path | Change to `singleLine: true` |
-| B | `FORMAT_NODE_LOG` defaults to formatted when unset | Defer to separate PR (`.env.production` handles this) |
-| C | pino-http uses default verbose messages | Add `customSuccessMessage` / `customErrorMessage` / `customLogLevel` |
-| D | Dev and prod pino-pretty configs identical | Differentiate via `singleLine` |
-
-### Change 1: TransportFactory — Differentiated `singleLine`
-
-**File**: `packages/logger/src/transport-factory.ts`
-
-Current production + FORMAT_NODE_LOG branch uses `singleLine: false`. Change to `singleLine: true`:
-
-```
-Dev:                    singleLine: false  (unchanged — full context)
-Prod + FORMAT_NODE_LOG: singleLine: true   (concise one-liners)
-Prod default:           raw JSON           (unchanged)
-```
-
-The dev branch remains multi-line so developers see full object context. The production formatted path becomes single-line for operator readability.
-
-### Change 2: HttpLoggerMiddleware — Custom Message Format
-
-**Files**: `apps/app/src/server/crowi/index.ts`, `apps/slackbot-proxy/src/Server.ts`
-
-Add pino-http message customization to produce morgan-like output:
-
-```typescript
-const customSuccessMessage: PinoHttpOptions['customSuccessMessage'] = (req, res, responseTime) => {
-  return `${req.method} ${req.url} ${res.statusCode} - ${Math.round(responseTime)}ms`;
-};
-
-const customErrorMessage: PinoHttpOptions['customErrorMessage'] = (req, res, error) => {
-  return `${req.method} ${req.url} ${res.statusCode} - ${error.message}`;
-};
-
-const customLogLevel: PinoHttpOptions['customLogLevel'] = (_req, res, error) => {
-  if (error != null || res.statusCode >= 500) return 'error';
-  if (res.statusCode >= 400) return 'warn';
-  return 'info';
-};
-```
-
-### Output Examples (Updated with dev-only bunyan-like format)
-
-**Dev** (bunyan-format transport + morgan-like HTTP messages):
-```
-10:06:30.419Z  INFO express: GET /page/path 200 - 12ms
-    req: {"method":"GET","url":"/page/path"}
-    res: {"statusCode":200}
-```
-
-**Prod + FORMAT_NODE_LOG=true** (standard pino-pretty, default pino-http messages):
-```
-[2026-03-30 12:00:00.000] INFO (express): request completed
-```
-
-**Prod default** (JSON, default pino-http messages):
-```json
-{"level":30,"time":1711792800000,"name":"express","msg":"request completed","req":{"method":"GET","url":"/page/path"},"res":{"statusCode":200},"responseTime":12}
-```
-
-### Testing
-
-- `transport-factory.spec.ts`: Verify transport target contains `bunyan-format` (not pino-pretty directly); dev transport passes no options (singleLine handled inside bunyan-format); prod + FORMAT_NODE_LOG returns pino-pretty with `singleLine: true`
-- `bunyan-format.spec.ts`: Verify transport module produces `HH:mm:ss.SSSZ LEVEL name: message` format; verify req/res are excluded from output
-- `http-logger.spec.ts`: Verify `createHttpLoggerMiddleware` returns middleware, applies morganLikeFormatOptions in dev, passes autoLogging options
-- `morgan-like-format-options.spec.ts`: Verify message formats using `strip()` to remove ANSI codes before assertion; verify customLogLevel returns correct levels for 2xx/4xx/5xx
-
----
-
-## Addendum: HTTP Logger Encapsulation (Post-Migration)
-
-> Added 2026-04-02. Moves pino-http usage from consumer apps into @growi/logger.
-
-### Background
-
-- Consumer apps (`apps/app`, `apps/slackbot-proxy`) currently import `pino-http` directly
-- This leaks implementation details and requires each app to configure morgan-like format options
-- Encapsulating in `@growi/logger` provides a single configuration point and cleaner dependency graph
-
-### Changes
-
-1. **New file**: `packages/logger/src/http-logger.ts` — exports `createHttpLoggerMiddleware(options)`
-2. **Package.json**: Add `pino-http` to `@growi/logger` dependencies
-3. **apps/app**: Replace direct `pino-http` import with `createHttpLoggerMiddleware` from `@growi/logger`
-4. **apps/slackbot-proxy**: Same as apps/app
-5. **Cleanup**: Remove `pino-http` from apps' direct dependencies (keep in @growi/logger)
-
----
-
-## Addendum: Dev-Only Module Isolation and Browser Compatibility (Post-Migration)
-
-> Added 2026-04-06. Restructures dev-only modules and fixes browser bundle compatibility.
-
-### Background
-
-- `bunyan-format` and `morgan-like-format-options` were mixed with production modules at the `src/` root level
-- `pino-http` imported at the module top-level caused browser bundle errors (Turbopack: `TypeError: __turbopack_context__.r(...).symbols is undefined`) when `@growi/logger` was imported by shared page code
-- HTTP request logs in dev were verbose (multi-line `req`/`res` JSON objects)
-- HTTP status codes in dev lacked visual differentiation
-
-### Changes
-
-1. **`src/dev/` directory**: All dev-only modules moved under `src/dev/`
-   - `src/transports/bunyan-format.ts` → `src/dev/bunyan-format.ts`
-   - `src/morgan-like-format-options.ts` → `src/dev/morgan-like-format-options.ts`
-   - `src/transports/` directory removed
-2. **`index.ts`**: Removed static `export { morganLikeFormatOptions }` — dev-only module must not appear in production-facing package exports
-3. **`http-logger.ts`**: `pino-http` import moved from module top-level into the async function body (`const { default: pinoHttp } = await import('pino-http')`) — prevents browser bundlers from including the Node.js-only package
-4. **`bunyan-format.ts`**: `ignore` extended to `'pid,hostname,name,req,res,responseTime'` — suppresses verbose pino-http req/res objects; morgan-like `customSuccessMessage` already provides all relevant HTTP metadata on one line
-5. **`morgan-like-format-options.ts`**: ANSI color codes added for status code (2xx=green, 3xx=cyan, 4xx=yellow, 5xx=red) and dim response time; `NO_COLOR` env var respected

+ 79 - 0
.kiro/specs/growi-logger/requirements.md

@@ -0,0 +1,79 @@
+# Requirements Document
+
+## Introduction
+
+`@growi/logger` is the shared logging package for the GROWI monorepo, wrapping pino with namespace-based level control, platform detection (Node.js/browser), and Express HTTP middleware. All GROWI applications and packages import from `@growi/logger` as the single logging entry point.
+
+## Requirements
+
+### Requirement 1: Logger Factory with Namespace Support
+
+**Objective:** Provide `loggerFactory(name: string)` returning a pino logger bound to the given namespace, so developers can identify the source of log messages and control granularity per module.
+
+**Summary**: `loggerFactory(name)` returns a cached pino child logger for the namespace — same namespace always returns the same instance. Namespaces follow colon-delimited hierarchical convention (e.g., `growi:service:page`). The logger exposes `.info()`, `.debug()`, `.warn()`, `.error()`, `.trace()`, and `.fatal()` methods compatible with all existing call sites.
+
+### Requirement 2: Namespace-Based Log Level Configuration via Config Files
+
+**Objective:** Load per-namespace log levels from configuration objects (separate for dev and prod), allowing fine-tuned verbosity per module without restart.
+
+**Summary**: Accepts a `LoggerConfig` object mapping namespace patterns to log levels (e.g., `{ 'growi:service:*': 'debug', 'default': 'info' }`). Uses minimatch-compatible glob patterns. When no pattern matches, falls back to the `default` level. Per-app loggerFactory wrappers load dev/prod config files and pass the result to `initializeLoggerFactory`.
+
+### Requirement 3: Environment Variable-Based Log Level Override
+
+**Objective:** Override log levels at runtime via environment variables, enabling debug/trace logging for specific namespaces without modifying config files.
+
+**Summary**: Reads `DEBUG`, `TRACE`, `INFO`, `WARN`, `ERROR`, and `FATAL` environment variables. Each supports comma-separated namespace patterns with glob wildcards (e.g., `DEBUG=growi:routes:*,growi:service:page`). Environment variable matches take precedence over config file entries.
+
+### Requirement 4: Platform-Aware Logger (Node.js and Browser)
+
+**Objective:** Work seamlessly in both Node.js and browser environments using the same `loggerFactory` import.
+
+**Summary**: Detects runtime environment via `typeof window` check and applies appropriate transport. In browsers, outputs to the developer console; defaults to `error` level in production to minimize console noise. In Node.js, uses transport-based formatting as defined in Requirement 5.
+
+### Requirement 5: Output Formatting (Development vs Production)
+
+**Objective:** Provide distinct log output formats for development (human-readable) and production (structured JSON).
+
+**Summary**: Development uses the bunyan-format custom transport (`HH:mm:ss.SSSZ LEVEL name: message` format, colorized). Production defaults to raw JSON. When `FORMAT_NODE_LOG` is set, production uses standard pino-pretty with `singleLine: true`. The logger namespace is included in all output.
+
+### Requirement 6: HTTP Request Logging
+
+**Objective:** Provide Express HTTP request logging via `createHttpLoggerMiddleware()`, encapsulating pino-http so consumer apps do not depend on it directly.
+
+**Summary**: `createHttpLoggerMiddleware(options?)` returns Express-compatible middleware. In development, applies morgan-like message formatting (method, URL, status, response time) via dynamic import of `src/dev/morgan-like-format-options.ts`. In production, uses pino-http's default format. Static file paths can be excluded via `autoLogging.ignore`.
+
+### Requirement 7: OpenTelemetry Integration
+
+**Objective:** Integrate with OpenTelemetry diagnostics so observability tooling continues to function.
+
+**Summary**: `DiagLoggerPinoAdapter` in apps/app wraps pino as an OTel `DiagLogger`, mapping `verbose` to pino `trace`. The OTel SDK configuration disables `@opentelemetry/instrumentation-pino`.
+
+### Requirement 8: Multi-App Consistency
+
+**Objective:** All GROWI monorepo applications use the same pino-based logging solution from `@growi/logger`.
+
+**Summary**: `apps/app`, `apps/slackbot-proxy`, `packages/slack`, `packages/remark-attachment-refs`, and `packages/remark-lsx` all import from `@growi/logger` via `workspace:*`. The package is `"private": true` — monorepo-internal only, not published to npm.
+
+### Requirement 10: Pino Logger Type Export
+
+**Objective:** Export a TypeScript type for logger instances compatible with pino-http and other pino-ecosystem packages.
+
+**Summary**: `@growi/logger` exports `Logger<string>` (not the default `Logger<never>`) so the type is assignable to pino-http's `logger` option and other external APIs. Consumers type-annotate logger variables using this export without importing pino directly.
+
+### Requirement 11: Single Worker Thread Performance Model
+
+**Objective:** Honor pino's design philosophy of minimal main-thread overhead.
+
+**Summary**: `pino.transport()` is called exactly once in `initializeLoggerFactory()`. All namespace loggers are created via `rootLogger.child({ name })`, sharing the single Worker thread. The root logger level is `'trace'` so children can independently apply their resolved level. The Worker thread count never exceeds 1, regardless of namespace count.
+
+### Requirement 12: Bunyan-Like Output Format (Development Only)
+
+**Objective:** Provide human-readable log output in development mode matching the legacy bunyan-format "short" style.
+
+**Summary**: In development, each log line uses `HH:mm:ss.SSSZ LEVEL name: message` with 5-char right-aligned level labels and level-based colorization (cyan/green/yellow/red). Implemented as a custom pino transport at `src/dev/bunyan-format.ts` — only loaded in development. Standard pino-pretty is used for `FORMAT_NODE_LOG` in production. The `NO_COLOR` environment variable is respected.
+
+### Requirement 13: HTTP Logger Middleware Encapsulation
+
+**Objective:** Encapsulate pino-http within `@growi/logger` so consumer apps do not import pino-http directly.
+
+**Summary**: `createHttpLoggerMiddleware(options?)` is the sole HTTP logging API. `pino-http` is a dependency of `@growi/logger`, imported lazily inside the async function body (preventing browser bundle inclusion via Turbopack/webpack). Morgan-like formatting (`src/dev/morgan-like-format-options.ts`) is dynamically imported only in development. Status codes are colorized (2xx=green, 3xx=cyan, 4xx=yellow, 5xx=red) with `NO_COLOR` env var support.

+ 1 - 1
.kiro/specs/migrate-logger-to-pino/research.md → .kiro/specs/growi-logger/research.md

@@ -5,7 +5,7 @@
 ---
 
 ## Summary
-- **Feature**: `migrate-logger-to-pino`
+- **Feature**: `growi-logger`
 - **Discovery Scope**: Complex Integration
 - **Key Findings**:
   - Pino and bunyan share identical argument patterns (`logger.info(obj, msg)`) — no call-site changes needed

+ 2 - 2
.kiro/specs/migrate-logger-to-pino/spec.json → .kiro/specs/growi-logger/spec.json

@@ -1,7 +1,7 @@
 {
-  "feature_name": "migrate-logger-to-pino",
+  "feature_name": "growi-logger",
   "created_at": "2026-03-23T00:00:00.000Z",
-  "updated_at": "2026-04-06T00:00:00.000Z",
+  "updated_at": "2026-04-10T00:00:00.000Z",
   "language": "en",
   "phase": "implementation-complete",
   "cleanup_completed": true,

+ 18 - 0
.kiro/specs/growi-logger/tasks.md

@@ -0,0 +1,18 @@
+# Implementation History
+
+All tasks completed (2026-03-23 → 2026-04-06). This section records the implementation scope for future reference.
+
+- [x] 1. Scaffold `@growi/logger` shared package — package.json (pino v9.x, minimatch, pino-pretty peer), TypeScript ESM config, vitest setup, package entry points (main/types/browser)
+- [x] 2. Environment variable parsing and level resolution — `EnvVarParser` (reads DEBUG/TRACE/INFO/WARN/ERROR/FATAL), `LevelResolver` (minimatch glob matching, env-override precedence)
+- [x] 3. Transport factory — `TransportFactory` for Node.js dev (bunyan-format), prod+FORMAT_NODE_LOG (pino-pretty singleLine), prod default (raw JSON), and browser (console)
+- [x] 4. Logger factory — `initializeLoggerFactory` (spawns one Worker thread), `loggerFactory(name)` (child logger cache, level resolution)
+- [x] 5. Migrate shared packages — packages/slack, packages/remark-attachment-refs, packages/remark-lsx; fix pino-style call sites (object-first argument order)
+- [x] 6. Migrate apps/slackbot-proxy — logger factory, pino-http HTTP middleware, type imports, pino-style call sites
+- [x] 7. Migrate apps/app — logger factory, pino-http HTTP middleware, DiagLoggerPinoAdapter (OTel), bunyan type references
+- [x] 8. Remove all bunyan/morgan dependencies; verify no residual imports across monorepo
+- [x] 9. Full monorepo validation — lint, type-check, test, build for @growi/app, @growi/slackbot-proxy, @growi/logger
+- [x] 10. Differentiate pino-pretty `singleLine`: dev=false (multi-line context), prod+FORMAT_NODE_LOG=true (concise one-liners)
+- [x] 11. Morgan-like HTTP formatting — `customSuccessMessage`, `customErrorMessage`, `customLogLevel` in pino-http config
+- [x] 12. Bunyan-format custom transport (`src/dev/bunyan-format.ts`) — `HH:mm:ss.SSSZ LEVEL name: message` format, colorization, NO_COLOR support, pino.transport() worker thread
+- [x] 13. `createHttpLoggerMiddleware` — encapsulate pino-http in `@growi/logger`; move morgan-like options inside; add to @growi/logger deps
+- [x] 14. Dev-only module isolation (`src/dev/`) and browser bundle fix — lazy pino-http import, extended `ignore` field in bunyan-format

+ 0 - 156
.kiro/specs/migrate-logger-to-pino/requirements.md

@@ -1,156 +0,0 @@
-# Requirements Document
-
-## Introduction
-
-GROWI currently uses bunyan as its logging library, wrapped by the custom `universal-bunyan` package (developed by WeSeek). The system provides namespace-based hierarchical logging with environment variable-driven log level control, platform detection (Node.js/Browser), and different output formatting for development and production environments. Morgan is used for HTTP request logging in development mode while `express-bunyan-logger` handles production HTTP logging.
-
-This specification covers the complete migration from bunyan to pino, replacing `universal-bunyan` with an equivalent pino-based solution, and eliminating morgan by consolidating HTTP request logging under pino. The migration must preserve all existing functionality without degradation.
-
-### Current Components to Replace
-- `bunyan` → `pino`
-- `universal-bunyan` (custom) → pino-based equivalent (official packages preferred, custom wrapper where needed)
-- `bunyan-format` → pino transport equivalent (e.g., `pino-pretty`)
-- `express-bunyan-logger` → `pino-http` or equivalent
-- `morgan` (dev only) → consolidated into pino-http
-- `browser-bunyan` / `@browser-bunyan/console-formatted-stream` → pino browser mode or equivalent
-- `@types/bunyan` → pino's built-in types
-
-## Requirements
-
-### Requirement 1: Logger Factory with Namespace Support
-
-**Objective:** As a developer, I want to create loggers with hierarchical namespace identifiers (e.g., `growi:service:page`), so that I can identify the source of log messages and control granularity per module.
-
-#### Acceptance Criteria
-1. The Logger Factory shall provide a `loggerFactory(name: string)` function that returns a logger instance bound to the given namespace.
-2. When `loggerFactory` is called multiple times with the same namespace, the Logger Factory shall return the same cached logger instance.
-3. The Logger Factory shall support colon-delimited hierarchical namespaces (e.g., `growi:crowi`, `growi:routes:login`).
-4. The Logger Factory shall maintain API compatibility so that callers use `logger.info()`, `logger.debug()`, `logger.warn()`, `logger.error()`, `logger.trace()`, and `logger.fatal()` without changes to call sites.
-
-### Requirement 2: Namespace-Based Log Level Configuration via Config Files
-
-**Objective:** As a developer, I want to define per-namespace log levels in configuration files (separate for dev and prod), so that I can fine-tune verbosity for specific modules without restarting with different env vars.
-
-#### Acceptance Criteria
-1. The Logger Factory shall load a configuration object mapping namespace patterns to log levels (e.g., `{ 'growi:service:*': 'debug', 'default': 'info' }`).
-2. The Logger Factory shall select the dev or prod configuration based on the `NODE_ENV` environment variable.
-3. The Logger Factory shall support glob pattern matching (e.g., `growi:service:*`) for namespace-to-level mapping using minimatch-compatible syntax.
-4. When no specific namespace match exists, the Logger Factory shall fall back to the `default` level defined in the configuration.
-
-### Requirement 3: Environment Variable-Based Log Level Override
-
-**Objective:** As an operator, I want to override log levels at runtime via environment variables, so that I can enable debug/trace logging for specific namespaces without modifying code or config files.
-
-#### Acceptance Criteria
-1. The Logger Factory shall read the environment variables `DEBUG`, `TRACE`, `INFO`, `WARN`, `ERROR`, and `FATAL` to parse namespace patterns.
-2. When an environment variable (e.g., `DEBUG=growi:routes:*,growi:service:page`) is set, the Logger Factory shall apply the corresponding log level to all matching namespaces.
-3. When both a config file entry and an environment variable match the same namespace, the environment variable shall take precedence.
-4. The Logger Factory shall support comma-separated namespace patterns within a single environment variable value.
-5. The Logger Factory shall support glob wildcard patterns (e.g., `growi:*`) in environment variable values.
-
-### Requirement 4: Platform-Aware Logger (Node.js and Browser)
-
-**Objective:** As a developer, I want the logger to work seamlessly in both Node.js (server) and browser (client) environments, so that I can use the same `loggerFactory` import in universal/shared code.
-
-#### Acceptance Criteria
-1. The Logger Factory shall detect the runtime environment (Node.js vs browser) and instantiate the appropriate logger implementation.
-2. While running in a browser environment, the Logger Factory shall output logs to the browser's developer console with readable formatting.
-3. While running in a browser production environment, the Logger Factory shall default to `error` level to minimize console noise.
-4. While running in a Node.js environment, the Logger Factory shall output structured logs suitable for machine parsing or human-readable formatting depending on configuration.
-
-### Requirement 5: Output Formatting (Development vs Production)
-
-**Objective:** As a developer/operator, I want distinct log output formats for development and production, so that dev logs are human-readable while production logs are structured and parseable.
-
-#### Acceptance Criteria
-1. While `NODE_ENV` is not `production`, the Logger Factory shall output human-readable formatted logs (equivalent to bunyan-format `short` mode) using pino-pretty or an equivalent transport.
-2. While `NODE_ENV` is `production`, the Logger Factory shall output structured JSON logs by default.
-3. Where the `FORMAT_NODE_LOG` environment variable is set, the Logger Factory shall respect it to toggle between formatted and raw JSON output in production (formatted by default when `FORMAT_NODE_LOG` is unset or truthy).
-4. The Logger Factory shall include the logger namespace in all log output so that the source module is identifiable.
-
-### Requirement 6: HTTP Request Logging
-
-**Objective:** As a developer/operator, I want HTTP request logging integrated with pino, so that request/response metadata is captured in a consistent format alongside application logs, eliminating the need for morgan.
-
-#### Acceptance Criteria
-1. The GROWI Server shall log HTTP requests using `pino-http` or an equivalent pino-based middleware, replacing both `morgan` (dev) and `express-bunyan-logger` (prod).
-2. While in development mode, the HTTP Logger shall skip logging for Next.js static file requests (paths starting with `/_next/static/`).
-3. The HTTP Logger shall use a logger instance obtained from the Logger Factory with the namespace `express` (or equivalent) for consistency with existing log namespaces.
-4. The HTTP Logger shall include standard HTTP metadata (method, URL, status code, response time) in log entries.
-
-### Requirement 7: OpenTelemetry Integration
-
-**Objective:** As a developer, I want the pino-based logger to integrate with OpenTelemetry diagnostics, so that observability tooling continues to function after migration.
-
-#### Acceptance Criteria
-1. The OpenTelemetry DiagLogger adapter shall be updated to wrap pino instead of bunyan.
-2. The OpenTelemetry DiagLogger adapter shall map OpenTelemetry verbose level to pino trace level.
-3. The OpenTelemetry SDK configuration shall disable pino instrumentation if an equivalent auto-instrumentation exists (analogous to the current bunyan instrumentation disable).
-
-### Requirement 8: Multi-App Consistency
-
-**Objective:** As a developer, I want all GROWI monorepo applications to use the same pino-based logging solution, so that logging behavior and configuration are consistent across the platform.
-
-#### Acceptance Criteria
-1. The `apps/app` application shall use the pino-based Logger Factory.
-2. The `apps/slackbot-proxy` application shall use the pino-based Logger Factory.
-3. The `packages/slack` package shall use the pino-based Logger Factory.
-4. The `packages/remark-attachment-refs` package shall use the pino-based Logger Factory.
-5. The Logger Factory shall be published as a shared package within the monorepo so that all consumers import from a single source.
-
-### Requirement 9: Dependency Cleanup
-
-**Objective:** As a maintainer, I want all bunyan-related and morgan dependencies removed after migration, so that the dependency tree is clean and there is no dead code.
-
-#### Acceptance Criteria
-1. When migration is complete, the monorepo shall have no references to `bunyan`, `universal-bunyan`, `bunyan-format`, `express-bunyan-logger`, `browser-bunyan`, `@browser-bunyan/console-formatted-stream`, or `@types/bunyan` in any `package.json`.
-2. When migration is complete, the monorepo shall have no references to `morgan` or `@types/morgan` in any `package.json`.
-3. When migration is complete, no source file shall contain imports or requires of the removed packages.
-
-### Requirement 11: Preserve Pino's Performance Characteristics
-
-**Objective:** As a developer, I want the logger implementation to honour pino's design philosophy of minimal overhead in the main thread, so that migrating from bunyan does not introduce performance regressions.
-
-#### Acceptance Criteria
-1. The Logger Factory shall create pino's worker-thread transport (`pino.transport()`) **at most once** per application lifetime (i.e., during `initializeLoggerFactory`), regardless of the number of unique namespaces.
-2. The Logger Factory shall create per-namespace loggers by calling `.child()` on a shared root pino instance, not by calling `pino()` and `pino.transport()` independently for each namespace.
-3. The Logger Factory shall not perform any blocking I/O or expensive computation on the hot path of each log method call (level-checking is performed by pino's internal mechanism and is acceptable).
-4. The number of active Worker threads used by the logger subsystem shall remain constant after the first call to `loggerFactory()`, regardless of how many distinct namespaces are subsequently requested.
-
-### Requirement 10: Backward-Compatible Log API
-
-**Objective:** As a developer, I want the new logger to expose the same method signatures as the current bunyan logger, so that existing log call sites require minimal or no changes.
-
-#### Acceptance Criteria
-1. The pino logger shall support `.info()`, `.debug()`, `.warn()`, `.error()`, `.trace()`, and `.fatal()` methods with the same argument patterns as bunyan (message string, optional object, optional error).
-2. If bunyan-specific APIs (e.g., `logger.child()`, serializers) are used at any call sites, the pino equivalent shall be provided or the call site shall be adapted.
-3. The Logger Factory shall export a TypeScript type for the logger instance that is compatible with the pino Logger type.
-
-### Requirement 12: Bunyan-Like Output Format (Development Only)
-
-**Objective:** As a developer, I want the log output in development mode to resemble bunyan-format's "short" mode, so that the visual experience remains familiar after migration.
-
-#### Acceptance Criteria
-1. While in development mode (`NODE_ENV !== 'production'`), the Logger Factory shall output each log line in the format: `HH:mm:ss.SSSZ LEVEL name: message` (e.g., `10:06:30.419Z DEBUG growi:service:page: some message`).
-2. The level label shall be right-aligned to 5 characters (e.g., `DEBUG`, ` INFO`, ` WARN`).
-3. The timestamp shall be UTC time-only in ISO 8601 format (`HH:mm:ss.SSSZ`), without date or surrounding brackets.
-4. The logger namespace (`name` field) shall appear directly after the level label, followed by a colon and the message, without parentheses.
-5. Log lines shall be colorized by level (cyan for DEBUG, green for INFO, yellow for WARN, red for ERROR).
-6. The bunyan-like format shall be implemented as a custom pino transport module within `@growi/logger`, so that `pino.transport()` can load it in a worker thread without function serialization issues.
-7. The bunyan-format transport module shall only be imported in development mode. In production, the module shall not be imported or bundled.
-8. While in production mode with `FORMAT_NODE_LOG` enabled, the Logger Factory shall use standard pino-pretty (not the bunyan-format transport) for formatted output.
-
-### Requirement 13: HTTP Logger Middleware Encapsulation
-
-**Objective:** As a developer, I want the HTTP request logging middleware encapsulated within `@growi/logger`, so that consumer applications do not need to depend on or import `pino-http` directly.
-
-#### Acceptance Criteria
-1. The `@growi/logger` package shall export a `createHttpLoggerMiddleware(options)` function that returns Express-compatible middleware for HTTP request logging.
-2. The middleware factory shall accept options for the logger namespace (defaulting to `'express'`) and optional `autoLogging` configuration (e.g., route ignore patterns).
-3. While in development mode, the middleware shall apply morgan-like formatting (custom success/error messages, custom log levels) via dynamic import. In production mode, the morgan-like format module shall not be imported; pino-http's default message format shall be used.
-4. After the encapsulation, `apps/app` and `apps/slackbot-proxy` shall not import `pino-http` directly; all HTTP logging shall go through `@growi/logger`.
-5. The `pino-http` dependency shall move from consumer applications to `@growi/logger`'s `dependencies`.
-6. The `morganLikeFormatOptions` module shall only be imported in development mode (dynamic import). In production, the module shall not be imported or bundled.
-7. The `pino-http` module shall be imported lazily inside the `createHttpLoggerMiddleware` function body (not at module top-level), so that bundlers (e.g., Turbopack, webpack) do not include the Node.js-only `pino-http` in browser bundles when `@growi/logger` is imported by shared/universal code.
-8. While in development mode with morgan-like formatting enabled, the HTTP log output shall suppress the verbose `req` and `res` serialized objects; the `customSuccessMessage` output (method, URL, status code, response time) is sufficient for development readability.
-9. While in development mode, the morgan-like format shall colorize the HTTP status code by range (2xx=green, 3xx=cyan, 4xx=yellow, 5xx=red) and dim the response time, respecting the `NO_COLOR` environment variable.

+ 0 - 263
.kiro/specs/migrate-logger-to-pino/tasks.md

@@ -1,263 +0,0 @@
-# Implementation Plan
-
-- [x] 1. Scaffold the @growi/logger shared package
-- [x] 1.1 Initialize the package directory, package.json, and TypeScript configuration within the monorepo packages directory
-  - Create the workspace entry as `@growi/logger` with pino v9.x and minimatch as dependencies, pino-pretty as an optional peer dependency
-  - Configure TypeScript with strict mode, ESM output, and appropriate path aliases
-  - Set up the package entry points (main, types, browser) so that bundlers resolve the correct build for Node.js vs browser
-  - Add vitest configuration for unit testing within the package
-  - _Requirements: 8.5_
-
-- [x] 1.2 Define the shared type contracts and configuration interface
-  - Define the `LoggerConfig` type representing a namespace-pattern-to-level mapping (including a `default` key)
-  - Define the `LoggerFactoryOptions` type accepted by the initialization function
-  - Export the pino `Logger` type so consumers can type-annotate their logger variables without importing pino directly
-  - _Requirements: 10.3_
-
-- [x] 2. Implement environment variable parsing and level resolution
-- [x] 2.1 (P) Build the environment variable parser
-  - Read the six log-level environment variables (`DEBUG`, `TRACE`, `INFO`, `WARN`, `ERROR`, `FATAL`) from the process environment
-  - Split each variable's value by commas and trim whitespace to extract individual namespace patterns
-  - Return a flat config map where each namespace pattern maps to its corresponding level string
-  - Handle edge cases: empty values, missing variables, duplicate patterns (last wins)
-  - Write unit tests covering: single variable with multiple patterns, all six variables set, no variables set, whitespace handling
-  - _Requirements: 3.1, 3.4, 3.5_
-
-- [x] 2.2 (P) Build the level resolver with glob pattern matching
-  - Accept a namespace string, a config map, and an env-override map; return the resolved level
-  - Check env-override map first (using minimatch for glob matching), then config map, then fall back to the config `default` entry
-  - When multiple patterns match, prefer the most specific (longest non-wildcard prefix) match
-  - Write unit tests covering: exact match, glob wildcard match, env override precedence over config, fallback to default, no matching pattern
-  - _Requirements: 2.1, 2.3, 2.4, 3.2, 3.3_
-
-- [x] 3. Implement the transport factory for dev, prod, and browser environments
-- [x] 3.1 (P) Build the Node.js transport configuration
-  - In development mode, produce pino-pretty transport options with human-readable timestamps, hidden pid/hostname fields, and multi-line output
-  - In production mode, produce raw JSON output to stdout by default
-  - When the `FORMAT_NODE_LOG` environment variable is unset or truthy in production, produce pino-pretty transport options with long-format output instead of raw JSON
-  - Include the logger namespace (`name` field) in all output configurations
-  - Write unit tests verifying correct options for each combination of NODE_ENV and FORMAT_NODE_LOG
-  - _Requirements: 5.1, 5.2, 5.3, 5.4_
-
-- [x] 3.2 (P) Build the browser transport configuration
-  - Detect the browser environment using window/document checks
-  - In browser development mode, produce pino browser options that output to the developer console with the resolved namespace level
-  - In browser production mode, produce pino browser options that default to `error` level to suppress non-critical console output
-  - Write unit tests verifying browser options for dev and prod scenarios
-  - _Requirements: 4.1, 4.2, 4.3, 4.4_
-
-- [x] 4. Implement the logger factory with caching and platform detection
-- [x] 4.1 Build the initialization and factory functions
-  - Implement `initializeLoggerFactory(options)` that stores the merged configuration, pre-parses environment overrides, and prepares the transport config
-  - Implement `loggerFactory(name)` that checks the cache for an existing logger, resolves the level via the level resolver, creates a pino instance with appropriate transport options, caches it, and returns it
-  - Detect the runtime platform (Node.js vs browser) and apply the corresponding transport configuration from the transport factory
-  - Ensure the module exports `loggerFactory` as the default export and `initializeLoggerFactory` as a named export for backward compatibility with existing import patterns
-  - Write unit tests covering: cache hit returns same instance, different namespaces return different instances, initialization stores config correctly
-  - _Requirements: 1.1, 1.2, 1.3, 1.4, 4.1, 10.1_
-
-- [x] 5. Migrate shared packages to @growi/logger (small scope first)
-- [x] 5.1 (P) Update packages/slack logger to use @growi/logger
-  - Replace the logger factory implementation to import from `@growi/logger` instead of universal-bunyan
-  - Update the inline config (`{ default: 'info' }`) to use the @growi/logger initialization pattern
-  - Replace bunyan type imports with the @growi/logger Logger type
-  - Add `@growi/logger` to packages/slack dependencies
-  - Run TypeScript compilation to verify no type errors
-  - _Requirements: 8.3_
-
-- [x] 5.2 (P) Update packages/remark-attachment-refs logger to use @growi/logger
-  - Replace the logger factory implementation to import from `@growi/logger`
-  - Update configuration and type imports to match the new package
-  - Add `@growi/logger` to packages/remark-attachment-refs dependencies
-  - Run TypeScript compilation to verify no type errors
-  - _Requirements: 8.4_
-
-- [x] 5.3 Fix pino-style logger call sites in packages/slack
-  - In the following files, convert all `logger.method('message', obj)` calls to the pino-canonical form `logger.method({ obj }, 'message')` (object first, message second)
-  - `src/middlewares/verify-growi-to-slack-request.ts` (lines 25, 34)
-  - `src/middlewares/verify-slack-request.ts` (lines 25, 36, 45, 76)
-  - `src/utils/interaction-payload-accessor.ts` (line 104)
-  - Run `pnpm --filter @growi/slack lint:typecheck` and confirm zero TS2769 errors
-  - _Requirements: 10.1_
-
-- [x] 5.4 Fix pino-style logger call site in packages/remark-attachment-refs
-  - In `src/client/services/renderer/refs.ts` (line 107), convert `logger.debug('message', attributes)` to `logger.debug({ attributes }, 'message')`
-  - Run `pnpm --filter @growi/remark-attachment-refs lint:typecheck` and confirm the TS2769 error is gone
-  - _Requirements: 10.1_
-
-- [x] 5.5 Migrate packages/remark-lsx server routes to use @growi/logger
-  - Add `@growi/logger` to packages/remark-lsx dependencies
-  - Create `src/utils/logger/index.ts` following the same pattern as remark-attachment-refs (import from `@growi/logger`, call `initializeLoggerFactory`, re-export `loggerFactory`)
-  - Replace `console.error` calls in `src/server/routes/list-pages/index.ts` (lines 89, 145-148) with proper logger calls using `loggerFactory('growi:remark-lsx:routes:list-pages')`
-  - Remove the `biome-ignore lint/suspicious/noConsole` comments from the replaced call sites
-  - Run `pnpm --filter @growi/remark-lsx lint:typecheck` to confirm no type errors
-  - _Requirements: 8.5_
-
-- [x] 6. Migrate apps/slackbot-proxy to @growi/logger
-- [x] 6.1 Replace the logger factory and HTTP middleware in slackbot-proxy
-  - Update the slackbot-proxy logger utility to import from `@growi/logger` and call `initializeLoggerFactory` with its existing dev/prod config
-  - Replace express-bunyan-logger and morgan usage in the server setup with pino-http middleware
-  - Replace all `import type Logger from 'bunyan'` references with the @growi/logger Logger type
-  - Add `@growi/logger` and `pino-http` to slackbot-proxy dependencies
-  - Run TypeScript compilation to verify no type errors
-  - _Requirements: 8.2, 6.1_
-
-- [x] 6.6 Fix pino-style logger call sites in apps/slackbot-proxy
-  - In the following files, convert all `logger.method('message', obj)` calls to `logger.method({ obj }, 'message')`
-  - `src/controllers/growi-to-slack.ts` (lines 109, 179, 231, 243, 359)
-  - `src/controllers/slack.ts` (lines 388, 586)
-  - `src/services/RegisterService.ts` (line 165)
-  - Run `pnpm --filter @growi/slackbot-proxy lint:typecheck` and confirm zero TS2769 errors
-  - _Requirements: 10.1_
-
-- [x] 6.7 Fix @growi/logger Logger type export and remove `as any` cast in slackbot-proxy
-  - In `packages/logger`, update the `loggerFactory` return type so it is compatible with `pino-http`'s `logger` option (i.e., `pino.Logger` without `<never>` narrowing, or by exporting `Logger<string>`)
-  - After the type export is fixed, remove the `as any` cast from `apps/slackbot-proxy/src/Server.ts` (line 166) and the associated `biome-ignore` comment
-  - Run `pnpm --filter @growi/slackbot-proxy lint:typecheck` to confirm no residual type errors
-  - _Requirements: 10.3_
-
-- [x] 6.5 Fix logger factory to preserve pino's single-worker-thread performance model
-  - Refactor `initializeLoggerFactory` to create the pino transport (`pino.transport()`) and root pino logger **once**, storing them in module scope
-  - Set the root logger's level to `'trace'` so that individual child loggers can apply their own resolved level without being silenced by the root
-  - Refactor `loggerFactory(name)` to call `rootLogger.child({ name })` and then set `childLogger.level = resolvedLevel` instead of calling `pino()` + `pino.transport()` per namespace
-  - Handle browser mode separately: the root browser logger is created once in `initializeLoggerFactory`; `loggerFactory` still calls `.child({ name })` and applies the resolved level
-  - Update unit tests in `logger-factory.spec.ts` to verify that calling `loggerFactory` for N distinct namespaces does not create N independent pino instances (all children share the root transport)
-  - _Requirements: 11.1, 11.2, 11.3, 11.4_
-
-- [x] 7. Migrate apps/app to @growi/logger (largest scope)
-- [x] 7.1 Replace the logger factory module in apps/app
-  - Update the apps/app logger utility to import from `@growi/logger` instead of `universal-bunyan`
-  - Call `initializeLoggerFactory` at application startup with the existing dev/prod config files (preserve current config content)
-  - Re-export `loggerFactory` as the default export so all existing consumer imports continue to work unchanged
-  - Add `@growi/logger` to apps/app dependencies and ensure pino-pretty is available for development formatting
-  - _Requirements: 8.1, 2.2_
-
-- [x] 7.2 Replace HTTP request logging middleware in apps/app
-  - Remove the morgan middleware (development mode) and express-bunyan-logger middleware (production mode) from the Express initialization
-  - Add pino-http middleware configured with a logger from the factory using the `express` namespace
-  - Configure route skipping to exclude `/_next/static/` paths in non-production mode
-  - Verify the middleware produces log entries containing method, URL, status code, and response time
-  - _Requirements: 6.1, 6.2, 6.3, 6.4_
-
-- [x] 7.3 Update the OpenTelemetry diagnostic logger adapter
-  - Rename the adapter class from `DiagLoggerBunyanAdapter` to `DiagLoggerPinoAdapter` and update the import to use pino types
-  - Preserve the existing `parseMessage` helper logic that parses JSON strings and merges argument objects
-  - Confirm the verbose-to-trace level mapping continues to work with pino's trace level
-  - Update the OpenTelemetry SDK configuration to disable `@opentelemetry/instrumentation-pino` instead of `@opentelemetry/instrumentation-bunyan`
-  - _Requirements: 7.1, 7.2, 7.3_
-
-- [x] 7.4 Update all bunyan type references in apps/app source files
-  - Replace `import type Logger from 'bunyan'` with the Logger type exported from `@growi/logger` across all source files in apps/app
-  - Verify that pino's Logger type is compatible with all existing usage patterns (info, debug, warn, error, trace, fatal method calls)
-  - Run the TypeScript compiler to confirm no type errors
-  - _Requirements: 10.1, 10.2, 10.3_
-
-- [x] 8. Remove old logging dependencies and verify cleanup
-- [x] 8.1 Remove bunyan-related packages from all package.json files
-  - Remove `bunyan`, `universal-bunyan`, `bunyan-format`, `express-bunyan-logger`, `browser-bunyan`, `@browser-bunyan/console-formatted-stream`, `@types/bunyan` from every package.json in the monorepo
-  - Remove `morgan` and `@types/morgan` from every package.json in the monorepo
-  - Run `pnpm install` to update the lockfile and verify no broken peer dependency warnings
-  - _Requirements: 9.1, 9.2_
-
-- [x] 8.2 Verify no residual references to removed packages
-  - Search all source files for any remaining imports or requires of the removed packages (bunyan, universal-bunyan, browser-bunyan, express-bunyan-logger, morgan, bunyan-format)
-  - Search all configuration and type definition files for stale bunyan references
-  - Fix any remaining references found during the search
-  - _Requirements: 9.3_
-
-- [x] 9. Run full monorepo validation
-- [x] 9.1 Execute lint, type-check, test, and build across the monorepo
-  - Run `turbo run lint --filter @growi/app` and fix any lint errors related to the migration
-  - Run `turbo run test --filter @growi/app` and verify all existing tests pass
-  - Run `turbo run build --filter @growi/app` and confirm the production build succeeds
-  - Run the same checks for slackbot-proxy and any other affected packages
-  - Verify the @growi/logger package's own tests pass
-  - _Requirements: 1.4, 8.1, 8.2, 8.3, 8.4, 10.1, 10.2_
-
-- [x] 10. Improve log output formatting for readability
-- [x] 10.1 (P) Differentiate pino-pretty singleLine between dev and production FORMAT_NODE_LOG
-  - In the transport factory, change the production + FORMAT_NODE_LOG path to use `singleLine: true` for concise one-liner output
-  - Keep the development path at `singleLine: false` so developers see full multi-line context
-  - Update unit tests to verify: dev returns `singleLine: false`, production + FORMAT_NODE_LOG returns `singleLine: true`, production without FORMAT_NODE_LOG still returns no transport
-  - _Requirements: 5.1, 5.3_
-
-- [x] 10.2 (P) Add morgan-like HTTP request message formatting to pino-http in apps/app
-  - Configure `customSuccessMessage` to produce `METHOD /url STATUS - TIMEms` format (e.g., `GET /page/path 200 - 12ms`)
-  - Configure `customErrorMessage` to include the error message alongside method, URL, and status code
-  - Configure `customLogLevel` to return `warn` for 4xx responses and `error` for 5xx or error responses, keeping `info` for successful requests
-  - Verify that `/_next/static/` path skipping in dev mode still works after the changes
-  - _Requirements: 6.1, 6.4_
-
-- [x] 10.3 (P) Add morgan-like HTTP request message formatting to pino-http in apps/slackbot-proxy
-  - Apply the same `customSuccessMessage`, `customErrorMessage`, and `customLogLevel` configuration as apps/app
-  - _Requirements: 6.1, 6.4_
-
-- [x] 11. Validate formatting improvements
-- [x] 11.1 Run tests and build for affected packages
-  - Run the @growi/logger package tests to confirm transport factory changes pass
-  - Run lint and type-check for apps/app and apps/slackbot-proxy
-  - Verify the production build succeeds
-  - _Requirements: 5.1, 5.3, 6.1, 6.4_
-
-- [x] 12. Implement bunyan-like output format (development only)
-- [x] 12.1 Create the bunyan-format custom transport module
-  - Create `packages/logger/src/transports/bunyan-format.ts` that default-exports a function returning a pino-pretty stream
-  - Use `customPrettifiers.time` to format epoch as `HH:mm:ss.SSSZ` (UTC time-only, no brackets)
-  - Use `customPrettifiers.level` to return `${label.padStart(5)} ${log.name}` (right-aligned 5-char level + namespace)
-  - Set `ignore: 'pid,hostname,name'` so name appears via the level prettifier, not in pino-pretty's default parens
-  - Accept `singleLine` option to pass through to pino-pretty
-  - Verify the module is built to `dist/transports/bunyan-format.js` by vite's `preserveModules` config
-  - _Requirements: 12.1, 12.2, 12.3, 12.4, 12.5, 12.6_
-
-- [x] 12.2 Update TransportFactory to use bunyan-format transport in dev only
-  - In the **development** branch of `createNodeTransportOptions`, change the transport target from `'pino-pretty'` to the resolved path of `bunyan-format.js` (via `import.meta.url`)
-  - Remove `translateTime` and `ignore` options from the dev transport config (now handled inside the custom transport)
-  - Pass `singleLine: false` for dev
-  - In the **production + FORMAT_NODE_LOG** branch, keep `target: 'pino-pretty'` with standard options (`translateTime: 'SYS:standard'`, `ignore: 'pid,hostname'`, `singleLine: true`) — do NOT use bunyan-format
-  - The bunyan-format module path is only resolved in the dev code path, ensuring it is never imported in production
-  - Update unit tests in `transport-factory.spec.ts`: dev target contains `bunyan-format`; prod + FORMAT_NODE_LOG target is `'pino-pretty'`
-  - _Requirements: 12.1, 12.6, 12.7, 12.8_
-
-- [x] 12.3 Verify bunyan-format output
-  - Run the dev server and confirm log output matches the bunyan-format "short" style: `HH:mm:ss.SSSZ LEVEL name: message`
-  - Confirm colorization works (DEBUG=cyan, INFO=green, WARN=yellow, ERROR=red)
-  - Confirm multi-line output in dev (extra fields on subsequent lines)
-  - _Requirements: 12.1, 12.2, 12.3, 12.4, 12.5_
-
-- [x] 13. Encapsulate pino-http in @growi/logger
-- [x] 13.1 Create HTTP logger middleware factory in @growi/logger
-  - Create `packages/logger/src/http-logger.ts` exporting `async createHttpLoggerMiddleware(options?)`
-  - The function creates `pinoHttp` middleware internally with `loggerFactory(namespace)`
-  - In development mode (`NODE_ENV !== 'production'`): dynamically import `morganLikeFormatOptions` via `await import('./morgan-like-format-options')` and apply to pino-http options
-  - In production mode: use pino-http with default message formatting (no morgan-like module imported)
-  - Accept optional `namespace` (default: `'express'`) and `autoLogging` options
-  - Handle the `Logger<string>` → pino-http's expected Logger type assertion internally
-  - Add `pino-http` to `@growi/logger` package.json dependencies
-  - Export `createHttpLoggerMiddleware` from `packages/logger/src/index.ts`
-  - _Requirements: 13.1, 13.2, 13.3, 13.5, 13.6_
-
-- [x] 13.2 (P) Migrate apps/app to use createHttpLoggerMiddleware
-  - Replace the direct `pinoHttp` import and configuration in `apps/app/src/server/crowi/index.ts` with `await createHttpLoggerMiddleware(...)` from `@growi/logger`
-  - Pass the `/_next/static/` autoLogging ignore function via the options
-  - Remove `pino-http` and its type imports from the file
-  - Remove `morganLikeFormatOptions` import (now applied internally in dev only)
-  - Remove `pino-http` from `apps/app/package.json` if no longer directly used
-  - Run `pnpm --filter @growi/app lint:typecheck` to confirm no type errors
-  - _Requirements: 13.4_
-
-- [x] 13.3 (P) Migrate apps/slackbot-proxy to use createHttpLoggerMiddleware
-  - Replace the direct `pinoHttp` import and configuration in `apps/slackbot-proxy/src/Server.ts` with `await createHttpLoggerMiddleware(...)` from `@growi/logger`
-  - Remove `pino-http` and its type imports from the file
-  - Remove `morganLikeFormatOptions` import (now applied internally in dev only)
-  - Remove the `as unknown as` type assertion (now handled internally)
-  - Remove `pino-http` from `apps/slackbot-proxy/package.json` if no longer directly used
-  - Run `pnpm --filter @growi/slackbot-proxy lint:typecheck` to confirm no type errors
-  - _Requirements: 13.4_
-
-- [x] 14. Validate bunyan-format and HTTP encapsulation
-- [x] 14.1 Run full validation
-  - Run `@growi/logger` package tests
-  - Run lint and type-check for apps/app and apps/slackbot-proxy
-  - Run `turbo run build --filter @growi/app` to verify production build succeeds
-  - Verify no remaining direct `pino-http` imports in apps/app or apps/slackbot-proxy source files
-  - Verify that bunyan-format transport and morganLikeFormatOptions are NOT imported in production (grep for dynamic import pattern)
-  - _Requirements: 12.1, 12.6, 12.7, 13.4, 13.5, 13.6_