Rastreo distribuido para funciones
Descubra cómo activar el rastreo y ver rastreos de funciones al depurar con OCI Functions.
Cuando se llama a una función pero no se ejecuta o se ejecuta como se esperaba, debe investigar el problema en un nivel detallado. La función de rastreo distribuido observa la ejecución de la función a medida que se mueve a través de los diferentes componentes del sistema. Puede rastrear e instrumentar funciones independientes para depurar problemas de ejecución y rendimiento. También puede utilizar el rastreo de funciones para depurar problemas con aplicaciones sin servidor completas que incluyen varias funciones y servicios, como:
- una función que llama a otra función
- una función que llama a otros servicios, como el servicio Object Storage
- una función que sirve como backend para un gateway de API desplegado en el servicio de gateway de API
- una función disparada en respuesta a un evento por el servicio Events, el servicio Notifications o Connector Hub
Las capacidades de rastreo de OCI Functions las proporciona el servicio Oracle Cloud Infrastructure Application Performance Monitoring. Las funciones de Application Performance Monitoring (APM) permiten identificar y solucionar fallos y problemas de latencia en las funciones que cree y despliegue.
En el servicio Application Performance Monitoring:
- Un dominio de APM contiene los sistemas supervisados por Application Performance Monitoring. Un dominio de APM es una instancia de un recopilador de datos de rastreo y período que almacena, agrega, muestra y visualiza los datos.
- Un rastreo es el flujo completo de una solicitud que pasa por todos los componentes de un sistema distribuido en un período de tiempo determinado. Consta de un árbol completo de intervalos, todos relacionados con el mismo flujo de solicitud global único.
- Un intervalo es una operación o una unidad lógica de trabajo con un nombre, una hora de inicio y una duración, dentro de un rastreo. Un intervalo es un segmento de tiempo asociado con la duración de una unidad de trabajo dentro del flujo de solicitud general.
El explorador de rastreo de Application Performance Monitoring permite visualizar todo el flujo de solicitudes y explorar los detalles de rastreo y período para el diagnóstico. Puede ver y supervisar rastreos lentos y rastreos con errores. Para aislar e identificar incidencias de rastreo, puede aumentar el detalle de períodos específicos, como cargas de páginas, llamadas AJAX y solicitudes de servicio. Para obtener más información sobre el servicio Application Performance Monitoring, consulte Application Performance Monitoring.
Para activar el rastreo de una función, debe:
- Configure una política para otorgar permiso al servicio OCI Functions para acceder a los dominios de APM, si la política aún no existe (consulte Sentencias de política para otorgar acceso al servicio OCI Functions y a los usuarios de OCI Functions a los recursos de rastreo).
- Configure un dominio de APM.
- Active el rastreo para la aplicación Functions y seleccione el dominio de APM que ha creado.
- Active el rastreo para una o más funciones.
Al activar el rastreo para una función, OCI Functions genera automáticamente un "plazo de llamada de función por defecto". El intervalo por defecto captura información sobre el contexto de ejecución de la función, incluido el tiempo total que se tarda en procesar la solicitud y devolver una respuesta al emisor de la llamada. Además del período de llamada de función por defecto, puede agregar código a las funciones para definir períodos personalizados. Utilice intervalos personalizados para capturar más información específica de la función para ayudar con la depuración. Por ejemplo, puede definir intervalos personalizados para capturar el inicio y el final de unidades de trabajo específicas. Por ejemplo, las unidades de trabajo podrían incluir la obtención de la contraseña de base de datos del almacén, la apertura de una conexión a base de datos y la recuperación de registros de la base de datos.
Se han agregado cuatro variables al contexto de OCI Functions que proporcionan información de rastreo útil. Estas variables incluyen:
FN_APP_NAME:
Nombre de la aplicación de la función.FN_FN_NAME:
Nombre de función.OCI_TRACE_COLLECTOR_URL
: URL de dominio de APM con clave de datos.OCI_TRACING_ENABLED:
¿Está activado el rastreo?- Cuando se recupera de variables de entorno, devuelve 0 o 1.
- Cuando se recupera del contexto de la función, devuelve
true
ofalse
según corresponda para el idioma utilizado.
Política de IAM necesaria para activar el rastreo
Para poder activar el rastreo, el grupo al que pertenece debe tener permiso para acceder a los dominios de APM existentes o para crear dominios de APM. Además, OCI Functions debe tener permiso para acceder a los dominios de APM. Consulte Sentencias de política para proporcionar a los usuarios del servicio OCI Functions y OCI Functions acceso a recursos de rastreo.
Uso de la consola para activar el rastreo y ver rastreos de funciones
Se necesitan un par de pasos para activar el rastreo y ver los rastreos de funciones para el servicio Oracle Cloud Infrastructure Application Performance Monitoring (APM). En primer lugar, active el rastreo para la aplicación que contiene la función. A continuación, active el rastreo para una o más funciones. A continuación, puede ver los rastreos de funciones en el explorador de rastreo de APM.
Uso de la consola para activar el rastreo
Para activar el rastreo, siga estos pasos.
- En la página de lista Aplicaciones, seleccione la aplicación con funciones para las que desea activar el rastreo. Si necesita ayuda para buscar la página de lista o la aplicación, consulte Listado de aplicaciones.
- Seleccione el separador Supervisión y vaya a la sección Rastreos.
- Para activar el rastreo para la aplicación:
- En el menú
- compartimento: compartimento en el que se va a crear el rastreo. Por defecto, el compartimento actual.
- Dominio de APM: dominio de APM (definido en el servicio Application Performance Monitoring) en el que crear el rastreo. Para utilizar un dominio de APM existente, seleccione un dominio de APM existente de la lista. O bien, para crear un nuevo dominio de APM, seleccione APM Domain (Dominio de APM). Para obtener más información sobre los dominios de APM, consulte Introducción a Application Performance Monitoring. Nota
El dominio de APM debe tener claves de datos públicas y privadas para que el rastreo de funciones funcione. Si las claves no existen, puede crearlas a través de la interfaz de la consola.
, seleccione Configurar y especifique: - Seleccione Activar Rastreo para activar el rastreo para la aplicación.
Una vez activado el rastreo para la aplicación de Functions, ahora puede activar el rastreo para una o más funciones de la aplicación.
- En el menú
- Para activar el rastreo para funciones específicas en la aplicación:
- Seleccione el separador Funciones.
- Seleccione la opción Activar rastreo en el menú Acciones (tres puntos) de las funciones para las que desea activar el rastreo.
La opción Activar Rastreo sólo se muestra si ha activado previamente el rastreo para la aplicación. Tenga en cuenta lo siguiente:
- Si no se muestra la opción Activar rastreo, debe activar el rastreo para la aplicación. Si aún no ha activado el rastreo para la aplicación, consulte el paso anterior.
- Si activó previamente el rastreo para la aplicación, pero posteriormente lo desactivó, se muestra un enlace Activar rastreo de aplicación. Seleccione el enlace Activar Rastreo de Aplicación para volver a activar el rastreo de la aplicación (consulte el paso anterior). Después de volver a activar el rastreo para la aplicación, puede activar el rastreo para funciones específicas.
Cuando haya activado el rastreo para la aplicación y una o más funciones, puede ver los rastreos de funciones.
Uso de la consola para ver rastreos de funciones
Para ver los rastreos de las funciones que tienen el rastreo activado:
- En la página de lista Aplicaciones, seleccione la aplicación que contiene las funciones para las que desea ver rastreos. Si necesita ayuda para buscar la página de lista o la aplicación, consulte Listado de aplicaciones.
- Para ver rastreos de funciones:
- Para ver los rastreos de todas las funciones que tienen el rastreo activado en la aplicación:
- Seleccione el separador Supervisión y vaya a la sección Rastreos.
- Seleccione el nombre del rastreo. Nota
Un nombre de rastreo solo se muestra si ya ha activado el rastreo para la aplicación.
- Para ver el rastreo de una función específica que tiene el rastreo activado:
- Seleccione el separador Funciones.
- Seleccione la opción Ver rastreo en el menú Acciones (tres puntos) de la función para la que desea ver el rastreo.Nota
La opción Ver rastreo solo se muestra si ya ha activado el rastreo para la función.
Los rastreos de las funciones seleccionadas se muestran en el explorador de rastreo de APM. Por defecto, se muestra un rastreo para el intervalo de llamada a la función por defecto y cualquier intervalo personalizado definido para la función.
- Para ver los rastreos de todas las funciones que tienen el rastreo activado en la aplicación:
- En el explorador de rastreo de APM:
- Seleccione un rastreo para ver los intervalos de ese rastreo.
- Seleccione un período para ver los detalles capturados para ese período.
Para obtener más información sobre el uso del explorador de rastreo de APM, consulte Uso del explorador de rastreo.
Rastreo de una Cadena de Funciones
Por defecto, el rastreo de funciones proporciona un rastreo para una llamada de función completa. Sin embargo, a menudo con las aplicaciones modernas en la nube, necesita encadenar llamadas a funciones. El rastreo de OCI Functions proporciona la capacidad de rastrear la ejecución de una función llamada por otra función. Esta capacidad significa que puede examinar la ejecución de cada función en una cadena de llamadas en un solo árbol de intervalos en el explorador de rastreo de APM.
Para rastrear una cadena de funciones, debe propagar las cabeceras X-B3 X-B3-TraceId
, X-B3-SpanId
, X-B3-ParentSpanId
y X-B3-Sampled
en la solicitud de llamada de función desde el código de función.
Una vez ejecutada la función, los datos de rastreo de las funciones se recopilan y están disponibles en el explorador de rastreo de APM. Para obtener más información sobre el uso del explorador de rastreo de APM, consulte Uso del explorador de rastreo.
A continuación, se muestra un ejemplo de cómo puede rastrear una cadena de funciones. Si desea probar este ejemplo, debe crear dos funciones de ejemplo. Siga estos pasos para configurar las funciones.
- Cree la función de rastreo de Python:
fn init --runtime python <your-function-name-1>
- ¡Crea tu "Hello World!" Función Python:
fn init --runtime python <your-function-name-2>
- Despliegue ambas funciones:
fn -v deploy --app <app-name>
- Obtenga el OCID de la segunda función y llame al punto final:
fn inspect function your-app-name your-function-name-2
- Cree un archivo JSON para transferir la información necesaria a la primera función. Por ejemplo, el archivo
test.json
podría tener el siguiente aspecto:{ "function_ocid": "ocid1.fnfunc.oc1.iad.aaaaaaaaxxxxxxxxxxx", "function_endpoint": "https://xxxxxxxxx.us-ashburn-1.functions.oci.oraclecloud.com", "function_body": "", "__comment": "Alternatively, you can set function_body to { \"name\": \"Oracle\" }" }
- Cuando se llama a la primera función, puede transferir la información de la segunda función mediante
test.json
:fn invoke <app-name> <your-function-name-1> < test.json
Ahora está listo para actualizar la primera función con las actualizaciones de código necesarias.
Configurar paquetes
Actualice el archivo requirements.txt
para incluir los siguientes paquetes:
fdk
oci
Guardar el archivo.
Actualización del código de función para propagar las cabeceras X-B3
La función Python llama a la función handler
y transfiere la información de JSON desde el comando de llamada. La función handler
se divide en varios bloques pequeños para simplificarla. El archivo de origen completo se proporciona en la parte inferior de esta sección.
Cargar los datos de JSON
En esta primera parte, los datos de JSON se cargan desde la llamada a la función.
import io
import json
import logging
import oci
from fdk import response
def handler(ctx, data: io.BytesIO=None):
app_name = ctx.AppName()
func_name = ctx.FnName()
logging.getLogger().info("Inside app: " + app_name + " | function: " + func_name + " | method: handler")
try:
body = json.loads(data.getvalue())
function_endpoint = body.get("function_endpoint")
function_ocid = body.get("function_ocid")
function_body = body.get("function_body")
except (Exception) as ex:
print('ERROR: Missing key in payload', ex, flush=True)
raise
Creación de Llamada de Cliente y Recopilación de Información de Cabecera
Cree el cliente de llamada de Functions mediante el SDK de Python de OCI y los principales de recursos de Functions. A continuación, recupere tracing_context
y extraiga la información necesaria para crear las cabeceras HTTP.
signer = oci.auth.signers.get_resource_principals_signer()
client = oci.functions.FunctionsInvokeClient(config={}, signer=signer, service_endpoint=function_endpoint)
#
# Zipkin X-B3- header propagation
#
tracing_context = ctx.TracingContext()
trace_id = tracing_context.trace_id()
span_id = tracing_context.span_id()
parent_span_id = tracing_context.parent_span_id()
is_sampled = tracing_context.is_sampled()
Propagar las cabeceras X-B3
El SDK de Python de OCI le permite definir cabeceras personalizadas. Utilice esta técnica para transferir las cabeceras X-B3 a la segunda llamada de función. La información de cabecera se transfiere para trace_id
, span_id
, parent_span_id
y is_sampled
. Por último, se llama a la segunda función con client
y la respuesta se transfiere a la respuesta de esta función.
# if tracing is enabled, is_sampled will be true in the tracing context
if is_sampled:
# To propagate headers in the OCI SDK in the request to the next function,
# add the X-B3- headers in the request. This header will be included in ALL
# subsequent calls made.
if trace_id is not None:
client.base_client.session.headers['X-B3-TraceId'] = trace_id
logging.getLogger().info("Inside app: " + app_name + " | function: " + func_name + " | method: handler | trace_id: " + trace_id)
if span_id is not None:
client.base_client.session.headers['X-B3-SpanId'] = span_id
logging.getLogger().info("Inside app: " + app_name + " | function: " + func_name + " | method: handler | span_id: " + span_id)
if parent_span_id is not None:
client.base_client.session.headers['X-B3-ParentSpanId'] = parent_span_id
logging.getLogger().info("Inside app: " + app_name + " | function: " + func_name + " | method: handler | parent_span_id: " + parent_span_id)
client.base_client.session.headers['X-B3-Sampled'] = str(int(is_sampled))
logging.getLogger().info("Inside app: " + app_name + " | function: " + func_name + " | method: handler | is_sampled: " + str(int(is_sampled)))
else:
# function.trace is DISABLED
logging.getLogger().info("Inside app: " + app_name + " | function: " + func_name + " | method: handler | function tracing is DISABLED")
resp = client.invoke_function(function_id=function_ocid, invoke_function_body=function_body)
logging.getLogger().info("Inside app: " + app_name + " | function: " + func_name + " | method: handler | Response: " + resp.data.text)
return response.Response(
ctx,
response_data=resp.data.text,
headers={"Content-Type": "application/json"}
)
Este es el código fuente completo para la función de Python de ejemplo.
#
# oci-invoke-function-python version 2.0.
#
# Copyright (c) 2021 Oracle, Inc.
# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl.
#
import io
import json
import logging
import oci
from fdk import response
def handler(ctx, data: io.BytesIO=None):
app_name = ctx.AppName()
func_name = ctx.FnName()
logging.getLogger().info("Inside app: " + app_name + " | function: " + func_name + " | method: handler")
try:
body = json.loads(data.getvalue())
function_endpoint = body.get("function_endpoint")
function_ocid = body.get("function_ocid")
function_body = body.get("function_body")
except (Exception) as ex:
print('ERROR: Missing key in payload', ex, flush=True)
raise
signer = oci.auth.signers.get_resource_principals_signer()
client = oci.functions.FunctionsInvokeClient(config={}, signer=signer, service_endpoint=function_endpoint)
#
# Zipkin X-B3- header propagation
#
tracing_context = ctx.TracingContext()
trace_id = tracing_context.trace_id()
span_id = tracing_context.span_id()
parent_span_id = tracing_context.parent_span_id()
is_sampled = tracing_context.is_sampled()
# if tracing is enabled, is_sampled will be true in the tracing context
if is_sampled:
# To propagate headers in the OCI SDK in the request to the next function,
# add the X-B3- headers in the request. This header will be included in ALL
# subsequent calls made.
if trace_id is not None:
client.base_client.session.headers['X-B3-TraceId'] = trace_id
logging.getLogger().info("Inside app: " + app_name + " | function: " + func_name + " | method: handler | trace_id: " + trace_id)
if span_id is not None:
client.base_client.session.headers['X-B3-SpanId'] = span_id
logging.getLogger().info("Inside app: " + app_name + " | function: " + func_name + " | method: handler | span_id: " + span_id)
if parent_span_id is not None:
client.base_client.session.headers['X-B3-ParentSpanId'] = parent_span_id
logging.getLogger().info("Inside app: " + app_name + " | function: " + func_name + " | method: handler | parent_span_id: " + parent_span_id)
client.base_client.session.headers['X-B3-Sampled'] = str(int(is_sampled))
logging.getLogger().info("Inside app: " + app_name + " | function: " + func_name + " | method: handler | is_sampled: " + str(int(is_sampled)))
else:
# function.trace is DISABLED
logging.getLogger().info("Inside app: " + app_name + " | function: " + func_name + " | method: handler | function tracing is DISABLED")
resp = client.invoke_function(function_id=function_ocid, invoke_function_body=function_body)
logging.getLogger().info("Inside app: " + app_name + " | function: " + func_name + " | method: handler | Response: " + resp.data.text)
return response.Response(
ctx,
response_data=resp.data.text,
headers={"Content-Type": "application/json"}
)
Adición de períodos personalizados a funciones
Con el rastreo de funciones activado, el período de llamada a la función por defecto proporciona un rastreo para toda la llamada a la función. El intervalo por defecto puede proporcionar buena información, pero al investigar el código puede que desee profundizar más. Los intervalos personalizados se agregan directamente al código y le permiten definir intervalos para un método o un bloque de código. Los datos resultantes proporcionan una mejor imagen de su función a medida que se ejecuta.
Para poder utilizar períodos personalizados, debe activar el rastreo para la aplicación y las funciones mediante el servicio Oracle Cloud Infrastructure Application Performance Monitoring (APM). Para definir el rastreo, debe:
- Configure una política para otorgar permiso al servicio OCI Functions para acceder a los dominios de APM, si la política aún no existe (consulte Sentencias de política para otorgar acceso al servicio OCI Functions y a los usuarios de OCI Functions a los recursos de rastreo).
- Configure un dominio de APM.
- Active el rastreo para la aplicación Functions y seleccione el dominio de APM que ha creado.
- Active el rastreo para una o más funciones.
Estos pasos ya se han tratado. Sin embargo, se necesitan un par de cosas más para intervalos personalizados:
- Seleccione una biblioteca de cliente de rastreo distribuido, por ejemplo Zipkin.
- Agregue bibliotecas de cliente a las dependencias de función.
- En el código de función, utilice la variable de contexto de función
OCI_TRACING_ENABLED
para comprobar si el rastreo está activado. - En el código de función, utilice la variable de contexto de función
OCI_TRACE_COLLECTOR_URL
para enviar los períodos personalizados al dominio de APM. - Agregue instrumentación al código de función.
Para utilizar intervalos personalizados, debe tener las siguientes versiones mínimas de los FDK de Fn Project:
- FDK DE Java: 1.0.129
- FDK de Python: 0.1.22
- Nodo FDK: 0.0.20
A continuación, se muestra un ejemplo de cómo utilizar Zipkin para agregar períodos personalizados a la función Java. Si desea probar este ejemplo, puede crear una función "Hello World!" de Java y agregar código de período personalizado. Para crear una función de ejemplo:
- Cree una función Java:
fn init --runtime java apm-fn-java
- Para simplificar, elimine el directorio
src/test
.
Configurar Maven
Agregue las siguientes dependencias a la sección <dependencies> del archivo pom.xml
de Maven.
<dependency>
<groupId>io.zipkin.reporter2</groupId>
<artifactId>zipkin-sender-urlconnection</artifactId>
<version>2.16.3</version>
</dependency>
<dependency>
<groupId>io.zipkin.reporter2</groupId>
<artifactId>zipkin-reporter-brave</artifactId>
<version>2.16.3</version>
</dependency>
<dependency>
<groupId>io.zipkin.brave</groupId>
<artifactId>brave</artifactId>
<version>5.13.3</version>
</dependency>
<dependency>
<groupId>io.zipkin.brave</groupId>
<artifactId>brave-core</artifactId>
<version>4.13.6</version>
</dependency>
Guardar el archivo.
El método HandleRequest
Las observaciones sobre el método siguen el código fuente handleRequest
.
package com.example.fn;
import brave.Span;
import brave.Tracer;
import brave.Tracing;
import brave.propagation.*;
import brave.sampler.Sampler;
import com.fnproject.fn.api.tracing.TracingContext;
import com.github.kristofa.brave.IdConversion;
import zipkin2.reporter.Sender;
import zipkin2.reporter.brave.AsyncZipkinSpanHandler;
import zipkin2.reporter.urlconnection.URLConnectionSender;
public class HelloFunction {
Sender sender;
AsyncZipkinSpanHandler zipkinSpanHandler;
Tracing tracing;
Tracer tracer;
String apmUrl;
TraceContext traceContext;
public String handleRequest(String input, TracingContext tracingContext) {
try {
intializeZipkin(tracingContext);
// Start a new trace or a span within an existing trace representing an operation
Span span = tracer.newChild(traceContext).name("MainHandle").start();
System.out.println("Inside Java Hello World function");
try (Tracer.SpanInScope ws = tracer.withSpanInScope(span)) {
method1();
method2();
method3();
} catch (RuntimeException | Error e) {
span.error(e); // Unless you handle exceptions, you might not know the operation failed!
throw e;
} finally {
span.finish(); // note the scope is independent of the span. Always finish a span.
tracing.close();
zipkinSpanHandler.flush();
}
} catch (Exception e) {
return e.getMessage();
}
return "Hello, AppName " + tracingContext.getAppName() + " :: fnName " + tracingContext.getFunctionName();
}
- El objeto
TracingContext tracingConext
transfiere toda la información relacionada con APM necesaria para realizar conexiones al servicio APM. - Se llama al método
intializeZipkin
, que actualizatracingContext
y crea un objetotracer
que se utiliza para configurar períodos personalizados. - Se crea un
span
para el período personalizado principal. A continuación, se llama a tres métodos en el ámbito del período principal. - Observe que en el bloque
finally
se cierran todos los objetos de rastreo.
El método initializeZipkin
Las observaciones sobre el método intializeZipkin
siguen el código fuente.
public void intializeZipkin(TracingContext tracingContext) throws Exception {
System.out.println("Initializing the variables");
apmUrl = tracingContext.getTraceCollectorURL();
sender = URLConnectionSender.create(apmUrl);
zipkinSpanHandler = AsyncZipkinSpanHandler.create(sender);
tracing = Tracing.newBuilder()
.localServiceName(tracingContext.getServiceName())
.sampler(Sampler.NEVER_SAMPLE)
.addSpanHandler(zipkinSpanHandler)
.build();
tracer = tracing.tracer();
tracing.setNoop(!tracingContext.isTracingEnabled());
traceContext = TraceContext.newBuilder()
.traceId(IdConversion.convertToLong(tracingContext.getTraceId()))
.spanId(IdConversion.convertToLong(tracingContext.getSpanId()))
.sampled(tracingContext.isSampled()).build();
}
traceContext
se transfiere para crear todos los objetos utilizados para crear períodos personalizados.apmURL
se recupera del métodogetTraceCollectorURL()
. La URL es el punto final del dominio de APM y se utiliza para crear el objetotracer
que crea los períodos personalizados.- Un creador toma
zipkinSpanHandler
y el nombre del servicio para crear un objetotracer
. Este objetotracer
se utiliza para crear períodos personalizados.
Creación de períodos personalizados
Con el objeto tracer
inicializado, se pueden crear períodos personalizados.
public void method1() {
System.out.println("Inside Method1 function");
TraceContext traceContext = tracing.currentTraceContext().get();
Span span = tracer.newChild(traceContext).name("Method1").start();
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
span.finish();
}
}
- El método
method1
crea un período personalizado denominado "Method1".
Este es el código fuente completo para la función de rastreo de Java de ejemplo.
package com.example.fn;
import brave.Span;
import brave.Tracer;
import brave.Tracing;
import brave.propagation.*;
import brave.sampler.Sampler;
import com.fnproject.fn.api.tracing.TracingContext;
import com.github.kristofa.brave.IdConversion;
import zipkin2.reporter.Sender;
import zipkin2.reporter.brave.AsyncZipkinSpanHandler;
import zipkin2.reporter.urlconnection.URLConnectionSender;
public class HelloFunction {
Sender sender;
AsyncZipkinSpanHandler zipkinSpanHandler;
Tracing tracing;
Tracer tracer;
String apmUrl;
TraceContext traceContext;
public void intializeZipkin(TracingContext tracingContext) throws Exception {
System.out.println("Initializing the variables");
apmUrl = tracingContext.getTraceCollectorURL();
sender = URLConnectionSender.create(apmUrl);
zipkinSpanHandler = AsyncZipkinSpanHandler.create(sender);
tracing = Tracing.newBuilder()
.localServiceName(tracingContext.getServiceName())
.sampler(Sampler.NEVER_SAMPLE)
.addSpanHandler(zipkinSpanHandler)
.build();
tracer = tracing.tracer();
tracing.setNoop(!tracingContext.isTracingEnabled());
traceContext = TraceContext.newBuilder()
.traceId(IdConversion.convertToLong(tracingContext.getTraceId()))
.spanId(IdConversion.convertToLong(tracingContext.getSpanId()))
.sampled(tracingContext.isSampled()).build();
}
public String handleRequest(String input, TracingContext tracingContext) {
try {
intializeZipkin(tracingContext);
// Start a new trace or a span within an existing trace representing an operation
Span span = tracer.newChild(traceContext).name("MainHandle").start();
System.out.println("Inside Java Hello World function");
try (Tracer.SpanInScope ws = tracer.withSpanInScope(span)) {
method1();
method2();
method3();
} catch (RuntimeException | Error e) {
span.error(e); // Unless you handle exceptions, you might not know the operation failed!
throw e;
} finally {
span.finish(); // note the scope is independent of the span. Always finish a span.
tracing.close();
zipkinSpanHandler.flush();
}
} catch (Exception e) {
return e.getMessage();
}
return "Hello, AppName " + tracingContext.getAppName() + " :: fnName " + tracingContext.getFunctionName();
}
public void method1() {
System.out.println("Inside Method1 function");
TraceContext traceContext = tracing.currentTraceContext().get();
Span span = tracer.newChild(traceContext).name("Method1").start();
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
span.finish();
}
}
public void method2() {
System.out.println("Inside Method2 function");
TraceContext traceContext = tracing.currentTraceContext().get();
Span span = tracer.newChild(traceContext).name("Method2").start();
try {
Thread.sleep(400);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
span.finish();
}
}
public void method3() {
System.out.println("Inside Method3 function");
TraceContext traceContext = tracing.currentTraceContext().get();
Span span = tracer.newChild(traceContext).name("Method3").start();
try {
Thread.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
span.finish();
}
}
}
A continuación, se muestra un ejemplo de cómo utilizar Zipkin para agregar períodos personalizados a la función de Python. Si desea probar este ejemplo, puede crear una función "Hello World!" de Python y agregar código de período personalizado. Para crear una función de ejemplo:
- Crear una función de Python:
fn init --runtime python apm-fn-python
Configurar paquetes
Actualice el archivo requirements.txt
para incluir los siguientes paquetes:
fdk
requests
py_zipkin
Guardar el archivo.
Creación de Clase de Manejador y Intervalo Personalizado Principal
La función Python llama a la función handler
y transfiere en el contexto de la función para crear períodos personalizados.
def handler(ctx, data: io.BytesIO = None):
tracing_context = ctx.TracingContext()
with zipkin_span(
service_name=tracing_context.service_name(),
span_name="Customer Code",
transport_handler=(
lambda encoded_span: transport_handler(
encoded_span, tracing_context
)
),
zipkin_attrs=tracing_context.zipkin_attrs(),
encoding=Encoding.V2_JSON,
binary_annotations=tracing_context.annotations()
):
name = "World"
try:
body = json.loads(data.getvalue())
name = body.get("name")
except (Exception, ValueError) as ex:
logging.getLogger().info('error parsing json payload: ' + str(ex))
logging.getLogger().info("Inside Python Hello World function")
time.sleep(0.005)
example(ctx)
return response.Response(
ctx, response_data=json.dumps(
{"message": "Hello {0}".format(name)}),
headers={"Content-Type": "application/json"}
)
tracing_context
se transfiere desde el contexto de la función y contiene toda la información necesaria para crear y configurar períodos personalizados.Nota
Si el rastreo no está activado, el contexto de rastreo es un objeto vacío. Con un contexto de rastreo vacío, el indicadoris_sampled
se define enNone
ypy_zipkin
no emite períodos.- La sentencia
with zipkin_span
se utiliza para crear períodos.- La información de
tracing_context
se utiliza para obtenerservice_name
, llamar atransport_handler
y definirzipking_attrs
. - Se especifica un nombre de período personalizado simplemente definiendo
span_name
. - Los atributos de rastreo necesarios para Zipkin se recuperan del contexto de rastreo:
tracing_context.zipkin_attrs()
.
- La información de
- Con la configuración personalizada del intervalo, el bloque principal ejecuta el código fijo "Hello World!" Con la única excepción, una llamada a la función
example
.
La función transport_handler
La función transport_handler
se comunica con el dominio de APM con mensajes sobre la ejecución del período.
# transport handler, needed by py_zipkin
def transport_handler(encoded_span, tracing_context):
return requests.post(
tracing_context.trace_collector_url(),
data=encoded_span,
headers={"Content-Type": "application/json"},
)
trace_collector_url
se devuelve desde el contexto de la función. Esta URL proporciona el punto final de comunicación para los períodos personalizados al dominio de APM.
Creación de un intervalo personalizado en una función de ejemplo
La función de ejemplo muestra la creación de un período personalizado.
def example(ctx):
with zipkin_span(
service_name=ctx.TracingContext().service_name(),
span_name="Get ADB Password from OCI Vault",
binary_annotations=ctx.TracingContext().annotations()
) as example_span_context:
try:
logging.getLogger().debug("Get ADB Password from OCI Vault")
time.sleep(0.005)
# throwing an exception to show how to add error messages to spans
raise Exception('Request failed')
except (Exception, ValueError) as error:
example_span_context.update_binary_annotations(
{"Error": True, "errorMessage": str(error)}
)
else:
FakeResponse = namedtuple("FakeResponse", "status, message")
fakeResponse = FakeResponse(200, "OK")
# how to update the span dimensions/annotations
example_span_context.update_binary_annotations(
{
"responseCode": fakeResponse.status,
"responseMessage": fakeResponse.message
}
)
- La sentencia
with zipkin_span
se utiliza para identificar el período personalizado y asignarle un nombre. - El bloque
example_span_context
emite una excepción y devuelve un mensaje de error.
Este es el código fuente completo para la función de rastreo de Python de ejemplo.
import io
import json
import logging
from fdk import response
import requests
import time
from py_zipkin import Encoding
from py_zipkin.zipkin import zipkin_span
from collections import namedtuple
# transport handler, needed by py_zipkin
def transport_handler(encoded_span, tracing_context):
return requests.post(
tracing_context.trace_collector_url(),
data=encoded_span,
headers={"Content-Type": "application/json"},
)
def handler(ctx, data: io.BytesIO = None):
tracing_context = ctx.TracingContext()
with zipkin_span(
service_name=tracing_context.service_name(),
span_name="Customer Code",
transport_handler=(
lambda encoded_span: transport_handler(
encoded_span, tracing_context
)
),
zipkin_attrs=tracing_context.zipkin_attrs(),
encoding=Encoding.V2_JSON,
binary_annotations=tracing_context.annotations()
):
name = "World"
try:
body = json.loads(data.getvalue())
name = body.get("name")
except (Exception, ValueError) as ex:
logging.getLogger().info('error parsing json payload: ' + str(ex))
logging.getLogger().info("Inside Python Hello World function")
time.sleep(0.005)
example(ctx)
return response.Response(
ctx, response_data=json.dumps(
{"message": "Hello {0}".format(name)}),
headers={"Content-Type": "application/json"}
)
def example(ctx):
with zipkin_span(
service_name=ctx.TracingContext().service_name(),
span_name="Get ADB Password from OCI Vault",
binary_annotations=ctx.TracingContext().annotations()
) as example_span_context:
try:
logging.getLogger().debug("Get ADB Password from OCI Vault")
time.sleep(0.005)
# throwing an exception to show how to add error messages to spans
raise Exception('Request failed')
except (Exception, ValueError) as error:
example_span_context.update_binary_annotations(
{"Error": True, "errorMessage": str(error)}
)
else:
FakeResponse = namedtuple("FakeResponse", "status, message")
fakeResponse = FakeResponse(200, "OK")
# how to update the span dimensions/annotations
example_span_context.update_binary_annotations(
{
"responseCode": fakeResponse.status,
"responseMessage": fakeResponse.message
}
)
A continuación, se muestra un ejemplo de cómo utilizar Zipkin para agregar períodos personalizados a la función Node.js. Si desea probar este ejemplo, puede crear una función "Hello World!" del nodo y agregar código de intervalo personalizado. Para crear una función de ejemplo:
- Crear una función de nodo:
fn init --runtime node apm-fn-node
Configurar dependencias de nodos
Actualice el archivo package.json
para incluir los siguientes paquetes:
{
"name": "apm-tracing-node-fdk-simple-trace-final",
"version": "1.0.0",
"description": "Example APM tracing function",
"main": "func.js",
"author": "",
"license": "Apache-2.0",
"dependencies": {
"@fnproject/fdk": ">=0.0.13",
"node-fetch": "^2.6.1",
"zipkin": "^0.22.0",
"zipkin-transport-http": "^0.22.0"
}
}
Guardar el archivo.
Actualizar método de controlador
Las observaciones clave sobre el método fdk.handle
siguen el código fuente.
// ZipkinJS core components.
const {
ExplicitContext,
Annotation,
Tracer,
TraceId,
BatchRecorder,
jsonEncoder,
sampler,
option
} = require('zipkin');
// An HTTP transport for dispatching Zipkin traces.
const {HttpLogger} = require('zipkin-transport-http');
fdk.handle(async function(input, ctx){
tracer = createOCITracer(ctx);
var result;
// Start a new 'scoped' server handling span.
await tracer.scoped(async function () {
// Fetch some resource
result = await tracer.local('fetchResource', () => {
return fetchResource();
});
// Perform some processing
result = await tracer.local('processResource', () => {
return someComputation(result);
});
// Update some resource
result = await tracer.local('updateResource', () => {
return updateResource(result);
});
await flush();
});
return result;
})
tracer
se crea y, a continuación, se utiliza para crear un período personalizado principal. A continuación, se crean intervalos secundarios para las funcionesfetchResource
,processResource
yupdateResource
.
Revisión de la función createOCITracer
Las observaciones clave sobre la función siguen el código fuente.
/**
* Creates a basic Zipkin Tracer using values from context of the function
* invocation.
*
* @param {*} ctx The function invocation context.
* @returns A configured Tracer for automatically tracing calls.
*/
function createOCITracer (ctx) {
// An OCI APM configured Tracer
//
const tracingCxt = ctx.tracingContext
const tracer = new Tracer({
ctxImpl: new ExplicitContext(),
recorder: new BatchRecorder({
logger: new HttpLogger({
// The configured OCI APM endpoint is available in the function
// invocation context.
endpoint: tracingCxt.traceCollectorUrl,
jsonEncoder: jsonEncoder.JSON_V2
})
}),
// APM Dimensions that should be included in all traces can be configured
// directly on Tracer.
defaultTags: createOCITags(ctx),
// A custom sampling strategy can be defined.
sampler: createOCISampler(ctx),
localServiceName: tracingCxt.serviceName,
supportsJoin: true,
traceId128Bit: true
})
// The initial function invocation trace identifiers can be added directly.
// If this is not defined a default TraceId is created.
const traceId = createOCITraceId(tracer, ctx)
tracer.setId(traceId)
return tracer
}
- El contexto de función (
ctx
) se transfiere a esta función, que proporciona la información necesaria para conectarse al dominio de APM. Si sigue las llamadas de función, puede ver cómo se crean los ID de rastreo y los campos.
Este es el código fuente completo para la función de rastreo de nodos de ejemplo.
const fdk = require('@fnproject/fdk')
// ZipkinJS core components.
const {
ExplicitContext,
Tracer,
TraceId,
BatchRecorder,
jsonEncoder,
sampler,
option
} = require('zipkin')
// An HTTP transport for dispatching Zipkin traces.
const { HttpLogger } = require('zipkin-transport-http')
fdk.handle(async function (input, ctx) {
var tracer = createOCITracer(ctx)
var result
// Start a new 'scoped' server handling span.
await tracer.scoped(async function () {
// Fetch some resource
result = await tracer.local('fetchResource', () => {
return fetchResource()
})
// Perform some processing
result = await tracer.local('processResource', () => {
return someComputation(result)
})
// Update some resource
result = await tracer.local('updateResource', () => {
return updateResource(result)
})
await flush()
})
return result
})
// ----------------------------------------------------------------------------
// App Simulation Functions
//
/**
* Simulate fetching some required resource. This could be another OCI service
* or an external call.
*
* @returns A Promise with the success or failure of the operation.
*/
function fetchResource () {
return simulate(1000, { fetchResource: 'OK' })
}
/**
* Simulate some work. This could be another OCI service.
*
* @returns A Promise with the success or failure of the operation.
*/
async function someComputation (toReturn) {
var i
for (i = 0; i < 5; i++) {
await simulate(1000)
}
toReturn.processResource = 'OK'
return toReturn
}
/**
* Simulate updating some resource. This could be another OCI service or an
* external call.
*
* @returns A Promise with the success or failure of the operation.
*/
async function updateResource (toReturn) {
await simulate(500)
toReturn.updateResource = 'OK'
return toReturn
}
/**
* A helper function to simulate an operation that takes a specified amount of time.
*
* @param {*} ms The simulated time for the activity in milliseconds.
* @returns A promise that resolves when the simulated activity finishes.
*/
function simulate (ms, result) {
return new Promise(resolve => setTimeout(resolve, ms, result))
}
/**
* Functions service may freeze or terminate the container on completion.
* This function gives extra time to allow the runtime to flush any pending traces.
* See: https://github.com/openzipkin/zipkin-js/issues/507
*
* @returns A Promise to await on.
*/
function flush () {
return new Promise(resolve => setTimeout(resolve, 1000))
}
// ----------------------------------------------------------------------------
// OpenZipkin ZipkinJS Utility Functions
//
/**
* Creates a basic Zipkin Tracer using values from context of the function
* invocation.
*
* @param {*} ctx The function invocation context.
* @returns A configured Tracer for automatically tracing calls.
*/
function createOCITracer (ctx) {
// An OCI APM configured Tracer
//
const tracingCxt = ctx.tracingContext
const tracer = new Tracer({
ctxImpl: new ExplicitContext(),
recorder: new BatchRecorder({
logger: new HttpLogger({
// The configured OCI APM endpoint is available in the function
// invocation context.
endpoint: tracingCxt.traceCollectorUrl,
jsonEncoder: jsonEncoder.JSON_V2
})
}),
// APM Dimensions that should be included in all traces can be configured
// directly on Tracer.
defaultTags: createOCITags(ctx),
// A custom sampling strategy can be defined.
sampler: createOCISampler(ctx),
localServiceName: tracingCxt.serviceName,
supportsJoin: true,
traceId128Bit: true
})
// The initial function invocation trace identifiers can be added directly.
// If this is not defined a default TraceId is created.
const traceId = createOCITraceId(tracer, ctx)
tracer.setId(traceId)
return tracer
}
/**
* A ZipkinJS 'TraceId' can be created directly from the function invocation
* context.
*
* @param {*} ctx The function invocation context.
* @returns A ZipkinJS 'TraceId' created from the invocation context.
*/
function createOCITraceId (tracer, ctx) {
const tracingCxt = ctx.tracingContext
if (tracingCxt.traceId && tracingCxt.spanId) {
return new TraceId({
traceId: tracingCxt.traceId,
spanId: tracingCxt.spanId,
sampled: new option.Some(tracingCxt.sampled),
debug: new option.Some(tracingCxt.debug),
shared: false
})
} else {
return tracer.createRootId(
new option.Some(tracingCxt.sampled),
new option.Some(tracingCxt.debug)
)
}
}
/**
* A ZipkinJS 'TraceId' can be crated directly from the function invocation
* context.
*
* This configurations will automatically add the function meta-data as APM
* dimensions to each trace. Function environment variable and other dimensions
* could also be added.
*
* @param {*} ctx The function invocation context.
* @returns A map of key-value pairs, that will be added as APM
* dimensions to the traces.
*/
function createOCITags (ctx) {
return {
appID: ctx.appID,
appName: ctx.appName,
fnID: ctx.fnID,
fnName: ctx.fnName
}
}
/**
* A ZipkinJS 'Sampler' can be created directly from the function invocation
* context.
*
* This configuration will only create a trace if the function is configured
* for tracing.
*
* @param {*} ctx The function invocation context.
* @returns A ZipkinJS 'TraceId' created from the invocation context.
*/
function createOCISampler (ctx) {
return new sampler.Sampler((traceId) => ctx.tracingContext.isEnabled)
}
Uso de la API
Para obtener más información sobre el uso de la API y la firma de solicitudes, consulte la documentación de la API de REST y Credenciales de seguridad. Para obtener información sobre los SDK, consulte Los SDK y la CLI.
Utilice estas operaciones de API para activar y desactivar el rastreo de aplicaciones y funciones que contienen: