Formação
Percurso de aprendizagem
Use advance techniques in canvas apps to perform custom updates and optimization - Training
Use advance techniques in canvas apps to perform custom updates and optimization
Este browser já não é suportado.
Atualize para o Microsoft Edge para tirar partido das mais recentes funcionalidades, atualizações de segurança e de suporte técnico.
Este artigo aplica-se a: ✔️ .NET Core 2.1 e versões posteriores .NET Framework 4.5 e versões ✔️ posteriores
Os aplicativos .NET podem ser instrumentados usando a API para produzir telemetria System.Diagnostics.Activity de rastreamento distribuído. Alguma instrumentação é incorporada em bibliotecas .NET padrão, mas você pode querer adicionar mais para tornar seu código mais facilmente diagnosticável. Neste tutorial, você adicionará uma nova instrumentação de rastreamento distribuído personalizada. Consulte o tutorial da coleção para saber mais sobre como gravar a telemetria produzida por esta instrumentação.
Primeiro, você criará um aplicativo de exemplo que coleta telemetria usando OpenTelemetry, mas ainda não tem nenhuma instrumentação.
dotnet new console
Os aplicativos destinados ao .NET 5 e posteriores já têm as APIs de rastreamento distribuído necessárias incluídas. Para aplicativos destinados a versões mais antigas do .NET, adicione o pacote NuGet System.Diagnostics.DiagnosticSource versão 5 ou superior.
dotnet add package System.Diagnostics.DiagnosticSource
Adicione os pacotes OpenTelemetry e OpenTelemetry.Exporter.Console NuGet, que serão usados para coletar a telemetria.
dotnet add package OpenTelemetry
dotnet add package OpenTelemetry.Exporter.Console
Substitua o conteúdo do Programa gerado.cs por esta fonte de exemplo:
using OpenTelemetry;
using OpenTelemetry.Resources;
using OpenTelemetry.Trace;
using System;
using System.Threading.Tasks;
namespace Sample.DistributedTracing
{
class Program
{
static async Task Main(string[] args)
{
using var tracerProvider = Sdk.CreateTracerProviderBuilder()
.SetResourceBuilder(ResourceBuilder.CreateDefault().AddService("MySample"))
.AddSource("Sample.DistributedTracing")
.AddConsoleExporter()
.Build();
await DoSomeWork("banana", 8);
Console.WriteLine("Example work done");
}
// All the functions below simulate doing some arbitrary work
static async Task DoSomeWork(string foo, int bar)
{
await StepOne();
await StepTwo();
}
static async Task StepOne()
{
await Task.Delay(500);
}
static async Task StepTwo()
{
await Task.Delay(1000);
}
}
}
O aplicativo ainda não tem instrumentação, portanto, não há informações de rastreamento para exibir:
> dotnet run
Example work done
Somente os desenvolvedores de aplicativos precisam fazer referência a uma biblioteca de terceiros opcional para coletar a telemetria de rastreamento distribuída, como OpenTelemetry neste exemplo. Os autores da biblioteca .NET podem confiar exclusivamente em APIs em System.Diagnostics.DiagnosticSource, que faz parte do tempo de execução do .NET. Isso garante que as bibliotecas serão executadas em uma ampla variedade de aplicativos .NET, independentemente das preferências do desenvolvedor do aplicativo sobre qual biblioteca ou fornecedor usar para coletar telemetria.
Aplicativos e bibliotecas adicionam instrumentação de rastreamento distribuído usando as System.Diagnostics.ActivitySource classes and System.Diagnostics.Activity .
Primeiro, crie uma instância de ActivitySource. ActivitySource fornece APIs para criar e iniciar objetos Activity. Adicione a variável estática ActivitySource acima de Main() e using System.Diagnostics;
às instruções using.
using OpenTelemetry;
using OpenTelemetry.Resources;
using OpenTelemetry.Trace;
using System;
using System.Diagnostics;
using System.Threading.Tasks;
namespace Sample.DistributedTracing
{
class Program
{
private static ActivitySource source = new ActivitySource("Sample.DistributedTracing", "1.0.0");
static async Task Main(string[] args)
{
...
Crie o ActivitySource uma vez, armazene-o em uma variável estática e use essa instância o tempo necessário. Cada biblioteca ou subcomponente de biblioteca pode (e muitas vezes deve) criar sua própria fonte. Considere criar uma nova fonte em vez de reutilizar uma existente se você antecipar que os desenvolvedores de aplicativos gostariam de poder habilitar e desabilitar a telemetria de atividade nas fontes de forma independente.
O nome de origem passado para o construtor tem que ser exclusivo para evitar os conflitos com quaisquer outras fontes.
Se houver várias fontes dentro do mesmo assembly, use um nome hierárquico que contenha o nome do assembly e, opcionalmente, um nome de componente, por exemplo, Microsoft.AspNetCore.Hosting
. Se um assembly estiver adicionando instrumentação para código em um segundo assembly independente, o nome deve ser baseado no assembly que define o ActivitySource, não no assembly cujo código está sendo instrumentado.
O parâmetro version é opcional. Recomendamos que você forneça a versão caso libere várias versões da biblioteca e faça alterações na telemetria instrumentada.
Nota
OpenTelemetry usa termos alternativos 'Tracer' e 'Span'. No .NET 'ActivitySource' é a implementação do Tracer e Activity é a implementação do 'Span'. . O tipo de atividade da NET é muito anterior à especificação OpenTelemetry e a nomenclatura original do .NET foi preservada para consistência dentro do ecossistema .NET e compatibilidade de aplicativos .NET.
Use o objeto ActivitySource para iniciar e parar objetos Activity em torno de unidades significativas de trabalho. Atualize DoSomeWork() com o código mostrado aqui:
static async Task DoSomeWork(string foo, int bar)
{
using (Activity activity = source.StartActivity("SomeWork"))
{
await StepOne();
await StepTwo();
}
}
A execução do aplicativo agora mostra a nova atividade sendo registrada:
> dotnet run
Activity.Id: 00-f443e487a4998c41a6fd6fe88bae644e-5b7253de08ed474f-01
Activity.DisplayName: SomeWork
Activity.Kind: Internal
Activity.StartTime: 2021-03-18T10:36:51.4720202Z
Activity.Duration: 00:00:01.5025842
Resource associated with Activity:
service.name: MySample
service.instance.id: 067f4bb5-a5a8-4898-a288-dec569d6dbef
ActivitySource.StartActivity cria e inicia a atividade ao mesmo tempo. O padrão de código listado está usando o bloco, que descarta automaticamente o using
objeto Activity criado após a execução do bloco. Eliminar o objeto Activity irá pará-lo para que o código não precise chamar Activity.Stop()explicitamente .
Isso simplifica o padrão de codificação.
ActivitySource.StartActivity determina internamente se há ouvintes gravando a Atividade. Se não houver ouvintes registrados ou se houver ouvintes que não estejam interessados, StartActivity()
retornarão null
e evitarão criar o objeto Activity. Esta é uma otimização de desempenho para que o padrão de código ainda possa ser usado em funções que são chamadas com freqüência.
As atividades suportam dados de chave-valor chamados Tags, comumente usados para armazenar quaisquer parâmetros do trabalho que possam ser úteis para diagnósticos. Atualize DoSomeWork() para incluí-los:
static async Task DoSomeWork(string foo, int bar)
{
using (Activity activity = source.StartActivity("SomeWork"))
{
activity?.SetTag("foo", foo);
activity?.SetTag("bar", bar);
await StepOne();
await StepTwo();
}
}
> dotnet run
Activity.Id: 00-2b56072db8cb5a4496a4bfb69f46aa06-7bc4acda3b9cce4d-01
Activity.DisplayName: SomeWork
Activity.Kind: Internal
Activity.StartTime: 2021-03-18T10:37:31.4949570Z
Activity.Duration: 00:00:01.5417719
Activity.TagObjects:
foo: banana
bar: 8
Resource associated with Activity:
service.name: MySample
service.instance.id: 25bbc1c3-2de5-48d9-9333-062377fea49c
Example work done
activity
devolvido por ActivitySource.StartActivity pode ser nulo. O operador ?.
null-coalescing em C# é uma abreviação conveniente para invocar Activity.SetTag apenas se activity
não for null. O comportamento é idêntico à escrita:if(activity != null)
{
activity.SetTag("foo", foo);
}
OpenTelemetry fornece um conjunto de convenções recomendadas para definir Tags em Atividades que representam tipos comuns de trabalho de aplicativo.
Se você estiver instrumentando funções com requisitos de alto desempenho, é uma dica que indica se algum dos códigos ouvindo Atividades pretende ler informações auxiliares, Activity.IsAllDataRequested como Tags. Se nenhum ouvinte o ler, não há necessidade de o código instrumentado passar ciclos de CPU preenchendo-o. Para simplificar, este exemplo não aplica essa otimização.
Os eventos são mensagens com carimbo de data/hora que podem anexar um fluxo arbitrário de dados de diagnóstico adicionais às Atividades. Adicione alguns eventos à Atividade:
static async Task DoSomeWork(string foo, int bar)
{
using (Activity activity = source.StartActivity("SomeWork"))
{
activity?.SetTag("foo", foo);
activity?.SetTag("bar", bar);
await StepOne();
activity?.AddEvent(new ActivityEvent("Part way there"));
await StepTwo();
activity?.AddEvent(new ActivityEvent("Done now"));
}
}
> dotnet run
Activity.Id: 00-82cf6ea92661b84d9fd881731741d04e-33fff2835a03c041-01
Activity.DisplayName: SomeWork
Activity.Kind: Internal
Activity.StartTime: 2021-03-18T10:39:10.6902609Z
Activity.Duration: 00:00:01.5147582
Activity.TagObjects:
foo: banana
bar: 8
Activity.Events:
Part way there [3/18/2021 10:39:11 AM +00:00]
Done now [3/18/2021 10:39:12 AM +00:00]
Resource associated with Activity:
service.name: MySample
service.instance.id: ea7f0fcb-3673-48e0-b6ce-e4af5a86ce4f
Example work done
O OpenTelemetry permite que cada atividade relate um Status que representa o resultado de aprovação/reprovação do trabalho. Atualmente, o .NET não tem uma API fortemente tipada para essa finalidade, mas há uma convenção estabelecida usando Tags:
otel.status_code
é o nome da tag usado para armazenar StatusCode
. Os valores para a tag StatusCode devem ser uma das cadeias de caracteres "UNSET", "OK" ou "ERROR", que correspondem respectivamente aos enums Unset
, Ok
e Error
de StatusCode.otel.status_description
é o nome da tag usado para armazenar o opcional Description
Atualize DoSomeWork() para definir o status:
static async Task DoSomeWork(string foo, int bar)
{
using (Activity activity = source.StartActivity("SomeWork"))
{
activity?.SetTag("foo", foo);
activity?.SetTag("bar", bar);
await StepOne();
activity?.AddEvent(new ActivityEvent("Part way there"));
await StepTwo();
activity?.AddEvent(new ActivityEvent("Done now"));
// Pretend something went wrong
activity?.SetTag("otel.status_code", "ERROR");
activity?.SetTag("otel.status_description", "Use this text give more information about the error");
}
}
As atividades podem ser aninhadas para descrever partes de uma unidade maior de trabalho. Isso pode ser valioso em torno de partes de código que podem não ser executadas rapidamente ou para localizar melhor falhas provenientes de dependências externas específicas. Embora este exemplo use uma atividade em cada método, isso ocorre apenas porque o código extra foi minimizado. Em um projeto maior e mais realista, usar uma atividade em todos os métodos produziria traços extremamente detalhados, por isso não é recomendado.
Atualize o StepOne e o StepTwo para adicionar mais rastreamento em torno destas etapas separadas:
static async Task StepOne()
{
using (Activity activity = source.StartActivity("StepOne"))
{
await Task.Delay(500);
}
}
static async Task StepTwo()
{
using (Activity activity = source.StartActivity("StepTwo"))
{
await Task.Delay(1000);
}
}
> dotnet run
Activity.Id: 00-9d5aa439e0df7e49b4abff8d2d5329a9-39cac574e8fda44b-01
Activity.ParentId: 00-9d5aa439e0df7e49b4abff8d2d5329a9-f16529d0b7c49e44-01
Activity.DisplayName: StepOne
Activity.Kind: Internal
Activity.StartTime: 2021-03-18T10:40:51.4278822Z
Activity.Duration: 00:00:00.5051364
Resource associated with Activity:
service.name: MySample
service.instance.id: e0a8c12c-249d-4bdd-8180-8931b9b6e8d0
Activity.Id: 00-9d5aa439e0df7e49b4abff8d2d5329a9-4ccccb6efdc59546-01
Activity.ParentId: 00-9d5aa439e0df7e49b4abff8d2d5329a9-f16529d0b7c49e44-01
Activity.DisplayName: StepTwo
Activity.Kind: Internal
Activity.StartTime: 2021-03-18T10:40:51.9441095Z
Activity.Duration: 00:00:01.0052729
Resource associated with Activity:
service.name: MySample
service.instance.id: e0a8c12c-249d-4bdd-8180-8931b9b6e8d0
Activity.Id: 00-9d5aa439e0df7e49b4abff8d2d5329a9-f16529d0b7c49e44-01
Activity.DisplayName: SomeWork
Activity.Kind: Internal
Activity.StartTime: 2021-03-18T10:40:51.4256627Z
Activity.Duration: 00:00:01.5286408
Activity.TagObjects:
foo: banana
bar: 8
otel.status_code: ERROR
otel.status_description: Use this text give more information about the error
Activity.Events:
Part way there [3/18/2021 10:40:51 AM +00:00]
Done now [3/18/2021 10:40:52 AM +00:00]
Resource associated with Activity:
service.name: MySample
service.instance.id: e0a8c12c-249d-4bdd-8180-8931b9b6e8d0
Example work done
Observe que StepOne e StepTwo incluem um ParentId que se refere a SomeWork. O console não é uma ótima visualização de árvores aninhadas de trabalho, mas muitos visualizadores de GUI, como o Zipkin, podem mostrar isso como um gráfico de Gantt:
As Atividades têm uma Activity.Kind propriedade, que descreve a relação entre a Atividade, seu pai e seus filhos. Por padrão, todas as novas Atividades são definidas como Internal, o que é apropriado para Atividades que são uma operação interna dentro de um aplicativo sem pai ou filhos remotos. Outros tipos podem ser definidos usando o parâmetro kind em ActivitySource.StartActivity. Para outras opções, consulte System.Diagnostics.ActivityKind.
Quando o trabalho ocorre em sistemas de processamento em lote, uma única atividade pode representar o trabalho em nome de muitas solicitações diferentes simultaneamente, cada uma das quais tem seu próprio trace-id. Embora a atividade seja restrita a ter um único pai, ela pode ser vinculada a rastreamentos adicionais usando System.Diagnostics.ActivityLinko . Cada ActivityLink é preenchido com um que armazena informações de ActivityContext ID sobre a Atividade à qual está sendo vinculada. ActivityContext pode ser recuperado de objetos Activity em processo usando ou pode ser analisado a partir de informações de ID serializadas usando Activity.ContextActivityContext.Parse(String, String).
void DoBatchWork(ActivityContext[] requestContexts)
{
// Assume each context in requestContexts encodes the trace-id that was sent with a request
using(Activity activity = s_source.StartActivity(name: "BigBatchOfWork",
kind: ActivityKind.Internal,
parentContext: default,
links: requestContexts.Select(ctx => new ActivityLink(ctx))
{
// do the batch of work here
}
}
Ao contrário de eventos e tags que podem ser adicionados sob demanda, os links devem ser adicionados durante StartActivity() e são imutáveis posteriormente.
Comentários do .NET
O .NET é um projeto código aberto. Selecione um link para fornecer comentários:
Formação
Percurso de aprendizagem
Use advance techniques in canvas apps to perform custom updates and optimization - Training
Use advance techniques in canvas apps to perform custom updates and optimization
Documentação
Recolher um rastreio distribuído - .NET
Tutoriais para recolher rastreios distribuídos em aplicações .NET com OpenTelemetry, Application Insights ou ActivityListener
Rastreamento distribuído - .NET
Uma introdução ao rastreamento distribuído do .NET.
Conceitos de rastreamento distribuído - .NET
Conceitos de rastreamento distribuído .NET
Atividades internas no .NET - .NET
Visão geral das atividades emitidas pelas bibliotecas .NET