open-telemetry / opentelemetry-js

OpenTelemetry JavaScript Client
https://opentelemetry.io
Apache License 2.0
2.67k stars 770 forks source link

Real User Monitoring (RUM) with Otel metrics #3500

Closed hxtpoe closed 1 year ago

hxtpoe commented 1 year ago

I need to collect browser metrics that represent user experience. There are Web Vitals signals that I wanted to use. This is pretty easy to collect them on the user side. My first idea was to use https://github.com/weaveworks/prom-aggregation-gateway That works fine, but the prom-aggregation-gateway is not maintained, and the official Docker image contains some security vulnerabilities. So I need to build it on my own.

I gave a chance to use Otel because metrics feature is now stable in opentelemetry-js. The following code demonstrates Otel usage. There is also a opentelementry-collector configured that has otlp-http receiver and prometheusremotewrite exporter. All those pieces work fine but I feel it produces invalid results.

I don't feel that the concept is correct. Shouldn't I create a simple web-exporter on server side that maintains the histogram and on every request it does myHistogram.record(value). It means that I send only metrics values to the server and client is not aware of Otel. WebVitals returns values once at page load, so having metricsReader that exports metrics in constant interval makes no sense. I thought about shooting down the reader after exporting values.

Is open-telemetry-collector able to deal with many concurrent histograms from multiple browser sessions? Aggregation is needed to avoid high cardinality.

import { getTTFB, Metric } from "web-vitals";
import { metrics } from "@opentelemetry/api";
import { OTLPMetricExporter } from "@opentelemetry/exporter-metrics-otlp-http";
import {
  MeterProvider,
  PeriodicExportingMetricReader,
  View,
  ExplicitBucketHistogramAggregation,
} from "@opentelemetry/sdk-metrics";

const TTFB = "web_ttfb";
const meterName = "webvitals.meter";
const exportIntervalMillis = 5000;

const meterProvider = new MeterProvider({
  views: [
    new View({
      aggregation: new ExplicitBucketHistogramAggregation([
        0, 5, 10, 15, 25, 35, 50, 75, 100, 150, 200,
      ]),
      instrumentName: TTFB,
      meterName: meterName,
    }),
  ],
});

metrics.setGlobalMeterProvider(meterProvider);
meterProvider.addMetricReader(
  new PeriodicExportingMetricReader({
    exporter: new OTLPMetricExporter({
      url: "http://localhost/o11y/metrics/v1/metrics",
    }),
    exportIntervalMillis: exportIntervalMillis,
  })
);

const meter = meterProvider.getMeter(meterName);

const ttfbHistogram = meter.createHistogram(TTFB, {
  description: "Time to First Byte (TTFB)",
  unit: "ms",
});

const attributes = {
  environment: "dev",
  path: window.location.pathname,
};

getTTFB(({ delta }: Metric) => {
  ttfbHistogram.record(Math.floor(delta), attributes);
});
github-actions[bot] commented 1 year ago

This issue is stale because it has been open 60 days with no activity. Remove stale label or comment or this will be closed in 14 days.

github-actions[bot] commented 1 year ago

This issue was closed because it has been stale for 14 days with no activity.