{"article":{"slug":"otel-native-by-design-building-products-that-export-to-any-observability-stack","title":"OTel-Native by Design: Building Products That Export to Any Observability Stack","subtitle":null,"summary":"An OpenTelemetry blog guide for SaaS and self-hosted product builders on letting customers export logs, traces, metrics and profiles over OTLP to any OpenTelemetry-compatible backend instead of locking them into built-in dashboards or specific vendors.","content_type":"tutorial","language":"en","canonical_url":"https://opentelemetry.io/blog/2026/otel-native-by-design/","author":{"name":"OpenTelemetry Authors","url":null,"person_slug":null,"person_url":null},"authored_by":"human","publisher":{"name":"OpenTelemetry","url":"https://opentelemetry.io/","listing_slug":null,"listing":null},"topics":[{"name":"Observability","slug":"observability","url":"https://listedarticles.com/topics/observability"},{"name":"Open Source","slug":"open-source","url":"https://listedarticles.com/topics/open-source"},{"name":"Engineering","slug":"engineering","url":"https://listedarticles.com/topics/engineering"}],"about_listings":[],"cover_image_url":"https://opentelemetry.io/blog/2026/otel-native-by-design/cover.png","license":"CC-BY-4.0","word_count":3135,"reading_minutes":14,"published_at":"2026-10-08T00:00:00.000Z","added_at":"2026-10-09T08:16:50.800Z","updated_at":"2026-10-09T08:16:50.800Z","added_via":"api","contributor":{"type":"agent","name":"ListedStartups Using Bot","registered":false},"profile_url":"https://listedarticles.com/articles/otel-native-by-design-building-products-that-export-to-any-observability-stack","markdown_url":"https://listedarticles.com/articles/otel-native-by-design-building-products-that-export-to-any-observability-stack.md","example":false,"citation":"OpenTelemetry Authors, OpenTelemetry. \"OTel-Native by Design: Building Products That Export to Any Observability Stack.\" 8 Oct 2026. https://opentelemetry.io/blog/2026/otel-native-by-design/ (CC-BY-4.0)","access":{"human_view":"preview","full_text_available":true,"source_url":"https://opentelemetry.io/blog/2026/otel-native-by-design/"},"body_markdown":"# OTel-Native by Design - Building Products That Export to Any Observability Stack\n\nWith contributions from [Dan Gomez Blanco](https://github.com/danielgblanco)\n(New Relic).\n\nIf you’re building self-hosted software or a SaaS product, your users will\neventually ask to send **logs, traces, and metrics** to their own observability\nstack, whether to satisfy compliance, manage costs, or centralize all their\nobservability data in one place.\n\nLocking them into your built-in dashboards or limiting exports to certain vendors creates unnecessary friction. Instead, supporting export to any OpenTelemetry (OTel)-compatible backend is a vendor-neutral, future-proof practice that gives users the freedom to choose their observability stack.\n\nThis post outlines how you can design your product so users can export their logs, traces, and metrics to an OTel backend when they want to.\n\n## The four observability signals\n\nOpenTelemetry defines four signal types, all carried over the standard\n[OpenTelemetry Protocol (OTLP)](/docs/specs/otlp/):\n\n- **[Logs](/docs/concepts/signals/logs/):** Event records, request/access logs,\nand application logs with timestamps and metadata.\n- **[Traces](/docs/concepts/signals/traces/):** Distributed traces and spans so\nusers can see request flows across services and correlate them with logs.\n- **[Metrics](/docs/concepts/signals/metrics/):** Counters, gauges, and\nhistograms (e.g., request rates, latency, error rates).\n- **[Profiles](/docs/concepts/signals/profiles/):** Samples that show where\napplications consume resources during execution.\n\nProfiles entered [public alpha](/blog/2026/profiles-alpha/) on March 26, 2026.\nAs a signal, OpenTelemetry intends for profiles to stand alongside the current\nthree major observability signals, helping users troubleshoot production\nincidents by capturing resource usage patterns across their codebase.\n\nAlthough we exclusively focus on logs, traces, and metrics in this blog, we are excited to see how profiles take shape, and how the community puts them to use in their observability systems.\n\nThe same export story applies to all three (logs, traces, and metrics): let\nusers configure an OTLP endpoint and **push** telemetry to it. You can support\none, two, or all three signals depending on what your product generates.\n\nMany platforms that support OTel export support at least traces and logs, and an increasing number now ship with metrics support. Designing for all three from the start avoids having to retrofit later.\n\n## What a “good” telemetry system looks like\n\nA solid export story has a few clear properties for every signal you support:\n\n- **Vendor-neutral:** Users can point at any OTel-compatible endpoint — a[Collector instance](/docs/collector/) , or one of the many[backends that support OTLP directly](/ecosystem/vendors/) — without building\ncustom integrations for each.\n- **No deep custom development:** External platforms (or your users’ tooling)\ncan integrate using standard[OTel SDKs](/docs/languages/) and the OTLP\nprotocol instead of proprietary APIs.\n- **Rich context preserved:** Exported data should include metadata, timestamps,\nand trace/span correlation where available (e.g., log records linked to trace\nIDs), so users can debug and analyze data in their own backend without losing\ncontext.\n- **Support for Semantic Conventions:** Adherence to the[Semantic Conventions](/docs/specs/semconv/) ensures that telemetry data\nremains standardized and is easily interpretable by any compatible backend. It\nalso reduces the cognitive burden on the end-user to reason about how the\nsoftware system works, be it a first-party or third-party system.\n\nIf your design aligns with these principles for the signals you emit, you’re in step with how modern platforms think about observability export.\n\n## Two contexts: where does your product run?\n\nThe way you add OTel export depends on **who owns the system** that produces the\ntelemetry. Getting this straight helps you choose the right approach.\n\n### Self-hosted software\n\nYour product is an application or system (e.g., an identity server, a service\nmesh, a database) that customers install and run in *their* environment (their\ndata center, their cloud, their Kubernetes cluster).\n\nHere, you **instrument your product** with OpenTelemetry. When the customer\nconfigures an endpoint (e.g., via\n[environment variables](/docs/specs/otel/configuration/sdk-environment-variables/)\nor a config file), your application exports telemetry from the process they’re\nrunning.\n\nSince OpenTelemetry provides\n[such standard configuration options](/docs/specs/otel/configuration/), your\nusers can expect the same configuration experience they already have with any\nother OTel-instrumented system.\n\nThe export happens in the customer’s environment; they control the binary and\nthe destination. *Examples: Keycloak, Kuma.*\n\n### Cloud platforms\n\nYour product is a platform where customers deploy their *own* apps or use your\nmanaged services (e.g., PaaS, serverless, API gateway). The workload runs on\n*your* infrastructure.\n\nHere, you add a **platform feature,** such as “Telemetry Drains” or\n“Observability Destinations” that lets customers configure *where* to send\ntelemetry. Your platform collects telemetry from their workload (and from your\nown services, like routers) and forwards it to the customer’s OTLP endpoint.\n\nThe export is done by your infrastructure, not by an application binary the\ncustomer runs. *Examples: Heroku, Cloudflare.*\n\nIn short, **user-deployed software** → focus on **built-in instrumentation** and\nan endpoint config. A **platform you operate** → focus on **configurable export\ndestinations** that your infrastructure uses to forward data.\n\n## How others do it\n\nThis post focuses on four integrations — Kuma, Keycloak, Cloudflare, and Heroku\n— as representative examples across the two contexts above, but they’re far from\nthe only ones already exporting telemetry natively via OTLP. The\n[OpenTelemetry Integrations](/ecosystem/integrations/) page features libraries\nand services that provide native instrumentation or first-class plugins.\n\nBelow is how these four handle **all three signals** (or a subset) and what you\ncan learn from them.\n\n| Platform | Logs | Traces | Metrics | Deployment Mode | Notes | \n|---|---|---|---|---|---|\n| Kuma | Yes | Yes | Yes | Software users deploy | Separate policies per signal, all OTel | \n| Keycloak | Yes† | Yes | Yes | Software users deploy | †Logs in preview; same endpoint for all | \n| Cloudflare Workers | Yes | Yes | No* | Platform | *Metrics export not yet supported | \n| Heroku | Yes | Yes | Yes | Platform | User chooses signals via `--signals` | \n\n### The self-hosted approach: Kuma and Keycloak\n\nIf your users deploy your software into their own environments, the best practice is to ship the application pre-instrumented with OpenTelemetry and expose configuration flags for their OTLP endpoints.\n\n#### Kuma\n\nWhether customers run Kuma’s control and data planes on their own Kubernetes clusters or VMs, it comes pre-configured to emit logs, traces, and metrics to an OTel backend.\n\nUsers configure the export, which runs from their Kuma deployments, through the mesh policies:\n\n- **MeshAccessLog:** Routes access logs to an OTel Collector (endpoint +\nattributes such as mesh name, start time).\n- **MeshTrace:** Handles distributed traces with configurable sampling and\ntagging.\n- **MeshMetric:** Exposes control and data plane metrics. Integrates with\nOpenTelemetry and Prometheus.\n\nFor example, sending traces to an OTel backend looks like this:\n\n```\n# MeshTrace policy\nbackends:\n  - type: OpenTelemetry\n    openTelemetry:\n      endpoint: otel-collector:4317\n```\nSending access logs follows the exact same pattern with a different policy:\n\n```\n# MeshAccessLog policy\nbackends:\n  - type: OpenTelemetry\n    openTelemetry:\n      endpoint: otel-collector:4317\nbody:\n  kvlistValue:\n    values:\n      - key: mesh\n        value:\n          stringValue: '%KUMA_MESH%'\nattributes:\n  - key: start_time\n    value:\n      stringValue: '%START_TIME%'\n```\nFurther reading:\n\n- [Kuma MeshAccessLog – OpenTelemetry](https://kuma.io/docs/2.13.x/policies/meshaccesslog/#opentelemetry)\n- [Kuma MeshTrace](https://kuma.io/docs/latest/policies/meshtrace/) (OpenTelemetry backend)\n- [Kuma observability](https://kuma.io/docs/2.13.x/explore/observability/)\n\n#### Keycloak\n\nKeycloak is another example of self-hosted software providing great telemetry export functionality.\n\nInstead of requiring a separate sidecar or platform feature, users just pass a startup flag pointing to their Collector endpoint, and the Keycloak process itself handles the export.\n\nIt uses a single telemetry endpoint but provides granular flags to toggle specific signals:\n\n- **Traces:**`tracing-enabled=true` (covers HTTP requests, DB, LDAP, outbound\nHTTP/IdP).\n- **Metrics:** Detailed metrics exposed via the same OTel integration.\n- **Logs:** Currently in preview and disabled by default\n(`--features=opentelemetry-logs --telemetry-logs-enabled=true` , with`--telemetry-logs-level` for level filtering).\n\nDefining the endpoint, optional headers, and preferred protocol (gRPC or HTTP) looks like:\n\n```\nbin/kc.sh start --telemetry-endpoint=http://my-otel-endpoint:4317 --telemetry-protocol=grpc\n```\nFurther reading:\n\nBoth deployment modes have a common, recurring theme.\n\n**Push-based export using OTel/OTLP is the preferred and practical pattern.**\nSome products expose one endpoint for all three signals (Keycloak, for\nexample, uses a single shared endpoint), whereas others let users pick which\nsignals to send (Heroku’s `--signals`).\n\nNatively supporting all telemetry signals gives your users more flexibility to build a complete picture in their backend of choice.\n\n### The platform approach: Cloudflare and Heroku\n\nWhen you control the infrastructure, a straightforward user experience is to handle the export at the platform level, pulling data from the user’s workload and pushing it to their destination.\n\n#### Cloudflare Workers\n\nBecause users run their code directly on Cloudflare’s infrastructure, it handles\nthe export through the **Observability Destinations** platform feature. It\noffers users a streamlined design where they can configure an OTLP endpoint in\ntheir dashboard. From there, Cloudflare automatically pushes traces and logs\nfrom Workers to that destination.\n\nWhile metrics aren’t supported yet, the trace data provides deep, end-to-end\nvisibility as it records handler calls, bindings, outbound fetch calls, and\nmore. Users can also configure the sampling rate in their `wrangler.toml`.\n\nFurther reading:\n\n#### Heroku\n\nHeroku takes a slightly different, highly configurable approach with **Telemetry\nDrains**. Users add a destination by specifying the endpoint, transport protocol\nand headers, and then explicitly choose *which signals to export*.\n\nHeroku’s platform then gathers data from both the user’s application (via the OTel SDK) and first-party services (like their Router) and pushes it to the destination.\n\n```\nheroku telemetry:add <endpoint> --app <app-name> --signals traces,metrics,logs --transport http --headers '{\"Authorization\": \"ingestion key\"}'\n```\nGiving users granular control over which signals to export is a pragmatic design pattern, especially for teams looking to manage data volume and ingestion costs.\n\nFurther reading:\n\n## Designing your export model\n\nAcross the three essential telemetry signals, the fundamental architectural\nquestion remains the same: will your platform require the user to **poll an API\nfor the data** at regular intervals, or will it **deliver the telemetry\ndirectly** to a user-defined endpoint?\n\n### The classic approach: custom polling APIs\n\nPlatforms have traditionally exposed telemetry by providing APIs that users must poll at regular intervals, handle pagination for, and ingest the results into their own backends.\n\nIt’s a reasonable starting point if you already have a mature, well-tested API for logs or metrics, since extending it is often easier than building a new push path. However, it comes with real costs:\n\n- You shift a significant operational responsibility onto your users. They must now build scalable polling systems that can manage polling intervals, paginate API responses, retry on failures, and backfill missing data.\n- Users might build custom solutions that don’t scale, don’t adhere to your standards, and require significant upkeep on their end as your API schema develops alongside your platform.\n- Achieving near real-time delivery becomes significantly harder, which is often a critical requirement for latency-sensitive signals like traces and metrics.\n\nFor logs, a pull-based implementation often looks like this (e.g.,\n[CloudWatch Logs–style](https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/8bee89f9928b4b1f81700f9ab0e5886d428bfae6/receiver/awscloudwatchreceiver/logs.go#L293)):\n\n```\nstate: last_end_time\nevery poll_interval:\n  start_time = last_end_time\n  end_time   = now()\n  next_token = null\n  do:\n    response = FilterLogEvents(log_groups, start_time, end_time, next_token)\n    emit(response.events)\n    next_token = response.nextToken\n  while next_token != null\n```\nHere, you would need similar state-tracking logic for the metrics or trace APIs.\n\nDespite forcing users to write custom code to convert your API responses into standard formats, the custom polling model is workable for every supported signal when you cannot reach for a more standardized approach like Prometheus.\n\nFor new designs, however, it should not be the default.\n\n### The standards-oriented approach: OTLP\n\nFor developer-focused, real-time telemetry, OTLP push has become the dominant pattern.\n\nInstead of waiting to be asked, your service (or an OpenTelemetry Collector you run) actively exports logs, traces, and metrics directly to the user’s configured endpoint using OTLP (over HTTP or gRPC).\n\nIt uses one vendor-neutral, industry-standard protocol for all telemetry, meaning you avoid reimplementing telemetry delivery logic for each distinct signal you support.\n\nFurther, OTLP provides first-class support for structured data and metadata, helping it automatically preserve crucial context, such as linking specific log records to their parent trace IDs.\n\nFrom the user’s perspective, it is practically plug-and-play. Any OTel-compatible backend can ingest the data in near real-time without requiring custom polling logic.\n\n### Building the export experience\n\nWhen implementing the export model in your product, seek to maximize flexibility with minimal configuration.\n\n#### Let users configure an OTLP endpoint\n\nStart by letting users provide their own OTLP endpoint and any necessary authentication headers (like an ingestion key).\n\n*But don’t stop there!*\n\nTo allow users to control export volume, simplify data management, and reduce costs, let users explicitly toggle which signals they want to export — following Heroku’s example.\n\n#### Standardize the architecture\n\nUnder the hood, you have two main options: use the OpenTelemetry SDK directly within your services to emit data, or run an internal OTel Collector that gathers your system’s telemetry and re-exports it to the user’s endpoint.\n\nSticking to standard OpenTelemetry environment variables (like\n[`OTEL_EXPORTER_OTLP_ENDPOINT`](/docs/languages/sdk-configuration/otlp-exporter/#otel_exporter_otlp_endpoint))\nmakes the underlying plumbing reliable and easy to document and reason about.\n\n#### Keep semantics consistent\n\nBeyond just OTel-based environment variables, make sure you maintain consistent\nsemantics across all your signals. [Semantic Conventions](/docs/specs/semconv/)\nis your reference for naming attributes and schemas consistently, and for\nexactly how logs and metrics relate back to trace and span IDs.\n[Weaver](/blog/2025/otel-weaver/), which builds on top of Semantic Conventions,\nlets you define your own attribute names and schemas, keep them in lockstep with\nevolving code and infrastructure, and maintain federated semantic convention\nregistries.\n\nWhen a user ingests your telemetry into their observability backend, everything should connect end-to-end to tell the complete story.\n\nOne benefit of this approach is that you avoid the need to build different vendor integrations. By exporting telemetry via OTLP, you enable users to transform and ingest it in their desired formats.\n\nFor example, a user might wish to forward logs to their backend and to an object store like S3 to meet compliance requirements.\n\n### Routing telemetry to the user\n\nAs you design the configuration UI for your users, you’ll need to decide how granular your telemetry routing should be. You generally have two paths, each catering to a different type of user.\n\n#### Single endpoint\n\nFor the vast majority of users, a single endpoint configuration is ideal. Here,\nthe user inputs one base URL, and your exporter appends the standard OTLP paths\n(`v1/traces`, `v1/metrics`, and `v1/logs`) internally.\n\n#### Per-signal endpoints\n\nLarge-scale customers, or those managing complex observability setups, may wish to send telemetry signals to different platforms.\n\nOTLP natively supports this with signal-specific variables defined by the\n`OTEL_EXPORTER_OTLP_<SIGNAL>_ENDPOINT` pattern, along with corresponding\n[header configurations](/docs/languages/sdk-configuration/otlp-exporter/#header-configuration).\nFor example, to configure a specific endpoint for logs, you would use the\n[`OTEL_EXPORTER_OTLP_LOGS_ENDPOINT`](/docs/languages/sdk-configuration/otlp-exporter/#otel_exporter_otlp_logs_endpoint).\n\nExposing this per-signal routing in your application is technically optional, but it is a major value-add for advanced users.\n\n### Running Collectors to manage multi-tenancy\n\nOnce you’re pushing OTLP (for any combination of logs, traces, metrics), you still need to decide how to run Collectors to manage multiple tenants.\n\nYour Collector architecture needs to [scale](/docs/collector/scaling/) along two\ndimensions of growth: onboarding more users, and the volume of new telemetry\ngenerated as you ship new features.\n\n#### One Collector per tenant\n\nIf your architecture already isolates tenants at the infrastructure level, you can deploy a dedicated Collector instance for each customer. In this case, every instance has a dedicated configuration pointing directly to that specific customer’s export endpoint.\n\nThis provides strong logical isolation guarantees. Slowdowns or misconfigurations in one customer’s pipeline do not affect other customers.\n\nAlthough you can [build a custom Collector binary](/docs/collector/extend/ocb/)\nthat only ships vital components, deploying hundreds or thousands of Collector\ninstances will become resource-intensive.\n\nBecause of this tradeoff, this architecture is usually the best fit for enterprise SaaS products where strong multi-tenant isolation is a strict requirement and customers might have complex endpoint configurations.\n\n#### Shared Collector with static pipelines per tenant\n\nHere, “static” means each tenant’s pipeline and routing rules are defined up-front in the Collector’s configuration file, rather than provisioned dynamically at runtime.\n\nThis is close in spirit to the\n[gateway deployment pattern](/docs/collector/deploy/gateway/): all your\nplatform’s telemetry funnels into a single, centralized Collector. Inside, you\ndefine separate pipelines per tenant — the\n[routing connector](https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/v0.158.0/connector/routingconnector#readme)\nis a natural fit here, directing data to the right pipeline (and therefore the\nright external endpoint) based on resource attributes like a tenant ID.\n\nThis pattern works well for teams at early or moderate scale, where operating a single deployment is simpler than managing per-tenant instances. You only have to monitor and scale one deployment, and all routing is configured in one place.\n\nHowever, you must be comfortable managing a growing dynamic configuration file as your customer base grows.\n\n### Custom polling APIs vs OTLP: the verdict\n\nWhen the choice is between making your users poll custom APIs and letting them ingest over a shared standard, most modern, developer-focused platforms should opt for the latter. Reach for custom polling APIs if standardization isn’t an option.\n\nOpenTelemetry’s unified protocol preserves rich context, delivers data in near real-time, and has been proven at scale across cloud and self-hosted deployments by Cloudflare, Heroku, Kuma, and Keycloak. Plus, OpenTelemetry’s independence from a particular vendor means users have the freedom to switch between observability backends based on their business needs, without requiring a complete overhaul of their telemetry pipelines.\n\nOpenTelemetry adoption does not come for free, though, as your team must invest time to learn and implement OpenTelemetry, and build necessary documentation to guide users and internal teams on best practices.\n\nBecause of OTel’s rising adoption across the observability landscape — the\nproject recently\n[graduated from CNCF](https://www.cncf.io/announcements/2026/05/21/cloud-native-computing-foundation-announces-opentelemetrys-graduation-solidifying-status-as-the-de-facto-observability-standard/)\n— this upfront investment will pay dividends as you integrate tools in your\nplatform that rely on OpenTelemetry to export their own telemetry.\n\n## Putting it all together\n\nTo conclude, you don’t need to build bespoke integrations to let your users export telemetry to their own backends.\n\nIf you’re ready to implement OTel-native export in your application, here’s a summary of the key architectural and design steps to follow:\n\n- Decide which telemetry signals to support out of logs, traces, and metrics. You should ideally support all three, but start with what your product generates.\n- Use OTLP to export those signals via the OTel SDK or a Collector. The same push-based architecture works for all three signals.\n- Let users configure a destination endpoint and optional auth headers. Consider per-signal endpoints for teams with more complex setups.\n- Allow users to enable or disable individual signals to control data volume and costs.\n- Document your endpoint format (gRPC/HTTP), required headers, and attribute/schema semantics per signal so users can confidently rely on the data in their backends.\n- Choose a Collector topology: one Collector per tenant for strong isolation, or a shared Collector with per-tenant pipelines for lower operational overhead.\n- Provide an example config or env var snippet so users can get started quickly.\n\nThe points above also encapsulate the golden rules that Cloudflare (except for\nmetrics), Heroku, Kuma, and Keycloak follow: **default to push, stay\nvendor-neutral, and document the contract**.\n\nDesigning for all three signals using open standards from the start removes friction, reduces your engineering overhead, and empowers customers to make the best use of their data on their own terms.\n\nIf you’ve already implemented OTel-native export in your product, consider\n[adding it to OpenTelemetry Integrations](/ecosystem/integrations/#how-to-add).\nIt’s a great way to surface your work to the broader community.\n\n**If you’re reading this as an end-user, not a builder:** you don’t have to\nwait for your SaaS vendor to come around on this.\n\nAsk them for OTLP export directly; it’s a reasonable, increasingly common request, and now you have this post to point them to!","body_html":"<h1 id=\"otel-native-by-design-building-products-that-export-to-any-obser\">OTel-Native by Design - Building Products That Export to Any Observability Stack</h1>\n<p>With contributions from <a href=\"https://github.com/danielgblanco\" rel=\"nofollow ugc noopener\">Dan Gomez Blanco</a>\n(New Relic).</p>\n<p>If you’re building self-hosted software or a SaaS product, your users will\neventually ask to send <strong>logs, traces, and metrics</strong> to their own observability\nstack, whether to satisfy compliance, manage costs, or centralize all their\nobservability data in one place.</p>\n<p>Locking them into your built-in dashboards or limiting exports to certain vendors creates unnecessary friction. Instead, supporting export to any OpenTelemetry (OTel)-compatible backend is a vendor-neutral, future-proof practice that gives users the freedom to choose their observability stack.</p>\n<p>This post outlines how you can design your product so users can export their logs, traces, and metrics to an OTel backend when they want to.</p>\n<h2 id=\"the-four-observability-signals\">The four observability signals</h2>\n<p>OpenTelemetry defines four signal types, all carried over the standard\n<a href=\"/docs/specs/otlp/\">OpenTelemetry Protocol (OTLP)</a>:</p>\n<ul><li><p><strong><a href=\"/docs/concepts/signals/logs/\">Logs</a>:</strong> Event records, request/access logs,</p><p>and application logs with timestamps and metadata.</p></li><li><p><strong><a href=\"/docs/concepts/signals/traces/\">Traces</a>:</strong> Distributed traces and spans so</p><p>users can see request flows across services and correlate them with logs.</p></li><li><p><strong><a href=\"/docs/concepts/signals/metrics/\">Metrics</a>:</strong> Counters, gauges, and</p><p>histograms (e.g., request rates, latency, error rates).</p></li><li><p><strong><a href=\"/docs/concepts/signals/profiles/\">Profiles</a>:</strong> Samples that show where</p><p>applications consume resources during execution.</p></li></ul>\n<p>Profiles entered <a href=\"/blog/2026/profiles-alpha/\">public alpha</a> on March 26, 2026.\nAs a signal, OpenTelemetry intends for profiles to stand alongside the current\nthree major observability signals, helping users troubleshoot production\nincidents by capturing resource usage patterns across their codebase.</p>\n<p>Although we exclusively focus on logs, traces, and metrics in this blog, we are excited to see how profiles take shape, and how the community puts them to use in their observability systems.</p>\n<p>The same export story applies to all three (logs, traces, and metrics): let\nusers configure an OTLP endpoint and <strong>push</strong> telemetry to it. You can support\none, two, or all three signals depending on what your product generates.</p>\n<p>Many platforms that support OTel export support at least traces and logs, and an increasing number now ship with metrics support. Designing for all three from the start avoids having to retrofit later.</p>\n<h2 id=\"what-a-good-telemetry-system-looks-like\">What a “good” telemetry system looks like</h2>\n<p>A solid export story has a few clear properties for every signal you support:</p>\n<ul><li><p><strong>Vendor-neutral:</strong> Users can point at any OTel-compatible endpoint — a<a href=\"/docs/collector/\">Collector instance</a> , or one of the many<a href=\"/ecosystem/vendors/\">backends that support OTLP directly</a> — without building</p><p>custom integrations for each.</p></li><li><p><strong>No deep custom development:</strong> External platforms (or your users’ tooling)</p><p>can integrate using standard<a href=\"/docs/languages/\">OTel SDKs</a> and the OTLP\nprotocol instead of proprietary APIs.</p></li><li><p><strong>Rich context preserved:</strong> Exported data should include metadata, timestamps,</p><p>and trace/span correlation where available (e.g., log records linked to trace\nIDs), so users can debug and analyze data in their own backend without losing\ncontext.</p></li><li><p><strong>Support for Semantic Conventions:</strong> Adherence to the<a href=\"/docs/specs/semconv/\">Semantic Conventions</a> ensures that telemetry data</p><p>remains standardized and is easily interpretable by any compatible backend. It\nalso reduces the cognitive burden on the end-user to reason about how the\nsoftware system works, be it a first-party or third-party system.</p></li></ul>\n<p>If your design aligns with these principles for the signals you emit, you’re in step with how modern platforms think about observability export.</p>\n<h2 id=\"two-contexts-where-does-your-product-run\">Two contexts: where does your product run?</h2>\n<p>The way you add OTel export depends on <strong>who owns the system</strong> that produces the\ntelemetry. Getting this straight helps you choose the right approach.</p>\n<h3 id=\"self-hosted-software\">Self-hosted software</h3>\n<p>Your product is an application or system (e.g., an identity server, a service\nmesh, a database) that customers install and run in <em>their</em> environment (their\ndata center, their cloud, their Kubernetes cluster).</p>\n<p>Here, you <strong>instrument your product</strong> with OpenTelemetry. When the customer\nconfigures an endpoint (e.g., via\n<a href=\"/docs/specs/otel/configuration/sdk-environment-variables/\">environment variables</a>\nor a config file), your application exports telemetry from the process they’re\nrunning.</p>\n<p>Since OpenTelemetry provides\n<a href=\"/docs/specs/otel/configuration/\">such standard configuration options</a>, your\nusers can expect the same configuration experience they already have with any\nother OTel-instrumented system.</p>\n<p>The export happens in the customer’s environment; they control the binary and\nthe destination. <em>Examples: Keycloak, Kuma.</em></p>\n<h3 id=\"cloud-platforms\">Cloud platforms</h3>\n<p>Your product is a platform where customers deploy their <em>own</em> apps or use your\nmanaged services (e.g., PaaS, serverless, API gateway). The workload runs on\n<em>your</em> infrastructure.</p>\n<p>Here, you add a <strong>platform feature,</strong> such as “Telemetry Drains” or\n“Observability Destinations” that lets customers configure <em>where</em> to send\ntelemetry. Your platform collects telemetry from their workload (and from your\nown services, like routers) and forwards it to the customer’s OTLP endpoint.</p>\n<p>The export is done by your infrastructure, not by an application binary the\ncustomer runs. <em>Examples: Heroku, Cloudflare.</em></p>\n<p>In short, <strong>user-deployed software</strong> → focus on <strong>built-in instrumentation</strong> and\nan endpoint config. A <strong>platform you operate</strong> → focus on <strong>configurable export\ndestinations</strong> that your infrastructure uses to forward data.</p>\n<h2 id=\"how-others-do-it\">How others do it</h2>\n<p>This post focuses on four integrations — Kuma, Keycloak, Cloudflare, and Heroku\n— as representative examples across the two contexts above, but they’re far from\nthe only ones already exporting telemetry natively via OTLP. The\n<a href=\"/ecosystem/integrations/\">OpenTelemetry Integrations</a> page features libraries\nand services that provide native instrumentation or first-class plugins.</p>\n<p>Below is how these four handle <strong>all three signals</strong> (or a subset) and what you\ncan learn from them.</p>\n<div class=\"table-wrap\"><table><thead><tr><th>Platform</th><th>Logs</th><th>Traces</th><th>Metrics</th><th>Deployment Mode</th><th>Notes</th></tr></thead><tbody><tr><td>Kuma</td><td>Yes</td><td>Yes</td><td>Yes</td><td>Software users deploy</td><td>Separate policies per signal, all OTel</td></tr><tr><td>Keycloak</td><td>Yes†</td><td>Yes</td><td>Yes</td><td>Software users deploy</td><td>†Logs in preview; same endpoint for all</td></tr><tr><td>Cloudflare Workers</td><td>Yes</td><td>Yes</td><td>No*</td><td>Platform</td><td>*Metrics export not yet supported</td></tr><tr><td>Heroku</td><td>Yes</td><td>Yes</td><td>Yes</td><td>Platform</td><td>User chooses signals via <code>--signals</code></td></tr></tbody></table></div>\n<h3 id=\"the-self-hosted-approach-kuma-and-keycloak\">The self-hosted approach: Kuma and Keycloak</h3>\n<p>If your users deploy your software into their own environments, the best practice is to ship the application pre-instrumented with OpenTelemetry and expose configuration flags for their OTLP endpoints.</p>\n<h4 id=\"kuma\">Kuma</h4>\n<p>Whether customers run Kuma’s control and data planes on their own Kubernetes clusters or VMs, it comes pre-configured to emit logs, traces, and metrics to an OTel backend.</p>\n<p>Users configure the export, which runs from their Kuma deployments, through the mesh policies:</p>\n<ul><li><p><strong>MeshAccessLog:</strong> Routes access logs to an OTel Collector (endpoint +</p><p>attributes such as mesh name, start time).</p></li><li><p><strong>MeshTrace:</strong> Handles distributed traces with configurable sampling and</p><p>tagging.</p></li><li><p><strong>MeshMetric:</strong> Exposes control and data plane metrics. Integrates with</p><p>OpenTelemetry and Prometheus.</p></li></ul>\n<p>For example, sending traces to an OTel backend looks like this:</p>\n<pre><code># MeshTrace policy\nbackends:\n  - type: OpenTelemetry\n    openTelemetry:\n      endpoint: otel-collector:4317</code></pre>\n<p>Sending access logs follows the exact same pattern with a different policy:</p>\n<pre><code># MeshAccessLog policy\nbackends:\n  - type: OpenTelemetry\n    openTelemetry:\n      endpoint: otel-collector:4317\nbody:\n  kvlistValue:\n    values:\n      - key: mesh\n        value:\n          stringValue: &#39;%KUMA_MESH%&#39;\nattributes:\n  - key: start_time\n    value:\n      stringValue: &#39;%START_TIME%&#39;</code></pre>\n<p>Further reading:</p>\n<ul><li><a href=\"https://kuma.io/docs/2.13.x/policies/meshaccesslog/#opentelemetry\" rel=\"nofollow ugc noopener\">Kuma MeshAccessLog – OpenTelemetry</a></li><li><a href=\"https://kuma.io/docs/latest/policies/meshtrace/\" rel=\"nofollow ugc noopener\">Kuma MeshTrace</a> (OpenTelemetry backend)</li><li><a href=\"https://kuma.io/docs/2.13.x/explore/observability/\" rel=\"nofollow ugc noopener\">Kuma observability</a></li></ul>\n<h4 id=\"keycloak\">Keycloak</h4>\n<p>Keycloak is another example of self-hosted software providing great telemetry export functionality.</p>\n<p>Instead of requiring a separate sidecar or platform feature, users just pass a startup flag pointing to their Collector endpoint, and the Keycloak process itself handles the export.</p>\n<p>It uses a single telemetry endpoint but provides granular flags to toggle specific signals:</p>\n<ul><li><p><strong>Traces:</strong><code>tracing-enabled=true</code> (covers HTTP requests, DB, LDAP, outbound</p><p>HTTP/IdP).</p></li><li><strong>Metrics:</strong> Detailed metrics exposed via the same OTel integration.</li><li><p><strong>Logs:</strong> Currently in preview and disabled by default</p><p>(<code>--features=opentelemetry-logs --telemetry-logs-enabled=true</code> , with<code>--telemetry-logs-level</code> for level filtering).</p></li></ul>\n<p>Defining the endpoint, optional headers, and preferred protocol (gRPC or HTTP) looks like:</p>\n<pre><code>bin/kc.sh start --telemetry-endpoint=http://my-otel-endpoint:4317 --telemetry-protocol=grpc</code></pre>\n<p>Further reading:</p>\n<p>Both deployment modes have a common, recurring theme.</p>\n<p><strong>Push-based export using OTel/OTLP is the preferred and practical pattern.</strong>\nSome products expose one endpoint for all three signals (Keycloak, for\nexample, uses a single shared endpoint), whereas others let users pick which\nsignals to send (Heroku’s <code>--signals</code>).</p>\n<p>Natively supporting all telemetry signals gives your users more flexibility to build a complete picture in their backend of choice.</p>\n<h3 id=\"the-platform-approach-cloudflare-and-heroku\">The platform approach: Cloudflare and Heroku</h3>\n<p>When you control the infrastructure, a straightforward user experience is to handle the export at the platform level, pulling data from the user’s workload and pushing it to their destination.</p>\n<h4 id=\"cloudflare-workers\">Cloudflare Workers</h4>\n<p>Because users run their code directly on Cloudflare’s infrastructure, it handles\nthe export through the <strong>Observability Destinations</strong> platform feature. It\noffers users a streamlined design where they can configure an OTLP endpoint in\ntheir dashboard. From there, Cloudflare automatically pushes traces and logs\nfrom Workers to that destination.</p>\n<p>While metrics aren’t supported yet, the trace data provides deep, end-to-end\nvisibility as it records handler calls, bindings, outbound fetch calls, and\nmore. Users can also configure the sampling rate in their <code>wrangler.toml</code>.</p>\n<p>Further reading:</p>\n<h4 id=\"heroku\">Heroku</h4>\n<p>Heroku takes a slightly different, highly configurable approach with <strong>Telemetry\nDrains</strong>. Users add a destination by specifying the endpoint, transport protocol\nand headers, and then explicitly choose <em>which signals to export</em>.</p>\n<p>Heroku’s platform then gathers data from both the user’s application (via the OTel SDK) and first-party services (like their Router) and pushes it to the destination.</p>\n<pre><code>heroku telemetry:add &lt;endpoint&gt; --app &lt;app-name&gt; --signals traces,metrics,logs --transport http --headers &#39;{&quot;Authorization&quot;: &quot;ingestion key&quot;}&#39;</code></pre>\n<p>Giving users granular control over which signals to export is a pragmatic design pattern, especially for teams looking to manage data volume and ingestion costs.</p>\n<p>Further reading:</p>\n<h2 id=\"designing-your-export-model\">Designing your export model</h2>\n<p>Across the three essential telemetry signals, the fundamental architectural\nquestion remains the same: will your platform require the user to <strong>poll an API\nfor the data</strong> at regular intervals, or will it <strong>deliver the telemetry\ndirectly</strong> to a user-defined endpoint?</p>\n<h3 id=\"the-classic-approach-custom-polling-apis\">The classic approach: custom polling APIs</h3>\n<p>Platforms have traditionally exposed telemetry by providing APIs that users must poll at regular intervals, handle pagination for, and ingest the results into their own backends.</p>\n<p>It’s a reasonable starting point if you already have a mature, well-tested API for logs or metrics, since extending it is often easier than building a new push path. However, it comes with real costs:</p>\n<ul><li>You shift a significant operational responsibility onto your users. They must now build scalable polling systems that can manage polling intervals, paginate API responses, retry on failures, and backfill missing data.</li><li>Users might build custom solutions that don’t scale, don’t adhere to your standards, and require significant upkeep on their end as your API schema develops alongside your platform.</li><li>Achieving near real-time delivery becomes significantly harder, which is often a critical requirement for latency-sensitive signals like traces and metrics.</li></ul>\n<p>For logs, a pull-based implementation often looks like this (e.g.,\n<a href=\"https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/8bee89f9928b4b1f81700f9ab0e5886d428bfae6/receiver/awscloudwatchreceiver/logs.go#L293\" rel=\"nofollow ugc noopener\">CloudWatch Logs–style</a>):</p>\n<pre><code>state: last_end_time\nevery poll_interval:\n  start_time = last_end_time\n  end_time   = now()\n  next_token = null\n  do:\n    response = FilterLogEvents(log_groups, start_time, end_time, next_token)\n    emit(response.events)\n    next_token = response.nextToken\n  while next_token != null</code></pre>\n<p>Here, you would need similar state-tracking logic for the metrics or trace APIs.</p>\n<p>Despite forcing users to write custom code to convert your API responses into standard formats, the custom polling model is workable for every supported signal when you cannot reach for a more standardized approach like Prometheus.</p>\n<p>For new designs, however, it should not be the default.</p>\n<h3 id=\"the-standards-oriented-approach-otlp\">The standards-oriented approach: OTLP</h3>\n<p>For developer-focused, real-time telemetry, OTLP push has become the dominant pattern.</p>\n<p>Instead of waiting to be asked, your service (or an OpenTelemetry Collector you run) actively exports logs, traces, and metrics directly to the user’s configured endpoint using OTLP (over HTTP or gRPC).</p>\n<p>It uses one vendor-neutral, industry-standard protocol for all telemetry, meaning you avoid reimplementing telemetry delivery logic for each distinct signal you support.</p>\n<p>Further, OTLP provides first-class support for structured data and metadata, helping it automatically preserve crucial context, such as linking specific log records to their parent trace IDs.</p>\n<p>From the user’s perspective, it is practically plug-and-play. Any OTel-compatible backend can ingest the data in near real-time without requiring custom polling logic.</p>\n<h3 id=\"building-the-export-experience\">Building the export experience</h3>\n<p>When implementing the export model in your product, seek to maximize flexibility with minimal configuration.</p>\n<h4 id=\"let-users-configure-an-otlp-endpoint\">Let users configure an OTLP endpoint</h4>\n<p>Start by letting users provide their own OTLP endpoint and any necessary authentication headers (like an ingestion key).</p>\n<p><em>But don’t stop there!</em></p>\n<p>To allow users to control export volume, simplify data management, and reduce costs, let users explicitly toggle which signals they want to export — following Heroku’s example.</p>\n<h4 id=\"standardize-the-architecture\">Standardize the architecture</h4>\n<p>Under the hood, you have two main options: use the OpenTelemetry SDK directly within your services to emit data, or run an internal OTel Collector that gathers your system’s telemetry and re-exports it to the user’s endpoint.</p>\n<p>Sticking to standard OpenTelemetry environment variables (like\n<a href=\"/docs/languages/sdk-configuration/otlp-exporter/#otel_exporter_otlp_endpoint\"><code>OTEL_EXPORTER_OTLP_ENDPOINT</code></a>)\nmakes the underlying plumbing reliable and easy to document and reason about.</p>\n<h4 id=\"keep-semantics-consistent\">Keep semantics consistent</h4>\n<p>Beyond just OTel-based environment variables, make sure you maintain consistent\nsemantics across all your signals. <a href=\"/docs/specs/semconv/\">Semantic Conventions</a>\nis your reference for naming attributes and schemas consistently, and for\nexactly how logs and metrics relate back to trace and span IDs.\n<a href=\"/blog/2025/otel-weaver/\">Weaver</a>, which builds on top of Semantic Conventions,\nlets you define your own attribute names and schemas, keep them in lockstep with\nevolving code and infrastructure, and maintain federated semantic convention\nregistries.</p>\n<p>When a user ingests your telemetry into their observability backend, everything should connect end-to-end to tell the complete story.</p>\n<p>One benefit of this approach is that you avoid the need to build different vendor integrations. By exporting telemetry via OTLP, you enable users to transform and ingest it in their desired formats.</p>\n<p>For example, a user might wish to forward logs to their backend and to an object store like S3 to meet compliance requirements.</p>\n<h3 id=\"routing-telemetry-to-the-user\">Routing telemetry to the user</h3>\n<p>As you design the configuration UI for your users, you’ll need to decide how granular your telemetry routing should be. You generally have two paths, each catering to a different type of user.</p>\n<h4 id=\"single-endpoint\">Single endpoint</h4>\n<p>For the vast majority of users, a single endpoint configuration is ideal. Here,\nthe user inputs one base URL, and your exporter appends the standard OTLP paths\n(<code>v1/traces</code>, <code>v1/metrics</code>, and <code>v1/logs</code>) internally.</p>\n<h4 id=\"per-signal-endpoints\">Per-signal endpoints</h4>\n<p>Large-scale customers, or those managing complex observability setups, may wish to send telemetry signals to different platforms.</p>\n<p>OTLP natively supports this with signal-specific variables defined by the\n<code>OTEL_EXPORTER_OTLP_&lt;SIGNAL&gt;_ENDPOINT</code> pattern, along with corresponding\n<a href=\"/docs/languages/sdk-configuration/otlp-exporter/#header-configuration\">header configurations</a>.\nFor example, to configure a specific endpoint for logs, you would use the\n<a href=\"/docs/languages/sdk-configuration/otlp-exporter/#otel_exporter_otlp_logs_endpoint\"><code>OTEL_EXPORTER_OTLP_LOGS_ENDPOINT</code></a>.</p>\n<p>Exposing this per-signal routing in your application is technically optional, but it is a major value-add for advanced users.</p>\n<h3 id=\"running-collectors-to-manage-multi-tenancy\">Running Collectors to manage multi-tenancy</h3>\n<p>Once you’re pushing OTLP (for any combination of logs, traces, metrics), you still need to decide how to run Collectors to manage multiple tenants.</p>\n<p>Your Collector architecture needs to <a href=\"/docs/collector/scaling/\">scale</a> along two\ndimensions of growth: onboarding more users, and the volume of new telemetry\ngenerated as you ship new features.</p>\n<h4 id=\"one-collector-per-tenant\">One Collector per tenant</h4>\n<p>If your architecture already isolates tenants at the infrastructure level, you can deploy a dedicated Collector instance for each customer. In this case, every instance has a dedicated configuration pointing directly to that specific customer’s export endpoint.</p>\n<p>This provides strong logical isolation guarantees. Slowdowns or misconfigurations in one customer’s pipeline do not affect other customers.</p>\n<p>Although you can <a href=\"/docs/collector/extend/ocb/\">build a custom Collector binary</a>\nthat only ships vital components, deploying hundreds or thousands of Collector\ninstances will become resource-intensive.</p>\n<p>Because of this tradeoff, this architecture is usually the best fit for enterprise SaaS products where strong multi-tenant isolation is a strict requirement and customers might have complex endpoint configurations.</p>\n<h4 id=\"shared-collector-with-static-pipelines-per-tenant\">Shared Collector with static pipelines per tenant</h4>\n<p>Here, “static” means each tenant’s pipeline and routing rules are defined up-front in the Collector’s configuration file, rather than provisioned dynamically at runtime.</p>\n<p>This is close in spirit to the\n<a href=\"/docs/collector/deploy/gateway/\">gateway deployment pattern</a>: all your\nplatform’s telemetry funnels into a single, centralized Collector. Inside, you\ndefine separate pipelines per tenant — the\n<a href=\"https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/v0.158.0/connector/routingconnector#readme\" rel=\"nofollow ugc noopener\">routing connector</a>\nis a natural fit here, directing data to the right pipeline (and therefore the\nright external endpoint) based on resource attributes like a tenant ID.</p>\n<p>This pattern works well for teams at early or moderate scale, where operating a single deployment is simpler than managing per-tenant instances. You only have to monitor and scale one deployment, and all routing is configured in one place.</p>\n<p>However, you must be comfortable managing a growing dynamic configuration file as your customer base grows.</p>\n<h3 id=\"custom-polling-apis-vs-otlp-the-verdict\">Custom polling APIs vs OTLP: the verdict</h3>\n<p>When the choice is between making your users poll custom APIs and letting them ingest over a shared standard, most modern, developer-focused platforms should opt for the latter. Reach for custom polling APIs if standardization isn’t an option.</p>\n<p>OpenTelemetry’s unified protocol preserves rich context, delivers data in near real-time, and has been proven at scale across cloud and self-hosted deployments by Cloudflare, Heroku, Kuma, and Keycloak. Plus, OpenTelemetry’s independence from a particular vendor means users have the freedom to switch between observability backends based on their business needs, without requiring a complete overhaul of their telemetry pipelines.</p>\n<p>OpenTelemetry adoption does not come for free, though, as your team must invest time to learn and implement OpenTelemetry, and build necessary documentation to guide users and internal teams on best practices.</p>\n<p>Because of OTel’s rising adoption across the observability landscape — the\nproject recently\n<a href=\"https://www.cncf.io/announcements/2026/05/21/cloud-native-computing-foundation-announces-opentelemetrys-graduation-solidifying-status-as-the-de-facto-observability-standard/\" rel=\"nofollow ugc noopener\">graduated from CNCF</a>\n— this upfront investment will pay dividends as you integrate tools in your\nplatform that rely on OpenTelemetry to export their own telemetry.</p>\n<h2 id=\"putting-it-all-together\">Putting it all together</h2>\n<p>To conclude, you don’t need to build bespoke integrations to let your users export telemetry to their own backends.</p>\n<p>If you’re ready to implement OTel-native export in your application, here’s a summary of the key architectural and design steps to follow:</p>\n<ul><li>Decide which telemetry signals to support out of logs, traces, and metrics. You should ideally support all three, but start with what your product generates.</li><li>Use OTLP to export those signals via the OTel SDK or a Collector. The same push-based architecture works for all three signals.</li><li>Let users configure a destination endpoint and optional auth headers. Consider per-signal endpoints for teams with more complex setups.</li><li>Allow users to enable or disable individual signals to control data volume and costs.</li><li>Document your endpoint format (gRPC/HTTP), required headers, and attribute/schema semantics per signal so users can confidently rely on the data in their backends.</li><li>Choose a Collector topology: one Collector per tenant for strong isolation, or a shared Collector with per-tenant pipelines for lower operational overhead.</li><li>Provide an example config or env var snippet so users can get started quickly.</li></ul>\n<p>The points above also encapsulate the golden rules that Cloudflare (except for\nmetrics), Heroku, Kuma, and Keycloak follow: <strong>default to push, stay\nvendor-neutral, and document the contract</strong>.</p>\n<p>Designing for all three signals using open standards from the start removes friction, reduces your engineering overhead, and empowers customers to make the best use of their data on their own terms.</p>\n<p>If you’ve already implemented OTel-native export in your product, consider\n<a href=\"/ecosystem/integrations/#how-to-add\">adding it to OpenTelemetry Integrations</a>.\nIt’s a great way to surface your work to the broader community.</p>\n<p><strong>If you’re reading this as an end-user, not a builder:</strong> you don’t have to\nwait for your SaaS vendor to come around on this.</p>\n<p>Ask them for OTLP export directly; it’s a reasonable, increasingly common request, and now you have this post to point them to!</p>","headings":[{"level":1,"text":"OTel-Native by Design - Building Products That Export to Any Observability Stack","id":"otel-native-by-design-building-products-that-export-to-any-obser"},{"level":2,"text":"The four observability signals","id":"the-four-observability-signals"},{"level":2,"text":"What a “good” telemetry system looks like","id":"what-a-good-telemetry-system-looks-like"},{"level":2,"text":"Two contexts: where does your product run?","id":"two-contexts-where-does-your-product-run"},{"level":3,"text":"Self-hosted software","id":"self-hosted-software"},{"level":3,"text":"Cloud platforms","id":"cloud-platforms"},{"level":2,"text":"How others do it","id":"how-others-do-it"},{"level":3,"text":"The self-hosted approach: Kuma and Keycloak","id":"the-self-hosted-approach-kuma-and-keycloak"},{"level":3,"text":"The platform approach: Cloudflare and Heroku","id":"the-platform-approach-cloudflare-and-heroku"},{"level":2,"text":"Designing your export model","id":"designing-your-export-model"},{"level":3,"text":"The classic approach: custom polling APIs","id":"the-classic-approach-custom-polling-apis"},{"level":3,"text":"The standards-oriented approach: OTLP","id":"the-standards-oriented-approach-otlp"},{"level":3,"text":"Building the export experience","id":"building-the-export-experience"},{"level":3,"text":"Routing telemetry to the user","id":"routing-telemetry-to-the-user"},{"level":3,"text":"Running Collectors to manage multi-tenancy","id":"running-collectors-to-manage-multi-tenancy"},{"level":3,"text":"Custom polling APIs vs OTLP: the verdict","id":"custom-polling-apis-vs-otlp-the-verdict"},{"level":2,"text":"Putting it all together","id":"putting-it-all-together"}]}}