Rastreamento Distribuído para o Serviço Functions
Descubra como ativar o rastreamento e exibir rastreamentos de funções ao depurar com o OCI Functions.
Quando uma função é chamada, mas não é executada ou executada conforme esperado, você precisa investigar o problema em um nível detalhado. O recurso de rastreamento distribuído observa a execução da função à medida que ela se move pelos diferentes componentes do sistema. Você pode rastrear e instrumentar funções independentes para depurar problemas de execução e desempenho. Você também pode usar o rastreamento de funções para depurar problemas com aplicativos sem servidor completos que compreendem várias funções e serviços, como:
- uma função chamando outra função
- uma função que chama outros serviços, como o serviço Object Storage
- uma função que serve como backend para um gateway de API implantado no serviço API Gateway
- uma função acionada em resposta a um evento pelo serviço Events, serviço Notifications ou Connector Hub
Os recursos de rastreamento do OCI Functions são fornecidos pelo serviço Oracle Cloud Infrastructure Application Performance Monitoring. Os recursos do Application Performance Monitoring (APM) permitem identificar e solucionar problemas de falhas e latência nas funções que você cria e implanta.
No serviço Application Performance Monitoring:
- Um domínio APM contém os sistemas monitorados pelo serviço Application Performance Monitoring. Um domínio do APM é uma instância de um coletor de dados de rastreamento e intervalo que armazena, agrega, exibe e visualiza os dados.
- Um rastreamento é o fluxo completo de uma solicitação à medida que ela passa por todos os componentes de um sistema distribuído em um determinado período. Ele consiste em uma árvore inteira de intervalos, todos relacionados ao mesmo fluxo de solicitação geral único.
- Um intervalo é uma operação ou uma unidade lógica de trabalho com um nome, hora inicial e duração, dentro de um rastreamento. Um intervalo é um segmento de tempo associado à duração de uma unidade de trabalho dentro do fluxo de solicitação geral.
O Trace Explorer do serviço Application Performance Monitoring permite visualizar todo o fluxo de solicitações e explorar detalhes de rastreamento e intervalo para diagnósticos. Você pode exibir e monitorar rastreamentos lentos e rastreamentos com erros. Para isolar e identificar problemas de rastreamento, você pode fazer drill-down em intervalos específicos, como carregamentos de página, chamadas AJAX e solicitações de serviço. Para obter mais informações sobre o serviço Application Performance Monitoring, consulte Application Performance Monitoring.
Para ativar o rastreamento de uma função, você deve:
- Configure uma política para conceder ao serviço OCI Functions permissão para acessar domínios do APM, se a política ainda não existir (consulte Instruções de Política para Conceder ao Serviço OCI Functions e aos Usuários do OCI Functions Acesso aos Recursos de Rastreamento).
- Configure um domínio do APM.
- Ative o rastreamento para o aplicativo Functions e selecione o domínio do APM que você criou.
- Ative o rastreamento para uma ou mais funções.
Quando você ativa o rastreamento de uma função, o OCI Functions gera automaticamente um "intervalo de chamada de função padrão". O intervalo padrão captura informações sobre o contexto de execução da função, incluindo o tempo geral necessário para processar a solicitação e retornar uma resposta ao chamador. Além do intervalo de chamada de função padrão, você pode adicionar código a funções para definir intervalos personalizados. Use intervalos personalizados para capturar mais informações específicas da função para ajudar na depuração. Por exemplo, você pode definir intervalos personalizados para capturar o início e o fim de unidades de trabalho específicas. Por exemplo, as unidades de trabalho podem incluir obter a senha do banco de dados do Vault, abrir uma conexão de banco de dados e recuperar registros do banco de dados.
Quatro variáveis foram adicionadas ao contexto do OCI Functions que fornecem informações úteis de rastreamento. Essas variáveis incluem:
FN_APP_NAME:
O nome do aplicativo da função.FN_FN_NAME:
O nome da função.OCI_TRACE_COLLECTOR_URL
: O URL do domínio do APM com chave de dados.OCI_TRACING_ENABLED:
O rastreamento está ativado?- Quando recuperado das variáveis de ambiente, retorna 0 ou 1.
- Quando recuperado do contexto da função, retorna
true
oufalse
conforme apropriado para o idioma usado.
Política do Serviço IAM Obrigatória para Ativar o Rastreamento
Para poder ativar o rastreamento, o grupo ao qual você pertence deve ter permissão para acessar domínios do APM existentes ou criar domínios do APM. Além disso, o OCI Functions deve ter permissão para acessar domínios do APM. Consulte Instruções de Política para Conceder ao Serviço OCI Functions e aos Usuários do OCI Functions Acesso aos Recursos de Rastreamento.
Usando a Console para Ativar Rastreamento e Exibir Rastreamentos de Funções
São necessárias algumas etapas para ativar o rastreamento e exibir rastreamentos de funções para o serviço Oracle Cloud Infrastructure Application Performance Monitoring (APM). Primeiro, ative o rastreamento do aplicativo que contém a função. Em seguida, ative o rastreamento de uma ou mais funções. Em seguida, você pode exibir rastreamentos de função no Explorador de Rastreamento do APM.
Usando a Console para Ativar o Rastreamento
Para ativar o rastreamento, siga estas etapas.
- Na página da lista Aplicativos, selecione o aplicativo com funções para as quais você deseja ativar o rastreamento. Se precisar de ajuda para localizar a página de lista ou o aplicativo, consulte Listando Aplicativos.
- Selecione a guia Monitoramento e vá para a seção Rastreamentos.
- Para ativar o rastreamento do aplicativo:
- No
- Compartimento: O compartimento no qual criar o rastreamento. Por padrão, o compartimento atual.
- Domínio do APM: O domínio do APM (definido no serviço Application Performance Monitoring) no qual o rastreamento será criado. Para usar um Domínio do APM existente, selecione um domínio do APM existente na lista. Ou, para criar um novo domínio do APM, selecione Domínio do APM. Para obter mais informações sobre domínios do APM, consulte Conceitos Básicos do Serviço Application Performance Monitoring. Observação
O Domínio do APM precisa ter chaves de dados públicas e privadas para que o rastreamento de funções funcione. Se as chaves não existirem, crie-as por meio da interface do console.
, selecione Configurar e especifique: - Selecione Ativar Rastreamento para ativar o rastreamento do aplicativo.
Com o rastreamento ativado para o aplicativo Functions, agora você pode ativar o rastreamento para uma ou mais funções no aplicativo.
- No
- Para ativar o rastreamento de funções específicas no aplicativo:
- Selecione a guia Funções.
- Selecione a opção Ativar Rastreamento no menu Ações
das funções para as quais você deseja ativar o rastreamento.
A opção Ativar Rastreamento só será mostrada se você tiver ativado o rastreamento anteriormente para o aplicativo. Observe o seguinte:
- Se a opção Ativar Rastreamento não for mostrada, você deverá ativar o rastreamento para o aplicativo. Se você ainda não ativou o rastreamento para o aplicativo, consulte a etapa anterior.
- Se você tiver ativado anteriormente o rastreamento do aplicativo, mas desativado posteriormente, um link Ativar rastreamento do aplicativo será mostrado. Selecione o link Ativar rastreamento do aplicativo para reativar o rastreamento do aplicativo (consulte a etapa anterior). Depois de reativar o rastreamento do aplicativo, você poderá ativar o rastreamento de funções específicas.
Quando você tiver ativado o rastreamento para o aplicativo e uma ou mais funções, poderá exibir os rastreamentos de funções.
Usando a Console para Exibir Rastreamentos de Funções
Para exibir rastreamentos de funções que têm rastreamento ativado:
- Na página da lista Aplicativos, selecione o aplicativo que contém as funções para as quais você deseja exibir rastreamentos. Se precisar de ajuda para localizar a página de lista ou o aplicativo, consulte Listando Aplicativos.
- Para ver rastreamentos de funções:
- Para ver rastreamentos de todas as funções que têm rastreamento ativado no aplicativo:
- Selecione a guia Monitoramento e vá para a seção Rastreamentos.
- Selecione o nome do rastreamento. Observação
Um nome de rastreamento só será mostrado se você já tiver ativado o rastreamento para o aplicativo.
- Para ver o rastreamento de uma função específica que tem o rastreamento ativado:
- Selecione a guia Funções.
- Selecione a opção Exibir Rastreamento no menu Ações
da função para a qual você deseja exibir o rastreamento.
Observação
A opção Exibir Rastreamento só será mostrada se você já tiver ativado o rastreamento para a função.
Os rastreamentos das funções selecionadas são mostrados no Explorador de Rastreamento do APM. Por padrão, um rastreamento é mostrado para o intervalo de chamada de função padrão e qualquer intervalo personalizado definido para a função.
- Para ver rastreamentos de todas as funções que têm rastreamento ativado no aplicativo:
- No Trace Explorer do APM:
- Selecione um rastreamento para ver os intervalos desse rastreamento.
- Selecione um intervalo para ver os detalhes capturados para esse intervalo.
Para obter mais informações sobre como usar o Explorador de Rastreamento do APM, consulte Usar o Explorador de Rastreamento.
Rastreando uma Cadeia de Funções
Por padrão, o rastreamento de função fornece um rastreamento para uma chamada de função inteira. No entanto, geralmente com aplicativos de nuvem modernos, você precisa encadear chamadas de função. O rastreamento do OCI Functions fornece a capacidade de rastrear a execução de uma função chamada por outra função. Essa capacidade significa que você pode examinar a execução de cada função em uma cadeia de chamadas em uma única árvore de intervalos no explorador de rastreamento do APM.
Para rastrear uma cadeia de funções, você precisa propagar os cabeçalhos X-B3 X-B3-TraceId
, X-B3-SpanId
, X-B3-ParentSpanId
e X-B3-Sampled
na solicitação de chamada de função do seu código de função.
Após a execução da função, os dados de rastreamento de suas funções são coletados e disponíveis no Explorador de Rastreamento do APM. Para obter mais informações sobre como usar o Explorador de Rastreamento do APM, consulte Usar o Explorador de Rastreamento.
Aqui está um exemplo de como você pode rastrear uma cadeia de funções. Se quiser experimentar este exemplo, você precisará criar duas funções de amostra. Siga estas etapas para configurar suas funções.
- Crie sua função Python de rastreamento:
fn init --runtime python <your-function-name-1>
- Crie o seu "Hello World!" Função Python:
fn init --runtime python <your-function-name-2>
- Implante as duas funções:
fn -v deploy --app <app-name>
- Obtenha o OCID das segunda funções e chame o ponto final:
fn inspect function your-app-name your-function-name-2
- Crie um arquivo JSON para transmitir as informações necessárias para a primeira função. Por exemplo, seu arquivo
test.json
pode ter esta aparência:{ "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\" }" }
- Quando a primeira função é chamada, você pode especificar as informações da segunda função usando
test.json
:fn invoke <app-name> <your-function-name-1> < test.json
Agora você está pronto para atualizar a primeira função com as atualizações de código necessárias.
Configurar Pacotes
Atualize o arquivo requirements.txt
para incluir os seguintes pacotes:
fdk
oci
Salvar o arquivo.
Atualize seu Código de Função para Propagar os Cabeçalhos X-B3
A função Python chama a função handler
e transmite as informações JSON do comando de chamada. A função handler
é dividida em vários blocos pequenos para simplificar. O arquivo de origem completo é fornecido na parte inferior desta seção.
Carregar os Dados JSON
Nesta primeira parte, os dados JSON são carregados da chamada de função.
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
Criar Cliente de Chamada e Coletar Informações do Cabeçalho
Crie o cliente de chamada do Functions usando os controladores de recursos do OCI Python SDK e do Functions. Em seguida, recupere o tracing_context
e extraia as informações necessárias para criar os cabeçalhos 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 os Cabeçalhos X-B3
O OCI Python SDK permite definir cabeçalhos personalizados. Use esta técnica para passar os cabeçalhos X-B3 para a segunda chamada de função. As informações do cabeçalho são especificadas para trace_id
, span_id
, parent_span_id
e is_sampled
. Finalmente, a segunda função é chamada com client
e a resposta é passada para a resposta dessa função.
# 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"}
)
Aqui está o código-fonte completo para a função Python de amostra.
#
# 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"}
)
Adicionando Intervalos Personalizados a Funções
Com o rastreamento de função ativado, o intervalo de chamada de função padrão fornece um rastreamento para toda a chamada de função. O intervalo padrão pode fornecer boas informações, mas ao investigar seu código, talvez você queira se aprofundar. Intervalos personalizados são adicionados diretamente ao código e permitem que você defina intervalos para um método ou um bloco de código. Os dados resultantes fornecem uma imagem melhor de sua função à medida que ela é executada.
Para poder usar intervalos personalizados, ative o rastreamento do seu aplicativo e funções usando o serviço Oracle Cloud Infrastructure Application Performance Monitoring (APM). Para configurar o rastreamento, você deve:
- Configure uma política para conceder ao serviço OCI Functions permissão para acessar domínios do APM, se a política ainda não existir (consulte Instruções de Política para Conceder ao Serviço OCI Functions e aos Usuários do OCI Functions Acesso aos Recursos de Rastreamento).
- Configure um domínio do APM.
- Ative o rastreamento para o aplicativo Functions e selecione o domínio do APM que você criou.
- Ative o rastreamento para uma ou mais funções.
Essas etapas já foram abordadas. No entanto, mais algumas coisas são necessárias para intervalos personalizados:
- Selecione uma biblioteca de clientes de rastreamento distribuído, por exemplo, Zipkin.
- Adicione bibliotecas do cliente às suas dependências de função.
- Em seu código de função, use a variável de contexto da função
OCI_TRACING_ENABLED
para verificar se o rastreamento está ativado. - Em seu código de função, use a variável de contexto da função
OCI_TRACE_COLLECTOR_URL
para enviar seus intervalos personalizados para seu domínio do APM. - Adicione instrumentação ao seu código de função.
Para usar intervalos personalizados, você deve ter as seguintes versões mínimas das FDKs do Fn Project:
- FDK DO Java: 1.0.129
- Python FDK: 0.1.22
- Nó FDK: 0.0.20
Veja um exemplo de como usar o Zipkin para adicionar intervalos personalizados à sua função Java. Se quiser testar este exemplo, você poderá criar uma função Java "Hello World!" e adicionar um código de intervalo personalizado. Para criar uma função de amostra:
- Crie uma função Java:
fn init --runtime java apm-fn-java
- Para simplificar, remova o diretório
src/test
.
Configurar o Maven
Adicione as dependências a seguir à seção <dependencies> do arquivo pom.xml
do 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>
Salvar o arquivo.
O Método HandleRequest
As observações sobre o método seguem o código-fonte 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();
}
- O objeto
TracingContext tracingConext
transmite todas as informações relacionadas ao APM necessárias para fazer conexões com o serviço do APM. - O método
intializeZipkin
é chamado, o que atualiza otracingContext
e cria um objetotracer
que é usado para configurar intervalos personalizados. - Um
span
é criado para o intervalo personalizado pai. Em seguida, três métodos são chamados no escopo do intervalo pai. - Observe que no bloco
finally
todos os objetos de rastreamento são fechados.
O Método initializeZipkin
As observações sobre o método intializeZipkin
seguem o código-fonte.
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();
}
- O
traceContext
é informado para criar todos os objetos usados para criar intervalos personalizados. - O
apmURL
é recuperado do métodogetTraceCollectorURL()
. O URL é o ponto final do domínio do APM e é usado para criar o objetotracer
que cria os intervalos personalizados. - Um construtor usa
zipkinSpanHandler
e o nome do serviço para criar um objetotracer
. Este objetotracer
é usado para criar intervalos personalizados.
Criando Intervalos Personalizados
Com o objeto tracer
inicializado, intervalos personalizados podem ser criados.
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();
}
}
- O método
method1
cria um intervalo personalizado chamado "Method1".
Aqui está o código-fonte completo da função de rastreamento Java de amostra.
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();
}
}
}
Veja um exemplo de como usar o Zipkin para adicionar intervalos personalizados à sua função Python. Se você quiser experimentar este exemplo, você pode criar uma função Python "Hello World!" e adicionar código de intervalo personalizado. Para criar uma função de amostra:
- Crie uma função Python:
fn init --runtime python apm-fn-python
Configurar Pacotes
Atualize o arquivo requirements.txt
para incluir os seguintes pacotes:
fdk
requests
py_zipkin
Salvar o arquivo.
Criando Classe de Handler e Intervalo Personalizado Pai
A função Python chama a função handler
e passa no contexto da função para criar intervalos 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"}
)
- O
tracing_context
é informado do contexto da função e contém todas as informações necessárias para criar e configurar intervalos personalizados.Observação
Se o rastreamento não estiver ativado, o contexto de rastreamento será um objeto vazio. Com um contexto de rastreamento vazio, o flagis_sampled
é definido comoNone
epy_zipkin
não emite intervalos. - A instrução
with zipkin_span
é usada para criar intervalos.- As informações em
tracing_context
são usadas para obter oservice_name
, chamar otransport_handler
e definir ozipking_attrs
. - Um nome de intervalo personalizado é especificado apenas definindo
span_name
. - Os atributos de rastreamento necessários para o Zipkin são recuperados do contexto de rastreamento:
tracing_context.zipkin_attrs()
.
- As informações em
- Com a configuração de extensão personalizada, o bloco principal executa o código padronizado "Hello World!". Com a única exceção, uma chamada para a função
example
.
A Função transport_handler
A função transport_handler
se comunica com o domínio do APM com mensagens sobre a execução do intervalo.
# 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"},
)
- O
trace_collector_url
é retornado do contexto da função. Este URL fornece o ponto final de comunicação para seus intervalos personalizados para o domínio do APM.
Criando um Intervalo Personalizado na Função de Exemplo
A função de exemplo demonstra a criação de um intervalo 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
}
)
- A instrução
with zipkin_span
é usada para identificar o intervalo personalizado e dar um nome a ele. - O bloco
example_span_context
gera uma exceção e retorna uma mensagem de erro.
Aqui está o código-fonte completo para a função de rastreamento Python de amostra.
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
}
)
Veja um exemplo de como usar o Zipkin para adicionar intervalos personalizados à sua função Node.js. Se quiser testar este exemplo, você poderá criar uma função Node "Hello World!" e adicionar um código de intervalo personalizado. Para criar uma função de amostra:
- Criar uma função de Nó:
fn init --runtime node apm-fn-node
Configurar Dependências de Nó
Atualize o arquivo package.json
para incluir os seguintes pacotes:
{
"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"
}
}
Salvar o arquivo.
Atualizar Método de Tratamento
As principais observações sobre o método fdk.handle
seguem o código-fonte.
// 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;
})
- O
tracer
é criado e, em seguida, usado para criar um intervalo personalizado pai. Em seguida, os intervalos filhos são criados para as funçõesfetchResource
,processResource
eupdateResource
.
Verificando a Função createOCITracer
As principais observações sobre a função seguem o código-fonte.
/**
* 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
}
- O contexto da função (
ctx
) é passado para essa função, que fornece as informações necessárias para estabelecer conexão com o domínio do APM. Se você seguir as chamadas de função, poderá ver como os IDs de rastreamento e os campos são criados.
Aqui está o código-fonte completo para a função de rastreamento de nó de amostra.
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)
}
Usando a API
Para obter informações sobre como usar a API e assinar solicitações, consulte a documentação da API REST e Credenciais de Segurança. Para obter informações sobre SDKs, consulte SDKs e a CLI.
Use estas operações de API para ativar e desativar o rastreamento de aplicativos e as funções que eles contêm: