Instrumentación personalizada con OpenTelemetry custom-instrumentation

Cuando OpenTelemetry está habilitado para su entorno, AEM as a Cloud Service rastrea cada solicitud y exporta automáticamente las métricas de JVM, HTTP y AEM. Esa telemetría describe a AEM. Muestra que una página tardó 900 milisegundos en procesarse y qué componentes procesó. No puede mostrar lo que estaba haciendo su propio código, como para qué elemento de lista estaba una consulta del repositorio y si encontró algo, o qué páginas vuelven a una búsqueda costosa porque un autor omitió un campo.

Puede añadir ese conocimiento usted mismo con la API de OpenTelemetry. Los espacios y atributos que crea su código viajan con la telemetría automática: mismo nombre de servicio, mismo procesamiento, mismo proveedor de APM. No configure un extremo, una credencial o un exportador en el código.

Si no tienes experiencia en OpenTelemetry en AEM as a Cloud Service, empieza con el tutorial Observability a través de OpenTelemetry para saber cómo funciona. A continuación, siga Configurar OpenTelemetry para obtener información sobre las aplicaciones de AEM para habilitarla para su entorno.

Los ejemplos de esta página instrumentan el sitio de referencia WKND. En cada ejemplo se describe lo que se desea lograr, el código que se va a agregar y el resultado que se puede esperar en el proveedor de APM. El código instrumentado completo, incluidas las pruebas unitarias que se afirman en los intervalos, se encuentra en la rama tutorial/otel-custom-instrumentation del repositorio WKND.

NOTE
La instrumentación personalizada requiere que OpenTelemetry esté habilitado para su entorno. Consulte Configurar la integración.

Funcionamiento de la instrumentación personalizada how-it-works

  • El agente Java de OpenTelemetry que Adobe ejecuta en su entorno de AEM contiene el SDK de OpenTelemetry y los exportadores de. Su código depende únicamente de la API de OpenTelemetry.
  • Cuando el código llama a GlobalOpenTelemetry, el agente lo conecta a su propia implementación. Los espacios forman parte del rastro de la solicitud que los ejecuta y la telemetría se exporta con el resto de la telemetría del entorno.
  • Cuando el agente no está presente (AEM SDK local, pruebas unitarias o un entorno sin OpenTelemetry habilitado), la API no hace nada, con un coste insignificante. Puede implementar el mismo código en cada entorno.
Lo que agrega
Cómo
Enviado a su proveedor de APM
Abarca
Una anotación Tracer o @WithSpan
Sí
Atributos, eventos y estado de error en intervalos
La API Span
Sí
Entradas de registro correlacionadas con seguimientos
Su registro SLF4J existente
Sí, en el nivel WARN y superior, cuando los registros están habilitados
NOTE
Las métricas personalizadas, como los contadores y los histogramas que crea el código, están planificadas para una próxima versión.

Configuración del proyecto set-up-project

Paso 1: Añadir la API de OpenTelemetry add-api

Agregue la API OpenTelemetry y las anotaciones de instrumentación a la sección dependencyManagement del elemento principal del proyecto pom.xml, con el ámbito provided:

<properties>
    <opentelemetry.version>1.61.0</opentelemetry.version>
    <opentelemetry-instrumentation.version>2.27.0</opentelemetry-instrumentation.version>
</properties>

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>io.opentelemetry</groupId>
            <artifactId>opentelemetry-api</artifactId>
            <version>${opentelemetry.version}</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>io.opentelemetry.instrumentation</groupId>
            <artifactId>opentelemetry-instrumentation-annotations</artifactId>
            <version>${opentelemetry-instrumentation.version}</version>
            <scope>provided</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

A continuación, agregue ambas dependencias, sin versiones, a pom.xml de cada paquete que las use, como core/pom.xml:

<dependency>
    <groupId>io.opentelemetry</groupId>
    <artifactId>opentelemetry-api</artifactId>
</dependency>
<dependency>
    <groupId>io.opentelemetry.instrumentation</groupId>
    <artifactId>opentelemetry-instrumentation-annotations</artifactId>
</dependency>

Usar opentelemetry-api 1.61.0 y opentelemetry-instrumentation-annotations 2.27.0 o versiones anteriores. Coinciden con el agente Java de OpenTelemetry que ejecuta AEM as a Cloud Service y las versiones posteriores del agente siguen admitiéndolas. Las funciones de la API añadidas después de la versión compatible con el agente no tienen ningún efecto.

Paso 2: Incrustar la API en el paquete embed-api

AEM as a Cloud Service no exporta los paquetes de API de OpenTelemetry, por lo que cada paquete que utilice la API debe contener su propia copia. Añada las siguientes instrucciones a la configuración de enlace del paquete. Con el tipo de archivo del proyecto AEM, esa es la configuración de bnd-maven-plugin en core/pom.xml. Conservar las entradas que la instrucción Import-Package ya tenga.

Import-Package: !io.opentelemetry.*,javax.annotation;version=0.0.0,*
-includepackage: io.opentelemetry.*
Instrucción
Por qué es necesario
!io.opentelemetry.* al principio de Import-Package
La API hace referencia a paquetes opcionales de OpenTelemetry que AEM no proporciona y los carga solo si están presentes. Sin esta exclusión, el paquete importa esos paquetes, no se puede iniciar y la compilación de Cloud Manager falla. Ver Solución de problemas.
-includepackage: io.opentelemetry.*
Copia la API complete en su paquete. El agente conecta la copia de la API del paquete con su propia implementación solo cuando toda la API está presente. Si incrusta solo los paquetes a los que hace referencia su código, por ejemplo, con -conditionalpackage, sus espacios y atributos se descartan sin error.

Cada paquete que utiliza la API incrusta su propia copia y el agente conecta cada copia por separado.

CAUTION
No agregue OpenTelemetry SDK, un exportador ni el módulo de configuración automática de SDK al proyecto, ni cree su propia instancia de OpenTelemetrySdk ni llame a GlobalOpenTelemetry.set(). La telemetría enviada de esa forma evita al agente, la eliminación de los detalles de infraestructura de Cloud Service descritos en Qué datos se envían y el punto de conexión que configuró.

Paso 3: Creación de un rastreador create-tracer

Cree un(a) Tracer para la base de código y reutilícelo en todas partes. En WKND, una pequeña clase lo sostiene:

package com.adobe.aem.guides.wknd.core.telemetry;

import io.opentelemetry.api.GlobalOpenTelemetry;
import io.opentelemetry.api.trace.Tracer;

public final class WkndTelemetry {

    /** Instrumentation scope: identifies telemetry produced by WKND code in your APM provider. */
    public static final String SCOPE = "com.adobe.aem.guides.wknd";

    public static final Tracer TRACER = GlobalOpenTelemetry.getTracer(SCOPE);

    private WkndTelemetry() {
    }
}

El nombre de ámbito aparece en cada intervalo que cree a través de este rastreador, como el ámbito de instrumentación (o nombre de biblioteca). Utilice un nombre que identifique el código, como el paquete Java base del proyecto.

Lugar en el que aparecen los espacios where-spans-appear

En AEM as a Cloud Service, una solicitud de página ya produce intervalos desde el propio AEM. Los espacios que crea el código se agregan dentro de ellos. Por ejemplo, esta es la ruta al componente Lista de imágenes en un seguimiento de la página de WKND Magazine:

GET /*                                                     the request (AEM)
└─ SlingMainServlet.service                                Sling request processing (AEM)
   └─ AEM component: cq:Page                               (AEM)
      └─ AEM component: wknd/components/page               the page component (AEM)
         └─ HTL: libs.core.wcm.components.page.v3.page.page__002e__html
            └─ ...                                         containers and layout grids (AEM)
               └─ AEM component: wknd/components/image-list
                  └─ HTL: apps.wknd.components.image__002d__list.image__002d__list__002e__html
                     └─ ...                                spans that the Image List's Sling Model creates

Mientras un componente se procesa, el intervalo de su script HTL es el intervalo actual. Un modelo Sling que el script carga con data-sly-use se ejecuta dentro de ese intervalo, de modo que el modelo creado se convierta en sus elementos secundarios.

Cada intervalo de solicitud se denomina GET /* (o POST /*, etc.). Para encontrar los seguimientos de una página determinada, busque en el atributo url.path de la extensión de solicitud o agregue sus propios atributos, como se muestra en Ejemplo 3.

Ejemplos examples

Ejemplo 1: Seguimiento de una operación costosa trace-operation

Lo que desea lograr trace-operation-goal

El componente Lista de imágenes WKND (wknd/components/image-list) muestra una lista de páginas, cada una con su primera imagen. Para encontrar esa imagen, su modelo Sling, ImageListImpl, ejecuta una consulta QueryBuilder para cada elemento de la lista. Los propios espacios de AEM ya muestran esas consultas: en la página de WKND Magazine, que enumera cinco artículos, el seguimiento contiene cinco espacios de JCR query bajo el espacio HTL de la lista de imágenes. No muestran para qué elemento de lista estaba cada consulta, si encontró una imagen o el tiempo empleado en procesar los resultados. El código original sincronizó la consulta con System.currentTimeMillis() y escribió el resultado en una entrada de registro DEBUG, que no está disponible en producción. Desea que cada consulta aparezca en el seguimiento como una operación con nombre, con ese contexto.

El código trace-operation-code

En ImageListImpl.findPageComponentResources(), cree un intervalo alrededor de la consulta:

import io.opentelemetry.api.trace.Span;
import io.opentelemetry.api.trace.StatusCode;
import io.opentelemetry.context.Scope;

// ... build the QueryBuilder parameters, as before

final Span span = WkndTelemetry.TRACER.spanBuilder("ImageList.findPageComponentResources")
        .setAttribute("wknd.query.path", page.getContentResource().getPath())
        .setAttribute("wknd.query.resource_type", slingResourceType)
        .setAttribute("wknd.query.limit", limit)
        .startSpan();

try (Scope ignored = span.makeCurrent()) {
    // ... run the query and collect the resources, as before

    span.setAttribute("wknd.query.result_count", componentResources.size());
} catch (RuntimeException e) {
    span.recordException(e);
    span.setStatus(StatusCode.ERROR, "Image List query failed");
    throw e;
} finally {
    span.end();
}

return componentResources;
  • makeCurrent() convierte su espacio en el elemento principal de todo lo que se ejecuta dentro del bloque, incluidos los espacios que AEM crea para la consulta del repositorio.
  • Llamar siempre a end() en un bloque de finally. Un intervalo que no ha finalizado nunca se exporta.
  • Asigne al intervalo un nombre fijo que describa la operación y coloque valores que varíen, como rutas, en los atributos.

El resultado trace-operation-outcome

En la página de la revista WKND, que enumera cinco artículos, el seguimiento ahora contiene cinco ImageList.findPageComponentResources espacios dentro de la lista de imágenes, uno para cada elemento de lista. Cada uno es el elemento principal de la extensión JCR query que AEM crea para su consulta:

AEM component: wknd/components/image-list
└─ HTL: apps.wknd.components.image__002d__list.image__002d__list__002e__html
   ├─ ImageList.findPageComponentResources        your span
   │  └─ JCR query                                AEM
   ├─ ImageList.findPageComponentResources
   │  └─ JCR query
   └─ ...                                         three more

Cada uno de los espacios tiene el ámbito de instrumentación com.adobe.aem.guides.wknd y los atributos siguientes:

Atributo
Valor de ejemplo
wknd.query.path
/content/wknd/us/en/magazine/arctic-surfing/jcr:content
wknd.query.resource_type
wknd/components/image
wknd.query.limit
1
wknd.query.result_count
1

Ahora puede ver, para cada elemento de la lista, qué página buscó la consulta, si encontró una imagen (wknd.query.result_count es 0 cuando no lo encontró) y cuánto tiempo tardó toda la búsqueda, incluido el procesamiento de los resultados. En la página WKND Magazine, cada búsqueda tarda unos 6,5 milisegundos, de los cuales la propia consulta del repositorio tarda unos 3,5: la mitad del tiempo se destina a procesar los resultados, que los propios intervalos de AEM no muestran. Dado que el recuento de consultas aumenta con la longitud de la lista, también puede ver cuánto tiempo de procesamiento del componente corresponde a las búsquedas.

Ejemplo 2: Rastrear un método con una anotación with-span

Lo que desea lograr with-span-goal

El modelo SocialImageImpl de WKND se ejecuta en todas las páginas para rellenar la metaetiqueta og:image. Si un autor no ha establecido una imagen destacada, el modelo recorre el árbol de contenido de la página para encontrar la primera imagen y visita hasta 500 recursos. Uno quiere saber con qué frecuencia corre ese paseo y qué parte del árbol visita sin reestructurar el método.

El código with-span-code

Anotar el método con @WithSpan:

import io.opentelemetry.api.trace.Span;
import io.opentelemetry.instrumentation.annotations.WithSpan;

@WithSpan("SocialImage.resolveFromFirstImageOnPage")
protected boolean resolveFromFirstImageOnPage() {
    Resource contentResource = currentPage.getContentResource();
    if (contentResource == null) {
        return false;
    }

    int[] visitedCount = new int[]{0};
    Resource firstImage = findFirstImage(contentResource, 0, visitedCount);

    // Span.current() is the span that @WithSpan started for this method.
    Span.current().setAttribute("wknd.social_image.nodes_visited", visitedCount[0]);
    Span.current().setAttribute("wknd.social_image.walk_truncated", visitedCount[0] > MAX_NODES_TO_VISIT);

    // ... use firstImage, as before
}
  • El agente inicia un intervalo cada vez que se ejecuta el método y lo finaliza cuando el método devuelve o emite una excepción.
  • @WithSpan funciona en métodos públicos, protegidos y privados.
  • Para registrar un parámetro de método como un atributo, anote el parámetro con @SpanAttribute, por ejemplo void applyTemplate(@SpanAttribute("wknd.page.template") String template).
  • La anotación solo tiene efecto cuando se ejecuta el agente. En el SDK local de AEM y en las pruebas unitarias, el método se ejecuta sin cambios.

El resultado with-span-outcome

Un intervalo de SocialImage.resolveFromFirstImageOnPage solo aparece en los seguimientos de páginas que no tienen una imagen destacada:

Atributo
Valor de ejemplo
wknd.social_image.nodes_visited
21
wknd.social_image.walk_truncated
false
code.namespace
com.adobe.aem.guides.wknd.core.models.impl.SocialImageImpl (agregado por el agente)
code.function
resolveFromFirstImageOnPage (agregado por el agente)

Busque este nombre de span para enumerar las páginas que necesitan una imagen destacada. Si wknd.social_image.walk_truncated es true, la caminata se detuvo a los 500 recursos y es posible que la página no tenga ninguna imagen social.

Los intervalos creados por @WithSpan tienen el ámbito de instrumentación del agente (io.opentelemetry.opentelemetry-instrumentation-annotations-1.16), no el suyo.

Ejemplo 3: Agregar contexto empresarial al intervalo actual current-span

Lo que desea lograr current-span-goal

Desea filtrar los seguimientos por un hecho que su código sepa, aquí, de dónde viene la imagen social de la página, sin crear otro intervalo.

El código current-span-code

En SocialImageImpl, agregue un atributo al span que ya está activo:

import io.opentelemetry.api.common.AttributeKey;
import io.opentelemetry.api.trace.Span;

private static final AttributeKey<String> IMAGE_SOURCE = AttributeKey.stringKey("wknd.social_image.source");

@PostConstruct
protected void init() {
    if (currentPage == null) {
        return;
    }

    final String source;
    if (resolveFromFeaturedImage()) {
        source = "featured_image";
    } else if (resolveFromFirstImageOnPage()) {
        source = "first_image_on_page";
    } else {
        source = "none";
    }

    Span.current().setAttribute(IMAGE_SOURCE, source);
}
  • Span.current() devuelve el intervalo activo más interno, independientemente de si AEM o el código lo han creado. Para un modelo Sling cargado desde HTL, es el lapso de la secuencia de comandos HTL que cargó el modelo.
  • Si no hay ningún span activo, la llamada a no hace nada.
  • Definir AttributeKey constantes una vez mantiene los nombres de atributos coherentes en todo el código.

El resultado current-span-outcome

El intervalo del script customheaderlibs.html de la página, donde WKND carga este modelo, lleva wknd.social_image.source con el valor featured_image, first_image_on_page o none. Busque rastros donde wknd.social_image.source es none para encontrar las páginas publicadas sin una imagen social.

En el sitio WKND, cada página informa de first_image_on_page o none: ninguna página establece una imagen destacada, por lo que cada página paga por el recorrido del árbol de contenido de Ejemplo 2. Al establecer una imagen destacada en las páginas más visitadas, se elimina ese trabajo.

Ejemplo 4: Registrar un error record-errors

Lo que desea lograr record-errors-goal

El modelo ContentAIAvailabilityImpl de WKND comprueba si la Búsqueda por IA de contenido está configurada leyendo una configuración OSGi. Si la búsqueda falla, el modelo trata la inteligencia artificial aplicada al contenido como no configurada y registra una advertencia. La página se sigue procesando y la solicitud se realiza correctamente, por lo que es fácil pasar por alto el error. Desea que el error aparezca en las vistas de error del proveedor de APM, adjuntas a la operación que falló.

El código record-errors-code

import io.opentelemetry.api.trace.Span;
import io.opentelemetry.api.trace.StatusCode;
import io.opentelemetry.context.Scope;

private boolean resolveConfigured() {
    if (configurationAdmin == null) {
        return false;
    }
    final Span span = WkndTelemetry.TRACER.spanBuilder("ContentAIAvailability.resolveConfigured").startSpan();
    // Close the scope in finally, not with try-with-resources: try-with-resources closes it before
    // the catch block runs, and the warning logged there must carry this span's trace and span IDs.
    final Scope scope = span.makeCurrent();
    try {
        Configuration configuration = lookupConfiguration();
        // ... read the API key, as before
        span.setAttribute("wknd.content_ai.configured", isConfigured);
        return isConfigured;
    } catch (IOException e) {
        span.recordException(e);
        span.setStatus(StatusCode.ERROR, "Content AI configuration lookup failed");
        LOGGER.warn("Unable to determine whether Content AI is configured (PID {})", CONTENT_AI_CLIENT_PID, e);
        return false;
    } finally {
        scope.close();
        span.end();
    }
}
  • recordException() agrega un evento exception al intervalo, con el tipo, el mensaje y el seguimiento de pila de la excepción.
  • setStatus(StatusCode.ERROR, ...) marca el intervalo como erróneo. Configúrelo en el span que represente la operación que ha fallado. La solicitud en sí no se marca como errónea, ya que el componente se recuperó y se procesó la página.

El resultado record-errors-outcome

El seguimiento contiene un intervalo de ContentAIAvailability.resolveConfigured con el estado Error y la descripción Content AI configuration lookup failed. El intervalo tiene un evento exception:

Atributo de evento
Valor de ejemplo
exception.type
java.io.IOException
exception.message
Configuration store unavailable
exception.stacktrace
El seguimiento de pila completo

La extensión se cuenta como un error en el proveedor de APM, por lo que aparece siempre que el proveedor muestre operaciones fallidas. El intervalo de solicitud mantiene su estado, ya que la solicitud devolvió el valor HTTP 200.

Ejemplo 5: Correlacionar registros con seguimientos correlate-logs

Lo que desea lograr correlate-logs-goal

Cuando la búsqueda de inteligencia artificial aplicada al contenido en Ejemplo 4 da error, desea pasar de la advertencia de los registros al seguimiento de la solicitud que la produjo y viceversa.

El código correlate-logs-code

No necesita ningún código OpenTelemetry. Inicie sesión con SLF4J como de costumbre y habilite los registros (logs: true) en su configuración de OpenTelemetry:

LOGGER.warn("Unable to determine whether Content AI is configured (PID {})", CONTENT_AI_CLIENT_PID, e);

El agente agrega los ID de seguimiento y de span del span actual a cada entrada de registro. Debido a que ContentAIAvailabilityImpl registra mientras su intervalo de Ejemplo 4 es actual, la advertencia está vinculada a dicho intervalo.

El resultado correlate-logs-outcome

Su proveedor de APM recibe una entrada de registro como la siguiente y puede mostrarla con su seguimiento:

Campo
Valor de ejemplo
Gravedad
WARN
Cuerpo
Unable to determine whether Content AI is configured (PID com.adobe.cq.wcm.core.components.internal.services.contentai.ContentAIClientImpl)
ID de seguimiento e ID de span
Los identificadores de la extensión ContentAIAvailability.resolveConfigured
exception.type, exception.message, exception.stacktrace
La excepción que se registró
Ámbito de instrumentación
El nombre del registrador com.adobe.aem.guides.wknd.core.models.impl.contentai.ContentAIAvailabilityImpl
NOTE
Solo se envían las entradas de registro en el nivel WARN o superior. Las entradas INFO y DEBUG no lo son. No confíe en ellos para los diagnósticos de producción: registre la información como un atributo span en su lugar.

Prácticas recomendadas best-practices

  • El nombre se expande después de las operaciones, no después de los datos. Use nombres como ImageList.findPageComponentResources y coloque valores que varíen, como rutas e ID, en los atributos.
  • Agregue a sus propios nombres de atributos un espacio de nombres para su proyecto, como wknd.. Esto evita choques con las convenciones semánticas de OpenTelemetry y con los nombres que utiliza AEM. No utilice el prefijo aem., que está reservado para AEM.
  • No registre datos personales ni secretos en los nombres de span, atributos, eventos o mensajes de registro. Esto incluye nombres, direcciones de correo electrónico, direcciones IP, tokens y cadenas de consulta que pueden contenerlos.
  • Agregar espacios para operaciones, no iteraciones. Por ejemplo, Ejemplo 2 crea un intervalo para el recorrido del árbol de contenido y registra el número de recursos visitados como un atributo, en lugar de crear un intervalo para cada recurso. AEM ya crea muchos espacios para una sola página (unos 260 para la página de WKND Magazine) y cada espacio agrega al volumen que ingiere su proveedor de APM.
  • Cree trazadores una vez y, y vuelva a usarlos.
  • Finalizar cada intervalo en un bloque de finally y cerrar cada Scope en el subproceso que lo abrió.
  • Pase el contexto a los subprocesos que inicie usted mismo, por ejemplo con Context.current().wrap(runnable), para que el trabajo realizado en otro subproceso se una al mismo seguimiento.
  • Se espera un nuevo seguimiento para trabajar fuera de una solicitud. El código que se ejecuta en un trabajo de Sling, una tarea programada o un detector de eventos no tiene intervalo de solicitud. Los espacios que crea allí comienzan sus propios seguimientos, uno para cada ejecución.

Prueba de la instrumentación test

  • Pruebas unitarias: Sin el agente, la API no hace nada, por lo que las pruebas unitarias existentes siguen pasando sin cambios.
  • Compilación: Ejecute mvn clean install localmente. El analizador de AEM, que también ejecuta Cloud Manager, comprueba que se puedan resolver las importaciones del paquete.
  • Contenido del paquete: Compruebe que el paquete contiene la API completa, tal como se describe en Paso 2. Por ejemplo, unzip -l core/target/<your-core-bundle>.jar | grep "io/opentelemetry/api/baggage/" debe enumerar clases y el paquete META-INF/MANIFEST.MF no debe importar ningún paquete io.opentelemetry.
  • Su proveedor de APM: Implemente en un entorno de desarrollo que tenga habilitado OpenTelemetry, solicite una página que ejecute su código y busque en su proveedor de APM los nombres de sus span o el ámbito de instrumentación que eligió en Paso 3.

Resolución de problemas troubleshooting

Síntoma
Causa y solución
La compilación de Cloud Manager falla con [api-regions-exportsimports] ... is importing package(s) [io.opentelemetry.api.incubator, io.opentelemetry.api.incubator.internal, io.opentelemetry.sdk.autoconfigure] in start level 20 but no bundle is exporting these for that start level.
Su paquete importa paquetes opcionales de OpenTelemetry. Agregar !io.opentelemetry.* al principio de Import-Package. Consulte Paso 2.
El paquete está instalado pero no está activo, con missing requirement ... osgi.wiring.package=io.opentelemetry.api.incubator.
La misma causa y solución que la fila anterior.
@WithSpan se expandirá, pero los demás expansiones y los atributos que configuró con Span.current() no aparecen.
El paquete contiene solo parte de la API, por lo que el agente no conecta su copia. Agregar -includepackage: io.opentelemetry.*. Consulte Paso 2.
No aparece ninguno de los espacios.
Compruebe que OpenTelemetry esté habilitado para el entorno y que se haya ejecutado una implementación desde que implementó el código. Compruebe que la ruta del código se haya ejecutado y que todos los intervalos hayan finalizado.
Los espacios aparecerán como seguimientos independientes en lugar de dentro de la solicitud.
El intervalo se creó en otro subproceso sin el contexto de la solicitud. Pase el contexto al subproceso, por ejemplo, con Context.current().wrap(runnable).
Las entradas de registro no aparecen.
Solo se envían las entradas de WARN nivel y superiores, y solo cuando los registros están habilitados en su configuración.
recommendation-more-help
experience-manager-cloud-service-help-main-toc