npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@ciag/observability

v0.1.0

Published

Observabilidade padrão CIAg (OpenTelemetry + protocolo Sentry) para NestJS e Angular, com redação de PII (LGPD).

Readme

@ciag/observability

Observabilidade padrão CIAg para NestJS (@ciag/observability/nest) e Angular (@ciag/observability/angular). O app fala só padrões abertos — OpenTelemetry (OTLP) para traces/métricas/logs e protocolo Sentry para erros —, então trocar a plataforma (Grafana ↔ SigNoz, GlitchTip ↔ Sentry) é configuração, nunca código do app.

  • Desligado sem configuração. Sem OTEL_EXPORTER_OTLP_ENDPOINT nada é exportado (e o SDK do OpenTelemetry nem é carregado); sem ERRORS_DSN nenhum erro é enviado (o Sentry nem é carregado). Local = custo zero e zero aviso.
  • Nunca derruba o app. Coletor/GlitchTip fora do ar, URL ou DSN inválidos: a API sobe e responde; só um aviso no log (no máximo 1 por minuto por motivo).
  • PII não sai do app (LGPD) — ver LGPD.

Instalação em 10 minutos

Back (NestJS 11)

npm i @ciag/observability \
  @opentelemetry/api @opentelemetry/api-logs @opentelemetry/core @opentelemetry/resources \
  @opentelemetry/semantic-conventions @opentelemetry/sdk-trace-node @opentelemetry/sdk-trace-base \
  @opentelemetry/sdk-metrics @opentelemetry/sdk-logs @opentelemetry/exporter-trace-otlp-http \
  @opentelemetry/exporter-metrics-otlp-http @opentelemetry/exporter-logs-otlp-http \
  @opentelemetry/instrumentation @opentelemetry/instrumentation-http @opentelemetry/instrumentation-express \
  @opentelemetry/instrumentation-nestjs-core @opentelemetry/instrumentation-mysql2 \
  @prisma/instrumentation@<mesma versão do prisma> @sentry/nestjs pino

(As dependências de cada lado são peerDependencies opcionais: quem usa só o Nest não instala Angular, e vice-versa. As versões testadas estão em devDependencies deste pacote.)

  1. src/instrumentation.ts (arquivo próprio, carregado ANTES do Nest):

    import { initTelemetry } from '@ciag/observability/nest';
    // Padrões do código; OTEL_SERVICE_NAME / APP_RELEASE / DEPLOYMENT_ENVIRONMENT têm prioridade.
    initTelemetry({ serviceName: 'teatime-api', serviceVersion: pkg.version });
  2. Primeira linha do src/main.ts: import './instrumentation'; e o logger JSON no create:

    import './instrumentation';
    import { observabilityLogger } from '@ciag/observability/nest';
    const app = await NestFactory.create(AppModule, { logger: observabilityLogger() });
  3. AppModule: imports: [ObservabilityModule.forRoot(), ...].

  4. CORS (front em outra origem): permita os cabeçalhos de trace e exponha o request id:

    allowedHeaders: [...os atuais, 'traceparent', 'tracestate', 'baggage', 'sentry-trace', 'X-Request-Id'],
    exposedHeaders: ['X-Request-Id'],

Front (Angular 19–22, standalone)

npm i @ciag/observability @sentry/angular
// app.config.ts
provideObservability({
  serviceName: 'teatime-web',
  release: VERSION,
  environment: environment.DEPLOYMENT_ENVIRONMENT, // 'prod' | 'hml' | 'local'
  errorsDsn: environment.ERRORS_DSN,               // '' = desligado
  apiOrigins: [environment.API_URL],               // SÓ estes recebem traceparent
  tracesSampleRate: 0.2,
}),
  • Mensagem de erro de API: errorReference(err) devolve o errorId (ou o X-Request-Id) para mostrar "Código do erro: …".
  • Login: setObservabilityUser(userId); logout: clearObservabilityUser().

Sem DSN, provideObservability devolve uma lista vazia de providers (o app fica idêntico). Com DSN, o SDK do Sentry vem num chunk separado carregado depois do bootstrap — não pesa no JS inicial. Erros anteriores ao carregamento ficam numa fila (até 20) e são enviados quando o SDK chega.

O que cada lado faz

@ciag/observability/nest

| API | Faz | | --- | --- | | initTelemetry(opts?) | OTel: traces (http, express, nestjs-core, mysql2, Prisma), métrica http.server.request.duration por http.route/status, logs por OTLP/HTTP; erros por @sentry/nestjs (release = versão, environment). Shutdown com flush em SIGTERM/SIGINT (só quando algo está ligado). | | ObservabilityModule.forRoot({ userId?, ignorePaths? }) | Middleware de request id (aceita X-Request-Id válido, senão UUID; devolve no cabeçalho); UMA linha de log por requisição (method, route padrão /projects/:id, status, duration_ms, userId, ip respeitando trust proxy, requestId, trace_id); filtro global de exceções. | | observabilityLogger() | LoggerService do Nest sobre pino: todos os Logger existentes ([SharepointService], ExceptionsHandler…) saem em JSON com requestId/trace_id/span_id. | | Filtro de exceções | Delega ao BaseExceptionFilter do próprio Nest (mesmo corpo, mesmo status, mesmo log) e só acrescenta errorId (= request id) ao JSON. 5xx e erros inesperados vão ao GlitchTip com as tags errorId, route, method, status, trace_id. 4xx não são erro; 401/403/429 viram contador ciag.http.server.rejected + breadcrumb. |

Exemplo de linha:

{"level":"info","time":"2026-10-12T18:28:12.327Z","context":"HTTP","requestId":"9c02…","method":"GET","route":"/projects/:id","status":200,"duration_ms":12.4,"ip":"10.0.0.7","userId":"6f1c…","trace_id":"a228…","span_id":"c3c0…","msg":"GET /projects/:id 200 12.4ms"}

@ciag/observability/angular

| API | Faz | | --- | --- | | provideObservability(cfg) | @sentry/angular: ErrorHandler (mantém o console.error padrão), tracing do roteador (nomes de rota parametrizados) e das chamadas HTTP com traceparent só para apiOrigins (tracePropagationTargets com regex exata + propagateTraceparent: true), sem replay, sem sessões, dataCollection restritivo, beforeSend com redação. | | errorReference(err) | errorId do corpo de erro ou X-Request-Id; null se não houver. | | setObservabilityUser(id) / clearObservabilityUser() | Usuário só por id. | | observabilityInterceptor / ObservabilityHttpInterceptor | Breadcrumb HTTP <status> com errorId e URL redigida (já incluído por provideObservability). |

Variáveis

| Variável | Lado | Padrão | Uso | | --- | --- | --- | --- | | OTEL_EXPORTER_OTLP_ENDPOINT | back | vazio = desligado | URL base OTLP/HTTP do coletor (Grafana Alloy), ex. http://alloy:4318. | | OTEL_SERVICE_NAME | back | opção serviceName | service.name (ex. teatime-api). Variáveis têm prioridade sobre as opções do código. | | APP_RELEASE | back | opção serviceVersion, senão npm_package_version | service.version e release do GlitchTip. | | DEPLOYMENT_ENVIRONMENT | back | opção environment, senão local | prod | hml | local (aceita production, staging). | | ERRORS_DSN | back/front | vazio = desligado | DSN do projeto no GlitchTip. No front entra no build (build-arg). | | OTEL_TRACES_SAMPLER_ARG | back | 1 | Fração de traces iniciados no back (os que vêm do front seguem a decisão do front). | | OTEL_TRACES_EXPORTER / OTEL_METRICS_EXPORTER / OTEL_LOGS_EXPORTER | back | otlp | none desliga um sinal (ex.: logs já coletados do stdout pelo Alloy). | | OTEL_METRIC_EXPORT_INTERVAL | back | 60000 | ms entre envios de métricas. | | OTEL_SDK_DISABLED | back | — | true desliga toda a telemetria mesmo com endpoint. | | ERRORS_SAMPLE_RATE | back | 1 | Fração de erros enviados. | | LOG_LEVEL | back | info | fatal…trace. |

Os exportadores também respeitam as variáveis padrão OTEL_EXPORTER_OTLP_HEADERS (ex.: token do coletor).

Convenções CIAg

  • Recurso: service.name, service.version, deployment.environment (prod|hml|local), service.namespace = ciag — iguais em todos os projetos, para dashboards/alertas comuns.
  • Correlação: cabeçalho X-Request-Id em toda resposta; errorId (= request id) no corpo de erro; trace_id/span_id em cada linha de log; W3C traceparent do front para a API.
  • Rotas sempre no padrão do framework (/projects/:id), nunca a URL com ids.
  • /health não gera trace nem linha de log (opção ignorePaths).

LGPD

Nunca saem do app (logs, atributos de span, eventos de erro e breadcrumbs):

  • valores das chaves authorization, cookie, set-cookie, senha, password, newPassword, confirmedNewPassword, currentPassword, token, accessToken, refreshToken, verificationToken, secret, clientSecret, apiKey (qualquer profundidade, sem diferenciar caixa, - ou _);
  • Bearer …/Basic … e JWTs soltos em texto; parâmetros de query sensíveis (?token=);
  • e-mails em texto são mascarados: [email protected] → a***@ciag.org.br;
  • usuário identificado só por id; corpo de requisição não é logado nem enviado; cookies e cabeçalhos HTTP não são coletados pelo Sentry (dataCollection).

Retenção e acesso ficam na plataforma (GlitchTip/Loki/Tempo: 15–30 dias, acesso restrito).

Distribuição

Até a publicação no npm, os apps consomem o tarball versionado no próprio repositório:

npm run build && npm pack            # gera ciag-observability-0.1.0.tgz
cp ciag-observability-0.1.0.tgz <app>/vendor/
# package.json do app: "@ciag/observability": "file:vendor/ciag-observability-0.1.0.tgz"

Funciona no Docker/CI sem credencial de registry. Depois de publicado (npm publish --access public): npm i @ciag/[email protected] --save-exact, apague vendor/ciag-observability-*.tgz e confira que o lockfile resolve para registry.npmjs.org.

Desenvolvimento

npm run check   # typecheck + testes (vitest) + build (tsup) + teste de contrato
  • test/*.test.ts: redação com corpos reais (login, troca de senha), módulo Nest real (supertest), lado Angular sem navegador (injector do Angular em Node).
  • scripts/contract-test.cjs: app Nest real (CommonJS, a partir de dist/) contra receptores em memória de OTLP/HTTP e do protocolo Sentry. Prova recurso, traceparent de entrada, http.route, métrica, log OTLP com o mesmo trace_id, evento de erro com errorId, ausência de PII, endpoint fora do ar, URL/DSN inválidos e modo desligado.

Decisões:

  • Sem NodeSDK (@opentelemetry/sdk-node): os mesmos componentes (TracerProvider, MeterProvider, LoggerProvider, exportadores OTLP/HTTP) montados à mão, carregados só quando há endpoint — o sdk-node puxaria gRPC, Zipkin, Prometheus e Jaeger para dentro de todo app.
  • ESM + CJS no lado Node: apps Nest compilam para CommonJS; require do pacote precisa funcionar sem require(esm).
  • Angular sem decorators (makeEnvironmentProviders, inject(), providers por factory): build com tsup, sem ng-packagr.
  • traceLifecycle: 'static' no front: o @sentry/* 11 envia spans em streaming por padrão, formato que o GlitchTip 6.2.6 aceita (HTTP 200) mas descarta; com static as navegações chegam como transações (conferido no E2E: /projects/project-list/ ; navigation).
  • CSP só com report-uri (sem report-to): o endpoint de segurança do GlitchTip 6.2.6 aceita o formato application/csp-report (201) e recusa o da Reporting API application/reports+json (422); com report-to o Chrome passaria a usar só o formato recusado. O DSN serve de base: <origem>/api/<projeto>/security/?sentry_key=<chave> (aceito pelo GlitchTip e pelo Sentry).
  • Sentry só para erros no back: enableOpenTelemetrySetup: false, sem integrações automáticas de http/express (traces são do OTel; não há instrumentação dupla).