Powrót do strony głównej

OpenTelemetry śledzenie w fintechu od frontendu

Artykuł opisuje wdrożenie OpenTelemetry dla śledzenia end-to-end w systemie fintech. Implementacja CompositeLogger na TypeScript z patchingiem fetch i integracją React. Architektura z Collector, Jaeger i Prometheus przyspiesza analizę incydentów.

Śledzenie OpenTelemetry: doświadczenie zespołu fintech
Advertisement 728x90

Śledzenie end-to-end w fintechu: od frontendu po Collector OpenTelemetry

W rozproszonych systemach fintechowych logi bez traceId nie pozwalają powiązać błędu 500 z konkretną czynnością użytkownika. OpenTelemetry zapewnia śledzenie end-to-end — od jednostronicowej aplikacji (SPA) po serwisy backendowe — dzięki Collectorowi. Implementacja w TypeScriptie z CompositeLogger i nadpisaniem fetch zachowuje hierarchię spanów dla kluczowych procesów biznesowych, np. przelewów pieniężnych.

Kluczowe pojęcia OpenTelemetry dla frontendu

OpenTelemetry standaryzuje zbieranie danych obserwowalności: śladów (traces), zakresów (spans), zdarzeń (events) i atrybutów (attributes).

  • Trace — struktura nadrzędna z unikalnym traceId, służąca do grupowania zakresów.
  • Span — przedział czasowy z określonym początkiem i końcem; obsługuje zagnieżdżanie.
  • Event — zdarzenie z sygnaturą czasową, atrybutami i odniesieniem do konkretnego spana.
  • Attribute — para klucz-wartość umożliwiająca filtrowanie i wyszukiwanie.

Te podstawowe elementy pozwalają zbudować pełną hierarchię — od kliknięcia użytkownika po integracje backendowe.

Google AdInline article slot

Architektura systemu obserwowalności

Frontend przesyła ślady przez mikrousługę Audit do OpenTelemetry Collector. Backendy (produktowe i platformowe) agregują dane w Jaegerze (śledzenie), Prometheusie (metryki) i Kibanie (logi). Grafana zapewnia ich wizualizację.

Zalety tej architektury:

  • Centralne zbieranie danych bez bezpośredniego dostępu frontendu do infrastruktury platformowej.
  • Stopniowe wdrażanie — po jednej mikrousłudze.
  • Obfuskacja danych wrażliwych jest obowiązkowa.

Implementacja na froncie: interfejs Logger

Podstawową abstrakcją jest interfejs Logger, zapewniający spójne logowanie we wszystkich warstwach aplikacji.

Google AdInline article slot
export interface Logger {
  readonly name: string;
  init?(options?: CompositeInitOptions | SentryInitOptions | AuditInitOptions): void;
  logError(error: Error, context?: ErrorContextType | ErrorContextType[]): void;
  logMessage?(message: string, context?: Record<string, unknown>): void;
}

CompositeLogger implementuje wzorzec Kompozytu, przekazując zdarzenia do wielu loggerów jednocześnie (OpenTelemetry, Sentry, Audit).

export class CompositeLogger implements Logger {
  public readonly name = "composite";

  constructor(private readonly loggers: Logger[]) {}

  init(options?: CompositeInitOptions): void {
    this.loggers.forEach((logger) => {
      logger.init?.(options?.[logger.name as keyof CompositeInitOptions]);
    });
  }

  logError(error: Error, context?: ErrorContextType[]): void {
    this.loggers.forEach((logger) => {
      logger.logError(error, context);
    });
  }

  logMessage(message: string, context?: Record<string, unknown>): void {
    this.loggers.forEach((logger) => {
      logger.logMessage?.(message, context);
    });
  }

  startBusinessProcess(name: string, attributes?: Attributes): void {
    this.loggers.forEach((logger) => {
      if (logger.name === "otlp") {
        (logger as OtlpLogger).startBusinessProcess(name, attributes);
      }
    });
  }

  addEvent(name: string, attributes?: Attributes): void {
    this.loggers.forEach((logger) => {
      if (logger.name === "otlp") {
        (logger as OtlpLogger).addEvent(name, attributes);
      }
    });
  }
}

Integracja z Reactem przez kontekst

Komponenty LoggerProvider i hook useLogger zapewniają dostęp do loggera w całym drzewie komponentów.

import React, { createContext, useContext } from "react";
import { CompositeLogger } from "./composite-logger";

const LoggerContext = createContext<CompositeLogger | null>(null);

export const LoggerProvider: React.FC<{
  logger: CompositeLogger;
  children: React.ReactNode;
}> = ({ logger, children }) => (
  <LoggerContext.Provider value={logger}>{children}</LoggerContext.Provider>
);

export const useLogger = (): CompositeLogger => {
  const logger = useContext(LoggerContext);
  if (!logger) {
    throw new Error("Logger nie został znaleziony w kontekście");
  }
  return logger;
};

OtlpLogger: nadpisywanie fetch i StackContextManager

OtlpLogger inicjuje WebTracerProvider oraz nadpisuje metodę fetch, aby zachować kontekst rodzica (parent span). StackContextManager rozwiązuje problem asynchronicznych wywołań w przeglądarce — automatycznie przypisuje nowe spany do odpowiedniego procesu biznesowego, bez konieczności ręcznego przekazywania kontekstu.

Google AdInline article slot

Przykład procesu „przelew pieniężny”:

  • startBusinessProcess('money-transfer').
  • addEvent('form-step-1', {amount: 1000}).
  • Wywołanie fetch do backendu w ramach bieżącego spana.
  • logError przy awarii integracji.

Korzyści z wdrożenia

  • Skrócenie średniego czasu naprawy (MTTR) nawet o kilkadziesiąt razy dzięki filtrowaniu po traceId.
  • Elastyczność: nowe loggery można dodać przez npm bez modyfikacji kodu produktu.
  • Dane strukturalne umożliwiające korelację akcji frontendu i backendu.

Wizualizacja w Jaegerze pokazuje pełną ścieżkę: kliknięcie → żądanie API → Kafka → backend platformowy.

Na co zwrócić uwagę

  • Śledzenie end-to-end łączy logi frontendu i backendu poprzez wspólny traceId.
  • CompositeLogger ukrywa złożoność przed deweloperami i wspiera wiele backendów logowania.
  • Nadpisywanie fetch zapewnia prawidłową hierarchię spanów w wywołaniach asynchronicznych.
  • StackContextManager minimalizuje boilerplate przy śledzeniu po stronie klienta.
  • Obfuskacja danych jest wymagana zgodnie z przepisami regulacyjnymi w sektorze fintech.

— Editorial Team

Advertisement 728x90

Czytaj dalej