<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Blog - Sematext Community</title>
	<atom:link href="https://sematext.com/blog/feed/" rel="self" type="application/rss+xml" />
	<link>https://sematext.com/blog/</link>
	<description>Solr / Elasticsearch Experts - Search &#38; Big Data Analytics</description>
	<lastBuildDate>Tue, 25 Aug 2026 15:17:37 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.9.7</generator>

<image>
	<url>https://sematext.com/wp-content/uploads/2024/12/cropped-ST-favicon-32x32.png</url>
	<title>Blog - Sematext Community</title>
	<link>https://sematext.com/blog/</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>Zero-Code OpenTelemetry for Go: Runtime Instrumentation with OBI vs. Compile-Time Instrumentation with Otelc</title>
		<link>https://sematext.com/blog/zero-code-opentelemetry-for-go-obi-vs-otelc/</link>
		
		<dc:creator><![CDATA[Otis]]></dc:creator>
		<pubDate>Tue, 25 Aug 2026 15:16:08 +0000</pubDate>
				<category><![CDATA[Monitoring]]></category>
		<category><![CDATA[OpenTelemetry]]></category>
		<category><![CDATA[Tracing]]></category>
		<category><![CDATA[application monitoring]]></category>
		<category><![CDATA[golang]]></category>
		<category><![CDATA[opentelemetry]]></category>
		<category><![CDATA[OpenTelemetry instrumentation best practices]]></category>
		<guid isPermaLink="false">https://sematext.com/?p=71029</guid>

					<description><![CDATA[<p>At Sematext we’ve been using Go for probably about a decade. But we didn’t start instrumenting it with OpenTelemetry until earlier this year. Go has historically had a relatively straightforward but hands-on OpenTelemetry instrumentation model: add the OpenTelemetry SDK, initialize it, instrument the libraries you use, and create custom spans where application-specific context matters. That [&#8230;]</p>
<p>The post <a href="https://sematext.com/blog/zero-code-opentelemetry-for-go-obi-vs-otelc/">Zero-Code OpenTelemetry for Go: Runtime Instrumentation with OBI vs. Compile-Time Instrumentation with Otelc</a> appeared first on <a href="https://sematext.com">Sematext</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>At Sematext we’ve been using Go for probably about a decade. But we didn’t start instrumenting it with OpenTelemetry until earlier this year.</p>
<p>Go has historically had a relatively straightforward but hands-on OpenTelemetry instrumentation model: add the OpenTelemetry SDK, initialize it, instrument the libraries you use, and create custom spans where application-specific context matters.</p>
<p>That approach still gives you the most control. But it is no longer the only practical option.</p>
<p>The manual instrumentation approach requires so much work from engineers that we decided to create a general <a href="https://github.com/sematext/sematext-otel-onboarding/tree/main/skills" target="_blank" rel="noopener noreferrer">AI Skill for instrumenting applications with OpenTelemetry SDK</a>, regardless of the runtime/SDK.</p>
<p>Two newer approaches can instrument Go applications with little or no manual source-code instrumentation:</p>
<ul>
<li><b>OpenTelemetry eBPF Instrumentation (OBI)</b> instruments applications at runtime.</li>
<li aria-level="1"><b>OpenTelemetry Go Compile-Time Instrumentation (</b><b>otelc</b><b>) </b>injects instrumentation during the build.</li>
</ul>
<p>Both are part of the OpenTelemetry ecosystem. Both can produce OpenTelemetry telemetry without requiring developers to manually wrap every HTTP handler, database call, gRPC client, or messaging operation.</p>
<p>Both can be described as “zero-code instrumentation”, but they solve the problem at completely different points in the software lifecycle.</p>
<p>OBI asks:</p>
<p><b>How can we observe this application without changing or rebuilding it?</b></p>
<p>Otelc asks:</p>
<p><b>How can we build this application with instrumentation already inside it without manually modifying its source code?</b></p>
<p>This article looks at both approaches from the perspective of a developer, SRE, DevOps engineer, or engineering manager who needs to make a practical decision about instrumenting a Go application.</p>
<h3 id="the-traditional-way-to-instrument-go-with-opentelemetry">The traditional way to instrument Go with OpenTelemetry</h3>
<p>The conventional way to instrument a Go application is to explicitly add OpenTelemetry support to the application using the SDK for Go.</p>
<p>At a high level, that usually means:</p>
<ol>
<li aria-level="1">Adding the <a href="https://opentelemetry.io/docs/languages/go" target="_blank" rel="noopener noreferrer">OpenTelemetry Go API and SDK</a>.</li>
<li aria-level="1">Configuring a TracerProvider.</li>
<li aria-level="1">Configuring exporters.</li>
<li aria-level="1">Instrumenting libraries such as HTTP servers and clients, gRPC, databases, and messaging clients.</li>
<li aria-level="1">Adding custom spans around important application operations.</li>
</ol>
<p>A simplified example might look like this:</p>
<pre>tracer := otel.Tracer("checkout")
ctx, span := tracer.Start(ctx, "reserve_inventory")
defer span.End()
if err := inventory.Reserve(ctx, order); err != nil {
  span.RecordError(err)
  return err
}</pre>
<p>For a more complete example, see our Gin service instrumentation example: <a href="https://github.com/sematext/sematext-otel-onboarding/tree/main/go" target="_blank" rel="noopener noreferrer">https://github.com/sematext/sematext-otel-onboarding/tree/main/go</a></p>
<p>This model has important advantages:</p>
<ul>
<li aria-level="1">You explicitly control where spans start and end.</li>
<li aria-level="1">You can attach application-specific attributes.</li>
<li aria-level="1">You can model important business operations.</li>
<li aria-level="1">You can decide what should and should not become telemetry.</li>
</ul>
<p>The downside is obvious: instrumentation becomes part of the application.</p>
<p>For a sufficiently large system, that can mean touching many services, maintaining instrumentation dependencies, reviewing instrumentation changes, and deciding how deeply each library and operation should be instrumented. This is the type of stuff we faced at Sematext when we said “OK, let’s go and instrument all our Go services now”.</p>
<p>A useful way to think about the landscape is:</p>
<p><img fetchpriority="high" decoding="async" class="alignnone size-full wp-image-71033" src="https://sematext.com/wp-content/uploads/2026/08/01-instrumentation-spectrum.webp" alt="OBI vs OTelc vs Manual OTel instrumentation" width="1600" height="900" srcset="https://sematext.com/wp-content/uploads/2026/08/01-instrumentation-spectrum.webp 1600w, https://sematext.com/wp-content/uploads/2026/08/01-instrumentation-spectrum-300x169.webp 300w, https://sematext.com/wp-content/uploads/2026/08/01-instrumentation-spectrum-1024x576.webp 1024w, https://sematext.com/wp-content/uploads/2026/08/01-instrumentation-spectrum-768x432.webp 768w, https://sematext.com/wp-content/uploads/2026/08/01-instrumentation-spectrum-1536x864.webp 1536w" sizes="(max-width: 1600px) 100vw, 1600px" /></p>
<p>OBI and Otelc occupy different places on that spectrum.</p>
<p>Note that <b>manual instrumentation is far from obsolete</b>, you and your team can still choose this approach. It’s just that the zero-code approaches provide another layer of options. As a matter of fact, as you read this article you will learn that manual instrumentation is still critical in certain situations and complements the auto-instrumentation approaches.</p>
<h3 id="why-zero-code-instrumentation-is-harder-in-go">Why zero-code instrumentation is harder in Go</h3>
<p>Automatic instrumentation is relatively familiar in languages with highly dynamic runtimes.</p>
<p>Java agents can modify or intercept bytecode. Python can wrap functions dynamically. Other runtimes provide mechanisms that make it possible to insert instrumentation after an application has been built.</p>
<p>But Go is different. A Go application is typically compiled into a native binary. Once the binary exists, there is no general-purpose equivalent of loading a Java agent and rewriting the application’s bytecode.</p>
<p>That creates a fundamental choice for automatic instrumentation:</p>
<p><b>Do you instrument the process from outside, or do you modify the application during compilation?</b></p>
<p>OBI takes the first approach and Otelc takes the second.</p>
<p>The architectures look like this.</p>
<h4>OBI: runtime instrumentation</h4>
<p><img decoding="async" class="alignnone size-full wp-image-71034" src="https://sematext.com/wp-content/uploads/2026/08/02-obi-runtime-architecture.webp" alt="OBI Runtime Instrumentation Architecture" width="1600" height="900" srcset="https://sematext.com/wp-content/uploads/2026/08/02-obi-runtime-architecture.webp 1600w, https://sematext.com/wp-content/uploads/2026/08/02-obi-runtime-architecture-300x169.webp 300w, https://sematext.com/wp-content/uploads/2026/08/02-obi-runtime-architecture-1024x576.webp 1024w, https://sematext.com/wp-content/uploads/2026/08/02-obi-runtime-architecture-768x432.webp 768w, https://sematext.com/wp-content/uploads/2026/08/02-obi-runtime-architecture-1536x864.webp 1536w" sizes="(max-width: 1600px) 100vw, 1600px" /></p>
<h4>Otelc: compile-time instrumentation</h4>
<p><img decoding="async" class="alignnone size-full wp-image-71035" src="https://sematext.com/wp-content/uploads/2026/08/03-otelc-build-architecture.webp" alt="OTelc Compile-time Instrumentation Architecture" width="1600" height="900" srcset="https://sematext.com/wp-content/uploads/2026/08/03-otelc-build-architecture.webp 1600w, https://sematext.com/wp-content/uploads/2026/08/03-otelc-build-architecture-300x169.webp 300w, https://sematext.com/wp-content/uploads/2026/08/03-otelc-build-architecture-1024x576.webp 1024w, https://sematext.com/wp-content/uploads/2026/08/03-otelc-build-architecture-768x432.webp 768w, https://sematext.com/wp-content/uploads/2026/08/03-otelc-build-architecture-1536x864.webp 1536w" sizes="(max-width: 1600px) 100vw, 1600px" /></p>
<p>This difference affects deployment, security, portability, ownership, and operational complexity.</p>
<p>Let’s look at each approach in detail.</p>
<h3 id="obi-instrumenting-a-running-go-process">OBI: Instrumenting a running Go process</h3>
<p><b>OpenTelemetry eBPF Instrumentation (OBI)</b> is the OpenTelemetry project’s eBPF-based automatic instrumentation technology.</p>
<p>It was originally based on technology from Grafana Beyla and is now developed as an OpenTelemetry project. OBI’s first OpenTelemetry release was announced in late 2025, and the project has continued to evolve since then.</p>
<p>We are big fans of eBPF at Sematext and have been using it for 10+ years now. When we first started instrumenting our Go applications we chose the OBI approach and included it in <a href="https://github.com/sematext/sematext-otel-onboarding/tree/main/go" target="_blank" rel="noopener noreferrer">our OTel examples for instrumenting Go applications</a>.</p>
<p>The central idea is simple:</p>
<p><b>Run instrumentation outside the application process and observe the application while it runs.</b></p>
<p>Unlike a traditional OpenTelemetry SDK integration, OBI does not require adding OpenTelemetry code to the application binary.</p>
<p>See also: <a href="https://opentelemetry.io/docs/zero-code/obi/setup/" target="_blank" rel="noopener noreferrer">OpenTelemetry eBPF Instrumentation documentation</a></p>
<h4>How OBI works</h4>
<p>OBI uses eBPF to observe application and system behavior. Depending on what is being instrumented, it can capture activity at protocol boundaries and use language-specific techniques, including user-space probes, or <a href="https://sematext.com/ebpf-userland-apps/">uprobes</a>, for supported Go instrumentation.</p>
<p>A simplified flow looks like this:</p>
<pre>         Client
           │
           │ HTTP request
           ▼
┌──────────────────────┐
│      Go service      │
│                      │
│   net/http handler   │◄───── OBI observes
│          │           │       supported operations
│          ▼           │
│     application      │
│       logic          │
│          │           │
│          ▼           │
│    database/sql      │◄───── OBI observes
│                      │
└──────────────────────┘
           │
           │ SQL
           ▼
       PostgreSQL
           │
           ▼
   ┌───────────────┐
   │      OBI      │
   └───────┬───────┘
           │
           ▼
    Trace + metrics</pre>
<p> </p>
<p>OBI can be deployed as:</p>
<ul>
<li aria-level="1">a standalone process,</li>
<li aria-level="1">a Docker container,</li>
<li aria-level="1">a Kubernetes sidecar,</li>
<li aria-level="1">or a Kubernetes DaemonSet.</li>
</ul>
<p>The exact deployment model depends on how broadly you want to instrument the environment.</p>
<h4>What can OBI instrument in Go?</h4>
<p>OBI supports a combination of protocol-level and Go library-level instrumentation.</p>
<p>The current OpenTelemetry documentation lists Go support for technologies including:</p>
<ul>
<li aria-level="1">net/http</li>
<li aria-level="1">HTTP/2</li>
<li aria-level="1">gorilla/mux</li>
<li aria-level="1">Gin</li>
<li aria-level="1">gRPC</li>
<li aria-level="1">database/sql</li>
<li aria-level="1">MySQL drivers</li>
<li aria-level="1">PostgreSQL drivers</li>
<li aria-level="1">Redis</li>
<li aria-level="1">Kafka</li>
<li aria-level="1">Sarama</li>
<li aria-level="1">pgx</li>
</ul>
<p>Compatibility depends on the specific library and version. For example, Go library-level instrumentation is documented for Go 1.17+, while some context propagation capabilities require Go 1.18+.</p>
<p>This is an important practical point. OBI is not simply “watching packets.”</p>
<p>For supported Go libraries, it can use Go-specific instrumentation to capture richer application behavior.</p>
<p>At the same time, it remains fundamentally an <b>out-of-process instrumentation system</b>.</p>
<h4>What using OBI looks like</h4>
<p>Imagine you have an existing Go service, say payment-api, and that it is already running in production.</p>
<p>As such, you may not want to:</p>
<ul>
<li aria-level="1">modify its source code,</li>
<li aria-level="1">change its dependencies,</li>
<li aria-level="1">rebuild it,</li>
<li aria-level="1">or restart it purely to add instrumentation.</li>
</ul>
<p>OBI can be deployed separately and configured to discover or target the application. This is particularly useful for existing workloads.</p>
<p>OBI’s runtime model is one of its strongest characteristics: it can observe applications without making OpenTelemetry instrumentation part of the application’s build artifact. The OpenTelemetry project describes OBI as out-of-process instrumentation that can provide telemetry without application code changes or application restarts in supported scenarios.</p>
<h4>Advantages of OBI</h4>
<h5>1. No source-code changes</h5>
<p>The obvious benefit is that developers do not need to edit the application.</p>
<p>There is no need to:</p>
<pre>git clone --&gt; add instrumentation --&gt; test instrumentation --&gt; commit --&gt; build --&gt; deploy</pre>
<p>Instead, instrumentation can be introduced independently of the application code.</p>
<p>This is particularly attractive when:</p>
<ul>
<li aria-level="1">the application is maintained by another team,</li>
<li aria-level="1">source code is unavailable,</li>
<li aria-level="1">the application is legacy,</li>
<li aria-level="1">you want to instrument an existing fleet,</li>
<li aria-level="1">or the platform team owns observability deployment.</li>
</ul>
<h5>2. No application rebuild</h5>
<p>OBI can instrument supported workloads without requiring you to produce a new binary. This is one of the clearest differences between OBI and Otelc.</p>
<p>Otelc, on the other hand, requires the ability to run the application through an instrumented build process.</p>
<h5>3. Instrumentation can be centrally operated</h5>
<p>OBI is well suited to an infrastructure-oriented operating model. For example, the platform team could choose to deploy OBI as a Kubernetes DaemonSet and have it handle the instrumentation of Go, Java, and Python applications. In other words, the application teams do not necessarily have to independently add and maintain instrumentation.</p>
<p>That can be valuable in organizations with many teams and inconsistent OpenTelemetry adoption.</p>
<h5>4. It is not Go-specific</h5>
<p>OBI can observe applications written in multiple languages. That means an SRE or platform engineering team can use a common instrumentation mechanism across a heterogeneous environment. For organizations operating Go, Java, Python, Node.js, NGINX, and other workloads, that can simplify initial telemetry coverage. This is one of the OBI aspects that we have benefited from at Sematext, as some of our legacy code still uses Java/Kotlin and Node.js.</p>
<h4>The limitations and trade-offs of OBI</h4>
<h5>1. OBI depends on the runtime environment</h5>
<p>OBI is fundamentally a Linux and eBPF-based technology.</p>
<p>That means your deployment environment must support the capabilities OBI needs.</p>
<p>The OpenTelemetry documentation describes OBI as a Linux process that can inspect other running processes and requires elevated privileges or the appropriate Linux capabilities, depending on deployment and configuration.</p>
<p>In containers and Kubernetes, this can become an architectural decision rather than a simple configuration change.</p>
<p>For example, some OBI deployments require or may use:</p>
<ul>
<li aria-level="1">privileged containers,</li>
<li aria-level="1">CAP_SYS_ADMIN,</li>
<li aria-level="1">CAP_PERFMON,</li>
<li aria-level="1">host or shared process namespaces,</li>
<li aria-level="1">access to /proc.</li>
</ul>
<p>The exact requirements depend on the instrumentation and deployment model. Recent kernel security changes can also affect Go instrumentation because OBI uses uprobes for Go-specific instrumentation.</p>
<p>This does not mean OBI is inherently unsuitable for production.It means that the <b>security model must be evaluated by the platform team</b>.</p>
<p>Of course, if your applications don’t run on Linux then OBI is not an option for you at all until other platforms, like Windows, get the needed eBPF support. See <a href="https://github.com/microsoft/ebpf-for-windows" target="_blank" rel="noopener noreferrer">https://github.com/microsoft/ebpf-for-windows</a> for what Microsoft is doing about that for Windows.</p>
<h5>2. Coverage depends on what OBI understands</h5>
<p>OBI can automatically observe supported protocols and libraries, but it does not automatically understand arbitrary application code.</p>
<p>Consider:</p>
<pre>func CalculateEnterpriseDiscount(
    customer Customer,
    contract Contract,
) (Discount, error) {
    // 500 lines of business logic
}</pre>
<p>There is no general way for an external runtime observer to know that this function represents an important business operation. OBI can show what happens around it:</p>
<pre>HTTP request
    │
    ├── database query
    ├── Redis lookup
    ├── gRPC call
    └── Kafka publish</pre>
<p>But it cannot automatically know that CalculateEnterpriseDiscount is an important domain-level operation.</p>
<h5>3. Automatic service names and routes may need tuning</h5>
<p>Because OBI observes applications externally, automatically derived service names, routes, and URLs may not always match how your organization wants to identify services.</p>
<p>The OpenTelemetry documentation specifically calls out route configuration and decoration as something that should be reviewed when generating traces with OBI.</p>
<p>That means you should validate the resulting telemetry rather than assuming that automatic discovery will always produce exactly the naming and cardinality you want.</p>
<h3 id="otelc-instrumenting-go-during-compilation">Otelc: Instrumenting Go during compilation</h3>
<p>The second approach moves instrumentation from runtime to build time.</p>
<p><b>Otelc</b>, the OpenTelemetry Go Compile-Time Instrumentation tool, modifies the Go build process so that supported instrumentation is injected while the application is being compiled.</p>
<p>The resulting application binary contains the instrumentation.</p>
<p>The normal Go build looks like this:</p>
<p>Source code ──► go build ──► Go binary</p>
<p> </p>
<p>While with Otelc it looks like this:</p>
<p>Source code ──► otelc + go build ──► Instrumented Go binary</p>
<p>So if you choose Otelc you will not need to change the application source code, but you will need to alter the build process.</p>
<h4>How Otelc works</h4>
<p>According to the <a href="https://opentelemetry.io/docs/zero-code/go/compile-time/" target="_blank" rel="noopener noreferrer">OpenTelemetry Go compile-time instrumentation documentation</a>, Otelc:</p>
<ol>
<li aria-level="1">Intercepts compilation using the Go toolchain’s -toolexec mechanism.</li>
<li aria-level="1">Matches packages and functions against instrumentation rules.</li>
<li aria-level="1">Injects lightweight hook points.</li>
<li aria-level="1">Links those hooks to OpenTelemetry instrumentation code.</li>
</ol>
<p>The resulting binary contains the instrumentation, so there is no separate runtime instrumentation agent that needs to attach to the process. Operationally, this is simpler because there is no additional moving piece running in production.</p>
<p>Visually things look like this:</p>
<p>Go application (source) ──► Otelc (match rules, inject hooks, link OTel code) ──► Instrumented Go binary ──► OTLP</p>
<p> </p>
<p>The Otelc project uses techniques including <a href="https://en.wikipedia.org/wiki/Trampoline_(computing)" target="_blank" rel="noopener noreferrer">trampoline code injection</a> and function hook mechanisms to connect instrumented functions with OpenTelemetry logic.</p>
<h4>What does using Otelc look like?</h4>
<p>A simple workflow can look like:</p>
<pre>otelc go build -o myapp .</pre>
<p>Alternatively, Otelc can be integrated with the standard Go toolchain using -toolexec.</p>
<p>A documented pattern is:</p>
<pre>otelc setup
export GOFLAGS="${GOFLAGS} '-toolexec=otelc toolexec'"
go build -o myapp .</pre>
<p>This can be useful when a build command is controlled by an existing Makefile, CI system, or another build tool.</p>
<p>Otelc can also be installed as a Go tool dependency in supported Go versions, allowing builds such as:</p>
<pre>go tool otelc go build -o myapp .</pre>
<p>Otelc also supports generating or maintaining instrumentation configuration based on the application’s dependency graph.</p>
<h4>What can Otelc instrument?</h4>
<p>The currently documented set of supported instrumentation includes:</p>
<ul>
<li aria-level="1">net/http</li>
<li aria-level="1">gRPC</li>
<li aria-level="1">database/sql</li>
<li aria-level="1">Gin</li>
<li aria-level="1">Redis</li>
<li aria-level="1">MongoDB</li>
<li aria-level="1">Kubernetes client-go</li>
<li aria-level="1">OpenAI Go SDK</li>
<li aria-level="1">Anthropic Go SDK</li>
<li aria-level="1">Kafka</li>
<li aria-level="1">AWS SDK for Go v2</li>
<li aria-level="1">selected logging libraries for trace/span correlation</li>
</ul>
<p>The supported set will continue to evolve, so it is worth checking the project’s <a href="https://opentelemetry.io/docs/zero-code/go/compile-time/supported-libraries/" target="_blank" rel="noopener noreferrer">current supported-library documentation</a> before choosing it for a specific application.</p>
<h4>Advantages of Otelc</h4>
<h5>1. No manual instrumentation changes</h5>
<p>Developers do not have to manually add instrumentation to every supported library boundary.</p>
<p>The source can remain:</p>
<pre>http.HandleFunc("/checkout", checkoutHandler)</pre>
<p>rather than becoming:</p>
<pre>handler := otelhttp.NewHandler(
    http.HandlerFunc(checkoutHandler),
    "checkout",
)
http.Handle("/checkout", handler)</pre>
<p>The instrumentation is introduced during compilation instead.</p>
<h5>2. No privileged runtime instrumentation process</h5>
<p>Once the application has been built, there is no eBPF process that needs to attach to it.</p>
<p>This can make Otelc attractive in environments where:</p>
<ul>
<li aria-level="1">privileged containers are prohibited,</li>
<li aria-level="1">eBPF is unavailable,</li>
<li aria-level="1">security policy restricts process instrumentation,</li>
<li aria-level="1">or platform teams do not want observability software attaching to production workloads.</li>
</ul>
<p>The OpenTelemetry documentation explicitly identifies this as a use case for compile-time instrumentation.</p>
<h5>3. Instrumentation can reach supported dependencies</h5>
<p>Because Otelc participates in the compilation process, it can instrument supported third-party dependencies that are part of the application’s build. That is useful when you use a library that you do not own but still want to instrument.</p>
<p>For example:</p>
<pre>Your application
       │
       ├── Gin
       ├── gRPC
       ├── database/sql
       ├── Redis
       └── AWS SDK</pre>
<p>Otelc can apply instrumentation rules to supported parts of that dependency graph without requiring you to fork or edit those dependencies.</p>
<h5>4. The build artifact contains the instrumentation</h5>
<p>This changes who owns the operational problem.</p>
<p>With OBI: application deployment + runtime instrumentation deployment</p>
<p>With Otelc: only the instrumented application artifact</p>
<p>The instrumentation becomes part of the software artifact produced by the build. That can fit naturally into organizations where application teams already own their build and deployment pipelines.</p>
<h4>The limitations and trade-offs of Otelc</h4>
<p>Nothing in this world seems to come without downsides… let’s look at Otelc’s cons.</p>
<h5>1. You must control the build</h5>
<p>Otelc requires access to the build process because that is where instrumentation is introduced. This is the most important limitation. If you have a precompiled production binary but cannot rebuild it, Otelc is simply not an option.</p>
<h5>2. The build pipeline becomes part of the instrumentation architecture</h5>
<p>Adding Otelc is not the same as adding another environment variable.</p>
<p>You now need to think about:</p>
<ul>
<li aria-level="1">local developer builds,</li>
<li aria-level="1">CI builds,</li>
<li aria-level="1">release builds,</li>
<li aria-level="1">test builds,</li>
<li aria-level="1">reproducibility,</li>
<li aria-level="1">dependency management,</li>
<li aria-level="1">cross-compilation,</li>
<li aria-level="1">monorepos,</li>
<li aria-level="1">and build caching.</li>
</ul>
<p>The good news is that Otelc is designed to work with the normal Go build workflow and documents approaches for integrating through go tool, direct build wrapping, and -toolexec.</p>
<p>But this should still be treated as a build-system change and tested accordingly.</p>
<h5>3. Coverage is limited to available instrumentation</h5>
<p>Like OBI, Otelc does not automatically understand every Go package. It needs instrumentation rules for the libraries and functions you want to observe.</p>
<p>If you use some framework that is not yet supported by the instrumentation, Otelc will not magically infer its semantics.</p>
<p>You may need to:</p>
<ul>
<li aria-level="1">add manual instrumentation,</li>
<li aria-level="1">create instrumentation for that library,</li>
<li aria-level="1">or accept that the library is not automatically traced.</li>
</ul>
<p>Luckily, the Otelc project includes an <a href="https://opentelemetry.io/docs/zero-code/go/compile-time/configuration/" target="_blank" rel="noopener noreferrer">instrumentation model and documentation for adding support for additional libraries</a>, so this scenario can be handled.</p>
<h5>4. Automatic instrumentation is still not the same as manual application instrumentation</h5>
<p>Otelc can add spans around supported framework and library operations. It does not automatically know which parts of your business logic are important.</p>
<p>For example:</p>
<pre>POST /checkout
       │
       ▼
ValidateOrder
       │
       ├── ReserveInventory
       │
       ├── CalculateDiscount
       │
       ├── ProcessPayment
       │
       └── CreateShipment</pre>
<p>Automatic instrumentation may produce excellent visibility into:</p>
<pre>HTTP server span
       │
       ├── SQL query
       ├── Redis operation
       ├── HTTP call to payment provider
       └── Kafka publish</pre>
<p>But it may not tell you how much time was spent specifically in CalculateDiscount unless you explicitly instrument that operation.</p>
<h3 id="obi-vs-otelc-side-by-side-comparison">OBI vs. Otelc: side-by-side comparison</h3>
<p>The following table summarizes the practical differences.</p>
<table>
<tbody>
<tr>
<td><b>Characteristic</b></td>
<td><b>OBI</b></td>
<td><b>Otelc</b></td>
</tr>
<tr>
<td><b>Instrumentation point</b></td>
<td>Runtime</td>
<td>Build time</td>
</tr>
<tr>
<td><b>Primary mechanism</b></td>
<td>eBPF, protocol observation, and language-specific probes such as uprobes</td>
<td>Go compiler/toolchain integration and injected instrumentation hooks</td>
</tr>
<tr>
<td><b>Source-code changes</b></td>
<td>None required</td>
<td>None required</td>
</tr>
<tr>
<td><b>Application rebuild required</b></td>
<td>No</td>
<td>Yes</td>
</tr>
<tr>
<td><b>Can instrument an existing binary</b></td>
<td>Yes, in supported environments</td>
<td>No</td>
</tr>
<tr>
<td><b>Can observe an already-running process</b></td>
<td>Yes</td>
<td>No</td>
</tr>
<tr>
<td><b>Requires build pipeline changes</b></td>
<td>No, not necessarily</td>
<td>Yes</td>
</tr>
<tr>
<td><b>Requires runtime instrumentation software</b></td>
<td>Yes</td>
<td>No separate attach-time agent</td>
</tr>
<tr>
<td><b>Requires Linux/eBPF support</b></td>
<td>Yes</td>
<td>No eBPF dependency</td>
</tr>
<tr>
<td><b>Requires elevated runtime privileges</b></td>
<td>Often requires privileged operation or specific Linux capabilities, depending on deployment</td>
<td>No eBPF-related runtime privileges</td>
</tr>
<tr>
<td><b>Can instrument supported Go libraries</b></td>
<td>Yes</td>
<td>Yes</td>
</tr>
<tr>
<td><b>Can instrument supported third-party dependencies</b></td>
<td>Yes, depending on supported protocol/library instrumentation</td>
<td>Yes, when supported instrumentation rules exist</td>
</tr>
<tr>
<td><b>Works when source code is unavailable</b></td>
<td>Potentially, yes</td>
<td>Only if you can rebuild from source</td>
</tr>
<tr>
<td><b>Works with precompiled binaries</b></td>
<td>Yes</td>
<td>No</td>
</tr>
<tr>
<td><b>Cross-language use</b></td>
<td>Yes</td>
<td>Primarily Go</td>
</tr>
<tr>
<td><b>Typical operational owner</b></td>
<td>Platform engineering, SRE, DevOps</td>
<td>Application engineering and/or CI/CD/platform engineering</td>
</tr>
<tr>
<td><b>Best fit</b></td>
<td>Existing workloads and centralized runtime instrumentation</td>
<td>Applications where you control the Go build</td>
</tr>
<tr>
<td><b>Business-level custom spans</b></td>
<td>Requires additional/manual instrumentation</td>
<td>Requires additional/manual instrumentation</td>
</tr>
<tr>
<td><b>Instrumentation deployment</b></td>
<td>Separate from the application artifact</td>
<td>Baked into the build artifact</td>
</tr>
</tbody>
</table>
<p> </p>
<h3 id="obi-vs-otelc-how-to-choose-which-one-to-use">OBI vs. Otelc: how to choose which one to use</h3>
<h4>A simple decision matrix</h4>
<p>A quick way to determine if you should be considering OBI or Otelc is by considering the following scenarios and asking a few questions.</p>
<p>If your starting point is:</p>
<h5>“I have a binary already running and I don’t want to rebuild it.”</h5>
<p>Start with <b>OBI</b>.</p>
<pre>Existing binary?
      │
      ├── Yes ──► OBI is the practical zero-code option
      │
      └── No</pre>
<h5>“I control the build but don’t want to modify the application source.”</h5>
<p>Look at <b>Otelc</b>.</p>
<pre>Control the Go build?
      │
      ├── Yes ──► Otelc is a strong candidate
      │
      └── No ──► Consider OBI</pre>
<h5>“I cannot run privileged instrumentation in production.”</h5>
<p>Look at <b>Otelc</b>.</p>
<pre>eBPF / runtime privileges allowed?
      │
      ├── No ──► Otelc
      │
      └── Yes ──► OBI or Otelc</pre>
<h5>“I need to instrument services in several languages.”</h5>
<p>OBI may provide a better common platform-level approach.</p>
<pre>Go + Java + Python + Node.js
             │
             ▼
            OBI</pre>
<h5>“I need to instrument important internal business operations.”</h5>
<p>Neither automatic approach completely solves the problem. You will probably still want <b>manual instrumentation</b>. Ooops! ;)</p>
<h4>A practical decision tree</h4>
<p>Here is another approach, a decision tree, that will help you quickly see which approach is more suitable.</p>
<p>If you are deciding how to instrument a Go application, start with this:</p>
<pre>                      ┌──────────────────────┐
                      │ Need OpenTelemetry?  │
                      └──────────┬───────────┘
                                 │
                                 ▼
                  ┌────────────────────────────────┐
                  │ Can you modify the application │
                  │ source code?                   │
                  └──────────┬─────────────────────┘
                             │
                ┌────────────┴────────────┐
                │                         │
               Yes                        No
                │                         │
                ▼                         ▼
        Manual instrumentation     Can you rebuild
        is available                the application?
                                      │
                           ┌──────────┴──────────┐
                           │                     │
                          Yes                    No
                           │                     │
                           ▼                     ▼
                     Consider Otelc        Consider OBI
                           │                     │
                           ▼                     ▼
                    Do you need             Does your runtime
                    domain-level spans?     support OBI/eBPF?
                           │                     │
                           ▼                     ▼
                  Add manual spans        Deploy and validate
                  where they matter       supported coverage</pre>
<p>In practice, the decision often reduces to four questions.</p>
<h5>Question 1: Do I control the build?</h5>
<p>If yes, Otelc becomes an option.</p>
<p>If no, it does not.</p>
<h5>Question 2: Can I run eBPF instrumentation in production?</h5>
<p>If yes, OBI becomes an option.</p>
<p>If no, Otelc may be easier operationally.</p>
<h5>Question 3: Do I need to instrument existing binaries?</h5>
<p>If yes, OBI is the more natural fit.</p>
<h5>Question 4: How much application-specific context do I need?</h5>
<p>If the answer is “a lot,” neither zero-code approach is likely to be sufficient by itself. Plan for some manual instrumentation.</p>
<h3 id="why-automatic-instrumentation-does-not-eliminate-the-need-for-manual-instrumentation">Why automatic instrumentation does not eliminate the need for manual instrumentation</h3>
<p>This is perhaps the most important point in this entire article. It is tempting to think of automatic instrumentation as a replacement for manual instrumentation. In our experience, it usually is not.</p>
<p>Automatic instrumentation and manual instrumentation solve different problems.</p>
<p>Automatic instrumentation is excellent at finding and instrumenting common technical boundaries:</p>
<ul>
<li aria-level="1">HTTP requests,</li>
<li aria-level="1">gRPC calls,</li>
<li aria-level="1">database queries,</li>
<li aria-level="1">Redis operations,</li>
<li aria-level="1">messaging operations,</li>
<li aria-level="1">cloud SDK calls,</li>
<li aria-level="1">and other supported libraries.</li>
</ul>
<p>Manual instrumentation is where you describe what your application actually does.</p>
<p>Consider a checkout service.</p>
<p>Automatic instrumentation may give you:</p>
<pre>POST /checkout                           820 ms
│
├── SELECT customer                      12 ms
├── SELECT inventory                     18 ms
├── Redis GET                             3 ms
├── POST payment-provider               410 ms
└── Kafka publish                         8 ms</pre>
<p>This is indeed already extremely useful and you should aim for this as your first step.</p>
<p>But your engineering team may care about something different:</p>
<pre>Checkout
│
├── ValidateOrder
├── ReserveInventory
├── CalculateDiscount
├── ProcessPayment
└── CreateShipment</pre>
<p>Those are domain operations. Neither OBI or Otelc can reliably infer that these operations are important simply by observing technical behavior.</p>
<p><strong>The most effective approach is often a hybrid.</strong></p>
<pre>                HTTP request
                       │
                       ▼
        ┌──────────────────────────┐
        │ Automatic instrumentation│
        └────────────┬─────────────┘
                     │
              Application code
                     │
                     ▼
        ┌─────────────────────────┐
        │   Manual business spans │
        │                         │
        │ ReserveInventory        │
        │ CalculateDiscount       │
        │ ProcessPayment          │
        └────────────┬────────────┘
                     │
                     ▼
        ┌──────────────────────────┐
        │ Automatic instrumentation│
        │ SQL / Redis / gRPC / etc │
        └──────────────────────────┘</pre>
<p>For OBI specifically, OpenTelemetry also provides the <a href="https://opentelemetry.io/docs/zero-code/go/autosdk/" target="_blank" rel="noopener noreferrer">Go Instrumentation Auto SDK</a>, which is intended to help integrate manually created spans with eBPF-generated spans and shared trace context.</p>
<p>That makes the hybrid model especially relevant:</p>
<div style="background: rgba(220,38,38,0.06); border-left: 3px solid #DC2626; border-radius: 0 8px 8px 0; padding: 22px 26px; margin: 36px 0; font-size: 17px; color: #7f1d1d;"><strong style="color: #991b1b;">Use automatic instrumentation for broad baseline coverage. Add manual spans only where application-specific context materially improves observability.</strong></div>
<p>This avoids two extremes:</p>
<h4>Extreme 1: Instrument nothing automatically</h4>
<p>Every team has to manually instrument every HTTP framework, database driver, messaging library, and client.</p>
<h4>Extreme 2: Instrument everything automatically and assume the result is sufficient</h4>
<p>You get infrastructure-level telemetry but may lack the domain context required to answer questions such as:</p>
<ul>
<li aria-level="1">Why is checkout slow?</li>
<li aria-level="1">Which business operation failed?</li>
<li aria-level="1">How much time is spent calculating pricing?</li>
<li aria-level="1">Which customer workflow is affected?</li>
<li aria-level="1">Did the payment provider fail, or did our own validation logic reject the request?</li>
</ul>
<p>The best observability, as I hope I’ve illustrated so far in this article, usually combines the two approaches.</p>
<p> </p>
<h3 id="conclusion">Conclusion</h3>
<p> </p>
<p>OBI and Otelc represent two fundamentally different approaches to zero-code OpenTelemetry instrumentation for Go.</p>
<p><b>OBI instruments from the outside.</b></p>
<p>It is attractive when you want to observe applications that already exist, especially when rebuilding or modifying them is difficult. It can be deployed independently of the application and can support a centralized, platform-owned instrumentation model.</p>
<p><b>Otelc instruments from the inside—during the build.</b></p>
<p>It is attractive when you control the Go build process and want supported OpenTelemetry instrumentation to become part of the resulting binary without manually modifying application source code. It also avoids the need for a privileged runtime eBPF instrumentation process.</p>
<p>Neither approach eliminates the value of manual instrumentation.</p>
<p>A practical architecture for many teams will look like this:</p>
<pre>┌─────────────────────────────────────────────┐
│           Automatic instrumentation         │
│                                             │
│   OBI or Otelc                              │
│                                             │
│   HTTP • gRPC • SQL • Redis • Kafka • etc.  │
└───────────────────────┬─────────────────────┘
                        │
                        ▼
┌─────────────────────────────────────────────┐
│           Manual instrumentation            │
│                                             │
│   Business operations                       │
│   Domain-specific spans                     │
│   Important attributes                      │
│   High-value application context            │
└───────────────────────┬─────────────────────┘
                        │
                        ▼
              OpenTelemetry backend</pre>
<p>The practical goal should not necessarily be to choose one instrumentation method and use it everywhere. Instead, choose the method that best fits the part of the system you are trying to observe.</p>
<ul>
<li aria-level="1"><b>Need visibility into existing workloads without rebuilding them?</b> Start with OBI.</li>
<li aria-level="1"><b>Control the Go build and want instrumentation baked into the binary?</b> Look at Otelc.</li>
<li aria-level="1"><b>Need detailed visibility into business operations?</b> Add manual instrumentation.</li>
<li aria-level="1"><b>Need all three?</b> A hybrid approach may be the most useful architecture.</li>
</ul>
<p>For implementation details and current compatibility information, start with the authoritative project documentation:</p>
<ul>
<li aria-level="1"><a href="https://opentelemetry.io/docs/zero-code/obi/setup/" target="_blank" rel="noopener noreferrer">OpenTelemetry eBPF Instrumentation (OBI)</a></li>
<li aria-level="1"><a href="https://opentelemetry.io/docs/zero-code/obi/configure/export-data/" target="_blank" rel="noopener noreferrer">OBI Go instrumentation compatibility and supported libraries</a></li>
<li aria-level="1"><a href="https://opentelemetry.io/docs/zero-code/go/compile-time/" target="_blank" rel="noopener noreferrer">OpenTelemetry Go compile-time instrumentation (Otelc)</a></li>
<li aria-level="1"><a href="https://github.com/open-telemetry/opentelemetry-go-compile-instrumentation/blob/main/docs/getting-started.md" target="_blank" rel="noopener noreferrer">Otelc getting started guide and supported libraries</a></li>
<li aria-level="1"><a href="https://opentelemetry.io/docs/zero-code/go/autosdk/" target="_blank" rel="noopener noreferrer">OpenTelemetry Go Instrumentation Auto SDK</a></li>
</ul>
<p class="space-top"><a href="https://apps.sematext.com/ui/registration" class="button-big" target="_blank" rel="noopener noreferrer">Start Free Trial</a></p><hr class="hidden"><p>The post <a href="https://sematext.com/blog/zero-code-opentelemetry-for-go-obi-vs-otelc/">Zero-Code OpenTelemetry for Go: Runtime Instrumentation with OBI vs. Compile-Time Instrumentation with Otelc</a> appeared first on <a href="https://sematext.com">Sematext</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Best 9 SLO Monitoring Tools in 2026: Review and Comparison Tables</title>
		<link>https://sematext.com/blog/best-slo-monitoring-tools/</link>
		
		<dc:creator><![CDATA[Otis]]></dc:creator>
		<pubDate>Sun, 23 Aug 2026 15:00:43 +0000</pubDate>
				<category><![CDATA[Monitoring]]></category>
		<category><![CDATA[Synthetic Monitoring]]></category>
		<category><![CDATA[Tools & comparisons]]></category>
		<category><![CDATA[api monitoring]]></category>
		<category><![CDATA[monitoring]]></category>
		<category><![CDATA[SLO]]></category>
		<category><![CDATA[uptime]]></category>
		<guid isPermaLink="false">https://sematext.com/?p=71024</guid>

					<description><![CDATA[<p>While working on adding SLO monitoring to Sematext we, of course, looked at other vendors and tools and their capabilities and approached. I think you will find this comparison of SLO monitoring platforms to be quite objective and factual. I provide an overview of 9 tools, their pros, cons, info about how their price their [&#8230;]</p>
<p>The post <a href="https://sematext.com/blog/best-slo-monitoring-tools/">Best 9 SLO Monitoring Tools in 2026: Review and Comparison Tables</a> appeared first on <a href="https://sematext.com">Sematext</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>While working on adding <a href="https://sematext.com/docs/synthetics/slo/">SLO monitoring to Sematext</a> we, of course, looked at other vendors and tools and their capabilities and approached. I think you will find this comparison of SLO monitoring platforms to be quite objective and factual. I provide an overview of 9 tools, their pros, cons, info about how their price their offering, as well as my take on each tool. The article contains several tool comparison tables comparing these vendors from multiple angles.</p>
<h1><b>Key Functionality to Compare</b></h1>
<p>At the basic level, an SLO defines the reliability target for a service—say, <b>99.9% successful requests over 30 days</b>. SLO monitoring turns that target into something actionable by tracking the underlying indicator, calculating error-budget consumption, and warning me when the budget is being burned too quickly.</p>
<p>The core functionality I look for is:</p>
<ul>
<li aria-level="1"><b>Flexible SLIs</b> based on availability, latency, errors, or custom metrics.</li>
<li aria-level="1"><b>Rolling and calendar-based time windows.</b></li>
<li aria-level="1"><b>Error budgets</b>, including remaining budget and burn rate.</li>
<li aria-level="1"><b>Burn-rate alerts</b>, ideally with multi-window alerting.</li>
<li aria-level="1"><b>Good integration with the telemetry I already collect</b>—metrics, traces, logs, or synthetic checks.</li>
<li aria-level="1"><b>Enough flexibility to model real services</b>, without forcing every SLO into a simplistic uptime template.</li>
</ul>
<p>The rest of this comparison focuses on the practical trade-offs: how each tool models SLOs, what data it can use, how usable the UI is, and how much complexity I have to accept to get reliable alerting. Note that service-level objectives are one of those things that sound straightforward until you try to operationalize them, so in addition to reviewing SLO monitoring tools it’s important to spend some time thinking about the SLOs themselves.</p>
<h2 id="quick-comparison"><b>Quick comparison</b></h2>
<p>I reviewed 9 tools. Most offer SLO as part of their wider observability platform, while one is a pure synthetic monitoring and another a purely SLO-focused tool. Here is a very quick high level comparison. There are several additional tables below that I suggest you look through.</p>
<table>
<tbody>
<tr>
<td><b>Tool</b></td>
<td><b>Best fit</b></td>
<td><b>SLI flexibility</b></td>
<td></td>
<td><b>Error-budget alerting</b></td>
<td><b>Pricing shape</b></td>
</tr>
<tr>
<td><b>Sematext</b></td>
<td>APIs, websites, and browser/user-journey reliability</td>
<td>Moderate</td>
<td></td>
<td>Yes</td>
<td>Monitor/usage-based</td>
</tr>
<tr>
<td><b>Datadog</b></td>
<td>Teams already deep in Datadog</td>
<td>High</td>
<td></td>
<td>Yes</td>
<td>Modular, usage-based</td>
</tr>
<tr>
<td><b>New Relic</b></td>
<td>Full-stack observability teams</td>
<td>High</td>
<td></td>
<td>Yes</td>
<td>Data + user/compute</td>
</tr>
<tr>
<td><b>Grafana Cloud</b></td>
<td>Prometheus/Grafana/OpenTelemetry environments</td>
<td>High</td>
<td></td>
<td>Yes</td>
<td>Platform fee + usage</td>
</tr>
<tr>
<td><b>Dynatrace</b></td>
<td>Complex enterprise and cloud environments</td>
<td>Very high</td>
<td></td>
<td>Yes</td>
<td>Platform/usage-based</td>
</tr>
<tr>
<td><b>Elastic Observability</b></td>
<td>Teams using Elastic for logs, metrics, and APM</td>
<td>Very high</td>
<td></td>
<td>Yes</td>
<td>Usage-based</td>
</tr>
<tr>
<td><b>Honeycomb</b></td>
<td>Cloud-native, tracing- and event-centric teams</td>
<td>High</td>
<td></td>
<td>Yes</td>
<td>Event/data-based</td>
</tr>
<tr>
<td><b>Nobl9</b></td>
<td>Organization-wide, vendor-neutral SLO programs</td>
<td>Very high</td>
<td></td>
<td>Yes</td>
<td>Quote-based</td>
</tr>
<tr>
<td><b>Checkly</b></td>
<td>Developer-centric synthetic/API monitoring</td>
<td>Moderate</td>
<td></td>
<td>Limited</td>
<td>Monitor/check-run based</td>
</tr>
</tbody>
</table>
<h1><b>Best SLO Monitoring Tools: A Practical Comparison</b></h1>
<p>SLO monitoring has become one of the more useful ways to answer a deceptively simple question: <b>is my service actually reliable enough?</b></p>
<p>I don’t mean “are all the dashboards green?” or “did CPU stay below 80%?” I mean whether users are getting the level of availability and performance that the service is supposed to provide.</p>
<p>A <b>Service Level Indicator (SLI)</b> is the measurement. A <b>Service Level Objective (SLO)</b> is the target for that measurement over a period of time. If my API successfully serves 99.9% of requests over 30 days, the SLO might be 99.9% availability. The remaining 0.1% is the <b>error budget</b>—the amount of unreliability I can afford before missing the objective.</p>
<p>SLO monitoring matters because raw monitoring data does not tell me how much a problem matters. A service can generate thousands of errors and still be within its reliability objective. Conversely, a relatively short outage can consume a huge chunk of the error budget and require immediate attention.</p>
<p>The core capabilities I look for are:</p>
<ul>
<li aria-level="1">Flexible ways to define SLIs for availability, latency, errors, throughput, or custom business signals.</li>
<li aria-level="1">Rolling and calendar-based evaluation windows.</li>
<li aria-level="1">Accurate error-budget calculations.</li>
<li aria-level="1">Burn-rate monitoring and alerting.</li>
<li aria-level="1">Support for grouping, filtering, and managing large numbers of SLOs.</li>
<li aria-level="1">Good integration with the telemetry I already collect.</li>
<li aria-level="1">Automation through APIs, Terraform, or configuration-as-code.</li>
<li aria-level="1">A practical path from an SLO violation to the logs, metrics, traces, or synthetic checks that explain it.</li>
</ul>
<p>The tools below take noticeably different approaches. Some treat SLOs as one capability inside a larger observability platform. Others, especially Nobl9, treat SLO management as the product itself.</p>
<h1><b>Tool Comparison</b></h1>
<table>
<tbody>
<tr>
<td><b>Tool</b></td>
<td><b>Primary category</b></td>
<td><b>Best for</b></td>
<td><b>Native SLO/error-budget management</b></td>
<td><b>External synthetic monitoring</b></td>
</tr>
<tr>
<td>Sematext</td>
<td>Full-stack + synthetics</td>
<td>API, website, and user-journey reliability</td>
<td>Yes</td>
<td>Yes</td>
</tr>
<tr>
<td>Datadog</td>
<td>Enterprise observability</td>
<td>Teams already using Datadog</td>
<td>Yes</td>
<td>Yes</td>
</tr>
<tr>
<td>New Relic</td>
<td>Full-stack observability</td>
<td>APM-centric teams</td>
<td>Yes</td>
<td>Yes</td>
</tr>
<tr>
<td>Grafana Cloud</td>
<td>Cloud-native observability</td>
<td>Prometheus and OpenTelemetry teams</td>
<td>Yes</td>
<td>Through integrations</td>
</tr>
<tr>
<td>Dynatrace</td>
<td>Enterprise/full-stack observability</td>
<td>Complex enterprise and cloud environments</td>
<td>Yes</td>
<td>Yes</td>
</tr>
<tr>
<td>Elastic Observability</td>
<td>Enterprise/full-stack observability</td>
<td>Flexible SLI definitions across multiple data types</td>
<td>Yes</td>
<td>Yes</td>
</tr>
<tr>
<td>Honeycomb</td>
<td>Cloud-native observability</td>
<td>Tracing and event-based reliability</td>
<td>Yes</td>
<td>Indirectly</td>
</tr>
<tr>
<td>Checkly</td>
<td>Synthetic monitoring</td>
<td>Monitoring-as-code and developer workflows</td>
<td>SLO-style reliability workflows</td>
<td>Yes</td>
</tr>
<tr>
<td>Nobl9</td>
<td>Dedicated SLO platform</td>
<td>Vendor-neutral SLO programs</td>
<td>Yes</td>
<td>Via data sources</td>
</tr>
</tbody>
</table>
<p> </p>
<h2 id="1-sematext">1. Sematext</h2>
<p>Sematext takes a relatively straightforward approach to SLO monitoring. Its current SLO functionality is built around synthetic monitoring, allowing me to create objectives from existing HTTP and browser monitors rather than requiring new instrumentation or a separate telemetry pipeline. That makes it particularly suitable for services where the thing I actually want to measure is externally observable behavior: can the API be reached, does the website respond quickly enough, and can a browser complete a critical user journey?</p>
<p>The implementation tracks compliance over configurable windows and provides error-budget visibility and early warnings. Sematext also connects the reliability view with the rest of its monitoring stack, including logs, infrastructure monitoring, and tracing. The main trade-off is that this is not trying to be a universal, vendor-neutral SLO layer that can model every imaginable SLI from every backend. It is simpler and more opinionated.</p>
<p><img decoding="async" class="alignnone size-full wp-image-70862" src="https://sematext.com/wp-content/uploads/2026/07/slo-screen-details.png" alt="SLO Error Budget and Compliance Charts and Alerts" width="2220" height="1494" srcset="https://sematext.com/wp-content/uploads/2026/07/slo-screen-details.png 2220w, https://sematext.com/wp-content/uploads/2026/07/slo-screen-details-300x202.png 300w, https://sematext.com/wp-content/uploads/2026/07/slo-screen-details-1024x689.png 1024w, https://sematext.com/wp-content/uploads/2026/07/slo-screen-details-768x517.png 768w, https://sematext.com/wp-content/uploads/2026/07/slo-screen-details-1536x1034.png 1536w, https://sematext.com/wp-content/uploads/2026/07/slo-screen-details-2048x1378.png 2048w" sizes="(max-width: 2220px) 100vw, 2220px" /></p>
<p> </p>
<h2 id="key-features"><b>Key features</b></h2>
<ul>
<li aria-level="1">SLOs built from HTTP and browser synthetic monitors.</li>
<li aria-level="1">Availability and performance-oriented objectives.</li>
<li aria-level="1">Configurable compliance targets and time windows.</li>
<li aria-level="1">Remaining error-budget tracking.</li>
<li aria-level="1">Historical and live compliance views.</li>
<li aria-level="1">Early warning and alerting before an objective is violated.</li>
<li aria-level="1">Synthetic checks from multiple locations.</li>
<li aria-level="1">Monitoring of APIs, websites, user journeys, and third-party dependencies.</li>
<li aria-level="1">Correlation with other Sematext telemetry.</li>
</ul>
<h2 id="pros"><b>Pros</b></h2>
<ul>
<li aria-level="1">Very little setup if synthetic monitors already exist.</li>
<li aria-level="1">Good fit for externally visible services and user-facing reliability.</li>
<li aria-level="1">Easy to understand without becoming an SRE research project.</li>
<li aria-level="1">Useful for monitoring third-party APIs against expected reliability.</li>
<li aria-level="1">Integrates with logs, metrics, tracing, and other Sematext capabilities.</li>
</ul>
<h2 id="cons"><b>Cons</b></h2>
<ul>
<li aria-level="1">Less flexible than tools that can define SLOs directly from arbitrary metrics or multiple external data sources.</li>
<li aria-level="1">Not as strong for organizations that want every SLO managed through Git and a formal SLO-as-code workflow.</li>
<li aria-level="1">Synthetic-monitor-based SLOs are not a replacement for every internal service-level indicator.</li>
</ul>
<h2 id="pricing"><b>Pricing</b></h2>
<p>Sematext currently offers a 14-day free trial. Synthetic Monitoring starts at <b>$2 per monitor per month</b> on monthly pricing, or <b>$1.80 per monitor per month</b> with annual pricing. SLO functionality is associated with the synthetic monitoring workflow, so the practical cost depends primarily on the monitors required to measure the service.</p>
<h2 id="my-opinion"><b>My opinion</b></h2>
<p>What I like is the lack of ceremony. If I already have HTTP or browser monitors, turning those measurements into an SLO is a pretty natural next step. I also like the fact that an external SLO can represent what users actually experience rather than what an internal metric claims is happening.</p>
<p>What I dislike is the relative lack of flexibility compared with something like Nobl9 or a metric-centric implementation. If I want to define complex internal SLIs across arbitrary telemetry sources, this would not be my first choice.</p>
<h2 id="2-datadog">2. Datadog</h2>
<p>Datadog has one of the more mature general-purpose SLO implementations. It supports metric-based, monitor-based, and time-slice SLOs, which gives me several different ways to model reliability. Metric-based SLOs work well when I can clearly define good and bad events. Monitor-based SLOs build on existing monitors, synthetic checks, or service checks. Time-slice SLOs are useful when reliability is defined as a metric satisfying a condition during discrete periods of time.</p>
<p>Datadog also provides error-budget and burn-rate alerting, SLO tagging, search, historical views, APIs, and Terraform support. If my infrastructure, APM, logs, and synthetics are already in Datadog, this is an obvious place to keep SLOs because the data is already there. The downside is the same one that applies to Datadog generally: the platform is broad, powerful, and easy to expand inside, but the pricing model can become difficult to reason about as more products and telemetry are added.</p>
<h2 id="key-features"><b>Key features</b></h2>
<ul>
<li aria-level="1">Metric-based SLOs.</li>
<li aria-level="1">Monitor-based SLOs.</li>
<li aria-level="1">Time-slice SLOs.</li>
<li aria-level="1">Availability, latency, and custom metric use cases.</li>
<li aria-level="1">Error-budget tracking.</li>
<li aria-level="1">Burn-rate indicators and alerts.</li>
<li aria-level="1">Rolling windows.</li>
<li aria-level="1">Grouped SLOs and tags.</li>
<li aria-level="1">SLO search and management views.</li>
<li aria-level="1">API and Terraform support.</li>
<li aria-level="1">Integration with APM, logs, RUM, synthetics, and infrastructure metrics.</li>
</ul>
<h2 id="pros"><b>Pros</b></h2>
<ul>
<li aria-level="1">Very flexible SLI modeling.</li>
<li aria-level="1">Excellent fit if Datadog is already the telemetry platform.</li>
<li aria-level="1">Good support for both event-based and time-based reliability measurements.</li>
<li aria-level="1">Mature burn-rate alerting.</li>
<li aria-level="1">Strong automation support.</li>
<li aria-level="1">Easy to move from an SLO problem into the rest of the observability stack.</li>
</ul>
<h2 id="cons"><b>Cons</b></h2>
<ul>
<li aria-level="1">Pricing can be complicated because SLOs are only one part of a larger set of billable products.</li>
<li aria-level="1">The number of possible ways to model an SLO can be confusing for teams new to SRE practices.</li>
<li aria-level="1">Monitor-based SLOs can introduce dependencies on the underlying monitor configuration.</li>
<li aria-level="1">The best experience assumes the relevant telemetry already lives in Datadog.</li>
</ul>
<h2 id="pricing"><b>Pricing</b></h2>
<p>Datadog’s pricing is modular and depends on the products used to collect and evaluate the underlying telemetry. Host-based products, including infrastructure monitoring and some APM offerings, use different billing models, while other capabilities are billed according to consumption. Datadog provides public list pricing, but in practice I would model the cost based on the complete telemetry architecture rather than looking for a standalone “SLO price.”</p>
<h2 id="my-opinion"><b>My opinion</b></h2>
<p>What I like most is flexibility. Datadog can handle the classic “good events divided by total events” model, monitor-based availability, and time-slice definitions without forcing me into a single interpretation of an SLI.</p>
<p>What I dislike is that I would hesitate to introduce Datadog purely for SLO monitoring. It makes the most sense when the organization is already committed to the platform. Otherwise, I am paying for and operating inside a much larger system than the specific SLO problem requires.</p>
<h2 id="3-new-relic">3. New Relic</h2>
<p>New Relic’s Service Level Management is tightly integrated into the rest of the New Relic platform. I can create service levels ranging from relatively simple one-click configurations to more advanced and customizable definitions, then view them alongside applications, workloads, and other observability data. The product also provides alerts and analysis views for tracking reliability over time and investigating breaches.</p>
<p>The advantage is that SLOs are not isolated objects sitting in a separate reliability tool. They are part of the APM and observability workflow. That can be useful when the next step after discovering that an error budget is being consumed is immediately opening the affected service, transaction, trace, or other telemetry.</p>
<p>New Relic’s pricing is also different from the traditional per-host approach. The current public model combines data ingest with user or compute-based access options. That can work well, but I would spend time modeling access requirements before committing.</p>
<h2 id="key-features"><b>Key features</b></h2>
<ul>
<li aria-level="1">Guided and advanced service-level creation.</li>
<li aria-level="1">SLI and SLO definitions integrated with New Relic services.</li>
<li aria-level="1">Reliability views across Navigator and Workloads.</li>
<li aria-level="1">Alerting for degradation and breaches.</li>
<li aria-level="1">Period-over-period analysis.</li>
<li aria-level="1">Investigation workflows around SLO breaches.</li>
<li aria-level="1">Integration with APM, infrastructure, logs, synthetics, and other New Relic capabilities.</li>
<li aria-level="1">Support for both simple and more customizable SLO definitions.</li>
</ul>
<h2 id="pros"><b>Pros</b></h2>
<ul>
<li aria-level="1">Strong integration with the rest of the observability platform.</li>
<li aria-level="1">Relatively approachable SLO creation.</li>
<li aria-level="1">Good fit for teams already using New Relic APM.</li>
<li aria-level="1">Useful free tier for evaluation and smaller deployments.</li>
<li aria-level="1">No need to count hosts as a primary pricing dimension.</li>
</ul>
<h2 id="cons"><b>Cons</b></h2>
<ul>
<li aria-level="1">Pricing can become more complicated once data volume and platform-user requirements grow.</li>
<li aria-level="1">The experience is best when New Relic is already the primary observability platform.</li>
<li aria-level="1">Some organizations may find the user-access model less attractive than purely telemetry-based pricing.</li>
<li aria-level="1">Less attractive as a standalone, vendor-neutral SLO layer.</li>
</ul>
<h2 id="pricing"><b>Pricing</b></h2>
<p>New Relic’s free tier includes <b>100 GB of data ingest per month</b>, unlimited basic users, and one free full-platform user. Beyond that, public pricing lists original data ingest at <b>$0.40/GB</b> beyond the included allowance. User pricing varies by edition, while New Relic also offers a compute-based model for eligible customers.</p>
<h2 id="my-opinion"><b>My opinion</b></h2>
<p>I like New Relic’s integrated approach. If I am already using New Relic for APM, I don’t want to export the same telemetry somewhere else just to calculate an error budget. Keeping the SLO next to the application data is operationally sensible.</p>
<p>What I dislike is the pricing complexity around data, user types, editions, and newer compute models. It is more transparent than some historical observability pricing models, but I would still build a realistic cost model before assuming the free tier or entry pricing reflects production costs.</p>
<h2 id="4-grafana-cloud">4. Grafana Cloud</h2>
<p>Grafana Cloud is particularly interesting to me because it fits naturally into Prometheus and OpenTelemetry-oriented environments. Grafana SLO provides a dedicated workflow for creating and managing SLOs, generating dashboards and alerts, tracking error budgets, and automating configuration through APIs and Terraform.</p>
<p>The biggest advantage is architectural familiarity. If my engineering organization already thinks in PromQL, metrics, recording rules, infrastructure-as-code, and Git-based workflows, Grafana SLO feels like an extension of the existing stack rather than a new conceptual layer. The product can generate supporting dashboards, recording rules, and alerting components instead of requiring me to hand-build everything.</p>
<p>The limitation is that Grafana SLO is a Grafana Cloud capability rather than a general feature of self-hosted open-source Grafana. That distinction matters if my primary reason for choosing Grafana is self-hosting and avoiding a managed SaaS dependency.</p>
<h2 id="key-features"><b>Key features</b></h2>
<ul>
<li aria-level="1">Guided SLO creation.</li>
<li aria-level="1">Metric-based SLIs.</li>
<li aria-level="1">Error-budget tracking.</li>
<li aria-level="1">SLO dashboards.</li>
<li aria-level="1">Error-budget alerts.</li>
<li aria-level="1">Generated recording rules.</li>
<li aria-level="1">Generated alerting rules.</li>
<li aria-level="1">API support.</li>
<li aria-level="1">Terraform support.</li>
<li aria-level="1">SLO-as-code workflows.</li>
<li aria-level="1">Integration with the broader Grafana observability stack.</li>
</ul>
<h2 id="pros"><b>Pros</b></h2>
<ul>
<li aria-level="1">Excellent fit for Prometheus-centric teams.</li>
<li aria-level="1">Strong infrastructure-as-code story.</li>
<li aria-level="1">Familiar workflow for teams already using Grafana.</li>
<li aria-level="1">Good separation between raw telemetry and reliability objectives.</li>
<li aria-level="1">Free tier and relatively accessible entry pricing.</li>
<li aria-level="1">Less architectural lock-in than some all-in-one observability platforms.</li>
</ul>
<h2 id="cons"><b>Cons</b></h2>
<ul>
<li aria-level="1">The managed SLO capability is specifically part of Grafana Cloud.</li>
<li aria-level="1">Metric modeling still requires Prometheus/Grafana expertise.</li>
<li aria-level="1">Usage-based pricing can become harder to predict as metric cardinality grows.</li>
<li aria-level="1">The SLO implementation is less useful if my relevant data is not accessible through the supported Grafana Cloud metric workflow.</li>
</ul>
<h2 id="pricing"><b>Pricing</b></h2>
<p>Grafana Cloud has a free tier and a Pro plan starting at <b>$19 per month plus usage</b>. The pricing page currently includes 10,000 active metric series in the platform fee, with additional metrics starting at <b>$6.50 per 1,000 series</b> before volume discounts. Enterprise starts with a <b>$25,000 annual spend commitment</b>.</p>
<h2 id="my-opinion"><b>My opinion</b></h2>
<p>I like Grafana Cloud when the organization already has Prometheus expertise. The SLO-as-code and Terraform story is especially important for teams that do not want critical reliability definitions to exist only as manually configured UI objects.</p>
<p>What I dislike is that I still need to understand the underlying metric model. That is not necessarily a flaw—SLOs should be based on carefully chosen indicators—but it means Grafana is not always the easiest tool for a team that wants a highly opinionated, guided reliability workflow.</p>
<h2 id="5-dynatrace">5. Dynatrace</h2>
<p>Dynatrace has a fairly powerful SLO implementation, especially for organizations already using its broader observability platform. I can create SLOs from predefined templates or define custom SLIs using DQL, which means the underlying indicator does not have to be limited to standard availability or latency metrics. Dynatrace can use data available through Grail, including metrics and other data types that can be queried into a time series. The current SLO experience includes error-budget tracking, visualization, and management through a dedicated application, while APIs and SDKs provide automation options. Dynatrace is particularly interesting for complex environments because its topology and entity model can provide useful context around the services being measured. The downside is that the platform is large and opinionated, so using it just for SLO monitoring would usually be excessive.</p>
<h3 id="key-features"><b>Key features</b></h3>
<ul>
<li aria-level="1">Template-based SLO creation.</li>
<li aria-level="1">Custom SLI definitions using DQL.</li>
<li aria-level="1">Service availability and performance objectives.</li>
<li aria-level="1">Infrastructure and Kubernetes-oriented templates.</li>
<li aria-level="1">Error-budget tracking.</li>
<li aria-level="1">SLO visualization and dashboard integration.</li>
<li aria-level="1">Entity-aware observability context.</li>
<li aria-level="1">API and SDK support.</li>
<li aria-level="1">Access to multiple Grail data types for SLI definitions.</li>
</ul>
<h3 id="pros"><b>Pros</b></h3>
<ul>
<li aria-level="1">Very flexible SLI definitions.</li>
<li aria-level="1">Strong fit for large and complex environments.</li>
<li aria-level="1">Good topology and entity context.</li>
<li aria-level="1">Templates make common SLOs easier to create.</li>
<li aria-level="1">Custom DQL opens up nontraditional SLI use cases.</li>
<li aria-level="1">Good API and SDK support.</li>
</ul>
<h3 id="cons"><b>Cons</b></h3>
<ul>
<li aria-level="1">A large platform if SLO monitoring is the only requirement.</li>
<li aria-level="1">DQL adds another query language to learn.</li>
<li aria-level="1">Pricing is not simple to evaluate from the SLO feature alone.</li>
<li aria-level="1">The platform can feel more opinionated than Prometheus-centric alternatives.</li>
</ul>
<h3 id="pricing"><b>Pricing</b></h3>
<p>Dynatrace pricing is based on the broader platform and the products or capabilities being used rather than a simple standalone SLO price. I would treat SLO cost as part of the overall observability architecture and request a realistic quote based on data, monitoring scope, and the Dynatrace platform capabilities required.</p>
<h3 id="my-opinion"><b>My opinion</b></h3>
<p>What I like is the flexibility. Being able to build an SLI from a custom DQL query means I am not limited to a small set of predefined reliability models. I also like the surrounding context: if an SLO is degrading, topology and entity information can help connect that reliability problem to the actual system.</p>
<p>What I dislike is the weight of the platform. If my team just wants straightforward SLOs on Prometheus metrics, Dynatrace would probably feel like bringing an entire observability platform to solve a narrower problem.</p>
<h2 id="6-elastic-observability">6. Elastic Observability</h2>
<p>Elastic has one of the more flexible SLO implementations among full-stack observability platforms. I can create SLIs from APM availability or latency, synthetic availability, custom metrics, histogram metrics, timeslice metrics, or custom KQL queries against data in Elasticsearch. That is a much broader set of options than tools that restrict SLOs to predefined monitor types.</p>
<p>Elastic supports both rolling and calendar-aligned windows, occurrences- and timeslice-based budgeting, error budgets, and burn-rate alerting. The SLO overview also makes it possible to see historical SLI performance and budget consumption without manually assembling dashboards.</p>
<p>I think Elastic is particularly interesting for teams that already use Elasticsearch for logs or observability data and want to define SLOs from that data without exporting it elsewhere.</p>
<h2 id="key-features"><b>Key features</b></h2>
<ul>
<li aria-level="1">APM availability and latency SLIs.</li>
<li aria-level="1">Synthetic availability SLIs.</li>
<li aria-level="1">Custom KQL-based SLIs.</li>
<li aria-level="1">Custom metric and histogram metric SLIs.</li>
<li aria-level="1">Timeslice and occurrences budgeting.</li>
<li aria-level="1">Rolling and calendar-aligned windows.</li>
<li aria-level="1">Error-budget tracking.</li>
<li aria-level="1">Burn-rate alerts.</li>
<li aria-level="1">Historical SLI and error-budget views.</li>
<li aria-level="1">Dashboard integration.</li>
<li aria-level="1">OpenTelemetry and Prometheus support across the broader observability platform.</li>
</ul>
<h2 id="pros"><b>Pros</b></h2>
<ul>
<li aria-level="1">One of the most flexible sets of SLI types in this comparison.</li>
<li aria-level="1">Can build SLOs from logs, metrics, APM, or synthetic data.</li>
<li aria-level="1">Supports both rolling and calendar windows.</li>
<li aria-level="1">Good support for error budgets and burn rates.</li>
<li aria-level="1">Strong fit for teams already using Elastic.</li>
<li aria-level="1">Useful combination of internal observability and external digital-experience monitoring.</li>
</ul>
<h2 id="cons"><b>Cons</b></h2>
<ul>
<li aria-level="1">Requires learning Elastic’s data model and query language if I want to use the most flexible SLI types.</li>
<li aria-level="1">Not a lightweight standalone SLO tool.</li>
<li aria-level="1">SLO availability depends on the appropriate Elastic deployment and licensing.</li>
<li aria-level="1">Self-managed deployments can require more operational work than SaaS-only alternatives.</li>
</ul>
<h2 id="pricing"><b>Pricing</b></h2>
<p>Elastic’s current serverless Observability Complete tier includes SLO functionality. Serverless pricing is usage-based: Elastic currently lists metrics ingest from <b>$0.023/GB</b>, other observability data from <b>$0.09/GB</b>, plus separate retention and egress charges. Synthetic monitoring is available as an add-on. I would calculate the real cost based on total telemetry volume rather than thinking of SLOs as a separately priced feature.</p>
<h2 id="my-opinion"><b>My opinion</b></h2>
<p>What I like most is flexibility. Elastic lets me define an SLO from the data I actually have instead of forcing me to translate everything into one specific metric format. I also like the combination of APM, logs, synthetics, and SLOs in the same platform.</p>
<p>What I dislike is the complexity. Elastic can do a lot, but that also means I need to understand how my data is structured before I can take full advantage of the SLO functionality.</p>
<h2 id="7-honeycomb">7. Honeycomb</h2>
<p>Honeycomb takes a more engineering-centric approach to observability than traditional metrics-first platforms, and its SLO implementation fits that model. SLOs are built from events, which makes them a natural fit for tracing and high-cardinality telemetry. I can define what successful behavior looks like and then track the remaining error budget over the selected time period.</p>
<p>The alerting model is particularly interesting. Honeycomb supports both <b>Exhaustion Time</b> alerts, which estimate when the error budget will run out, and <b>Budget Rate</b> alerts, which trigger when the budget is being consumed faster than expected. The UI also provides a budget-burndown graph that helps tune alerts before blindly picking a burn-rate threshold.</p>
<p>For teams already using OpenTelemetry and distributed tracing heavily, Honeycomb is one of the more natural SLO implementations I would evaluate.</p>
<h2 id="key-features"><b>Key features</b></h2>
<ul>
<li aria-level="1">Event-based SLOs.</li>
<li aria-level="1">Error-budget tracking.</li>
<li aria-level="1">Budget burndown visualization.</li>
<li aria-level="1">Historical burn-rate analysis.</li>
<li aria-level="1">Exhaustion Time burn alerts.</li>
<li aria-level="1">Budget Rate burn alerts.</li>
<li aria-level="1">Slack and PagerDuty notification support.</li>
<li aria-level="1">Distributed tracing and OpenTelemetry integration.</li>
<li aria-level="1">High-cardinality telemetry support.</li>
<li aria-level="1">Service-level SLO workflows.</li>
</ul>
<h2 id="pros"><b>Pros</b></h2>
<ul>
<li aria-level="1">Very natural fit for tracing and event-based telemetry.</li>
<li aria-level="1">Excellent burn-alert model.</li>
<li aria-level="1">Budget-burndown visualization helps tune alerts.</li>
<li aria-level="1">Strong OpenTelemetry support.</li>
<li aria-level="1">Particularly well suited to modern distributed systems.</li>
<li aria-level="1">More engineering-focused than dashboard-heavy.</li>
</ul>
<h2 id="cons"><b>Cons</b></h2>
<ul>
<li aria-level="1">Less attractive if the organization primarily thinks in Prometheus metrics.</li>
<li aria-level="1">SLOs are not available on the free plan.</li>
<li aria-level="1">The Pro plan includes only two SLOs.</li>
<li aria-level="1">Teams unfamiliar with event-based observability may need to adjust their mental model.</li>
</ul>
<h2 id="pricing"><b>Pricing</b></h2>
<p>Honeycomb has a free plan with up to <b>20 million events per month</b> and <b>100 million metric data points per month</b>. The Pro plan starts at <b>$150 per month</b> and includes <b>2 SLOs</b>. Enterprise pricing is custom and starts with significantly larger trigger and SLO allowances.</p>
<h2 id="my-opinion"><b>My opinion</b></h2>
<p>I like Honeycomb because the SLO implementation feels connected to how engineers actually investigate modern distributed systems. If the SLO burns, I can work from the relevant events and traces rather than switching into an entirely separate monitoring model.</p>
<p>What I dislike is the relatively limited number of SLOs in the Pro plan. Two SLOs is enough for evaluation or a small service footprint, but it is restrictive if I want to make SLOs a standard part of every production service.</p>
<h2 id="8-checkly">8. Checkly</h2>
<p>Checkly represents a different approach from the large observability platforms. It is primarily a synthetic monitoring platform designed around developers and monitoring-as-code. I can define API checks, browser checks, Playwright test suites, uptime monitors, and multistep checks, then manage them through the Checkly CLI, Terraform, or Pulumi.</p>
<p>For SLO-style monitoring, this is useful when the thing I care about is externally observable behavior. Instead of defining availability from an internal metric, I can measure whether an API responds correctly or whether a user can actually complete a critical workflow.</p>
<p>I would not put Checkly in the same category as Nobl9 or Elastic when it comes to arbitrary SLI definitions. Its strength is narrower but useful: taking synthetic tests and treating them as production reliability signals that can be managed like code.</p>
<h2 id="key-features"><b>Key features</b></h2>
<ul>
<li aria-level="1">HTTP, TCP, DNS, ICMP, and heartbeat monitoring.</li>
<li aria-level="1">API and multistep checks.</li>
<li aria-level="1">Browser checks using Playwright.</li>
<li aria-level="1">Playwright Check Suites.</li>
<li aria-level="1">Global and private monitoring locations.</li>
<li aria-level="1">Automatic retries.</li>
<li aria-level="1">Monitoring-as-code workflows.</li>
<li aria-level="1">Checkly CLI.</li>
<li aria-level="1">Terraform provider.</li>
<li aria-level="1">Pulumi provider.</li>
<li aria-level="1">Prometheus metrics export.</li>
<li aria-level="1">Status pages and alerting integrations.</li>
</ul>
<h2 id="pros"><b>Pros</b></h2>
<ul>
<li aria-level="1">Excellent developer experience.</li>
<li aria-level="1">Strong monitoring-as-code support.</li>
<li aria-level="1">Native Playwright integration.</li>
<li aria-level="1">Useful for testing critical user journeys.</li>
<li aria-level="1">Terraform, Pulumi, and CLI support.</li>
<li aria-level="1">Good fit for CI/CD and production monitoring workflows.</li>
</ul>
<h2 id="cons"><b>Cons</b></h2>
<ul>
<li aria-level="1">Less flexible for arbitrary internal metric-based SLIs.</li>
<li aria-level="1">Synthetic checks can become expensive at high frequency and across many locations.</li>
<li aria-level="1">Not a full observability platform.</li>
<li aria-level="1">Best suited to externally observable services and workflows.</li>
</ul>
<h2 id="pricing"><b>Pricing</b></h2>
<p>Checkly has a free Hobby plan with <b>10 uptime monitors</b>, <b>10,000 API check runs</b>, and <b>1,000 browser check runs per month</b>. The Starter plan starts at <b>$24 per month</b>, while Team starts at <b>$64 per month</b>. Enterprise pricing is custom. Pricing scales through monitor counts and synthetic check runs.</p>
<h2 id="my-opinion"><b>My opinion</b></h2>
<p>What I like is the developer workflow. I would rather keep monitoring definitions in Git and deploy them with the rest of the application infrastructure than manually create hundreds of checks in a UI.</p>
<p>What I dislike is that it is easy to confuse “synthetic monitoring” with complete SLO management. Checkly is excellent when my SLI is based on externally observable behavior, but I would use another tool if I needed to define reliability objectives from arbitrary application metrics.</p>
<h2 id="9-nobl9">9. Nobl9</h2>
<p>Nobl9 is the most specialized SLO product in this comparison. Rather than assuming that SLOs should live inside one observability backend, it acts as a dedicated reliability layer that can connect to multiple existing telemetry systems. Its platform includes error-budget alerting, composite SLOs, backtesting, service-health views, reporting, and a strong SLO-as-code workflow.</p>
<p>That vendor-neutral approach is its biggest differentiator. A large organization may have Datadog in one team, Prometheus in another, New Relic somewhere else, and cloud-native telemetry in yet another environment. Nobl9 is designed to put SLO definitions above those individual systems rather than requiring telemetry consolidation first.</p>
<p>It also has one of the strongest configuration-as-code stories through OpenSLO, YAML-based definitions, Git workflows, validation tooling, and automation. The trade-off is additional platform complexity and a separate product to operate.</p>
<h2 id="key-features"><b>Key features</b></h2>
<ul>
<li aria-level="1">Vendor-neutral SLO management.</li>
<li aria-level="1">Multiple telemetry integrations.</li>
<li aria-level="1">Error-budget tracking and alerting.</li>
<li aria-level="1">Composite SLOs.</li>
<li aria-level="1">SLO backtesting.</li>
<li aria-level="1">Service Health Dashboard.</li>
<li aria-level="1">SLO annotations.</li>
<li aria-level="1">Reporting.</li>
<li aria-level="1">OpenSLO support.</li>
<li aria-level="1">YAML and Git-based workflows.</li>
<li aria-level="1">OpenSLO validation through the Oslo CLI.</li>
<li aria-level="1">SLO-as-code automation.</li>
</ul>
<h2 id="pros"><b>Pros</b></h2>
<ul>
<li aria-level="1">Purpose-built for SLO management.</li>
<li aria-level="1">Works across heterogeneous observability environments.</li>
<li aria-level="1">Excellent SLO-as-code support.</li>
<li aria-level="1">Strong OpenSLO ecosystem involvement.</li>
<li aria-level="1">Useful for large organizations standardizing reliability practices.</li>
<li aria-level="1">Advanced capabilities such as composite SLOs and backtesting.</li>
</ul>
<h2 id="cons"><b>Cons</b></h2>
<ul>
<li aria-level="1">Another platform to buy, integrate, and maintain.</li>
<li aria-level="1">Probably excessive for a small team with a handful of services.</li>
<li aria-level="1">Less compelling if all telemetry already lives comfortably in one observability platform.</li>
<li aria-level="1">Public pricing is not as simple as self-service competitors.</li>
</ul>
<h2 id="pricing"><b>Pricing</b></h2>
<p>Nobl9 provides pricing through its sales process rather than publishing a simple per-monitor or per-host price. The pricing offering is aimed at selecting an option based on organizational requirements, so I would expect to request a quote for a real deployment.</p>
<h2 id="my-opinion"><b>My opinion</b></h2>
<p>Nobl9 is the one I would look at if SLOs themselves are becoming a platform concern. If multiple teams use different monitoring systems and I need a consistent reliability model across all of them, a dedicated abstraction layer makes sense.</p>
<p>What I dislike is the obvious trade-off: if I only have 10 services and everything already lives in Grafana, Datadog, or New Relic, adding another product may solve a problem I don’t actually have.</p>
<p> </p>
<h1><b>Feature comparison</b></h1>
<p>Here is a product-feature matrix for all SLO monitoring tools we are comparing here.</p>
<table>
<tbody>
<tr>
<td><b>Feature</b></td>
<td><b>Sematext</b></td>
<td><b>Datadog</b></td>
<td><b>New Relic</b></td>
<td><b>Grafana Cloud</b></td>
<td><b>Dynatrace</b></td>
<td><b>Elastic Observability</b></td>
<td><b>Honeycomb</b></td>
<td><b>Nobl9</b></td>
<td><b>Checkly</b></td>
</tr>
<tr>
<td>Availability SLOs</td>
<td>Yes</td>
<td>Yes</td>
<td>Yes</td>
<td>Yes</td>
<td>Yes</td>
<td>Yes</td>
<td>Yes</td>
<td>Yes</td>
<td>Yes*</td>
</tr>
<tr>
<td>Latency SLOs</td>
<td>Yes</td>
<td>Yes</td>
<td>Yes</td>
<td>Yes</td>
<td>Yes</td>
<td>Yes</td>
<td>Yes</td>
<td>Yes</td>
<td>Yes*</td>
</tr>
<tr>
<td>Custom metric SLIs</td>
<td>Limited</td>
<td>Yes</td>
<td>Yes</td>
<td>Yes</td>
<td>Yes</td>
<td>Yes</td>
<td>Event-based</td>
<td>Yes</td>
<td>No</td>
</tr>
<tr>
<td>Synthetic-monitor-based SLIs</td>
<td>Yes</td>
<td>Yes</td>
<td>Yes</td>
<td>Indirectly</td>
<td>Yes</td>
<td>Yes</td>
<td>Indirectly</td>
<td>Via data sources</td>
<td>Yes</td>
</tr>
<tr>
<td>Arbitrary query-based SLIs</td>
<td>Limited</td>
<td>Yes</td>
<td>Yes</td>
<td>Yes</td>
<td>Yes</td>
<td>Yes</td>
<td>Event/query-based</td>
<td>Yes</td>
<td>No</td>
</tr>
<tr>
<td>Error budgets</td>
<td>Yes</td>
<td>Yes</td>
<td>Yes</td>
<td>Yes</td>
<td>Yes</td>
<td>Yes</td>
<td>Yes</td>
<td>Yes</td>
<td>Limited*</td>
</tr>
<tr>
<td>Burn-rate alerting</td>
<td>Yes</td>
<td>Yes</td>
<td>Yes</td>
<td>Yes</td>
<td>Yes</td>
<td>Yes</td>
<td>Yes</td>
<td>Yes</td>
<td>Limited*</td>
</tr>
<tr>
<td>Rolling time windows</td>
<td>Yes</td>
<td>Yes</td>
<td>Yes</td>
<td>Yes</td>
<td>Yes</td>
<td>Yes</td>
<td>Yes</td>
<td>Yes</td>
<td>Depends on check configuration*</td>
</tr>
<tr>
<td>Calendar-based windows</td>
<td>Yes</td>
<td>Yes</td>
<td>Yes</td>
<td>Yes</td>
<td>Yes</td>
<td>Yes</td>
<td>Yes</td>
<td>Yes</td>
<td>No native SLO model</td>
</tr>
<tr>
<td>Multi-window burn-rate alerts</td>
<td>Limited</td>
<td>Yes</td>
<td>Yes</td>
<td>Yes</td>
<td>Yes</td>
<td>Yes</td>
<td>Yes</td>
<td>Yes</td>
<td>No native SLO model</td>
</tr>
<tr>
<td>SLO dashboard / overview</td>
<td>Yes</td>
<td>Yes</td>
<td>Yes</td>
<td>Yes</td>
<td>Yes</td>
<td>Yes</td>
<td>Yes</td>
<td>Yes</td>
<td>Check dashboards rather than dedicated SLO views</td>
</tr>
<tr>
<td>Historical error-budget analysis</td>
<td>Yes</td>
<td>Yes</td>
<td>Yes</td>
<td>Yes</td>
<td>Yes</td>
<td>Yes</td>
<td>Yes</td>
<td>Yes</td>
<td>Limited*</td>
</tr>
<tr>
<td>API support</td>
<td>Yes</td>
<td>Yes</td>
<td>Yes</td>
<td>Yes</td>
<td>Yes</td>
<td>Yes</td>
<td>Yes</td>
<td>Yes</td>
<td>Yes</td>
</tr>
<tr>
<td>Terraform support</td>
<td>Limited</td>
<td>Yes</td>
<td>Limited</td>
<td>Yes</td>
<td>Yes</td>
<td>Yes</td>
<td>Limited</td>
<td>Yes</td>
<td>Yes</td>
</tr>
<tr>
<td>SLO as code</td>
<td>Limited</td>
<td>Yes</td>
<td>API/Terraform workflows</td>
<td>Yes</td>
<td>Yes</td>
<td>Yes</td>
<td>API/configuration-driven</td>
<td>Excellent</td>
<td>Monitoring as code</td>
</tr>
<tr>
<td>OpenSLO support</td>
<td>No</td>
<td>No</td>
<td>No</td>
<td>No</td>
<td>No</td>
<td>No</td>
<td>No</td>
<td>Yes</td>
<td>No</td>
</tr>
<tr>
<td>Multi-source / vendor-neutral telemetry</td>
<td>No</td>
<td>Primarily Datadog</td>
<td>Primarily New Relic</td>
<td>Yes, within Grafana ecosystem</td>
<td>Primarily Dynatrace</td>
<td>Primarily Elastic</td>
<td>Primarily Honeycomb</td>
<td>Yes</td>
<td>No</td>
</tr>
<tr>
<td>Best data perspective</td>
<td>Synthetic + observability</td>
<td>Full-stack</td>
<td>Full-stack</td>
<td>Metrics / Prometheus / OTel</td>
<td>Full-stack + topology</td>
<td>Logs + metrics + APM + synthetics</td>
<td>Events + traces</td>
<td>Vendor-neutral</td>
<td>External synthetic checks</td>
</tr>
</tbody>
</table>
<p> </p>
<p>* <b>Checkly is the outlier in this table.</b> I would describe it as a synthetic monitoring and monitoring-as-code tool rather than a full native SLO management platform. It can measure availability and latency and can be used to implement reliability targets from checks, but it does not provide the same general-purpose SLI, error-budget, and multi-window burn-rate model as Datadog, Grafana Cloud, Dynatrace, Elastic, Honeycomb, or Nobl9.</p>
<p>This distinction is useful because it prevents the comparison from making Checkly look weaker at something it is not primarily designed to do. Its real strength is <b>defining production checks as code and using them to monitor APIs and critical user journeys</b>.</p>
<p> </p>
<h1><b>Which SLO monitoring tool would I choose?</b></h1>
<p>My choice would depend less on the number of features in the product and more on where my telemetry already lives and what I am actually trying to measure.</p>
<p>If I had to narrow the list down based on the problem rather than the vendor:</p>
<table>
<tbody>
<tr>
<td><b>What I need</b></td>
<td><b>Tools I would evaluate first</b></td>
</tr>
<tr>
<td>Simple external/API/user-journey SLOs</td>
<td><b>Sematext, Checkly</b></td>
</tr>
<tr>
<td>SLOs inside an existing observability platform</td>
<td><b>Datadog, New Relic, Dynatrace, Elastic</b></td>
</tr>
<tr>
<td>Prometheus/OpenTelemetry + infrastructure as code</td>
<td><b>Grafana Cloud</b></td>
</tr>
<tr>
<td>Tracing and event-centric observability</td>
<td><b>Honeycomb</b></td>
</tr>
<tr>
<td>Vendor-neutral, organization-wide SLO management</td>
<td><b>Nobl9</b></td>
</tr>
<tr>
<td>Maximum flexibility in a large enterprise environment</td>
<td><b>Dynatrace, Datadog, Elastic</b></td>
</tr>
</tbody>
</table>
<p>The important distinction for me is that <b>Checkly and Sematext are strongest when the SLI represents externally observable behavior</b>, while <b>Datadog, New Relic, Grafana Cloud, Dynatrace, Elastic, and Honeycomb can build SLOs from deeper application telemetry</b>. <b>Nobl9 is different again: its main value is separating the SLO/reliability layer from the underlying observability system.</b></p>
<p>So I would not pick a winner based on a feature checklist. I would first decide where the SLI should come from, and then choose the tool that makes that workflow the least painful.</p>
<p> </p>
<h1><b>Final thoughts</b></h1>
<p>The most important thing I have learned about SLO monitoring is that the tool is rarely the hard part. The difficult part is choosing an SLI that actually represents user experience and setting an objective that is neither meaningless nor impossible.</p>
<p>A monitoring tool can calculate an error budget perfectly and still give me the wrong answer if I am measuring the wrong thing.</p>
<p>So before comparing vendors too deeply, I would start with a few concrete questions:</p>
<ol>
<li aria-level="1"><b>What user behavior am I trying to protect?</b></li>
<li aria-level="1"><b>What counts as a good event and a bad event?</b></li>
<li aria-level="1"><b>Should every request count equally, or is time-based availability more appropriate?</b></li>
<li aria-level="1"><b>How much unreliability can the business actually tolerate?</b></li>
<li aria-level="1"><b>What should happen when the error budget is being consumed too quickly?</b></li>
<li aria-level="1"><b>Where will engineers go next to investigate the problem?</b></li>
</ol>
<p>Once those answers are clear, the choice usually becomes much easier. For most teams, I would strongly prefer using the SLO capability already available in the observability platform they trust—unless they have a real need for vendor-neutral, organization-wide SLO management.</p>
<p class="space-top"><a href="https://apps.sematext.com/ui/registration" class="button-big" target="_blank" rel="noopener noreferrer">Start Free Trial</a></p><hr class="hidden"><p>The post <a href="https://sematext.com/blog/best-slo-monitoring-tools/">Best 9 SLO Monitoring Tools in 2026: Review and Comparison Tables</a> appeared first on <a href="https://sematext.com">Sematext</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Top 12 Network Monitoring Tools in 2026: Complete Comparison &#038; Reviews</title>
		<link>https://sematext.com/blog/top-12-network-monitoring-tools-in-2026-complete-comparison-reviews/</link>
		
		<dc:creator><![CDATA[fulya.uluturk]]></dc:creator>
		<pubDate>Thu, 16 Jul 2026 10:17:47 +0000</pubDate>
				<category><![CDATA[Monitoring]]></category>
		<category><![CDATA[monitoring]]></category>
		<category><![CDATA[network]]></category>
		<category><![CDATA[network monitoring]]></category>
		<category><![CDATA[service monitoring]]></category>
		<guid isPermaLink="false">https://sematext.com/?p=70791</guid>

					<description><![CDATA[<p>Modern infrastructure is no longer a stack of routers, switches, and racks sitting in a single data center. Most teams now run a mix of Kubernetes clusters, virtual machines, managed cloud services, and SaaS dependencies spread across regions and providers. Knowing which device is up is not the same as knowing whether your application is [&#8230;]</p>
<p>The post <a href="https://sematext.com/blog/top-12-network-monitoring-tools-in-2026-complete-comparison-reviews/">Top 12 Network Monitoring Tools in 2026: Complete Comparison &#038; Reviews</a> appeared first on <a href="https://sematext.com">Sematext</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>Modern infrastructure is no longer a stack of routers, switches, and racks sitting in a single data center. Most teams now run a mix of Kubernetes clusters, virtual machines, managed cloud services, and SaaS dependencies spread across regions and providers. Knowing which device is up is not the same as knowing whether your application is healthy.</p>
<p>That shift is why network monitoring in 2026 looks very different from a decade ago. Traditional SNMP-based tools still have a place, but cloud-native teams need service-aware visibility, automatic topology discovery, and tight correlation with logs, metrics, and traces. The right tool can cut hours off incident response, surface cost leaks, and replace the architecture diagrams that are always getting outdated that nobody wants to maintain.</p>
<p>This guide compares the 12 best network monitoring tools available in 2026. For each one, you will find the features that matter, honest pros and cons, real pricing, and the kind of team it fits best.</p>
<h2 id="what-is-network-monitoring">What is Network Monitoring?</h2>
<p>Network monitoring is the continuous practice of collecting data about network devices, traffic, and connections to detect performance issues, outages, and security events before they affect users. The data can come from many sources: SNMP polling for device health, NetFlow or sFlow for traffic analysis, packet captures for deep inspection, eBPF for kernel-level connection visibility, and synthetic probes for path validation.</p>
<p>The category has expanded beyond LAN and WAN. In 2026, network monitoring also covers:</p>
<ul>
<li aria-level="1">Pod-to-pod and node-to-node traffic inside Kubernetes clusters</li>
<li aria-level="1">East-west traffic between microservices</li>
<li aria-level="1">Cross-region and cross-cloud connectivity costs</li>
<li aria-level="1">Connections to managed databases, message queues, and SaaS APIs</li>
<li aria-level="1">Suspicious outbound traffic that might signal a compromise</li>
</ul>
<p>Good network monitoring helps teams answer questions like: which services are talking to which databases right now, where is traffic getting slow, which connections are eating our cloud egress budget, and what process on which host opened that unexpected outbound connection.</p>
<h2 id="what-to-look-for-in-a-network-monitoring-tool">What to Look for in a Network Monitoring Tool</h2>
<p>Before comparing tools, here are the criteria that matter most in 2026.</p>
<h3 id="topology-discovery-and-visualization">Topology Discovery and Visualization</h3>
<p>A good tool draws the map for you. Static Visio diagrams go stale the moment someone deploys a new service. Look for automatic discovery and a real-time topology view that updates as your infrastructure changes.</p>
<h3 id="kubernetes-and-cloud-native-support">Kubernetes and Cloud-Native Support</h3>
<p>Containerized workloads are short-lived and dynamic. Tools designed for static IP-based device lists struggle here. Native Kubernetes support, pod-aware identity, and service detection are now table stakes for any team running clusters.</p>
<h3 id="protocol-visibility">Protocol Visibility</h3>
<p>Knowing that two hosts exchanged 500 MB tells you less than knowing that an application server made 12,000 PostgreSQL queries to a specific database. Tools that detect protocols (HTTP, PostgreSQL, MongoDB, Kafka, Redis, and others) give you context, not just byte counts.</p>
<h3 id="auto-detection-of-services">Auto-Detection of Services</h3>
<p>Manually labeling every host and process does not scale. The best tools recognize databases, caches, message queues, web servers, and runtimes automatically based on process names, container images, network signatures, and orchestrator metadata.</p>
<h3 id="correlation-with-logs-metrics-and-traces">Correlation with Logs, Metrics, and Traces</h3>
<p>A network alert in isolation is not very useful. Tools that let you pivot from a network connection to the related logs, application metrics, or distributed traces dramatically reduce time to root cause.</p>
<h3 id="alerting-and-thresholds">Alerting and Thresholds</h3>
<p>Alerts that fire on every minor blip get ignored. Look for tools that support tunable thresholds, baseline-aware anomaly detection, and clear escalation paths.</p>
<h3 id="pricing-transparency">Pricing Transparency</h3>
<p>Network monitoring is one of the categories where cost surprises are most common, especially with consumption-based pricing on flow data or device counts. Predictable, published pricing matters more than free tiers that turn into bill shock at scale.</p>
<h2 id="the-12-best-network-monitoring-tools-in-2026">The 12 Best Network Monitoring Tools in 2026</h2>
<h3 id="1-sematext-network-map">1. Sematext Network Map</h3>
<p><b>Best for: Cloud-native teams that want eBPF-powered service and infrastructure visibility inside a full observability platform</b></p>
<p><a href="https://sematext.com/wp-content/uploads/2026/05/network-map-2026-07-1-scaled.png"><img decoding="async" class="wp-image-70817 size-full" src="https://sematext.com/wp-content/uploads/2026/05/network-map-2026-07-1-scaled.png" alt="Sematext Network Map" width="2560" height="1498" srcset="https://sematext.com/wp-content/uploads/2026/05/network-map-2026-07-1-scaled.png 2560w, https://sematext.com/wp-content/uploads/2026/05/network-map-2026-07-1-300x176.png 300w, https://sematext.com/wp-content/uploads/2026/05/network-map-2026-07-1-1024x599.png 1024w, https://sematext.com/wp-content/uploads/2026/05/network-map-2026-07-1-768x450.png 768w, https://sematext.com/wp-content/uploads/2026/05/network-map-2026-07-1-1536x899.png 1536w, https://sematext.com/wp-content/uploads/2026/05/network-map-2026-07-1-2048x1199.png 2048w" sizes="(max-width: 2560px) 100vw, 2560px" /></a></p>
<p> </p>
<p><a href="https://sematext.com/network-monitoring/">Sematext Network Map</a> gives you a real-time, visual representation of your entire infrastructure topology. Using eBPF-powered network insights collected by the Sematext Agent, you can see how services, pods, containers, and processes communicate across Kubernetes clusters and standalone hosts without configuring anything beyond installing the agent.</p>
<p>Instead of polling devices and inferring relationships, Network Map shows the actual connections happening right now. When something breaks, you can immediately see which services are affected and trace the problem to its source. Two complementary views, Services View and Infrastructure View (see <a href="https://sematext.com/docs/network-map/#two-ways-to-view-your-infrastructure">details in the docs</a>), let you flip between application-level dependency analysis and Kubernetes infrastructure drill-down.</p>
<p><b>Key Features:</b></p>
<ul>
<li aria-level="1"><b>eBPF-based connection capture</b>: Kernel-level visibility into every connection, no port mirroring or packet capture required. Next to zero overhead.</li>
<li aria-level="1"><b>Two views, one map</b>: Services View for application dependencies, Infrastructure View for cluster, node, pod, container, and process drill-down</li>
<li aria-level="1"><b>Automatic service detection</b>: Over 100 service types recognized out of the box, including PostgreSQL, MySQL, MongoDB, Redis, Kafka, RabbitMQ, Elasticsearch, Nginx, HAProxy, Envoy, and more</li>
<li aria-level="1"><b>Protocol-aware connections</b>: Detects HTTP, PostgreSQL, MongoDB, Kafka, Redis, and dozens of other protocols with traffic volume per connection</li>
<li aria-level="1"><strong>Key metrics</strong>: network latency, round trip time (RTT), packet loss, retransmissions, CPU, memory, disk I/O, HTTP latency, HTTP response codes, etc.</li>
<li aria-level="1"><b>Custom thresholds</b>: Configurable warning and critical levels for CPU, memory, network I/O, and disk I/O so the map highlights what matters in your environment</li>
<li aria-level="1"><b>Color-coded health</b>: Green, yellow, and red service cards and connection lines make problems visible at a glance</li>
<li aria-level="1"><b>Kubernetes-native</b>: Cluster to node to pod to container to process navigation with namespace, deployment, and workload context</li>
<li aria-level="1"><b>Standalone host support</b>: Works equally well for VMs and bare-metal servers, not just containers</li>
<li aria-level="1"><b>Filtering and search</b>: Quickly focus on a namespace, service type, host, or specific service when the topology gets dense</li>
<li aria-level="1"><b>Unified observability</b>: Pivot from network connections to logs, metrics, traces, and synthetic checks in the same UI</li>
</ul>
<p><b>Pros:</b></p>
<ul>
<li aria-level="1">Truly cloud-native and Kubernetes-first, designed for dynamic infrastructure rather than retrofitted onto SNMP foundations</li>
<li aria-level="1">eBPF means no application code changes, no sidecars, and no instrumentation work, and next to zero overhead</li>
<li aria-level="1">Service detection works automatically for the technologies most teams actually run</li>
<li aria-level="1">Integrated with the rest of the Sematext platform, so traces, logs, and metrics live next to your topology</li>
<li aria-level="1">Significantly cheaper than the major enterprise observability platforms</li>
<li aria-level="1">Useful for discovering unknown dependencies, suspicious connections, data exfiltration patterns, and cross-region cost leakage</li>
<li aria-level="1">Replaces stale architecture diagrams with a live picture of what is actually running</li>
</ul>
<p><b>Cons:</b></p>
<ul>
<li aria-level="1">Newer product compared to legacy NMS incumbents, less brand recognition among traditional network teams</li>
<li aria-level="1">Focuses on host, container, and service visibility rather than deep packet inspection or SNMP-style switch and router polling</li>
</ul>
<p><b>Pricing:</b> Starts at $1.68 per host per month. Network Map scales with your Sematext Infrastructure Monitoring plan and retention. 14-day free trial, no credit card required.</p>
<p><b>Best For:</b> DevOps and SRE teams running Kubernetes or hybrid cloud infrastructure who want service-aware network visibility tightly integrated with logs, metrics, and traces. Teams that find traditional NMS tools too device-centric and modern APM platforms too expensive.</p>
<p><a href="https://apps.sematext.com/ui/registration" target="_blank" rel="noopener noreferrer">Get started with Sematext Network Map</a> or check out <a href="https://sematext.com/docs/network-map/">Network Map docs</a>.</p>
<h3 id="2-datadog-network-monitoring">2. Datadog Network Monitoring</h3>
<p><b>Best for: Enterprises already on Datadog who want network visibility inside the same platform</b></p>
<p>Datadog offers two related products: Network Performance Monitoring (NPM) for host-to-host and pod-to-pod traffic analysis, and Cloud Network Monitoring for cloud provider flow logs and load balancer telemetry. Both plug into the Datadog platform alongside APM, logs, and infrastructure monitoring.</p>
<p><b>Key Features:</b></p>
<ul>
<li aria-level="1">Host and Kubernetes pod-level traffic analysis via the Datadog Agent</li>
<li aria-level="1">VPC flow log analysis for AWS, Azure, and GCP</li>
<li aria-level="1">DNS monitoring with query-level visibility</li>
<li aria-level="1">Cloud load balancer and gateway telemetry</li>
<li aria-level="1">Integration with Datadog APM, logs, and infrastructure metrics</li>
</ul>
<p><b>Pros:</b></p>
<ul>
<li aria-level="1">Strong if you already use Datadog for everything else</li>
<li aria-level="1">Detailed flow analysis with rich filtering</li>
<li aria-level="1">AI-assisted anomaly detection through Watchdog</li>
</ul>
<p><b>Cons:</b></p>
<ul>
<li aria-level="1">Each Datadog product is billed separately, and bills add up quickly at scale</li>
<li aria-level="1">“Bill shock” is a recurring complaint from customers</li>
<li aria-level="1">Network products require agent and integrations setup separately from core APM</li>
</ul>
<p><b>Pricing:</b> Network Performance Monitoring starts at $5 per host per month. Cloud Network Monitoring is priced per analyzed flow. Costs scale with hosts, flows, and retention.</p>
<p><b>Best For:</b> Mid-size to large enterprises already standardized on Datadog who can absorb the platform-wide cost.</p>
<h3 id="3-dynatrace">3. Dynatrace</h3>
<p><b>Best for: Large enterprises wanting AI-driven full-stack observability with network context</b></p>
<p>Dynatrace covers network visibility as part of its broader Davis AI observability platform. The OneAgent captures process-level network connections alongside code-level traces and infrastructure metrics.</p>
<p><b>Key Features:</b></p>
<ul>
<li aria-level="1">OneAgent captures host, process, and connection-level data automatically</li>
<li aria-level="1">Smartscape topology view combining services, processes, and infrastructure</li>
<li aria-level="1">Davis AI for root cause analysis across signals</li>
<li aria-level="1">Multi-cloud and Kubernetes coverage</li>
<li aria-level="1">Strong support for traditional enterprise workloads like SAP and mainframe</li>
</ul>
<p><b>Pros:</b></p>
<ul>
<li aria-level="1">Truly automatic instrumentation with minimal manual configuration</li>
<li aria-level="1">AI correlation across network, traces, logs, and metrics</li>
<li aria-level="1">Strong enterprise governance and compliance features</li>
</ul>
<p><b>Cons:</b></p>
<ul>
<li aria-level="1">Premium pricing, often the highest in the market</li>
<li aria-level="1">OneAgent has a significant resource footprint compared to lightweight collectors</li>
<li aria-level="1">Steep learning curve across the many Dynatrace apps and modules</li>
</ul>
<p><b>Pricing:</b> Custom pricing through sales. Typically positioned at the high end of the enterprise observability market.</p>
<p><b>Best For:</b> Large enterprises with complex hybrid environments and the budget for a premium AI-powered observability platform.</p>
<h3 id="4-kentik">4. Kentik</h3>
<p><b>Best for: Network teams running large hybrid, multi-cloud, and internet-facing infrastructure</b></p>
<p>Kentik focuses on what it calls network intelligence: deep traffic analysis, BGP and internet path visibility, and AI-assisted investigation for hybrid and multi-cloud networks. It is particularly strong for service providers, SaaS companies, and large enterprises with significant cloud and internet exposure.</p>
<p><b>Key Features:</b></p>
<ul>
<li aria-level="1">NetFlow, sFlow, IPFIX, VPC flow logs, and synthetic test ingestion</li>
<li aria-level="1">BGP and internet performance visibility</li>
<li aria-level="1">Kentik AI Advisor and Cause Analysis for guided troubleshooting</li>
<li aria-level="1">DDoS detection and mitigation analytics</li>
<li aria-level="1">Hybrid and multi-cloud topology</li>
</ul>
<p><b>Pros:</b></p>
<ul>
<li aria-level="1">Industry-leading depth for traffic analytics and internet path visibility</li>
<li aria-level="1">Strong for cloud egress cost analysis</li>
<li aria-level="1">AI-assisted investigation reduces time on complex incidents</li>
</ul>
<p><b>Cons:</b></p>
<ul>
<li aria-level="1">Pricing aimed at mid-market and enterprise, not small teams</li>
<li aria-level="1">Steeper learning curve than general-purpose monitoring tools</li>
<li aria-level="1">Focused on NetOps workflows more than application-level observability</li>
</ul>
<p><b>Pricing:</b> Custom pricing through sales. Aimed at enterprise budgets.</p>
<p><b>Best For:</b> Mid-market and enterprise NetOps and SRE teams managing complex hybrid and internet-connected networks.</p>
<h3 id="5-cisco-thousandeyes">5. Cisco ThousandEyes</h3>
<p><b>Best for: Teams that need path visibility across the public internet and SaaS providers</b></p>
<p>ThousandEyes monitors network paths from your users and applications to wherever they need to go: SaaS apps, cloud regions, third-party APIs, and your own services. It is the go-to tool for understanding ISP, CDN, and public internet performance.</p>
<p><b>Key Features:</b></p>
<ul>
<li aria-level="1">Synthetic agents on user devices, in cloud regions, and on enterprise networks</li>
<li aria-level="1">Hop-by-hop path visualization across the internet</li>
<li aria-level="1">BGP route monitoring and outage detection</li>
<li aria-level="1">SaaS application performance tests (Microsoft 365, Salesforce, Zoom, and similar)</li>
<li aria-level="1">Internet outage detection and notifications</li>
</ul>
<p><b>Pros:</b></p>
<ul>
<li aria-level="1">Best-in-class for public internet and SaaS path visibility</li>
<li aria-level="1">Wide global agent network</li>
<li aria-level="1">Strong correlation between user experience and underlying network paths</li>
</ul>
<p><b>Cons:</b></p>
<ul>
<li aria-level="1">Does not cover host or pod-level traffic inside your infrastructure</li>
<li aria-level="1">Pricing scales quickly with agent counts and test frequency</li>
<li aria-level="1">Less useful as a standalone tool, usually has to be paired with another monitoring platform</li>
</ul>
<p><b>Pricing:</b> Subscription-based, priced per agent and per test. Custom quotes through sales.</p>
<p><b>Best For:</b> Enterprises with significant SaaS, hybrid work, and internet-facing service dependencies that need to prove whether the problem is them, their ISP, or a SaaS provider.</p>
<h3 id="6-solarwinds-network-performance-monitor">6. SolarWinds Network Performance Monitor</h3>
<p><b>Best for: Traditional enterprise IT teams managing on-premises networks</b></p>
<p>SolarWinds Network Performance Monitor is one of the longest-running enterprise NPM products. It excels at SNMP-based monitoring of routers, switches, firewalls, wireless access points, and other traditional network gear.</p>
<p><b>Key Features:</b></p>
<ul>
<li aria-level="1">SNMP v1, v2c, and v3 polling with deep multi-vendor support</li>
<li aria-level="1">Automated Layer 2 and Layer 3 topology mapping</li>
<li aria-level="1">NetPath for hop-by-hop path analysis</li>
<li aria-level="1">Wireless network monitoring and heat maps</li>
<li aria-level="1">Integration with the broader SolarWinds Observability portfolio</li>
</ul>
<p><b>Pros:</b></p>
<ul>
<li aria-level="1">Deep coverage for traditional network gear from Cisco, Juniper, Aruba, Fortinet, and many others</li>
<li aria-level="1">Mature alerting and reporting features</li>
<li aria-level="1">Familiar to most network engineers</li>
</ul>
<p><b>Cons:</b></p>
<ul>
<li aria-level="1">Built for static network device inventories rather than dynamic cloud-native workloads</li>
<li aria-level="1">Self-hosted deployment requires Windows Server infrastructure</li>
<li aria-level="1">Modernization toward SaaS has been gradual</li>
</ul>
<p><b>Pricing:</b> Starts around $1,995 for SolarWinds NPM perpetual license. SaaS pricing through SolarWinds Observability is consumption-based.</p>
<p><b>Best For:</b> Enterprise NetOps teams managing campus, branch, or data center networks where SNMP and traditional NPM workflows dominate.</p>
<h3 id="7-auvik">7. Auvik</h3>
<p><b>Best for: MSPs and IT teams managing many distributed sites</b></p>
<p>Auvik is a cloud-based network monitoring platform aimed primarily at managed service providers and multi-site IT teams. Its automated topology mapping and per-site collector model make it easy to onboard new networks quickly.</p>
<p><b>Key Features:</b></p>
<ul>
<li aria-level="1">Cloud-managed with lightweight on-premises collectors per site</li>
<li aria-level="1">Automatic Layer 1, 2, and 3 topology discovery and visualization</li>
<li aria-level="1">TrafficInsights for NetFlow, sFlow, J-Flow, and IPFIX analysis</li>
<li aria-level="1">Configuration backup and change tracking</li>
<li aria-level="1">Multi-tenant architecture for MSPs</li>
</ul>
<p><b>Pros:</b></p>
<ul>
<li aria-level="1">Fast time to value, sites usually online within an hour</li>
<li aria-level="1">Excellent automated topology maps that update in real time</li>
<li aria-level="1">Multi-tenant model purpose-built for MSPs</li>
<li aria-level="1">Configuration management and syslog included</li>
</ul>
<p><b>Cons:</b></p>
<ul>
<li aria-level="1">Per-billable-device pricing can get expensive at scale</li>
<li aria-level="1">Less depth than Kentik or SolarWinds NPM for deep traffic analytics</li>
<li aria-level="1">Not designed for application-level or container-level visibility</li>
</ul>
<p><b>Pricing:</b> Per-billable-device pricing through sales. Free trial available.</p>
<p><b>Best For:</b> MSPs and IT teams managing dozens or hundreds of distributed sites where rapid onboarding matters.</p>
<h3 id="8-paessler-prtg-network-monitor">8. Paessler PRTG Network Monitor</h3>
<p><b>Best for: Small and mid-size organizations that prefer sensor-based, all-in-one monitoring</b></p>
<p>PRTG is a long-established Windows-based monitoring tool that uses a sensor model: each metric, port, or check is a sensor, and you pay for the total number of sensors. It covers networks, servers, applications, and IoT devices through SNMP, WMI, NetFlow, and HTTP probes.</p>
<p><b>Key Features:</b></p>
<ul>
<li aria-level="1">250+ pre-built sensor types for network devices, servers, and applications</li>
<li aria-level="1">SNMP, WMI, NetFlow, sFlow, J-Flow, and IPFIX support</li>
<li aria-level="1">Maps, dashboards, and reporting included</li>
<li aria-level="1">On-premises and PRTG Hosted options</li>
<li aria-level="1">Free tier up to 100 sensors</li>
</ul>
<p><b>Pros:</b></p>
<ul>
<li aria-level="1">Easy to install and configure on Windows</li>
<li aria-level="1">Broad sensor catalog covers network and infrastructure in one tool</li>
<li aria-level="1">Free tier is genuinely useful for small environments</li>
<li aria-level="1">Strong fit for SMB and mid-market IT generalists</li>
</ul>
<p><b>Cons:</b></p>
<ul>
<li aria-level="1">Sensor-based licensing gets complex and expensive as environments grow</li>
<li aria-level="1">Windows-centric architecture (probes can be on Linux but the core is Windows-only)</li>
<li aria-level="1">Less suitable for dynamic Kubernetes workloads</li>
</ul>
<p><b>Pricing:</b> Perpetual licenses from around $2,149 for 500 sensors. PRTG Hosted available as SaaS. Free up to 100 sensors.</p>
<p><b>Best For:</b> Small and mid-size organizations running mixed Windows and Linux infrastructure that want a single tool for network and server monitoring.</p>
<h3 id="9-manageengine-opmanager">9. ManageEngine OpManager</h3>
<p><b>Best for: IT teams that want broad multi-vendor monitoring at a moderate price</b></p>
<p>OpManager is part of the ManageEngine portfolio. It provides SNMP, WMI, CLI, and Telnet-based monitoring with extensive multi-vendor support and offers both subscription and perpetual licensing.</p>
<p><b>Key Features:</b></p>
<ul>
<li aria-level="1">200+ pre-built device templates across Cisco, Juniper, HP, Dell, Fortinet, and others</li>
<li aria-level="1">Network Configuration Manager add-on for backup and compliance</li>
<li aria-level="1">Workflow automation for routine remediation</li>
<li aria-level="1">NetFlow Analyzer add-on for traffic analysis</li>
<li aria-level="1">On-premises deployment with broad OS support</li>
</ul>
<p><b>Pros:</b></p>
<ul>
<li aria-level="1">Significantly cheaper than SolarWinds NPM with comparable feature depth</li>
<li aria-level="1">Perpetual licensing option for teams that prefer capital over operating expenses</li>
<li aria-level="1">Strong multi-vendor coverage out of the box</li>
</ul>
<p><b>Cons:</b></p>
<ul>
<li aria-level="1">Interface feels dated compared to modern SaaS tools</li>
<li aria-level="1">Add-on modules increase the total cost</li>
<li aria-level="1">Self-hosted only, no fully managed SaaS option</li>
</ul>
<p><b>Pricing:</b> Starts around $245 per year for 25 devices. Perpetual licenses available. NetFlow Analyzer and Network Configuration Manager priced separately.</p>
<p><b>Best For:</b> Mid-market IT teams wanting broad on-premises network and server monitoring at a moderate price.</p>
<h3 id="10-logicmonitor">10. LogicMonitor</h3>
<p><b>Best for: Hybrid infrastructure teams wanting SaaS-based monitoring across networks, servers, and cloud</b></p>
<p>LogicMonitor is a SaaS infrastructure monitoring platform that covers network devices, servers, cloud resources, and containers from a single console. The Edwin AI engine adds anomaly detection and event correlation across signals.</p>
<p><b>Key Features:</b></p>
<ul>
<li aria-level="1">3,000+ integrations and modules across networks, servers, and cloud</li>
<li aria-level="1">Cloud-managed with on-premises collectors</li>
<li aria-level="1">Edwin AI for anomaly detection and alert correlation</li>
<li aria-level="1">Per-device licensing rather than per-sensor or per-interface</li>
<li aria-level="1">Topology mapping and synthetic checks</li>
</ul>
<p><b>Pros:</b></p>
<ul>
<li aria-level="1">One of the broadest integration libraries in the market</li>
<li aria-level="1">Predictable per-device pricing</li>
<li aria-level="1">Strong fit for hybrid environments mixing data center and cloud</li>
</ul>
<p><b>Cons:</b></p>
<ul>
<li aria-level="1">Enterprise pricing, not aimed at small teams</li>
<li aria-level="1">Less focused on application-level or service-level visibility than Datadog or Dynatrace</li>
<li aria-level="1">Some users report complex initial setup for advanced features</li>
</ul>
<p><b>Pricing:</b> Custom pricing through sales. Aimed at mid-market and enterprise.</p>
<p><b>Best For:</b> Mid-market and enterprise IT teams managing hybrid infrastructure that want a single SaaS tool for networks, servers, and cloud.</p>
<h3 id="11-zabbix">11. Zabbix</h3>
<p><b>Best for: Teams wanting a free, open-source enterprise-grade monitoring platform</b></p>
<p>Zabbix is one of the most widely deployed open-source monitoring tools. It supports SNMP, agent-based, agentless, and API-based monitoring for networks, servers, applications, and cloud services. The project has been actively developed for over two decades.</p>
<p><b>Key Features:</b></p>
<ul>
<li aria-level="1">SNMP, IPMI, JMX, ODBC, agent, and agentless monitoring</li>
<li aria-level="1">Network discovery and topology maps</li>
<li aria-level="1">Flexible templating and macros</li>
<li aria-level="1">Notifications across many channels</li>
<li aria-level="1">Distributed proxy architecture for large environments</li>
</ul>
<p><b>Pros:</b></p>
<ul>
<li aria-level="1">Completely free and open-source under AGPL</li>
<li aria-level="1">Mature, production-proven at very large scale</li>
<li aria-level="1">Active community and commercial support available</li>
<li aria-level="1">Flexible enough to cover networks, servers, and applications</li>
</ul>
<p><b>Cons:</b></p>
<ul>
<li aria-level="1">Requires operational expertise to deploy, scale, and maintain</li>
<li aria-level="1">UI feels less polished than commercial SaaS tools</li>
<li aria-level="1">Initial configuration can be time-consuming</li>
</ul>
<p><b>Pricing:</b> Free. Commercial support and training available through Zabbix LLC.</p>
<p><b>Best For:</b> Teams with the operational capacity to run their own monitoring infrastructure and that want full control without licensing costs.</p>
<h3 id="12-cilium-hubble">12. Cilium Hubble</h3>
<p><b>Best for: Kubernetes teams wanting open-source eBPF-native network observability</b></p>
<p>Cilium Hubble is the observability layer for Cilium, the CNCF-graduated eBPF-based networking and security project. It provides flow-level visibility, service maps, and policy enforcement insights for Kubernetes clusters.</p>
<p><b>Key Features:</b></p>
<ul>
<li aria-level="1">eBPF-based flow capture without sidecars</li>
<li aria-level="1">L3, L4, and L7 visibility including HTTP, gRPC, Kafka, and DNS</li>
<li aria-level="1">Service map visualization</li>
<li aria-level="1">Network policy verification and dropped-flow analysis</li>
<li aria-level="1">Integration with Prometheus and Grafana</li>
</ul>
<p><b>Pros:</b></p>
<ul>
<li aria-level="1">Completely free and open-source under Apache 2.0</li>
<li aria-level="1">Native eBPF approach with low overhead</li>
<li aria-level="1">Strong for Kubernetes security and network policy use cases</li>
<li aria-level="1">Tight integration with Cilium CNI</li>
</ul>
<p><b>Cons:</b></p>
<ul>
<li aria-level="1">Requires Cilium as the CNI plugin, which is a significant architectural choice</li>
<li aria-level="1">Operational expertise needed to deploy and scale Hubble</li>
<li aria-level="1">Less suited for standalone hosts, VMs, or non-Kubernetes workloads</li>
</ul>
<p><b>Pricing:</b> Free. Commercial support through Isovalent (now part of Cisco).</p>
<p><b>Best For:</b> Kubernetes-native teams that have adopted or are willing to adopt Cilium as their CNI and want deep eBPF-based flow observability without commercial licensing.</p>
<h2 id="network-monitoring-tools-comparison-table">Network Monitoring Tools Comparison Table</h2>
<table>
<thead>
<tr>
<th><b>Tool</b></th>
<th><b>Deployment</b></th>
<th><b>Kubernetes</b></th>
<th><b>Telemetry</b></th>
<th><b>Pricing</b></th>
<th><b>Best For</b></th>
</tr>
</thead>
<tbody>
<tr>
<td><b>Sematext Network Map</b></td>
<td>SaaS</td>
<td>Native</td>
<td>eBPF, agent metrics</td>
<td>Per host</td>
<td>Cloud-native teams wanting service-aware visibility</td>
</tr>
<tr>
<td><b>Datadog Network Monitoring</b></td>
<td>SaaS</td>
<td>Yes</td>
<td>Agent flows, VPC flow logs</td>
<td>Per host + per flow</td>
<td>Enterprises already on Datadog</td>
</tr>
<tr>
<td><b>Dynatrace</b></td>
<td>SaaS or managed</td>
<td>Yes</td>
<td>OneAgent</td>
<td>Custom</td>
<td>Large enterprises wanting AI-driven observability</td>
</tr>
<tr>
<td><b>Kentik</b></td>
<td>SaaS</td>
<td>Yes</td>
<td>NetFlow, sFlow, VPC flows, BGP</td>
<td>Custom</td>
<td>NetOps in hybrid and multicloud</td>
</tr>
<tr>
<td><b>Cisco ThousandEyes</b></td>
<td>SaaS</td>
<td>Limited</td>
<td>Synthetic agents, BGP</td>
<td>Per agent</td>
<td>Internet and SaaS path visibility</td>
</tr>
<tr>
<td><b>SolarWinds NPM</b></td>
<td>Self-hosted or SaaS</td>
<td>Limited</td>
<td>SNMP, NetPath</td>
<td>License + maintenance</td>
<td>Traditional enterprise NetOps</td>
</tr>
<tr>
<td><b>Auvik</b></td>
<td>SaaS</td>
<td>Limited</td>
<td>SNMP, NetFlow</td>
<td>Per device</td>
<td>MSPs and multi-site IT</td>
</tr>
<tr>
<td><b>Paessler PRTG</b></td>
<td>Self-hosted or SaaS</td>
<td>Limited</td>
<td>SNMP, WMI, NetFlow</td>
<td>Per sensor</td>
<td>SMB and mid-market generalists</td>
</tr>
<tr>
<td><b>ManageEngine OpManager</b></td>
<td>Self-hosted</td>
<td>Limited</td>
<td>SNMP, WMI, NetFlow</td>
<td>Per device</td>
<td>Mid-market multi-vendor</td>
</tr>
<tr>
<td><b>LogicMonitor</b></td>
<td>SaaS</td>
<td>Yes</td>
<td>Agent, SNMP, cloud APIs</td>
<td>Per device</td>
<td>Hybrid infrastructure teams</td>
</tr>
<tr>
<td><b>Zabbix</b></td>
<td>Self-hosted</td>
<td>Yes</td>
<td>Agent, SNMP, IPMI</td>
<td>Free</td>
<td>Open-source enterprise</td>
</tr>
<tr>
<td><b>Cilium Hubble</b></td>
<td>Self-hosted</td>
<td>Native</td>
<td>eBPF</td>
<td>Free</td>
<td>Kubernetes-native eBPF</td>
</tr>
</tbody>
</table>
<h2 id="how-to-choose-the-right-network-monitoring-tool">How to Choose the Right Network Monitoring Tool</h2>
<h3 id="start-with-your-stack">Start With Your Stack</h3>
<p>A team running mostly Cisco switches and on-premises servers has a very different set of needs from a team running Kubernetes on EKS. SNMP-based tools like SolarWinds, ManageEngine, and PRTG remain the right answer for traditional network gear. For Kubernetes and dynamic cloud workloads, eBPF-based tools like Sematext Network Map and Cilium Hubble give you visibility that legacy NMS products cannot match.</p>
<h3 id="decide-whether-you-want-a-standalone-or-integrated-tool">Decide Whether You Want a Standalone or Integrated Tool</h3>
<p>Network monitoring used to be a separate tool maintained by a separate team. That model still works for some organizations, but most modern teams benefit from network visibility that lives next to logs, metrics, and traces. Sematext, Datadog, Dynatrace, and LogicMonitor all let you correlate network signals with application data. Standalone specialists like Kentik and ThousandEyes go deeper in their niche, often paired with a broader observability platform.</p>
<h3 id="model-your-cost-at-scale">Model Your Cost at Scale</h3>
<p>Pricing models vary widely. Per-sensor models like PRTG can balloon as you add checks. Per-device models like Auvik and LogicMonitor are predictable but can become expensive as device counts grow. Per-host models like Sematext and Datadog scale with infrastructure size. Always run a quick projection for your expected scale rather than going by starter-tier pricing.</p>
<h3 id="test-before-you-buy">Test Before You Buy</h3>
<p>Most commercial tools offer free trials. Open-source tools cost only your time. Pick two or three candidates that fit your environment and run them on a real workload for a week or two. You will learn more from a short pilot than from any vendor demo.</p>
<h3 id="do-not-forget-about-people">Do Not Forget About People</h3>
<p>A tool nobody uses is worse than no tool at all. Whatever you choose, make sure your on-call engineers find the UI usable, the alerts trustworthy, and the data easy to share across teams.</p>
<h2 id="conclusion">Conclusion</h2>
<p>Network monitoring in 2026 is no longer a single category. Some teams need deep SNMP visibility for traditional gear. Others need eBPF-based service maps for Kubernetes. Many need both, plus correlation with logs, metrics, and traces.</p>
<p>For cloud-native and hybrid teams, <b>Sematext Network Map</b> stands out as the most pragmatic choice in 2026. It gives you eBPF-powered service and infrastructure visibility, automatic detection of over 100 service types, Kubernetes-native drill-down, and integration with logs, metrics, traces, and synthetic monitoring in one platform. All of that comes at a fraction of the cost of the major enterprise observability incumbents.</p>
<p>If your environment is dominated by traditional network gear, SolarWinds, ManageEngine, and Auvik remain solid choices. If you need internet and SaaS path visibility, ThousandEyes is hard to beat. If you want fully open-source, Zabbix covers the broad case and Cilium Hubble covers the Kubernetes-native case.</p>
<p>Whatever you choose, prioritize tools that fit how your infrastructure actually works today, not how it worked five years ago.</p>
<p><b>Ready to see your infrastructure as it really is?</b> <a href="https://apps.sematext.com/ui/registration" target="_blank" rel="noopener noreferrer">Try Sematext Network Map free for 14 days</a>, no credit card required.</p>
<h2 id="faq">FAQ</h2>
<h3 id="what-is-the-difference-between-network-monitoring-and-observability">What is the difference between network monitoring and observability?</h3>
<p>Network monitoring focuses on the health and behavior of network infrastructure: devices, interfaces, traffic, paths, and connections. Observability is a broader practice that combines logs, metrics, traces, and topology to understand the health of the entire system, including applications. Modern tools like Sematext, Datadog, and Dynatrace blur the line by treating network visibility as one signal among many.</p>
<h3 id="do-i-need-separate-network-monitoring-if-i-already-have-apm">Do I need separate network monitoring if I already have APM?</h3>
<p>APM tells you what your application code is doing. Network monitoring tells you what is happening at the network connection and infrastructure level. The two are complementary. A slow database query shows up in APM. A flaky network path, a saturated link, or a rogue process making outbound calls shows up in network monitoring, but not in APM. Most teams benefit from both, ideally on the same platform.</p>
<h3 id="what-is-ebpf-and-why-does-it-matter-for-network-monitoring">What is eBPF and why does it matter for network monitoring?</h3>
<p>eBPF (extended Berkeley Packet Filter) is a Linux kernel technology that lets tools safely run sandboxed programs inside the kernel to observe events such as network connections, system calls, and packet processing. For network monitoring, eBPF allows tools to capture connection-level data with very low overhead and without modifying applications. Tools like Sematext Network Map and Cilium Hubble use eBPF to build accurate, real-time topology views without sidecars, port mirroring, or packet captures.</p>
<h3 id="can-network-monitoring-tools-work-with-kubernetes">Can network monitoring tools work with Kubernetes?</h3>
<p>Yes, but with very different levels of effectiveness. Tools designed for static device inventories struggle with the dynamic nature of pods and containers. eBPF-based and Kubernetes-aware tools like Sematext Network Map, Cilium Hubble, Datadog NPM, and Dynatrace are built for this environment. Traditional SNMP-focused tools can still monitor the underlying nodes, but they will not give you pod-level or service-level visibility.</p>
<h3 id="what-are-the-best-free-or-open-source-network-monitoring-tools-in-2026">What are the best free or open-source network monitoring tools in 2026?</h3>
<p>Zabbix remains the most widely deployed open-source choice for general infrastructure and network monitoring. Nagios and Icinga are still in active use for traditional environments. LibreNMS and OpenNMS are strong for SNMP-heavy networks. For Kubernetes-native, eBPF-based observability, Cilium Hubble is the leading open-source option. All of these are free to use but require operational expertise to deploy and scale.</p>
<h3 id="how-much-do-network-monitoring-tools-cost">How much do network monitoring tools cost?</h3>
<p>Pricing varies enormously. Open-source tools are free but carry operational cost. Per-device tools like Auvik and OpManager typically range from a few dollars to tens of dollars per device per month. Per-host tools like Sematext Network Map start around $1.68 per host per month. Per-sensor tools like PRTG depend on how many checks you configure. Enterprise platforms like Datadog, Dynatrace, Kentik, and LogicMonitor are usually priced through sales and can run from tens of thousands to hundreds of thousands of dollars per year depending on scale.</p>
<p class="space-top"><a href="https://apps.sematext.com/ui/registration" class="button-big" target="_blank" rel="noopener noreferrer">Start Free Trial</a></p><hr class="hidden"><p>The post <a href="https://sematext.com/blog/top-12-network-monitoring-tools-in-2026-complete-comparison-reviews/">Top 12 Network Monitoring Tools in 2026: Complete Comparison &#038; Reviews</a> appeared first on <a href="https://sematext.com">Sematext</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Using AI to Instrument Applications with OpenTelemetry</title>
		<link>https://sematext.com/blog/using-ai-to-instrument-applications-with-opentelemetry/</link>
		
		<dc:creator><![CDATA[fulya.uluturk]]></dc:creator>
		<pubDate>Thu, 21 May 2026 07:20:49 +0000</pubDate>
				<category><![CDATA[OpenTelemetry]]></category>
		<category><![CDATA[Programming languages & frameworks]]></category>
		<guid isPermaLink="false">https://sematext.com/?p=70785</guid>

					<description><![CDATA[<p>OpenTelemetry is one of the best things that’s happened to observability in the last decade. It’s open. It has SDKs for every language that matters. It’s vendor neutral. The OTel community has been doing the hard work of standardizing how applications emit telemetry, so that you, the engineer, don’t have to learn five different agent [&#8230;]</p>
<p>The post <a href="https://sematext.com/blog/using-ai-to-instrument-applications-with-opentelemetry/">Using AI to Instrument Applications with OpenTelemetry</a> appeared first on <a href="https://sematext.com">Sematext</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p><a href="https://opentelemetry.io/" target="_blank" rel="noopener noreferrer">OpenTelemetry</a> is one of the best things that’s happened to observability in the last decade. It’s open. It has SDKs for every language that matters. It’s vendor neutral. The OTel community has been doing the hard work of standardizing how applications emit telemetry, so that you, the engineer, don’t have to learn five different agent formats to monitor five different services.</p>
<p>But there’s a part of the OTel pitch that often gets glossed over: <b>somebody still has to instrument the application</b>. And that part isn’t quick or easy. Even now.</p>
<h2 id="the-instrumentation-tax"><b>The instrumentation tax</b></h2>
<p>Modern applications aren’t a single binary anymore. At Sematext we run <b>30+ microservices</b> to power the <a href="https://sematext.com/docs/">Sematext Cloud</a> platform: alerts, metrics receivers and consumers, log receivers and consumers, user experience services, tracing pipelines, network map, various APIs and more, across Java (Spring Boot), Go, and a few other stacks.</p>
<p><img decoding="async" class="alignnone wp-image-70788 size-full" src="https://sematext.com/wp-content/uploads/2026/05/network-map-big.png" alt="" width="2500" height="1332" srcset="https://sematext.com/wp-content/uploads/2026/05/network-map-big.png 2500w, https://sematext.com/wp-content/uploads/2026/05/network-map-big-300x160.png 300w, https://sematext.com/wp-content/uploads/2026/05/network-map-big-1024x546.png 1024w, https://sematext.com/wp-content/uploads/2026/05/network-map-big-768x409.png 768w, https://sematext.com/wp-content/uploads/2026/05/network-map-big-1536x818.png 1536w, https://sematext.com/wp-content/uploads/2026/05/network-map-big-2048x1091.png 2048w" sizes="(max-width: 2500px) 100vw, 2500px" /></p>
<p>That’s a lot of surface area, several languages, multiple frameworks, different build systems for different stacks. None of this is unusual for a mature system. This means that instrumentation effort grows with that diversity, so any mechanism that helps minimize instrumentation mistakes will be welcomed by engineers tasked with instrumentation.</p>
<p>If you want <b>end-to-end tracing</b> through that stack, the kind that actually tells you where a slow request spent its time, you can’t just instrument one service. You have to instrument the whole chain: frontend → API gateway → backend service A → backend service B → database. Skip a hop and the trace breaks. The dependency graph the trace gives you stops being useful exactly at the boundary you didn’t instrument.</p>
<p>So in practice, “let’s adopt OpenTelemetry” turns into a checklist of dozens of services that each need their own instrumentation work. The good news is that it doesn’t have to happen all at once and AI can help.</p>
<h2 id="how-much-does-opentelemetry-instrumentation-cost"><b>How much does OpenTelemetry instrumentation cost?</b></h2>
<p>By cost, we mean the cost to an engineer, the team, and the organization. We can look at it as a non-monetary cost, but if we trace (pun intended!) this cost all the way down then yes, there is also a financial cost associated with this effort.</p>
<p>Three things make this hard, even with OTel:</p>
<p><b>Prioritization. </b>Instrumenting a service competes with shipping features and fixing bugs. It’s preventive work; its value shows up the next time something breaks at 3am, not this sprint. That’s a hard sell to a product manager.</p>
<p><b>Unknown territory. </b>When the chain spans services you didn’t write, in languages you don’t use day to day, you’re spending most of your time on context switch overhead. You’re not adding instrumentation; you’re re-learning a framework you saw once two years ago.</p>
<p><b>Time needed even for auto-instrumentation. </b>“Auto-instrumentation” means no code changes. It doesn’t mean no work. For one service the loop typically goes:</p>
<ol>
<li aria-level="1">Read the right OTel SDK docs for your language</li>
<li aria-level="1">Pick the right auto-instrumentation package (there are usually three options, only one of which is current)</li>
<li aria-level="1">Install it in the build (pom.xml, package.json, requirements.txt, …)</li>
<li aria-level="1">Configure the OTLP endpoint, the auth header, the service name</li>
<li aria-level="1">Restart, hit the service, watch what happens</li>
<li aria-level="1">Debug the first attempt: wrong port (4317 vs 4318 vs 4338), wrong protocol (http/protobuf vs grpc), wrong auth header (Bearer vs vendor-specific), region mismatch on the endpoint</li>
<li aria-level="1">Verify the data lands in the right place in your observability tool</li>
<li aria-level="1">Multiply by the number of services in your chain</li>
</ol>
<p>Forty minutes to two hours per service if you’re moving carefully, and that’s just for traces and metrics. The OTel auto-instrumentation packages don’t ship logs in most SDKs. For logs you need to switch to manual instrumentation, which is another SDK init block per service.</p>
<h2 id="and-then-theres-custom-opentelemetry-instrumentation"><b>And then there’s custom OpenTelemetry instrumentation</b></h2>
<p>The above buys you “spans for every incoming HTTP request” and a generic metrics set. The moment you want anything specific (e.g., a custom span attribute for the user’s account tier, a business metric counting checkouts, a log enriched with the trace ID so you can correlate logs and traces for faster Root Cause Analysis), you’re back in manual-instrumentation land, writing SDK code in every service you care about. The auto path ends; the per-language SDK learning curve begins.</p>
<p>For one service that’s an afternoon. For thirty services that’s a quarter. What can we do about this?</p>
<h2 id="can-we-use-ai-to-instrument-applications-with-opentelemetry"><b>Can we use AI to instrument applications with OpenTelemetry?</b></h2>
<p>The instrumentation work (pick the SDK, set the env vars, debug the endpoint, verify it landed) is exactly the kind of structured, repetitive, well-documented task an AI agent does well. The blockers aren’t intellectual; they’re “look up the right thing, paste it in the right place, watch for the obvious gotcha.”</p>
<p>That’s not “let the AI do your engineering.” It’s “let the AI do the parts that already had a right answer, written down somewhere, and just needed someone to fetch it.”</p>
<p>We tried this for instrumenting applications against Sematext Cloud. The result is a small,but highly valuable open-source artifact: a <b>Claude Code Agent Skill</b> that walks an engineer through OTel instrumentation conversationally. It’s plain markdown, lives in our public Github repository, and works with any AI agent that can read a URL.</p>
<h2 id="what-the-otel-instrumentation-ai-skill-does"><b>What the OTel instrumentation AI skill does</b></h2>
<p>The Sematext OTel skill at <a href="https://github.com/sematext/sematext-otel-onboarding/blob/main/skills/SKILL.md" target="_blank" rel="noopener noreferrer">sematext-otel-onboarding/blob/main/skills/SKILL.md</a> is the AI-readable version of “how to wire your application to Sematext.” When loaded into Claude Code (or any agent that can fetch a markdown URL), it triages the user through six short questions:</p>
<ol>
<li aria-level="1">Sematext region (US or EU)</li>
<li aria-level="1">Which App types you’re wiring (Tracing, Logs, Monitoring, any combination)</li>
<li aria-level="1">Flow: managed OTLP endpoint or Sematext Agent</li>
<li aria-level="1">Protocol: HTTP (default) or gRPC</li>
<li aria-level="1">Language and deployment environment</li>
<li aria-level="1">Auto or manual instrumentation</li>
</ol>
<p>Then it produces the exact env-var block, parameterized to your answers, including:</p>
<ul>
<li aria-level="1">The correct OTLP endpoint URL for your region and protocol</li>
<li aria-level="1">The Sematext-specific X-API-TOKEN header (different from the standard Authorization: Bearer … most OTel docs show, easy to miss)</li>
<li aria-level="1">One header per signal type, so you only configure what you’re using</li>
<li aria-level="1">A pointer to a runnable reference example in the same repo, in your language</li>
</ul>
<p>Similarly, you can use the skill not only to add instrumentation to uninstrumented applications, but also to fix broken instrumentation that’s not really working. Auto-instrumentation doesn’t ship logs? The skill flags that and asks if you want to switch to manual. Region-token mismatch? The skill warns explicitly. Custom header convention? Documented. The skill is opinionated and aware of the setup required where the official OpenTelemetry docs may be silent or difficult to understand and follow.</p>
<h2 id="what-an-instrumentation-session-looks-like"><b>What an instrumentation session looks like</b></h2>
<p>In practice, an engineer with Claude Code in their editor opens their service’s directory and pastes:</p>
<p><code>Use <a href="https://github.com/sematext/sematext-otel-onboarding/blob/main/skills/SKILL.md" target="_blank" rel="noopener noreferrer">https://github.com/sematext/sematext-otel-onboarding/blob/main/skills/SKILL.md</a> to instrument this app for Sematext.<br>
Region: US. App type: Tracing. Token: &lt;pasted-from-Sematext-UI&gt;.</code></p>
<p>Claude loads the skill, reads the project’s files to figure out the language and framework, asks the two remaining triage questions, then proposes the diff to the project (adds the OTel SDK to the build file, adds the env vars to docker-compose or systemd or .env or wherever they belong, and shows you what to expect in the Sematext UI within 60 seconds). You review the diff. You apply. You restart. You see traces.</p>
<p>Compared to the manual path (read docs, pick SDK, install, configure, debug, verify), we’d expect the instrumentation effort time to first data to drop from the typical 40 to 120 minutes per service to a handful of minutes. For an organization adopting OTel across dozens of services, that compounds quickly. A quarter of part-time effort becomes a couple of focused days. Thousands of dollars in engineering time drops to a much more sane number. The effort has a positive ROI.</p>
<h2 id="what-the-skill-doesnt-do"><b>What the skill doesn’t do</b></h2>
<p>This is where AI posts usually start hand-waving. Here’s the honest list:</p>
<ul>
<li aria-level="1"><b>It doesn’t write custom span attributes or business metrics for you. </b>It writes the boilerplate that gets you to the point where you <i>can</i> write those. The judgment about what to measure is still yours. The benefit is that the skill gives you all the scaffolding, a working instrumentation, so the effort of collecting custom/business metrics becomes significantly lower.</li>
<li aria-level="1"><b>It doesn’t psychic-debug your network. </b>If your service can’t reach the OTLP endpoint on first run (corporate proxy, missing TLS cert chain, wrong port), the skill points at the common causes, but you still have to look at the service’s own logs to confirm what happened.</li>
<li aria-level="1"><b>It doesn’t change OTel’s reality. </b>Auto-instrumentation still doesn’t ship logs in most SDKs. AI doesn’t fix the SDK. But it does tell you up front, so you don’t spend an hour wondering why your Logs App is empty.</li>
</ul>
<h2 id="try-it-the-skill-is-vendor-agnostic"><b>Try it, the skill is vendor-agnostic</b></h2>
<p>The skill is open-source and lives in our <a href="https://github.com/sematext/sematext-otel-onboarding/" target="_blank" rel="noopener noreferrer">OTel onboarding repo</a>. The same repo has runnable reference apps for Node.js, Java, Python, .NET, and PHP across baremetal, Docker, and Kubernetes deployments, so if you want to see what the skill is going to walk you through, the reference is right there.</p>
<p>If you have Claude Code, point it at the URL above. If you use a different AI agent, the skill is just markdown. Load it however your agent loads documentation. There’s no install step; there’s no vendor lock-in. The skill shared is not Sematext-specific. Sematext’s contribution is the knowledge, encoded in a format an AI can act on.</p>
<h2 id="where-this-is-going"><b>Where this is going</b></h2>
<p>The OTel skill is one example of a broader pattern we think makes sense for observability tools: <b>knowledge as something an AI can use, not just something a human can read.</b></p>
<p>A few directions we’re exploring:</p>
<ul>
<li aria-level="1"><b>Per-language sub-skills </b>for deeper, opinionated guidance when a language has subtle gotchas (Node async hooks, Java agent attach, Python startup ordering)</li>
<li aria-level="1"><b>In-product wiring </b>so the App creation page in Sematext Cloud gives you a one-click “use AI to set this up” alongside the existing manual instructions, with your region and token pre-filled into the prompt</li>
<li aria-level="1"><b>Skills for the rest of the observability journey </b>(picking sensible default alerts, creating dashboards, interpreting RCA results), each as a small, auditable markdown file you can use, fork, or ignore</li>
</ul>
<p>The bet is that the value of an observability platform isn’t just the data it collects; it’s how quickly an engineer can go from “we should monitor this” to “we’re monitoring it and we know what to do when it breaks.” AI doesn’t replace the judgment in that loop as of yet. But it can absolutely replace the busywork around it.</p>
<p>If you give the skill a try and find a gap, <a href="https://github.com/sematext/sematext-otel-onboarding/blob/main/skills/sematext-otel.md" target="_blank" rel="noopener noreferrer">open a PR</a>. The fastest way for this pattern to get good is for more people to use it on more apps.</p>
<p class="space-top"><a href="https://apps.sematext.com/ui/registration" class="button-big" target="_blank" rel="noopener noreferrer">Start Free Trial</a></p><hr class="hidden"><p>The post <a href="https://sematext.com/blog/using-ai-to-instrument-applications-with-opentelemetry/">Using AI to Instrument Applications with OpenTelemetry</a> appeared first on <a href="https://sematext.com">Sematext</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Pull Request Velocity as a Proxy for AI Usage for Software Development</title>
		<link>https://sematext.com/blog/pull-request-velocity-as-a-proxy-for-ai-usage-for-software-development/</link>
		
		<dc:creator><![CDATA[Otis]]></dc:creator>
		<pubDate>Tue, 31 Mar 2026 07:06:27 +0000</pubDate>
				<category><![CDATA[Engineering]]></category>
		<guid isPermaLink="false">https://sematext.com/?p=70751</guid>

					<description><![CDATA[<p>While AI have usage has been growing steadily for the last several years, the LLM models noticeably improved around the end of 2025. Specifically, they become more viable for software development. We are seeing the results. The feature and product delivery has picked up. One way to visualize this is by looking at the number [&#8230;]</p>
<p>The post <a href="https://sematext.com/blog/pull-request-velocity-as-a-proxy-for-ai-usage-for-software-development/">Pull Request Velocity as a Proxy for AI Usage for Software Development</a> appeared first on <a href="https://sematext.com">Sematext</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>While AI have usage has been growing steadily for the last several years, the LLM models noticeably improved around the end of 2025. Specifically, they become more viable for software development. We are seeing the results. The feature and product delivery has picked up. One way to visualize this is by looking at the number of pull requests for your organization / software development teams.  This chart shows the number of Github pull requests created by a team. Can you spot when AI usage increased?</p>
<p><img decoding="async" class="alignnone  wp-image-70752" src="https://sematext.com/wp-content/uploads/2026/03/gh-pr-ai-usage-proxy-300x86.png" alt="" width="677" height="194" srcset="https://sematext.com/wp-content/uploads/2026/03/gh-pr-ai-usage-proxy-300x86.png 300w, https://sematext.com/wp-content/uploads/2026/03/gh-pr-ai-usage-proxy-1024x293.png 1024w, https://sematext.com/wp-content/uploads/2026/03/gh-pr-ai-usage-proxy-768x220.png 768w, https://sematext.com/wp-content/uploads/2026/03/gh-pr-ai-usage-proxy-1536x440.png 1536w, https://sematext.com/wp-content/uploads/2026/03/gh-pr-ai-usage-proxy-2048x586.png 2048w" sizes="(max-width: 677px) 100vw, 677px" /></p>
<p>It starts in late November, 2025. This marks the beginning of increased AI usage (for coding) in Sematext. That’s when the LLMs got better. It <i>roughly </i>matches the change in velocity as visualized in JIRA.</p>
<h3 id="individual-ai-adoption">Individual AI Adoption</h3>
<p>The blurred part are PR author names, which we can use for filtering. If we look at trends of individuals we can spot early adopters like this one:</p>
<p><img decoding="async" class="alignnone  wp-image-70753" src="https://sematext.com/wp-content/uploads/2026/03/gh-pr-early-ai-user-300x65.png" alt="" width="660" height="143" srcset="https://sematext.com/wp-content/uploads/2026/03/gh-pr-early-ai-user-300x65.png 300w, https://sematext.com/wp-content/uploads/2026/03/gh-pr-early-ai-user-1024x221.png 1024w, https://sematext.com/wp-content/uploads/2026/03/gh-pr-early-ai-user-768x166.png 768w, https://sematext.com/wp-content/uploads/2026/03/gh-pr-early-ai-user-1536x332.png 1536w, https://sematext.com/wp-content/uploads/2026/03/gh-pr-early-ai-user-2048x442.png 2048w" sizes="(max-width: 660px) 100vw, 660px" /></p>
<p>Or another individual who started making more use of AI later:</p>
<p><img decoding="async" class="alignnone  wp-image-70754" src="https://sematext.com/wp-content/uploads/2026/03/gh-pr-late-ai-user-300x64.png" alt="" width="656" height="140" srcset="https://sematext.com/wp-content/uploads/2026/03/gh-pr-late-ai-user-300x64.png 300w, https://sematext.com/wp-content/uploads/2026/03/gh-pr-late-ai-user-1024x219.png 1024w, https://sematext.com/wp-content/uploads/2026/03/gh-pr-late-ai-user-768x164.png 768w, https://sematext.com/wp-content/uploads/2026/03/gh-pr-late-ai-user-1536x329.png 1536w, https://sematext.com/wp-content/uploads/2026/03/gh-pr-late-ai-user-2048x438.png 2048w" sizes="(max-width: 656px) 100vw, 656px" /></p>
<h3 id="source-github-webhook-events">Source: Github WebHook Events</h3>
<p>This data comes into Sematext via <a href="https://sematext.com/docs/integration/github-webhook-events-integration/">Github Webhook Events.</a> It takes about 5-10 minutes to set up. It can be set up at the Github organization level or for individual repositories.</p>
<h3 id="a-word-of-caution">A Word of Caution</h3>
<ol>
<li aria-level="1">There are many software development styles. There are people who commit frequently and incrementally and there are those who keep things to themselves until everything is nearly done. This is a fun chart to look at and is helpful when you want to get the feel for the “pulse” of a team or even an individual. But be careful not to judge people on this sort of data alone. Use this with a grain of salt and in combination with other inputs, observations, etc.</li>
<li aria-level="1">Creating more code or PRs doesn’t always equal better code or higher effectiveness. A person may be tapping in the dark trying to debug or implement something with the help of AI and, in the process, creating a lot of (temporary?) code and PRs.</li>
<li aria-level="1">As velocity increases, so will regressions, unless you take countermeasures. See <a href="https://www.linkedin.com/pulse/faster-coding-ai-increased-regressions-otis-gospodneti%C4%87-bi6ve/" target="_blank" rel="noopener noreferrer">Faster Coding with AI and Increased Regressions</a>.</li>
</ol>
<p class="space-top"><a href="https://apps.sematext.com/ui/registration" class="button-big" target="_blank" rel="noopener noreferrer">Start Free Trial</a></p><hr class="hidden"><p>The post <a href="https://sematext.com/blog/pull-request-velocity-as-a-proxy-for-ai-usage-for-software-development/">Pull Request Velocity as a Proxy for AI Usage for Software Development</a> appeared first on <a href="https://sematext.com">Sematext</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Running OpenTelemetry at Scale: Architecture Patterns for 100s of Services</title>
		<link>https://sematext.com/blog/running-opentelemetry-at-scale-architecture-patterns-for-100s-of-services/</link>
		
		<dc:creator><![CDATA[fulya.uluturk]]></dc:creator>
		<pubDate>Tue, 03 Mar 2026 12:06:59 +0000</pubDate>
				<category><![CDATA[OpenTelemetry]]></category>
		<category><![CDATA[Tracing]]></category>
		<category><![CDATA[distributed tracing]]></category>
		<category><![CDATA[microservices]]></category>
		<category><![CDATA[opentelemetry]]></category>
		<guid isPermaLink="false">https://sematext.com/?p=70550</guid>

					<description><![CDATA[<p>It feels great getting OpenTelemetry working in a demo environment. Spans appear, metrics flow, you connect it to a backend and everything lights up in a satisfying cascade. You write the internal doc, you present it to the team, but it’s just a matter of time when somebody on the team asks: “Great, so how [&#8230;]</p>
<p>The post <a href="https://sematext.com/blog/running-opentelemetry-at-scale-architecture-patterns-for-100s-of-services/">Running OpenTelemetry at Scale: Architecture Patterns for 100s of Services</a> appeared first on <a href="https://sematext.com">Sematext</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p><span style="font-weight: 400;">It feels great getting OpenTelemetry working in a demo environment. Spans appear, metrics flow, you connect it to a backend and everything lights up in a satisfying cascade. You write the internal doc, you present it to the team, but it’s just a matter of time when somebody on the team asks: “Great, so how do we roll this out to all 100 services?” If you are at that point on your OTel journey, this article will help you roll out OTel to production.</span></p>
<p><span style="font-weight: 400;">Running OTel across a handful of services and running it across a few hundred are genuinely different problems. The instrumentation part stays roughly the same. Everything around it — how you collect the data, how you route it, how you make sure a traffic spike in one region does not take down your entire observability pipeline — that is where teams either build something resilient or spend the next six months fire-fighting because of inadequate planning or suboptimal architecture.</span></p>
<p><span style="font-weight: 400;">I wrote this article to share the patterns that actually hold up at scale: collector tiers, load balancing strategies, sampling at volume, and multi-cluster setups. Everything comes with real config examples because “it depends” is only useful advice if you can see what it depends on.</span></p>
<p><span style="font-weight: 400;">See</span><a href="https://sematext.com/blog/from-debugging-to-slos-how-opentelemetry-changes-the-way-teams-do-observability/" target="_blank" rel="noopener"> <span style="font-weight: 400;">How OpenTelemetry changes the way teams do observability</span></a><span style="font-weight: 400;"> for why OpenTelemetry matters and how it shifts focus from traditional metrics and logs to full, end-to-end observability.</span></p>
<h2 id="why-a-single-collector-falls-apart-and-when"><b>Why a Single Collector Falls Apart (and When)</b></h2>
<p><span style="font-weight: 400;">Most OTel tutorials show you a single collector instance receiving spans from all your services and forwarding everything to a backend. That setup works until about the point where it stops working, which tends to happen quietly and at the worst possible time. You are not going to notice a single collector struggling until it is already dropping data, buffering is maxed out, and your traces have gaps you cannot explain.</span></p>
<p><span style="font-weight: 400;">The core issue is that a single collector is both a single point of failure and a resource bottleneck. At low traffic it sits there looking fine. Add a few dozen services, let traffic spike during a product launch or a retry storm, and you will watch it start falling behind. The exporter queue fills up. Backpressure kicks in. Services start dropping spans rather than blocking on the export. By the time anyone notices, you have lost the exact telemetry you needed to understand what just happened.</span></p>
<div style="background: rgba(220,38,38,0.06); border-left: 3px solid #DC2626; border-radius: 0 8px 8px 0; padding: 22px 26px; margin: 36px 0; font-size: 17px; color: #7f1d1d;"><strong style="color: #991b1b;">The failure mode is silent.</strong> <b></b><span style="font-weight: 400;"> When a collector falls behind, it does not usually crash spectacularly. It drops spans without loud errors, your traces become incomplete, and your dashboards show suspiciously clean latency numbers because the slow requests stopped being recorded. If your p99 looks unexpectedly healthy during an incident, check your collector queue depth before trusting it.</span></div>
<p><span style="font-weight: 400;">The solution is to stop thinking about the collector as a single process and start thinking about it as a tier. Two tiers cover most production scenarios. Three tiers cover the rest. The architecture you need depends on your traffic, whether you need tail-based sampling, and how many backends you are exporting to.</span></p>
<p><span style="font-weight: 400;">Let me make this more specific: if you have fewer than 20 services and under 500 requests per second total, a single well-configured collector will likely hold up (yes, of course it depends on the underlying hardware/resources). At 20 to 80 services or 500 to 5,000 RPS, the two-tier model becomes worthwhile. Above 80 services or 5,000 RPS, you need the full tiered setup with </span><a href="https://opentelemetry.io/docs/collector/deploy/gateway/" target="_blank" rel="noopener noreferrer"><span style="font-weight: 400;">trace-aware load balancing</span></a><span style="font-weight: 400;"> and </span><a href="https://sematext.com/blog/opentelemetry-production-monitoring-what-breaks-and-how-to-prevent-it/#tail-sampling" target="_blank" rel="noopener"><span style="font-weight: 400;">tail-based sampling</span></a><span style="font-weight: 400;"> at the gateway. </span></p>
<p><span style="font-weight: 400;">For more information on common production pitfalls and strategies to prevent them, see </span><a href="https://sematext.com/blog/opentelemetry-production-monitoring-what-breaks-and-how-to-prevent-it/" target="_blank" rel="noopener"> <span style="font-weight: 400;">OpenTelemetry Production Monitoring: What Breaks and How to Prevent It</span></a><span style="font-weight: 400;">.</span></p>
<h2 id="collector-tiers-the-architecture-that-actually-scales"><b>Collector Tiers: The Architecture That Actually Scales</b></h2>
<p><span style="font-weight: 400;">The tiered collector model separates two concerns that should never have been combined in the first place: getting data off your services quickly, and doing something intelligent with that data before it hits your backend.</span></p>
<p><span style="font-weight: 400;">Before getting into the architecture, it helps to know that the OTel Collector can run in three modes — and in a scaled setup, you will use all three:</span></p>
<ul>
<li style="font-weight: 400;" aria-level="1"><b>Agent</b><span style="font-weight: 400;"> — a collector running on the same host as your services, collecting telemetry locally and forwarding it upstream. It stays thin: no heavy processing, just receive-and-forward.</span></li>
<li style="font-weight: 400;" aria-level="1"><b>Gateway</b><span style="font-weight: 400;"> — a collector running as a standalone service, receiving data from agents (or directly from SDKs) and doing the heavier work: sampling, routing, fan-out to backends, attribute redaction.</span></li>
</ul>
<p><b>Combined</b><span style="font-weight: 400;"> — the full pattern, where agent collectors feed into gateway collectors. Agents handle what only makes sense per-host (host metrics, file logs, resource detection). Gateways handle what only makes sense centrally (tail-based sampling, cross-service routing, policy management). The </span><a href="https://opentelemetry.io/docs/collector/deploy/gateway/#combined-deployment-of-collectors-as-agents-and-gateways" target="_blank" rel="noopener noreferrer"> <span style="font-weight: 400;">OTel Collector deployment docs</span></a><span style="font-weight: 400;"> call this the combined deployment pattern.</span></p>
<p><span style="font-weight: 400;">The tiered setup this article describes is the combined pattern. Here is what it looks like:</span></p>
<div style="margin: 32px 0;">
<div style="font-size: 11px; font-weight: bold; letter-spacing: 0.12em; color: #64748b; text-transform: uppercase; margin-bottom: 24px;">TWO-TIER COLLECTOR ARCHITECTURE</div>
<p><!-- SERVICES ROW --></p>
<div style="text-align: center; font-size: 10px; font-weight: bold; letter-spacing: 0.1em; color: #94a3b8; margin-bottom: 8px;">SERVICES</div>
<div style="display: flex; justify-content: center; gap: 12px; margin-bottom: 6px;">
<div style="background: #1e3a5f; border: 2px solid #f59e0b; border-radius: 6px; padding: 10px 18px; text-align: center; min-width: 90px;">
<div style="font-size: 9px; font-weight: bold; letter-spacing: 0.1em; color: #f59e0b; margin-bottom: 3px;">Service</div>
<div style="font-size: 15px; font-weight: bold; color: #ffffff;">App A</div>
</div>
<div style="background: #1e3a5f; border: 2px solid #f59e0b; border-radius: 6px; padding: 10px 18px; text-align: center; min-width: 90px;">
<div style="font-size: 9px; font-weight: bold; letter-spacing: 0.1em; color: #f59e0b; margin-bottom: 3px;">Service</div>
<div style="font-size: 15px; font-weight: bold; color: #ffffff;">App B</div>
</div>
<div style="background: #1e3a5f; border: 2px solid #f59e0b; border-radius: 6px; padding: 10px 18px; text-align: center; min-width: 90px;">
<div style="font-size: 9px; font-weight: bold; letter-spacing: 0.1em; color: #f59e0b; margin-bottom: 3px;">Service</div>
<div style="font-size: 15px; font-weight: bold; color: #ffffff;">App C</div>
</div>
<div style="background: #1e3a5f; border: 2px solid #f59e0b; border-radius: 6px; padding: 10px 18px; text-align: center; min-width: 90px;">
<div style="font-size: 9px; font-weight: bold; letter-spacing: 0.1em; color: #f59e0b; margin-bottom: 3px;">Service</div>
<div style="font-size: 15px; font-weight: bold; color: #ffffff;">App N</div>
</div>
</div>
<p><!-- Arrow down --></p>
<div style="text-align: center; color: #94a3b8; font-size: 20px; line-height: 1; margin: 4px 0;">↓</div>
<p><!-- TIER 1 LABEL --></p>
<div style="text-align: center; font-size: 10px; font-weight: bold; letter-spacing: 0.1em; color: #94a3b8; margin-bottom: 8px;">TIER 1 — AGENT / SIDECAR COLLECTORS</div>
<div style="display: flex; justify-content: center; gap: 12px; margin-bottom: 6px;">
<div style="background: #14532d; border: 2px solid #14532d; border-radius: 6px; padding: 10px 18px; text-align: center; min-width: 90px;">
<div style="font-size: 9px; font-weight: bold; letter-spacing: 0.1em; color: #86efac; margin-bottom: 3px;">Agent</div>
<div style="font-size: 15px; font-weight: bold; color: #ffffff;">Collector</div>
</div>
<div style="background: #14532d; border: 2px solid #14532d; border-radius: 6px; padding: 10px 18px; text-align: center; min-width: 90px;">
<div style="font-size: 9px; font-weight: bold; letter-spacing: 0.1em; color: #86efac; margin-bottom: 3px;">Agent</div>
<div style="font-size: 15px; font-weight: bold; color: #ffffff;">Collector</div>
</div>
<div style="background: #14532d; border: 2px solid #14532d; border-radius: 6px; padding: 10px 18px; text-align: center; min-width: 90px;">
<div style="font-size: 9px; font-weight: bold; letter-spacing: 0.1em; color: #86efac; margin-bottom: 3px;">Agent</div>
<div style="font-size: 15px; font-weight: bold; color: #ffffff;">Collector</div>
</div>
<div style="background: #14532d; border: 2px solid #14532d; border-radius: 6px; padding: 10px 18px; text-align: center; min-width: 90px;">
<div style="font-size: 9px; font-weight: bold; letter-spacing: 0.1em; color: #86efac; margin-bottom: 3px;">Agent</div>
<div style="font-size: 15px; font-weight: bold; color: #ffffff;">Collector</div>
</div>
</div>
<p><!-- Arrow down --></p>
<div style="text-align: center; color: #94a3b8; font-size: 20px; line-height: 1; margin: 4px 0;">↓</div>
<p><!-- TIER 2 LABEL --></p>
<div style="text-align: center; font-size: 10px; font-weight: bold; letter-spacing: 0.1em; color: #94a3b8; margin-bottom: 8px;">TIER 2 — GATEWAY COLLECTORS</div>
<div style="display: flex; justify-content: center; gap: 12px; margin-bottom: 6px;">
<div style="background: #1e293b; border: 2px solid #1e293b; border-radius: 6px; padding: 10px 18px; text-align: center; min-width: 110px;">
<div style="font-size: 9px; font-weight: bold; letter-spacing: 0.1em; color: #94a3b8; margin-bottom: 3px;">Gateway</div>
<div style="font-size: 15px; font-weight: bold; color: #ffffff;">Collector (HA)</div>
</div>
<div style="background: #1e293b; border: 2px solid #1e293b; border-radius: 6px; padding: 10px 18px; text-align: center; min-width: 110px;">
<div style="font-size: 9px; font-weight: bold; letter-spacing: 0.1em; color: #94a3b8; margin-bottom: 3px;">Gateway</div>
<div style="font-size: 15px; font-weight: bold; color: #ffffff;">Collector (HA)</div>
</div>
</div>
<p><!-- Arrow down --></p>
<div style="text-align: center; color: #94a3b8; font-size: 20px; line-height: 1; margin: 4px 0;">↓</div>
<p><!-- BACKENDS --></p>
<div style="display: flex; justify-content: center; gap: 12px; margin-bottom: 6px;">
<div style="background: #1e293b; border: 2px solid #1e293b; border-radius: 6px; padding: 10px 18px; text-align: center; min-width: 90px;">
<div style="font-size: 9px; font-weight: bold; letter-spacing: 0.1em; color: #94a3b8; margin-bottom: 3px;">Backend</div>
<div style="font-size: 15px; font-weight: bold; color: #ffffff;">Traces</div>
</div>
<div style="background: #1e293b; border: 2px solid #1e293b; border-radius: 6px; padding: 10px 18px; text-align: center; min-width: 90px;">
<div style="font-size: 9px; font-weight: bold; letter-spacing: 0.1em; color: #94a3b8; margin-bottom: 3px;">Backend</div>
<div style="font-size: 15px; font-weight: bold; color: #ffffff;">Metrics</div>
</div>
<div style="background: #1e293b; border: 2px solid #1e293b; border-radius: 6px; padding: 10px 18px; text-align: center; min-width: 90px;">
<div style="font-size: 9px; font-weight: bold; letter-spacing: 0.1em; color: #94a3b8; margin-bottom: 3px;">Backend</div>
<div style="font-size: 15px; font-weight: bold; color: #ffffff;">Logs</div>
</div>
</div>
<p><!-- Caption --></p>
<div style="font-size: 13px; color: #94a3b8; font-style: italic; margin-top: 16px; text-align: center; max-width: 480px; margin-left: auto; margin-right: auto;">Tier 1 agents sit close to services and do minimal work. Tier 2 gateways handle sampling, routing, and backend fan-out.</div>
</div>
<h3 id="tier-1-collectors-running-as-agents"><b>Tier 1: Collectors running as agents</b></h3>
<p><span style="font-weight: 400;">The agent tier runs as a sidecar. Its job is exactly one thing: receive telemetry from the services and forward it as fast as possible. No tail-based sampling, no complex routing logic, no fan-out to multiple backends. The only processing you want at this tier is cheap and stateless: adding resource attributes like cluster name, node name, and environment; batching spans to reduce connection overhead; and basic filtering to drop genuinely worthless spans like health check endpoints generating thousands of spans per minute and telling you nothing.</span></p>
<div style="background: rgba(217,119,6,0.06); border-left: 3px solid #D97706; border-radius: 0 8px 8px 0; padding: 22px 26px; margin: 36px 0; font-size: 17px; color: #78350f;"><span style="font-weight: 400;">Only stamp resource attributes that are low-cardinality and apply to the whole node or pod — things like environment, cluster name, and region. Adding high-cardinality values like user IDs or request IDs as resource attributes will explode your metrics storage, because each unique value becomes a separate time series.</span></div>
<div>
<div style="font-size: 11px; font-weight: bold; letter-spacing: 0.12em; color: #64748b; text-transform: uppercase;">TIER 1 AGENT COLLECTOR CONFIG</div>
<pre># Tier 1: runs as DaemonSet, minimal processing
receivers:
  otlp:
    protocols:
      grpc: {endpoint: "0.0.0.0:4317"}
      http: {endpoint: "0.0.0.0:4318"}

processors:
  batch:                    # batch before forwarding
    send_batch_size: 1024
    timeout: 5s
  resourcedetection:        # stamp node/pod metadata
    detectors: [k8snode, env]
  filter/drop_healthchecks:
    spans:
      exclude:
        match_type: regexp
        attributes:
          - {key: http.route, value: ".*/health.*"}

exporters:
  otlp:
    # forward to gateway tier, not directly to backend
    endpoint: "otel-gateway:4317"
    sending_queue:
      enabled: true
      num_consumers: 4
      queue_size: 500

service:
  pipelines:
    traces:
      receivers:  [otlp]
      processors: [batch, resourcedetection, filter/drop_healthchecks]
      exporters:  [otlp]
</pre>
<div style="font-size: 13px; color: #94a3b8; font-style: italic; text-align: center; max-width: 480px; margin-left: auto; margin-right: auto; margin-bottom: 24px;">Agent config stays thin. Anything heavier than batching and attribute stamping belongs in the gateway tier.</div>
<h3 id="tier-2-collectors-running-as-gateways"><b>Tier 2: Collectors running as gateways</b></h3>
<p><span style="font-weight: 400;">The gateway tier is where the interesting work happens: tail-based sampling, fan-out to multiple backends, and the routing logic that sends traces, metrics, and logs where they need to go. Once you introduce a gateway tier, it needs careful resource sizing. In practice, that means running at least two gateway collectors behind a load balancer to </span><b>avoid single points of failure</b><span style="font-weight: 400;">.</span></p>
<p><span style="font-weight: 400;">How you deploy them depends on your environment. In Kubernetes, that typically means a Deployment scaled by load rather than node count. In a VM-based setup, two or more collector processes behind a hardware or software load balancer works just as well. The important thing is that the gateway tier scales horizontally based on traffic, not based on how many hosts you have.</span></p>
<p><span style="font-weight: 400;">Two to four instances is a reasonable starting point for a deployment handling roughly 1,000 to 5,000 spans per second across 20 to 50 services. Beyond that, sizing should be driven primarily by your tail-based sampling configuration — specifically the </span><code>decision_wait</code><span style="font-weight: 400;"> window and the </span><code>num_traces</code><span style="font-weight: 400;"> value — which determine how much trace state each gateway must hold in memory.</span></p>
<h2 id="load-balancing-the-subtle-trap-with-tail-based-sampling"><b>Load Balancing: The Subtle Trap with Tail-Based Sampling</b></h2>
<p><span style="font-weight: 400;">If you are using tail-based sampling and running multiple gateway collector instances, standard round-robin load balancing will silently break your sampling decisions. Tail-based sampling works by collecting all spans for a given trace and then making a single keep-or-drop decision once the trace is complete. With round-robin, spans for the same trace end up scattered across different collector instances. Each instance only sees a fragment, so no instance ever has enough context to make a valid decision.</span></p>
<div style="background: rgba(217,119,6,0.06); border-left: 3px solid #D97706; border-radius: 0 8px 8px 0; padding: 22px 26px; margin: 36px 0; font-size: 17px; color: #78350f;"><i><span style="font-weight: 400;">The symptom is traces that look complete but are not. You will see traces that hit your sampling rate but are missing spans from certain services, because those spans went to a different collector instance that independently decided to drop its fragment. This is one of the harder things to debug because the data loss is structured rather than random.</span></i></div>
<p><span style="font-weight: 400;">The solution is </span><b>trace-aware load balancing</b><span style="font-weight: 400;">, where spans are routed to gateway instances based on their trace ID. The OTel Collector has a </span><a href="https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/exporter/loadbalancingexporter" target="_blank" rel="noopener noreferrer"><span style="font-weight: 400;">loadbalancing exporter</span></a><span style="font-weight: 400;"> built for exactly this. It consistently hashes trace IDs to the same downstream collector, which means all spans for a given trace always end up in the same place regardless of which agent they came from.</span></p>
<div style="font-size: 11px; font-weight: bold; letter-spacing: 0.12em; color: #64748b; text-transform: uppercase;">LOAD BALANCING EXPORTER CONFIG — AGENT TIER</div>
<pre>exporters:
  loadbalancing:
    routing_key: "traceID"   # hash by trace ID, not round-robin
    resolver:
      k8s:                    # auto-discover gateway pods via DNS
        service: "otel-gateway"
        ports: [4317]
    protocol:
      otlp:
        timeout: 1s
        sending_queue:
          enabled: true
          queue_size: 1000
</pre>
<div style="font-size: 13px; color: #94a3b8; font-style: italic; text-align: center; max-width: 480px; margin-left: auto; margin-right: auto; margin-bottom: 24px;">The k8s resolver watches the gateway headless service and automatically updates routing when pods scale up or down.</div>
<p><span style="font-weight: 400;">Gateway restarts or scale-in events can occasionally produce incomplete traces.  See </span><a href="https://opentelemetry.io/docs/collector/scaling/" target="_blank" rel="noopener noreferrer"><span style="font-weight: 400;">OTel Collector scaling documentation</span></a><span style="font-weight: 400;"> for details.</span></p>
<h2 id="sampling-strategies-at-volume-picking-the-right-one"><b style="letter-spacing: 0.12em; text-transform: uppercase; font-size: 16px;">Sampling Strategies at Volume: Picking the Right One</b></h2>
</div>
<div>
<p><span style="font-weight: 400;">At small scale, sampling feels like an optional optimization. At large scale, it is a </span><b>financial and operational necessity</b><span style="font-weight: 400;">. Sending 100 percent of traces from a service handling 10,000 requests per second generates a staggering volume of data, most of which you will never look at. This is not too different from logs – for example, Sematext’s log pipeline contains the </span><a href="https://sematext.com/docs/logs/sampling-processor/" target="_blank" rel="noopener"><span style="font-weight: 400;">Sampling Processor</span></a><span style="font-weight: 400;"> for the same reason. Getting sampling right means you keep the traces that help you debug real incidents and drop the ones that would just sit there consuming storage.</span></p>
<p><span style="font-weight: 400;">The tricky part is that “keep the useful traces” is not as simple as it sounds. The traces you most need to keep are the ones with errors and high latency, which are often a small fraction of total traffic. If you use pure random sampling at 1 percent, you will statistically drop 99 percent of your error traces along with everything else. That is the core tension that drives the choice between head-based and tail-based sampling.</span></p>
<div style="margin: 32px 0;">
<div style="font-size: 11px; font-weight: bold; letter-spacing: 0.12em; color: #64748b; text-transform: uppercase; margin-bottom: 10px;">SAMPLING STRATEGY COMPARISON</div>
<table style="width: 100%; border-collapse: collapse; font-family: inherit; font-size: 14px;">
<thead>
<tr style="background: #0f172a;">
<th style="padding: 10px 14px; text-align: left; font-size: 10px; font-weight: bold; letter-spacing: 0.1em; color: #f59e0b; border: 1px solid #334155;">STRATEGY</th>
<th style="padding: 10px 14px; text-align: left; font-size: 10px; font-weight: bold; letter-spacing: 0.1em; color: #f59e0b; border: 1px solid #334155;">WHERE</th>
<th style="padding: 10px 14px; text-align: left; font-size: 10px; font-weight: bold; letter-spacing: 0.1em; color: #f59e0b; border: 1px solid #334155;">KEEPS ERRORS</th>
<th style="padding: 10px 14px; text-align: left; font-size: 10px; font-weight: bold; letter-spacing: 0.1em; color: #f59e0b; border: 1px solid #334155;">MEMORY COST</th>
<th style="padding: 10px 14px; text-align: left; font-size: 10px; font-weight: bold; letter-spacing: 0.1em; color: #f59e0b; border: 1px solid #334155;">BEST FOR</th>
<th style="padding: 10px 14px; text-align: left; font-size: 10px; font-weight: bold; letter-spacing: 0.1em; color: #f59e0b; border: 1px solid #334155;">WHAT IT DOES</th>
</tr>
</thead>
<tbody>
<tr style="background: #ffffff;">
<td style="padding: 10px 14px; border: 1px solid #e2e8f0; font-weight: 600; color: #1e293b;">Always-on</td>
<td style="padding: 10px 14px; border: 1px solid #e2e8f0; color: #475569;">SDK</td>
<td style="padding: 10px 14px; border: 1px solid #e2e8f0; font-weight: bold; color: #16a34a;">YES</td>
<td style="padding: 10px 14px; border: 1px solid #e2e8f0; font-weight: bold; color: #dc2626;">HIGH</td>
<td style="padding: 10px 14px; border: 1px solid #e2e8f0; color: #475569;">Dev / staging only</td>
<td style="padding: 10px 14px; border: 1px solid #e2e8f0; color: #475569;">Keep all spans, no sampling</td>
</tr>
<tr style="background: #f8fafc;">
<td style="padding: 10px 14px; border: 1px solid #e2e8f0; font-weight: 600; color: #1e293b;">Parent-based</td>
<td style="padding: 10px 14px; border: 1px solid #e2e8f0; color: #475569;">SDK</td>
<td style="padding: 10px 14px; border: 1px solid #e2e8f0; font-weight: bold; color: #f59e0b;">INHERITS</td>
<td style="padding: 10px 14px; border: 1px solid #e2e8f0; font-weight: bold; color: #16a34a;">LOW</td>
<td style="padding: 10px 14px; border: 1px solid #e2e8f0; color: #475569;">Consistent decisions across services</td>
<td style="padding: 10px 14px; border: 1px solid #e2e8f0; color: #475569;">Keep/drop based on parent trace</td>
</tr>
<tr style="background: #ffffff;">
<td style="padding: 10px 14px; border: 1px solid #e2e8f0; font-weight: 600; color: #1e293b;">Probabilistic</td>
<td style="padding: 10px 14px; border: 1px solid #e2e8f0; color: #475569;">SDK/Collector</td>
<td style="padding: 10px 14px; border: 1px solid #e2e8f0; font-weight: bold; color: #dc2626;">NO</td>
<td style="padding: 10px 14px; border: 1px solid #e2e8f0; font-weight: bold; color: #16a34a;">LOW</td>
<td style="padding: 10px 14px; border: 1px solid #e2e8f0; color: #475569;">Volume reduction on healthy traffic</td>
<td style="padding: 10px 14px; border: 1px solid #e2e8f0; color: #475569;">Randomly keep spans at a fixed rate</td>
</tr>
<tr style="background: #f8fafc;">
<td style="padding: 10px 14px; border: 1px solid #e2e8f0; font-weight: 600; color: #1e293b;">Rate-limiting</td>
<td style="padding: 10px 14px; border: 1px solid #e2e8f0; color: #475569;">Collector</td>
<td style="padding: 10px 14px; border: 1px solid #e2e8f0; font-weight: bold; color: #dc2626;">NO</td>
<td style="padding: 10px 14px; border: 1px solid #e2e8f0; font-weight: bold; color: #16a34a;">LOW</td>
<td style="padding: 10px 14px; border: 1px solid #e2e8f0; color: #475569;">Capping ingest cost during spikes</td>
<td style="padding: 10px 14px; border: 1px solid #e2e8f0; color: #475569;">Keep spans until a fixed rate limit</td>
</tr>
<tr style="background: #ffffff;">
<td style="padding: 10px 14px; border: 1px solid #e2e8f0; font-weight: 600; color: #1e293b;">Tail-based</td>
<td style="padding: 10px 14px; border: 1px solid #e2e8f0; color: #475569;">Collector (GW)</td>
<td style="padding: 10px 14px; border: 1px solid #e2e8f0; font-weight: bold; color: #16a34a;">YES</td>
<td style="padding: 10px 14px; border: 1px solid #e2e8f0; font-weight: bold; color: #dc2626;">HIGH</td>
<td style="padding: 10px 14px; border: 1px solid #e2e8f0; color: #475569;">Error-aware sampling at scale</td>
<td style="padding: 10px 14px; border: 1px solid #e2e8f0; color: #475569;">Keep spans based on errors &amp; latency</td>
</tr>
</tbody>
</table>
<div style="font-size: 13px; color: #94a3b8; font-style: italic; margin-top: 12px; text-align: center; max-width: 540px; margin-left: auto; margin-right: auto; margin-bottom: 12px;">Most production deployments combine parent-based sampling at the SDK with tail-based sampling at the gateway tier.</div>
<div>
<h3 id="the-combination-that-works-at-scale"><b>The combination that works at scale</b></h3>
<p><span style="font-weight: 400;">Parent-based sampling means the sampling decision is made once at the root span — the first service that receives the request — and every downstream service in that trace inherits the same decision automatically, so you never end up with a trace where some spans were kept and others were dropped by different services making independent choices.</span></p>
<p><span style="font-weight: 400;">Use </span><a href="https://opentelemetry.io/docs/languages/go/sampling/" target="_blank" rel="noopener noreferrer"><span style="font-weight: 400;">parent-based sampling at the SDK level</span></a><span style="font-weight: 400;"> to reduce overall span volume before it even reaches the collector, then use tail-based sampling at the gateway tier to make intelligent keep-or-drop decisions on what makes it through. Two passes of selection — aggressive on volume, smart about what survives.</span></p>
<p><span style="font-weight: 400;">A concrete example: set parent-based sampling at 10 percent for general traffic at the SDK. At the gateway, keep 100 percent of error traces, 100 percent of traces exceeding your latency SLO, and 10 percent of everything else. You end up storing roughly 11 to 12 percent of total trace volume, but with near-complete coverage of the production incidents you actually need to investigate.</span></p>
<div style="font-size: 11px; font-weight: bold; letter-spacing: 0.12em; color: #64748b; text-transform: uppercase;">TAIL SAMPLING POLICY CONFIG — GATEWAY TIER</div>
<pre>processors:
  tail_sampling:
    decision_wait: 10s      # wait for all spans before deciding
    num_traces: 100000      # traces held in memory simultaneously
    expected_new_traces_per_sec: 1000
    policies:
      # always keep error traces
      - name: keep-errors
        type: status_code
        status_code: {status_codes: [ERROR]}

      # always keep slow traces (adjust threshold to your SLO)
      - name: keep-slow
        type: latency
        latency: {threshold_ms: 500}

      # keep 100% of checkout and payment — business critical
      - name: keep-critical-services
        type: string_attribute
        string_attribute:
          key: service.name
          values: [checkout-api, payment-service]

      # probabilistic baseline for everything else
      - name: baseline-sample
        type: probabilistic
        probabilistic: {sampling_percentage: 10}
</pre>
<div style="font-size: 13px; color: #94a3b8; font-style: italic; text-align: center; max-width: 480px; margin-left: auto; margin-right: auto; margin-bottom: 24px;">Policies are evaluated in order. A trace is kept if any policy matches. The probabilistic baseline catches everything the specific policies did not select.</div>
<h3 id="memory-sizing-for-tail-based-sampling"><b>Memory sizing for tail-based sampling</b></h3>
<p><span style="font-weight: 400;">The <code>num_traces</code> parameter is the one that will bite you if you undershoot it. It controls how many traces the gateway holds in memory simultaneously while waiting for all their spans to arrive. A rough formula: multiply your expected traces per second by your decision_wait value, then add 20 percent headroom. For 1,000 traces per second with a 10 second wait, you need at least 12,000 slots — not the 1,000 that most tutorial configs show.</span></p>
<p><a href="https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/processor/tailsamplingprocessor" target="_blank" rel="noopener noreferrer"><span style="font-weight: 400;">The tail sampling processor documentation</span></a><span style="font-weight: 400;"> has the full parameter reference including the memory limiter integration, which you absolutely want enabled at the gateway tier to prevent OOM kills during traffic spikes.</span></p>
<h2 id="multi-cluster-setups-when-one-pipeline-is-not-enough"><b>Multi-Cluster Setups: When One Pipeline Is Not Enough</b></h2>
<p><span style="font-weight: 400;">At some point, a single OTel pipeline stops being the right model. Maybe you operate in multiple regions with data residency requirements. Maybe you have a mix of Kubernetes clusters running different workloads with different SLOs. Whatever the reason, multi-cluster OTel setups introduce a layer of complexity that single-cluster thinking does not prepare you for.</span></p>
<p><span style="font-weight: 400;">The fundamental question is where aggregation happens. Aggregate within each cluster and forward summarized telemetry to a global backend, and you keep cross-region bandwidth low but lose the ability to do cross-cluster trace correlation. Forward raw telemetry to a central aggregation layer, and you get full correlation capability at significantly higher egress cost. Most organizations end up with a hybrid: metrics and logs aggregate locally, traces are forwarded to a central tier for correlation.</span></p>
<h3 id="getting-trace-context-across-cluster-boundaries"><b>Getting trace context across cluster boundaries</b></h3>
<p><span style="font-weight: 400;">Cross-cluster trace correlation only works if your services propagate the </span><a href="https://www.w3.org/TR/trace-context/" target="_blank" rel="noopener noreferrer"><span style="font-weight: 400;">W3C traceparent header</span></a><span style="font-weight: 400;"> across cluster boundaries. Internal service mesh traffic usually handles this correctly. However, cross-cluster calls that pass through an API gateway, CDN, or any reverse proxy that strips unknown headers will </span><b>break trace continuity</b><span style="font-weight: 400;"> at that boundary.</span></p>
<p><span style="font-weight: 400;">Diagnosing this is straightforward: if you see a trace starting at an API gateway span and the first downstream service shows a different root span with no parent, there’s a propagation break. To fix it, add </span><span style="font-weight: 400;"><code>traceparent</code></span><span style="font-weight: 400;"> and </span><span style="font-weight: 400;"><code>tracestate</code></span><span style="font-weight: 400;"> to your proxy’s header allowlist.</span></p>
<p><span style="font-weight: 400;">Here is what that looks like in the two most common cases:</span></p>
<div style="font-size: 11px; font-weight: bold; letter-spacing: 0.12em; color: #64748b; text-transform: uppercase;">PROXY HEADER CONFIG — NGINX AND ENVOY</div>
<pre># nginx — add inside your proxy_pass block
proxy_set_header traceparent $http_traceparent;
proxy_set_header tracestate  $http_tracestate;

---

# Envoy — request_headers_to_add in HttpConnectionManager
route_config:
  request_headers_to_add:
    - header: { key: traceparent }
    - header: { key: tracestate }
</pre>
<div style="font-size: 13px; color: #94a3b8; font-style: italic; text-align: center; max-width: 480px; margin-left: auto; margin-right: auto; margin-bottom: 24px;">One of these two covers the vast majority of cases. If you are behind a CDN, check their documentation for custom header passthrough settings.</div>
<h3 id="data-residency-and-the-gdpr-headache"><b>Data residency and the GDPR headache</b></h3>
<p><span style="font-weight: 400;">If you operate in the EU, forwarding raw traces containing user identifiers to a central tier outside the EU can be a compliance problem. The practical solution is to run attribute redaction in your regional gateway before any data leaves the region. The OTel Collector’s transform processor lets you hash, mask, or drop specific attributes before export.</span></p>
<div style="font-size: 11px; font-weight: bold; letter-spacing: 0.12em; color: #64748b; text-transform: uppercase;">PII REDACTION CONFIG — EU GATEWAY PROCESSOR</div>
<pre>processors:
  transform/redact_pii:
    trace_statements:
      - context: span
        statements:
          # hash user IDs rather than drop
          - set(attributes["user.id"], SHA256(attributes["user.id"]))
          # drop email entirely
          - delete_key(attributes, "user.email")
          # truncate IP to /24 for geo without individual tracking
          - replace_pattern(attributes["net.peer.ip"], "\\d+$", "0")
</pre>
<div style="font-size: 13px; color: #94a3b8; font-style: italic; text-align: center; max-width: 480px; margin-left: auto; margin-right: auto; margin-bottom: 24px;">Run PII redaction at the regional gateway, not the central tier. By the time data reaches central, sensitive attributes should already be gone.</div>
<h2 id="keeping-the-pipeline-itself-observable"><b>Keeping the Pipeline Itself Observable</b></h2>
<p><span style="font-weight: 400;">It would be funny if the  observability tools couldn’t be observed. The </span><a href="https://opentelemetry.io/docs/collector/" target="_blank" rel="noopener noreferrer"><span style="font-weight: 400;">OTel Collector</span></a><span style="font-weight: 400;"> exposes its own internal telemetry as a standard OTLP pipeline, which means you can route it to any backend or an observability solution that you are already using.</span></p>
<p><span style="font-weight: 400;"><code>otelcol_processor_batch_timeout_trigger_send</code> (gotta love this long property name!) tells you whether the batch processor is flushing because the timeout fired rather than because the batch was full. </span><b>A high ratio of timeout-triggered flushes means your traffic volume is lower than your batch config expects, and you are adding unnecessary latency.</b></p>
<p><span style="font-weight: 400;"><code>otelcol_exporter_queue_size</code> is the canary for backpressure. </span><b>When <code>otelcol_exporter_queue_size</code> climbs toward your configured maximum, your exporter is falling behind the ingest rate.</b><span style="font-weight: 400;"> If it hits the maximum, the collector starts dropping data. Set an alert at 80 percent of queue capacity and you will catch pressure building before it becomes data loss.</span></p>
<p><span style="font-weight: 400;">otelcol_processor_tail_sampling_sampling_decision_timer_latency (another awesome long name!) tells you how long the tail sampling processor is taking to make decisions. A sudden increase here usually means the number of active traces in memory has grown past what the processor can efficiently scan — either increase resources or tighten your sampling policy.</span></p>
<div style="font-size: 11px; font-weight: bold; letter-spacing: 0.12em; color: #64748b; text-transform: uppercase;">COLLECTOR SELF-MONITORING CONFIG</div>
<pre>receivers:
  prometheus:
    config:
      scrape_configs:
        - job_name: otel-collector
          scrape_interval: 15s
          static_configs:
            - targets: ["localhost:8888"]

# Expose collector's own telemetry via its service config
service:
  telemetry:
    metrics:
      level: detailed   # basic | normal | detailed
      address: 0.0.0.0:8888
    logs:
      level: warn       # keep collector logs quiet in production

</pre>
<div style="font-size: 13px; color: #94a3b8; font-style: italic; text-align: center; max-width: 480px; margin-left: auto; margin-right: auto; margin-bottom: 24px;">Set telemetry level to ‘detailed’ in staging to understand baseline behavior, then dial back to ‘normal’ in production.</div>
<h2 id="rolling-this-out-without-breaking-everything"><b>Rolling This Out Without Breaking Everything</b></h2>
<p><span style="font-weight: 400;">The migration path from a single collector to a tiered setup does not have to be a big-bang cutover. You could introduce the gateway tier first while keeping the existing single collector in place, route a small percentage of services to the new tier, and validate that data is flowing correctly before moving everything over.</span></p>
<p><span style="font-weight: 400;">I suggest you start with a non-critical service — one that has decent traffic but where gaps in telemetry during the migration window would not cause anyone to lose sleep. Verify spans arrive at the gateway, verify they arrive at the backend with the right resource attributes, and check that your tail sampling policies are making sensible decisions. That validation loop is worth running for a week before you touch any of your critical services.</span></p>
<p><span style="font-weight: 400;">The config change on the service side is usually just updating the OTLP endpoint to the new agent address. If you are using the </span><a href="https://opentelemetry.io/docs/kubernetes/operator/" target="_blank" rel="noopener noreferrer"><span style="font-weight: 400;">OTel Operator for Kubernetes</span></a><span style="font-weight: 400;">, you can inject the agent endpoint as an environment variable through the Instrumentation custom resource — no application code changes, no redeployment of service configs when the collector topology changes.</span></p>
<p><span style="font-weight: 400;">The pattern across all of this — tiered collectors, trace-aware load balancing, layered sampling strategies, regional pipelines — is that scaling OTel is fundamentally an architecture problem, not an instrumentation problem. The instrumentation is the relatively easy part. The hard part is building a pipeline that stays operational under load, degrades gracefully when individual components have problems, and gives you enough visibility into itself that you can tell when something is wrong before it starts affecting the data your engineers depend on during incidents.</span></p>
<p><span style="font-weight: 400;">Once your OpenTelemetry pipeline is running at scale, the next step is learning how to interpret the traces to identify performance bottlenecks and root causes. See </span><a href="https://sematext.com/blog/troubleshooting-microservices-with-opentelemetry-distributed-tracing/" target="_blank" rel="noopener"><span style="font-weight: 400;">Troubleshooting Microservices with OpenTelemetry Distributed Tracing</span></a><span style="font-weight: 400;"> for an in-depth and very practical guidance on that subject.</span></p>
</div>
</div>
</div>
<p class="space-top"><a href="https://apps.sematext.com/ui/registration" class="button-big" target="_blank" rel="noopener noreferrer">Start Free Trial</a></p><hr class="hidden"><p>The post <a href="https://sematext.com/blog/running-opentelemetry-at-scale-architecture-patterns-for-100s-of-services/">Running OpenTelemetry at Scale: Architecture Patterns for 100s of Services</a> appeared first on <a href="https://sematext.com">Sematext</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>From Debugging to SLOs: How OpenTelemetry Changes the Way Teams Do Observability</title>
		<link>https://sematext.com/blog/from-debugging-to-slos-how-opentelemetry-changes-the-way-teams-do-observability/</link>
		
		<dc:creator><![CDATA[fulya.uluturk]]></dc:creator>
		<pubDate>Mon, 23 Feb 2026 10:15:34 +0000</pubDate>
				<category><![CDATA[OpenTelemetry]]></category>
		<category><![CDATA[Tracing]]></category>
		<category><![CDATA[distributed tracing]]></category>
		<category><![CDATA[microservices]]></category>
		<category><![CDATA[opentelemetry]]></category>
		<guid isPermaLink="false">https://sematext.com/?p=70545</guid>

					<description><![CDATA[<p>At some point in every team’s life, someone gets paged at 2 AM because a service is ‘slow.’ Nobody knows which service. Nobody knows why. Someone opens five different dashboards, pastes a trace ID into a Slack thread, and thirty minutes later you have twelve engineers in a call arguing about whether the problem is [&#8230;]</p>
<p>The post <a href="https://sematext.com/blog/from-debugging-to-slos-how-opentelemetry-changes-the-way-teams-do-observability/">From Debugging to SLOs: How OpenTelemetry Changes the Way Teams Do Observability</a> appeared first on <a href="https://sematext.com">Sematext</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p><span style="font-weight: 400;">At some point in every team’s life, someone gets paged at 2 AM because a service is ‘slow.’ Nobody knows which service. Nobody knows why. Someone opens five different dashboards, pastes a trace ID into a Slack thread, and thirty minutes later you have twelve engineers in a call arguing about whether the problem is in the database or the API gateway. By the time you find the actual culprit, half the team has memorized each other’s sleep schedules.</span></p>
<p><span style="font-weight: 400;">This is what life looks like when observability is an afterthought: logs in one place, metrics in another, and a custom monitoring agent that only works for two services because the third one was written in a language nobody on the team uses anymore. It works, technically. Until it does not.</span></p>
<p><span style="font-weight: 400;">OpenTelemetry came out of a genuine frustration with this fragmented mess. It is an open-source observability framework that gives you a </span><a href="https://sematext.com/guides/understanding-opentelemetry-a-practical-guide/" target="_blank" rel="noopener"><span style="font-weight: 400;">vendor-neutral, standardized way to instrument your applications</span></a><span style="font-weight: 400;"> and then connect that instrumentation to service health, error budgets, and eventually SLOs that your entire organization actually understands. This article walks through what that shift looks like in practice, and why it matters for more than just the people who are on call.</span></p>
<h2 id="the-old-world-logs-apm-agents-and-the-dashboard-graveyard"><b>The Old World: Logs, APM Agents, and the Dashboard Graveyard</b></h2>
<p><span style="font-weight: 400;">Let’s be direct about how most teams actually do observability before they invest in it properly. You have application logs going into a log management platform, with varying levels of structure depending on who wrote which service. You have an APM tool that auto-instruments some of your services but not all of them, and the traces it produces are siloed within its own ecosystem. And you have a monitoring dashboard that someone built eighteen months ago and that might or might not reflect how the service actually behaves today.</span></p>
<div style="background: rgba(220,38,38,0.06); border-left: 3px solid #DC2626; border-radius: 0 8px 8px 0; padding: 22px 26px; margin: 36px 0; font-size: 17px; color: #7f1d1d;"><strong style="color: #991b1b;">The real cost is not the outage. It is the investigation.</strong> A 2023 industry study on downtime costs found that engineering teams spend an average of 200-plus hours per year just on incident investigation, separate from the time actually fixing things. A good chunk of that is tool-switching and context-switching because telemetry data lives in silos.</div>
<p><span style="font-weight: 400;">The deeper problem is not the tools themselves; it is that each one has its own instrumentation model. Your APM agent captures HTTP spans one way. Your custom metrics library reports latency percentiles slightly differently. Your logs do not correlate to your traces automatically. So when something breaks, you are stitching together three different narratives instead of reading one coherent story about what happened.</span></p>
<div style="background: rgba(26,86,160,0.06); border-left: 3px solid #1a56dc; border-radius: 0 8px 8px 0; padding: 22px 26px; margin: 36px 0; font-size: 17px; color: #1e3a5f;">This is actually the origin of Sematext – back in 2012 Sematext was the first platform to offer both performance monitoring (so metrics) and log monitoring in one observability platform, and then distributed transaction tracing in 2015.</div>
<h2 id="what-opentelemetry-actually-is-without-the-fluff"><b>What OpenTelemetry Actually Is (Without the Fluff)</b></h2>
<p><span style="font-weight: 400;">OpenTelemetry standardizes how you generate, collect, and export telemetry data. It covers three signal types (with more to come), which are the foundation of everything else in this article:</span></p>
<div style="margin: 36px 0; font-family: inherit;">
<p style="font-family: 'JetBrains Mono', monospace; font-size: 11px; font-weight: 600; letter-spacing: 2.5px; text-transform: uppercase; color: #94a3b8; margin-bottom: 16px;">THE THREE PILLARS OF OPENTELEMETRY</p>
<p><!-- Card grid wrapper --></p>
<div style="background: #f8fafc; border: 1px solid #e2e8f0; border-radius: 12px; padding: 24px; box-sizing: border-box;">
<table style="width: 100%; border-collapse: separate; border-spacing: 14px; table-layout: fixed;">
<tbody>
<tr style="vertical-align: top;"><!-- Traces -->
<td style="border-radius: 10px; padding: 24px 16px 20px; text-align: center; border: 1px solid rgba(59,130,246,0.3); background: rgba(59,130,246,0.05); width: 33.33%;">
<div style="font-size: 28px; margin-bottom: 12px;">🔗</div>
<div style="font-family: 'JetBrains Mono', monospace; font-size: 13px; font-weight: 600; letter-spacing: 1px; color: #2563eb; margin-bottom: 10px;">Traces</div>
<p style="font-size: 13px; color: #475569; line-height: 1.6; margin: 0;">End-to-end request paths across services. Shows exactly where time is spent and where errors propagate.</p>
</td>
<p><!-- Metrics --></p>
<td style="border-radius: 10px; padding: 24px 16px 20px; text-align: center; border: 1px solid rgba(16,185,129,0.3); background: rgba(16,185,129,0.05); width: 33.33%;">
<div style="font-size: 28px; margin-bottom: 12px;">📊</div>
<div style="font-family: 'JetBrains Mono', monospace; font-size: 13px; font-weight: 600; letter-spacing: 1px; color: #059669; margin-bottom: 10px;">Metrics</div>
<p style="font-size: 13px; color: #475569; line-height: 1.6; margin: 0;">Numeric measurements over time: latency histograms, request counts, error rates, resource utilization. The raw material for SLOs.</p>
</td>
<p><!-- Logs --></p>
<td style="border-radius: 10px; padding: 24px 16px 20px; text-align: center; border: 1px solid rgba(245,158,11,0.3); background: rgba(245,158,11,0.05); width: 33.33%;">
<div style="font-size: 28px; margin-bottom: 12px;">📋</div>
<div style="font-family: 'JetBrains Mono', monospace; font-size: 13px; font-weight: 600; letter-spacing: 1px; color: #d97706; margin-bottom: 10px;">Logs</div>
<p style="font-size: 13px; color: #475569; line-height: 1.6; margin: 0;">Structured event records with trace context attached. No more copy-pasting trace IDs; logs link directly to the span that generated them, and an error span links back to every log event emitted during that span.</p>
</td>
</tr>
</tbody>
</table>
</div>
<p><!-- Caption --></p>
<p style="text-align: center; font-size: 13px; font-style: italic; color: #94a3b8; margin-top: 12px;">Traces, Metrics, and Logs share the same context propagation model in OTel, which lets you jump from a log line to its trace in seconds.</p>
</div>
<p><span style="font-weight: 400;">What makes OTel different from what came before is not magic; it is the fact that all three signals share the same </span><a href="https://opentelemetry.io/docs/specs/otel/context/" target="_blank" rel="noopener noreferrer"><span style="font-weight: 400;">context propagation model</span></a><span style="font-weight: 400;">. A trace ID that starts in your frontend propagates through every instrumented microservice call, and if your logs are also emitting that trace ID, you can jump from a log line to its trace in seconds. Not minutes. Seconds. If you are the person doing production troubleshooting you know how valuable this difference is!</span></p>
<h2 id="slos-what-they-are-and-why-otel-makes-them-achievable"><b>SLOs: What They Are and Why OTel Makes Them Achievable</b></h2>
<p><a href="https://sematext.com/glossary/service-level-objective/" target="_blank" rel="noopener"><span style="font-weight: 400;">Service Level Objectives</span></a><span style="font-weight: 400;"> have been a thing since Google wrote about them in the </span><a href="https://sre.google/sre-book/service-level-objectives/" target="_blank" rel="noopener noreferrer"><span style="font-weight: 400;">Site Reliability Engineering book</span></a><span style="font-weight: 400;">, and they have been misunderstood and poorly implemented since roughly the same time. The core idea is simple: you agree on a target for how reliable a service needs to be, you measure it consistently, and you manage your engineering work in relation to how much reliability budget you have consumed or have left.</span></p>
<p><span style="font-weight: 400;">The reason SLOs often fail is not the concept; it is that teams try to define them before they have reliable telemetry. You cannot set a meaningful availability target for a service if your metrics come from three different monitoring agents that measure availability in subtly different ways. You end up with SLOs that nobody trusts, which means nobody uses them to make decisions.</span></p>
<div style="margin: 36px 0;">
<p><!-- Section label --></p>
<p style="font-family: 'JetBrains Mono', monospace; font-size: 11px; font-weight: 600; letter-spacing: 2.5px; text-transform: uppercase; color: #94a3b8; margin-bottom: 16px;">Example SLOs Built on OTel Metrics</p>
<p><!-- Table wrapper --></p>
<div style="background: #f8fafc; border: 1px solid #e2e8f0; border-radius: 12px; padding: 4px; overflow: hidden;">
<table style="width: 100%; border-collapse: collapse; font-size: 14px;">
<thead>
<tr>
<th style="background: #1e3a5f; color: #93c5fd; font-family: 'JetBrains Mono', monospace; font-size: 11px; text-transform: uppercase; letter-spacing: 1.2px; padding: 13px 14px; text-align: left; border-bottom: 1px solid #e2e8f0;">Service</th>
<th style="background: #1e3a5f; color: #93c5fd; font-family: 'JetBrains Mono', monospace; font-size: 11px; text-transform: uppercase; letter-spacing: 1.2px; padding: 13px 14px; text-align: left; border-bottom: 1px solid #e2e8f0;">SLI</th>
<th style="background: #1e3a5f; color: #93c5fd; font-family: 'JetBrains Mono', monospace; font-size: 11px; text-transform: uppercase; letter-spacing: 1.2px; padding: 13px 14px; text-align: left; border-bottom: 1px solid #e2e8f0;">Target</th>
<th style="background: #1e3a5f; color: #93c5fd; font-family: 'JetBrains Mono', monospace; font-size: 11px; text-transform: uppercase; letter-spacing: 1.2px; padding: 13px 14px; text-align: left; border-bottom: 1px solid #e2e8f0;">Error Budget</th>
<th style="background: #1e3a5f; color: #93c5fd; font-family: 'JetBrains Mono', monospace; font-size: 11px; text-transform: uppercase; letter-spacing: 1.2px; padding: 13px 14px; text-align: left; border-bottom: 1px solid #e2e8f0;">Status</th>
</tr>
</thead>
<tbody>
<tr>
<td style="padding: 12px 14px; border-bottom: 1px solid #e2e8f0; color: #1e293b; vertical-align: middle;">Checkout API</td>
<td style="padding: 12px 14px; border-bottom: 1px solid #e2e8f0; color: #475569; vertical-align: middle;">% requests &lt; 500 ms, non-5xx</td>
<td style="padding: 12px 14px; border-bottom: 1px solid #e2e8f0; color: #1e293b; vertical-align: middle;">99.5%</td>
<td style="padding: 12px 14px; border-bottom: 1px solid #e2e8f0; color: #475569; vertical-align: middle;">3 h 36 m remaining</td>
<td style="padding: 12px 14px; border-bottom: 1px solid #e2e8f0; vertical-align: middle;"><span style="display: inline-block; font-family: 'JetBrains Mono', monospace; font-size: 10px; font-weight: 600; padding: 3px 9px; border-radius: 12px; letter-spacing: 0.5px; background: rgba(16,185,129,0.1); color: #059669; border: 1px solid rgba(16,185,129,0.3);">HEALTHY</span></td>
</tr>
<tr>
<td style="padding: 12px 14px; border-bottom: 1px solid #e2e8f0; color: #1e293b; vertical-align: middle;">Auth Service</td>
<td style="padding: 12px 14px; border-bottom: 1px solid #e2e8f0; color: #475569; vertical-align: middle;">% successful token validations</td>
<td style="padding: 12px 14px; border-bottom: 1px solid #e2e8f0; color: #1e293b; vertical-align: middle;">99.9%</td>
<td style="padding: 12px 14px; border-bottom: 1px solid #e2e8f0; color: #475569; vertical-align: middle;">0 h 22 m remaining</td>
<td style="padding: 12px 14px; border-bottom: 1px solid #e2e8f0; vertical-align: middle;"><span style="display: inline-block; font-family: 'JetBrains Mono', monospace; font-size: 10px; font-weight: 600; padding: 3px 9px; border-radius: 12px; letter-spacing: 0.5px; background: rgba(245,158,11,0.1); color: #d97706; border: 1px solid rgba(245,158,11,0.3);">AT RISK</span></td>
</tr>
<tr>
<td style="padding: 12px 14px; border-bottom: 1px solid #e2e8f0; color: #1e293b; vertical-align: middle;">Search API</td>
<td style="padding: 12px 14px; border-bottom: 1px solid #e2e8f0; color: #475569; vertical-align: middle;">% queries returning results &lt; 1 s</td>
<td style="padding: 12px 14px; border-bottom: 1px solid #e2e8f0; color: #1e293b; vertical-align: middle;">98.0%</td>
<td style="padding: 12px 14px; border-bottom: 1px solid #e2e8f0; color: #475569; vertical-align: middle;">Budget exhausted</td>
<td style="padding: 12px 14px; border-bottom: 1px solid #e2e8f0; vertical-align: middle;"><span style="display: inline-block; font-family: 'JetBrains Mono', monospace; font-size: 10px; font-weight: 600; padding: 3px 9px; border-radius: 12px; letter-spacing: 0.5px; background: rgba(220,38,38,0.1); color: #dc2626; border: 1px solid rgba(220,38,38,0.3);">BREACHED</span></td>
</tr>
<tr>
<td style="padding: 12px 14px; color: #1e293b; vertical-align: middle;">Order Worker</td>
<td style="padding: 12px 14px; color: #475569; vertical-align: middle;">% jobs processed without retry</td>
<td style="padding: 12px 14px; color: #1e293b; vertical-align: middle;">99.0%</td>
<td style="padding: 12px 14px; color: #475569; vertical-align: middle;">5 h 12 m remaining</td>
<td style="padding: 12px 14px; vertical-align: middle;"><span style="display: inline-block; font-family: 'JetBrains Mono', monospace; font-size: 10px; font-weight: 600; padding: 3px 9px; border-radius: 12px; letter-spacing: 0.5px; background: rgba(16,185,129,0.1); color: #059669; border: 1px solid rgba(16,185,129,0.3);">HEALTHY</span></td>
</tr>
</tbody>
</table>
</div>
<p><!-- Caption --></p>
<p style="text-align: center; font-size: 13px; font-style: italic; color: #94a3b8; margin-top: 12px;">When SLIs are computed from OTel semantic conventions, every service uses the same measurement logic regardless of language or framework.</p>
</div>
<p><span style="font-weight: 400;">When your </span><a href="https://sematext.com/glossary/service-level-indicator/" target="_blank" rel="noopener"><span style="font-weight: 400;">SLIs</span></a><span style="font-weight: 400;"> are computed from OTel metrics, specifically from the semantic conventions that define how HTTP span duration and status should be recorded, you get consistency across services by default. The latency histogram for your Go service and the one for your .NET service use the same bucket boundaries. The error classification follows the same logic. Suddenly your SLOs are comparing apples to apples, and that changes what you can do with them.</span></p>
<h2 id="the-correlation-story-how-one-trace-id-connects-everything"><b>The Correlation Story: How one Trace ID Connects Everything</b></h2>
<p><span style="font-weight: 400;">One of the things that sounds academic until you experience it is </span><a href="https://opentelemetry.io/docs/concepts/context-propagation/" target="_blank" rel="noopener noreferrer"><span style="font-weight: 400;">trace context propagation</span></a><span style="font-weight: 400;">. When a request comes into your frontend and you are using OTel instrumentation, a trace ID gets generated and passed along to every downstream service call via HTTP headers, gRPC metadata, message queue attributes, or whatever transport you are using. Every span in that trace carries the same trace ID, and your logs carry it too if you have set up log correlation.</span></p>
<p><span style="font-weight: 400;">What this means in practice: when your error rate alert fires because the checkout service just breached its error budget, you do not start by guessing. You go to the traces for that time window, filter for error spans, and you are already looking at the full call path: frontend, checkout API, inventory service, payment gateway, with timing for each hop. If the inventory service was slow, you will see a long span there. If the payment gateway returned a 503, you will see that in the span status. No grep-ing through logs trying to find a request ID that someone may or may not have remembered to log. For a step-by-step breakdown of what these patterns look like in real incidents,</span><a href="https://sematext.com/blog/troubleshooting-microservices-with-opentelemetry-distributed-tracing/" target="_blank" rel="noopener"> <span style="font-weight: 400;">troubleshooting microservices with distributed tracing</span></a><span style="font-weight: 400;"> is a good companion read.</span></p>
<div style="margin: 36px 0;">
<p><!-- Section label --></p>
<p style="font-family: 'JetBrains Mono', monospace; font-size: 11px; font-weight: 600; letter-spacing: 2.5px; text-transform: uppercase; color: #94a3b8; margin-bottom: 16px;">Before vs After: What Investigation Actually Looks Like</p>
<p><!-- Cards wrapper --></p>
<div style="background: #f8fafc; border: 1px solid #e2e8f0; border-radius: 12px; padding: 24px; box-sizing: border-box;">
<table style="width: 100%; border-collapse: separate; border-spacing: 16px; table-layout: fixed;">
<tbody>
<tr style="vertical-align: top;"><!-- Before -->
<td style="border-radius: 10px; padding: 22px 18px; background: rgba(220,38,38,0.05); border: 1px solid rgba(220,38,38,0.25); width: 50%;">
<div style="font-family: 'JetBrains Mono', monospace; font-size: 11px; font-weight: 600; letter-spacing: 2px; text-transform: uppercase; color: #dc2626; margin-bottom: 14px;">Before OTel</div>
<p style="font-size: 14px; color: #475569; margin-bottom: 8px; line-height: 1.6;">Alert fires. Open APM tool, find service.</p>
<p style="font-size: 14px; color: #475569; margin-bottom: 8px; line-height: 1.6;">Open logging tool, search by timestamp.</p>
<p style="font-size: 14px; color: #475569; margin-bottom: 8px; line-height: 1.6;">Paste trace ID into search; hope the log format includes it.</p>
<p style="font-size: 14px; color: #475569; margin-bottom: 8px; line-height: 1.6;">Cross-reference three tools. Escalate because nobody can reproduce it.</p>
<p style="font-size: 14px; color: #94a3b8; font-style: italic; margin: 0; line-height: 1.6;">MTTR: 45 to 90 min for medium-severity incidents.</p>
</td>
<p><!-- After --></p>
<td style="border-radius: 10px; padding: 22px 18px; background: rgba(16,185,129,0.05); border: 1px solid rgba(16,185,129,0.25); width: 50%;">
<div style="font-family: 'JetBrains Mono', monospace; font-size: 11px; font-weight: 600; letter-spacing: 2px; text-transform: uppercase; color: #059669; margin-bottom: 14px;">After OTel</div>
<p style="font-size: 14px; color: #475569; margin-bottom: 8px; line-height: 1.6;">Alert fires with a link to the error budget burn rate.</p>
<p style="font-size: 14px; color: #475569; margin-bottom: 8px; line-height: 1.6;">Click through to traces for that time window.</p>
<p style="font-size: 14px; color: #475569; margin-bottom: 8px; line-height: 1.6;">Follow the trace to the failing span.</p>
<p style="font-size: 14px; color: #475569; margin-bottom: 8px; line-height: 1.6;">Logs automatically surfaced by trace ID.</p>
<p style="font-size: 14px; color: #94a3b8; font-style: italic; margin: 0; line-height: 1.6;">MTTR: 5 to 20 min for the same incidents.</p>
</td>
</tr>
</tbody>
</table>
</div>
<p><!-- Caption --></p>
<p style="text-align: center; font-size: 13px; font-style: italic; color: #94a3b8; margin-top: 12px;">The difference in MTTR is not about effort. It is about whether correlated telemetry exists at all.</p>
</div>
<h2 id="auto-instrumentation-getting-value-without-rewriting-everything"><b>Auto-Instrumentation: Getting Value Without Rewriting Everything</b></h2>
<p><span style="font-weight: 400;">One of the biggest objections to investing in observability is the instrumentation cost. If you have thirty microservices and each one needs to be manually instrumented before you see any benefit, that is a project with a very long feedback loop. This is actually what we saw with our initial distributed tracing implementation at Sematext back in 2015 – adoption of a challenge due to how much work engineers would have to invest in instrumenting their applications. OTel’s auto-instrumentation libraries change that equation significantly.</span></p>
<p><span style="font-weight: 400;">For Java, the </span><a href="https://opentelemetry.io/docs/zero-code/java/agent/" target="_blank" rel="noopener noreferrer"><span style="font-weight: 400;">OTel Java agent</span></a><span style="font-weight: 400;"> attaches to your JVM at startup and automatically instruments common frameworks such as Spring Boot, gRPC, JDBC, and Kafka without any code changes. For Python, </span><a href="https://opentelemetry.io/docs/zero-code/python/" target="_blank" rel="noopener noreferrer"><span style="font-weight: 400;">opentelemetry-instrument</span></a><span style="font-weight: 400;"> does the same for Flask, Django, FastAPI, and SQLAlchemy. The .NET ecosystem has similar coverage through the </span><a href="https://opentelemetry.io/docs/zero-code/dotnet/" target="_blank" rel="noopener noreferrer"><span style="font-weight: 400;">automatic instrumentation package</span></a><span style="font-weight: 400;">. You get spans for every incoming HTTP request, every outgoing call, and every database query without touching the application code. If you want to skip the boilerplate and start from something that already works,</span><a href="https://github.com/sematext/sematext-opentelemetry-examples" target="_blank" rel="noopener noreferrer"> <span style="font-weight: 400;">these language-specific OTel examples</span></a><span style="font-weight: 400;"> cover the setup end to end.</span></p>
<h2 id="what-to-actually-watch-out-for"><b>What to Actually Watch Out For</b></h2>
<p><span style="font-weight: 400;">None of this comes without tradeoffs, and articles that only cover the benefits are setting you up for some unpleasant surprises. A few things will bite you if you do not plan for them.</span></p>
<p><span style="font-weight: 400;">A deep dive into</span><a href="https://sematext.com/blog/opentelemetry-instrumentation-best-practices-for-microservices-observability/" target="_blank" rel="noopener"> <span style="font-weight: 400;">OpenTelemetry instrumentation best practices</span></a><span style="font-weight: 400;"> covers all of these in detail, but here is the short version.</span></p>
<h3 id="cardinality-explodes-if-you-are-not-careful"><b>Cardinality explodes if you are not careful</b></h3>
<p><span style="font-weight: 400;">OTel metrics support rich attribute sets, which is great for debugging but problematic for storage if you start adding high-cardinality attributes like user IDs or request IDs to your metrics. The OTel metrics spec includes cardinality limits, and you should understand them before you start attaching attributes to everything.</span></p>
<h3 id="sampling-is-necessary-at-scale-and-confusing-to-get-right"><b>Sampling is necessary at scale and confusing to get right</b></h3>
<p><span style="font-weight: 400;">Sending 100 percent of traces when you are handling thousands of requests per second is expensive. Head-based sampling, where you decide at the start of a trace whether to keep it, is simple but means you might drop the interesting traces. Tail-based sampling, where you decide after seeing the whole trace, keeps the errors but requires the OTel Collector to buffer spans, which adds complexity. There is no right answer, only tradeoffs that depend on your volume and budget.</span></p>
<h3 id="auto-instrumentation-vs-manual-instrumentation-the-honest-tradeoff"><b>Auto-instrumentation vs manual instrumentation: the honest tradeoff</b></h3>
<p><span style="font-weight: 400;">Auto instrumentation gets you running in an afternoon with zero code changes and gives consistent coverage across your entire fleet from day one. The trade off is that it understands frameworks, not business intent. It can tell you a database query took 800 ms but not that it was pricing a cart for a high value customer.</span></p>
<p><span style="font-weight: 400;">Manual instrumentation fills the gap that actually matters for SLOs. Checkout completion time, order processing latency by fulfillment partner, or time to first search result. It takes more effort, but it is what turns a latency alert into a business conversation.</span></p>
<p><span style="font-weight: 400;">In practice, auto instrumentation provides the foundational 80 percent. Requests, error rates, and durations (aka RED) from day one. You then layer manual instrumentation on top for the business critical signals your SLOs should be measuring.</span></p>
<h3 id="the-collector-configuration-gets-complex-fast"><b>The Collector configuration gets complex fast</b></h3>
<p><span style="font-weight: 400;">Once you start running multiple pipelines, applying transforms, doing tail-based sampling, and exporting to multiple backends, your collector config becomes something that needs to be tested and versioned like application code. Treat it that way from the start.</span></p>
<h2 id="starting-without-starting-over"><b>Starting Without Starting Over</b></h2>
<p><span style="font-weight: 400;">The most common mistake teams make when adopting OTel is treating it as a big-bang migration. You do not need to instrument every service before any of it becomes useful. Pick one service, ideally something that sits in the middle of your call graph so you can see upstream and downstream spans, and get it fully instrumented with OTel, exporting to a collector and from there to whatever backend you already have. Define one or two SLIs for it. Watch them for a week and see if they match your intuition about how the service is performing.</span></p>
<p><span style="font-weight: 400;">That first service will teach you things that no amount of reading can. You will find out how your framework handles context propagation. You will discover that your log format does not include trace IDs and need to fix that. You will learn what your normal latency histogram looks like and be surprised by the long tail. Do that before you roll out to thirty services, and the rollout will go much faster. </span></p>
<p><span style="font-weight: 400;">To get started see the </span><a href="https://sematext.com/docs/tracing/getting-started/" target="_blank" rel="noopener"><span style="font-weight: 400;">Sematext step-by-step setup guide</span></a><span style="font-weight: 400;"> for OpenTelemetry tracing. Once you have that in place, the article on </span><a href="https://sematext.com/blog/troubleshooting-microservices-with-opentelemetry-distributed-tracing/#building-a-troubleshooting-workflow-with-sematext-tracing" target="_blank" rel="noopener"><span style="font-weight: 400;">building a troubleshooting workflow with Sematext tracing</span></a><span style="font-weight: 400;"> shows how to use those first traces to investigate issues and iterate on your instrumentation.</span></p>
<p class="space-top"><a href="https://apps.sematext.com/ui/registration" class="button-big" target="_blank" rel="noopener noreferrer">Start Free Trial</a></p><hr class="hidden"><p>The post <a href="https://sematext.com/blog/from-debugging-to-slos-how-opentelemetry-changes-the-way-teams-do-observability/">From Debugging to SLOs: How OpenTelemetry Changes the Way Teams Do Observability</a> appeared first on <a href="https://sematext.com">Sematext</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>OpenTelemetry Production Monitoring: What Breaks, and How to Prevent It</title>
		<link>https://sematext.com/blog/opentelemetry-production-monitoring-what-breaks-and-how-to-prevent-it/</link>
		
		<dc:creator><![CDATA[fulya.uluturk]]></dc:creator>
		<pubDate>Tue, 17 Feb 2026 11:15:15 +0000</pubDate>
				<category><![CDATA[OpenTelemetry]]></category>
		<category><![CDATA[Tracing]]></category>
		<category><![CDATA[distributed tracing]]></category>
		<category><![CDATA[microservices]]></category>
		<category><![CDATA[opentelemetry]]></category>
		<guid isPermaLink="false">https://sematext.com/?p=70527</guid>

					<description><![CDATA[<p>OpenTelemetry almost always works beautifully in staging, demos, and videos. You enable auto-instrumentation, spans appear, metrics flow, the collector starts, and dashboards light up. Everything looks clean and predictable. However, production has a way of humbling even the most carefully prepared setups. When real traffic hits, and it always spikes sooner or later, you start [&#8230;]</p>
<p>The post <a href="https://sematext.com/blog/opentelemetry-production-monitoring-what-breaks-and-how-to-prevent-it/">OpenTelemetry Production Monitoring: What Breaks, and How to Prevent It</a> appeared first on <a href="https://sematext.com">Sematext</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>OpenTelemetry almost always works beautifully in staging, demos, and videos. You enable auto-instrumentation, spans appear, metrics flow, the collector starts, and dashboards light up. Everything looks clean and predictable.</p>
<p>However, production has a way of humbling even the most carefully prepared setups. When real traffic hits, and it always spikes sooner or later, you start seeing dropped spans. Collector memory climbs until the process gets killed, and if you are running a single-instance collector, you can forget about collecting any telemetry until you bring it back up. Costs climb faster than anyone budgeted for. A few traces look incomplete. The bossman asks why latency increased by 12% after “just adding observability.”</p>
<p>None of this means OpenTelemetry is broken. It means production behaves differently than demos. This guide walks through what actually breaks when OpenTelemetry meets real-world scale, and what you can do about it before it becomes a 2 AM incident. Catching these issues early is the difference between a boring Tuesday and a war room.</p>
<p><span style="font-weight: 400;">For a practical setup of OpenTelemetry in microservices, see our </span><a href="https://sematext.com/blog/how-to-implement-distributed-tracing-in-microservices-with-opentelemetry-auto-instrumentation/" target="_blank" rel="noopener"><span style="font-weight: 400;">step-by-step guide on distributed tracing with auto-instrumentation</span></a><span style="font-weight: 400;">.</span></p>
<h2 id="the-first-production-surprise-cardinality-explosions"><b>The First Production Surprise: Cardinality Explosions</b></h2>
<p>High cardinality is one of the fastest ways to destabilize an otherwise healthy observability setup, and it almost always starts innocently. Someone with the best intension adds a genuinely helpful attribute:</p>
<ul>
<li aria-level="1">user_id</li>
<li aria-level="1">session_id</li>
<li aria-level="1">request_uuid</li>
<li aria-level="1">a fully expanded URL path</li>
</ul>
<p><span style="font-weight: 400;">In development, nothing bad happens. In production, that single decision can create </span><i><span style="font-weight: 400;">millions</span></i><span style="font-weight: 400;"> of unique time series. For example, if a request counter is labeled with </span><code>user_id</code><span style="font-weight: 400;">and you have two million users, you have just created two million distinct metric series for one metric. Multiply that across services and dimensions, and storage, memory, and the performance of your observability tool degrades quickly.</span></p>
<p><span style="font-weight: 400;">You will notice it in a few ways: dashboards become noisy or slow, request latency increases, storage costs spike, and collector memory usage grows for no obvious reason.</span></p>
<p><span style="font-weight: 400;">The fix is not complicated, but it requires discipline. </span><b>Metrics should use low-cardinality dimensions only</b><span style="font-weight: 400;">, things like environment (prod, staging), service name, endpoint patterns rather than full URLs, and HTTP status classes (2xx, 4xx, 5xx). Anything that is essentially unique per request does not belong on a metric.</span></p>
<p><span style="font-weight: 400;">With auto-instrumentation, you do not always control attribute creation directly, but you can still suppress high-cardinality attributes via agent configuration, or drop and transform attributes in the collector using processors like filter, attributes, or transform. With manual instrumentation, you have full control and full responsibility. If you truly need high-cardinality identifiers, consider hashing or aggregating them before attaching them.</span></p>
<p><span style="font-weight: 400;">The key habit is to monitor cardinality continuously, not just after a cost spike. Keep an eye on the collector metrics that look like </span><code>processor_accepted_metric_points</code><span style="font-weight: 400;"> broken down by metric name. These reveal which metrics are growing out of control before they degrade performance or inflate your bill.</span></p>
<p><span style="font-weight: 400;">For more guidance on instrumentation hygiene and preventing cardinality issues from the start, see our</span><a href="https://sematext.com/blog/opentelemetry-instrumentation-best-practices-for-microservices-observability/" target="_blank" rel="noopener"> <span style="font-weight: 400;">OpenTelemetry instrumentation best practices</span></a><span style="font-weight: 400;">.</span></p>
<h2 id="scaling-pressure-in-opentelemetry-production-pipelines"><b>Scaling Pressure in OpenTelemetry Production Pipelines</b></h2>
<p><span style="font-weight: 400;">OpenTelemetry components, SDKs, agents, and collectors, are not magic. They are software services that can be overloaded, and in high-throughput systems they often are.</span></p>
<p><span style="font-weight: 400;">In busy environments, traces can be generated at hundreds of thousands per second. Metrics multiply across services, containers, and pods. If batching, memory limits, and exporter throughput are not tuned, the pipeline itself becomes the bottleneck. The symptoms are predictable: </span><code>processor_refused_spans</code><span style="font-weight: 400;"> starts increasing, collector memory climbs steadily, export failures appear, and telemetry arrives late or gets dropped entirely.</span></p>
<p><span style="font-weight: 400;">To understand where these bottlenecks occur, consider the overall OpenTelemetry production pipeline:</span></p>
<p><img decoding="async" class="alignnone size-large wp-image-70532" src="https://sematext.com/wp-content/uploads/2026/02/opentelemetry-production-pipeline.png" alt="" width="618" height="1024"></p>
<p><span style="font-weight: 400;">If you are using manual SDK instrumentation, you can tune batching and flush intervals directly. Larger batches reduce per-span overhead but increase memory pressure in the application itself, raising the risk of an OOM kill for containerized workloads. Smaller batches reduce memory but increase network calls. There is a balance, and you find it through load testing rather than guesswork.</span></p>
<p><span style="font-weight: 400;">With auto-instrumentation agents, you do not have direct SDK access, but most agents expose equivalent environment variables for batch size and schedule delay. These matter in production just as much as they do with manual instrumentation. A simple example showing where these settings live can save a lot of trial and error:</span></p>
<p><span style="font-weight: 400;">OTEL_BSP_MAX_EXPORT_BATCH_SIZE=512</span></p>
<p><span style="font-weight: 400;">OTEL_BSP_SCHEDULE_DELAY=5000</span></p>
<p><span style="font-weight: 400;">For detailed information, see </span><a href="https://opentelemetry.io/docs/specs/otel/configuration/sdk-environment-variables/" target="_blank" rel="noopener noreferrer"><span style="font-weight: 400;">Environment Variable Specification</span></a><span style="font-weight: 400;">. </span></p>
<p><span style="font-weight: 400;">Regardless of instrumentation type, the collector itself must be treated like any other production service. Monitor its CPU and memory, scale it horizontally when needed, use load balancing with trace ID based routing so spans for the same trace land on the same collector instance, and watch queue lengths in the batch processor. If your collector is not monitored, you do not have observability, you have a single point of failure. </span></p>
<p><span style="font-weight: 400;">For detailed guidance, see</span><a href="https://opentelemetry.io/docs/collector/" target="_blank" rel="noopener noreferrer"> <span style="font-weight: 400;">OpenTelemetry Collector architecture and best practices</span></a><span style="font-weight: 400;">.</span></p>
<h2 id="sampling-strategies-for-opentelemetry-in-production"><b>Sampling Strategies for OpenTelemetry in Production</b></h2>
<p><span style="font-weight: 400;">At some point, you realize capturing 100% of traces is not sustainable. Sampling becomes necessary. However, sampling is not just a cost decision, it also changes what you can see, so it deserves more thought than simply dialing a number down.</span></p>
<h3 id="agent-level-sampling"><b>Agent-Level Sampling</b></h3>
<p><span style="font-weight: 400;">Agent-level sampling makes the decision immediately when a request starts, before a single span hits the collector. The benefit is immediate volume reduction: CPU, memory, and network overhead all drop. The trade-off is permanent blindness for discarded traces. If an error happens in a trace that was not sampled, it simply does not exist in your backend. There is no way to recover it after the fact.</span></p>
<p><span style="font-weight: 400;">Agent-level sampling works well as a baseline control mechanism. Many production systems start at 5 to 10% and adjust based on throughput and debugging needs. It is particularly useful when throughput is extremely high, infrastructure or observability vendor cost is the primary concern, or you need to protect the collector from being overwhelmed. Just keep in mind that it does not guarantee you will retain slow or rare traces that would have been most useful during an incident.</span></p>
<h3 id="tail-sampling"><b>Tail Sampling</b></h3>
<p><span style="font-weight: 400;">Tail sampling moves the decision to the collector, after the entire trace has been observed. This enables smarter decisions: keep slow traces, keep error traces, retain 100% of traffic from business-critical services, and sample normal traffic probabilistically.</span></p>
<p><span style="font-weight: 400;">This is more powerful, but it comes with real operational weight. The collector has to buffer complete traces in memory while waiting for all spans to arrive, which means memory usage is meaningfully higher than with head-based sampling. It also adds latency to trace delivery, since the collector has to wait for the full trace before deciding whether to keep it. If your typical transaction takes 90 seconds to complete, your collector is buffering 90 seconds of trace data before it can act, which is a lot of memory at scale, and your traces will arrive in your backend 90 or more seconds after the fact. For short-lived transactions this is barely noticeable. For long-running workflows, plan accordingly.</span></p>
<p><span style="font-weight: 400;">In distributed systems, spans for the same trace can arrive at multiple collector instances. If each collector makes independent sampling decisions, traces become fragmented, leaving gaps that make debugging much harder. Using tail sampling with load-balanced routing, where all spans for a trace are routed to the same collector instance using trace ID hashing, keeps traces intact and reliable. To be precise – this </span><b>sticky routing is required for well-functioning tail-sampling.</b></p>
<p><img decoding="async" class="alignnone size-large wp-image-70531" src="https://sematext.com/wp-content/uploads/2026/02/opentelemetry-sampling.png" alt="" width="618" height="1024"></p>
<p><span style="font-weight: 400;">The most effective production strategy usually combines both approaches: use agent-level sampling to cut down overall span volume and prevent the collector from being overwhelmed, then use tail sampling at the collector to make sure high-value traces, slow requests, errors, and critical transactions, are preserved. Sampling is not random volume reduction. It is selecting the traces that help you debug real incidents.</span></p>
<p><img decoding="async" class="alignnone size-large wp-image-70533" src="https://sematext.com/wp-content/uploads/2026/02/opentelemetry-lb-routing.png" alt="" width="618" height="1024"></p>
<p><span style="font-weight: 400;">For the official OpenTelemetry guidance, refer to the </span><a href="https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/sdk.md#sampling" target="_blank" rel="noopener noreferrer"><span style="font-weight: 400;">OpenTelemetry sampling specification</span></a><span style="font-weight: 400;">.</span></p>
<h3 id="how-to-set-tail-sampling-policies-in-practice"><b>How to Set Tail Sampling Policies in Practice</b></h3>
<p><span style="font-weight: 400;">Before writing any tail sampling policy, start by asking yourself a few practical questions: what types of incidents happen most often? Are latency regressions more frequent than hard failures? Which services are business-critical or compliance-sensitive? The answers should guide your sampling decisions, not the other way around.</span></p>
<p><span style="font-weight: 400;">For example, if most of your incidents are latency-related, prioritize keeping slow traces. A common starting point is to retain 100% of traces slower than twice your </span><a href="https://sematext.com/glossary/service-level-objective/"><span style="font-weight: 400;">SLO</span></a><span style="font-weight: 400;">, while sampling just 5 to 10% of normal traffic. For compliance-sensitive endpoints, always keep those traces intact. For business-critical services, bias your sampling to capture a higher proportion of requests, perhaps 50% from your payment service but only 5% from static content services.</span></p>
<p><span style="font-weight: 400;">It is also worth maintaining a small baseline sample across all services, around 5 to 10% of overall traffic, even for well-behaved paths. This gives you trend data and lets you detect unknown failure modes you did not anticipate when writing the policies. Without that baseline, you lose visibility into normal system behavior and can miss gradual degradations that do not trigger your explicit rules.</span></p>
<h2 id="agent-and-collector-stability-the-hidden-risk"><b>Agent and Collector Stability: The Hidden Risk</b></h2>
<p><span style="font-weight: 400;">Agents and collectors are not passive observers. They are active components in your application infrastructure, and they can fail like any other component.</span></p>
<p><span style="font-weight: 400;">The collector is the more straightforward case. OpenTelemetry SDKs instrument your application code directly, and the collector runs as a separate process (or set of processes) that receives, processes, and exports telemetry. When a collector crashes, all buffered data is lost, including any traces that were being held in memory for tail sampling decisions. Memory spikes can trigger </span><a href="https://sematext.com/glossary/linux-out-of-memory-killer/" target="_blank" rel="noopener"><span style="font-weight: 400;">OOM kills</span></a><span style="font-weight: 400;">, and if you are running a single collector instance, the entire observability pipeline goes dark until it recovers.</span></p>
<p><span style="font-weight: 400;">The common causes are predictable: exporters fall behind because the backend is slow or throttling ingest, queues grow, memory fills, and eventually the collector crashes. The practical safeguard against this is the memory limiter processor, which watches the collector’s overall memory consumption and temporarily refuses incoming data when it crosses your configured threshold, giving the collector room to catch up.</span></p>
<pre><code><span style="font-weight: 400;">processors:</span>

<span style="font-weight: 400;">  memory_limiter:</span>

<span style="font-weight: 400;">    check_interval: 1s</span>

<span style="font-weight: 400;">    limit_mib: 2000</span>

<span style="font-weight: 400;">    spike_limit_mib: 400</span>

<span style="font-weight: 400;">service:</span>

<span style="font-weight: 400;">  pipelines:</span>

<span style="font-weight: 400;">    traces:</span>

<span style="font-weight: 400;">      receivers: [otlp]</span>

<span style="font-weight: 400;">      processors: [memory_limiter, batch]</span>

<span style="font-weight: 400;">      exporters: [otlphttp]</span></code></pre>
<p><span style="font-weight: 400;">This is one of those configurations that feels optional until the day it is not.</span></p>
<p><span style="font-weight: 400;">Auto-instrumentation adds another layer of complexity. Java agents rewrite bytecode at runtime, async context propagation in .NET or Node.js can behave unexpectedly under load, and in high-throughput systems you may spend measurable CPU time just recording spans. This is why load testing your instrumentation matters as much as load testing your application. Before rolling out to production, measure baseline latency without instrumentation, then measure P50, P95, and P99 latency with it enabled. A 5 to 10% latency increase is often acceptable. Triple-digit millisecond overhead per request is not.</span></p>
<p><span style="font-weight: 400;">For detailed instructions by language, see the </span><a href="https://opentelemetry.io/docs/languages/" target="_blank" rel="noopener noreferrer"><span style="font-weight: 400;">OpenTelemetry auto-instrumentation documentation</span></a><span style="font-weight: 400;">.</span></p>
<h3 id="exporter-bottlenecks-when-the-backend-cannot-keep-up"><b>Exporter Bottlenecks: When the Backend Cannot Keep Up</b></h3>
<p><span style="font-weight: 400;">Even if your SDKs and collectors are perfectly tuned, the backend you are exporting to may not be. When the backend is slow, throttling requests, or simply unable to absorb your telemetry volume, batches start piling up in the exporter queues inside the collector. Left unchecked, this cascades into collector instability.</span></p>
<p><span style="font-weight: 400;">The signals to watch for are </span><code>otelcol_exporter_send_failed_spans</code><span style="font-weight: 400;"> (a counter visible in the collector’s own self-monitoring metrics), growing exporter queue lengths, increased export latency, and rising memory pressure in the collector process.</span></p>
<p><span style="font-weight: 400;">For self-hosted backends like Elasticsearch, OpenSearch, or Prometheus, ingestion capacity must match telemetry throughput and cardinality. For external vendors, you need to understand their API rate limits, network latency characteristics, and burst handling policies before you are under pressure. An asynchronous exporter with buffering, retry logic, and exponential backoff is essential. Without it, a temporary backend slowdown cascades through the entire pipeline. Your observability stack is only as reliable as its slowest component.</span></p>
<h3 id="why-this-matters-in-real-systems"><b>Why This Matters in Real Systems</b></h3>
<p><span style="font-weight: 400;">Many OpenTelemetry tutorials and examples show instrumentation working out of the box, which it does, in a demo environment with predictable traffic and no cost constraints. Real production systems are a different beast entirely: high throughput, distributed microservices, partial network failures, uneven traffic spikes, and budgets that someone is accountable for.</span></p>
<p><span style="font-weight: 400;">OpenTelemetry is genuinely powerful, but it requires operational discipline. When you adopt it, you are not just instrumenting a few services. You are operating an observability pipeline that itself needs capacity planning, monitoring, load testing, a clear sampling strategy, and ongoing cardinality governance. Treat it as first-class infrastructure and it becomes a strong foundation for understanding your systems. Treat it as a set-and-forget library and it becomes your next incident.</span></p>
<p class="space-top"><a href="https://apps.sematext.com/ui/registration" class="button-big" target="_blank" rel="noopener noreferrer">Start Free Trial</a></p><hr class="hidden"><p>The post <a href="https://sematext.com/blog/opentelemetry-production-monitoring-what-breaks-and-how-to-prevent-it/">OpenTelemetry Production Monitoring: What Breaks, and How to Prevent It</a> appeared first on <a href="https://sematext.com">Sematext</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Troubleshooting Microservices with OpenTelemetry Distributed Tracing</title>
		<link>https://sematext.com/blog/troubleshooting-microservices-with-opentelemetry-distributed-tracing/</link>
		
		<dc:creator><![CDATA[fulya.uluturk]]></dc:creator>
		<pubDate>Sun, 15 Feb 2026 13:46:17 +0000</pubDate>
				<category><![CDATA[OpenTelemetry]]></category>
		<category><![CDATA[Tracing]]></category>
		<category><![CDATA[distributed tracing]]></category>
		<category><![CDATA[microservices]]></category>
		<category><![CDATA[opentelemetry]]></category>
		<guid isPermaLink="false">https://sematext.com/?p=70515</guid>

					<description><![CDATA[<p>Distributed tracing doesn’t just show you what happened. It shows you why things broke. While logs tell you a service returned a 500 error and metrics show latency spiked, only traces reveal the full chain of causation: the upstream timeout that triggered a retry storm, the N+1 query pattern that saturated your connection pool, or [&#8230;]</p>
<p>The post <a href="https://sematext.com/blog/troubleshooting-microservices-with-opentelemetry-distributed-tracing/">Troubleshooting Microservices with OpenTelemetry Distributed Tracing</a> appeared first on <a href="https://sematext.com">Sematext</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>Distributed tracing doesn’t just show you what happened. It shows you <i>why</i> things broke. While logs tell you a service returned a 500 error and metrics show latency spiked, only traces reveal the full chain of causation: the upstream timeout that triggered a retry storm, the N+1 query pattern that saturated your connection pool, or the missing cache hit that turned a 50ms call into a 3-second database roundtrip.</p>
<p>This guide covers practical, trace-based troubleshooting patterns for production microservices. You’ll learn how to use OpenTelemetry distributed traces to diagnose the most common, and most frustrating, problems that surface in distributed architectures.</p>
<p><b>What you’ll learn:</b></p>
<ul>
<li aria-level="1">How to identify latency bottlenecks using trace waterfall analysis</li>
<li aria-level="1">Detecting N+1 query patterns and database performance issues in traces</li>
<li aria-level="1">Diagnosing retry storms, timeout cascades, and circuit breaker failures</li>
<li aria-level="1">Using error propagation traces to find root causes across service boundaries</li>
<li aria-level="1">Spotting connection pool exhaustion, cache misses, and queue backlogs</li>
<li aria-level="1">Correlating traces with logs and metrics for full-context debugging</li>
</ul>
<p>For step-by-step instrumentation setup, see our companion guide: <a href="https://sematext.com/blog/how-to-implement-distributed-tracing-in-microservices-with-opentelemetry-auto-instrumentation/">How to Implement Distributed Tracing in Microservices with OpenTelemetry Auto-Instrumentation</a>. For production-hardening your instrumentation, see <a href="https://sematext.com/blog/opentelemetry-instrumentation-best-practices-for-microservices-observability/">OpenTelemetry Instrumentation Best Practices for Microservices Observability</a>.</p>
<h2 id="why-traces-are-the-best-tool-for-microservices-troubleshooting"><b>Why Traces Are the Best Tool for Microservices Troubleshooting</b></h2>
<p>Logs, metrics, and traces each serve a different purpose. But when a production incident hits a distributed system, traces are uniquely positioned to answer the hardest questions, especially those that span service boundaries.</p>
<table>
<thead>
<tr>
<th><b>Troubleshooting Question</b></th>
<th><b>Logs</b></th>
<th><b>Metrics</b></th>
<th><b>Traces</b></th>
</tr>
</thead>
<tbody>
<tr>
<td>Which service is slow?</td>
<td>❌ Scattered across services</td>
<td>✅ Latency dashboards</td>
<td>✅ Waterfall shows exact span</td>
</tr>
<tr>
<td>Why is it slow?</td>
<td>🟡 If you logged enough context</td>
<td>❌ No causal detail</td>
<td>✅ Child spans reveal cause</td>
</tr>
<tr>
<td>Which upstream call caused the error?</td>
<td>❌ Requires correlation IDs</td>
<td>❌ Only shows error rate</td>
<td>✅ Error propagation is visible</td>
</tr>
<tr>
<td>Is it a single request or systemic?</td>
<td>❌ Hard to aggregate</td>
<td>✅ Rate/error trends</td>
<td>✅ Trace grouping by pattern</td>
</tr>
<tr>
<td>What was the exact sequence of calls?</td>
<td>❌ Requires reconstruction</td>
<td>❌ No ordering info</td>
<td>✅ Waterfall shows call graph</td>
</tr>
</tbody>
</table>
<p>The key insight is that traces give you <i>causation</i>, not just <i>correlation</i>. When service A calls service B, which calls service C, and C fails, a trace shows you the entire chain, the timing of each call, and exactly where things went wrong.</p>
<h2 id="anatomy-of-a-troubleshooting-trace"><b>Anatomy of a Troubleshooting Trace</b></h2>
<p>Before diving into specific patterns, let’s establish what you’re looking at in a trace waterfall. Understanding the structure makes pattern recognition faster during incidents.</p>
<p>A distributed trace consists of spans organized in a parent-child hierarchy, as defined by the<a href="https://opentelemetry.io/docs/specs/otel/trace/api/" target="_blank" rel="noopener noreferrer"> OpenTelemetry Trace specification</a>. Each span represents a single operation: an HTTP request, a database query, a cache lookup, a message publish. The root span represents the entry point, and child spans represent downstream operations.</p>
<pre><code>[Root Span: GET /api/orders/12345] ─────────── 1,247ms

├── [auth-service: POST /validate] ── 23ms
├── [order-service: GET /orders/12345] ────── 1,180ms
│     ├── [PostgreSQL: SELECT * FROM orders] ── 12ms
│     ├── [inventory-service: GET /stock] ─── 890ms  ← BOTTLENECK
│     │     ├── [Redis: GET inventory:12345] ── 2ms (miss)
│     │     └── [PostgreSQL: SELECT ...] ── 875ms  ← ROOT CAUSE
│     └── [pricing-service: GET /calculate] ── 45ms
└── [notification-service: POST /email] ── 18ms</code></pre>
<p>In this trace, the total request took 1,247ms. The trace waterfall immediately shows that inventory-service consumed 890ms, and within it, a database query took 875ms following a cache miss. Without the trace, you’d see a slow /api/orders endpoint in your metrics and have to investigate each service individually.</p>
<p><b>Key span attributes to examine during troubleshooting (see the full</b><a href="https://opentelemetry.io/docs/specs/semconv/" target="_blank" rel="noopener noreferrer"> <b>OpenTelemetry Semantic Conventions</b></a><b> for reference):</b></p>
<table>
<thead>
<tr>
<th><b>Attribute</b></th>
<th><b>What It Tells You</b></th>
</tr>
</thead>
<tbody>
<tr>
<td>http.status_code</td>
<td>HTTP response status for service calls</td>
</tr>
<tr>
<td>db.statement</td>
<td>The actual SQL query executed</td>
</tr>
<tr>
<td>db.system</td>
<td>Which database (PostgreSQL, MySQL, Redis)</td>
</tr>
<tr>
<td>http.method + http.url</td>
<td>Which endpoint was called</td>
</tr>
<tr>
<td>otel.status_code = ERROR</td>
<td>Span completed with an error</td>
</tr>
<tr>
<td>exception.message</td>
<td>Error details if an exception occurred</td>
</tr>
<tr>
<td>net.peer.name</td>
<td>Which host the call went to</td>
</tr>
<tr>
<td>messaging.system</td>
<td>Message broker involved (Kafka, RabbitMQ)</td>
</tr>
<tr>
<td>Span duration</td>
<td>How long the operation took</td>
</tr>
</tbody>
</table>
<h1></h1>
<h2 id="diagnosing-latency-bottlenecks-with-trace-waterfall-analysis"><b>Diagnosing Latency Bottlenecks with Trace Waterfall Analysis</b></h2>
<p>Latency issues are the most common reason teams reach for traces. The waterfall view transforms a vague “the API is slow” complaint into a precise diagnosis.</p>
<h3 id="pattern-the-slow-database-query"><b>Pattern: The Slow Database Query</b></h3>
<p><b>Symptoms in metrics: </b>Elevated p95/p99 latency on a specific endpoint. Database CPU or connection usage may appear normal.</p>
<p><b>What the trace reveals:</b></p>
<pre><code>[order-service: GET /orders] ────────── 2,340ms

├── [PostgreSQL: SELECT o.*, oi.* FROM orders o
│    JOIN order_items oi ON o.id = oi.order_id
│    WHERE o.customer_id = $1
│    ORDER BY o.created_at DESC] ──── 2,280ms  ← Problem
└── [Redis: SET order-cache:customer:789] ── 3ms</code></pre>
<p>The trace shows a single database query consuming 97% of the request time. The db.statement attribute reveals the actual SQL, which is a full table scan joining orders with order items, likely missing an index on customer_id.</p>
<p><b>What to look for in spans:</b></p>
<ul>
<li aria-level="1"><b>db.statement</b>: Check for missing WHERE clauses, full table scans, large JOINs, or unoptimized queries. Use<a href="https://www.postgresql.org/docs/current/sql-explain.html" target="_blank" rel="noopener noreferrer"> EXPLAIN</a> to confirm.</li>
<li aria-level="1"><b>Span duration vs. typical duration</b>: Compare against baseline traces for the same operation</li>
<li aria-level="1"><b>Sequential vs. parallel queries</b>: Are queries running sequentially when they could be parallelized?</li>
</ul>
<h3 id="pattern-sequential-service-calls-missed-parallelization"><b>Pattern: Sequential Service Calls (Missed Parallelization)</b></h3>
<p><b>Symptoms in metrics: </b>High latency that seems disproportionate to what any single service reports.</p>
<p><b>What the trace reveals:</b></p>
<pre><code>[api-gateway: GET /dashboard] ──────────── 1,850ms

├── [user-service: GET /profile] ── 320ms
├── [order-service: GET /recent] ─── 480ms    (starts after user-svc)
├── [notification-svc: GET /unread] ── 410ms  (starts after order-svc)
└── [recommendation-svc: GET /for-you] ── 590ms (starts after notif.)</code></pre>
<p>The waterfall reveals that four independent service calls are executing sequentially. Total time is the sum of all calls (1,800ms) instead of the max (590ms), a 3x penalty. The trace makes this immediately visible because spans don’t overlap.</p>
<p><b>The fix: </b>Refactor to concurrent calls. With parallelization, the trace collapses to ~620ms as all four spans overlap.</p>
<h3 id="pattern-fan-out-amplification"><b>Pattern: Fan-out Amplification</b></h3>
<p><b>Symptoms in metrics: </b>Latency increases with load, but individual service latencies look normal.</p>
<p>The trace reveals a product catalog page making 50 individual HTTP calls to the inventory service, one per product. Each call is fast (45–60ms), but the accumulated overhead of 50 sequential HTTP roundtrips adds up to over 3 seconds.</p>
<p><b>The fix: </b>Replace individual calls with a batch API (GET /stock?skus=A001,A002,…,A050) or use a GraphQL-style query that returns all needed data in a single request.</p>
<h1></h1>
<h2 id="detecting-n1-query-patterns-in-traces"><b>Detecting N+1 Query Patterns in Traces</b></h2>
<p>N+1 queries are one of the most common performance killers in microservices, and traces make them trivially easy to spot. The pattern appears as one initial query followed by N repetitive queries, and in the trace waterfall, it’s unmistakable.</p>
<h3 id="pattern-classic-orm-n1"><b>Pattern: Classic ORM N+1</b></h3>
<p><b>What the trace reveals:</b></p>
<pre><code>[order-service: GET /orders] ─────────── 1,890ms

├── [PostgreSQL: SELECT * FROM orders WHERE status = 'active'
│    LIMIT 50] ── 15ms                              (1 query)
├── [PostgreSQL: SELECT * FROM customers WHERE id = 101] ── 8ms
├── [PostgreSQL: SELECT * FROM customers WHERE id = 102] ── 9ms
├── [PostgreSQL: SELECT * FROM customers WHERE id = 103] ── 7ms
│   ... (47 more identical-pattern queries)
└── [PostgreSQL: SELECT * FROM customers WHERE id = 150] ── 11ms</code></pre>
<p>The trace shows 1 query to fetch orders + 50 individual queries to fetch each order’s customer. ORM lazy loading is the usual culprit. Each query is fast individually, but 51 database roundtrips add up to nearly 2 seconds.</p>
<p><b>How to spot N+1 patterns in your tracing tool:</b></p>
<ul>
<li aria-level="1"><b>High span count on a single trace</b>: A trace with 50+ database spans for a simple endpoint is almost always an N+1</li>
<li aria-level="1"><b>Repetitive db.statement patterns</b>: Same query template with different parameter values</li>
<li aria-level="1"><b>Low individual span duration but high total trace duration</b>: Each query is fast, but there are too many</li>
</ul>
<p><b>The fix: </b>Replace lazy loading with eager loading (JOIN or IN clause):</p>
<p>— Instead of 51 queries, use 1:</p>
<pre><code>SELECT o.*, c.* FROM orders o

JOIN customers c ON o.customer_id = c.id

WHERE o.status = 'active' LIMIT 50</code></pre>
<h3 id="pattern-service-level-n1-microservice-fan-out"><b>Pattern: Service-Level N+1 (Microservice Fan-out)</b></h3>
<p>The N+1 pattern isn’t limited to databases. It manifests across service boundaries too:</p>
<pre><code>[checkout-service: POST /checkout] ───────── 4,100ms
├── [cart-service: GET /cart/items] ── 35ms
│    Response: [{productId: "P1"}, ..., {productId: "P20"}]
├── [product-service: GET /products/P1] ── 120ms
├── [product-service: GET /products/P2] ── 135ms
│   ... (18 more calls)
└── [product-service: GET /products/P20] ── 128ms</code></pre>
<p>The checkout service fetches cart items, then calls the product service individually for each item. The fix: implement a batch endpoint (POST /products/batch accepting a list of IDs) or use request collapsing.</p>
<h1></h1>
<h2 id="diagnosing-timeout-cascades-and-retry-storms"><b>Diagnosing Timeout Cascades and Retry Storms</b></h2>
<p>Timeout cascades are among the most dangerous failure modes in microservices. Patterns like the<a href="https://learn.microsoft.com/en-us/azure/architecture/patterns/circuit-breaker" target="_blank" rel="noopener noreferrer"> circuit breaker</a> exist specifically to contain them. A single slow dependency can cause cascading failures across your entire system, and traces are the fastest way to understand the chain reaction.</p>
<h3 id="pattern-timeout-cascade"><b>Pattern: Timeout Cascade</b></h3>
<p><b>Symptoms in metrics: </b>Multiple services show elevated error rates simultaneously. Latency spikes propagate across services.</p>
<p><b>What the trace reveals:</b></p>
<pre><code>[api-gateway: POST /orders] ──────────── 30,012ms (TIMEOUT)
└── [order-service: POST /create] ─────── 30,005ms (TIMEOUT)
├── [inventory-svc: POST /reserve] ──── 30,001ms (TIMEOUT)
│     └── [PostgreSQL: UPDATE inventory ...] ── 30,000ms
│           otel.status_code: ERROR
│           exception.message: "Lock wait timeout exceeded"
└── [payment-service: POST /charge] (NOT REACHED)</code></pre>
<p>The trace reveals the cascade: a database lock timeout in inventory causes inventory to time out, which causes order-service to time out, which causes the gateway to time out. Without the trace, you’d see three services all timing out and might investigate the wrong one first.</p>
<p><b>Key diagnostic signals in timeout traces:</b></p>
<ul>
<li aria-level="1">Span duration equals the configured timeout value exactly (e.g., 30,000ms), which confirms a timeout rather than slow processing</li>
<li aria-level="1">otel.status_code: ERROR with timeout-related exception messages</li>
<li aria-level="1">Child spans that were never started (like payment-service above), which confirms the timeout interrupted the flow</li>
<li aria-level="1">Multiple parent spans with identical durations, meaning each parent waited for the full timeout of its child</li>
</ul>
<h3 id="pattern-retry-storm"><b>Pattern: Retry Storm</b></h3>
<p><b>Symptoms in metrics: </b>Sudden traffic spike to a downstream service. Error rates increase rather than decrease.</p>
<p><b>What the trace reveals:</b></p>
<pre><code>[order-service: POST /create] ─────────── 12,450ms
├── [inventory-svc: POST /reserve] ── 5,001ms TIMEOUT
├── [inventory-svc: POST /reserve] ── 5,002ms TIMEOUT (retry 1)
├── [inventory-svc: POST /reserve] ── 2,410ms TIMEOUT (retry 2)
│     exception.message: "Connection pool exhausted"
└── Result: ERROR "Failed after 3 retries"</code></pre>
<p>The trace shows the order service retrying the inventory call three times. With 100 concurrent requests all doing the same, the inventory service receives 300 requests instead of 100, a 3x amplification. The connection pool exhaustion on retry 2 confirms the retry storm is making things worse.</p>
<p><b>Multi-layer retry amplification: </b>When multiple layers retry, the multiplication compounds:</p>
<pre><code>Gateway (3 retries) → Order Service (3 retries) → Inventory

= 3 × 3 = 9 requests to inventory per user request</code></pre>
<h2 id="troubleshooting-error-propagation-across-service-boundaries"><b>Troubleshooting Error Propagation Across Service Boundaries</b></h2>
<p>When an error surfaces at the API boundary, the root cause often lies several services deep. Traces let you follow the error propagation chain backwards from symptom to cause.</p>
<h3 id="pattern-hidden-error-origin"><b>Pattern: Hidden Error Origin</b></h3>
<p><b>Symptoms: </b>Users see “Internal Server Error” on the checkout page. Logs show 500 errors cascading through services.</p>
<p><b>What the trace reveals in a single view:</b></p>
<pre><code>[api-gateway: POST /checkout] ─ 500 Internal Server Error
└── [checkout-service: POST /process] ─ 500
├── [cart-service: GET /cart] ─ 200 OK (45ms)
└── [payment-service: POST /charge] ─ 500
└── [fraud-service: POST /evaluate] ─ 500
└── [ML model: POST /predict] ─ 503

exception.message: "Model server OOM:
cannot allocate 2GB for inference batch"</code></pre>
<p>The trace cuts through four levels of error wrapping and reveals the actual root cause: the ML model server ran out of memory. Without the trace, the on-call engineer would start by investigating the checkout service, then the payment service, before eventually reaching the fraud detection service, potentially losing 30+ minutes following the chain manually.</p>
<h3 id="pattern-silent-error-swallowing"><b>Pattern: Silent Error Swallowing</b></h3>
<p>Sometimes errors don’t propagate. Instead, they get silently caught, and the system returns degraded results instead of errors:</p>
<pre><code>[product-service: GET /product/123] ─ 200 OK (890ms)
├── [PostgreSQL: SELECT ...] ── 12ms ─ 200 OK
├── [review-service: GET /reviews] ── 5,001ms ─ TIMEOUT
│     otel.status_code: ERROR
├── [recommendation-svc: GET /similar] ── 5,002ms ─ TIMEOUT
│     otel.status_code: ERROR
└── [Redis: SET product-cache:123] ── 3ms</code></pre>
<p>The product page returns 200 OK, but the trace reveals two child services timed out. Metrics show 200 OK and ~900ms latency. Only the trace reveals the degraded user experience.</p>
<p><b>To catch this pattern: </b>Filter traces by spans with otel.status_code: ERROR even when the root span shows success.</p>
<h2 id="spotting-connection-pool-exhaustion"><b>Spotting Connection Pool Exhaustion</b></h2>
<p>Connection pool exhaustion is subtle. It doesn’t always produce errors, but it silently adds latency to every request as threads wait for available connections.</p>
<h3 id="pattern-pool-wait-time"><b>Pattern: Pool Wait Time</b></h3>
<p><b>What the trace reveals:</b></p>
<pre><code>[order-service: GET /orders] ───────── 2,340ms
├── [PostgreSQL: SELECT ...] ── 15ms
├── [gap: 1,800ms]  ← No spans, just waiting
└── [PostgreSQL: SELECT ...] ── 12ms</code></pre>
<p>The telltale sign is gaps between spans, periods where the service is doing nothing visible. The 1,800ms gap between the first and second database query indicates the thread was waiting for a connection from the pool.</p>
<p><b>Diagnostic approach: </b>Look for consistent gaps in trace waterfalls that don’t correspond to any span. When you see this pattern across multiple traces for the same service, check connection pool metrics (active connections, wait queue depth, pool size). The trace points you to the exact service experiencing pool pressure, and metrics confirm the diagnosis.</p>
<h2 id="diagnosing-cache-effectiveness-issues"><b>Diagnosing Cache Effectiveness Issues</b></h2>
<p>Caches are supposed to reduce latency, but misconfigured caches can make things worse. Traces reveal cache behavior that’s invisible in aggregate metrics.</p>
<h3 id="pattern-cache-miss-cascade"><b>Pattern: Cache Miss Cascade</b></h3>
<pre><code>[product-service: GET /product/456] ─────── 1,250ms
├── [Redis: GET product:456] ── 1ms (MISS)
├── [PostgreSQL: SELECT * FROM products ...] ── 85ms
├── [Redis: GET product:456:reviews] ── 1ms (MISS)
├── [review-service: GET /reviews] ── 890ms
│     ├── [PostgreSQL: SELECT ...reviews...] ── 45ms
│     └── [PostgreSQL: SELECT ...users...] ── 830ms  ← Slow join
├── [Redis: SET product:456] ── 2ms
└── [Redis: SET product:456:reviews] ── 1ms</code></pre>
<p>The trace shows: both cache lookups missed, forcing expensive database queries and service calls. The review service’s slow user join (830ms) is the real latency contributor, normally hidden behind a cache hit.</p>
<p><b>To monitor cache effectiveness with traces: </b>Add custom span attributes for cache hit/miss status. Then in your tracing tool, filter and group by this attribute to see miss rates per operation, not just aggregate miss rates.</p>
<pre><code># Python example: Adding cache status to spans

from opentelemetry import trace

tracer = trace.get_tracer("cache-instrumentation")

def get_from_cache(key):

with tracer.start_as_current_span("cache.lookup") as span:

span.set_attribute("cache.key", key)

result = redis_client.get(key)

span.set_attribute("cache.hit", result is not None)

return result</code></pre>
<h3 id="pattern-cache-stampede"><b>Pattern: Cache Stampede</b></h3>
<p>When a popular cache key expires, many concurrent requests simultaneously miss the cache and hit the database, a problem known as<a href="https://redis.io/blog/cache-stampede/" target="_blank" rel="noopener noreferrer"> cache stampede</a>. Looking at multiple traces for the same endpoint around the same timestamp reveals the stampede: each trace shows a cache miss, and database query durations increase progressively as the database becomes overloaded. All traces set the same cache key, resulting in redundant writes.</p>
<h2 id="troubleshooting-message-queue-issues"><b>Troubleshooting Message Queue Issues</b></h2>
<p>Asynchronous messaging adds complexity to troubleshooting because the producer and consumer execute at different times. OpenTelemetry’s context propagation via<a href="https://www.w3.org/TR/trace-context/" target="_blank" rel="noopener noreferrer"> W3C Trace Context</a> headers connects these spans into a single trace.</p>
<h3 id="pattern-consumer-lag"><b>Pattern: Consumer Lag</b></h3>
<pre><code>[order-service: POST /orders] ─ (publishes to Kafka)

├── [Kafka: produce to orders-topic] ── 5ms
│     messaging.kafka.partition: 3
│     messaging.kafka.offset: 1847293
│
│  ~~~ 45,000ms gap (consumer lag) ~~~
│
└── [fulfillment-svc: consume from orders-topic] ── 120ms
└── [PostgreSQL: INSERT INTO fulfillment_queue] ── 8ms </code></pre>
<p>The trace links the producer span (order-service) to the consumer span (fulfillment-service) through propagated context. The 45-second gap between produce and consume timestamps reveals consumer lag. The consumer itself processes quickly (120ms), so the problem is in<a href="https://kafka.apache.org/documentation/#consumerconfigs" target="_blank" rel="noopener noreferrer"> Kafka consumer group</a> throughput, not processing logic.</p>
<h3 id="pattern-poison-message-dead-letter"><b>Pattern: Poison Message / Dead Letter</b></h3>
<pre><code>[order-service: produce to orders-topic] ── 3ms

→ [fulfillment-svc: consume attempt 1] ── 15ms ── ERROR
│    exception.message: "Invalid product SKU format: null"
→ [fulfillment-svc: consume attempt 2] ── 12ms ── ERROR
→ [fulfillment-svc: consume attempt 3] ── 14ms ── ERROR
→ [dead-letter-queue: produce to orders-dlq] ── 4ms </code></pre>
<p>The trace shows a message being consumed, failing, retried twice, and finally sent to the dead letter queue. The exception message reveals the root cause: a null product SKU, likely a producer-side validation issue.</p>
<h2 id="using-trace-based-alerting-for-proactive-troubleshooting"><b>Using Trace-Based Alerting for Proactive Troubleshooting</b></h2>
<p>Reactive troubleshooting (waiting for users to complain) isn’t good enough. Modern tracing tools support alerting on trace-derived signals that catch issues before they impact users.</p>
<h3 id="alert-on-red-metrics-derived-from-traces"><b>Alert on RED Metrics Derived from Traces</b></h3>
<table>
<thead>
<tr>
<th><b>Alert</b></th>
<th><b>Condition</b></th>
<th><b>What It Catches</b></th>
</tr>
</thead>
<tbody>
<tr>
<td>Error rate spike</td>
<td>Error rate &gt; 5% for 5 minutes</td>
<td>Failed deployments, dependency outages</td>
</tr>
<tr>
<td>Latency degradation</td>
<td>p95 latency &gt; 2x baseline for 10 min</td>
<td>Slow queries, missing indexes, cache failures</td>
</tr>
<tr>
<td>Throughput drop</td>
<td>Request rate &lt; 50% of expected for 5 min</td>
<td>Upstream routing issues, DNS failures</td>
</tr>
<tr>
<td>Error rate by operation</td>
<td>Any operation error rate &gt; 10%</td>
<td>Targeted failures in specific endpoints</td>
</tr>
</tbody>
</table>
<h3 id="trace-specific-alerts"><b>Trace-Specific Alerts</b></h3>
<p>Beyond RED metrics, some conditions are only visible through trace analysis:</p>
<ul>
<li aria-level="1"><b>Span count anomaly</b>: Alert when average spans-per-trace exceeds a threshold, catching N+1 regressions after deployments</li>
<li aria-level="1"><b>New error types</b>: Alert when exception.type values appear that haven’t been seen in the last 7 days</li>
<li aria-level="1"><b>Missing service in trace</b>: Alert when an expected service stops appearing in traces for a critical flow</li>
</ul>
<h2 id="building-a-troubleshooting-workflow-with-sematext-tracing"><b>Building a Troubleshooting Workflow with Sematext Tracing</b></h2>
<p><a href="https://sematext.com/tracing/">Sematext Tracing</a> provides the trace analysis capabilities needed to apply all the patterns described above. Here’s how to build an effective troubleshooting workflow.</p>
<h3 id="step-1-start-with-the-service-overview"><b>Step 1: Start with the Service Overview</b></h3>
<p>The <a href="https://sematext.com/docs/tracing/reports/overview/">Tracing Overview</a> dashboard provides RED metrics (Rate, Error, Duration) across all instrumented services. This is your starting point: identify which service has elevated error rates or latency, and in which time window the problem started.</p>
<p><img decoding="async" class="alignnone size-large wp-image-70391" src="https://sematext.com/wp-content/uploads/2026/01/tracing-overview-01-618x1024.png" alt="" width="618" height="1024" srcset="https://sematext.com/wp-content/uploads/2026/01/tracing-overview-01-618x1024.png 618w, https://sematext.com/wp-content/uploads/2026/01/tracing-overview-01-181x300.png 181w, https://sematext.com/wp-content/uploads/2026/01/tracing-overview-01-768x1273.png 768w, https://sematext.com/wp-content/uploads/2026/01/tracing-overview-01-927x1536.png 927w, https://sematext.com/wp-content/uploads/2026/01/tracing-overview-01-1235x2048.png 1235w, https://sematext.com/wp-content/uploads/2026/01/tracing-overview-01-scaled.png 1544w" sizes="(max-width: 618px) 100vw, 618px" /></p>
<h3 id="step-2-drill-into-the-trace-explorer"><b>Step 2: Drill into the Trace Explorer</b></h3>
<p>Use the <a href="https://sematext.com/docs/tracing/reports/explorer/">Trace Explorer</a> to filter traces by the affected service, time window, and error status. Sort by duration to find the slowest traces, or filter by otel.status_code: ERROR to find failures.</p>
<p><b>Key filters for troubleshooting:</b></p>
<ul>
<li aria-level="1"><b>By service name</b>: Isolate traces involving a specific service</li>
<li aria-level="1"><b>By minimum duration</b>: Find traces exceeding your latency SLO</li>
<li aria-level="1"><b>By status</b>: Filter for error traces only</li>
<li aria-level="1"><b>By operation</b>: Focus on a specific endpoint or database operation</li>
<li aria-level="1"><b>By custom attributes</b>: Filter by customer ID, order ID, or other business context</li>
</ul>
<p><img decoding="async" class="alignnone size-large wp-image-70517" src="https://sematext.com/wp-content/uploads/2026/02/trace-explorer-01-1024x794.png" alt="" width="640" height="496" srcset="https://sematext.com/wp-content/uploads/2026/02/trace-explorer-01-1024x794.png 1024w, https://sematext.com/wp-content/uploads/2026/02/trace-explorer-01-300x233.png 300w, https://sematext.com/wp-content/uploads/2026/02/trace-explorer-01-768x596.png 768w, https://sematext.com/wp-content/uploads/2026/02/trace-explorer-01-1536x1191.png 1536w, https://sematext.com/wp-content/uploads/2026/02/trace-explorer-01-2048x1589.png 2048w" sizes="(max-width: 640px) 100vw, 640px" /></p>
<h3 id="step-3-analyze-the-trace-waterfall"><b>Step 3: Analyze the Trace Waterfall</b></h3>
<p>Open the <a href="https://sematext.com/docs/tracing/reports/trace-details/">Trace Details</a> view for a representative trace. The waterfall visualization shows the complete request flow with timing for each span. Look for the patterns described in this guide: long spans, gaps between spans, high span counts, and error spans.</p>
<p><img decoding="async" class="alignnone size-large wp-image-70516" src="https://sematext.com/wp-content/uploads/2026/02/span-details-01-1024x850.png" alt="" width="640" height="531" srcset="https://sematext.com/wp-content/uploads/2026/02/span-details-01-1024x850.png 1024w, https://sematext.com/wp-content/uploads/2026/02/span-details-01-300x249.png 300w, https://sematext.com/wp-content/uploads/2026/02/span-details-01-768x638.png 768w, https://sematext.com/wp-content/uploads/2026/02/span-details-01-1536x1276.png 1536w, https://sematext.com/wp-content/uploads/2026/02/span-details-01-2048x1701.png 2048w" sizes="(max-width: 640px) 100vw, 640px" /></p>
<h3 id="step-4-set-up-alerts"><b>Step 4: Set Up Alerts</b></h3>
<p>Configure <a href="https://sematext.com/alerts/">alerts</a> on the RED metrics derived from your traces. Start with error rate and p95 latency alerts for your most critical services and endpoints, then expand to more specific alerts as you learn your system’s failure patterns.</p>
<h2 id="troubleshooting-checklist-for-production-incidents"><b>Troubleshooting Checklist for Production Incidents</b></h2>
<p>When an incident hits, use this trace-based workflow to minimize time-to-resolution:</p>
<ol>
<li aria-level="1"><b>Identify the scope</b>: Check the service overview: is the issue isolated to one service or affecting multiple? Are error rates or latency elevated?</li>
<li aria-level="1"><b>Find representative traces</b>: Use the trace explorer to filter for affected traces. Sort by duration for latency issues, filter by error status for failures.</li>
<li aria-level="1"><b>Read the waterfall</b>: Open 3–5 representative traces. Look for: the longest span (bottleneck), error spans (root cause), gaps between spans (pool exhaustion), high span counts (N+1 patterns), and missing expected spans (service unreachable).</li>
<li aria-level="1"><b>Check span attributes</b>: Examine db.statement for bad queries, http.status_code for upstream failures, exception.message for error details, and custom attributes for business context.</li>
<li aria-level="1"><b>Correlate with other signals</b>: Jump to logs for detailed error messages and stack traces. Check infrastructure metrics for resource exhaustion. Look at deployment events for recent changes.</li>
<li aria-level="1"><b>Verify the fix</b>: After applying a fix, compare new traces against the problematic ones. Confirm the bottleneck span duration decreased, error spans disappeared, or the N+1 pattern resolved.</li>
</ol>
<h2 id="summary"><b>Summary</b></h2>
<p>Distributed tracing transforms microservices troubleshooting from guesswork into systematic diagnosis. The patterns covered in this guide, including latency bottlenecks, N+1 queries, timeout cascades, retry storms, error propagation, connection pool exhaustion, cache failures, and message queue issues, account for the vast majority of production incidents in distributed systems.</p>
<p>The key is developing pattern recognition: learn what healthy traces look like for your critical flows, and the unhealthy patterns will stand out immediately during incidents. OpenTelemetry auto-instrumentation provides the data foundation, and a capable tracing backend like <a href="https://sematext.com/tracing/">Sematext Tracing</a> gives you the analysis tools to turn that data into fast resolution.</p>
<p><b>Next steps:</b></p>
<ul>
<li aria-level="1">Not yet instrumented? Start with <a href="https://sematext.com/blog/how-to-implement-distributed-tracing-in-microservices-with-opentelemetry-auto-instrumentation/">How to Implement Distributed Tracing in Microservices with OpenTelemetry Auto-Instrumentation</a></li>
<li aria-level="1">Need to optimize your instrumentation? Read <a href="https://sematext.com/blog/opentelemetry-instrumentation-best-practices-for-microservices-observability/">OpenTelemetry Instrumentation Best Practices for Microservices Observability</a></li>
<li aria-level="1">Want to extract higher-level insights? See From Raw Traces to Operational Intelligence (coming soon – <a href="mailto:info@sematext.com">contact us</a>)</li>
<li aria-level="1">Ready to try? <a href="https://apps.sematext.com/ui/registration" target="_blank" rel="noopener noreferrer">Start your free Sematext trial</a>, no credit card required</li>
</ul>
<p class="space-top"><a href="https://apps.sematext.com/ui/registration" class="button-big" target="_blank" rel="noopener noreferrer">Start Free Trial</a></p><hr class="hidden"><p>The post <a href="https://sematext.com/blog/troubleshooting-microservices-with-opentelemetry-distributed-tracing/">Troubleshooting Microservices with OpenTelemetry Distributed Tracing</a> appeared first on <a href="https://sematext.com">Sematext</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>OpenTelemetry in Production: Design for Order, High Signal, Low Noise, and Survival</title>
		<link>https://sematext.com/blog/opentelemetry-production-design/</link>
		
		<dc:creator><![CDATA[Otis]]></dc:creator>
		<pubDate>Wed, 11 Feb 2026 10:33:15 +0000</pubDate>
				<category><![CDATA[Logging]]></category>
		<category><![CDATA[Monitoring]]></category>
		<category><![CDATA[OpenTelemetry]]></category>
		<category><![CDATA[Tracing]]></category>
		<guid isPermaLink="false">https://sematext.com/?p=70498</guid>

					<description><![CDATA[<p>A lot of talk around OpenTelemetry has to do with instrumentation, especially auto-instrumentation, about OTel being vendor neutral, being open and a defacto standard. But how you use the final output of OTel is what makes business difference. In other words, how do you use it to make your life as an SRE/DevOps/biz person easier? [&#8230;]</p>
<p>The post <a href="https://sematext.com/blog/opentelemetry-production-design/">OpenTelemetry in Production: Design for Order, High Signal, Low Noise, and Survival</a> appeared first on <a href="https://sematext.com">Sematext</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>A lot of talk around OpenTelemetry has to do with instrumentation, especially <a href="https://sematext.com/blog/how-to-implement-distributed-tracing-in-microservices-with-opentelemetry-auto-instrumentation/">auto-instrumentation</a>, about OTel being vendor neutral, being open and a defacto standard. But how you use the final output of OTel is what makes business difference.</p>
<p>In other words, how do you use it to make your life as an SRE/DevOps/biz person easier?</p>
<p>How do you have to set things up to truly solve production issues faster?</p>
<p>And does doing that require you to spend more money on observability or can you be smart about how you set things up so that OTel doesn’t break the bank?</p>
<p>While we were putting the finishing touches on Sematext’s OTel support, I asked one of my friends about their experience with and use of OTel in the context of questions like the ones above. The friend, the company, and the monitoring vendor they used will go unnamed, but here are the experiences and the practices my friend shared.</p>
<p>We’re a mid-sized org with about 30 frontend and backend developers. We know our way around observability, but have not adopted OpenTelemetry until late 2025. When we first rolled out OpenTelemetry in production, it felt like we had finally “done observability right.”</p>
<p>Every service was instrumented. OK, almost every service. ;)<br>
Every request had a trace.<br>
Every component had a metric.<br>
Logs were nicely structured and correlated.</p>
<p>It was not quick and easy to set it all up, but we split the work among several team members and we did it.</p>
<p>However, within about two weeks we started observing – pun intended – problems:</p>
<ul>
<li aria-level="1">our storage bill doubled</li>
<li aria-level="1">dashboards became slow</li>
<li aria-level="1">our team stopped opening traces</li>
<li aria-level="1">cardinality exploded</li>
<li aria-level="1">and we started sampling randomly just to survive</li>
</ul>
<p>It became apparent pretty quickly that just adopting OpenTelemetry is not automatically going to give us good monitoring. OpenTelemetry doesn’t give you a signal strategy. Out of the box, with naive usage, it just gives you a firehose and enables you to drown in your own telemetry more quickly.</p>
<p>We kept this new firehose on, but we had to quickly start making decisions around things like:</p>
<ul>
<li aria-level="1">what belongs in metrics</li>
<li aria-level="1">what belongs in traces</li>
<li aria-level="1">what belongs in logs</li>
<li aria-level="1">and, perhaps most importantly, what should never be emitted at all!</li>
</ul>
<h2 id="how-i-think-about-the-three-telemetry-signals-now"><b>How I Think About the Three Telemetry Signals Now</b></h2>
<p>Early on, we treated metrics, logs, and traces as three different ways to describe the same thing. They are not. That was a mistake. They are different tools with different costs and different failure modes.</p>
<p>Now I think about them like this:</p>
<ul>
<li aria-level="1">Metrics answer: “Is the system healthy?” (both from tech/engineering perspective and business – we use metrics to understand the business side of things, too)</li>
<li aria-level="1">Traces answer: “Where did the time go?”</li>
<li aria-level="1">Logs answer: “What exactly happened?”</li>
</ul>
<p>This separation of concerns feels simple and straightforward. As long as the observability tool you’re using has good UX for cross-connecting and correlating these signals, this separation should serve you well.</p>
<h2 id="the-architecture-we-ended-up-with"><b>The Architecture We Ended Up With</b></h2>
<p>This is the shape that finally worked for us:</p>
<p><img decoding="async" class="alignnone wp-image-70499" src="https://sematext.com/wp-content/uploads/2026/02/application-collector-telemetry-300x148.png" alt="" width="867" height="428" srcset="https://sematext.com/wp-content/uploads/2026/02/application-collector-telemetry-300x148.png 300w, https://sematext.com/wp-content/uploads/2026/02/application-collector-telemetry-1024x505.png 1024w, https://sematext.com/wp-content/uploads/2026/02/application-collector-telemetry-768x379.png 768w, https://sematext.com/wp-content/uploads/2026/02/application-collector-telemetry.png 1532w" sizes="(max-width: 867px) 100vw, 867px" /></p>
<p> </p>
<p>The key idea is simple:<br>
<b>Applications emit everything. The collector acts as a filter, among other things, and decides what survives.</b></p>
<p>If you try to enforce strategy in application code, you’ll fail. Teams move too fast, especially now with AI. You need one place where you can say:</p>
<ul>
<li aria-level="1">keep error traces</li>
<li aria-level="1">drop noisy attributes</li>
<li aria-level="1">batch aggressively</li>
<li aria-level="1">deduplicate</li>
<li aria-level="1">enforce memory limits</li>
<li aria-level="1">…</li>
</ul>
<p>That place is the <a href="https://opentelemetry.io/docs/collector/" target="_blank" rel="noopener noreferrer">collector</a>.</p>
<h2 id="metrics-what-we-actually-trust-during-incidents"><b>Metrics: What We Actually Trust During Incidents</b></h2>
<p>The first real incident after we adopted OpenTelemetry was a checkout latency spike. Nobody opened a trace first. We all looked at metrics because our alert notifications pointed us there.</p>
<p>Metrics are what we trust when:</p>
<ul>
<li aria-level="1">we get an alert notification</li>
<li aria-level="1">the CTO asks “are we down?”</li>
<li aria-level="1">a deploy goes wrong</li>
</ul>
<p>So we designed metrics to answer only three questions:</p>
<ul>
<li aria-level="1">How many requests?</li>
<li aria-level="1">How many errors?</li>
<li aria-level="1">How slow are they?</li>
</ul>
<p>Sounds familiar? 👌Yes, <a href="https://thenewstack.io/monitoring-methodologies-red-and-use/" target="_blank" rel="noopener noreferrer">RED</a>!</p>
<p>Here’s a snippet from the relevant Python application.</p>
<h3 id="example-python"><b>Example (Python)</b></h3>
<pre>from opentelemetry import metrics

meter = metrics.get_meter("checkout")

request_counter = meter.create_counter(
    "http.server.requests",
    description="Total HTTP requests"
)

latency_histogram = meter.create_histogram(
    "http.server.duration",
    unit="ms"
)

def handle_request():
    request_counter.add(1, {"route": "/checkout", "status": "200"})
    latency_histogram.record(245, {"route": "/checkout"})

</pre>
<h3 id="hard-rule-we-learned"><b>Hard Rule We Learned</b></h3>
<p>It’s actually very simple: If a label (aka tag) can be different for every request, it does not belong in metrics.</p>
<p>These caused real problems for us:</p>
<ul>
<li aria-level="1">user_id</li>
<li aria-level="1">email</li>
<li aria-level="1">request_id</li>
<li aria-level="1">order_id</li>
</ul>
<p>You see where this is going? Yeah, cardinality. Cardinality tends to kill storage, makes certain UI elements unusable (think dropdowns with 1000+ values – fun!), etc.</p>
<p><span style="font-weight: 400;">See </span><a href="https://sematext.com/blog/opentelemetry-production-monitoring-what-breaks-and-how-to-prevent-it/#the-first-production-surprise-cardinality-explosions" target="_blank" rel="noopener"><span style="font-weight: 400;">The First Production Surprise: Cardinality Explosions</span></a><span style="font-weight: 400;"> for more details on cardinality problems in OpenTelemetry.</span></p>
<h2 id="traces-how-we-debugged-slow-requests"><b>Traces: How We Debugged Slow Requests</b></h2>
<p>When it comes to traces you might think that they are like logs and you want to have them all so you can really dig in when you need to troubleshoot. However, for us at least, traces became useful only after we stopped trying to store all of them.</p>
<p>At first, we sampled at 100%. Meaning we didn’t sample at all.<br>
Then we realized how much that was going to cost us.<br>
Then we went for the other extreme and sampled at 1%.<br>
But then we missed the interesting traces.</p>
<p>What finally worked was <a href="https://opentelemetry.io/blog/2022/tail-sampling/" target="_blank" rel="noopener noreferrer"><b>tail-based sampling</b></a>:<br>
We decide after the trace finishes whether it’s worth keeping.</p>
<p>Earlier, I mentioned a collector acting as a filter that decides what survives. This is a perfect example of that. Here’s the collector config for sampling.</p>
<h3 id="tail-sampling-config"><b>Tail Sampling Config</b></h3>
<pre>processors:
  tail_sampling:
    policies:
      - name: errors
        type: status_code
        status_code:
          status_codes: [ERROR]

      - name: slow
        type: latency
        latency:
          threshold_ms: 500
</pre>
<p> </p>
<p>So now what we have does this:</p>
<ul>
<li aria-level="1">slow requests survive</li>
<li aria-level="1">failed requests survive</li>
<li aria-level="1">boring 200ms health checks die</li>
</ul>
<p>This changed traces from “expensive noise” into “high-signal debugging data.”</p>
<p>We also learned to be careful with attributes.<br>
Anything that explodes into millions of values makes sampling useless.</p>
<p><span style="font-weight: 400;">For more details on sampling strategies, see our article </span><a href="https://sematext.com/blog/opentelemetry-production-monitoring-what-breaks-and-how-to-prevent-it/#sampling-strategies-for-opentelemetry-in-production" target="_blank" rel="noopener"><span style="font-weight: 400;">The First Production Surprise: Cardinality Explosions</span></a><span style="font-weight: 400;">.</span></p>
<h2 id="logs-the-last-mile-of-debugging"><b>Logs: The Last Mile of Debugging</b></h2>
<p>We still rely on logs like we relied on them before, except with tracing in place oftentimes logs are what we read after traces tell us “this DB call is slow” and we need to know why beyond what we can see through traces themselves.</p>
<p>So the big change – the key – for us was <b>correlating logs with traces</b>.</p>
<p>Here’s how we do it with Python. You’d do something like this in any language. Note how we get the trace_id and span_id from the context and include it in the log event.</p>
<h3 id="python-logging-with-trace-context"><b>Python Logging with Trace Context</b></h3>
<pre>from opentelemetry.trace import get_current_span
import logging

logger = logging.getLogger(__name__)

span = get_current_span()
ctx = span.get_span_context()

logger.error(
    "payment failed",
    extra={
        "trace_id": format(ctx.trace_id, "x"),
        "span_id": format(ctx.span_id, "x"),
        "order_id": 1234
    }
)
</pre>
<p> </p>
<p>Once we did this, debugging became a flow instead of a search:</p>
<p><img decoding="async" class="alignnone wp-image-70500" src="https://sematext.com/wp-content/uploads/2026/02/flow-metrics-traces-logs-300x153.png" alt="" width="839" height="428" srcset="https://sematext.com/wp-content/uploads/2026/02/flow-metrics-traces-logs-300x153.png 300w, https://sematext.com/wp-content/uploads/2026/02/flow-metrics-traces-logs-1024x521.png 1024w, https://sematext.com/wp-content/uploads/2026/02/flow-metrics-traces-logs-768x391.png 768w, https://sematext.com/wp-content/uploads/2026/02/flow-metrics-traces-logs.png 1528w" sizes="(max-width: 839px) 100vw, 839px" /></p>
<p> </p>
<p>Alert → metric → trace → log.<br>
That’s the loop we optimized for.</p>
<h2 id="the-collector-is-where-strategy-lives"><b>The Collector Is Where Strategy Lives</b></h2>
<p>Here’s a simplified version of the <a href="https://opentelemetry.io/docs/collector/configuration/" target="_blank" rel="noopener noreferrer">collector config</a> we ended up with:</p>
<pre>receivers:
  otlp:
    protocols:
      grpc:
      http:

processors:
  memory_limiter:
    limit_mib: 400
  batch:
  tail_sampling:
    policies:
      - name: errors
        type: status_code
        status_code:
          status_codes: [ERROR]
      - name: slow
        type: latency
        latency:
          threshold_ms: 500

exporters:
  otlp:
    endpoint: backend:4317

service:
  pipelines:
    traces:
      receivers: [otlp]
      processors: [memory_limiter, tail_sampling, batch]
      exporters: [otlp]

    metrics:
      receivers: [otlp]
      processors: [batch]
      exporters: [otlp]

    logs:
      receivers: [otlp]
      processors: [batch]
      exporters: [otlp]
</pre>
<p>This let us:</p>
<ul>
<li aria-level="1">tune sampling without redeploying apps</li>
<li aria-level="1">cap memory</li>
<li aria-level="1">drop junk centrally</li>
</ul>
<h2 id="what-scaling-telemetry-really-means"><b>What Scaling Telemetry Really Means</b></h2>
<p>When people say “scaling OpenTelemetry,” they usually mean handling “more traffic/observability data.”</p>
<p>Based on our experience, though, what we actually hit first was:</p>
<ul>
<li aria-level="1">cardinality</li>
<li aria-level="1">storage</li>
<li aria-level="1">query performance</li>
<li aria-level="1">human attention</li>
</ul>
<p>And thus, what scaling really meant for us in this context was:</p>
<ul>
<li aria-level="1">having fewer but better metrics</li>
<li aria-level="1">having fewer but selectively chosen traces</li>
<li aria-level="1">well structured logs that we can not just search but really slice and dice</li>
</ul>
<h2 id="what-id-do-again-and-what-i-wouldnt"><b>What I’d Do Again (and What I Wouldn’t)</b></h2>
<table>
<tbody>
<tr>
<td><b>Decision</b></td>
<td><b>Result</b></td>
</tr>
<tr>
<td>Tail-sample traces</td>
<td>Saved money and sanity</td>
</tr>
<tr>
<td>Golden signal metrics only</td>
<td>Stable dashboards</td>
</tr>
<tr>
<td>Correlate logs with traces</td>
<td>Faster debugging</td>
</tr>
<tr>
<td>Put strategy in collector</td>
<td>Central control</td>
</tr>
<tr>
<td>Let teams emit anything</td>
<td>Mistake (at first)</td>
</tr>
</tbody>
</table>
<p> </p>
<h2 id="the-gist"><b>The Gist</b></h2>
<p>OpenTelemetry is neither an observability <i>strategy</i> or <i>solution</i>. It’s a transport, a spec, an implementation in the form of SDKs. It’s just a tool. And one capable of drowning you in your own telemetry.</p>
<p>The strategy is being smart about how you set it up and how you use it. I strongly suggest counting on needing to spend some time on this. It pays off in the long run. Questions to answer:</p>
<ul>
<li aria-level="1">what questions you want answered</li>
<li aria-level="1">what data you’re willing to pay for</li>
<li aria-level="1">what engineers will actually use</li>
</ul>
<p>Metrics tell me when things break.<br>
Traces tell me where they break.<br>
Logs tell me why they break.</p>
<p>Everything else …….send to <a href="https://www.geeksforgeeks.org/linux-unix/what-is-dev-null-in-linux/" target="_blank" rel="noopener noreferrer">/dev/null</a>?</p>
<p class="space-top"><a href="https://apps.sematext.com/ui/registration" class="button-big" target="_blank" rel="noopener noreferrer">Start Free Trial</a></p><hr class="hidden"><p>The post <a href="https://sematext.com/blog/opentelemetry-production-design/">OpenTelemetry in Production: Design for Order, High Signal, Low Noise, and Survival</a> appeared first on <a href="https://sematext.com">Sematext</a>.</p>
]]></content:encoded>
					
		
		
			</item>
	</channel>
</rss>
