1from __future__ import annotations
 2
 3import atexit
 4
 5from opentelemetry import metrics, trace
 6from opentelemetry.exporter.otlp.proto.http.metric_exporter import OTLPMetricExporter
 7from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
 8from opentelemetry.sdk.metrics import MeterProvider
 9from opentelemetry.sdk.metrics.export import PeriodicExportingMetricReader
10from opentelemetry.sdk.resources import Resource
11from opentelemetry.sdk.trace import TracerProvider, sampling
12from opentelemetry.sdk.trace.export import BatchSpanProcessor
13from opentelemetry.semconv.attributes import service_attributes
14
15from plain.packages import PackageConfig, register_config
16from plain.runtime import settings
17
18
19@register_config
20class Config(PackageConfig):
21    package_label = "plaincloud"
22
23    def ready(self) -> None:
24        if not settings.CLOUD_EXPORT_ENABLED or not settings.CLOUD_EXPORT_TOKEN:
25            return
26
27        resource = Resource.create(
28            {
29                service_attributes.SERVICE_NAME: settings.NAME,
30                service_attributes.SERVICE_VERSION: settings.VERSION,
31            }
32        )
33
34        export_url = str(settings.CLOUD_EXPORT_URL).rstrip("/")
35        headers = {"Authorization": f"Bearer {settings.CLOUD_EXPORT_TOKEN}"}
36
37        # Traces
38        current_provider = trace.get_tracer_provider()
39        if current_provider and not isinstance(
40            current_provider, trace.ProxyTracerProvider
41        ):
42            raise RuntimeError(
43                "A tracer provider already exists."
44                " plain.cloud must be listed before plain.observer in INSTALLED_PACKAGES."
45            )
46
47        span_exporter = OTLPSpanExporter(
48            endpoint=f"{export_url}/v1/traces",
49            headers=headers,
50        )
51        sampler = sampling.TraceIdRatioBased(settings.CLOUD_TRACE_SAMPLE_RATE)
52        tracer_provider = TracerProvider(sampler=sampler, resource=resource)
53        tracer_provider.add_span_processor(BatchSpanProcessor(span_exporter))
54        trace.set_tracer_provider(tracer_provider)
55
56        # Metrics
57        metric_exporter = OTLPMetricExporter(
58            endpoint=f"{export_url}/v1/metrics",
59            headers=headers,
60        )
61        reader = PeriodicExportingMetricReader(metric_exporter)
62        meter_provider = MeterProvider(metric_readers=[reader], resource=resource)
63        metrics.set_meter_provider(meter_provider)
64
65        atexit.register(tracer_provider.shutdown)
66        atexit.register(meter_provider.shutdown)