Por qué una aplicación Kotlin "silenciosa" es una bomba de tiempo
\n\nImagina: lanzaste un servicio en Kotlin, funciona como un reloj. Los usuarios están contentos, las métricas están en verde. Pero un buen día, la carga se dispara, la latencia aumenta y no tienes idea de por qué. Sin registro ni monitoreo, eres un capitán de barco sin mapa ni radar. En este artículo, veremos cómo transformar tu aplicación de una "caja negra" a un sistema transparente, donde cada fallo sea visible a simple vista.
\n\nNo copiaremos la documentación. En su lugar, presentaremos casos reales, fragmentos de código listos para usar y soluciones arquitectónicas que funcionan en producción. ¡Allá vamos!
\n\nDividiendo el elefante en partes: registro vs monitoreo
\n\nAntes de escribir código, acordemos los términos. Registro es la grabación de eventos (errores, advertencias, información de depuración) en formato estructurado o de texto. Monitoreo es la recopilación de métricas (tiempo de respuesta, cantidad de solicitudes, carga de CPU) y su visualización. Idealmente, trabajan en conjunto: los registros son necesarios para un análisis profundo de incidentes, las métricas para la detección rápida de anomalías.
\n\nConfiguración del registro en Kotlin: de lo simple a lo complejo
\n\nEligiendo la biblioteca: SLF4J + Logback: el estándar de oro
\n\nKotlin se lleva muy bien con el ecosistema Java, por lo que SLF4J (fachada) y Logback (implementación) son la opción número 1. Son ligeros, eficientes y admiten el registro asíncrono.
\n\nAgrega en build.gradle.kts:
implementation("ch.qos.logback:logback-classic:1.4.14")\nimplementation("io.github.microutils:kotlin-logging-jvm:3.0.5")\n\nkotlin-logging es un envoltorio delgado sobre SLF4J que hace que el código sea más limpio. Compara:
\n\nSin envoltorio:
\nprivate val logger = LoggerFactory.getLogger(MyService::class.java)\nlogger.info("User {} logged in", userId)\n\nCon kotlin-logging:
\nprivate val logger = KotlinLogging.logger {}\nlogger.info { "User $userId logged in" }\n\nLa evaluación perezosa de cadenas es un plus para el rendimiento: la cadena se construirá solo si el nivel de registro está habilitado.
\n\nConfiguración de Logback: registros estructurados: algo imprescindible
\n\nCrea logback.xml en src/main/resources:
<configuration>\n <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">\n <encoder class="net.logstash.logback.encoder.LogstashEncoder"/>\n </appender>\n\n <root level="INFO">\n <appender-ref ref="CONSOLE"/>\n </root>\n</configuration>\n\nUsamos LogstashEncoder que convierte los registros a JSON. Esto es crítico si envías registros a Elasticsearch, Loki o cualquier otro agregador. Los registros JSON se analizan, filtran y procesan fácilmente.
\n\nNo olvides agregar la dependencia:
\nimplementation("net.logstash.logback:logstash-logback-encoder:7.4")\n\nEscribiendo registros significativos: el contexto lo es todo
\n\nLos registros como «Error occurred» son inútiles. Agrega MDC (Mapped Diagnostic Context) — es como etiquetas para tus registros.
import org.slf4j.MDC\n\nclass OrderService {\n private val logger = KotlinLogging.logger {}\n\n fun processOrder(orderId: String, userId: String) {\n MDC.put("orderId", orderId)\n MDC.put("userId", userId)\n try {\n logger.info { "Processing order" }\n // lógica de negocio\n } catch (e: Exception) {\n logger.error(e) { "Order processing failed" }\n } finally {\n MDC.clear()\n }\n }\n}\n\nAhora, en el agregador de registros, podrás encontrar todas las entradas para un pedido o usuario específico en segundos.
\n\n