Para enviar tus registros a Datadog, registra en un archivo y haz el seguimiento de las últimas líneas tail de ese archivo con tu Datadog Agent.

Las trazas de pila de los registros típicos de Java se dividen en múltiples líneas, lo que dificulta asociarlas al evento de registro original. Por ejemplo:

//4 events generated when only one is expected!
Exception in thread "main" java.lang.NullPointerException
        at com.example.myproject.Book.getTitle(Book.java:16)
        at com.example.myproject.Author.getBookTitles(Author.java:25)
        at com.example.myproject.Bootstrap.main(Bootstrap.java:14)

Para abordar este problema, configura tu biblioteca de registro para producir tus registros en formato JSON. Al registrar en JSON, tú:

  • Asegúrate de que la traza de pila esté correctamente envuelta en el evento de registro.
  • Asegúrate de que todos los atributos del evento de registro (como severidad, nombre del registrador y nombre del hilo) estén correctamente extraídos.
  • Obtén acceso a los atributos del Contexto de Diagnóstico Mapeado (MDC), que puedes adjuntar a cualquier evento de registro.
  • Evita la necesidad de reglas de análisis personalizadas.

Las siguientes instrucciones muestran ejemplos de configuración para las bibliotecas de registro Log4j, Log4j 2 y Logback.

Configura tu registrador

Formato JSON

Para Log4j, registra en formato JSON utilizando el módulo SLF4J log4j-over-slf4j combinado con Logback. log4j-over-slf4j reemplaza limpiamente a Log4j en tu aplicación, por lo que no necesitas hacer ningún cambio en el código.

  1. En tu archivo pom.xml, reemplaza la dependencia log4j.jar con una dependencia log4j-over-slf4j.jar, y agrega las dependencias de Logback. Por ejemplo:

    <dependency>
      <groupId>org.slf4j</groupId>
      <artifactId>log4j-over-slf4j</artifactId>
      <version>1.7.32</version>
    </dependency>
    <dependency>
      <groupId>ch.qos.logback</groupId>
      <artifactId>logback-classic</artifactId>
      <version>1.2.9</version>
    </dependency>
    <dependency>
      <groupId>net.logstash.logback</groupId>
      <artifactId>logstash-logback-encoder</artifactId>
      <version>6.6</version>
    </dependency>
    
  2. Configura un appender utilizando el diseño JSON en logback.xml. Consulta los siguientes ejemplos de configuraciones para archivo y consola.

    Para archivo:

    <configuration>
      <appender name="FILE" class="ch.qos.logback.core.FileAppender">
        <file>logs/app.log</file>
        <encoder class="net.logstash.logback.encoder.LogstashEncoder" />
      </appender>
    
      <root level="INFO">
        <appender-ref ref="FILE"/>
      </root>
    </configuration>
    

    For console:

    <configuration>
      <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
          <encoder class="ch.qos.logback.classic.encoder.JsonEncoder"/>
      </appender>
    
      <root>
        <level value="DEBUG"/>
          <appender-ref ref="CONSOLE"/>
        </root>
    </configuration>
    

Log4j 2 incluye un diseño JSON.

  1. Configura un appender utilizando el diseño JSON en log4j2.xml. Consulta los siguientes ejemplos de configuraciones para el appender de archivo y consola. Para una descripción completa de los plugins de Log4j, consulta la referencia de plugins de Log4j.

log4j2.xml

<?xml version="1.0" encoding="UTF-8"?>
  <Configuration>
    <Appenders>
      <File name="FILE" fileName="logs/app.log" >
        <JsonTemplateLayout eventTemplateUri="classpath:MyLayout.json"/>
      </File>
    </Appenders>
    <Loggers>
      <Root level="INFO">
        <AppenderRef ref="FILE"/>
      </Root>
    </Loggers>
  </Configuration>

log4j2.xml

  <?xml version="1.0" encoding="UTF-8"?>
  <Configuration>
    <Appenders>
      <Console name="console" target="SYSTEM_OUT">
        <JsonTemplateLayout eventTemplateUri="classpath:MyLayout.json"/>
      </Console>
    </Appenders>
    <Loggers>
      <Root level="INFO">
        <AppenderRef ref="console"/>
      </Root>
    </Loggers>
  </Configuration>
  1. Agrega el archivo de plantilla de diseño JSON (como MyLayout.json) en el directorio src/main/resources de tu proyecto Java. Por ejemplo:

    {
       "timestamp":{
          "$resolver":"timestamp",
          "pattern":{
             "format":"yyyy-MM-dd'T'HH:mm:ss.SSS'Z'",
             "timeZone":"UTC"
          }
       },
       "status":{
          "$resolver":"level",
          "field":"name"
       },
       "thread_name":{
          "$resolver":"thread",
          "field":"name"
       },
       "logger_name":{
          "$resolver":"logger",
          "field":"name"
       },
       "message":{
          "$resolver":"message",
          "stringified":true
       },
       "exception_class":{
          "$resolver":"exception",
          "field":"className"
       },
       "exception_message":{
          "$resolver":"exception",
          "field":"message"
       },
       "stack_trace":{
          "$resolver":"exception",
          "field":"stackTrace",
          "stackTrace":{
             "stringified":true
          }
       },
       "host":"${hostName}",
       "service":"${env:DD_SERVICE}",
       "version":"${env:DD_VERSION}",
       "dd.trace_id":{
          "$resolver":"mdc",
          "key":"dd.trace_id"
       },
       "dd.span_id":{
          "$resolver":"mdc",
          "key":"dd.span_id"
       }
    }
    
  2. Agrega las dependencias de diseño JSON a tu pom.xml. Por ejemplo:

    <dependency>
        <groupId>org.apache.logging.log4j</groupId>
        <artifactId>log4j-core</artifactId>
        <version>2.17.1</version>
    </dependency>
    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-core</artifactId>
        <version>2.13.0</version>
    </dependency>
    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-databind</artifactId>
        <version>2.13.0</version>
    </dependency>
    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-annotations</artifactId>
        <version>2.13.0</version>
    </dependency>
    

Utiliza el logstash-logback-encoder para registros formateados en JSON en Logback.

  1. Configura un appender de archivo utilizando el diseño JSON en logback.xml. Por ejemplo:

    <configuration>
      <appender name="FILE" class="ch.qos.logback.core.FileAppender">
        <file>logs/app.log</file>
        <encoder class="net.logstash.logback.encoder.LogstashEncoder" />
      </appender>
    
      <root level="INFO">
        <appender-ref ref="FILE"/>
      </root>
    </configuration>
    
  2. Agrega la dependencia del codificador Logstash a tu archivo pom.xml. Por ejemplo:

    <dependency>
      <groupId>ch.qos.logback</groupId>
      <artifactId>logback-classic</artifactId>
      <version>1.2.9</version>
    </dependency>
    <dependency>
      <groupId>net.logstash.logback</groupId>
      <artifactId>logstash-logback-encoder</artifactId>
      <version>6.6</version>
    </dependency>
    

Crea una configuración de escritor JSON basada en la documentación oficial de Tinylog.

Utiliza el siguiente formato en un archivo tinylog.properties:

writer                     = json
writer.file                = log.json
writer.format              = LDJSON
writer.level               = info
writer.field.level         = level
writer.field.source        = {class}.{method}()
writer.field.message       = {message}
writer.field.dd.trace_id   = {context: dd.trace_id}
writer.field.dd.span_id    = {context: dd.span_id}
writer.field.dd.service    = {context: dd.service}
writer.field.dd.version    = {context: dd.version}
writer.field.dd.env        = {context: dd.env}

Formato en bruto

Configura un appender de archivo en log4j.xml. Por ejemplo:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
<log4j:configuration>

  <appender name="FILE" class="org.apache.log4j.FileAppender">
    <param name="File" value="logs/app.log"/>
    <param name="Append" value="true"/>

    <layout class="org.apache.log4j.PatternLayout">
      <param name="ConversionPattern" value="%d{yyyy-MM-dd HH:mm:ss} %-5p %C:%L - %X{dd.trace_id} %X{dd.span_id} - %m%n"/>
    </layout>
  </appender>

  <root>
    <priority value="INFO"/>
    <appender-ref ref="FILE"/>
  </root>

</log4j:configuration>

Configura un appender de archivo en log4j2.xml. Por ejemplo:

<?xml version="1.0" encoding="UTF-8"?>
<Configuration>
  <Appenders>
    <File name="FILE" fileName="logs/app.log">
      <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss} %-5p %C:%L - %X{dd.trace_id} %X{dd.span_id} - %m%n"/>
    </File>
  </Appenders>

  <Loggers>
    <Root level="INFO">
      <AppenderRef ref="FILE"/>
    </Root>
  </Loggers>
</Configuration>

Configura un appender de archivo en logback.xml. Por ejemplo:

<configuration>
  <appender name="FILE" class="ch.qos.logback.core.FileAppender">
    <file>${dd.test.logfile}</file>
    <append>false</append>
    <immediateFlush>true</immediateFlush>

    <encoder>
      <pattern>%d{yyyy-MM-dd HH:mm:ss} %-5p %C:%L - %X{dd.trace_id} %X{dd.span_id} - %m%n</pattern>
    </encoder>
  </appender>

  <root level="INFO">
    <appender-ref ref="FILE"/>
  </root>
</configuration>

Crea una configuración de escritor que envíe a un archivo basada en la documentación oficial de Tinylog.

Utiliza el siguiente formato en un archivo tinylog.properties:

writer          = file
writer.level    = debug
writer.format   = {level} - {message} - "dd.trace_id":{context: dd.trace_id} - "dd.span_id":{context: dd.span_id}
writer.file     = log.txt

Inyecta ID de traza en tus registros

Si APM está habilitado para esta aplicación, puedes correlacionar registros y trazas habilitando la inyección de ID de traza. Consulta Conectando Registros y Trazas de Java.

Si no estás correlacionando registros y trazas, elimina los marcadores de posición MDC (%X{dd.trace_id} %X{dd.span_id}) de los patrones de registro incluidos en los ejemplos de configuración anteriores.

Por ejemplo, si estás utilizando Log4j 2 pero no correlacionando registros y trazas, elimina el siguiente bloque de la plantilla de diseño de registro de ejemplo, MyLayout.json:

"dd.trace_id":{
   "$resolver":"mdc",
   "key":"dd.trace_id"
},
"dd.span_id":{
   "$resolver":"mdc",
   "key":"dd.span_id"
}

Configura el Datadog Agent

Una vez que la recolección de registros esté habilitada, configura la recolección de registros personalizada para el seguimiento de las últimas líneas de tus archivos de registro y enviarlos a Datadog.

  1. Crea una java.d/ carpeta en el conf.d/ directorio de configuración del Agent.

  2. Crea un conf.yaml archivo en java.d/ con el siguiente contenido:

    #Log section
    logs:
    
      - type: file
        path: "<path_to_your_java_log>.log"
        service: <service_name>
        source: java
        sourcecategory: sourcecode
        # For multiline logs, if they start by the date with the format yyyy-mm-dd uncomment the following processing rule
        #log_processing_rules:
        #  - type: multi_line
        #    name: new_log_start_with_date
        #    pattern: \d{4}\-(0?[1-9]|1[012])\-(0?[1-9]|[12][0-9]|3[01])
    
  3. Reinicia el Agent.

  4. Ejecuta el subcomando de estado del Agent y busca java en la sección Checks para confirmar que los registros se han enviado correctamente a Datadog.

Si los registros están en formato JSON, Datadog automáticamente analiza los mensajes de registro para extraer los atributos de registro. Utiliza el Explorador de registros para ver y solucionar problemas en tus registros.

Transmite registros directamente al Agent

En el caso excepcional en que tu aplicación se esté ejecutando en una máquina a la que no se puede acceder o no puede registrar en un archivo, es posible transmitir registros a Datadog o directamente al Agent de Datadog. Esta no es la configuración recomendada, porque requiere que tu aplicación maneje problemas de conexión.

Para transmitir registros directamente a Datadog:

  1. Agrega la biblioteca de registro Logback a tu código, o conecta tu registrador actual a Logback.
  2. Configura Logback para enviar registros a Datadog.

Conecta desde bibliotecas de registro de Java a Logback

Si aún no estás utilizando Logback, la mayoría de las bibliotecas de registro comunes pueden conectarse a Logback.

Utiliza el módulo SLF4J log4j-over-slf4j con Logback para enviar registros a otro servidor. log4j-over-slf4j reemplaza limpiamente Log4j en tu aplicación para que no tengas que hacer cambios en el código.

  1. En tu archivo pom.xml, reemplaza la dependencia log4j.jar con una dependencia log4j-over-slf4j.jar, y agrega las dependencias de Logback. Por ejemplo:
    <dependency>
      <groupId>org.slf4j</groupId>
      <artifactId>log4j-over-slf4j</artifactId>
      <version>1.7.32</version>
    </dependency>
    <dependency>
      <groupId>ch.qos.logback</groupId>
      <artifactId>logback-classic</artifactId>
      <version>1.2.9</version>
    </dependency>
    <dependency>
      <groupId>net.logstash.logback</groupId>
      <artifactId>logstash-logback-encoder</artifactId>
      <version>6.6</version>
    </dependency>
    
  2. Configura Logback.

Nota: Como resultado de este cambio, los archivos de configuración de Log4j ya no serán recogidos. Migra tu log4j.properties archivo a logback.xml con el traductor de Log4j.

Log4j 2 permite el registro en un host remoto, pero no ofrece la capacidad de prefijar los registros con una clave API. Debido a esto, utiliza el módulo SLF4J log4j-over-slf4j y Logback. log4j-to-slf4j.jar reemplaza limpiamente Log4j 2 en tu aplicación para que no tengas que hacer ningún cambio en el código. Para usarlo:

  1. En tu archivo pom.xml, reemplaza la dependencia log4j.jar con una dependencia log4j-over-slf4j.jar, y agrega las dependencias de Logback. Por ejemplo:
    <dependency>
        <groupId>org.apache.logging.log4j</groupId>
        <artifactId>log4j-to-slf4j</artifactId>
        <version>2.17.1</version>
    </dependency>
    <dependency>
        <groupId>ch.qos.logback</groupId>
        <artifactId>logback-classic</artifactId>
        <version>1.2.9</version>
    </dependency>
    <dependency>
        <groupId>net.logstash.logback</groupId>
        <artifactId>logstash-logback-encoder</artifactId>
        <version>6.6</version>
    </dependency>
    
  2. Configura Logback.

Notas:

Configura Logback

Datadog no admite el envío de registros directamente a través de TCP a la ingesta de Datadog. En su lugar, configura Logback a tu Agente local de Datadog, que luego reenvía los registros a Datadog a través de HTTPS con enriquecimiento automático.

  1. Instala un Agente local de Datadog (v6+ / v7+).

  2. Habilita la recolección de registros en datadog.yaml, y asegúrate de que el Agente reenvíe los registros a través de HTTPS (HTTPS es el transporte predeterminado para el Agente v6.19+/v7.19+ y posteriores):

    logs_enabled: true
    logs_config:
      # HTTPS is the default. Keep or set this to force HTTPS forwarding.
      force_use_http: true
      # (Optional) auto-detect multi-line patterns
      auto_multi_line_detection: true
    
  3. Habilita la recolección de registros en el Agent.

    # /etc/datadog-agent/conf.d/logback.d/conf.yaml
    logs:
      - type: tcp
        port: 10518           # Port the Agent will listen on
        service: my-java-app  # Your service name (unified service tagging)
        source: java          # Or a more specific source, e.g., "logback"
    
  4. Reinicia el Agent para aplicar los cambios.

  5. Configura Logback para enviar registros al Agent. Utiliza el logstash-logback-encoder TCP appender en tu logback.xml para reenviar registros al Agent:

    <configuration>
      <appender name="DD_TCP_JSON" class="net.logstash.logback.appender.LogstashTcpSocketAppender">
        <destination>localhost:10518</destination>
        <encoder class="net.logstash.logback.encoder.LoggingEventCompositeJsonEncoder">
          <providers>
            <timestamp/>
            <pattern>
              <pattern>
                {
                  "message": "%message",
                  "level": "%level",
                  "logger": "%logger",
                  "service": "${DD_SERVICE:-my-java-app}",
                  "env": "${DD_ENV:-prod}",
                  "version": "${DD_VERSION:-1.0.0}",
                  "dd.trace_id": "%X{dd.trace_id}",
                  "dd.span_id": "%X{dd.span_id}"
                }
              </pattern>
            </pattern>
            <arguments/>
            <stackTrace/>
          </providers>
        </encoder>
      </appender>
    </configuration>
    

    Luego, haz referencia a él en tu logger raíz:

    <root level="INFO">
      <appender-ref ref="DD_TCP_JSON"/>
    </root>
    
  6. Verifica el reenvío de registros. Ejecuta datadog-agent status para confirmar tu TCP listener y revisa el Logs Explorer en busca de entradas etiquetadas con tu servicio.

Obteniendo más

Enriquece tus eventos de registro con atributos contextuales.

Usando el analizador de clave-valor

El analizador de clave-valor extrae cualquier patrón <KEY>=<VALUE> reconocido en cualquier evento de registro.

Para enriquecer tus eventos de registro en Java, puedes reescribir mensajes en tu código e introducir secuencias <KEY>=<VALUE>.

Por ejemplo, si tienes:

logger.info("Emitted 1001 messages during the last 93 seconds for customer scope prod30");

Puedes cambiarlo a:

logger.info("Emitted quantity=1001 messages during the last durationInMs=93180 ms for customer scope=prod30");

Con el analizador de clave-valor habilitado, cada par se extrae del JSON:

{
  "message": "Emitted quantity=1001 messages during the last durationInMs=93180 ms for customer scope=prod30",
  "scope": "prod30",
  "durationInMs": 93180,
  "quantity": 1001
}

Así que puedes utilizar scope como un campo, y durationInMs y quantity como medidas de registro.

MDC

Otra opción para enriquecer tus registros es usar [Mapped Diagnostic Contexts (MDC)] de Java.

Si usas SLF4J, utiliza el siguiente código Java:

...
MDC.put("scope", "prod30");
logger.info("Emitted 1001 messages during the last 93 seconds");
...

Para generar este JSON:

{
  "message": "Emitted 1001 messages during the last 93 seconds",
  "scope": "prod30"
}

Nota: MDC solo permite tipos de cadena, así que no los uses para métricas de valores numéricos.

Lectura Adicional