I frequently encounter architectural decisions that seem counterintuitive from an operational perspective. Recently, while containerizing a Next.js application, I discovered one such puzzling design choice that required a creative engineering solution.
The Problem: Silent Production Builds
During the Docker image creation process for our Next.js application, I encountered an unexpected operational blind spot: Next.js production builds generate zero access logs. This absence of fundamental observability data immediately raised concerns about our ability to monitor application behavior in production environments.
The conventional wisdom suggests deploying an nginx reverse proxy to capture access logs. However, as an architect focused on operational efficiency, introducing an additional process layer solely for logging felt architecturally unsound, particularly within containerized environments where process minimalism is a core principle.
Exploring Conventional Solutions
My initial investigation led me to application-level logging libraries such as winston and pino. While these tools excel at application logging, they operate within the application boundary and don't provide the standardized access log format that operations teams expect from web applications.
Root Cause Analysis
After extensive research into similar reported issues, I discovered the underlying cause: Vercel has intentionally omitted access logging from Next.js production builds. This architectural decision, while perhaps suitable for Vercel's managed platform, creates operational challenges for self-hosted deployments.
Deep Dive
Taking a source-code-first approach, I downloaded the Next.js repository and traced the request handling flow to its core: the async function requestListener(req, res) function. By strategically placing console.log statements within the node_modules Next.js installation, I successfully exposed the access log data we needed.
However, this manual modification approach presented obvious maintainability challenges for automated deployment pipelines.
Production-Ready Implementation
While researching sustainable patching methodologies, I discovered an excellent resource by TomUps (https://www.tomups.com/posts/log-nextjs-request-response-as-json/) that introduced patch-package, a tool designed precisely for this type of systematic source modification.
Their approach provided the foundational technique, though it captured extensive request/response metadata including headers and body content. For our operational requirements, I needed a more focused solution that provided essential access log fields: timestamp, URL, and HTTP status code.
Architectural Solution
The final implementation leverages patch-package combined with pino-http-print to deliver clean, standardized access logs that integrate seamlessly with our existing observability stack. This approach:
- container efficiency by avoiding additional processes
- Provides operational visibility through standard access log formats
- Ensures deployment consistency via automated patching during image builds
- Preserves maintainability through version-controlled patch files
Key Takeaway
This experience reinforces a fundamental architectural principle: when platform decisions conflict with operational requirements, creative engineering solutions can bridge the gap while maintaining system integrity. The key is balancing pragmatic problem-solving with long-term maintainability, exactly what patch-package enables in this scenario.
Steps
You can follow the steps in TomUps post and use the following patch.
No comments:
Post a Comment