Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions backend/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,14 @@
import { serve } from '@hono/node-server';
import { Hono } from 'hono';
import { cors } from 'hono/cors';
import { logger as honoLogger } from 'hono/logger';
import { prettyJSON } from 'hono/pretty-json';
import { serveStatic } from '@hono/node-server/serve-static';
import 'dotenv/config';
import { ProxyAgent, setGlobalDispatcher } from 'undici';
import { createLogger, generateTraceId } from './lib/logger';
import { prisma } from './lib/prisma';
import { traceMiddleware } from './lib/trace-middleware';
import { httpLogger } from './lib/http-logger';

// Create a startup logger with shared traceId for all startup logs
const startupTraceId = generateTraceId();
Expand Down Expand Up @@ -235,7 +235,7 @@ const app = new Hono();
// Middleware
// Trace middleware must be first to capture traceId for all subsequent logging
app.use('*', traceMiddleware());
app.use('*', honoLogger());
app.use('*', httpLogger());
app.use('*', prettyJSON());

// CORS must be before all routes
Expand Down
63 changes: 63 additions & 0 deletions backend/src/lib/http-logger.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/**
* HTTP Request Logger Middleware
*
* Provides structured JSON logging for HTTP requests using Pino.
* Replaces Hono's default text logger for production compatibility.
*
* Output format:
* {"level":"info","time":1735368000,"service":"m3w-backend","region":"jp",
* "source":"http.request","col1":"http","col2":"GET","method":"GET",
* "path":"/api/health","status":200,"duration":12,"msg":"GET /api/health 200 12ms"}
*/

import type { Context, Next } from 'hono';
import { logger } from './logger';

/**
* HTTP logger middleware using Pino
*
* Logs incoming requests and outgoing responses with:
* - Method, path, status code
* - Response duration in milliseconds
* - Structured fields for Loki/Grafana filtering
*
* @returns Hono middleware function
*/
export function httpLogger() {
return async (c: Context, next: Next) => {
const start = Date.now();
const method = c.req.method;
const path = c.req.path;

// Get traceId from context (set by traceMiddleware)
const traceId = c.get('traceId') as string | undefined;

await next();

const duration = Date.now() - start;
const status = c.res.status;

// Build log entry with structured fields
const logEntry = {
source: 'http.request',
col1: 'http',
col2: method,
method,
path,
status,
duration,
...(traceId && { traceId }),
};

// Log at appropriate level based on status code
const message = `${method} ${path} ${status} ${duration}ms`;

if (status >= 500) {
logger.error(logEntry, message);
} else if (status >= 400) {
logger.warn(logEntry, message);
} else {
logger.info(logEntry, message);
}
};
}