惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

量子位
博客园 - 三生石上(FineUI控件)
D
DataBreaches.Net
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
GbyAI
GbyAI
P
Proofpoint News Feed
Microsoft Security Blog
Microsoft Security Blog
月光博客
月光博客
I
InfoQ
V
Visual Studio Blog
罗磊的独立博客
Engineering at Meta
Engineering at Meta
Vercel News
Vercel News
Jina AI
Jina AI
L
LangChain Blog
Apple Machine Learning Research
Apple Machine Learning Research
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
B
Blog
The Cloudflare Blog
小众软件
小众软件
雷峰网
雷峰网
V
V2EX
人人都是产品经理
人人都是产品经理
Stack Overflow Blog
Stack Overflow Blog

DEV Community

Authentication Security Deep Dive: From Brute Force to Salted Hashing (With Java Examples) Why AI Systems Don’t Fail — They Drift Spilling beans for how i learn for exam😁"Reinforcement Learning Cheat Sheet" I Replaced Chrome with Safari for AI Browser Automation. Here's What Broke (and What Finally Worked) How Python Borrows Other People's Work The $40 Architecture: Processing 1 Billion API Requests with 99.99% Uptime Vibe Coding: A Workflow Guide (From Zero to SaaS) Most webhook security guides protect the wrong side. The scary part is delivery. Headless CMS for TanStack Start: Build a Blog with Cosmic EU Age Verification App "Hacked in 2 Minutes" — What Actually Happened Comfy Cloud’s delete function does not actually remove files Running AI Models on GPU Cloud Servers: A Beginner Guide Event-driven media intelligence with AWS Step Functions and Bedrock I scored 500 AI prompts across 8 quality dimensions — here's what broke How to Call Google Gemini API from Next.js (Free Tier, No Backend Needed) The Portal Protocol: Reclaiming Human Connection in the Age of AI How to Fix Your Team's Scattered Knowledge Problem With a Self-Hosted Forum Intro to tc Cloud Functors: A Graph-First Mental Model for the Modern Cloud Designing Multi-Tenant Backends With Both Ownership and Team Access I Built a Neumorphic CSS Library with 77+ Components — Here's What I Learned PostgreSQL Performance Optimization: Why Connection Pooling Is Critical at Scale Cómo construí un SaaS multi-rubro para gestionar expensas en Argentina con FastAPI + Vue 3 🚀 I Built an Ethical Hacking Scanner Tool – Open Source Project I Replaced /usage and /context in Claude Code With a Single Statusline A Pythonic Way to Handle Emails (IMAP/SMTP) with Auto-Discovery and AI-Ready Design I Collected 8.9 Million Polymarket Price Points — Here's What I Found About How Markets Really Move EcoTrack AI — Carbon Footprint Tracker & Dashboard Everyone's Using AI. No One Agrees How. 5 self-hosted ebook managers worth trying in 2026 Building Your First AI Agent with LangChain: From Chatbot to Autonomous Assistant
Día 11: System.out.println en producción — la confesión q...
Alejandro La · 2026-04-24 · via DEV Community

Son las 3 de la mañana. Producción está caído. Abres los logs. 50.000 líneas de System.out.println que dicen cosas como "entró al método", "valor: " + algo, "pasó por acá". Ninguna tiene timestamp. Ninguna tiene nivel de severidad. Ninguna te dice qué request generó ese log.

Bienvenido a tu propia pesadilla. La diseñaste tú mismo.

Y antes de que digas "yo no hago eso" — haz un grep rápido de System.out.println en tu proyecto. O de console.log si estás en Node. O de print() sin logging si estás en Python. El resultado te va a sorprender.

Esto no es solo de Java

El anti-pattern es universal. Cada lenguaje tiene su versión:

  • Java: System.out.println en vez de SLF4J/Logback
  • JavaScript/Node: console.log en vez de Winston/Pino
  • Python: print() en vez del módulo logging
  • Go: fmt.Println en vez de log/slog o Zap
  • Ruby: puts en vez de Logger
  • .NET: Console.WriteLine en vez de ILogger

El problema es el mismo en todos los casos: estás imprimiendo texto plano a stdout sin estructura, sin niveles, sin metadata. Parece que estás logueando. Pero cuando lo necesitas de verdad, descubres que lo que tienes es ruido.

Por qué System.out.println destruye tu producción

No es solo que no tenga metadata. Es que afecta el rendimiento:

  1. Es sincrónico — cada println bloquea el thread hasta que el output se flushea. Con 200 threads concurrentes, todos escribiendo a stdout, creas contención en un recurso compartido.
  2. No tiene niveles — no puedes filtrar. ¿Quieres ver solo errores? Imposible. Es todo o nada.
  3. No tiene contexto — cuando 100 requests concurrentes imprimen "Procesando pago...", no sabes cuál es cuál.
  4. No rota — stdout no tiene rotación de archivos. Si redireccionas a un archivo, crece hasta llenar el disco.

El ANTES: el caos

@Service
public class PagoService {

    public PagoResponse procesarPago(PagoRequest request) {
        System.out.println("Procesando pago...");
        System.out.println("Monto: " + request.getMonto());
        System.out.println("Cliente: " + request.getClienteId());

        try {
            var resultado = gateway.cobrar(request);
            System.out.println("Pago OK: " + resultado.getId());
            return resultado;
        } catch (Exception e) {
            System.out.println("Error en pago: " + e.getMessage());
            throw e;
        }
    }
}

Enter fullscreen mode Exit fullscreen mode

En producción esto se ve así:

Procesando pago...
Monto: 15000
Procesando pago...
Cliente: 4821
Monto: 8700
Error en pago: timeout
Cliente: 9102
Pago OK: TXN-44291
Procesando pago...

Enter fullscreen mode Exit fullscreen mode

¿Qué pago falló? ¿De qué cliente? ¿A qué hora? No tienes idea. Los logs de distintos requests se mezclan. Es ruido puro.

El DESPUÉS: logging con sentido

@Service
public class PagoService {

    private static final Logger log = LoggerFactory.getLogger(PagoService.class);

    public PagoResponse procesarPago(PagoRequest request) {
        log.info("Procesando pago. clienteId={}, monto={}",
                 request.getClienteId(), request.getMonto());

        try {
            var resultado = gateway.cobrar(request);
            log.info("Pago exitoso. txnId={}, clienteId={}",
                     resultado.getId(), request.getClienteId());
            return resultado;
        } catch (Exception e) {
            log.error("Fallo al procesar pago. clienteId={}, monto={}",
                      request.getClienteId(), request.getMonto(), e);
            throw e;
        }
    }
}

Enter fullscreen mode Exit fullscreen mode

Ahora los logs se ven así:

2026-04-13 03:14:22.841 INFO  [http-nio-8080-exec-7] c.a.s.PagoService : Procesando pago. clienteId=4821, monto=15000
2026-04-13 03:14:23.102 ERROR [http-nio-8080-exec-3] c.a.s.PagoService : Fallo al procesar pago. clienteId=9102, monto=8700
java.net.SocketTimeoutException: timeout
    at com.app.gateway.GatewayClient.cobrar(GatewayClient.java:45)

Enter fullscreen mode Exit fullscreen mode

Cada línea tiene timestamp, nivel, thread, clase y datos estructurados. En 2 segundos sabes qué falló, cuándo y para quién.

Los niveles no son decoración

Cada framework de logging tiene los mismos niveles. La tabla aplica a cualquier lenguaje:

Nivel Cuándo usarlo Ejemplo
ERROR Algo falló y necesita atención Error al procesar pago, conexión perdida
WARN Algo raro pero la app sigue Retry #3, cache miss inesperado
INFO Eventos de negocio relevantes Pago procesado, usuario creado
DEBUG Detalle técnico para troubleshooting Query ejecutada, payload recibido
TRACE Todo lo demás (casi nunca en prod) Entrada/salida de cada método

La regla: en producción corres en INFO. Si hay un problema, subes un paquete específico a DEBUG sin reiniciar. En Spring Boot, Actuator te permite cambiar niveles de log en caliente:

curl -X POST localhost:8080/actuator/loggers/com.tuapp.gateway \
  -H 'Content-Type: application/json' \
  -d '{"configuredLevel": "DEBUG"}'

Enter fullscreen mode Exit fullscreen mode

Sin redeploy. Sin restart. Cambias el nivel, miras los logs, resuelves el problema, vuelves a INFO.

En Node con Winston es similar: cambias el level del transport en runtime. En Python con logging.getLogger().setLevel(). El concepto es el mismo en todos lados.

Structured logging: el salto de calidad

Si tus logs van a un ELK Stack, Datadog, CloudWatch o cualquier herramienta de observabilidad, el formato texto plano es un problema. Lo que necesitas es JSON:

{
  "timestamp": "2026-04-13T03:14:23.102Z",
  "level": "ERROR",
  "logger": "com.tuapp.service.PagoService",
  "message": "Fallo al procesar pago",
  "clienteId": 9102,
  "monto": 8700,
  "stack_trace": "java.net.SocketTimeoutException..."
}

Enter fullscreen mode Exit fullscreen mode

Buscable. Filtrable. Alertable. Puedes hacer queries como "dame todos los ERROR del PagoService en la última hora donde monto > 10000". Intenta hacer eso con System.out.println.

En Spring Boot se configura con Logstash encoder. En Node con Pino (que ya sale en JSON por default). En Python con python-json-logger. En Go con slog (que viene integrado desde Go 1.21).

Lo que NO deberías loguear

Antes de loguear todo, un segundo:

  • Passwords, tokens, API keys. Jamás. Ni en DEBUG. log.debug("Token: {}", token) es un incidente de seguridad esperando a pasar.
  • Datos personales sensibles. DNI, tarjetas de crédito, datos médicos. Si tu log tiene PII, tienes un problema de compliance.
  • El request body completo. Un JSON de 2MB en cada log llena tu disco en horas.
  • Datos que no ayudan a diagnosticar nada. "Entró al método" no es información. "Procesando pedido 4821 para cliente 102" sí lo es.

El error de fondo

System.out.println te da una falsa sensación de visibilidad. Parece que estás logueando. Parece que tienes información. Pero cuando la necesitas de verdad, a las 3AM con producción caído, descubres que lo que tienes es ruido.

Logging profesional no es difícil. Todos los lenguajes y frameworks modernos ya tienen las herramientas listas. Solo tienes que dejar de usar print y empezar a pensar qué información necesitarías para diagnosticar un problema que todavía no pasó.

Haz un grep de System.out.println en tu proyecto. Si aparece más de cero veces en código de producción, tienes tarea.

Esto es el Día 11

Este artículo es parte de #100ArchitectureDays — una serie de problemas reales de arquitectura con soluciones reales.

Sigue la saga completa en #100ArchitectureDays.

Todo el código está en GitHub — con el BEFORE/AFTER para que veas la diferencia en la consola en tiempo real. Si te está sirviendo, déjame una estrella — es gratis y ayuda a que más gente lo encuentre.