diff --git a/content/pt/docs/languages/js/instrumentation.md b/content/pt/docs/languages/js/instrumentation.md new file mode 100644 index 000000000000..b24bdb8d0277 --- /dev/null +++ b/content/pt/docs/languages/js/instrumentation.md @@ -0,0 +1,1861 @@ +--- +title: Instrumentação +aliases: + - /docs/languages/js/api/tracing + - manual +weight: 30 +default_lang_commit: 9cdf79c9345a05fb82d0e67132c9f68dabddadba +description: Instrumentação para OpenTelemetry JavaScript +cSpell:ignore: dicelib instrumentacao Millis rolldice versao +--- + +{{% include instrumentation-intro.md %}} + +{{% alert title="Nota" %}} + +Nesta página você irá aprender como adicionar rastros, métricas e logs ao código +manualmente. No entanto, não é necessário utilizar apenas um tipo de +instrumentação: utilize a [instrumentação automática](/docs/zero-code/js/) para +começar e depois enriqueça o código com a instrumentação manual conforme +necessário. + +Além disso, para bibliotecas das quais o código depende, não é necessário +escrever o código de instrumentação manualmente, pois elas podem vir com +OpenTelemetry integrado de forma nativa ou é possível utilizar +[bibliotecas de instrumentação](/docs/languages/js/libraries/). + +{{% /alert %}} + +## Preparação da aplicação de exemplo {#example-app} + +Esta página utiliza uma versão modificada da aplicação de exemplo mostrada em +[Primeiros Passos](/docs/languages/js/getting-started/nodejs/) para auxiliar no +aprendizado sobre instrumentação manual. + +Não é obrigatório utilizar a aplicação de exemplo: caso deseje instrumentar uma +aplicação ou biblioteca própria, basta seguir as instruções desta seção para +adaptar o processo ao seu código. + +### Dependências {#example-app-dependencies} + +Crie um arquivo `package.json` vazio do NPM em um novo diretório: + +```shell +npm init -y +``` + +Em seguida, instale as dependências do Express. + +{{< tabpane text=true >}} {{% tab TypeScript %}} + +```sh +npm install express @types/express +npm install -D tsx # ferramenta para executar arquivos TypeScript (.ts) diretamente com node +``` + +{{% /tab %}} {{% tab JavaScript %}} + +```sh +npm install express +``` + +{{% /tab %}} {{< /tabpane >}} + +### Criar e iniciar um servidor HTTP {#create-and-launch-an-http-server} + +Para destacar a diferença entre instrumentar uma biblioteca e uma aplicação, +separe a lógica de rolagem de dados em um arquivo de biblioteca, que será +importado como dependência pelo arquivo da aplicação. + +Crie o arquivo de biblioteca chamado `dice.ts` (ou `dice.js` caso não esteja +utilizando TypeScript) e adicione o seguinte código: + +{{< tabpane text=true >}} {{% tab TypeScript %}} + +```ts +/*dice.ts*/ +function rollOnce(min: number, max: number) { + return Math.floor(Math.random() * (max - min + 1) + min); +} + +export function rollTheDice(rolls: number, min: number, max: number) { + const result: number[] = []; + for (let i = 0; i < rolls; i++) { + result.push(rollOnce(min, max)); + } + return result; +} +``` + +{{% /tab %}} {{% tab JavaScript %}} + +```js +/*dice.js*/ +function rollOnce(min, max) { + return Math.floor(Math.random() * (max - min + 1) + min); +} + +function rollTheDice(rolls, min, max) { + const result = []; + for (let i = 0; i < rolls; i++) { + result.push(rollOnce(min, max)); + } + return result; +} + +module.exports = { rollTheDice }; +``` + +{{% /tab %}} {{< /tabpane >}} + +Crie o arquivo da aplicação chamado `app.ts` (ou `app.js` caso não esteja +utilizando TypeScript) e adicione o seguinte código: + +{{< tabpane text=true >}} {{% tab TypeScript %}} + +```ts +/*app.ts*/ +import express, { type Express } from 'express'; +import { rollTheDice } from './dice'; + +const PORT: number = parseInt(process.env.PORT || '8080'); +const app: Express = express(); + +app.get('/rolldice', (req, res) => { + const rolls = req.query.rolls ? parseInt(req.query.rolls.toString()) : NaN; + if (isNaN(rolls)) { + res + .status(400) + .send("O parâmetro 'rolls' está ausente ou não é um número."); + return; + } + res.send(JSON.stringify(rollTheDice(rolls, 1, 6))); +}); + +app.listen(PORT, () => { + console.log(`Escutando requisições em http://localhost:${PORT}`); +}); +``` + +{{% /tab %}} {{% tab JavaScript %}} + +```js +/*app.js*/ +const express = require('express'); +const { rollTheDice } = require('./dice.js'); + +const PORT = parseInt(process.env.PORT || '8080'); +const app = express(); + +app.get('/rolldice', (req, res) => { + const rolls = req.query.rolls ? parseInt(req.query.rolls.toString()) : NaN; + if (isNaN(rolls)) { + res + .status(400) + .send("O parâmetro 'rolls' está ausente ou não é um número."); + return; + } + res.send(JSON.stringify(rollTheDice(rolls, 1, 6))); +}); + +app.listen(PORT, () => { + console.log(`Escutando requisições em http://localhost:${PORT}`); +}); +``` + +{{% /tab %}} {{< /tabpane >}} + +Para garantir que tudo está funcionando, execute a aplicação com o seguinte +comando e abra no navegador. + +{{< tabpane text=true >}} {{% tab TypeScript %}} + +```console +$ npx tsx app.ts +Escutando requisições em http://localhost:8080 +``` + +{{% /tab %}} {{% tab JavaScript %}} + +```console +$ node app.js +Escutando requisições em http://localhost:8080 +``` + +{{% /tab %}} {{< /tabpane >}} + +## Configuração de instrumentação manual {#manual-instrumentation-setup} + +### Dependências {#dependencies} + +Instale os pacotes da API do OpenTelemetry: + +```shell +npm install @opentelemetry/api @opentelemetry/resources @opentelemetry/semantic-conventions +``` + +### Inicializar o SDK {#initialize-the-sdk} + +{{% alert title="Note" %}} Caso esteja instrumentando uma biblioteca, **ignore +esta etapa**. {{% /alert %}} + +Para instrumentar uma aplicação Node.js, instale o +[SDK OpenTelemetry para Node.js](https://www.npmjs.com/package/@opentelemetry/sdk-node): + +```shell +npm install @opentelemetry/sdk-node +``` + +Antes que qualquer outro módulo da aplicação seja carregado, é necessário +inicializar o SDK. Caso o SDK não seja inicializado, ou seja inicializado tarde +demais, implementações _no-op_ (pronunciada "no-op", de "no operation", +significando "sem operação") serão fornecidas a qualquer biblioteca que obtenha +um Tracer ou Meter da API. + +{{< tabpane text=true >}} {{% tab TypeScript %}} + +```ts +/*instrumentation.ts*/ +import { NodeSDK } from '@opentelemetry/sdk-node'; +import { ConsoleSpanExporter } from '@opentelemetry/sdk-trace-node'; +import { + PeriodicExportingMetricReader, + ConsoleMetricExporter, +} from '@opentelemetry/sdk-metrics'; +import { resourceFromAttributes } from '@opentelemetry/resources'; +import { + ATTR_SERVICE_NAME, + ATTR_SERVICE_VERSION, +} from '@opentelemetry/semantic-conventions'; + +const sdk = new NodeSDK({ + resource: resourceFromAttributes({ + [ATTR_SERVICE_NAME]: 'nomeDoServico', + [ATTR_SERVICE_VERSION]: '1.0', + }), + traceExporter: new ConsoleSpanExporter(), + metricReader: new PeriodicExportingMetricReader({ + exporter: new ConsoleMetricExporter(), + }), +}); + +sdk.start(); +``` + +{{% /tab %}} {{% tab JavaScript %}} + +```js +/*instrumentation.mjs*/ +import { NodeSDK } from '@opentelemetry/sdk-node'; +import { ConsoleSpanExporter } from '@opentelemetry/sdk-trace-node'; +import { + PeriodicExportingMetricReader, + ConsoleMetricExporter, +} from '@opentelemetry/sdk-metrics'; +import { resourceFromAttributes } from '@opentelemetry/resources'; +import { + ATTR_SERVICE_NAME, + ATTR_SERVICE_VERSION, +} from '@opentelemetry/semantic-conventions'; + +const sdk = new NodeSDK({ + resource: resourceFromAttributes({ + [ATTR_SERVICE_NAME]: 'dice-server', + [ATTR_SERVICE_VERSION]: '0.1.0', + }), + traceExporter: new ConsoleSpanExporter(), + metricReader: new PeriodicExportingMetricReader({ + exporter: new ConsoleMetricExporter(), + }), +}); + +sdk.start(); +``` + +{{% /tab %}} {{< /tabpane >}} + +Para fins de depuração (_debugging_) e desenvolvimento local, o exemplo a seguir +exporta telemetria para o console. Após concluir a configuração da +instrumentação manual, será necessário configurar um exportador apropriado para +[exportar os dados de telemetria da aplicação](/docs/languages/js/exporters/) +para um ou mais _backends_ de observabilidade. + +O exemplo também define o atributo padrão obrigatório do SDK `service.name`, que +contém o nome lógico do serviço, e o atributo opcional (porém altamente +recomendado!) `service.version`, que contém a versão da API ou implementação do +serviço. + +Existem métodos alternativos para definir atributos de recurso. Para mais +informações, consulte [Recursos](/docs/languages/js/resources/). + +{{% alert title="Note" %}} Os exemplos a seguir que utilizam +`--import instrumentation.ts` (TypeScript) requerem Node.js v20 ou posterior. +Caso esteja utilizando Node.js v18, utilize o exemplo em JavaScript. +{{% /alert %}} + +{{< tabpane text=true >}} {{% tab TypeScript %}} + +```sh +npx tsx --import ./instrumentation.ts app.ts +``` + +{{% /tab %}} {{% tab JavaScript %}} + +```sh +node --import ./instrumentation.mjs app.js +``` + +{{% /tab %}} {{< /tabpane >}} + +Esta configuração básica ainda não tem efeito na aplicação. É necessário +adicionar código para [rastros](#traces), [métricas](#metrics) e/ou +[logs](#logs). + +Também é possível registrar bibliotecas de instrumentação com o SDK +OpenTelemetry para Node.js a fim de gerar dados de telemetria para as +dependências. Para mais informações, veja +[Bibliotecas](/docs/languages/js/libraries/). + +## Rastros {#traces} + +### Inicializar rastros {#initialize-tracing} + +{{% alert title="Nota" %}} Caso esteja instrumentando uma biblioteca, **ignore +esta etapa**. {{% /alert %}} + +Para habilitar [rastros](/docs/concepts/signals/traces/) em uma aplicação, será +necessário ter um +[`TracerProvider`](/docs/concepts/signals/traces/#tracer-provider) inicializado, +que permitirá criar um [`Tracer`](/docs/concepts/signals/traces/#tracer). + +Caso um `TracerProvider` não seja criado, as APIs do OpenTelemetry irão utilizar +uma implementação _no-op_ e não irão gerar dados. Conforme explicado a seguir, o +arquivo `instrumentation.ts` (ou `instrumentation.js`) deve incluir todo o +código de inicialização do SDK. + +#### Node.js + +Caso as instruções para [inicializar o SDK](#initialize-the-sdk) acima tenham +sido seguidas, já existe um `TracerProvider` configurado. É possível continuar +com [obter um Tracer](#acquiring-a-tracer). + +#### Navegador {#browser} + +{{% include browser-instrumentation-warning.md %}} + +Primeiro, certifique-se de ter instalado os pacotes corretos: + +```shell +npm install @opentelemetry/sdk-trace-web +``` + +Em seguida, atualize o arquivo `instrumentation.ts` (ou `instrumentation.js`) +para conter todo o código de inicialização do SDK: + +{{< tabpane text=true >}} {{% tab TypeScript %}} + +```ts +import { + defaultResource, + resourceFromAttributes, +} from '@opentelemetry/resources'; +import { + ATTR_SERVICE_NAME, + ATTR_SERVICE_VERSION, +} from '@opentelemetry/semantic-conventions'; +import { WebTracerProvider } from '@opentelemetry/sdk-trace-web'; +import { + BatchSpanProcessor, + ConsoleSpanExporter, +} from '@opentelemetry/sdk-trace-base'; + +const resource = defaultResource().merge( + resourceFromAttributes({ + [ATTR_SERVICE_NAME]: 'nome-do-servico', + [ATTR_SERVICE_VERSION]: '0.1.0', + }), +); + +const exporter = new ConsoleSpanExporter(); +const processor = new BatchSpanProcessor(exporter); + +const provider = new WebTracerProvider({ + resource: resource, + spanProcessors: [processor], +}); + +provider.register(); +``` + +{{% /tab %}} {{% tab JavaScript %}} + +```js +const opentelemetry = require('@opentelemetry/api'); +const { + defaultResource, + resourceFromAttributes, +} = require('@opentelemetry/resources'); +const { + ATTR_SERVICE_NAME, + ATTR_SERVICE_VERSION, +} = require('@opentelemetry/semantic-conventions'); +const { WebTracerProvider } = require('@opentelemetry/sdk-trace-web'); +const { + ConsoleSpanExporter, + BatchSpanProcessor, +} = require('@opentelemetry/sdk-trace-base'); + +const resource = defaultResource().merge( + resourceFromAttributes({ + [ATTR_SERVICE_NAME]: 'nome-do-servico', + [ATTR_SERVICE_VERSION]: '0.1.0', + }), +); + +const exporter = new ConsoleSpanExporter(); +const processor = new BatchSpanProcessor(exporter); + +const provider = new WebTracerProvider({ + resource: resource, + spanProcessors: [processor], +}); + +provider.register(); +``` + +{{% /tab %}} {{< /tabpane >}} + +Será necessário gerar um pacote que inclua este arquivo na sua aplicação para +que seja possível utilizar os rastros em todo o restante da aplicação web. + +Isso ainda não terá efeito algum na sua aplicação: é preciso criar +[trechos](#create-spans) para que a telemetria seja emitida. + +#### Escolhendo o processador de trechos correto {#picking-the-right-span-processor} + +Por padrão, o SDK do Node utiliza o `BatchSpanProcessor`, e este processador de +trechos também é escolhido no exemplo do SDK Web. O `BatchSpanProcessor` +processa trechos em lotes antes de serem exportados. Geralmente, este é o +processador correto a ser utilizado por uma aplicação. + +Por outro lado, o `SimpleSpanProcessor` processa trechos conforme são criados. +Isso significa que se forem criados 5 trechos, cada um deles será processado e +exportado antes que o próximo seja criado no código. Esse comportamento pode ser +útil em cenários nos quais não se deseja correr o risco de perder um lote, ou ao +experimentar o OpenTelemetry em ambiente de desenvolvimento. No entanto, esta +escolha também pode gerar uma sobrecarga significativa, especialmente se os +trechos estiverem sendo exportados por uma rede — cada vez que um trecho é +criado, este trecho seria processado e enviado antes que a execução da aplicação +pudesse continuar. + +Na maioria dos casos, utilize `BatchSpanProcessor` em vez de +`SimpleSpanProcessor`. + +### Obtendo um Tracer {#acquiring-a-tracer} + +Em qualquer parte da sua aplicação onde for escrito código de rastreamento +manual, deve-se chamar `getTracer` para obter um Tracer. Exemplo: + +{{< tabpane text=true >}} {{% tab TypeScript %}} + +```ts +import opentelemetry from '@opentelemetry/api'; +//... + +const tracer = opentelemetry.trace.getTracer( + 'nome-do-escopo-de-instrumentacao', + 'versao-do-escopo-de-instrumentacao', +); + +// Agora é possível usar 'tracer' para fazer rastreamento! +``` + +{{% /tab %}} {{% tab JavaScript %}} + +```js +const opentelemetry = require('@opentelemetry/api'); +//... + +const tracer = opentelemetry.trace.getTracer( + 'nome-do-escopo-de-instrumentacao', + 'versao-do-escopo-de-instrumentacao', +); + +// Agora é possível usar 'tracer' para fazer rastreamento! +``` + +{{% /tab %}} {{< /tabpane >}} + +Os valores `nome-do-escopo-de-instrumentacao` e +`versao-do-escopo-de-instrumentacao` devem identificar exclusivamente o +[Escopo de Instrumentação](/docs/concepts/instrumentation-scope/), como o nome +de um pacote, módulo ou classe. O nome é obrigatório, enquanto a versão, embora +opcional, é recomendada. + +De modo geral, recomenda-se chamar `getTracer` na aplicação sempre que for +necessário, em vez de exportar a instância de `tracer` para o restante do +código. Essa abordagem ajuda a evitar problemas mais complexos de carregamento +da aplicação quando há outras dependências envolvidas. + +No caso da [aplicação de exemplo](#example-app), há dois pontos em que um Tracer +pode ser obtido com o Escopo de Instrumentação apropriado: + +Primeiro, no _arquivo da aplicação_ `app.ts` (ou `app.js`): + +{{< tabpane text=true >}} {{% tab TypeScript %}} + +```ts {hl_lines=[6]} +/*app.ts*/ +import { trace } from '@opentelemetry/api'; +import express, { type Express } from 'express'; +import { rollTheDice } from './dice'; + +const tracer = trace.getTracer('dice-server', '0.1.0'); + +const PORT: number = parseInt(process.env.PORT || '8080'); +const app: Express = express(); + +app.get('/rolldice', (req, res) => { + const rolls = req.query.rolls ? parseInt(req.query.rolls.toString()) : NaN; + if (isNaN(rolls)) { + res + .status(400) + .send("O parâmetro 'rolls' está ausente ou não é um número."); + return; + } + res.send(JSON.stringify(rollTheDice(rolls, 1, 6))); +}); + +app.listen(PORT, () => { + console.log(`Escutando requisições em http://localhost:${PORT}`); +}); +``` + +{{% /tab %}} {{% tab JavaScript %}} + +```js {hl_lines=[6]} +/*app.js*/ +const { trace } = require('@opentelemetry/api'); +const express = require('express'); +const { rollTheDice } = require('./dice.js'); + +const tracer = trace.getTracer('dice-server', '0.1.0'); + +const PORT = parseInt(process.env.PORT || '8080'); +const app = express(); + +app.get('/rolldice', (req, res) => { + const rolls = req.query.rolls ? parseInt(req.query.rolls.toString()) : NaN; + if (isNaN(rolls)) { + res + .status(400) + .send("O parâmetro 'rolls' está ausente ou não é um número."); + return; + } + res.send(JSON.stringify(rollTheDice(rolls, 1, 6))); +}); + +app.listen(PORT, () => { + console.log(`Escutando requisições em http://localhost:${PORT}`); +}); +``` + +{{% /tab %}} {{< /tabpane >}} + +E segundo, no _arquivo de biblioteca_ `dice.ts` (ou `dice.js`): + +{{< tabpane text=true >}} {{% tab TypeScript %}} + +```ts {hl_lines=[4]} +/*dice.ts*/ +import { trace } from '@opentelemetry/api'; + +const tracer = trace.getTracer('dice-lib'); + +function rollOnce(min: number, max: number) { + return Math.floor(Math.random() * (max - min + 1) + min); +} + +export function rollTheDice(rolls: number, min: number, max: number) { + const result: number[] = []; + for (let i = 0; i < rolls; i++) { + result.push(rollOnce(min, max)); + } + return result; +} +``` + +{{% /tab %}} {{% tab JavaScript %}} + +```js {hl_lines=[4]} +/*dice.js*/ +const { trace } = require('@opentelemetry/api'); + +const tracer = trace.getTracer('dice-lib'); + +function rollOnce(min, max) { + return Math.floor(Math.random() * (max - min + 1) + min); +} + +function rollTheDice(rolls, min, max) { + const result = []; + for (let i = 0; i < rolls; i++) { + result.push(rollOnce(min, max)); + } + return result; +} + +module.exports = { rollTheDice }; +``` + +{{% /tab %}} {{< /tabpane >}} + +### Criar trechos (_spans_) {#create-spans} + +Agora que os [Tracers](/docs/concepts/signals/traces/#tracer) foram +inicializados, é possível criar [trechos](/docs/concepts/signals/traces/#spans). + +A API do OpenTelemetry JavaScript oferece dois métodos para criar trechos: + +- [`tracer.startSpan`](https://open-telemetry.github.io/opentelemetry-js/interfaces/_opentelemetry_api._opentelemetry_api.Tracer.html#startspan): + Inicia um novo trecho sem defini-lo no contexto. +- [`tracer.startActiveSpan`](https://open-telemetry.github.io/opentelemetry-js/interfaces/_opentelemetry_api._opentelemetry_api.Tracer.html#startactivespan): + Inicia um novo trecho e executa a função de _callback_ fornecida, passando o + trecho criado como primeiro argumento. O novo trecho é definido no contexto + ativo enquanto a função é executada. + +Na maioria dos casos, é preferível utilizar `tracer.startActiveSpan`, pois ele +gerencia automaticamente o contexto ativo. + +O código a seguir ilustra como criar um trecho ativo. + +{{< tabpane text=true >}} {{% tab TypeScript %}} + +```ts +import { trace, type Span } from '@opentelemetry/api'; + +/* ... */ + +export function rollTheDice(rolls: number, min: number, max: number) { + // Cria um trecho ativo. O trecho deve ser finalizado. + return tracer.startActiveSpan('rollTheDice', (span: Span) => { + const result: number[] = []; + for (let i = 0; i < rolls; i++) { + result.push(rollOnce(min, max)); + } + // Certifique-se de encerrar o trecho! + span.end(); + return result; + }); +} +``` + +{{% /tab %}} {{% tab JavaScript %}} + +```js +function rollTheDice(rolls, min, max) { + // Cria um trecho ativo. O trecho deve ser finalizado. + return tracer.startActiveSpan('rollTheDice', (span) => { + const result = []; + for (let i = 0; i < rolls; i++) { + result.push(rollOnce(min, max)); + } + // Certifique-se de encerrar o trecho! + span.end(); + return result; + }); +} +``` + +{{% /tab %}} {{< /tabpane >}} + +Caso as instruções utilizando a [aplicação de exemplo](#example-app) tenham sido +seguidas até este ponto, é possível copiar o código acima no arquivo de +biblioteca `dice.ts` (ou `dice.js`). Você poderá visualizar trechos sendo +emitidos pela sua aplicação. + +Execute o comando abaixo para iniciar a aplicação e, em seguida, envie +requisições acessando pelo navegador +ou utilizando o `curl`. + +{{< tabpane text=true >}} {{% tab TypeScript %}} + +```sh +npx tsx --import ./instrumentation.ts app.ts +``` + +{{% /tab %}} {{% tab JavaScript %}} + +```sh +node --import ./instrumentation.mjs app.js +``` + +{{% /tab %}} {{< /tabpane >}} + +Após algum tempo, os trechos poderão ser vistos impressos no console pelo +`ConsoleSpanExporter`, similar a isto: + +```js +{ + resource: { + attributes: { + 'service.name': 'dice-server', + 'service.version': '0.1.0', + // ... + } + }, + instrumentationScope: { name: 'dice-lib', version: undefined, schemaUrl: undefined }, + traceId: '30d32251088ba9d9bca67b09c43dace0', + parentSpanContext: undefined, + traceState: undefined, + name: 'rollTheDice', + id: 'cc8a67c2d4840402', + kind: 0, + timestamp: 1756165206470000, + duration: 35.584, + attributes: {}, + status: { code: 0 }, + events: [], + links: [] +} +``` + +### Criar trechos aninhados {#create-nested-spans} + +[Trechos](/docs/concepts/signals/traces/#spans) aninhados permitem rastrear +operações que são aninhadas por natureza. Por exemplo, a função `rollOnce()` +abaixo representa uma operação aninhada. O exemplo a seguir cria um trecho +aninhado que rastreia `rollOnce()`. + +{{< tabpane text=true >}} {{% tab TypeScript %}} + +```ts +function rollOnce(i: number, min: number, max: number) { + return tracer.startActiveSpan(`rollOnce:${i}`, (span: Span) => { + const result = Math.floor(Math.random() * (max - min + 1) + min); + span.end(); + return result; + }); +} + +export function rollTheDice(rolls: number, min: number, max: number) { + // Cria um trecho ativo. O trecho deve ser finalizado. + return tracer.startActiveSpan('rollTheDice', (parentSpan: Span) => { + const result: number[] = []; + for (let i = 0; i < rolls; i++) { + result.push(rollOnce(i, min, max)); + } + // Certifique-se de encerrar o trecho! + parentSpan.end(); + return result; + }); +} +``` + +{{% /tab %}} {{% tab JavaScript %}} + +```js +function rollOnce(i, min, max) { + return tracer.startActiveSpan(`rollOnce:${i}`, (span) => { + const result = Math.floor(Math.random() * (max - min + 1) + min); + span.end(); + return result; + }); +} + +function rollTheDice(rolls, min, max) { + // Cria um trecho ativo. O trecho deve ser finalizado. + return tracer.startActiveSpan('rollTheDice', (parentSpan) => { + const result = []; + for (let i = 0; i < rolls; i++) { + result.push(rollOnce(i, min, max)); + } + // Certifique-se de encerrar o trecho! + parentSpan.end(); + return result; + }); +} +``` + +{{% /tab %}} {{< /tabpane >}} + +Este código cria um trecho filho para cada rolagem, tendo o ID do `parentSpan` +como seu ID de pai (_parent ID_): + +```js +{ + traceId: '6469e115dc1562dd768c999da0509615', + parentSpanContext: { + traceId: '6469e115dc1562dd768c999da0509615', + spanId: '38691692d6bc3395', + // ... + }, + name: 'rollOnce:0', + id: '36423bc1ce7532b0', + timestamp: 1756165362215000, + duration: 85.667, + // ... +} +{ + traceId: '6469e115dc1562dd768c999da0509615', + parentSpanContext: { + traceId: '6469e115dc1562dd768c999da0509615', + spanId: '38691692d6bc3395', + // ... + }, + name: 'rollOnce:1', + id: 'ed9bbba2264d6872', + timestamp: 1756165362215000, + duration: 16.834, + // ... +} +{ + traceId: '6469e115dc1562dd768c999da0509615', + parentSpanContext: undefined, + name: 'rollTheDice', + id: '38691692d6bc3395', + timestamp: 1756165362214000, + duration: 1022.209, + // ... +} +``` + +### Criar trechos independentes {#create-independent-spans} + +Os exemplos anteriores mostraram como criar um trecho ativo. Em alguns casos, +você vai querer criar trechos inativos que são irmãos uns dos outros, em vez de +serem aninhados. + +```js +const doWork = () => { + const span1 = tracer.startSpan('work-1'); + // realizar alguma operação + const span2 = tracer.startSpan('work-2'); + // realizar mais alguma operação + const span3 = tracer.startSpan('work-3'); + // realizar ainda mais operações + + span1.end(); + span2.end(); + span3.end(); +}; +``` + +Neste exemplo, `span1`, `span2` e `span3` são trechos irmãos e nenhum deles é +considerado o trecho ativo no momento. Eles compartilham o mesmo pai, em vez de +serem aninhados uns sob os outros. + +Essa disposição pode ser útil caso você tenha unidades de trabalho que estão +agrupadas, mas são conceitualmente independentes umas das outras. + +### Obter o trecho atual {#get-the-current-span} + +Às vezes, é útil fazer algo com o [trecho](/docs/concepts/signals/traces/#spans) +atual/ativo em um determinado ponto na execução da aplicação. + +```js +const activeSpan = opentelemetry.trace.getActiveSpan(); + +// fazer algo com o trecho ativo, opcionalmente encerrando-o se for apropriado para o caso de uso. +``` + +### Obter um trecho do contexto {#get-a-span-from-context} + +Também pode ser útil obter o [trecho](/docs/concepts/signals/traces/#spans) de +um determinado contexto que não é necessariamente o trecho ativo. + +```js +const ctx = getContextFromSomewhere(); +const span = opentelemetry.trace.getSpan(ctx); + +// fazer algo com o trecho adquirido, opcionalmente encerrando-o se for apropriado para o caso de uso. +``` + +### Atributos {#attributes} + +[Atributos](/docs/concepts/signals/traces/#attributes) permitem anexar pares de +chave/valor a um [`Trecho`](/docs/concepts/signals/traces/#spans) para que ele +carregue mais informações sobre a operação atual que está sendo rastreada. + +{{< tabpane text=true >}} {{% tab TypeScript %}} + +```ts +function rollOnce(i: number, min: number, max: number) { + return tracer.startActiveSpan(`rollOnce:${i}`, (span: Span) => { + const result = Math.floor(Math.random() * (max - min + 1) + min); + + // Adiciona um atributo ao trecho + span.setAttribute('dicelib.rolled', result.toString()); + + span.end(); + return result; + }); +} +``` + +{{% /tab %}} {{% tab JavaScript %}} + +```js +function rollOnce(i, min, max) { + return tracer.startActiveSpan(`rollOnce:${i}`, (span) => { + const result = Math.floor(Math.random() * (max - min + 1) + min); + + // Adiciona um atributo ao trecho + span.setAttribute('dicelib.rolled', result.toString()); + + span.end(); + return result; + }); +} +``` + +{{% /tab %}} {{< /tabpane >}} + +Também é possível adicionar atributos a um trecho quando ele é criado: + +```javascript +tracer.startActiveSpan( + 'app.new-span', + { attributes: { attribute1: 'value1' } }, + (span) => { + // realizar alguma operação... + + span.end(); + }, +); +``` + +{{< tabpane text=true >}} {{% tab TypeScript %}} + +```ts +function rollTheDice(rolls: number, min: number, max: number) { + return tracer.startActiveSpan( + 'rollTheDice', + { attributes: { 'dicelib.rolls': rolls.toString() } }, + (span: Span) => { + /* ... */ + }, + ); +} +``` + +{{% /tab %}} {{% tab JavaScript %}} + +```js +function rollTheDice(rolls, min, max) { + return tracer.startActiveSpan( + 'rollTheDice', + { attributes: { 'dicelib.rolls': rolls.toString() } }, + (span) => { + /* ... */ + }, + ); +} +``` + +{{% /tab %}} {{< /tabpane >}} + +#### Atributos semânticos {#semantic-attributes} + +Existem convenções semânticas para trechos que representam operações em +protocolos bem conhecidos, como HTTP ou chamadas de banco de dados. As +convenções semânticas para esses trechos estão definidas na especificação em +[Convenções Semânticas de Rastreamento (_Trace semantic conventions_)](/docs/specs/semconv/general/trace/). +No exemplo deste guia, podem ser utilizados atributos de código fonte. + +Primeiro, adicione o pacote de convenções semânticas como dependência da +aplicação: + +```shell +npm install --save @opentelemetry/semantic-conventions +``` + +Adicione as seguintes importações no topo do arquivo da aplicação: + +{{< tabpane text=true >}} {{% tab TypeScript %}} + +```ts +import { + ATTR_CODE_FUNCTION_NAME, + ATTR_CODE_FILE_PATH, +} from '@opentelemetry/semantic-conventions'; +``` + +{{% /tab %}} {{% tab JavaScript %}} + +```js +const { + ATTR_CODE_FUNCTION_NAME, + ATTR_CODE_FILE_PATH, +} = require('@opentelemetry/semantic-conventions'); +``` + +{{% /tab %}} {{< /tabpane >}} + +Por fim, atualize o código para incluir os atributos semânticos: + +```javascript +const doWork = () => { + tracer.startActiveSpan('app.doWork', (span) => { + span.setAttribute(ATTR_CODE_FUNCTION_NAME, 'doWork'); + span.setAttribute(ATTR_CODE_FILE_PATH, __filename); + + // Realizar alguma operação... + + span.end(); + }); +}; +``` + +### Eventos de trecho {#span-events} + +Um [Evento de Trecho (_Span Event_)](/docs/concepts/signals/traces/#span-events) +é uma mensagem legível por humanos em um +[`Trecho`](/docs/concepts/signals/traces/#spans) que representa um evento +discreto, sem duração, que pode ser rastreado por um único carimbo de data e +hora (_timestamp_). Você pode pensar nisso como uma forma primitiva de log. + +```js +span.addEvent('Executando algo'); + +const result = doWork(); +``` + +Também é possível criar Eventos de Trecho com +[Atributos](/docs/concepts/signals/traces/#attributes): + +```js +span.addEvent('some log', { + 'log.severity': 'error', + 'log.message': 'Data not found', + 'request.id': requestId, +}); +``` + +### Links de trecho {#span-links} + +[Trechos](/docs/concepts/signals/traces/#spans) podem ser criados com zero ou +mais [Links](/docs/concepts/signals/traces/#span-links) para outros Trechos que +estão casualmente relacionados. Um cenário comum é correlacionar um ou mais +rastros com o trecho atual. + +```js +const someFunction = (spanToLinkFrom) => { + const options = { + links: [ + { + context: spanToLinkFrom.spanContext(), + }, + ], + }; + + tracer.startActiveSpan('app.someFunction', options, (span) => { + // Executa alguma operação... + + span.end(); + }); +}; +``` + +### Estado de trecho {#span-status} + +{{% include "span-status-preamble.md" %}} + +{{< tabpane text=true >}} {{% tab TypeScript %}} + +```ts +import opentelemetry, { SpanStatusCode } from '@opentelemetry/api'; + +// ... + +tracer.startActiveSpan('app.doWork', (span) => { + for (let i = 0; i <= Math.floor(Math.random() * 40000000); i += 1) { + if (i > 10000) { + span.setStatus({ + code: SpanStatusCode.ERROR, + message: 'Erro', + }); + } + } + + span.end(); +}); +``` + +{{% /tab %}} {{% tab JavaScript %}} + +```js +const opentelemetry = require('@opentelemetry/api'); + +// ... + +tracer.startActiveSpan('app.doWork', (span) => { + for (let i = 0; i <= Math.floor(Math.random() * 40000000); i += 1) { + if (i > 10000) { + span.setStatus({ + code: opentelemetry.SpanStatusCode.ERROR, + message: 'Erro', + }); + } + } + + span.end(); +}); +``` + +{{% /tab %}} {{< /tabpane >}} + +### Registrar exceções {#recording-exceptions} + +Registrar exceções no momento em que ocorrem é uma boa prática, especialmente +quando combinado com a definição de [estado do trecho](#span-status). + +{{< tabpane text=true >}} {{% tab TypeScript %}} + +```ts +import opentelemetry, { SpanStatusCode } from '@opentelemetry/api'; + +// ... + +try { + doWork(); +} catch (ex) { + if (ex instanceof Error) { + span.recordException(ex); + } + span.setStatus({ code: SpanStatusCode.ERROR }); +} +``` + +{{% /tab %}} {{% tab JavaScript %}} + +```js +const opentelemetry = require('@opentelemetry/api'); + +// ... + +try { + doWork(); +} catch (ex) { + if (ex instanceof Error) { + span.recordException(ex); + } + span.setStatus({ code: opentelemetry.SpanStatusCode.ERROR }); +} +``` + +{{% /tab %}} {{< /tabpane >}} + +### Usando `sdk-trace-base` e propagando manualmente o contexto do trecho {#using-sdk-trace-base-and-manually-propagating-span-context} + +Em alguns casos, pode não ser possível utilizar o SDK do Node.js nem o SDK para +Web. A maior diferença, além do código de inicialização, é que será necessário +definir manualmente trechos como ativos no contexto atual para poder criar +trechos aninhados. + +#### Inicializando rastreamento com `sdk-trace-base` {#initializing-tracing-with-sdk-trace-base} + +A inicialização é semelhante a como seria feito com Node.js ou o SDK Web. + +{{< tabpane text=true >}} {{% tab TypeScript %}} + +```ts +import opentelemetry from '@opentelemetry/api'; +import { + CompositePropagator, + W3CTraceContextPropagator, + W3CBaggagePropagator, +} from '@opentelemetry/core'; +import { + BasicTracerProvider, + BatchSpanProcessor, + ConsoleSpanExporter, +} from '@opentelemetry/sdk-trace-base'; + +opentelemetry.trace.setGlobalTracerProvider( + new BasicTracerProvider({ + // Configura o Span Processor para enviar trechos ao exportador + spanProcessors: [new BatchSpanProcessor(new ConsoleSpanExporter())], + }), +); + +opentelemetry.propagation.setGlobalPropagator( + new CompositePropagator({ + propagators: [new W3CTraceContextPropagator(), new W3CBaggagePropagator()], + }), +); + +// Isso é o que será acessado em todo o código de instrumentação +const tracer = opentelemetry.trace.getTracer('example-basic-tracer-node'); +``` + +{{% /tab %}} {{% tab JavaScript %}} + +```js +const opentelemetry = require('@opentelemetry/api'); +const { + CompositePropagator, + W3CTraceContextPropagator, + W3CBaggagePropagator, +} = require('@opentelemetry/core'); +const { + BasicTracerProvider, + ConsoleSpanExporter, + BatchSpanProcessor, +} = require('@opentelemetry/sdk-trace-base'); + +opentelemetry.trace.setGlobalTracerProvider( + new BasicTracerProvider({ + // Configura o Span Processor para enviar trechos ao exportador + spanProcessors: [new BatchSpanProcessor(new ConsoleSpanExporter())], + }), +); + +opentelemetry.propagation.setGlobalPropagator( + new CompositePropagator({ + propagators: [new W3CTraceContextPropagator(), new W3CBaggagePropagator()], + }), +); + +// Isso é o que será acessado em todo o código de instrumentação +const tracer = opentelemetry.trace.getTracer('example-basic-tracer-node'); +``` + +{{% /tab %}} {{< /tabpane >}} + +Assim como os outros exemplos neste documento, isso exporta um Tracer que pode +ser usado em toda a aplicação. + +#### Criando trechos aninhados com `sdk-trace-base` {#creating-nested-spans-with-sdk-trace-base} + +Para criar trechos aninhados, é necessário definir qualquer trecho atualmente +criado como o trecho ativo no contexto atual. O uso de `startActiveSpan` não é +recomendado aqui, pois ele não define o trecho como ativo. + +```javascript +const mainWork = () => { + const parentSpan = tracer.startSpan('main'); + + for (let i = 0; i < 3; i += 1) { + doWork(parentSpan, i); + } + + // Certifique-se de encerrar o trecho pai! + parentSpan.end(); +}; + +const doWork = (parent, i) => { + // Para criar um trecho filho, é necessário marcar o trecho atual (pai) como o trecho ativo + // no contexto, e então utilizar o contexto resultante para criar um trecho filho. + const ctx = opentelemetry.trace.setSpan( + opentelemetry.context.active(), + parent, + ); + const span = tracer.startSpan(`doWork:${i}`, undefined, ctx); + + // simular alguma operação aleatória. + for (let i = 0; i <= Math.floor(Math.random() * 40000000); i += 1) { + // vazio + } + + // Certifique-se de encerrar o span filho! Se não o fizer, + // ele continuará a rastrear as operações além de 'doWork'! + span.end(); +}; +``` + +Todas as outras APIs se comportam da mesma forma ao usar o `sdk-trace-base`, em +comparação com os SDKs do Node.js ou Web. + +## Métricas {#metrics} + +As [métricas](/docs/concepts/signals/metrics) combinam medições individuais em +agregados, gerando dados constantes independentemente da carga do sistema. Os +agregados não contêm detalhes suficientes para diagnosticar problemas de baixo +nível, mas complementam os trechos ao ajudar a identificar tendências e fornecer +telemetria sobre o tempo de execução da aplicação. + +### Inicializar métricas {#initialize-metrics} + +{{% alert %}} Caso esteja instrumentando uma biblioteca, ignore esta etapa. +{{% /alert %}} + +Para ativar [métricas](/docs/concepts/signals/metrics/) em sua aplicação, é +necessário inicializar um +[`MeterProvider`](/docs/concepts/signals/metrics/#meter-provider), que permitirá +criar um [`Meter`](/docs/concepts/signals/metrics/#meter). + +Caso um `MeterProvider` não seja criado, as APIs do OpenTelemetry para métricas +utilizarão uma implementação _no-op_ (pronunciada "no-op", de "no operation", +significando "sem operação") e não conseguirão gerar dados. Conforme explicado a +seguir, modifique o arquivo `instrumentation.ts` (ou `instrumentation.js`) para +incluir todo o código de inicialização do SDK no Node e no navegador. + +#### Node.js {#initialize-metrics-nodejs} + +Caso tenha seguido as instruções para [inicializar o SDK](#initialize-the-sdk) +acima, você já possui um `MeterProvider` configurado. É possível continuar com +[Obtendo um Meter](#acquiring-a-meter). + +##### Inicializando métricas com `sdk-metrics` {#initializing-metrics-with-sdk-metrics} + +Em alguns casos, pode não ser possível — ou desejável — utilizar o +[SDK completo do OpenTelemetry para Node.js](https://www.npmjs.com/package/@opentelemetry/sdk-node). +O mesmo vale se você quiser utilizar o usar OpenTelemetry JavaScript no +navegador. + +Nesses casos, é possível inicializar as métricas com o pacote +`@opentelemetry/sdk-metrics`: + +```shell +npm install @opentelemetry/sdk-metrics +``` + +Caso ainda não tenha feito isso, crie um arquivo `instrumentation.ts` (ou +`instrumentation.js`) que contenha todo o código de inicialização do SDK: + +{{< tabpane text=true >}} {{% tab TypeScript %}} + +```ts +import opentelemetry from '@opentelemetry/api'; +import { + ConsoleMetricExporter, + MeterProvider, + PeriodicExportingMetricReader, +} from '@opentelemetry/sdk-metrics'; +import { + defaultResource, + resourceFromAttributes, +} from '@opentelemetry/resources'; +import { + ATTR_SERVICE_NAME, + ATTR_SERVICE_VERSION, +} from '@opentelemetry/semantic-conventions'; + +const resource = defaultResource().merge( + resourceFromAttributes({ + [ATTR_SERVICE_NAME]: 'dice-server', + [ATTR_SERVICE_VERSION]: '0.1.0', + }), +); + +const metricReader = new PeriodicExportingMetricReader({ + exporter: new ConsoleMetricExporter(), + // O valor padrão é 60000ms (60 segundos). Vamos utilizar 10 segundos apenas para fins de demonstração. + exportIntervalMillis: 10000, +}); + +const myServiceMeterProvider = new MeterProvider({ + resource: resource, + readers: [metricReader], +}); + +// Defina este MeterProvider para ser global para a aplicação que está sendo instrumentada. +opentelemetry.metrics.setGlobalMeterProvider(myServiceMeterProvider); +``` + +{{% /tab %}} {{% tab JavaScript %}} + +```js +const opentelemetry = require('@opentelemetry/api'); +const { + MeterProvider, + PeriodicExportingMetricReader, + ConsoleMetricExporter, +} = require('@opentelemetry/sdk-metrics'); +const { + defaultResource, + resourceFromAttributes, +} = require('@opentelemetry/resources'); +const { + ATTR_SERVICE_NAME, + ATTR_SERVICE_VERSION, +} = require('@opentelemetry/semantic-conventions'); + +const resource = defaultResource().merge( + resourceFromAttributes({ + [ATTR_SERVICE_NAME]: 'service-name-here', + [ATTR_SERVICE_VERSION]: '0.1.0', + }), +); + +const metricReader = new PeriodicExportingMetricReader({ + exporter: new ConsoleMetricExporter(), + + // O valor padrão é 60000ms (60 segundos). Vamos utilizar 10 segundos apenas para fins de demonstração. + exportIntervalMillis: 10000, +}); + +const myServiceMeterProvider = new MeterProvider({ + resource: resource, + readers: [metricReader], +}); +// Defina este MeterProvider para ser global para a aplicação que está sendo instrumentada. +opentelemetry.metrics.setGlobalMeterProvider(myServiceMeterProvider); +``` + +{{% /tab %}} {{< /tabpane >}} + +Será necessário utilizar `--import` com este arquivo ao executar a aplicação, +como: + +{{< tabpane text=true >}} {{% tab TypeScript %}} + +```sh +npx tsx --import ./instrumentation.ts app.ts +``` + +{{% /tab %}} {{% tab JavaScript %}} + +```sh +node --import ./instrumentation.mjs app.js +``` + +{{% /tab %}} {{< /tabpane >}} + +Agora que o `MeterProvider` está configurado, é possível adquirir um `Meter`. + +### Obtendo um Meter {#acquiring-a-meter} + +Em qualquer parte da aplicação onde houver código instrumentado manualmente, é +possível chamar `getMeter` para obter um Meter. Por exemplo: + +{{< tabpane text=true >}} {{% tab TypeScript %}} + +```ts +import opentelemetry from '@opentelemetry/api'; + +const myMeter = opentelemetry.metrics.getMeter( + 'nome-do-escopo-de-instrumentacao', + 'versao-do-escopo-de-instrumentacao', +); + +// Agora é possível utilizar 'meter' para criar instrumentos! +``` + +{{% /tab %}} {{% tab JavaScript %}} + +```js +const opentelemetry = require('@opentelemetry/api'); + +const myMeter = opentelemetry.metrics.getMeter( + 'nome-do-escopo-de-instrumentacao', + 'versao-do-escopo-de-instrumentacao', +); + +// Agora é possível utilizar 'meter' para criar instrumentos! +``` + +{{% /tab %}} {{< /tabpane >}} + +Os valores `nome-do-escopo-de-instrumentacao` e +`versao-do-escopo-de-instrumentacao` devem identificar exclusivamente o +[Escopo de Instrumentação](/docs/concepts/instrumentation-scope/), como o nome +de um pacote, módulo ou classe. O nome é obrigatório, enquanto a versão, embora +opcional, é recomendada. + +De modo geral, recomenda-se chamar `getMeter` na aplicação sempre que for +necessário, em vez de exportar a instância de `meter` para o restante do código. +Essa abordagem ajuda a evitar problemas mais complexos de carregamento da +aplicação quando há outras dependências envolvidas. + +No caso da [aplicação de exemplo](#example-app), há dois pontos em que um Meter +pode ser obtido com o Escopo de Instrumentação apropriado: + +Primeiro, no _arquivo da aplicação_ `app.ts` (ou `app.js`): + +{{< tabpane text=true >}} {{% tab TypeScript %}} + +```ts +/*app.ts*/ +import { metrics, trace } from '@opentelemetry/api'; +import express, { type Express } from 'express'; +import { rollTheDice } from './dice'; + +const tracer = trace.getTracer('dice-server', '0.1.0'); +const meter = metrics.getMeter('dice-server', '0.1.0'); + +const PORT: number = parseInt(process.env.PORT || '8080'); +const app: Express = express(); + +app.get('/rolldice', (req, res) => { + const rolls = req.query.rolls ? parseInt(req.query.rolls.toString()) : NaN; + if (isNaN(rolls)) { + res + .status(400) + .send("O parâmetro 'rolls' está ausente ou não é um número."); + return; + } + res.send(JSON.stringify(rollTheDice(rolls, 1, 6))); +}); + +app.listen(PORT, () => { + console.log(`Escutando requisições em http://localhost:${PORT}`); +}); +``` + +{{% /tab %}} {{% tab JavaScript %}} + +```js +/*app.js*/ +const { trace, metrics } = require('@opentelemetry/api'); +const express = require('express'); +const { rollTheDice } = require('./dice.js'); + +const tracer = trace.getTracer('dice-server', '0.1.0'); +const meter = metrics.getMeter('dice-server', '0.1.0'); + +const PORT = parseInt(process.env.PORT || '8080'); +const app = express(); + +app.get('/rolldice', (req, res) => { + const rolls = req.query.rolls ? parseInt(req.query.rolls.toString()) : NaN; + if (isNaN(rolls)) { + res + .status(400) + .send("O parâmetro 'rolls' está ausente ou não é um número."); + return; + } + res.send(JSON.stringify(rollTheDice(rolls, 1, 6))); +}); + +app.listen(PORT, () => { + console.log(`Escutando requisições em http://localhost:${PORT}`); +}); +``` + +{{% /tab %}} {{< /tabpane >}} + +E segundo, no _arquivo de biblioteca_ `dice.ts` (ou `dice.js`): + +{{< tabpane text=true >}} {{% tab TypeScript %}} + +```ts +/*dice.ts*/ +import { trace, metrics } from '@opentelemetry/api'; + +const tracer = trace.getTracer('dice-lib'); +const meter = metrics.getMeter('dice-lib'); + +function rollOnce(min: number, max: number) { + return Math.floor(Math.random() * (max - min + 1) + min); +} + +export function rollTheDice(rolls: number, min: number, max: number) { + const result: number[] = []; + for (let i = 0; i < rolls; i++) { + result.push(rollOnce(min, max)); + } + return result; +} +``` + +{{% /tab %}} {{% tab JavaScript %}} + +```js +/*dice.js*/ +const { trace, metrics } = require('@opentelemetry/api'); + +const tracer = trace.getTracer('dice-lib'); +const meter = metrics.getMeter('dice-lib'); + +function rollOnce(min, max) { + return Math.floor(Math.random() * (max - min + 1) + min); +} + +function rollTheDice(rolls, min, max) { + const result = []; + for (let i = 0; i < rolls; i++) { + result.push(rollOnce(min, max)); + } + return result; +} + +module.exports = { rollTheDice }; +``` + +{{% /tab %}} {{< /tabpane >}} + +Agora que os [Meters](/docs/concepts/signals/metrics/#meter) foram +inicializados, é possível criar +[instrumentos de métrica](/docs/concepts/signals/metrics/#metric-instruments). + +### Utilizando Contadores (Counters) {#using-counters} + +Contadores são usados para medir valores cumulativos, não-negativos e +crescentes. + +No caso da [aplicação de exemplo](#example-app), é possível contar quantas vezes +os dados foram lançados: + +{{< tabpane text=true >}} {{% tab TypeScript %}} + +```ts +/*dice.ts*/ +const counter = meter.createCounter('dice-lib.rolls.counter'); + +function rollOnce(min: number, max: number) { + counter.add(1); + return Math.floor(Math.random() * (max - min + 1) + min); +} +``` + +{{% /tab %}} {{% tab JavaScript %}} + +```js +/*dice.js*/ +const counter = meter.createCounter('dice-lib.rolls.counter'); + +function rollOnce(min, max) { + counter.add(1); + return Math.floor(Math.random() * (max - min + 1) + min); +} +``` + +{{% /tab %}} {{< /tabpane >}} + +### Utilizando Contadores UpDown (UpDown Counters) {#using-updown-counters} + +Contadores UpDown podem incrementar e decrementar, permitindo observar um valor +cumulativo que aumenta ou diminui ao longo do tempo. + +```js +const counter = myMeter.createUpDownCounter('events.counter'); + +//... + +counter.add(1); + +//... + +counter.add(-1); +``` + +### Utilizando Histogramas (Histograms) {#using-histograms} + +Histogramas são utilizados para medir uma distribuição de valores ao longo do +tempo. + +Por exemplo, veja como reportar uma distribuição de tempos de resposta para uma +rota de API com Express: + +{{< tabpane text=true >}} {{% tab TypeScript %}} + +```ts +import express from 'express'; + +const app = express(); + +app.get('/', (_req, _res) => { + const histogram = myMeter.createHistogram('task.duration'); + const startTime = new Date().getTime(); + + // realiza alguma operação em uma chamada de API + + const endTime = new Date().getTime(); + const executionTime = endTime - startTime; + + // Registra a duração da operação da tarefa + histogram.record(executionTime); +}); +``` + +{{% /tab %}} {{% tab JavaScript %}} + +```js +const express = require('express'); + +const app = express(); + +app.get('/', (_req, _res) => { + const histogram = myMeter.createHistogram('task.duration'); + const startTime = new Date().getTime(); + + // realiza alguma operação em uma chamada de API + + const endTime = new Date().getTime(); + const executionTime = endTime - startTime; + + // Registra a duração da operação da tarefa + histogram.record(executionTime); +}); +``` + +{{% /tab %}} {{< /tabpane >}} + +### Utilizando Contadores Observáveis (Assíncronos) {#using-observable-async-counters} + +Contadores observáveis (_Observable counters_) são utilizados para medir um +valor aditivo, não-negativo e monotonicamente crescente. + +```js +const events = []; + +const addEvent = (name) => { + events.push(name); +}; + +const counter = myMeter.createObservableCounter('events.counter'); + +counter.addCallback((result) => { + result.observe(events.length); +}); + +//... chamadas para addEvent +``` + +### Utilizando Contadores UpDown Observáveis (Assíncronos) {#using-observable-async-updown-counters} + +Contadores UpDown observáveis (_Observable UpDown counters_) podem incrementar e +decrementar, permitindo medir um valor cumulativo aditivo, não-negativo e +não-monotonicamente crescente. + +```js +const events = []; + +const addEvent = (name) => { + events.push(name); +}; + +const removeEvent = () => { + events.pop(); +}; + +const counter = myMeter.createObservableUpDownCounter('events.counter'); + +counter.addCallback((result) => { + result.observe(events.length); +}); + +//... chamadas para addEvent e removeEvent +``` + +### Utilizando Medidores Observáveis (Assíncronos) {#using-observable-async-gauges} + +Medidores Observáveis (_Observable Gauges_) devem ser usados para medir valores +não-aditivos. + +```js +let temperature = 32; + +const gauge = myMeter.createObservableGauge('temperature.gauge'); + +gauge.addCallback((result) => { + result.observe(temperature); +}); + +//... a variável temperature é modificada por um sensor +``` + +### Descrevendo instrumentos {#describing-instruments} + +Ao criar instrumentos como contadores, histogramas, etc., é possível fornecer +uma descrição. + +```js +const httpServerResponseDuration = myMeter.createHistogram( + 'http.server.duration', + { + description: 'Distribuição do tempo de resposta do servidor HTTP', + unit: 'milliseconds', + valueType: ValueType.INT, + }, +); +``` + +Em JavaScript, cada tipo de configuração significa o seguinte: + +- `description` - Uma descrição para o instrumento, legível por humanos. +- `unit` - A descrição da unidade de medida que o valor pretende representar. + Por exemplo, `milliseconds` para medir duração, ou `bytes` para contar número + de bytes. +- `valueType` - O tipo de valor numérico usado nas medições. + +De modo geral, recomenda-se descrever cada instrumento criado. + +### Adicionando Atributos {#adding-attributes} + +É possível adicionar atributos às métricas no momento em que são geradas: + +```js +const counter = myMeter.createCounter('my.counter'); + +counter.add(1, { 'algum.atributo.opcional': 'algum valor' }); +``` + +### Configurar Metric Views {#configure-metric-views} + +Uma Metric View fornece aos desenvolvedores a capacidade de personalizar +métricas expostas pelo SDK de Métricas. + +#### Seletores {#selectors} + +Para instanciar uma _view_, primeiro é necessário selecionar o instrumento de +destino. Os seguintes seletores são válidos para métricas: + +- `instrumentType` +- `instrumentName` +- `meterName` +- `meterVersion` +- `meterSchemaUrl` + +A seleção por `instrumentName` (do tipo _string_) oferece suporte a curingas +(_wildcards_), permitindo, por exemplo, selecionar todos os instrumentos com `*` +ou todos aqueles cujo nome começa com `http`, utilizando `http*`. + +#### Exemplos {#examples} + +Filtrar atributos em todos os tipos de métrica: + +```js +const limitAttributesView = { + // exporta apenas o atributo 'environment' + attributeKeys: ['environment'], + // aplica a view a todos os instrumentos + instrumentName: '*', +}; +``` + +Descartar todos os instrumentos cujo nome do `Meter` seja `pubsub`: + +```js +const dropView = { + aggregation: { type: AggregationType.DROP }, + meterName: 'pubsub', +}; +``` + +Definir intervalos de _buckets_ explícitos para o Histograma chamado +`http.server.duration`: + +```js +const histogramView = { + aggregation: { + type: AggregationType.EXPLICIT_BUCKET_HISTOGRAM, + options: { boundaries: [0, 1, 5, 10, 15, 20, 25, 30] }, + }, + instrumentName: 'http.server.duration', + instrumentType: InstrumentType.HISTOGRAM, +}; +``` + +#### Anexar ao Meter Provider {#attach-to-meter-provider} + +Depois de configuradas as _views_, é preciso anexá-las ao Meter Provider +correspondente: + +```js +const meterProvider = new MeterProvider({ + views: [limitAttributesView, dropView, histogramView], +}); +``` + +## Logs + +As APIs e SDKs de logs estão atualmente em desenvolvimento. + +## Próximos passos {#next-steps} + +Também é necessário configurar um exportador apropriado para +[exportar seus dados de telemetria](/docs/languages/js/exporters) para um ou +mais _backends_ de observabilidade.