Todo programa que funciona en serio necesita dos cosas que no son código: una forma de contar lo que hace mientras funciona y una forma de cambiar cómo se comporta sin tener que reescribirlo. La primera son los logs (en español, registros); la segunda, los archivos de configuración, que en el mundo Java se llaman a menudo «archivos de propiedades». Esta guía explica qué son, para qué sirven, qué errores evitar y qué archivos concretos usa cada lenguaje y cada framework.
Qué es un log
Un log es el diario de a bordo de un programa: una lista de anotaciones, en orden, de lo que va pasando. «Se creó el pedido 1042», «queda poco stock del producto 15», «no se pudo conectar con el banco». Mientras todo va bien, nadie lo lee. Cuando algo falla, a las tres de la mañana y en un servidor al que nadie estaba mirando, el log es lo único que cuenta qué pasó.
Sirve para tres cosas:
- Encontrar errores: qué falló, cuándo, con qué datos y qué pasó justo antes.
- Vigilar: cuántos errores hay por minuto, qué tarda demasiado, quién intenta entrar sin permiso.
- Dejar constancia: quién hizo qué y cuándo, para una auditoría o para resolver una reclamación.
Las partes de una línea
Además de esas cuatro, los logs modernos añaden contexto: el identificador del pedido, del usuario o de la petición web. Con un identificador común se pueden seguir todos los pasos de una misma operación aunque pase por varios servidores.
Los niveles: no todo es igual de grave
Cada anotación lleva un nivel de gravedad. Los nombres cambian un poco entre lenguajes, pero la idea es la misma:
| Nivel | Para qué | Ejemplo |
|---|---|---|
TRACE y DEBUG | detalle para quien programa; apagados en producción | «Consulta SQL con estos parámetros…» |
INFO | lo normal, lo que conviene saber que pasó | «Pedido 1042 creado» |
WARN o WARNING | algo raro que todavía no es un error | «Quedan 2 unidades del producto 15» |
ERROR | algo falló y hay que mirarlo | «No se pudo cobrar el pedido 1042» |
FATAL o CRITICAL | el programa no puede seguir | «No hay conexión con la base de datos al arrancar» |
El nivel se usa para filtrar: en producción se suele registrar de INFO hacia arriba, y si hay un problema se baja a DEBUG durante un rato para ver el detalle. En los sistemas Linux, el estándar syslog tiene ocho niveles, de emerg a debug.
Así se ve un log en cada lenguaje
Los mismos dos mensajes, escritos con la herramienta que trae cada lenguaje, tal como salen de verdad:
Python (módulo logging)2026-10-27 21:15:29,324 INFO tienda.pedidos: Pedido 1042 creado para el cliente 77
2026-10-27 21:15:29,325 WARNING tienda.pedidos: Stock bajo del producto 15: quedan 2
2026-10-27 21:15:29,326 ERROR tienda.pedidos: No se pudo calcular el descuento del pedido 1042
Traceback (most recent call last):
File "/p/log.py", line 8, in <module>
1 / 0
~~^~~
ZeroDivisionError: division by zeroJava (java.util.logging)Oct 27, 2026 9:15:29 PM Log main INFO: Pedido 1042 creado para el cliente 77 Oct 27, 2026 9:15:30 PM Log main WARNING: Stock bajo del producto 15: quedan 2
Go (log/slog, en texto y en JSON)time=2026-10-27T21:15:33.589-06:00 level=INFO msg="pedido creado" pedido=1042 cliente=77
{"time":"2026-10-27T21:15:33.589789688-06:00","level":"WARN","msg":"stock bajo","producto":15,"quedan":2}PHP (error_log y un aviso)[28-Oct-2026 03:15:33 UTC] Pedido 1042 creado para el cliente 77 [28-Oct-2026 03:15:33 UTC] PHP Warning: Stock bajo del producto 15: quedan 2 in /p/log.php on line 3
Fíjate en tres cosas. Python registra el error completo, con la línea donde ocurrió; por eso conviene registrar las excepciones con su traza. Java escribe la fecha en inglés y en dos líneas, un formato antiguo que se cambia con su configuración. Y PHP escribe la hora en UTC, mientras los demás usan la de México: si no se configura la zona horaria, cada programa elige la suya, y comparar logs de dos programas se vuelve un lío. Lo más práctico es registrar siempre en UTC o con la diferencia horaria incluida, como hace Go.
Texto o JSON
La segunda línea de Go es un log estructurado: en vez de una frase, un objeto JSON con campos. Para una persona es menos cómodo de leer, pero para una máquina es perfecto: se puede buscar «todos los avisos del producto 15» o contar errores por minuto sin trucos con expresiones regulares. Cuando los logs de muchos servidores se reúnen en un mismo sitio, lo habitual es JSON.
Dónde acaban
- En la consola (la salida estándar). Es lo recomendado en contenedores: Docker y Kubernetes recogen lo que el programa escribe ahí.
- En archivos, normalmente en
/var/log/en Linux o en una carpetalogsde la aplicación. Un programa comologrotatelos rota: cada día empieza un archivo nuevo, comprime los anteriores y borra los muy viejos, para que no llenen el disco. - En el diario del sistema, en los Linux con
systemd, que se consulta conjournalctl. - En un sistema central, como Elasticsearch u OpenSearch con Kibana, Grafana Loki, Graylog o el servicio de registros de cada nube, que permiten buscar en los de todos los servidores y avisar cuando algo pasa.
Los servidores web tienen los suyos: Nginx y Apache escriben un access log, una línea por cada visita, y un error log. Si quieres aprender a leerlos, en Cómo saber si atacaron tu WordPress lo explicamos paso a paso.
Lo que nunca debe ir en un log
- Cuidado con lo que escribe el usuario. Si un texto del usuario va tal cual al log, puede meter saltos de línea y fabricar líneas falsas que confundan a quien lo lea. Las bibliotecas de log estructurado lo evitan solas; con texto plano, hay que limpiar los saltos de línea.
- La biblioteca de logs también se actualiza. En diciembre de 2021, un fallo en Log4j, la biblioteca de logs más usada de Java, permitía tomar el control de un servidor con solo conseguir que registrara un texto concreto. Se llamó Log4Shell (CVE-2021-44228) y afectó a miles de empresas. Las dependencias de logs se mantienen al día como cualquier otra.
- Protege los archivos de log: que solo los lean quienes deben, y que no queden dentro de la carpeta pública de una web.
Qué es un archivo de configuración
Un archivo de configuración es el panel de ajustes de un programa: guarda lo que cambia de un sitio a otro sin cambiar el código. La dirección de la base de datos, el puerto en el que escucha, el nivel de log, el correo desde el que se envían avisos o si el modo de depuración está activado.
La idea clave es que el mismo programa funcione en tu ordenador, en el servidor de pruebas y en producción cambiando solo la configuración. Si la dirección de la base de datos estuviera escrita en el código, habría que tocar y volver a compilar el programa cada vez que se instala en un sitio distinto.
Los formatos más habituales
Esta es la misma configuración de «tutienda» en los formatos que verás con más frecuencia:
# application.properties (Java) servidor.puerto=8080 basedatos.url=jdbc:postgresql://localhost/tutienda log.nivel=INFO
# application.yml (YAML) servidor: puerto: 8080 basedatos: url: jdbc:postgresql://localhost/tutienda log: nivel: INFO
{
"Servidor": { "Puerto": 8080 },
"BaseDatos": { "Url": "Host=localhost;Database=tutienda" },
"Logging": { "LogLevel": { "Default": "Information" } }
}# .env (variables de entorno en un archivo) PUERTO=8080 DATABASE_URL=postgres://localhost/tutienda LOG_LEVEL=info
# config.toml (TOML) [servidor] puerto = 8080 [basedatos] url = "postgres://localhost/tutienda"
| Formato | Cómo es | Dónde se ve |
|---|---|---|
.properties | líneas clave=valor, con puntos para agrupar | Java y Spring Boot |
YAML (.yml, .yaml) | jerárquico por sangría; legible, pero los espacios importan | Spring Boot, Symfony, Rails, Docker Compose, Kubernetes |
| JSON | jerárquico con llaves; estricto y sin comentarios | .NET, Node.js (package.json), TypeScript (tsconfig.json) |
| TOML | secciones entre corchetes; pensado para personas | Rust (Cargo.toml), Python (pyproject.toml), Go |
| INI | secciones y clave=valor; el más antiguo | PHP (php.ini), Python, Windows |
.env | variables de entorno en un archivo, una por línea | Laravel, Symfony, Node, Python, Docker |
| XML | etiquetas; muy detallado | Maven (pom.xml), .NET antiguo (web.config), Tomcat |
| código | un archivo del propio lenguaje | Django (settings.py), Laravel (config/*.php), WordPress (wp-config.php) |
Capas y entornos
Casi todos los frameworks leen la configuración de varios sitios a la vez, en un orden fijo, y lo que llega después sobrescribe lo anterior:
Gracias a eso, el archivo general lleva valores que sirven para todos, cada entorno (desarrollo, pruebas, producción) cambia solo lo suyo, y el servidor puede ajustar algo puntual con una variable de entorno sin tocar ningún archivo. Es la forma recomendada de trabajar en contenedores: la misma imagen en todas partes, y la configuración, fuera.
Los secretos, aparte
.env que está en .gitignore o en un gestor de secretos (los de cada nube, HashiCorp Vault, los secrets de Kubernetes). En el repositorio, solo un archivo de ejemplo con valores falsos.- Nunca dentro de la carpeta pública de una web. Un
.envo una copia dewp-config.phpque se puede descargar con una URL es una de las filtraciones más frecuentes. - El modo de depuración, apagado en producción (
APP_DEBUG=falseen Laravel,DEBUG = Falseen Django): si no, un error enseña al visitante la configuración y a veces las contraseñas. - Comprueba la configuración al arrancar: si falta un valor obligatorio, mejor que el programa no arranque a que falle a mitad de una compra.
Qué usa cada lenguaje y cada framework
Lo que sigue son los archivos y herramientas que encontrarás en cada caso, empezando por lo que trae el lenguaje y siguiendo por lo más usado.
Logs
| Lenguaje o framework | Herramienta de logs | Configuración de los logs | Dónde escribe por omisión |
|---|---|---|---|
| Java | java.util.logging (incluido); Log4j 2 y Logback, casi siempre detrás de SLF4J | logging.properties, log4j2.xml, logback.xml | consola |
| Spring Boot | Logback, a través de SLF4J | application.properties (logging.level.*) o logback-spring.xml | consola; a archivo con logging.file.name |
| Python | módulo logging (incluido); también structlog y loguru | en código, o un archivo con logging.config | consola (salida de errores) |
| Django | módulo logging | el diccionario LOGGING de settings.py | consola |
| Node.js | console (incluido); pino y winston | en código o en la configuración de la biblioteca | consola |
| PHP | error_log() (incluido); Monolog, el estándar de facto (PSR-3) | php.ini (error_log, log_errors) | el registro del servidor o la consola |
| Laravel | Monolog | config/logging.php y LOG_CHANNEL en .env | storage/logs/laravel.log |
| Symfony | Monolog | config/packages/monolog.yaml | var/log/dev.log, var/log/prod.log… |
| C# y .NET | ILogger (incluido); Serilog y NLog | sección Logging de appsettings.json | consola |
| Go | log y log/slog (incluidos, slog desde Go 1.21); zap y zerolog | en código | consola (salida de errores para log) |
| Rust | log con env_logger, o tracing | la variable de entorno RUST_LOG | consola |
| Ruby on Rails | Rails.logger | config/environments/*.rb | log/development.log, log/production.log |
Configuración
| Lenguaje o framework | Archivos de configuración | Proyecto y dependencias | Por entorno y secretos |
|---|---|---|---|
| Java | .properties con java.util.Properties | pom.xml (Maven), build.gradle (Gradle) | variables de entorno |
| Spring Boot | application.properties o application.yml | pom.xml o build.gradle | application-prod.properties con perfiles (spring.profiles.active) |
| Python | INI con configparser, TOML, .env con python-dotenv | pyproject.toml | variables de entorno; pydantic-settings |
| Django | settings.py | pyproject.toml o requirements.txt | un settings por entorno, secretos de variables de entorno |
| Node.js | .env (con dotenv o node --env-file, desde Node 20.6), JSON | package.json | NODE_ENV y variables de entorno |
| PHP | php.ini | composer.json | variables de entorno |
| Laravel | .env y config/*.php | composer.json | APP_ENV; el .env nunca en Git |
| Symfony | .env, .env.local y config/packages/*.yaml | composer.json | APP_ENV y el gestor de secretos de Symfony |
| WordPress | wp-config.php | — | fuera de la carpeta pública si se puede |
| C# y .NET | appsettings.json; en proyectos antiguos, web.config o app.config | .csproj | appsettings.Production.json con ASPNETCORE_ENVIRONMENT; dotnet user-secrets en desarrollo |
| Go | sin estándar: argumentos, variables de entorno, YAML o TOML (por ejemplo, con Viper) | go.mod | variables de entorno |
| Rust | TOML o variables de entorno (crates config y dotenvy) | Cargo.toml | variables de entorno |
| Ruby on Rails | config/database.yml y config/environments/*.rb | Gemfile | config/credentials.yml.enc, cifrado (desde Rails 5.2) |
| Docker y Kubernetes | compose.yaml; ConfigMap y Secret | — | variables de entorno y secrets |
Algunos casos con más detalle en el propio sitio: la configuración por entorno en Docker, tsconfig.json explicado y las variables de entorno en PHP, C# y Java.
Resumen
| Logs | Configuración | |
|---|---|---|
| Qué es | el diario de lo que hace el programa | el panel de ajustes que cambia sin tocar el código |
| Lo escribe | el programa, mientras funciona | quien lo instala, antes de arrancarlo |
| Lo lee | una persona o un sistema de vigilancia | el programa, al arrancar |
| Formato | texto con fecha, nivel, origen y mensaje, o JSON | .properties, YAML, JSON, TOML, INI, .env… |
| Lo que no puede llevar | contraseñas, tokens ni datos personales innecesarios | secretos subidos al repositorio |
| Buena costumbre | niveles bien usados, contexto y rotación | un archivo por entorno y los secretos, aparte |
Las líneas de log de Python 3.14, Java 21, Go 1.27 y PHP 8.5 se generaron el 27 de octubre de 2026 en contenedores, con la herramienta que trae cada lenguaje y su configuración por omisión. Niveles de syslog: RFC 5424. Log4Shell: CVE-2021-44228 en el NVD.
