<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Metrics-generator on Grafana Labs</title><link>https://grafana.com/docs/tempo/v3.0.x/metrics-from-traces/metrics-generator/</link><description>Recent content in Metrics-generator on Grafana Labs</description><generator>Hugo -- gohugo.io</generator><language>en</language><atom:link href="/docs/tempo/v3.0.x/metrics-from-traces/metrics-generator/index.xml" rel="self" type="application/rss+xml"/><item><title>Active series</title><link>https://grafana.com/docs/tempo/v3.0.x/metrics-from-traces/metrics-generator/active-series/</link><pubDate>Thu, 28 May 2026 17:50:33 +0100</pubDate><guid>https://grafana.com/docs/tempo/v3.0.x/metrics-from-traces/metrics-generator/active-series/</guid><content><![CDATA[&lt;h1 id=&#34;active-series&#34;&gt;Active series&lt;/h1&gt;
&lt;p&gt;An active series is a time series that receives new data points or samples. When you stop writing new data points to a time series, shortly afterwards it&amp;rsquo;s no longer considered active.&lt;/p&gt;
&lt;p&gt;Metrics generated by the metrics-generator can provide both RED (Rate/Error/Duration) metrics and interdependency graphs between services in a trace (the Service Graph functionality in Grafana).
These capabilities rely on a set of generated span metrics and service metrics.&lt;/p&gt;
&lt;p&gt;Any spans that are ingested by Tempo can create many metric series. However, this doesn&amp;rsquo;t mean that every time a span is ingested that a new active series is created.&lt;/p&gt;
&lt;p&gt;The number of active series generated depends on the label pairs generated from span data that are associated with the metrics, similar to other Prometheus-formatted data.&lt;/p&gt;
&lt;p&gt;For additional information, refer to the &lt;a href=&#34;/docs/grafana-cloud/cost-management-and-billing/understand-your-invoice/metrics-invoice/&#34;&gt;Active series and DPM documentation&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&#34;active-series-calculation&#34;&gt;Active series calculation&lt;/h2&gt;
&lt;p&gt;Active series for a metric increase when a new value for a label key is introduced. For example, the &lt;code&gt;span_kind&lt;/code&gt; label has a total of five possible values, and the &lt;code&gt;status_code&lt;/code&gt; label has a total of three possible values.&lt;/p&gt;
&lt;p&gt;At first glance, you might make an assumption that this means that at least 15 (5*3) active series will be generated for each span. But this isn&amp;rsquo;t the case.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s consider a span that&amp;rsquo;s emitted from some piece of code in a service:&lt;/p&gt;
&lt;p&gt;&lt;img
  class=&#34;lazyload d-inline-block&#34;
  data-src=&#34;/static/img/docs/tempo/SingleSpan.jpeg&#34;
  alt=&#34;Single span visualization&#34; width=&#34;371&#34;
     height=&#34;350&#34;/&gt;&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s a single service with a single span.
If the code inside the span never leaves the service, then the &lt;code&gt;span_kind&lt;/code&gt; label generated by the metrics generator will be &lt;code&gt;SPAN_KIND_INTERNAL&lt;/code&gt; and never deviate. It&amp;rsquo;ll never be one of the other four possible values.&lt;/p&gt;
&lt;p&gt;Similarly, if the code inside the span never errors, it&amp;rsquo;ll only have the &lt;code&gt;STATUS_CODE_OK&lt;/code&gt; state for the &lt;code&gt;span_status&lt;/code&gt; label.
This means that the metrics generator will only generate a single active series, where the service name will be &lt;em&gt;Service 1&lt;/em&gt; and the span name will be &lt;em&gt;span1&lt;/em&gt;.
If we looked at the Prometheus data for the &lt;code&gt;traces_spanmetrics_call_total&lt;/code&gt; metric, we&amp;rsquo;d see a single active series:&lt;/p&gt;
&lt;section class=&#34;expand-table-wrapper&#34;&gt;&lt;div class=&#34;button-div&#34;&gt;
      &lt;button class=&#34;expand-table-btn&#34;&gt;Expand table&lt;/button&gt;
    &lt;/div&gt;&lt;div class=&#34;responsive-table-wrapper&#34;&gt;
    &lt;table&gt;
      &lt;thead&gt;
          &lt;tr&gt;
              &lt;th&gt;service&lt;/th&gt;
              &lt;th&gt;span_name&lt;/th&gt;
              &lt;th&gt;span_kind&lt;/th&gt;
              &lt;th&gt;status_code&lt;/th&gt;
              &lt;th&gt;Metric value&lt;/th&gt;
          &lt;/tr&gt;
      &lt;/thead&gt;
      &lt;tbody&gt;
          &lt;tr&gt;
              &lt;td&gt;Service 1&lt;/td&gt;
              &lt;td&gt;span1&lt;/td&gt;
              &lt;td&gt;SPAN_KIND_INTERNAL&lt;/td&gt;
              &lt;td&gt;STATUS_CODE_OK&lt;/td&gt;
              &lt;td&gt;1&lt;/td&gt;
          &lt;/tr&gt;
      &lt;/tbody&gt;
    &lt;/table&gt;
  &lt;/div&gt;
&lt;/section&gt;&lt;p&gt;It doesn&amp;rsquo;t matter how many times that span occurs in a trace either, for example maybe a span is generated within a loop.
In code run once, 10 times, 100 times, 1000 times, only a single active series will be produced, where a counter might be increased 1, 10, 100, or 1000 times:&lt;/p&gt;
&lt;p&gt;&lt;img
  class=&#34;lazyload d-inline-block&#34;
  data-src=&#34;/static/img/docs/tempo/SingleSpanLoop.jpeg&#34;
  alt=&#34;Single span with loop&#34; width=&#34;371&#34;
     height=&#34;350&#34;/&gt;&lt;/p&gt;
&lt;p&gt;If you looked at the Prometheus data, you&amp;rsquo;d see an instant value for &lt;code&gt;traces_spanmetrics_call_total&lt;/code&gt; similar to the table. Again, one active series for the metric:&lt;/p&gt;
&lt;section class=&#34;expand-table-wrapper&#34;&gt;&lt;div class=&#34;button-div&#34;&gt;
      &lt;button class=&#34;expand-table-btn&#34;&gt;Expand table&lt;/button&gt;
    &lt;/div&gt;&lt;div class=&#34;responsive-table-wrapper&#34;&gt;
    &lt;table&gt;
      &lt;thead&gt;
          &lt;tr&gt;
              &lt;th&gt;service&lt;/th&gt;
              &lt;th&gt;span_name&lt;/th&gt;
              &lt;th&gt;span_kind&lt;/th&gt;
              &lt;th&gt;status_code&lt;/th&gt;
              &lt;th&gt;Metric value&lt;/th&gt;
          &lt;/tr&gt;
      &lt;/thead&gt;
      &lt;tbody&gt;
          &lt;tr&gt;
              &lt;td&gt;Service 1&lt;/td&gt;
              &lt;td&gt;span1&lt;/td&gt;
              &lt;td&gt;SPAN_KIND_INTERNAL&lt;/td&gt;
              &lt;td&gt;STATUS_CODE_OK&lt;/td&gt;
              &lt;td&gt;120&lt;/td&gt;
          &lt;/tr&gt;
      &lt;/tbody&gt;
    &lt;/table&gt;
  &lt;/div&gt;
&lt;/section&gt;&lt;p&gt;However, let&amp;rsquo;s now assume that it does loop and there are occasionally errors.&lt;/p&gt;
&lt;p&gt;&lt;img
  class=&#34;lazyload d-inline-block&#34;
  data-src=&#34;/static/img/docs/tempo/SinglespanLoopError.jpeg&#34;
  alt=&#34;Single span with loop and errors&#34; width=&#34;371&#34;
     height=&#34;350&#34;/&gt;&lt;/p&gt;
&lt;p&gt;There are now two potential outcomes for a span when the code loops: one where everything successfully completes and one where there is an error.
This means that when the span completes &lt;code&gt;status_code&lt;/code&gt; is now either &lt;code&gt;STATUS_CODE_OK&lt;/code&gt; or &lt;code&gt;STATUS_CODE_ERROR&lt;/code&gt;.
Because of that, the label values can be one of two values on a metric, and we now have two active series being generated based on the &lt;code&gt;status_code&lt;/code&gt;, one for the &lt;code&gt;OK&lt;/code&gt; status and one for the error.&lt;/p&gt;
&lt;p&gt;Again, we could loop once, 10 times, 100, or more times, but there will only ever be two active series.&lt;/p&gt;
&lt;p&gt;If we now looked at Prometheus instant values for &lt;code&gt;traces_spanmetrics_call_total&lt;/code&gt;, we&amp;rsquo;d now see the following table:&lt;/p&gt;
&lt;section class=&#34;expand-table-wrapper&#34;&gt;&lt;div class=&#34;button-div&#34;&gt;
      &lt;button class=&#34;expand-table-btn&#34;&gt;Expand table&lt;/button&gt;
    &lt;/div&gt;&lt;div class=&#34;responsive-table-wrapper&#34;&gt;
    &lt;table&gt;
      &lt;thead&gt;
          &lt;tr&gt;
              &lt;th&gt;service&lt;/th&gt;
              &lt;th&gt;span_name&lt;/th&gt;
              &lt;th&gt;span_kind&lt;/th&gt;
              &lt;th&gt;status_code&lt;/th&gt;
              &lt;th&gt;Metric value&lt;/th&gt;
          &lt;/tr&gt;
      &lt;/thead&gt;
      &lt;tbody&gt;
          &lt;tr&gt;
              &lt;td&gt;Service 1&lt;/td&gt;
              &lt;td&gt;span1&lt;/td&gt;
              &lt;td&gt;SPAN_KIND_INTERNAL&lt;/td&gt;
              &lt;td&gt;STATUS_CODE_OK&lt;/td&gt;
              &lt;td&gt;96&lt;/td&gt;
          &lt;/tr&gt;
          &lt;tr&gt;
              &lt;td&gt;Service 1&lt;/td&gt;
              &lt;td&gt;span1&lt;/td&gt;
              &lt;td&gt;SPAN_KIND_INTERNAL&lt;/td&gt;
              &lt;td&gt;STATUS_CODE_ERROR&lt;/td&gt;
              &lt;td&gt;24&lt;/td&gt;
          &lt;/tr&gt;
      &lt;/tbody&gt;
    &lt;/table&gt;
  &lt;/div&gt;
&lt;/section&gt;&lt;p&gt;What happens if you call out to another service though? Let&amp;rsquo;s add an option where, based on some arbitrary data, we sometimes make a downstream call to another service, but otherwise continue to runs loops in our own service:&lt;/p&gt;
&lt;p&gt;&lt;img
  class=&#34;lazyload d-inline-block&#34;
  data-src=&#34;/static/img/docs/tempo/SingleSpanLoopErrorAnotherService.jpeg&#34;
  alt=&#34;Multiple spans with loops and errors&#34; width=&#34;720&#34;
     height=&#34;303&#34;/&gt;&lt;/p&gt;
&lt;p&gt;In this scenario, &lt;code&gt;span1&lt;/code&gt;&amp;rsquo;s &lt;code&gt;span_kind&lt;/code&gt; label would now be one of either &lt;code&gt;SPAN_KIND_INTERNAL&lt;/code&gt; or &lt;code&gt;SPAN_KIND_CLIENT&lt;/code&gt; (as it has acted as a client calling a downstream server).
If a call to the downstream service could also potentially fail, then for &lt;code&gt;SPAN_KIND_CLIENT&lt;/code&gt;, the &lt;code&gt;status_code&lt;/code&gt; could be either &lt;code&gt;STATUS_CODE_ERROR&lt;/code&gt; or &lt;code&gt;STATUS_CODE_OK&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;At this point, &lt;code&gt;traces_spanmetrics_call_total&lt;/code&gt; would have four different variations in labels:&lt;/p&gt;
&lt;section class=&#34;expand-table-wrapper&#34;&gt;&lt;div class=&#34;button-div&#34;&gt;
      &lt;button class=&#34;expand-table-btn&#34;&gt;Expand table&lt;/button&gt;
    &lt;/div&gt;&lt;div class=&#34;responsive-table-wrapper&#34;&gt;
    &lt;table&gt;
      &lt;thead&gt;
          &lt;tr&gt;
              &lt;th&gt;service&lt;/th&gt;
              &lt;th&gt;span_name&lt;/th&gt;
              &lt;th&gt;span_kind&lt;/th&gt;
              &lt;th&gt;status_code&lt;/th&gt;
              &lt;th&gt;Metric value&lt;/th&gt;
          &lt;/tr&gt;
      &lt;/thead&gt;
      &lt;tbody&gt;
          &lt;tr&gt;
              &lt;td&gt;Service 1&lt;/td&gt;
              &lt;td&gt;span1&lt;/td&gt;
              &lt;td&gt;SPAN_KIND_INTERNAL&lt;/td&gt;
              &lt;td&gt;STATUS_CODE_OK&lt;/td&gt;
              &lt;td&gt;34&lt;/td&gt;
          &lt;/tr&gt;
          &lt;tr&gt;
              &lt;td&gt;Service 1&lt;/td&gt;
              &lt;td&gt;span1&lt;/td&gt;
              &lt;td&gt;SPAN_KIND_INTERNAL&lt;/td&gt;
              &lt;td&gt;STATUS_CODE_ERROR&lt;/td&gt;
              &lt;td&gt;6&lt;/td&gt;
          &lt;/tr&gt;
          &lt;tr&gt;
              &lt;td&gt;Service 1&lt;/td&gt;
              &lt;td&gt;span1&lt;/td&gt;
              &lt;td&gt;SPAN_KIND_CLIENT&lt;/td&gt;
              &lt;td&gt;STATUS_CODE_OK&lt;/td&gt;
              &lt;td&gt;23&lt;/td&gt;
          &lt;/tr&gt;
          &lt;tr&gt;
              &lt;td&gt;Service 1&lt;/td&gt;
              &lt;td&gt;span1&lt;/td&gt;
              &lt;td&gt;SPAN_KIND_CLIENT&lt;/td&gt;
              &lt;td&gt;STATUS_CODE_ERROR&lt;/td&gt;
              &lt;td&gt;3&lt;/td&gt;
          &lt;/tr&gt;
      &lt;/tbody&gt;
    &lt;/table&gt;
  &lt;/div&gt;
&lt;/section&gt;&lt;p&gt;Because of the variation in values, we now have four active series for our metric instead of one. But, as far as Service 1 is concerned, there&amp;rsquo;s still only four active series, because there isn&amp;rsquo;t any other variation of the values for labels. You can run 1 trace, 10 traces, 100 traces (each with however many loops of spans there are) and only four active series will ever be produced.&lt;/p&gt;
&lt;p&gt;We&amp;rsquo;ve actually only told half the story in our last diagram. &lt;em&gt;Service 1&lt;/em&gt; called a second service, &lt;em&gt;Service 2&lt;/em&gt;, which continues the trace by adding a new span, &lt;code&gt;span2&lt;/code&gt;.
If there was a loop inside Service 2 with a single span that was generated from an upstream call from Service 1, and then a number of spans that were driven internally, which could also error, we&amp;rsquo;d end up with the possible values in the metric for &lt;code&gt;traces_spanmetrics_call_total&lt;/code&gt; below:&lt;/p&gt;
&lt;section class=&#34;expand-table-wrapper&#34;&gt;&lt;div class=&#34;button-div&#34;&gt;
      &lt;button class=&#34;expand-table-btn&#34;&gt;Expand table&lt;/button&gt;
    &lt;/div&gt;&lt;div class=&#34;responsive-table-wrapper&#34;&gt;
    &lt;table&gt;
      &lt;thead&gt;
          &lt;tr&gt;
              &lt;th&gt;service&lt;/th&gt;
              &lt;th&gt;span_name&lt;/th&gt;
              &lt;th&gt;span_kind&lt;/th&gt;
              &lt;th&gt;status_code&lt;/th&gt;
              &lt;th&gt;Metric value&lt;/th&gt;
          &lt;/tr&gt;
      &lt;/thead&gt;
      &lt;tbody&gt;
          &lt;tr&gt;
              &lt;td&gt;Service 1&lt;/td&gt;
              &lt;td&gt;span1&lt;/td&gt;
              &lt;td&gt;SPAN_KIND_INTERNAL&lt;/td&gt;
              &lt;td&gt;STATUS_CODE_OK&lt;/td&gt;
              &lt;td&gt;89&lt;/td&gt;
          &lt;/tr&gt;
          &lt;tr&gt;
              &lt;td&gt;Service 1&lt;/td&gt;
              &lt;td&gt;span1&lt;/td&gt;
              &lt;td&gt;SPAN_KIND_INTERNAL&lt;/td&gt;
              &lt;td&gt;STATUS_CODE_ERROR&lt;/td&gt;
              &lt;td&gt;13&lt;/td&gt;
          &lt;/tr&gt;
          &lt;tr&gt;
              &lt;td&gt;Service 1&lt;/td&gt;
              &lt;td&gt;span1&lt;/td&gt;
              &lt;td&gt;SPAN_KIND_CLIENT&lt;/td&gt;
              &lt;td&gt;STATUS_CODE_OK&lt;/td&gt;
              &lt;td&gt;44&lt;/td&gt;
          &lt;/tr&gt;
          &lt;tr&gt;
              &lt;td&gt;Service 1&lt;/td&gt;
              &lt;td&gt;span1&lt;/td&gt;
              &lt;td&gt;SPAN_KIND_CLIENT&lt;/td&gt;
              &lt;td&gt;STATUS_CODE_ERROR&lt;/td&gt;
              &lt;td&gt;9&lt;/td&gt;
          &lt;/tr&gt;
          &lt;tr&gt;
              &lt;td&gt;Service 2&lt;/td&gt;
              &lt;td&gt;span2&lt;/td&gt;
              &lt;td&gt;SPAN_KIND_SERVER&lt;/td&gt;
              &lt;td&gt;STATUS_CODE_OK&lt;/td&gt;
              &lt;td&gt;30&lt;/td&gt;
          &lt;/tr&gt;
          &lt;tr&gt;
              &lt;td&gt;Service 2&lt;/td&gt;
              &lt;td&gt;span2&lt;/td&gt;
              &lt;td&gt;SPAN_KIND_SERVER&lt;/td&gt;
              &lt;td&gt;STATUS_CODE_ERROR&lt;/td&gt;
              &lt;td&gt;14&lt;/td&gt;
          &lt;/tr&gt;
          &lt;tr&gt;
              &lt;td&gt;Service 2&lt;/td&gt;
              &lt;td&gt;span2&lt;/td&gt;
              &lt;td&gt;SPAN_KIND_INTERNAL&lt;/td&gt;
              &lt;td&gt;STATUS_CODE_OK&lt;/td&gt;
              &lt;td&gt;99&lt;/td&gt;
          &lt;/tr&gt;
          &lt;tr&gt;
              &lt;td&gt;Service 2&lt;/td&gt;
              &lt;td&gt;span2&lt;/td&gt;
              &lt;td&gt;SPAN_KIND_INTERNAL&lt;/td&gt;
              &lt;td&gt;STATUS_CODE_ERROR&lt;/td&gt;
              &lt;td&gt;23&lt;/td&gt;
          &lt;/tr&gt;
      &lt;/tbody&gt;
    &lt;/table&gt;
  &lt;/div&gt;
&lt;/section&gt;&lt;p&gt;At this point, all our traces will be composed of two potential span names, each of which produce two separate types of &lt;code&gt;span_kind&lt;/code&gt; and two separate types of &lt;code&gt;status_code&lt;/code&gt;. So we have eight active series for a metric.&lt;/p&gt;
&lt;p&gt;The variability of values for each potential span condition determines the number of active series being produced by Tempo when ingesting spans for a trace, and not the number of traces of spans that are seen.&lt;/p&gt;
&lt;h2 id=&#34;custom-span-attributes&#34;&gt;Custom span attributes&lt;/h2&gt;
&lt;p&gt;There&amp;rsquo;s another consideration for active series: extra label key/value pairs that can be added onto metrics from a span&amp;rsquo;s attributes.
The Tempo metrics generator allows the user to use arbitrary span attributes to be created as label pairs for metrics.
When considering the number of active series generated, you also need to determine how many possible values there are for the span attribute being turned into a label.&lt;/p&gt;
&lt;p&gt;For example, if you added an &lt;code&gt;http.method&lt;/code&gt; span attribute into a metric label pair, there are five possible values (because there are five possible REST methods):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;HEAD&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;GET&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;POST&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;PUT&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;DELETE&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If this label pair is added to every span metric, that&amp;rsquo;s another 5 &lt;em&gt;potential&lt;/em&gt; active series generated for each metric (in all likelihood this is a very worst case scenario, very few spans will call all five REST methods).
Instead of 8 active series in the last table above, we&amp;rsquo;d have 40 (8 * 5).&lt;/p&gt;
]]></content><description>&lt;h1 id="active-series">Active series&lt;/h1>
&lt;p>An active series is a time series that receives new data points or samples. When you stop writing new data points to a time series, shortly afterwards it&amp;rsquo;s no longer considered active.&lt;/p></description></item><item><title>Cardinality</title><link>https://grafana.com/docs/tempo/v3.0.x/metrics-from-traces/metrics-generator/cardinality/</link><pubDate>Thu, 28 May 2026 17:50:33 +0100</pubDate><guid>https://grafana.com/docs/tempo/v3.0.x/metrics-from-traces/metrics-generator/cardinality/</guid><content><![CDATA[&lt;h1 id=&#34;cardinality&#34;&gt;Cardinality&lt;/h1&gt;
&lt;p&gt;Cardinality refers to the total combination of key/value pairs, such as labels and label values for a given metric series or log stream, and how many unique combinations they generate.
For more information on cardinality, refer to the &lt;a href=&#34;/blog/2022/02/15/what-are-cardinality-spikes-and-why-do-they-matter/&#34;&gt;What are cardinality spikes and why do they matter?&lt;/a&gt; blog post.&lt;/p&gt;
&lt;p&gt;Because writes to a time-series database (TSDB) database are in series, high cardinality doesn&amp;rsquo;t make a big difference to performance at ingest.
However, cardinality can have a major impact on querying where, the higher the cardinality, the more items are required to be iterated over.&lt;/p&gt;
&lt;h2 id=&#34;traces-collection-and-metrics&#34;&gt;Traces collection and metrics&lt;/h2&gt;
&lt;p&gt;Tempo’s server-side metrics generation adds functionality to the collection of traces by creating Prometheus-based metrics that track a variety of metrics such as:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Total span call counts&lt;/li&gt;
&lt;li&gt;Span latency histograms&lt;/li&gt;
&lt;li&gt;Total span size count&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The metrics-generator creates metrics which define the relationship between services via edges and nodes.
Each of these metrics are queryable using a set of Prometheus labels (key/value pairs).&lt;/p&gt;
&lt;p&gt;Each new value for a label increases the number of active series associated with a metric. (To learn more about active series, refer to the &lt;a href=&#34;../active-series/&#34;&gt;Trace active series&lt;/a&gt; documentation.)&lt;/p&gt;
&lt;p&gt;This is also known as an increase in cardinality, and the number of active series generated for a metric is directly proportional to the number of labels that exist for that metrics alongside the number of values each label has added.&lt;/p&gt;
&lt;p&gt;In a non-modified instance of the metrics generator, a small number of labels are added automatically.
Because labels like &lt;code&gt;span_kind&lt;/code&gt; and &lt;code&gt;status_code&lt;/code&gt; only have a few valid values, the largest variable for the number of active series produced for each metric depends on the number of service names and span names associated with trace spans.&lt;/p&gt;
&lt;p&gt;The metrics-generator can also be configured to also add extra labels on metrics, using span attribute key/value pairs which are mapped directly to these labels. Refer to the 
    &lt;a href=&#34;/docs/tempo/v3.0.x/configuration/#metrics-generator&#34;&gt;custom span attribute documentation&lt;/a&gt; for more information.&lt;/p&gt;
&lt;p&gt;Be careful when configuring custom attributes: the greater the number of values seen in a specific attribute, the greater the number of active series are produced. For more information about active series, refer to the &lt;a href=&#34;../active-series/&#34;&gt;active series documentation&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s say that you are adding a custom attribute that includes unique customer IDs as a metrics label. If you have 100 customers, this could potentially multiply the number of active series generated by up to 100 (for example, going from 25,000 active series to 2.5M).
Always consider which attributes are useful as labels for querying metrics, as well as the cardinality that they can increase metrics by.&lt;/p&gt;
&lt;h2 id=&#34;dry-running-the-metrics-generator&#34;&gt;Dry-running the metrics-generator&lt;/h2&gt;
&lt;p&gt;A good practice, before fully enabling metrics-generator, is to run the metrics-generator in dry-run mode.
Using the dry-run mode generates metrics but doesn&amp;rsquo;t collect them, and thus doesn&amp;rsquo;t write them to a metrics storage database.
The override &lt;code&gt;metrics_generator.disable_collection&lt;/code&gt; is defined for this use-case.&lt;/p&gt;
&lt;p&gt;To get an estimate, set the override to &lt;code&gt;true&lt;/code&gt;, and run the metrics-generator as you would normally.
Then, check &lt;code&gt;tempo_metrics_generator_registry_active_series&lt;/code&gt; for the calculated estimation of active series for your set-up.&lt;/p&gt;
&lt;p&gt;If your active series limit is already reached and &lt;code&gt;tempo_metrics_generator_registry_active_series&lt;/code&gt; no longer reflects true demand, use the &lt;code&gt;tempo_metrics_generator_registry_active_series_demand_estimate&lt;/code&gt; metric instead. This metric uses HyperLogLog estimation to approximate the actual cardinality even when limits are in effect.&lt;/p&gt;
&lt;h2 id=&#34;reduce-cardinality-with-span-name-sanitization&#34;&gt;Reduce cardinality with span name sanitization&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;span_name&lt;/code&gt; label is one of the largest contributors to high cardinality in generated metrics. Applications that include dynamic values in span names, for example, &lt;code&gt;GET /users/123&lt;/code&gt; or &lt;code&gt;query-abc-def-ghi&lt;/code&gt;, create a unique series for every distinct path or identifier.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;span_name_sanitization&lt;/code&gt; option uses the DRAIN algorithm to automatically group similar span names and replace variable segments with a placeholder. For example, &lt;code&gt;GET /users/123&lt;/code&gt; and &lt;code&gt;GET /users/456&lt;/code&gt; are both mapped to &lt;code&gt;GET /users/&amp;lt;_&amp;gt;&lt;/code&gt;, which reduces them to a single series.&lt;/p&gt;
&lt;p&gt;To configure span name sanitization, refer to &lt;a href=&#34;../reduce-cardinality/&#34;&gt;Reduce cardinality with span name sanitization&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&#34;manage-cardinality-with-limits&#34;&gt;Manage cardinality with limits&lt;/h2&gt;
&lt;p&gt;For troubleshooting and managing cardinality limits at runtime, refer to the following sections in the 
    &lt;a href=&#34;/docs/tempo/v3.0.x/troubleshooting/metrics-generator/&#34;&gt;metrics-generator troubleshooting&lt;/a&gt; documentation:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
    &lt;a href=&#34;/docs/tempo/v3.0.x/troubleshooting/metrics-generator/#max-active-series&#34;&gt;Max active series&lt;/a&gt; to detect and configure the active series limit&lt;/li&gt;
&lt;li&gt;
    &lt;a href=&#34;/docs/tempo/v3.0.x/troubleshooting/metrics-generator/#entity-based-limiting&#34;&gt;Entity-based limiting&lt;/a&gt; to limit by unique label combinations instead of individual series&lt;/li&gt;
&lt;li&gt;
    &lt;a href=&#34;/docs/tempo/v3.0.x/troubleshooting/metrics-generator/#per-label-cardinality-limiting&#34;&gt;Per-label cardinality limiting&lt;/a&gt; to limit cardinality (distinct values) per label&lt;/li&gt;
&lt;/ul&gt;
]]></content><description>&lt;h1 id="cardinality">Cardinality&lt;/h1>
&lt;p>Cardinality refers to the total combination of key/value pairs, such as labels and label values for a given metric series or log stream, and how many unique combinations they generate.
For more information on cardinality, refer to the &lt;a href="/blog/2022/02/15/what-are-cardinality-spikes-and-why-do-they-matter/">What are cardinality spikes and why do they matter?&lt;/a> blog post.&lt;/p></description></item><item><title>Estimate cardinality from traces</title><link>https://grafana.com/docs/tempo/v3.0.x/metrics-from-traces/metrics-generator/estimate-cardinality/</link><pubDate>Thu, 28 May 2026 17:50:33 +0100</pubDate><guid>https://grafana.com/docs/tempo/v3.0.x/metrics-from-traces/metrics-generator/estimate-cardinality/</guid><content><![CDATA[&lt;h1 id=&#34;estimate-cardinality-from-traces&#34;&gt;Estimate cardinality from traces&lt;/h1&gt;
&lt;p&gt;Cardinality can pose a problem when you have lots of services.
There isn&amp;rsquo;t a direct formula or solution to this issue.
The following guide should help estimate the cardinality that a feature generates.&lt;/p&gt;
&lt;p&gt;For more information on cardinality, refer to the &lt;a href=&#34;../cardinality/&#34;&gt;Cardinality&lt;/a&gt; documentation.&lt;/p&gt;
&lt;h3 id=&#34;how-to-estimate-the-cardinality&#34;&gt;How to estimate the cardinality&lt;/h3&gt;
&lt;p&gt;The amount of edges depends on the number of nodes in the system and the direction of the requests between them.
Let’s call this amount hops. Every hop is a unique combination of client &#43; server labels.&lt;/p&gt;
&lt;p&gt;For example:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A system with 3 nodes &lt;code&gt;(A, B, C)&lt;/code&gt; of which &lt;code&gt;A&lt;/code&gt; only calls &lt;code&gt;B&lt;/code&gt; and &lt;code&gt;B&lt;/code&gt; only calls &lt;code&gt;C&lt;/code&gt; will have 2 hops &lt;code&gt;(A → B, B → C)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;A system with 3 nodes &lt;code&gt;(A, B, C)&lt;/code&gt; that call each other (that is, all bidirectional links) will have 6 hops &lt;code&gt;(A → B, B → A, B → C, C → B, A → C, C → A)&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You can’t calculate the amount of hops automatically based upon the nodes,
but it should be a value between &lt;code&gt;#services - 1&lt;/code&gt; and &lt;code&gt;#services!&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;If you know the amount of hops in a system, you can calculate the cardinality of the generated
service graphs (assuming &lt;code&gt;#hb&lt;/code&gt; is the number of histogram buckets):&lt;/p&gt;

&lt;div class=&#34;code-snippet code-snippet__mini&#34;&gt;&lt;div class=&#34;lang-toolbar__mini&#34;&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet code-snippet__border&#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-none&#34;&gt;  traces_service_graph_request_total: #hops
  traces_service_graph_request_failed_total: #hops
  traces_service_graph_request_server_seconds: #hb * #hops
  traces_service_graph_request_client_seconds: #hb * #hops
  traces_service_graph_unpaired_spans_total: #services (absolute worst case)
  traces_service_graph_dropped_spans_total: #services (absolute worst case)&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Finally, you get the following cardinality estimation:&lt;/p&gt;

&lt;div class=&#34;code-snippet code-snippet__mini&#34;&gt;&lt;div class=&#34;lang-toolbar__mini&#34;&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet code-snippet__border&#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-none&#34;&gt;  Sum: [([2 * #hb] &amp;#43; 2) * #hops] &amp;#43; [2 * #services]&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;


&lt;div class=&#34;admonition admonition-note&#34;&gt;&lt;blockquote&gt;&lt;p class=&#34;title text-uppercase&#34;&gt;Note&lt;/p&gt;&lt;p&gt;If &lt;code&gt;enable_messaging_system_latency_histogram&lt;/code&gt; configuration is set to &lt;code&gt;true&lt;/code&gt;, another histogram is produced:&lt;/p&gt;

&lt;div class=&#34;code-snippet code-snippet__mini&#34;&gt;&lt;div class=&#34;lang-toolbar__mini&#34;&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet code-snippet__border&#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-none&#34;&gt;  traces_service_graph_request_messaging_system_seconds: #hb * #hops&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;In that case, the estimation formula would be:&lt;/p&gt;

&lt;div class=&#34;code-snippet code-snippet__mini&#34;&gt;&lt;div class=&#34;lang-toolbar__mini&#34;&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet code-snippet__border&#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-none&#34;&gt;  Sum: [([3 * #hb] &amp;#43; 2) * #hops] &amp;#43; [2 * #services]&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;&lt;/blockquote&gt;&lt;/div&gt;



&lt;div class=&#34;admonition admonition-note&#34;&gt;&lt;blockquote&gt;&lt;p class=&#34;title text-uppercase&#34;&gt;Note&lt;/p&gt;&lt;p&gt;To estimate the number of metrics, refer to the &lt;a href=&#34;../cardinality/&#34;&gt;Dry run metrics generator&lt;/a&gt; documentation.&lt;/p&gt;&lt;/blockquote&gt;&lt;/div&gt;

&lt;h2 id=&#34;managing-cardinality-with-limits&#34;&gt;Managing cardinality with limits&lt;/h2&gt;
&lt;p&gt;For troubleshooting and managing cardinality limits at runtime, refer to the following sections in the 
    &lt;a href=&#34;/docs/tempo/v3.0.x/troubleshooting/metrics-generator/&#34;&gt;metrics-generator troubleshooting&lt;/a&gt; documentation:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
    &lt;a href=&#34;/docs/tempo/v3.0.x/troubleshooting/metrics-generator/#max-active-series&#34;&gt;Max active series&lt;/a&gt; to detect and configure the active series limit&lt;/li&gt;
&lt;li&gt;
    &lt;a href=&#34;/docs/tempo/v3.0.x/troubleshooting/metrics-generator/#entity-based-limiting&#34;&gt;Entity-based limiting&lt;/a&gt; to limit by unique label combinations instead of individual series&lt;/li&gt;
&lt;li&gt;
    &lt;a href=&#34;/docs/tempo/v3.0.x/troubleshooting/metrics-generator/#per-label-cardinality-limiting&#34;&gt;Per-label cardinality limiting&lt;/a&gt; to limit cardinality (distinct values) per label&lt;/li&gt;
&lt;/ul&gt;
]]></content><description>&lt;h1 id="estimate-cardinality-from-traces">Estimate cardinality from traces&lt;/h1>
&lt;p>Cardinality can pose a problem when you have lots of services.
There isn&amp;rsquo;t a direct formula or solution to this issue.
The following guide should help estimate the cardinality that a feature generates.&lt;/p></description></item><item><title>Reduce cardinality with span name sanitization</title><link>https://grafana.com/docs/tempo/v3.0.x/metrics-from-traces/metrics-generator/reduce-cardinality/</link><pubDate>Thu, 28 May 2026 17:50:33 +0100</pubDate><guid>https://grafana.com/docs/tempo/v3.0.x/metrics-from-traces/metrics-generator/reduce-cardinality/</guid><content><![CDATA[&lt;h1 id=&#34;reduce-cardinality-with-span-name-sanitization&#34;&gt;Reduce cardinality with span name sanitization&lt;/h1&gt;



&lt;div class=&#34;admonition admonition-note&#34;&gt;&lt;blockquote&gt;&lt;p class=&#34;title text-uppercase&#34;&gt;Note&lt;/p&gt;&lt;p&gt;span name sanitization is an &lt;a href=&#34;/docs/release-life-cycle/&#34;&gt;experimental feature&lt;/a&gt;. Engineering and on-call support is not available. Documentation is either limited or not provided outside of code comments. No SLA is provided. Enable the  feature toggle in Grafana to use this feature. Do not enable this feature in production environments.&lt;/p&gt;&lt;/blockquote&gt;&lt;/div&gt;


&lt;p&gt;The &lt;code&gt;span_name&lt;/code&gt; label often contributes the most to high cardinality in generated metrics.
Applications that embed dynamic values in span names, for example, REST paths with user IDs (&lt;code&gt;GET /users/123&lt;/code&gt;) or auto-generated operation names (&lt;code&gt;query-abc-def-ghi&lt;/code&gt;), create a unique series for every distinct value.
This drives up active series counts, increases storage and query costs, and can push you past cardinality limits.&lt;/p&gt;
&lt;p&gt;You can enable &lt;code&gt;span_name_sanitization&lt;/code&gt; to solve this without changing instrumentation across your services.
The option uses the DRAIN algorithm to learn patterns from incoming span names and replace variable segments with a &lt;code&gt;&amp;lt;_&amp;gt;&lt;/code&gt; placeholder, reducing many unique span names down to a single representative series.&lt;/p&gt;
&lt;p&gt;Use span name sanitization when:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Your &lt;code&gt;span_name&lt;/code&gt; label has high cardinality due to embedded IDs, timestamps, or request parameters.&lt;/li&gt;
&lt;li&gt;You don&amp;rsquo;t control the instrumentation that produces these span names, or changing it across all services isn&amp;rsquo;t practical.&lt;/li&gt;
&lt;li&gt;You want to reclaim active series budget without losing meaningful aggregation in your span metrics.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;before-you-begin&#34;&gt;Before you begin&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;The metrics-generator must be enabled and processing spans.&lt;/li&gt;
&lt;li&gt;You need access to the Tempo configuration or the 
    &lt;a href=&#34;/docs/tempo/v3.0.x/operations/manage-advanced-systems/user-configurable-overrides/&#34;&gt;user-configurable overrides API&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;how-the-drain-span-name-sanitization-works&#34;&gt;How the DRAIN span name sanitization works&lt;/h2&gt;
&lt;p&gt;DRAIN is a streaming algorithm that learns patterns from span names as they arrive.
It breaks each span name into tokens (segments separated by delimiters like &lt;code&gt;/&lt;/code&gt;, &lt;code&gt;-&lt;/code&gt;, or spaces) and organizes them into a tree structure.
When the algorithm sees enough variation at a specific position, for example, numeric IDs, UUIDs, or other data-like values, it replaces that position with a &lt;code&gt;&amp;lt;_&amp;gt;&lt;/code&gt; wildcard.&lt;/p&gt;
&lt;p&gt;For example, given these span names:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;GET /users/123&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;GET /users/456&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;GET /users/789&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;DRAIN detects that the third token varies while the rest stays constant, and produces the sanitized pattern &lt;code&gt;GET /users/&amp;lt;_&amp;gt;&lt;/code&gt;.
All three original span names map to this single pattern in generated metrics.&lt;/p&gt;
&lt;p&gt;DRAIN preserves meaningful trailing segments.
For example, given &lt;code&gt;GET /users/123/data&lt;/code&gt; and &lt;code&gt;GET /users/123/settings&lt;/code&gt;, DRAIN sanitizes the ID but keeps &lt;code&gt;/data&lt;/code&gt; and &lt;code&gt;/settings&lt;/code&gt; distinct, producing &lt;code&gt;GET /users/&amp;lt;_&amp;gt;/data&lt;/code&gt; and &lt;code&gt;GET /users/&amp;lt;_&amp;gt;/settings&lt;/code&gt; rather than collapsing both paths into a single pattern.&lt;/p&gt;
&lt;p&gt;The algorithm adapts continuously.
As new span names arrive, DRAIN refines its patterns and merges additional variable segments.
It also prunes stale patterns that haven&amp;rsquo;t been seen recently.&lt;/p&gt;
&lt;p&gt;For more details on the algorithm, refer to &lt;a href=&#34;https://ieeexplore.ieee.org/document/8029742/&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;Drain: An Online Log Parsing Approach with Fixed Depth Tree&lt;/a&gt; (He et al., IEEE ICWS 2017).&lt;/p&gt;
&lt;h2 id=&#34;configure-the-span-name-sanitizer&#34;&gt;Configure the span name sanitizer&lt;/h2&gt;
&lt;p&gt;Set the &lt;code&gt;span_name_sanitization&lt;/code&gt; option in the &lt;code&gt;overrides&lt;/code&gt; block of your Tempo configuration:&lt;/p&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;YAML&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-yaml&#34;&gt;overrides:
  defaults:
    metrics_generator:
      span_name_sanitization: &amp;#34;enabled&amp;#34;&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;The &lt;code&gt;span_name_sanitization&lt;/code&gt; option accepts these values:&lt;/p&gt;
&lt;section class=&#34;expand-table-wrapper&#34;&gt;&lt;div class=&#34;button-div&#34;&gt;
      &lt;button class=&#34;expand-table-btn&#34;&gt;Expand table&lt;/button&gt;
    &lt;/div&gt;&lt;div class=&#34;responsive-table-wrapper&#34;&gt;
    &lt;table&gt;
      &lt;thead&gt;
          &lt;tr&gt;
              &lt;th&gt;Value&lt;/th&gt;
              &lt;th&gt;Behavior&lt;/th&gt;
          &lt;/tr&gt;
      &lt;/thead&gt;
      &lt;tbody&gt;
          &lt;tr&gt;
              &lt;td&gt;&lt;code&gt;&amp;quot;&amp;quot;&lt;/code&gt; (empty string)&lt;/td&gt;
              &lt;td&gt;Disabled. No sanitization is applied. This is the default.&lt;/td&gt;
          &lt;/tr&gt;
          &lt;tr&gt;
              &lt;td&gt;&lt;code&gt;dry_run&lt;/code&gt;&lt;/td&gt;
              &lt;td&gt;Trains the DRAIN model and produces the &lt;code&gt;tempo_metrics_generator_registry_post_sanitization_demand_estimate&lt;/code&gt; metric, but doesn&amp;rsquo;t modify span names in generated metrics. Use this mode to evaluate the cardinality impact before enabling.&lt;/td&gt;
          &lt;/tr&gt;
          &lt;tr&gt;
              &lt;td&gt;&lt;code&gt;enabled&lt;/code&gt;&lt;/td&gt;
              &lt;td&gt;Applies DRAIN sanitization to span names in generated metrics.&lt;/td&gt;
          &lt;/tr&gt;
      &lt;/tbody&gt;
    &lt;/table&gt;
  &lt;/div&gt;
&lt;/section&gt;&lt;p&gt;You can also set this option per-tenant using the 
    &lt;a href=&#34;/docs/tempo/v3.0.x/operations/manage-advanced-systems/user-configurable-overrides/&#34;&gt;user-configurable overrides API&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&#34;evaluate-the-impact-with-dry-run-mode&#34;&gt;Evaluate the impact with dry-run mode&lt;/h2&gt;
&lt;p&gt;Before enabling &lt;code&gt;span_name_sanitization&lt;/code&gt;, use &lt;code&gt;dry_run&lt;/code&gt; mode to understand the impact without changing your metrics:&lt;/p&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;YAML&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-yaml&#34;&gt;overrides:
  defaults:
    metrics_generator:
      span_name_sanitization: &amp;#34;dry_run&amp;#34;&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;After the model has trained for a few minutes, compare the estimated demand against your current active series:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;tempo_metrics_generator_registry_post_sanitization_demand_estimate&lt;/code&gt; estimates cardinality if sanitization were fully applied&lt;/li&gt;
&lt;li&gt;&lt;code&gt;tempo_metrics_generator_registry_active_series&lt;/code&gt; shows the current active series count&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If the post-sanitization estimate is significantly lower, switching to &lt;code&gt;enabled&lt;/code&gt; reduces your active series count.&lt;/p&gt;
&lt;h2 id=&#34;monitor-the-span-name-sanitizer&#34;&gt;Monitor the span name sanitizer&lt;/h2&gt;
&lt;p&gt;After you enable &lt;code&gt;span_name_sanitization&lt;/code&gt;, use these metrics to observe its effect:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;tempo_metrics_generator_registry_spans_sanitized_total&lt;/code&gt; counts the spans whose &lt;code&gt;span_name&lt;/code&gt; label was replaced by a sanitized pattern&lt;/li&gt;
&lt;li&gt;&lt;code&gt;tempo_metrics_generator_registry_post_sanitization_demand_estimate&lt;/code&gt; shows the ongoing cardinality demand after sanitization is applied&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;next-steps&#34;&gt;Next steps&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;../cardinality/&#34;&gt;Cardinality&lt;/a&gt; for background on how cardinality impacts the metrics-generator&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;../estimate-cardinality/&#34;&gt;Estimate cardinality from traces&lt;/a&gt; to calculate expected active series for service graphs&lt;/li&gt;
&lt;li&gt;
    &lt;a href=&#34;/docs/tempo/v3.0.x/troubleshooting/metrics-generator/&#34;&gt;Troubleshoot metrics-generator&lt;/a&gt; for diagnosing metrics quality issues&lt;/li&gt;
&lt;/ul&gt;
]]></content><description>&lt;h1 id="reduce-cardinality-with-span-name-sanitization">Reduce cardinality with span name sanitization&lt;/h1>
&lt;div class="admonition admonition-note">&lt;blockquote>&lt;p class="title text-uppercase">Note&lt;/p>&lt;p>span name sanitization is an &lt;a href="/docs/release-life-cycle/">experimental feature&lt;/a>. Engineering and on-call support is not available. Documentation is either limited or not provided outside of code comments. No SLA is provided. Enable the feature toggle in Grafana to use this feature. Do not enable this feature in production environments.&lt;/p></description></item><item><title>Multi-tenancy support</title><link>https://grafana.com/docs/tempo/v3.0.x/metrics-from-traces/metrics-generator/multitenancy/</link><pubDate>Thu, 28 May 2026 17:50:33 +0100</pubDate><guid>https://grafana.com/docs/tempo/v3.0.x/metrics-from-traces/metrics-generator/multitenancy/</guid><content><![CDATA[&lt;h1 id=&#34;multi-tenancy-support&#34;&gt;Multi-tenancy support&lt;/h1&gt;
&lt;p&gt;Multi-tenancy is supported in the metrics-generator through the use of environment variables and per-tenant overrides.
This is useful when you want to propagate the multi-tenancy to the metrics backend,
keeping the data separated and secure.&lt;/p&gt;
&lt;h2 id=&#34;usage&#34;&gt;Usage&lt;/h2&gt;
&lt;p&gt;To use this feature, you need to define the &lt;code&gt;remote_write_headers&lt;/code&gt; override for each tenant in your configuration.
You can also use environment variables in your configuration file, which will be expanded at runtime.
To make use of environment variables, you need to pass the &lt;code&gt;--config.expand-env&lt;/code&gt; flag to Tempo.&lt;/p&gt;


&lt;div class=&#34;admonition admonition-warning&#34;&gt;&lt;blockquote&gt;&lt;p class=&#34;title text-uppercase&#34;&gt;Warning&lt;/p&gt;&lt;p&gt;Tempo 3.0 disables legacy flat (&lt;code&gt;unscoped&lt;/code&gt;) overrides by default. If your overrides use the legacy format, either migrate to the scoped format shown below or set &lt;code&gt;enable_legacy_overrides: true&lt;/code&gt; temporarily.&lt;/p&gt;&lt;/blockquote&gt;&lt;/div&gt;

&lt;p&gt;Example using the scoped overrides format:&lt;/p&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;YAML&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-yaml&#34;&gt;overrides:
  defaults:
    metrics_generator:
      processors: [ &amp;#39;span-metrics&amp;#39; ]
  team-traces-a:
    metrics_generator:
      remote_write_headers:
        Authorization: ${PROM_A_BASIC_AUTH}
  team-traces-b:
    metrics_generator:
      processors: [ &amp;#39;span-metrics&amp;#39;, &amp;#39;service-graphs&amp;#39; ]
      remote_write_headers:
        Authorization: ${PROM_B_BEARER_AUTH}&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;Bash&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-bash&#34;&gt;export PROM_A_BASIC_AUTH=&amp;#34;Basic $(echo &amp;#34;team-a:$(cat /token-prometheus-a)&amp;#34;|base64|tr -d &amp;#39;[:space:]&amp;#39;)&amp;#34;
export PROM_B_BEARER_AUTH=&amp;#34;Bearer $(cat /token-prometheus-b)&amp;#34;&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;In this example, &lt;code&gt;PROM_A_BASIC_AUTH&lt;/code&gt; and &lt;code&gt;PROM_B_BEARER_AUTH&lt;/code&gt; are environment variables that contain the respective tenants&amp;rsquo; authorization tokens.
The &lt;code&gt;remote_write_headers&lt;/code&gt; override is used to specify the &lt;code&gt;Authorization&lt;/code&gt; header for each tenant.
The &lt;code&gt;Authorization&lt;/code&gt; header is used to authenticate the remote write request to the Prometheus remote write endpoint.&lt;/p&gt;
]]></content><description>&lt;h1 id="multi-tenancy-support">Multi-tenancy support&lt;/h1>
&lt;p>Multi-tenancy is supported in the metrics-generator through the use of environment variables and per-tenant overrides.
This is useful when you want to propagate the multi-tenancy to the metrics backend,
keeping the data separated and secure.&lt;/p></description></item></channel></rss>