<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Operations on Grafana Labs</title><link>https://grafana.com/docs/loki/v2.9.x/operations/</link><description>Recent content in Operations on Grafana Labs</description><generator>Hugo -- gohugo.io</generator><language>en</language><atom:link href="/docs/loki/v2.9.x/operations/index.xml" rel="self" type="application/rss+xml"/><item><title>Zone aware ingesters</title><link>https://grafana.com/docs/loki/v2.9.x/operations/zone-ingesters/</link><pubDate>Mon, 18 Mar 2024 22:57:01 +0000</pubDate><guid>https://grafana.com/docs/loki/v2.9.x/operations/zone-ingesters/</guid><content><![CDATA[&lt;h1 id=&#34;zone-aware-ingesters&#34;&gt;Zone aware ingesters&lt;/h1&gt;
&lt;p&gt;Loki&amp;rsquo;s zone aware ingesters are used by Grafana Labs in order to allow for easier rollouts of large Loki deployments. You can think of them as three logical zones, however with some extra k8s config you could deploy them in separate zones.&lt;/p&gt;
&lt;p&gt;By default, an incoming log stream&amp;rsquo;s logs are replicated to 3 random ingesters. Except in the case of some replica scaling up or down, a given stream will always be replicated to the same 3 ingesters. This means that if one of those ingesters is restarted no data is lost, but two restarting can mean data is lost and also impacts the systems ability to ingest logs because of an unhealthy ring status.&lt;/p&gt;
&lt;p&gt;With zone awareness enabled, an incomming log line will be replicated to one ingester in each zone. This means that we&amp;rsquo;re not only concerned if ingesters in multiple zones restart at the same time. We can now rollout, or lose, an entire zone at once and not impact the system. This allows deployments with a large number of ingesters to be deployed too much more quickly.&lt;/p&gt;
&lt;p&gt;We also make use of &lt;a href=&#34;https://github.com/grafana/rollout-operator&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;rollout-operator&lt;/a&gt; to manage rollouts to the 3 StatefulSets gracefully. The rollout-operator looks for labels on StatefulSets to know which StatefulSets are part of a certain rollout group, and coordinate rollouts of pods only from a single StatefulSet in the group at a time. See the README in the rollout-operator repo. for a more in depth explanation.&lt;/p&gt;
&lt;h2 id=&#34;migration&#34;&gt;Migration&lt;/h2&gt;
&lt;p&gt;Migrating from a single ingester StatefulSet to 3 zone aware ingester StatefulSets. The migration follows a few general steps, regardless of deployment method.&lt;/p&gt;
&lt;ol start=&#34;0&#34;&gt;
&lt;li&gt;Configure your existing ingesters to be part of a zone, for example &lt;code&gt;zone-default&lt;/code&gt;, this will allow us to later exclude them from the write path while still allowing for graceful shutdowns.&lt;/li&gt;
&lt;li&gt;Prep for the increase in active streams (due to the way streams are split between ingesters) by increasing the # of active streams allowed for your tenants.&lt;/li&gt;
&lt;li&gt;Add and scale up your new zone aware ingester StatefulSets such that each has 1/3 of the total # of replicas you want to run.&lt;/li&gt;
&lt;li&gt;Enable zone awareness on the write path by setting &lt;code&gt;distributor.zone-awareness-enabled&lt;/code&gt; to true for distributors and rulers.&lt;/li&gt;
&lt;li&gt;Wait some time to ensure that the new zone aware ingesters have data for the time period they are queried for (&lt;code&gt;query_ingesters_within&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Enable zone awareness on the read path by setting &lt;code&gt;distributor.zone-awareness-enabled&lt;/code&gt; to true for queriers.&lt;/li&gt;
&lt;li&gt;Configure distributors and rulers to exclude ingesters in the &lt;code&gt;zone-default&lt;/code&gt; so those ingesters no longer receive write traffic via &lt;code&gt;distributor.excluded-zones&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Use the shutdown endpoint to flush data from the default ingesters, then scale down and remove the associated StatefulSet.&lt;/li&gt;
&lt;li&gt;Clean up any config remaining from the migration.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&#34;detailed-migration-steps&#34;&gt;Detailed Migration Steps&lt;/h3&gt;
&lt;p&gt;The following are steps to live migrate (no downtime) an existing Loki deployment from a single ingester StatefulSet to 3 zone aware ingester StatefulSets.&lt;/p&gt;
&lt;p&gt;These instructions assume you are using the zone aware ingester jsonnet deployment code from this repo, see &lt;a href=&#34;https://github.com/grafana/loki/blob/main/production/ksonnet/loki/multi-zone.libsonnet&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;here&lt;/a&gt;. &lt;strong&gt;If you are not using jsonnet see the relevant annotations in some steps that describe how to perform that step manually.&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Configure the zone for the existing “ingester” StatefulSet as zone-default by setting multi_zone_default_ingester_zone: true, this allows us to later filter out that zone from the write path.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Configure ingester-pdb with maxUnavailable=0 and deploy 3x zone-aware StatefulSets with 0 replicas by setting&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;_config&amp;#43;:: {
multi_zone_ingester_enabled: true,
multi_zone_ingester_migration_enabled: true,
multi_zone_ingester_replicas: 0,
// These last two lines are necessary now that we enable zone aware ingester by default
// so that newly created cells will not be migrated later on. If you miss them you will
// break writes in the cell.
multi_zone_ingester_replication_write_path_enabled: false,
multi_zone_ingester_replication_read_path_enabled: false,
},&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;If you&amp;rsquo;re not using jsonnet the new ingester StatefulSets should have a label with &lt;code&gt;rollout-group: ingester&lt;/code&gt;, annotation &lt;code&gt;rollout-max-unavailable: x&lt;/code&gt; (put a placeholder value in, later you should set the value of this to be some portion of the StatefulSets total replicas, for example in jsonnet we template this so that each StatefulSet runs 1/3 of the total replicas and the max unavailable is 1/3 of each StatefulSets replicas), and set the update strategy to &lt;code&gt;OnDelete&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Diff ingester and ingester-zone-a StatefulSets and make sure all config matches&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;kubectl get statefulset -n loki-dev-008 ingester -o yaml &amp;gt; ingester.yaml
kubectl get statefulset -n loki-dev-008 ingester-zone-a -o yaml &amp;gt; ingester-zone-a.yaml
diff ingester.yaml ingester-zone-a.yaml&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;expected diffs are things like: creation time and revision #, the zone, fields used by rollout operator, # of replicas, anything related to kustomize/flux, and PVC for the wal since the containers don&amp;rsquo;t exist yet.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Temporarily double max series limits for users that are using more than 50% of their current limit, the queries are as follows (add label selectors as appropriate):&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 by (tenant)(sum (loki_ingester_memory_streams) by (cluster, namespace, tenant) / on (namespace) group_left max by(namespace) (loki_distributor_replication_factor))
&amp;gt;
on (tenant) (
max by (tenant) (label_replace(loki_overrides{limit_name=&amp;#34;max_global_streams_per_user&amp;#34;} / 2.5, &amp;#34;tenant&amp;#34;, &amp;#34;$1&amp;#34;, &amp;#34;user&amp;#34;, &amp;#34;(.&amp;#43;)&amp;#34;))
)&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&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 (loki_ingester_memory_streams) by (cluster, namespace, tenant) / on (namespace) group_left max by(namespace) (loki_distributor_replication_factor)
) / ignoring(tenant) group_left max by (cluster, namespace)(loki_overrides_defaults{limit_name=&amp;#34;max_global_streams_per_user&amp;#34;}) &amp;gt; 0.4)
unless on (tenant) (
(label_replace(loki_overrides{limit_name=&amp;#34;max_global_streams_per_user&amp;#34;},&amp;#34;tenant&amp;#34;, &amp;#34;$1&amp;#34;, &amp;#34;user&amp;#34;, &amp;#34;(.&amp;#43;)&amp;#34;)))&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Scale up zone-aware StatefulSets until they have ⅓ of replicas each. In small cells you can do this all at once, in larger cells it might be safer to do it in chunks. The config value you need to change is &lt;code&gt;multi_zone_ingester_replicas: 6&lt;/code&gt;, the value will be split across the three StatefulSets. So in this case each StatefulSet would run 2 replicas.&lt;/p&gt;
&lt;p&gt;If you&amp;rsquo;re not using jsonnet this is the step where you would also set the annotation &lt;code&gt;rollout-max-unavailable&lt;/code&gt; to some value that is less than or equal to the # of replicas each StatefulSet is running.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;enable zone awareness on the write path via &lt;code&gt;multi_zone_ingester_replication_write_path_enabled: true&lt;/code&gt;, this causes distributors and rulers to reshuffle series to distributors in each zone, be sure to check that all the distributors and rulers have restarted properly.&lt;/p&gt;
&lt;p&gt;If you&amp;rsquo;re not using jsonnet enable zone awareness on the write path by setting &lt;code&gt;distributor.zone-awareness-enabled&lt;/code&gt; to true for distributors and rulers.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Wait &lt;code&gt;query_ingesters_within&lt;/code&gt; configured hours, by default this is 3h. This ensures that no data will be missing if we query a new ingester. However, because we cut chunks at least every 30m due to &lt;code&gt;chunk_idle_period&lt;/code&gt; we can likely reduce this amount of time.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Check that rule evaluations are still correct on the migration, look for increases in the rate for metrics with names with the following suffixes:&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;rule_evaluations_total
rule_evaluation_failures_total
rule_group_iterations_missed_total&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Enable zone-aware replication on the read path &lt;code&gt;multi_zone_ingester_replication_read_path_enabled: true&lt;/code&gt; or if you&amp;rsquo;re not using jsonnet set &lt;code&gt;distributor.zone-awareness-enabled&lt;/code&gt; to true for queriers.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Check that queries are still executing correctly, for example look at &lt;code&gt;loki_logql_querystats_latency_seconds_count&lt;/code&gt; to see that you don&amp;rsquo;t have a big increase in latency or error count for a specific query type.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Configure distributor / ruler to exclude ingesters in the “zone-default” so those ingesters no longer receive write traffic &lt;code&gt;multi_zone_ingester_exclude_default: true&lt;/code&gt; or if you&amp;rsquo;re not using jsonnet set &lt;code&gt;distributor.excluded-zones&lt;/code&gt; on distributors and rulers.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s a good idea to check rules evaluations again at this point, and also that the zone aware ingester StatefulSet is now receiving all the write traffic, you can compare &lt;code&gt;sum(loki_ingester_memory_streams{cluster=&amp;quot;&amp;lt;cluster&amp;gt;&amp;quot;,job=~&amp;quot;(&amp;lt;namespace&amp;gt;)/ingester&amp;quot;})&lt;/code&gt; to &lt;code&gt;sum(loki_ingester_memory_streams{cluster=&amp;quot;&amp;lt;cluster&amp;gt;&amp;quot;,job=~&amp;quot;(&amp;lt;namespace&amp;gt;)/ingester-zone.*&amp;quot;})&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;if you&amp;rsquo;re using an automated reconcilliation/deployment system like flux, disable it now (for example using flux ignore), if possible for just the default ingester StatefulSet&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Shutdown flush the default ingesters, unregistering them from the ring, you can do this by port-forwarding each ingester Pod and using the endpoint: &lt;code&gt;&amp;quot;http://url:PORT/ingester/shutdown?flush=true&amp;amp;delete_ring_tokens=true&amp;amp;terminate=false&amp;quot;&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;manually scale down the default ingester StatefulSet to 0 replicas, we do this via &lt;code&gt;tk apply&lt;/code&gt; but you could do it via modifying the yaml&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;merge a PR to your central config repo to keep the StatefulSet 0&amp;rsquo;d, and then remove the flux ignore&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;clean up any remaining temporary config from the migration, for example &lt;code&gt;multi_zone_ingester_migration_enabled: true&lt;/code&gt; is no longer needed&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;ensure that all the old default ingester PVC/PV are removed&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
]]></content><description>&lt;h1 id="zone-aware-ingesters">Zone aware ingesters&lt;/h1>
&lt;p>Loki&amp;rsquo;s zone aware ingesters are used by Grafana Labs in order to allow for easier rollouts of large Loki deployments. You can think of them as three logical zones, however with some extra k8s config you could deploy them in separate zones.&lt;/p></description></item><item><title>Observability</title><link>https://grafana.com/docs/loki/v2.9.x/operations/observability/</link><pubDate>Thu, 10 Apr 2025 12:15:54 +0000</pubDate><guid>https://grafana.com/docs/loki/v2.9.x/operations/observability/</guid><content><![CDATA[&lt;h1 id=&#34;observability&#34;&gt;Observability&lt;/h1&gt;
&lt;p&gt;Both Grafana Loki and Promtail expose a &lt;code&gt;/metrics&lt;/code&gt; endpoint that expose Prometheus
metrics (the default port is 3100 for Loki and 80 for Promtail). You will need
a local Prometheus and add Loki and Promtail as targets. See &lt;a href=&#34;https://prometheus.io/docs/prometheus/latest/configuration/configuration&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;configuring
Prometheus&lt;/a&gt;
for more information.&lt;/p&gt;
&lt;p&gt;All components of Loki expose the following metrics:&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;Metric Name&lt;/th&gt;
              &lt;th&gt;Metric Type&lt;/th&gt;
              &lt;th&gt;Description&lt;/th&gt;
          &lt;/tr&gt;
      &lt;/thead&gt;
      &lt;tbody&gt;
          &lt;tr&gt;
              &lt;td&gt;&lt;code&gt;loki_log_messages_total&lt;/code&gt;&lt;/td&gt;
              &lt;td&gt;Counter&lt;/td&gt;
              &lt;td&gt;DEPRECATED. Use internal_log_messages_total for the same functionality. Total number of log messages created by loki itself.&lt;/td&gt;
          &lt;/tr&gt;
          &lt;tr&gt;
              &lt;td&gt;&lt;code&gt;loki_internal_log_messages_total&lt;/code&gt;&lt;/td&gt;
              &lt;td&gt;Counter&lt;/td&gt;
              &lt;td&gt;Total number of log messages created by loki itself.&lt;/td&gt;
          &lt;/tr&gt;
          &lt;tr&gt;
              &lt;td&gt;&lt;code&gt;loki_request_duration_seconds&lt;/code&gt;&lt;/td&gt;
              &lt;td&gt;Histogram&lt;/td&gt;
              &lt;td&gt;Number of received HTTP requests.&lt;/td&gt;
          &lt;/tr&gt;
      &lt;/tbody&gt;
    &lt;/table&gt;
  &lt;/div&gt;
&lt;/section&gt;&lt;p&gt;The Loki Distributors expose the following metrics:&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;Metric Name&lt;/th&gt;
              &lt;th&gt;Metric Type&lt;/th&gt;
              &lt;th&gt;Description&lt;/th&gt;
          &lt;/tr&gt;
      &lt;/thead&gt;
      &lt;tbody&gt;
          &lt;tr&gt;
              &lt;td&gt;&lt;code&gt;loki_distributor_ingester_appends_total&lt;/code&gt;&lt;/td&gt;
              &lt;td&gt;Counter&lt;/td&gt;
              &lt;td&gt;The total number of batch appends sent to ingesters.&lt;/td&gt;
          &lt;/tr&gt;
          &lt;tr&gt;
              &lt;td&gt;&lt;code&gt;loki_distributor_ingester_append_failures_total&lt;/code&gt;&lt;/td&gt;
              &lt;td&gt;Counter&lt;/td&gt;
              &lt;td&gt;The total number of failed batch appends sent to ingesters.&lt;/td&gt;
          &lt;/tr&gt;
          &lt;tr&gt;
              &lt;td&gt;&lt;code&gt;loki_distributor_bytes_received_total&lt;/code&gt;&lt;/td&gt;
              &lt;td&gt;Counter&lt;/td&gt;
              &lt;td&gt;The total number of uncompressed bytes received per both tenant and retention hours.&lt;/td&gt;
          &lt;/tr&gt;
          &lt;tr&gt;
              &lt;td&gt;&lt;code&gt;loki_distributor_lines_received_total&lt;/code&gt;&lt;/td&gt;
              &lt;td&gt;Counter&lt;/td&gt;
              &lt;td&gt;The total number of log &lt;em&gt;entries&lt;/em&gt; received per tenant (not necessarily of &lt;em&gt;lines&lt;/em&gt;, as an entry can have more than one line of text).&lt;/td&gt;
          &lt;/tr&gt;
      &lt;/tbody&gt;
    &lt;/table&gt;
  &lt;/div&gt;
&lt;/section&gt;&lt;p&gt;The Loki Ingesters expose the following metrics:&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;Metric Name&lt;/th&gt;
              &lt;th&gt;Metric Type&lt;/th&gt;
              &lt;th&gt;Description&lt;/th&gt;
          &lt;/tr&gt;
      &lt;/thead&gt;
      &lt;tbody&gt;
          &lt;tr&gt;
              &lt;td&gt;&lt;code&gt;cortex_ingester_flush_queue_length&lt;/code&gt;&lt;/td&gt;
              &lt;td&gt;Gauge&lt;/td&gt;
              &lt;td&gt;The total number of series pending in the flush queue.&lt;/td&gt;
          &lt;/tr&gt;
          &lt;tr&gt;
              &lt;td&gt;&lt;code&gt;loki_chunk_store_index_entries_per_chunk&lt;/code&gt;&lt;/td&gt;
              &lt;td&gt;Histogram&lt;/td&gt;
              &lt;td&gt;Number of index entries written to storage per chunk.&lt;/td&gt;
          &lt;/tr&gt;
          &lt;tr&gt;
              &lt;td&gt;&lt;code&gt;loki_ingester_memory_chunks&lt;/code&gt;&lt;/td&gt;
              &lt;td&gt;Gauge&lt;/td&gt;
              &lt;td&gt;The total number of chunks in memory.&lt;/td&gt;
          &lt;/tr&gt;
          &lt;tr&gt;
              &lt;td&gt;&lt;code&gt;loki_ingester_memory_streams&lt;/code&gt;&lt;/td&gt;
              &lt;td&gt;Gauge&lt;/td&gt;
              &lt;td&gt;The total number of streams in memory.&lt;/td&gt;
          &lt;/tr&gt;
          &lt;tr&gt;
              &lt;td&gt;&lt;code&gt;loki_ingester_chunk_age_seconds&lt;/code&gt;&lt;/td&gt;
              &lt;td&gt;Histogram&lt;/td&gt;
              &lt;td&gt;Distribution of chunk ages when flushed.&lt;/td&gt;
          &lt;/tr&gt;
          &lt;tr&gt;
              &lt;td&gt;&lt;code&gt;loki_ingester_chunk_encode_time_seconds&lt;/code&gt;&lt;/td&gt;
              &lt;td&gt;Histogram&lt;/td&gt;
              &lt;td&gt;Distribution of chunk encode times.&lt;/td&gt;
          &lt;/tr&gt;
          &lt;tr&gt;
              &lt;td&gt;&lt;code&gt;loki_ingester_chunk_entries&lt;/code&gt;&lt;/td&gt;
              &lt;td&gt;Histogram&lt;/td&gt;
              &lt;td&gt;Distribution of lines per-chunk when flushed.&lt;/td&gt;
          &lt;/tr&gt;
          &lt;tr&gt;
              &lt;td&gt;&lt;code&gt;loki_ingester_chunk_size_bytes&lt;/code&gt;&lt;/td&gt;
              &lt;td&gt;Histogram&lt;/td&gt;
              &lt;td&gt;Distribution of chunk sizes when flushed.&lt;/td&gt;
          &lt;/tr&gt;
          &lt;tr&gt;
              &lt;td&gt;&lt;code&gt;loki_ingester_chunk_utilization&lt;/code&gt;&lt;/td&gt;
              &lt;td&gt;Histogram&lt;/td&gt;
              &lt;td&gt;Distribution of chunk utilization (filled uncompressed bytes vs maximum uncompressed bytes) when flushed.&lt;/td&gt;
          &lt;/tr&gt;
          &lt;tr&gt;
              &lt;td&gt;&lt;code&gt;loki_ingester_chunk_compression_ratio&lt;/code&gt;&lt;/td&gt;
              &lt;td&gt;Histogram&lt;/td&gt;
              &lt;td&gt;Distribution of chunk compression ratio when flushed.&lt;/td&gt;
          &lt;/tr&gt;
          &lt;tr&gt;
              &lt;td&gt;&lt;code&gt;loki_ingester_chunk_stored_bytes_total&lt;/code&gt;&lt;/td&gt;
              &lt;td&gt;Counter&lt;/td&gt;
              &lt;td&gt;Total bytes stored in chunks per tenant.&lt;/td&gt;
          &lt;/tr&gt;
          &lt;tr&gt;
              &lt;td&gt;&lt;code&gt;loki_ingester_chunks_created_total&lt;/code&gt;&lt;/td&gt;
              &lt;td&gt;Counter&lt;/td&gt;
              &lt;td&gt;The total number of chunks created in the ingester.&lt;/td&gt;
          &lt;/tr&gt;
          &lt;tr&gt;
              &lt;td&gt;&lt;code&gt;loki_ingester_chunks_stored_total&lt;/code&gt;&lt;/td&gt;
              &lt;td&gt;Counter&lt;/td&gt;
              &lt;td&gt;Total stored chunks per tenant.&lt;/td&gt;
          &lt;/tr&gt;
          &lt;tr&gt;
              &lt;td&gt;&lt;code&gt;loki_ingester_received_chunks&lt;/code&gt;&lt;/td&gt;
              &lt;td&gt;Counter&lt;/td&gt;
              &lt;td&gt;The total number of chunks sent by this ingester whilst joining during the handoff process.&lt;/td&gt;
          &lt;/tr&gt;
          &lt;tr&gt;
              &lt;td&gt;&lt;code&gt;loki_ingester_samples_per_chunk&lt;/code&gt;&lt;/td&gt;
              &lt;td&gt;Histogram&lt;/td&gt;
              &lt;td&gt;The number of samples in a chunk.&lt;/td&gt;
          &lt;/tr&gt;
          &lt;tr&gt;
              &lt;td&gt;&lt;code&gt;loki_ingester_sent_chunks&lt;/code&gt;&lt;/td&gt;
              &lt;td&gt;Counter&lt;/td&gt;
              &lt;td&gt;The total number of chunks sent by this ingester whilst leaving during the handoff process.&lt;/td&gt;
          &lt;/tr&gt;
          &lt;tr&gt;
              &lt;td&gt;&lt;code&gt;loki_ingester_streams_created_total&lt;/code&gt;&lt;/td&gt;
              &lt;td&gt;Counter&lt;/td&gt;
              &lt;td&gt;The total number of streams created per tenant.&lt;/td&gt;
          &lt;/tr&gt;
          &lt;tr&gt;
              &lt;td&gt;&lt;code&gt;loki_ingester_streams_removed_total&lt;/code&gt;&lt;/td&gt;
              &lt;td&gt;Counter&lt;/td&gt;
              &lt;td&gt;The total number of streams removed per tenant.&lt;/td&gt;
          &lt;/tr&gt;
      &lt;/tbody&gt;
    &lt;/table&gt;
  &lt;/div&gt;
&lt;/section&gt;&lt;p&gt;The Loki compactor exposes the following metrics:&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;Metric Name&lt;/th&gt;
              &lt;th&gt;Metric Type&lt;/th&gt;
              &lt;th&gt;Description&lt;/th&gt;
          &lt;/tr&gt;
      &lt;/thead&gt;
      &lt;tbody&gt;
          &lt;tr&gt;
              &lt;td&gt;&lt;code&gt;loki_compactor_delete_requests_processed_total&lt;/code&gt;&lt;/td&gt;
              &lt;td&gt;Counter&lt;/td&gt;
              &lt;td&gt;Number of delete requests processed per user.&lt;/td&gt;
          &lt;/tr&gt;
          &lt;tr&gt;
              &lt;td&gt;&lt;code&gt;loki_compactor_delete_requests_chunks_selected_total&lt;/code&gt;&lt;/td&gt;
              &lt;td&gt;Counter&lt;/td&gt;
              &lt;td&gt;Number of chunks selected while building delete plans per user.&lt;/td&gt;
          &lt;/tr&gt;
          &lt;tr&gt;
              &lt;td&gt;&lt;code&gt;loki_compactor_delete_processing_fails_total&lt;/code&gt;&lt;/td&gt;
              &lt;td&gt;Counter&lt;/td&gt;
              &lt;td&gt;Number of times the delete phase of compaction has failed.&lt;/td&gt;
          &lt;/tr&gt;
          &lt;tr&gt;
              &lt;td&gt;&lt;code&gt;loki_compactor_load_pending_requests_attempts_total&lt;/code&gt;&lt;/td&gt;
              &lt;td&gt;Counter&lt;/td&gt;
              &lt;td&gt;Number of attempts that were made to load pending requests with status.&lt;/td&gt;
          &lt;/tr&gt;
          &lt;tr&gt;
              &lt;td&gt;&lt;code&gt;loki_compactor_oldest_pending_delete_request_age_seconds&lt;/code&gt;&lt;/td&gt;
              &lt;td&gt;Gauge&lt;/td&gt;
              &lt;td&gt;Age of oldest pending delete request in seconds since they are over their cancellation period.&lt;/td&gt;
          &lt;/tr&gt;
          &lt;tr&gt;
              &lt;td&gt;&lt;code&gt;loki_compactor_pending_delete_requests_count&lt;/code&gt;&lt;/td&gt;
              &lt;td&gt;Gauge&lt;/td&gt;
              &lt;td&gt;Count of delete requests which are over their cancellation period and have not finished processing yet.&lt;/td&gt;
          &lt;/tr&gt;
          &lt;tr&gt;
              &lt;td&gt;&lt;code&gt;loki_compactor_deleted_lines&lt;/code&gt;&lt;/td&gt;
              &lt;td&gt;Counter&lt;/td&gt;
              &lt;td&gt;Number of deleted lines per user.&lt;/td&gt;
          &lt;/tr&gt;
      &lt;/tbody&gt;
    &lt;/table&gt;
  &lt;/div&gt;
&lt;/section&gt;&lt;p&gt;Promtail exposes these metrics:&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;Metric Name&lt;/th&gt;
              &lt;th&gt;Metric Type&lt;/th&gt;
              &lt;th&gt;Description&lt;/th&gt;
          &lt;/tr&gt;
      &lt;/thead&gt;
      &lt;tbody&gt;
          &lt;tr&gt;
              &lt;td&gt;&lt;code&gt;promtail_read_bytes_total&lt;/code&gt;&lt;/td&gt;
              &lt;td&gt;Gauge&lt;/td&gt;
              &lt;td&gt;Number of bytes read.&lt;/td&gt;
          &lt;/tr&gt;
          &lt;tr&gt;
              &lt;td&gt;&lt;code&gt;promtail_read_lines_total&lt;/code&gt;&lt;/td&gt;
              &lt;td&gt;Counter&lt;/td&gt;
              &lt;td&gt;Number of lines read.&lt;/td&gt;
          &lt;/tr&gt;
          &lt;tr&gt;
              &lt;td&gt;&lt;code&gt;promtail_dropped_bytes_total&lt;/code&gt;&lt;/td&gt;
              &lt;td&gt;Counter&lt;/td&gt;
              &lt;td&gt;Number of bytes dropped because failed to be sent to the ingester after all retries.&lt;/td&gt;
          &lt;/tr&gt;
          &lt;tr&gt;
              &lt;td&gt;&lt;code&gt;promtail_dropped_entries_total&lt;/code&gt;&lt;/td&gt;
              &lt;td&gt;Counter&lt;/td&gt;
              &lt;td&gt;Number of log entries dropped because failed to be sent to the ingester after all retries.&lt;/td&gt;
          &lt;/tr&gt;
          &lt;tr&gt;
              &lt;td&gt;&lt;code&gt;promtail_encoded_bytes_total&lt;/code&gt;&lt;/td&gt;
              &lt;td&gt;Counter&lt;/td&gt;
              &lt;td&gt;Number of bytes encoded and ready to send.&lt;/td&gt;
          &lt;/tr&gt;
          &lt;tr&gt;
              &lt;td&gt;&lt;code&gt;promtail_file_bytes_total&lt;/code&gt;&lt;/td&gt;
              &lt;td&gt;Gauge&lt;/td&gt;
              &lt;td&gt;Number of bytes read from files.&lt;/td&gt;
          &lt;/tr&gt;
          &lt;tr&gt;
              &lt;td&gt;&lt;code&gt;promtail_files_active_total&lt;/code&gt;&lt;/td&gt;
              &lt;td&gt;Gauge&lt;/td&gt;
              &lt;td&gt;Number of active files.&lt;/td&gt;
          &lt;/tr&gt;
          &lt;tr&gt;
              &lt;td&gt;&lt;code&gt;promtail_request_duration_seconds&lt;/code&gt;&lt;/td&gt;
              &lt;td&gt;Histogram&lt;/td&gt;
              &lt;td&gt;Number of send requests.&lt;/td&gt;
          &lt;/tr&gt;
          &lt;tr&gt;
              &lt;td&gt;&lt;code&gt;promtail_sent_bytes_total&lt;/code&gt;&lt;/td&gt;
              &lt;td&gt;Counter&lt;/td&gt;
              &lt;td&gt;Number of bytes sent.&lt;/td&gt;
          &lt;/tr&gt;
          &lt;tr&gt;
              &lt;td&gt;&lt;code&gt;promtail_sent_entries_total&lt;/code&gt;&lt;/td&gt;
              &lt;td&gt;Counter&lt;/td&gt;
              &lt;td&gt;Number of log entries sent to the ingester.&lt;/td&gt;
          &lt;/tr&gt;
          &lt;tr&gt;
              &lt;td&gt;&lt;code&gt;promtail_targets_active_total&lt;/code&gt;&lt;/td&gt;
              &lt;td&gt;Gauge&lt;/td&gt;
              &lt;td&gt;Number of total active targets.&lt;/td&gt;
          &lt;/tr&gt;
          &lt;tr&gt;
              &lt;td&gt;&lt;code&gt;promtail_targets_failed_total&lt;/code&gt;&lt;/td&gt;
              &lt;td&gt;Counter&lt;/td&gt;
              &lt;td&gt;Number of total failed targets.&lt;/td&gt;
          &lt;/tr&gt;
      &lt;/tbody&gt;
    &lt;/table&gt;
  &lt;/div&gt;
&lt;/section&gt;&lt;p&gt;Most of these metrics are counters and should continuously increase during normal operations:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Your app emits a log line to a file that is tracked by Promtail.&lt;/li&gt;
&lt;li&gt;Promtail reads the new line and increases its counters.&lt;/li&gt;
&lt;li&gt;Promtail forwards the log line to a Loki distributor, where the received
counters should increase.&lt;/li&gt;
&lt;li&gt;The Loki distributor forwards the log line to a Loki ingester, where the
request duration counter should increase.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;If Promtail uses any pipelines with metrics stages, those metrics will also be
exposed by Promtail at its &lt;code&gt;/metrics&lt;/code&gt; endpoint. See Promtail&amp;rsquo;s documentation on
&lt;a href=&#34;../../send-data/promtail/pipelines/&#34;&gt;Pipelines&lt;/a&gt; for more information.&lt;/p&gt;
&lt;p&gt;An example Grafana dashboard was built by the community and is available as
dashboard &lt;a href=&#34;/dashboards/10004&#34;&gt;10004&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&#34;metrics-cardinality&#34;&gt;Metrics cardinality&lt;/h2&gt;
&lt;p&gt;Some of the Loki observability metrics are emitted per tracked file (active), with the file path included in labels.
This increases the quantity of label values across the environment, thereby increasing cardinality. Best practices with Prometheus &lt;a href=&#34;https://prometheus.io/docs/practices/naming/#labels&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;labels&lt;/a&gt; discourage increasing cardinality in this way.
Review your emitted metrics before scraping with Prometheus, and configure the scraping to avoid this issue.&lt;/p&gt;
&lt;h2 id=&#34;mixins&#34;&gt;Mixins&lt;/h2&gt;
&lt;p&gt;The Loki repository has a &lt;a href=&#34;https://github.com/grafana/loki/blob/main/production/loki-mixin&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;mixin&lt;/a&gt; that includes a
set of dashboards, recording rules, and alerts. Together, the mixin gives you a
comprehensive package for monitoring Loki in production.&lt;/p&gt;
&lt;p&gt;For more information about mixins, take a look at the docs for the
&lt;a href=&#34;https://github.com/monitoring-mixins/docs&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;monitoring-mixins project&lt;/a&gt;.&lt;/p&gt;
]]></content><description>&lt;h1 id="observability">Observability&lt;/h1>
&lt;p>Both Grafana Loki and Promtail expose a &lt;code>/metrics&lt;/code> endpoint that expose Prometheus
metrics (the default port is 3100 for Loki and 80 for Promtail). You will need
a local Prometheus and add Loki and Promtail as targets. See &lt;a href="https://prometheus.io/docs/prometheus/latest/configuration/configuration" target="_blank" rel="noopener noreferrer">configuring
Prometheus&lt;/a>
for more information.&lt;/p></description></item><item><title>Overrides exporter</title><link>https://grafana.com/docs/loki/v2.9.x/operations/overrides-exporter/</link><pubDate>Mon, 29 Apr 2024 15:52:20 +0000</pubDate><guid>https://grafana.com/docs/loki/v2.9.x/operations/overrides-exporter/</guid><content><![CDATA[&lt;h1 id=&#34;overrides-exporter&#34;&gt;Overrides exporter&lt;/h1&gt;
&lt;p&gt;Loki is a multi-tenant system that supports applying limits to each tenant as a mechanism for resource management. The &lt;code&gt;overrides-exporter&lt;/code&gt; module exposes these limits as Prometheus metrics in order to help operators better understand tenant behavior.&lt;/p&gt;
&lt;h2 id=&#34;context&#34;&gt;Context&lt;/h2&gt;
&lt;p&gt;Configuration updates to tenant limits can be applied to Loki without restart via the 
    &lt;a href=&#34;/docs/loki/v2.9.x/configure/#runtime_config&#34;&gt;&lt;code&gt;runtime_config&lt;/code&gt;&lt;/a&gt; feature.&lt;/p&gt;
&lt;h2 id=&#34;example&#34;&gt;Example&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;overrides-exporter&lt;/code&gt; module is disabled by default. We recommend running a single instance per cluster to avoid issues with metric cardinality. The &lt;code&gt;overrides-exporter&lt;/code&gt; creates one metric for every scalar field in the limits configuration under the metric &lt;code&gt;loki_overrides_defaults&lt;/code&gt; with the default value for that field after loading the Loki configuration. It also exposes another metric for &lt;em&gt;every&lt;/em&gt; differing field for &lt;em&gt;every&lt;/em&gt; tenant.&lt;/p&gt;
&lt;p&gt;Using an example &lt;code&gt;runtime.yaml&lt;/code&gt;:&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:
  &amp;#34;tenant_1&amp;#34;:
    ingestion_rate_mb: 10
    max_streams_per_user: 100000
    max_chunks_per_query: 100000&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Launch an instance of the &lt;code&gt;overrides-exporter&lt;/code&gt;:&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;shell&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-shell&#34;&gt;loki -target=overrides-exporter -runtime-config.file=runtime.yaml -config.file=basic_schema_config.yaml -server.http-listen-port=8080&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;To inspect the tenant limit overrides:&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;shell&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-shell&#34;&gt;$ curl -sq localhost:8080/metrics | grep override
# HELP loki_overrides Resource limit overrides applied to tenants
# TYPE loki_overrides gauge
loki_overrides{limit_name=&amp;#34;ingestion_rate_mb&amp;#34;,user=&amp;#34;tenant_1&amp;#34;} 10
loki_overrides{limit_name=&amp;#34;max_chunks_per_query&amp;#34;,user=&amp;#34;tenant_1&amp;#34;} 100000
loki_overrides{limit_name=&amp;#34;max_streams_per_user&amp;#34;,user=&amp;#34;tenant_1&amp;#34;} 100000
# HELP loki_overrides_defaults Default values for resource limit overrides applied to tenants
# TYPE loki_overrides_defaults gauge
loki_overrides_defaults{limit_name=&amp;#34;cardinality_limit&amp;#34;} 100000
loki_overrides_defaults{limit_name=&amp;#34;creation_grace_period&amp;#34;} 6e&amp;#43;11
loki_overrides_defaults{limit_name=&amp;#34;ingestion_burst_size_mb&amp;#34;} 6
loki_overrides_defaults{limit_name=&amp;#34;ingestion_rate_mb&amp;#34;} 4
loki_overrides_defaults{limit_name=&amp;#34;max_cache_freshness_per_query&amp;#34;} 6e&amp;#43;10
loki_overrides_defaults{limit_name=&amp;#34;max_chunks_per_query&amp;#34;} 2e&amp;#43;06
loki_overrides_defaults{limit_name=&amp;#34;max_concurrent_tail_requests&amp;#34;} 10
loki_overrides_defaults{limit_name=&amp;#34;max_entries_limit_per_query&amp;#34;} 5000
loki_overrides_defaults{limit_name=&amp;#34;max_global_streams_per_user&amp;#34;} 5000
loki_overrides_defaults{limit_name=&amp;#34;max_label_name_length&amp;#34;} 1024
loki_overrides_defaults{limit_name=&amp;#34;max_label_names_per_series&amp;#34;} 30
loki_overrides_defaults{limit_name=&amp;#34;max_label_value_length&amp;#34;} 2048
loki_overrides_defaults{limit_name=&amp;#34;max_line_size&amp;#34;} 0
loki_overrides_defaults{limit_name=&amp;#34;max_queriers_per_tenant&amp;#34;} 0
loki_overrides_defaults{limit_name=&amp;#34;max_query_length&amp;#34;} 2.5956e&amp;#43;15
loki_overrides_defaults{limit_name=&amp;#34;max_query_lookback&amp;#34;} 0
loki_overrides_defaults{limit_name=&amp;#34;max_query_parallelism&amp;#34;} 32
loki_overrides_defaults{limit_name=&amp;#34;max_query_series&amp;#34;} 500
loki_overrides_defaults{limit_name=&amp;#34;max_streams_matchers_per_query&amp;#34;} 1000
loki_overrides_defaults{limit_name=&amp;#34;max_streams_per_user&amp;#34;} 0
loki_overrides_defaults{limit_name=&amp;#34;min_sharding_lookback&amp;#34;} 0
loki_overrides_defaults{limit_name=&amp;#34;per_stream_rate_limit&amp;#34;} 3.145728e&amp;#43;06
loki_overrides_defaults{limit_name=&amp;#34;per_stream_rate_limit_burst&amp;#34;} 1.572864e&amp;#43;07
loki_overrides_defaults{limit_name=&amp;#34;per_tenant_override_period&amp;#34;} 1e&amp;#43;10
loki_overrides_defaults{limit_name=&amp;#34;reject_old_samples_max_age&amp;#34;} 1.2096e&amp;#43;15
loki_overrides_defaults{limit_name=&amp;#34;retention_period&amp;#34;} 2.6784e&amp;#43;15
loki_overrides_defaults{limit_name=&amp;#34;ruler_evaluation_delay_duration&amp;#34;} 0
loki_overrides_defaults{limit_name=&amp;#34;ruler_max_rule_groups_per_tenant&amp;#34;} 0
loki_overrides_defaults{limit_name=&amp;#34;ruler_max_rules_per_rule_group&amp;#34;} 0
loki_overrides_defaults{limit_name=&amp;#34;ruler_remote_write_queue_batch_send_deadline&amp;#34;} 0
loki_overrides_defaults{limit_name=&amp;#34;ruler_remote_write_queue_capacity&amp;#34;} 0
loki_overrides_defaults{limit_name=&amp;#34;ruler_remote_write_queue_max_backoff&amp;#34;} 0
loki_overrides_defaults{limit_name=&amp;#34;ruler_remote_write_queue_max_samples_per_send&amp;#34;} 0
loki_overrides_defaults{limit_name=&amp;#34;ruler_remote_write_queue_max_shards&amp;#34;} 0
loki_overrides_defaults{limit_name=&amp;#34;ruler_remote_write_queue_min_backoff&amp;#34;} 0
loki_overrides_defaults{limit_name=&amp;#34;ruler_remote_write_queue_min_shards&amp;#34;} 0
loki_overrides_defaults{limit_name=&amp;#34;ruler_remote_write_timeout&amp;#34;} 0
loki_overrides_defaults{limit_name=&amp;#34;split_queries_by_interval&amp;#34;} 0&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Alerts can be created based on these metrics to inform operators when tenants are close to hitting their limits allowing for increases to be applied before the tenant limits are exceeded.&lt;/p&gt;
]]></content><description>&lt;h1 id="overrides-exporter">Overrides exporter&lt;/h1>
&lt;p>Loki is a multi-tenant system that supports applying limits to each tenant as a mechanism for resource management. The &lt;code>overrides-exporter&lt;/code> module exposes these limits as Prometheus metrics in order to help operators better understand tenant behavior.&lt;/p></description></item><item><title>Autoscaling Loki queriers</title><link>https://grafana.com/docs/loki/v2.9.x/operations/autoscaling_queriers/</link><pubDate>Wed, 06 Sep 2023 12:47:18 +0000</pubDate><guid>https://grafana.com/docs/loki/v2.9.x/operations/autoscaling_queriers/</guid><content><![CDATA[&lt;h1 id=&#34;autoscaling-loki-queriers&#34;&gt;Autoscaling Loki queriers&lt;/h1&gt;
&lt;p&gt;A microservices deployment of a Loki cluster that runs on Kubernetes typically handles a
workload that varies throughout the day.
To make Loki easier to operate and optimize the cost of running Loki at scale,
we have designed a set of resources to help you autoscale your Loki queriers.&lt;/p&gt;
&lt;h2 id=&#34;prerequisites&#34;&gt;Prerequisites&lt;/h2&gt;
&lt;p&gt;You need to run Loki in Kubernetes as a set of microservices. You need to use the query-scheduler.&lt;/p&gt;
&lt;p&gt;We recommend using &lt;a href=&#34;https://keda.sh/&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;Kubernetes Event-Driven Autoscaling (KEDA)&lt;/a&gt; to configure autoscaling
based on Prometheus metrics. Refer to &lt;a href=&#34;https://keda.sh/docs/latest/deploy&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;Deploying KEDA&lt;/a&gt; to learn more
about setting up KEDA in your Kubernetes cluster.&lt;/p&gt;
&lt;h2 id=&#34;scaling-metric&#34;&gt;Scaling metric&lt;/h2&gt;
&lt;p&gt;Because queriers pull queries from the query-scheduler queue and process them on the querier workers, you should scale metrics based on:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The scheduler queue size.&lt;/li&gt;
&lt;li&gt;The queries running in the queriers.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The query-scheduler exposes the &lt;code&gt;cortex_query_scheduler_inflight_requests&lt;/code&gt; metric.
It tracks the sum of queued queries plus the number of queries currently running in the querier workers.
The following query is useful to scale queriers based on the inflight requests.&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;promql&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-promql&#34;&gt;sum(
  max_over_time(
    cortex_query_scheduler_inflight_requests{namespace=&amp;#34;loki-cluster&amp;#34;, quantile=&amp;#34;&amp;lt;Q&amp;gt;&amp;#34;}[&amp;lt;R&amp;gt;]
  )
)&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Use the quantile (Q) and the range (R) parameters to fine-tune the metric.
The higher Q is, the more sensitive the metric is to short-lasting spikes.
As R increases, you can reduce the variation over time in the metric.
A higher R-value helps avoid the autoscaler from modifying the number of replicas too frequently.&lt;/p&gt;
&lt;p&gt;In our experience, we have found that a Q of 0.75 and an R of 2 minutes work well.
You can adjust these values according to your workload.&lt;/p&gt;
&lt;h2 id=&#34;cluster-capacity-planning&#34;&gt;Cluster capacity planning&lt;/h2&gt;
&lt;p&gt;To scale the Loki queries, you configure the following settings:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The threshold for scaling up and down&lt;/li&gt;
&lt;li&gt;The scale down stabilization period&lt;/li&gt;
&lt;li&gt;The minimum and the maximum number of queriers&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Querier workers process queries from the queue. You can configure each Loki querier to run several workers.
To reserve workforce headroom to address workload spikes, our recommendation is not to use more than 75% of the workers.
For example, if you configure the Loki queriers to run 6 workers, set a threshold of &lt;code&gt;floor(0.75 * 6) = 4&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;To determine the minimum number of queries that you should run, run at least one querier and determine the average
number of inflight requests the system processes 75% of the time over seven days. The target utilization of the queries is 75%.
So if we use 6 workers per querier, we will use the following query:&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;promql&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-promql&#34;&gt;clamp_min(ceil(
    avg(
        avg_over_time(cortex_query_scheduler_inflight_requests{namespace=&amp;#34;loki-cluster&amp;#34;, quantile=&amp;#34;0.75&amp;#34;}[7d])
    ) / scalar(floor(vector(6 * 0.75)))
), 1)&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;The maximum number of queriers to run is equal to the number of queriers required to process all inflight
requests 50% of the time during a seven-day timespan.
As for the previous example, if each querier runs 6 workers, divide the inflight requests by 6.
The resulting query becomes:&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;promql&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-promql&#34;&gt;ceil(
    max(
        max_over_time(cortex_query_scheduler_inflight_requests{namespace=&amp;#34;loki-cluster&amp;#34;, quantile=&amp;#34;0.5&amp;#34;}[7d])
    ) / 6
)&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;To minimize the scenario where Loki scales up shortly after scaling down, set
a stabilization window for scaling down.&lt;/p&gt;
&lt;h3 id=&#34;keda-configuration&#34;&gt;KEDA configuration&lt;/h3&gt;
&lt;p&gt;This &lt;a href=&#34;https://keda.sh/docs/latest/concepts/scaling-deployments/&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;KEDA ScaledObject&lt;/a&gt; example configures autoscaling
for the querier deployment in the &lt;code&gt;loki-cluster&lt;/code&gt; namespace.
The example shows the minimum number of replicas set to 10 and the maximum number of replicas set to 50.
Because each querier runs 6 workers, aiming to use 75% of those workers, the threshold is set to 4.
The metric is served at &lt;code&gt;http://prometheus.default:9090/prometheus&lt;/code&gt;. We configure a stabilization window of 30 minutes.&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;apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
  name: querier
  namespace: loki-cluster
spec:
  maxReplicaCount: 50
  minReplicaCount: 10
  scaleTargetRef:
    kind: Deployment
    name: querier
  triggers:
  - metadata:
      metricName: querier_autoscaling_metric
      query: sum(max_over_time(cortex_query_scheduler_inflight_requests{namespace=&amp;#34;loki-cluster&amp;#34;, quantile=&amp;#34;0.75&amp;#34;}[2m]))
      serverAddress: http://prometheus.default:9090/prometheus
      threshold: &amp;#34;4&amp;#34;
    type: prometheus
  advanced:
    horizontalPodAutoscalerConfig:
      behavior:
        scaleDown:
          stabilizationWindowSeconds: 1800&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;h2 id=&#34;prometheus-alerting-when-at-capacity&#34;&gt;Prometheus alerting when at capacity&lt;/h2&gt;
&lt;p&gt;Because the configured maximum might not be sufficient, a Prometheus alert can identify
when the quantity of queriers has been at its configured maximum for an extended time. The following example specifies three hours (&lt;code&gt;3h&lt;/code&gt;) as the extended time:&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;name: LokiAutoscalerMaxedOut
expr: kube_horizontalpodautoscaler_status_current_replicas{namespace=~&amp;#34;loki-cluster&amp;#34;} == kube_horizontalpodautoscaler_spec_max_replicas{namespace=~&amp;#34;loki-cluster&amp;#34;}
for: 3h
labels:
  severity: warning
annotations:
  description: HPA {{ $labels.namespace }}/{{ $labels.horizontalpodautoscaler }} has been running at max replicas for longer than 3h; this can indicate underprovisioning.
  summary: HPA has been running at max replicas for an extended time&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
]]></content><description>&lt;h1 id="autoscaling-loki-queriers">Autoscaling Loki queriers&lt;/h1>
&lt;p>A microservices deployment of a Loki cluster that runs on Kubernetes typically handles a
workload that varies throughout the day.
To make Loki easier to operate and optimize the cost of running Loki at scale,
we have designed a set of resources to help you autoscale your Loki queriers.&lt;/p></description></item><item><title>Request Validation &amp; Rate-Limit Errors</title><link>https://grafana.com/docs/loki/v2.9.x/operations/request-validation-rate-limits/</link><pubDate>Mon, 08 Apr 2024 17:50:08 +0000</pubDate><guid>https://grafana.com/docs/loki/v2.9.x/operations/request-validation-rate-limits/</guid><content><![CDATA[&lt;h1 id=&#34;request-validation--rate-limit-errors&#34;&gt;Request Validation &amp;amp; Rate-Limit Errors&lt;/h1&gt;
&lt;p&gt;Loki will reject requests if they exceed a usage threshold (rate-limit error) or if they are invalid (validation error).&lt;/p&gt;
&lt;p&gt;All occurrences of these errors can be observed using the &lt;code&gt;loki_discarded_samples_total&lt;/code&gt; and &lt;code&gt;loki_discarded_bytes_total&lt;/code&gt; metrics. The sections below describe the various possible reasons specified in the &lt;code&gt;reason&lt;/code&gt; label of these metrics.&lt;/p&gt;
&lt;p&gt;It is recommended that Loki operators set up alerts or dashboards with these metrics to detect when rate-limits or validation errors occur.&lt;/p&gt;
&lt;h3 id=&#34;terminology&#34;&gt;Terminology&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;sample&lt;/strong&gt;: a log line&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;stream&lt;/strong&gt;: samples with a unique combination of labels&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;active stream&lt;/strong&gt;: streams that are present in the ingesters - these have recently received log lines within the &lt;code&gt;chunk_idle_period&lt;/code&gt; period (default: 30m)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;rate-limit-errors&#34;&gt;Rate-Limit Errors&lt;/h2&gt;
&lt;p&gt;Rate-limits are enforced when Loki cannot handle more requests from a tenant.&lt;/p&gt;
&lt;h3 id=&#34;rate_limited&#34;&gt;&lt;code&gt;rate_limited&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;This rate-limit is enforced when a tenant has exceeded their configured log ingestion rate-limit.&lt;/p&gt;
&lt;p&gt;One solution if you&amp;rsquo;re seeing samples dropped due to &lt;code&gt;rate_limited&lt;/code&gt; is simply to increase the rate limits on your Loki cluster. These limits can be modified globally in the 
    &lt;a href=&#34;/docs/loki/v2.9.x/configuration/#limits_config&#34;&gt;&lt;code&gt;limits_config&lt;/code&gt;&lt;/a&gt; block, or on a per-tenant basis in the 
    &lt;a href=&#34;/docs/loki/v2.9.x/configuration/#runtime-configuration-file&#34;&gt;runtime overrides&lt;/a&gt; file. The config options to use are &lt;code&gt;ingestion_rate_mb&lt;/code&gt; and &lt;code&gt;ingestion_burst_size_mb&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Note that you&amp;rsquo;ll want to make sure your Loki cluster has sufficient resources provisioned to be able to accommodate these higher limits. Otherwise your cluster may experience performance degradation as it tries to handle this higher volume of log lines to ingest.&lt;/p&gt;
&lt;p&gt;Another option to address samples being dropped due to &lt;code&gt;rate_limits&lt;/code&gt; is simply to decrease the rate of log lines being sent to your Loki cluster. Consider collecting logs from fewer targets or setting up &lt;code&gt;drop&lt;/code&gt; stages in Promtail to filter out certain log lines. Promtail&amp;rsquo;s 
    &lt;a href=&#34;/docs/loki/v2.9.x/send-data/promtail/configuration/#limits_config&#34;&gt;limits configuration&lt;/a&gt; also gives you the ability to control the volume of logs Promtail remote writes to your Loki cluster.&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;Property&lt;/th&gt;
              &lt;th&gt;Value&lt;/th&gt;
          &lt;/tr&gt;
      &lt;/thead&gt;
      &lt;tbody&gt;
          &lt;tr&gt;
              &lt;td&gt;Enforced by&lt;/td&gt;
              &lt;td&gt;&lt;code&gt;distributor&lt;/code&gt;&lt;/td&gt;
          &lt;/tr&gt;
          &lt;tr&gt;
              &lt;td&gt;Outcome&lt;/td&gt;
              &lt;td&gt;Request rejected&lt;/td&gt;
          &lt;/tr&gt;
          &lt;tr&gt;
              &lt;td&gt;Retryable&lt;/td&gt;
              &lt;td&gt;Yes&lt;/td&gt;
          &lt;/tr&gt;
          &lt;tr&gt;
              &lt;td&gt;Sample discarded&lt;/td&gt;
              &lt;td&gt;No&lt;/td&gt;
          &lt;/tr&gt;
          &lt;tr&gt;
              &lt;td&gt;Configurable per tenant&lt;/td&gt;
              &lt;td&gt;Yes&lt;/td&gt;
          &lt;/tr&gt;
          &lt;tr&gt;
              &lt;td&gt;HTTP status code&lt;/td&gt;
              &lt;td&gt;&lt;code&gt;429 Too Many Requests&lt;/code&gt;&lt;/td&gt;
          &lt;/tr&gt;
      &lt;/tbody&gt;
    &lt;/table&gt;
  &lt;/div&gt;
&lt;/section&gt;&lt;h3 id=&#34;per_stream_rate_limit&#34;&gt;&lt;code&gt;per_stream_rate_limit&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;This limit is enforced when a single stream reaches its rate-limit.&lt;/p&gt;
&lt;p&gt;Each stream has a rate-limit applied to it to prevent individual streams from overwhelming the set of ingesters it is distributed to (the size of that set is equal to the &lt;code&gt;replication_factor&lt;/code&gt; value).&lt;/p&gt;
&lt;p&gt;This value can be modified globally in the 
    &lt;a href=&#34;/docs/loki/v2.9.x/configuration/#limits_config&#34;&gt;&lt;code&gt;limits_config&lt;/code&gt;&lt;/a&gt; block, or on a per-tenant basis in the 
    &lt;a href=&#34;/docs/loki/v2.9.x/configuration/#runtime-configuration-file&#34;&gt;runtime overrides&lt;/a&gt; file. The config options to adjust are &lt;code&gt;per_stream_rate_limit&lt;/code&gt; and &lt;code&gt;per_stream_rate_limit_burst&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Another option you could consider to decrease the rate of samples dropped due to &lt;code&gt;per_stream_rate_limit&lt;/code&gt; is to split the stream that is getting rate limited into several smaller streams. A third option is to use Promtail&amp;rsquo;s 
    &lt;a href=&#34;/docs/loki/v2.9.x/send-data/promtail/stages/limit/#limit-stage&#34;&gt;limit stage&lt;/a&gt; to limit the rate of samples sent to the stream hitting the &lt;code&gt;per_stream_rate_limit&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;We typically recommend setting &lt;code&gt;per_stream_rate_limit&lt;/code&gt; no higher than 5MB, and &lt;code&gt;per_stream_rate_limit_burst&lt;/code&gt; no higher than 20MB.&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;Property&lt;/th&gt;
              &lt;th&gt;Value&lt;/th&gt;
          &lt;/tr&gt;
      &lt;/thead&gt;
      &lt;tbody&gt;
          &lt;tr&gt;
              &lt;td&gt;Enforced by&lt;/td&gt;
              &lt;td&gt;&lt;code&gt;ingester&lt;/code&gt;&lt;/td&gt;
          &lt;/tr&gt;
          &lt;tr&gt;
              &lt;td&gt;Outcome&lt;/td&gt;
              &lt;td&gt;Request rejected&lt;/td&gt;
          &lt;/tr&gt;
          &lt;tr&gt;
              &lt;td&gt;Retryable&lt;/td&gt;
              &lt;td&gt;Yes&lt;/td&gt;
          &lt;/tr&gt;
          &lt;tr&gt;
              &lt;td&gt;Sample discarded&lt;/td&gt;
              &lt;td&gt;No&lt;/td&gt;
          &lt;/tr&gt;
          &lt;tr&gt;
              &lt;td&gt;Configurable per tenant&lt;/td&gt;
              &lt;td&gt;Yes&lt;/td&gt;
          &lt;/tr&gt;
          &lt;tr&gt;
              &lt;td&gt;HTTP status code&lt;/td&gt;
              &lt;td&gt;&lt;code&gt;429 Too Many Requests&lt;/code&gt;&lt;/td&gt;
          &lt;/tr&gt;
      &lt;/tbody&gt;
    &lt;/table&gt;
  &lt;/div&gt;
&lt;/section&gt;&lt;h3 id=&#34;stream_limit&#34;&gt;&lt;code&gt;stream_limit&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;This limit is enforced when a tenant reaches their maximum number of active streams.&lt;/p&gt;
&lt;p&gt;Active streams are held in memory buffers in the ingesters, and if this value becomes sufficiently large then it will cause the ingesters to run out of memory.&lt;/p&gt;
&lt;p&gt;This value can be modified globally in the 
    &lt;a href=&#34;/docs/loki/v2.9.x/configuration/#limits_config&#34;&gt;&lt;code&gt;limits_config&lt;/code&gt;&lt;/a&gt; block, or on a per-tenant basis in the 
    &lt;a href=&#34;/docs/loki/v2.9.x/configuration/#runtime-configuration-file&#34;&gt;runtime overrides&lt;/a&gt; file.  To increase the allowable active streams, adjust &lt;code&gt;max_global_streams_per_user&lt;/code&gt;. Alternatively, the number of active streams can be reduced by removing extraneous labels or removing excessive unique label 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;Property&lt;/th&gt;
              &lt;th&gt;Value&lt;/th&gt;
          &lt;/tr&gt;
      &lt;/thead&gt;
      &lt;tbody&gt;
          &lt;tr&gt;
              &lt;td&gt;Enforced by&lt;/td&gt;
              &lt;td&gt;&lt;code&gt;ingester&lt;/code&gt;&lt;/td&gt;
          &lt;/tr&gt;
          &lt;tr&gt;
              &lt;td&gt;Outcome&lt;/td&gt;
              &lt;td&gt;Request rejected&lt;/td&gt;
          &lt;/tr&gt;
          &lt;tr&gt;
              &lt;td&gt;Retryable&lt;/td&gt;
              &lt;td&gt;Yes&lt;/td&gt;
          &lt;/tr&gt;
          &lt;tr&gt;
              &lt;td&gt;Sample discarded&lt;/td&gt;
              &lt;td&gt;No&lt;/td&gt;
          &lt;/tr&gt;
          &lt;tr&gt;
              &lt;td&gt;Configurable per tenant&lt;/td&gt;
              &lt;td&gt;Yes&lt;/td&gt;
          &lt;/tr&gt;
          &lt;tr&gt;
              &lt;td&gt;HTTP status code&lt;/td&gt;
              &lt;td&gt;&lt;code&gt;429 Too Many Requests&lt;/code&gt;&lt;/td&gt;
          &lt;/tr&gt;
      &lt;/tbody&gt;
    &lt;/table&gt;
  &lt;/div&gt;
&lt;/section&gt;&lt;h2 id=&#34;validation-errors&#34;&gt;Validation Errors&lt;/h2&gt;
&lt;p&gt;Validation errors occur when a request violates a validation rule defined by Loki.&lt;/p&gt;
&lt;h3 id=&#34;line_too_long&#34;&gt;&lt;code&gt;line_too_long&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;This error occurs when a log line exceeds the maximum allowable length in bytes. The HTTP response will include the stream to which the offending log line belongs as well as its size in bytes.&lt;/p&gt;
&lt;p&gt;This value can be modified globally in the 
    &lt;a href=&#34;/docs/loki/v2.9.x/configuration/#limits_config&#34;&gt;&lt;code&gt;limits_config&lt;/code&gt;&lt;/a&gt; block, or on a per-tenant basis in the 
    &lt;a href=&#34;/docs/loki/v2.9.x/configuration/#runtime-configuration-file&#34;&gt;runtime overrides&lt;/a&gt; file. To increase the maximum line size, adjust &lt;code&gt;max_line_size&lt;/code&gt;.  We recommend that you do not increase this value above 256kb for performance reasons. Alternatively, Loki can be configured to ingest truncated versions of log lines over the length limit by using the &lt;code&gt;max_line_size_truncate&lt;/code&gt; option.&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;Property&lt;/th&gt;
              &lt;th&gt;Value&lt;/th&gt;
          &lt;/tr&gt;
      &lt;/thead&gt;
      &lt;tbody&gt;
          &lt;tr&gt;
              &lt;td&gt;Enforced by&lt;/td&gt;
              &lt;td&gt;&lt;code&gt;distributor&lt;/code&gt;&lt;/td&gt;
          &lt;/tr&gt;
          &lt;tr&gt;
              &lt;td&gt;Retryable&lt;/td&gt;
              &lt;td&gt;&lt;strong&gt;No&lt;/strong&gt;&lt;/td&gt;
          &lt;/tr&gt;
          &lt;tr&gt;
              &lt;td&gt;Sample discarded&lt;/td&gt;
              &lt;td&gt;&lt;strong&gt;Yes&lt;/strong&gt;&lt;/td&gt;
          &lt;/tr&gt;
          &lt;tr&gt;
              &lt;td&gt;Configurable per tenant&lt;/td&gt;
              &lt;td&gt;Yes&lt;/td&gt;
          &lt;/tr&gt;
      &lt;/tbody&gt;
    &lt;/table&gt;
  &lt;/div&gt;
&lt;/section&gt;&lt;h3 id=&#34;invalid_labels&#34;&gt;&lt;code&gt;invalid_labels&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;This error occurs when one or more labels in the submitted streams fail validation.&lt;/p&gt;
&lt;p&gt;Loki uses the &lt;a href=&#34;https://prometheus.io/docs/concepts/data_model/#metric-names-and-labels&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;same validation rules as Prometheus&lt;/a&gt; for validating labels.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Label names may contain ASCII letters, numbers, as well as underscores. They must match the regex &lt;code&gt;[a-zA-Z_][a-zA-Z0-9_]*&lt;/code&gt;. Label names beginning with __ are reserved for internal use.&lt;/p&gt;&lt;/blockquote&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;Property&lt;/th&gt;
              &lt;th&gt;Value&lt;/th&gt;
          &lt;/tr&gt;
      &lt;/thead&gt;
      &lt;tbody&gt;
          &lt;tr&gt;
              &lt;td&gt;Enforced by&lt;/td&gt;
              &lt;td&gt;&lt;code&gt;distributor&lt;/code&gt;&lt;/td&gt;
          &lt;/tr&gt;
          &lt;tr&gt;
              &lt;td&gt;Retryable&lt;/td&gt;
              &lt;td&gt;&lt;strong&gt;No&lt;/strong&gt;&lt;/td&gt;
          &lt;/tr&gt;
          &lt;tr&gt;
              &lt;td&gt;Sample discarded&lt;/td&gt;
              &lt;td&gt;&lt;strong&gt;Yes&lt;/strong&gt;&lt;/td&gt;
          &lt;/tr&gt;
          &lt;tr&gt;
              &lt;td&gt;Configurable per tenant&lt;/td&gt;
              &lt;td&gt;No&lt;/td&gt;
          &lt;/tr&gt;
      &lt;/tbody&gt;
    &lt;/table&gt;
  &lt;/div&gt;
&lt;/section&gt;&lt;h2 id=&#34;missing_labels&#34;&gt;&lt;code&gt;missing_labels&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;This validation error is returned when a stream is submitted without any 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;Property&lt;/th&gt;
              &lt;th&gt;Value&lt;/th&gt;
          &lt;/tr&gt;
      &lt;/thead&gt;
      &lt;tbody&gt;
          &lt;tr&gt;
              &lt;td&gt;Enforced by&lt;/td&gt;
              &lt;td&gt;&lt;code&gt;distributor&lt;/code&gt;&lt;/td&gt;
          &lt;/tr&gt;
          &lt;tr&gt;
              &lt;td&gt;Retryable&lt;/td&gt;
              &lt;td&gt;&lt;strong&gt;No&lt;/strong&gt;&lt;/td&gt;
          &lt;/tr&gt;
          &lt;tr&gt;
              &lt;td&gt;Sample discarded&lt;/td&gt;
              &lt;td&gt;&lt;strong&gt;Yes&lt;/strong&gt;&lt;/td&gt;
          &lt;/tr&gt;
          &lt;tr&gt;
              &lt;td&gt;Configurable per tenant&lt;/td&gt;
              &lt;td&gt;No&lt;/td&gt;
          &lt;/tr&gt;
      &lt;/tbody&gt;
    &lt;/table&gt;
  &lt;/div&gt;
&lt;/section&gt;&lt;h2 id=&#34;too_far_behind-and-out_of_order&#34;&gt;&lt;code&gt;too_far_behind&lt;/code&gt; and &lt;code&gt;out_of_order&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;too_far_behind&lt;/code&gt; and &lt;code&gt;out_of_order&lt;/code&gt; reasons are identical. Loki clusters with &lt;code&gt;unordered_writes=true&lt;/code&gt; (the default value as of Loki v2.4) use &lt;code&gt;reason=too_far_behind&lt;/code&gt;. Loki clusters with &lt;code&gt;unordered_writes=false&lt;/code&gt; use &lt;code&gt;reason=out_of_order&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;This validation error is returned when a stream is submitted out of order. More details can be found 
    &lt;a href=&#34;/docs/loki/v2.9.x/configuration/#accept-out-of-order-writes&#34;&gt;here&lt;/a&gt; about Loki&amp;rsquo;s ordering constraints.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;unordered_writes&lt;/code&gt; config value can be modified globally in the 
    &lt;a href=&#34;/docs/loki/v2.9.x/configuration/#limits_config&#34;&gt;&lt;code&gt;limits_config&lt;/code&gt;&lt;/a&gt; block, or on a per-tenant basis in the 
    &lt;a href=&#34;/docs/loki/v2.9.x/configuration/#runtime-configuration-file&#34;&gt;runtime overrides&lt;/a&gt; file, whereas &lt;code&gt;max_chunk_age&lt;/code&gt; is a global configuration.&lt;/p&gt;
&lt;p&gt;This problem can be solved by ensuring that log delivery is configured correctly, or by increasing the &lt;code&gt;max_chunk_age&lt;/code&gt; value.&lt;/p&gt;
&lt;p&gt;It is recommended to resist modifying the default value of &lt;code&gt;max_chunk_age&lt;/code&gt; as this has other implications, and to instead try track down the cause for delayed logged delivery. It should also be noted that this a per-stream error, so by simply splitting streams (adding more labels) this problem can be circumvented, especially if multiple hosts are sending samples for a single stream.&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;Property&lt;/th&gt;
              &lt;th&gt;Value&lt;/th&gt;
          &lt;/tr&gt;
      &lt;/thead&gt;
      &lt;tbody&gt;
          &lt;tr&gt;
              &lt;td&gt;Enforced by&lt;/td&gt;
              &lt;td&gt;&lt;code&gt;ingester&lt;/code&gt;&lt;/td&gt;
          &lt;/tr&gt;
          &lt;tr&gt;
              &lt;td&gt;Retryable&lt;/td&gt;
              &lt;td&gt;&lt;strong&gt;No&lt;/strong&gt;&lt;/td&gt;
          &lt;/tr&gt;
          &lt;tr&gt;
              &lt;td&gt;Sample discarded&lt;/td&gt;
              &lt;td&gt;&lt;strong&gt;Yes&lt;/strong&gt;&lt;/td&gt;
          &lt;/tr&gt;
          &lt;tr&gt;
              &lt;td&gt;Configurable per tenant&lt;/td&gt;
              &lt;td&gt;No&lt;/td&gt;
          &lt;/tr&gt;
      &lt;/tbody&gt;
    &lt;/table&gt;
  &lt;/div&gt;
&lt;/section&gt;&lt;h2 id=&#34;greater_than_max_sample_age&#34;&gt;&lt;code&gt;greater_than_max_sample_age&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;If the &lt;code&gt;reject_old_samples&lt;/code&gt; config option is set to &lt;code&gt;true&lt;/code&gt; (it is by default), then samples will be rejected with &lt;code&gt;reason=greater_than_max_sample_age&lt;/code&gt; if they are older than the &lt;code&gt;reject_old_samples_max_age&lt;/code&gt; value. You should not see samples rejected for &lt;code&gt;reason=greater_than_max_sample_age&lt;/code&gt; if &lt;code&gt;reject_old_samples=false&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;This value can be modified globally in the 
    &lt;a href=&#34;/docs/loki/v2.9.x/configuration/#limits_config&#34;&gt;&lt;code&gt;limits_config&lt;/code&gt;&lt;/a&gt; block, or on a per-tenant basis in the 
    &lt;a href=&#34;/docs/loki/v2.9.x/configuration/#runtime-configuration-file&#34;&gt;runtime overrides&lt;/a&gt; file. This error can be solved by increasing the &lt;code&gt;reject_old_samples_max_age&lt;/code&gt; value, or investigating why log delivery is delayed for this particular stream. The stream in question will be returned in the body of the HTTP response.&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;Property&lt;/th&gt;
              &lt;th&gt;Value&lt;/th&gt;
          &lt;/tr&gt;
      &lt;/thead&gt;
      &lt;tbody&gt;
          &lt;tr&gt;
              &lt;td&gt;Enforced by&lt;/td&gt;
              &lt;td&gt;&lt;code&gt;distributor&lt;/code&gt;&lt;/td&gt;
          &lt;/tr&gt;
          &lt;tr&gt;
              &lt;td&gt;Outcome&lt;/td&gt;
              &lt;td&gt;Request rejected&lt;/td&gt;
          &lt;/tr&gt;
          &lt;tr&gt;
              &lt;td&gt;Retryable&lt;/td&gt;
              &lt;td&gt;&lt;strong&gt;No&lt;/strong&gt;&lt;/td&gt;
          &lt;/tr&gt;
          &lt;tr&gt;
              &lt;td&gt;Sample discarded&lt;/td&gt;
              &lt;td&gt;&lt;strong&gt;Yes&lt;/strong&gt;&lt;/td&gt;
          &lt;/tr&gt;
          &lt;tr&gt;
              &lt;td&gt;Configurable per tenant&lt;/td&gt;
              &lt;td&gt;Yes&lt;/td&gt;
          &lt;/tr&gt;
          &lt;tr&gt;
              &lt;td&gt;HTTP status code&lt;/td&gt;
              &lt;td&gt;&lt;code&gt;400 Bad Request&lt;/code&gt;&lt;/td&gt;
          &lt;/tr&gt;
      &lt;/tbody&gt;
    &lt;/table&gt;
  &lt;/div&gt;
&lt;/section&gt;&lt;h2 id=&#34;too_far_in_future&#34;&gt;&lt;code&gt;too_far_in_future&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;If a sample&amp;rsquo;s timestamp is greater than the current timestamp, Loki allows for a certain grace period during which samples will be accepted. If the grace period is exceeded, the error will occur.&lt;/p&gt;
&lt;p&gt;This value can be modified globally in the 
    &lt;a href=&#34;/docs/loki/v2.9.x/configuration/#limits_config&#34;&gt;&lt;code&gt;limits_config&lt;/code&gt;&lt;/a&gt; block, or on a per-tenant basis in the 
    &lt;a href=&#34;/docs/loki/v2.9.x/configuration/#runtime-configuration-file&#34;&gt;runtime overrides&lt;/a&gt; file. This error can be solved by increasing the &lt;code&gt;creation_grace_period&lt;/code&gt; value, or investigating why this particular stream has a timestamp too far into the future. The stream in question will be returned in the body of the HTTP response.&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;Property&lt;/th&gt;
              &lt;th&gt;Value&lt;/th&gt;
          &lt;/tr&gt;
      &lt;/thead&gt;
      &lt;tbody&gt;
          &lt;tr&gt;
              &lt;td&gt;Enforced by&lt;/td&gt;
              &lt;td&gt;&lt;code&gt;distributor&lt;/code&gt;&lt;/td&gt;
          &lt;/tr&gt;
          &lt;tr&gt;
              &lt;td&gt;Outcome&lt;/td&gt;
              &lt;td&gt;Request rejected&lt;/td&gt;
          &lt;/tr&gt;
          &lt;tr&gt;
              &lt;td&gt;Retryable&lt;/td&gt;
              &lt;td&gt;&lt;strong&gt;No&lt;/strong&gt;&lt;/td&gt;
          &lt;/tr&gt;
          &lt;tr&gt;
              &lt;td&gt;Sample discarded&lt;/td&gt;
              &lt;td&gt;&lt;strong&gt;Yes&lt;/strong&gt;&lt;/td&gt;
          &lt;/tr&gt;
          &lt;tr&gt;
              &lt;td&gt;Configurable per tenant&lt;/td&gt;
              &lt;td&gt;Yes&lt;/td&gt;
          &lt;/tr&gt;
          &lt;tr&gt;
              &lt;td&gt;HTTP status code&lt;/td&gt;
              &lt;td&gt;&lt;code&gt;400 Bad Request&lt;/code&gt;&lt;/td&gt;
          &lt;/tr&gt;
      &lt;/tbody&gt;
    &lt;/table&gt;
  &lt;/div&gt;
&lt;/section&gt;&lt;h2 id=&#34;max_label_names_per_series&#34;&gt;&lt;code&gt;max_label_names_per_series&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;If a sample is submitted with more labels than Loki has been configured to allow, it will be rejected with the &lt;code&gt;max_label_names_per_series&lt;/code&gt; reason. Note that &amp;lsquo;series&amp;rsquo; is the same thing as a &amp;lsquo;stream&amp;rsquo; in Loki - the &amp;lsquo;series&amp;rsquo; term is a legacy name.&lt;/p&gt;
&lt;p&gt;This value can be modified globally in the 
    &lt;a href=&#34;/docs/loki/v2.9.x/configuration/#limits_config&#34;&gt;&lt;code&gt;limits_config&lt;/code&gt;&lt;/a&gt; block, or on a per-tenant basis in the 
    &lt;a href=&#34;/docs/loki/v2.9.x/configuration/#runtime-configuration-file&#34;&gt;runtime overrides&lt;/a&gt; file. This error can be solved by increasing the &lt;code&gt;max_label_names_per_series&lt;/code&gt; value. The stream to which the offending sample (i.e. the one with too many label names) belongs will be returned in the body of the HTTP response.&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;Property&lt;/th&gt;
              &lt;th&gt;Value&lt;/th&gt;
          &lt;/tr&gt;
      &lt;/thead&gt;
      &lt;tbody&gt;
          &lt;tr&gt;
              &lt;td&gt;Enforced by&lt;/td&gt;
              &lt;td&gt;&lt;code&gt;distributor&lt;/code&gt;&lt;/td&gt;
          &lt;/tr&gt;
          &lt;tr&gt;
              &lt;td&gt;Outcome&lt;/td&gt;
              &lt;td&gt;Request rejected&lt;/td&gt;
          &lt;/tr&gt;
          &lt;tr&gt;
              &lt;td&gt;Retryable&lt;/td&gt;
              &lt;td&gt;&lt;strong&gt;No&lt;/strong&gt;&lt;/td&gt;
          &lt;/tr&gt;
          &lt;tr&gt;
              &lt;td&gt;Sample discarded&lt;/td&gt;
              &lt;td&gt;&lt;strong&gt;Yes&lt;/strong&gt;&lt;/td&gt;
          &lt;/tr&gt;
          &lt;tr&gt;
              &lt;td&gt;Configurable per tenant&lt;/td&gt;
              &lt;td&gt;Yes&lt;/td&gt;
          &lt;/tr&gt;
          &lt;tr&gt;
              &lt;td&gt;HTTP status code&lt;/td&gt;
              &lt;td&gt;&lt;code&gt;400 Bad Request&lt;/code&gt;&lt;/td&gt;
          &lt;/tr&gt;
      &lt;/tbody&gt;
    &lt;/table&gt;
  &lt;/div&gt;
&lt;/section&gt;&lt;h2 id=&#34;label_name_too_long&#34;&gt;&lt;code&gt;label_name_too_long&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;If a sample is sent with a label name that has a length in bytes greater than Loki has been configured to allow, it will be rejected with the &lt;code&gt;label_name_too_long&lt;/code&gt; reason.&lt;/p&gt;
&lt;p&gt;This value can be modified globally in the 
    &lt;a href=&#34;/docs/loki/v2.9.x/configuration/#limits_config&#34;&gt;&lt;code&gt;limits_config&lt;/code&gt;&lt;/a&gt; block, or on a per-tenant basis in the 
    &lt;a href=&#34;/docs/loki/v2.9.x/configuration/#runtime-configuration-file&#34;&gt;runtime overrides&lt;/a&gt; file. This error can be solved by increasing the &lt;code&gt;max_label_name_length&lt;/code&gt; value, though we do not recommend raising it significantly above the default value of &lt;code&gt;1024&lt;/code&gt; for performance reasons. The offending stream will be returned in the body of the HTTP response.&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;Property&lt;/th&gt;
              &lt;th&gt;Value&lt;/th&gt;
          &lt;/tr&gt;
      &lt;/thead&gt;
      &lt;tbody&gt;
          &lt;tr&gt;
              &lt;td&gt;Enforced by&lt;/td&gt;
              &lt;td&gt;&lt;code&gt;distributor&lt;/code&gt;&lt;/td&gt;
          &lt;/tr&gt;
          &lt;tr&gt;
              &lt;td&gt;Outcome&lt;/td&gt;
              &lt;td&gt;Request rejected&lt;/td&gt;
          &lt;/tr&gt;
          &lt;tr&gt;
              &lt;td&gt;Retryable&lt;/td&gt;
              &lt;td&gt;&lt;strong&gt;No&lt;/strong&gt;&lt;/td&gt;
          &lt;/tr&gt;
          &lt;tr&gt;
              &lt;td&gt;Sample discarded&lt;/td&gt;
              &lt;td&gt;&lt;strong&gt;Yes&lt;/strong&gt;&lt;/td&gt;
          &lt;/tr&gt;
          &lt;tr&gt;
              &lt;td&gt;Configurable per tenant&lt;/td&gt;
              &lt;td&gt;Yes&lt;/td&gt;
          &lt;/tr&gt;
          &lt;tr&gt;
              &lt;td&gt;HTTP status code&lt;/td&gt;
              &lt;td&gt;&lt;code&gt;400 Bad Request&lt;/code&gt;&lt;/td&gt;
          &lt;/tr&gt;
      &lt;/tbody&gt;
    &lt;/table&gt;
  &lt;/div&gt;
&lt;/section&gt;&lt;h2 id=&#34;label_value_too_long&#34;&gt;&lt;code&gt;label_value_too_long&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;If a sample has a label value with a length in bytes greater than Loki has been configured to allow, it will be rejected for the &lt;code&gt;label_value_too_long&lt;/code&gt; reason.&lt;/p&gt;
&lt;p&gt;This value can be modified globally in the 
    &lt;a href=&#34;/docs/loki/v2.9.x/configuration/#limits_config&#34;&gt;&lt;code&gt;limits_config&lt;/code&gt;&lt;/a&gt; block, or on a per-tenant basis in the 
    &lt;a href=&#34;/docs/loki/v2.9.x/configuration/#runtime-configuration-file&#34;&gt;runtime overrides&lt;/a&gt; file. This error can be solved by increasing the &lt;code&gt;max_label_value_length&lt;/code&gt; value. The offending stream will be returned in the body of the HTTP response.&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;Property&lt;/th&gt;
              &lt;th&gt;Value&lt;/th&gt;
          &lt;/tr&gt;
      &lt;/thead&gt;
      &lt;tbody&gt;
          &lt;tr&gt;
              &lt;td&gt;Enforced by&lt;/td&gt;
              &lt;td&gt;&lt;code&gt;distributor&lt;/code&gt;&lt;/td&gt;
          &lt;/tr&gt;
          &lt;tr&gt;
              &lt;td&gt;Outcome&lt;/td&gt;
              &lt;td&gt;Request rejected&lt;/td&gt;
          &lt;/tr&gt;
          &lt;tr&gt;
              &lt;td&gt;Retryable&lt;/td&gt;
              &lt;td&gt;&lt;strong&gt;No&lt;/strong&gt;&lt;/td&gt;
          &lt;/tr&gt;
          &lt;tr&gt;
              &lt;td&gt;Sample discarded&lt;/td&gt;
              &lt;td&gt;&lt;strong&gt;Yes&lt;/strong&gt;&lt;/td&gt;
          &lt;/tr&gt;
          &lt;tr&gt;
              &lt;td&gt;Configurable per tenant&lt;/td&gt;
              &lt;td&gt;Yes&lt;/td&gt;
          &lt;/tr&gt;
          &lt;tr&gt;
              &lt;td&gt;HTTP status code&lt;/td&gt;
              &lt;td&gt;&lt;code&gt;400 Bad Request&lt;/code&gt;&lt;/td&gt;
          &lt;/tr&gt;
      &lt;/tbody&gt;
    &lt;/table&gt;
  &lt;/div&gt;
&lt;/section&gt;&lt;h2 id=&#34;duplicate_label_names&#34;&gt;&lt;code&gt;duplicate_label_names&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;If a sample is sent with two or more identical labels, it will be rejected for the &lt;code&gt;duplicate_label_names&lt;/code&gt; reason.&lt;/p&gt;
&lt;p&gt;The offending stream will be returned in the body of the HTTP response.&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;Property&lt;/th&gt;
              &lt;th&gt;Value&lt;/th&gt;
          &lt;/tr&gt;
      &lt;/thead&gt;
      &lt;tbody&gt;
          &lt;tr&gt;
              &lt;td&gt;Enforced by&lt;/td&gt;
              &lt;td&gt;&lt;code&gt;distributor&lt;/code&gt;&lt;/td&gt;
          &lt;/tr&gt;
          &lt;tr&gt;
              &lt;td&gt;Outcome&lt;/td&gt;
              &lt;td&gt;Request rejected&lt;/td&gt;
          &lt;/tr&gt;
          &lt;tr&gt;
              &lt;td&gt;Retryable&lt;/td&gt;
              &lt;td&gt;&lt;strong&gt;No&lt;/strong&gt;&lt;/td&gt;
          &lt;/tr&gt;
          &lt;tr&gt;
              &lt;td&gt;Sample discarded&lt;/td&gt;
              &lt;td&gt;&lt;strong&gt;Yes&lt;/strong&gt;&lt;/td&gt;
          &lt;/tr&gt;
          &lt;tr&gt;
              &lt;td&gt;Configurable per tenant&lt;/td&gt;
              &lt;td&gt;No&lt;/td&gt;
          &lt;/tr&gt;
          &lt;tr&gt;
              &lt;td&gt;HTTP status code&lt;/td&gt;
              &lt;td&gt;&lt;code&gt;400 Bad Request&lt;/code&gt;&lt;/td&gt;
          &lt;/tr&gt;
      &lt;/tbody&gt;
    &lt;/table&gt;
  &lt;/div&gt;
&lt;/section&gt;]]></content><description>&lt;h1 id="request-validation--rate-limit-errors">Request Validation &amp;amp; Rate-Limit Errors&lt;/h1>
&lt;p>Loki will reject requests if they exceed a usage threshold (rate-limit error) or if they are invalid (validation error).&lt;/p>
&lt;p>All occurrences of these errors can be observed using the &lt;code>loki_discarded_samples_total&lt;/code> and &lt;code>loki_discarded_bytes_total&lt;/code> metrics. The sections below describe the various possible reasons specified in the &lt;code>reason&lt;/code> label of these metrics.&lt;/p></description></item><item><title>Scalability</title><link>https://grafana.com/docs/loki/v2.9.x/operations/scalability/</link><pubDate>Thu, 10 Apr 2025 12:15:54 +0000</pubDate><guid>https://grafana.com/docs/loki/v2.9.x/operations/scalability/</guid><content><![CDATA[&lt;h1 id=&#34;scalability&#34;&gt;Scalability&lt;/h1&gt;
&lt;p&gt;When scaling Loki, operators should consider running several Loki processes
partitioned by role (ingester, distributor, querier) rather than a single Loki
process. Grafana Labs&amp;rsquo; &lt;a href=&#34;https://github.com/grafana/loki/blob/main/production/ksonnet/loki&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;production setup&lt;/a&gt;
contains &lt;code&gt;.libsonnet&lt;/code&gt; files that demonstrates configuring separate components
and scaling for resource usage.&lt;/p&gt;
&lt;h2 id=&#34;separate-query-scheduler&#34;&gt;Separate Query Scheduler&lt;/h2&gt;
&lt;p&gt;The Query frontend has an in-memory queue that can be moved out into a separate process similar to the
&lt;a href=&#34;/docs/mimir/latest/operators-guide/architecture/components/query-scheduler/&#34;&gt;Grafana Mimir query-scheduler&lt;/a&gt;. This allows running multiple query frontends.&lt;/p&gt;
&lt;p&gt;To run with the Query Scheduler, the frontend needs to be passed the scheduler&amp;rsquo;s address via &lt;code&gt;-frontend.scheduler-address&lt;/code&gt; and the querier processes needs to be started with &lt;code&gt;-querier.scheduler-address&lt;/code&gt; set to the same address. Both options can also be defined via the &lt;a href=&#34;../../configure/&#34;&gt;configuration file&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;It is not valid to start the querier with both a configured frontend and a scheduler address.&lt;/p&gt;
&lt;p&gt;The query scheduler process itself can be started via the &lt;code&gt;-target=query-scheduler&lt;/code&gt; option of the Loki Docker image. For instance, &lt;code&gt;docker run grafana/loki:latest -config.file=/etc/loki/config.yaml -target=query-scheduler -server.http-listen-port=8009 -server.grpc-listen-port=9009&lt;/code&gt; starts the query scheduler listening on ports &lt;code&gt;8009&lt;/code&gt; and &lt;code&gt;9009&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&#34;memory-ballast&#34;&gt;Memory ballast&lt;/h2&gt;
&lt;p&gt;In compute-constrained environments, garbage collection can become a significant performance factor. Frequently-run garbage collection interferes with running the application by using CPU resources. The use of memory ballast can mitigate the issue. Memory ballast allocates extra, but unused virtual memory in order to inflate the quantity of live heap space. Garbage collection is triggered by the growth of heap space usage. The inflated quantity of heap space reduces the perceived growth, so garbage collection occurs less frequently.&lt;/p&gt;
&lt;p&gt;Configure memory ballast using the ballast_bytes configuration option.&lt;/p&gt;
&lt;h2 id=&#34;remote-rule-evaluation&#34;&gt;Remote rule evaluation&lt;/h2&gt;
&lt;p&gt;&lt;em&gt;This feature was first proposed in &lt;a href=&#34;https://github.com/grafana/loki/pull/8129&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;&lt;code&gt;LID-0002&lt;/code&gt;&lt;/a&gt;; it contains the design decisions
which informed the implementation.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;By default, the &lt;code&gt;ruler&lt;/code&gt; component embeds a query engine to evaluate rules. This generally works fine, except when rules
are complex or have to process a large amount of data regularly. Poor performance of the &lt;code&gt;ruler&lt;/code&gt; manifests as recording rules metrics
with gaps or missed alerts. This situation can be detected by alerting on the &lt;code&gt;cortex_prometheus_rule_group_iterations_missed_total&lt;/code&gt; metric
when it has a non-zero value.&lt;/p&gt;
&lt;p&gt;A solution to this problem is to externalize rule evaluation from the &lt;code&gt;ruler&lt;/code&gt; process. The &lt;code&gt;ruler&lt;/code&gt; embedded query engine
is single-threaded, meaning that rules are not split, sharded, or otherwise accelerated like regular Loki queries. The &lt;code&gt;query-frontend&lt;/code&gt;
component exists explicitly for this purpose and, when combined with a number of &lt;code&gt;querier&lt;/code&gt; instances, can massively
improve rule evaluation performance and lead to fewer missed iterations.&lt;/p&gt;
&lt;p&gt;It is generally recommended to create a separate &lt;code&gt;query-frontend&lt;/code&gt; deployment and &lt;code&gt;querier&lt;/code&gt; pool from your existing one - which handles adhoc
queries via Grafana, &lt;code&gt;logcli&lt;/code&gt;, or the API. Rules should be given priority over adhoc queries because they are used to produce
metrics or alerts which may be crucial to the reliable operation of your service; if you use the same &lt;code&gt;query-frontend&lt;/code&gt; and &lt;code&gt;querier&lt;/code&gt; pool
for both, your rules will be executed with the same priority as adhoc queries which could lead to unpredictable performance.&lt;/p&gt;
&lt;p&gt;To enable remote rule evaluation, set the following configuration options:&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;ruler:
  evaluation:
    mode: remote
    query_frontend:
      address: dns:///&amp;lt;query-frontend-service&amp;gt;:&amp;lt;grpc-port&amp;gt;&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;See &lt;a href=&#34;/configuration/#ruler&#34;&gt;&lt;code&gt;here&lt;/code&gt;&lt;/a&gt; for further configuration options.&lt;/p&gt;
&lt;p&gt;When you enable remote rule evaluation, the &lt;code&gt;ruler&lt;/code&gt; component becomes a gRPC client to the &lt;code&gt;query-frontend&lt;/code&gt; service;
this will result in far lower &lt;code&gt;ruler&lt;/code&gt; resource usage because the majority of the work has been externalized.
The LogQL queries coming from the &lt;code&gt;ruler&lt;/code&gt; will be executed against the given &lt;code&gt;query-frontend&lt;/code&gt; service.
Requests will be load-balanced across all &lt;code&gt;query-frontend&lt;/code&gt; IPs if the &lt;code&gt;dns:///&lt;/code&gt; prefix is used.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Queries that fail to execute are &lt;em&gt;not&lt;/em&gt; retried.&lt;/p&gt;&lt;/blockquote&gt;
&lt;h3 id=&#34;limits--observability&#34;&gt;Limits &amp;amp; Observability&lt;/h3&gt;
&lt;p&gt;Remote rule evaluation can be tuned with the following options:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;ruler_remote_evaluation_timeout&lt;/code&gt;: maximum allowable execution time for rule evaluations&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ruler_remote_evaluation_max_response_size&lt;/code&gt;: maximum allowable response size over gRPC connection from &lt;code&gt;query-frontend&lt;/code&gt; to &lt;code&gt;ruler&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Both of these can be specified globally in the &lt;a href=&#34;/configuration/#limits_config&#34;&gt;&lt;code&gt;limits_config&lt;/code&gt;&lt;/a&gt; section
or on a &lt;a href=&#34;/configuration/#runtime-configuration-file&#34;&gt;per-tenant basis&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Remote rule evaluation exposes a number of metrics:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;loki_ruler_remote_eval_request_duration_seconds&lt;/code&gt;: time taken for rule evaluation (histogram)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;loki_ruler_remote_eval_response_bytes&lt;/code&gt;: number of bytes in rule evaluation response (histogram)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;loki_ruler_remote_eval_response_samples&lt;/code&gt;: number of samples in rule evaluation response (histogram)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;loki_ruler_remote_eval_success_total&lt;/code&gt;: successful rule evaluations (counter)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;loki_ruler_remote_eval_failure_total&lt;/code&gt;: unsuccessful rule evaluations with reasons (counter)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Each of these metrics are per-tenant, so cardinality must be taken into consideration.&lt;/p&gt;
]]></content><description>&lt;h1 id="scalability">Scalability&lt;/h1>
&lt;p>When scaling Loki, operators should consider running several Loki processes
partitioned by role (ingester, distributor, querier) rather than a single Loki
process. Grafana Labs&amp;rsquo; &lt;a href="https://github.com/grafana/loki/blob/main/production/ksonnet/loki" target="_blank" rel="noopener noreferrer">production setup&lt;/a>
contains &lt;code>.libsonnet&lt;/code> files that demonstrates configuring separate components
and scaling for resource usage.&lt;/p></description></item><item><title>Grafana Loki Storage</title><link>https://grafana.com/docs/loki/v2.9.x/operations/storage/</link><pubDate>Thu, 10 Apr 2025 12:15:54 +0000</pubDate><guid>https://grafana.com/docs/loki/v2.9.x/operations/storage/</guid><content><![CDATA[&lt;h1 id=&#34;grafana-loki-storage&#34;&gt;Grafana Loki Storage&lt;/h1&gt;
&lt;p&gt;&lt;a href=&#34;../../storage/&#34;&gt;High level storage overview here&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Grafana Loki needs to store two different types of data: &lt;strong&gt;chunks&lt;/strong&gt; and &lt;strong&gt;indexes&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Loki receives logs in separate streams, where each stream is uniquely identified
by its tenant ID and its set of labels. As log entries from a stream arrive,
they are compressed as &amp;ldquo;chunks&amp;rdquo; and saved in the chunks store. See &lt;a href=&#34;#chunk-format&#34;&gt;chunk
format&lt;/a&gt; for how chunks are stored internally.&lt;/p&gt;
&lt;p&gt;The &lt;strong&gt;index&lt;/strong&gt; stores each stream&amp;rsquo;s label set and links them to the individual
chunks.&lt;/p&gt;
&lt;p&gt;Refer to Loki&amp;rsquo;s &lt;a href=&#34;../../configure/&#34;&gt;configuration&lt;/a&gt; for details on
how to configure the storage and the index.&lt;/p&gt;
&lt;p&gt;For more information:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href=&#34;table-manager/&#34;&gt;Table Manager&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;retention/&#34;&gt;Retention&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;logs-deletion/&#34;&gt;Logs Deletion&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&#34;supported-stores&#34;&gt;Supported Stores&lt;/h2&gt;
&lt;p&gt;The following are supported for the index:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
    &lt;a href=&#34;/docs/loki/v2.9.x/operations/storage/tsdb/&#34;&gt;Single Store (TSDB)&lt;/a&gt; index store which stores TSDB index files in the object store. Recommended store for Loki 2.8 and newer.&lt;/li&gt;
&lt;li&gt;
    &lt;a href=&#34;/docs/loki/v2.9.x/operations/storage/boltdb-shipper/&#34;&gt;Single Store (boltdb-shipper)&lt;/a&gt; index store which stores boltdb index files in the object store. Recommended store for Loki 2.0 through 2.7.x.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://aws.amazon.com/dynamodb&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;Amazon DynamoDB&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://cloud.google.com/bigtable&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;Google Bigtable&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://cassandra.apache.org&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;Apache Cassandra&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/boltdb/bolt&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;BoltDB&lt;/a&gt; (doesn&amp;rsquo;t work when clustering Loki)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The following are supported for the chunks:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://aws.amazon.com/dynamodb&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;Amazon DynamoDB&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://cloud.google.com/bigtable&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;Google Bigtable&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://cassandra.apache.org&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;Apache Cassandra&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://aws.amazon.com/s3&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;Amazon S3&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://cloud.google.com/storage/&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;Google Cloud Storage&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;filesystem/&#34;&gt;Filesystem&lt;/a&gt; (please read more about the filesystem to understand the pros/cons before using with production data)&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://cloud.baidu.com/product/bos.html&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;Baidu Object Storage&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://www.ibm.com/cloud/object-storage&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;IBM Cloud Object Storage&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;cloud-storage-permissions&#34;&gt;Cloud Storage Permissions&lt;/h2&gt;
&lt;h3 id=&#34;s3&#34;&gt;S3&lt;/h3&gt;
&lt;p&gt;When using S3 as object storage, the following permissions are needed:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;s3:ListBucket&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;s3:PutObject&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;s3:GetObject&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;s3:DeleteObject&lt;/code&gt; (if running the Single Store (boltdb-shipper) compactor)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Resources: &lt;code&gt;arn:aws:s3:::&amp;lt;bucket_name&amp;gt;&lt;/code&gt;, &lt;code&gt;arn:aws:s3:::&amp;lt;bucket_name&amp;gt;/*&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;See the &lt;a href=&#34;../../storage/#aws-deployment-s3-single-store&#34;&gt;AWS deployment section&lt;/a&gt; on the storage page for a detailed setup guide.&lt;/p&gt;
&lt;h3 id=&#34;dynamodb&#34;&gt;DynamoDB&lt;/h3&gt;
&lt;p&gt;When using DynamoDB for the index, the following permissions are needed:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;dynamodb:BatchGetItem&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;dynamodb:BatchWriteItem&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;dynamodb:DeleteItem&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;dynamodb:DescribeTable&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;dynamodb:GetItem&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;dynamodb:ListTagsOfResource&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;dynamodb:PutItem&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;dynamodb:Query&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;dynamodb:TagResource&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;dynamodb:UntagResource&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;dynamodb:UpdateItem&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;dynamodb:UpdateTable&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;dynamodb:CreateTable&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;dynamodb:DeleteTable&lt;/code&gt; (if &lt;code&gt;table_manager.retention_period&lt;/code&gt; is more than 0s)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Resources: &lt;code&gt;arn:aws:dynamodb:&amp;lt;aws_region&amp;gt;:&amp;lt;aws_account_id&amp;gt;:table/&amp;lt;prefix&amp;gt;*&lt;/code&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;dynamodb:ListTables&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Resources: &lt;code&gt;*&lt;/code&gt;&lt;/p&gt;
&lt;h4 id=&#34;autoscaling&#34;&gt;AutoScaling&lt;/h4&gt;
&lt;p&gt;If you enable autoscaling from table manager, the following permissions are needed:&lt;/p&gt;
&lt;h5 id=&#34;application-autoscaling&#34;&gt;Application Autoscaling&lt;/h5&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;application-autoscaling:DescribeScalableTargets&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;application-autoscaling:DescribeScalingPolicies&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;application-autoscaling:RegisterScalableTarget&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;application-autoscaling:DeregisterScalableTarget&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;application-autoscaling:PutScalingPolicy&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;application-autoscaling:DeleteScalingPolicy&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Resources: &lt;code&gt;*&lt;/code&gt;&lt;/p&gt;
&lt;h5 id=&#34;iam&#34;&gt;IAM&lt;/h5&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;iam:GetRole&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;iam:PassRole&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Resources: &lt;code&gt;arn:aws:iam::&amp;lt;aws_account_id&amp;gt;:role/&amp;lt;role_name&amp;gt;&lt;/code&gt;&lt;/p&gt;
&lt;h3 id=&#34;ibm-cloud-object-storage&#34;&gt;IBM Cloud Object Storage&lt;/h3&gt;
&lt;p&gt;When using IBM Cloud Object Storage (COS) as object storage, IAM &lt;code&gt;Writer&lt;/code&gt; role is needed.&lt;/p&gt;
&lt;p&gt;See the &lt;a href=&#34;../../storage/#ibm-cloud-object-storage-cos&#34;&gt;IBM Cloud Object Storage section&lt;/a&gt; on the storage page for a detailed setup guide.&lt;/p&gt;
&lt;h2 id=&#34;chunk-format&#34;&gt;Chunk Format&lt;/h2&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;  -------------------------------------------------------------------
  |                               |                                 |
  |        MagicNumber(4b)        |           version(1b)           |
  |                               |                                 |
  -------------------------------------------------------------------
  |         block-1 bytes         |          checksum (4b)          |
  -------------------------------------------------------------------
  |         block-2 bytes         |          checksum (4b)          |
  -------------------------------------------------------------------
  |         block-n bytes         |          checksum (4b)          |
  -------------------------------------------------------------------
  |                        #blocks (uvarint)                        |
  -------------------------------------------------------------------
  | #entries(uvarint) | mint, maxt (varint) | offset, len (uvarint) |
  -------------------------------------------------------------------
  | #entries(uvarint) | mint, maxt (varint) | offset, len (uvarint) |
  -------------------------------------------------------------------
  | #entries(uvarint) | mint, maxt (varint) | offset, len (uvarint) |
  -------------------------------------------------------------------
  | #entries(uvarint) | mint, maxt (varint) | offset, len (uvarint) |
  -------------------------------------------------------------------
  |                      checksum(from #blocks)                     |
  -------------------------------------------------------------------
  |           metasOffset - offset to the point with #blocks        |
  -------------------------------------------------------------------&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
]]></content><description>&lt;h1 id="grafana-loki-storage">Grafana Loki Storage&lt;/h1>
&lt;p>&lt;a href="../../storage/">High level storage overview here&lt;/a>&lt;/p>
&lt;p>Grafana Loki needs to store two different types of data: &lt;strong>chunks&lt;/strong> and &lt;strong>indexes&lt;/strong>.&lt;/p>
&lt;p>Loki receives logs in separate streams, where each stream is uniquely identified
by its tenant ID and its set of labels. As log entries from a stream arrive,
they are compressed as &amp;ldquo;chunks&amp;rdquo; and saved in the chunks store. See &lt;a href="#chunk-format">chunk
format&lt;/a> for how chunks are stored internally.&lt;/p></description></item><item><title>Multi-tenancy</title><link>https://grafana.com/docs/loki/v2.9.x/operations/multi-tenancy/</link><pubDate>Wed, 06 Sep 2023 12:47:18 +0000</pubDate><guid>https://grafana.com/docs/loki/v2.9.x/operations/multi-tenancy/</guid><content><![CDATA[&lt;h1 id=&#34;multi-tenancy&#34;&gt;Multi-tenancy&lt;/h1&gt;
&lt;p&gt;Grafana Loki is a multi-tenant system; requests and data for tenant A are isolated from
tenant B. Requests to the Loki API should include an HTTP header
(&lt;code&gt;X-Scope-OrgID&lt;/code&gt;) that identifies the tenant for the request.&lt;/p&gt;
&lt;p&gt;Tenant IDs can be any alphanumeric string that fits within the Go HTTP header
limit (1MB). Operators are recommended to use a reasonable limit for uniquely
identifying tenants; 20 bytes is usually enough.&lt;/p&gt;
&lt;p&gt;Loki defaults to running in multi-tenant mode.
Multi-tenant mode is set in the configuration with &lt;code&gt;auth_enabled: true&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;When configured with &lt;code&gt;auth_enabled: false&lt;/code&gt;, Loki uses a single tenant.
The &lt;code&gt;X-Scope-OrgID&lt;/code&gt; header is not required in Loki API requests.
The single tenant ID will be the string &lt;code&gt;fake&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&#34;multi-tenant-queries&#34;&gt;Multi-tenant Queries&lt;/h2&gt;
&lt;p&gt;In multi-tenant mode, queries may gather results from multiple tenants.
Set the querier configuration option &lt;code&gt;multi_tenant_queries_enabled: true&lt;/code&gt; to enable queries across tenants.
The query API request defines the tenants.
Specify multiple tenants
in the query request HTTP header &lt;code&gt;X-Scope-OrgID&lt;/code&gt; by separating the tenant IDs with the pipe character (&lt;code&gt;|&lt;/code&gt;).
For example, a query for tenants &lt;code&gt;A&lt;/code&gt; and &lt;code&gt;B&lt;/code&gt; requires the header &lt;code&gt;X-Scope-OrgID: A|B&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Only query endpoints support multi-tenant calls.
Calls to &lt;code&gt;GET /loki/api/v1/tail&lt;/code&gt; and &lt;code&gt;POST /loki/api/v1/push&lt;/code&gt; will return an HTTP 400 error if more than one tenant is defined in the HTTP header.&lt;/p&gt;
&lt;p&gt;Instant and range queries support label filtering using tenant IDs.
For example, the query&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;{app=&amp;#34;foo&amp;#34;, __tenant_id__=~&amp;#34;a.&amp;#43;&amp;#34;} | logfmt&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;will return results for all tenants
that have a tenant ID that begins with the character &lt;code&gt;a&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;If the label &lt;code&gt;__tenant_id__&lt;/code&gt; is already present in a log stream, it is prepended with the string &lt;code&gt;original_&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Tenant ID filtering in stages is not supported.
An example of a query that will &lt;em&gt;not&lt;/em&gt; work:&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;{app=&amp;#34;foo&amp;#34;} | __tenant_id__=&amp;#34;1&amp;#34; | logfmt&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
]]></content><description>&lt;h1 id="multi-tenancy">Multi-tenancy&lt;/h1>
&lt;p>Grafana Loki is a multi-tenant system; requests and data for tenant A are isolated from
tenant B. Requests to the Loki API should include an HTTP header
(&lt;code>X-Scope-OrgID&lt;/code>) that identifies the tenant for the request.&lt;/p></description></item><item><title>Blocking Queries</title><link>https://grafana.com/docs/loki/v2.9.x/operations/blocking-queries/</link><pubDate>Thu, 10 Apr 2025 12:15:54 +0000</pubDate><guid>https://grafana.com/docs/loki/v2.9.x/operations/blocking-queries/</guid><content><![CDATA[&lt;h1 id=&#34;blocking-queries&#34;&gt;Blocking Queries&lt;/h1&gt;
&lt;p&gt;In certain situations, you may not be able to control the queries being sent to your Loki installation. These queries
may be intentionally or unintentionally expensive to run, and they may affect the overall stability or cost of running
your service.&lt;/p&gt;
&lt;p&gt;You can block queries using 
    &lt;a href=&#34;/docs/loki/v2.9.x/configure/#runtime-configuration-file&#34;&gt;per-tenant overrides&lt;/a&gt;, like so:&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:
  &amp;#34;tenant-id&amp;#34;:
    blocked_queries:
      # block this query exactly
      - pattern: &amp;#39;sum(rate({env=&amp;#34;prod&amp;#34;}[1m]))&amp;#39;

      # block any query matching this regex pattern 
      - pattern: &amp;#39;.*prod.*&amp;#39;
        regex: true

      # block all metric queries
      - types: metric

      # block any filter or limited queries matching this regex pattern 
      - pattern: &amp;#39;.*prod.*&amp;#39;
        regex: true
        types: filter,limited

      # block any query that matches this query hash
      - hash: 2943214005          # hash of {stream=&amp;#34;stdout&amp;#34;,pod=&amp;#34;loki-canary-9w49x&amp;#34;}
        types: filter,limited&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;Changes to these configurations &lt;strong&gt;do not require a restart&lt;/strong&gt;; they are defined in the 
    &lt;a href=&#34;/docs/loki/v2.9.x/configure/#runtime-configuration-file&#34;&gt;runtime configuration file&lt;/a&gt;.&lt;/p&gt;&lt;/blockquote&gt;&lt;/div&gt;

&lt;p&gt;The available query types are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;metric&lt;/code&gt;: a query with an aggregation, e.g. &lt;code&gt;sum(rate({env=&amp;quot;prod&amp;quot;}[1m]))&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;filter&lt;/code&gt;: a query with a log filter, e.g. &lt;code&gt;{env=&amp;quot;prod&amp;quot;} |= &amp;quot;error&amp;quot;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;limited&lt;/code&gt;: a query without a filter or a metric aggregation&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The &lt;code&gt;hash&lt;/code&gt; option uses a &lt;a href=&#34;https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;32-bit FNV-1&lt;/a&gt; hash of the query string, represented as a 32-bit unsigned integer.
This can often be easier to use than query strings that are long or require lots of string escaping. A &lt;code&gt;query_hash&lt;/code&gt; field
is logged with every query request in the &lt;code&gt;query-frontend&lt;/code&gt; and &lt;code&gt;querier&lt;/code&gt; logs, for easy reference. Here&amp;rsquo;s an example log line:&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;logfmt&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-logfmt&#34;&gt;level=info ts=2023-03-30T09:08:15.2614555Z caller=metrics.go:152 component=frontend org_id=29 latency=fast 
query=&amp;#34;{stream=\&amp;#34;stdout\&amp;#34;,pod=\&amp;#34;loki-canary-9w49x\&amp;#34;}&amp;#34; query_hash=2943214005 query_type=limited range_type=range ...&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; the order of patterns is preserved, so the first matching pattern will be used&lt;/p&gt;
&lt;h2 id=&#34;observing-blocked-queries&#34;&gt;Observing blocked queries&lt;/h2&gt;
&lt;p&gt;Blocked queries are logged, as well as counted in the &lt;code&gt;loki_blocked_queries&lt;/code&gt; metric on a per-tenant basis.&lt;/p&gt;
&lt;h2 id=&#34;scope&#34;&gt;Scope&lt;/h2&gt;
&lt;p&gt;Queries received via the API and executed as &lt;a href=&#34;../../alert/&#34;&gt;alerting/recording rules&lt;/a&gt; will be blocked.&lt;/p&gt;
]]></content><description>&lt;h1 id="blocking-queries">Blocking Queries&lt;/h1>
&lt;p>In certain situations, you may not be able to control the queries being sent to your Loki installation. These queries
may be intentionally or unintentionally expensive to run, and they may affect the overall stability or cost of running
your service.&lt;/p></description></item><item><title>Loki Canary</title><link>https://grafana.com/docs/loki/v2.9.x/operations/loki-canary/</link><pubDate>Mon, 18 Mar 2024 22:57:01 +0000</pubDate><guid>https://grafana.com/docs/loki/v2.9.x/operations/loki-canary/</guid><content><![CDATA[&lt;h1 id=&#34;loki-canary&#34;&gt;Loki Canary&lt;/h1&gt;
&lt;p&gt;Loki Canary is a standalone app that audits the log-capturing performance of
a Grafana Loki cluster.&lt;/p&gt;
&lt;p&gt;Loki Canary generates artificial log lines.
These log lines are sent to the Loki cluster.
Loki Canary communicates with the Loki cluster to capture metrics about the
artificial log lines,
such that Loki Canary forms information about the performance of the
Loki cluster.
The information is available as Prometheus time series metrics.&lt;/p&gt;
&lt;p&gt;&lt;img
  class=&#34;lazyload d-inline-block&#34;
  data-src=&#34;./loki-canary-block.png&#34;
  alt=&#34;block_diagram&#34;/&gt;&lt;/p&gt;
&lt;p&gt;Loki Canary writes a log to a file and stores the timestamp in an internal
array. The contents look something like this:&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;nohighlight&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-nohighlight&#34;&gt;1557935669096040040 ppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;The relevant part of the log entry is the timestamp; the &lt;code&gt;p&lt;/code&gt;s are just filler
bytes to make the size of the log configurable.&lt;/p&gt;
&lt;p&gt;An agent (like Promtail) should be configured to read the log file and ship it
to Loki.&lt;/p&gt;
&lt;p&gt;Meanwhile, Loki Canary will open a WebSocket connection to Loki and will tail
the logs it creates. When a log is received on the WebSocket, the timestamp
in the log message is compared to the internal array.&lt;/p&gt;
&lt;p&gt;If the received log is:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The next in the array to be received, it is removed from the array and the
(current time - log timestamp) is recorded in the &lt;code&gt;response_latency&lt;/code&gt;
histogram. This is the expected behavior for well behaving logs.&lt;/li&gt;
&lt;li&gt;Not the next in the array to be received, it is removed from the array, the
response time is recorded in the &lt;code&gt;response_latency&lt;/code&gt; histogram, and the
&lt;code&gt;out_of_order_entries&lt;/code&gt; counter is incremented.&lt;/li&gt;
&lt;li&gt;Not in the array at all, it is checked against a separate list of received
logs to either increment the &lt;code&gt;duplicate_entries&lt;/code&gt; counter or the
&lt;code&gt;unexpected_entries&lt;/code&gt; counter.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In the background, Loki Canary also runs a timer which iterates through all of
the entries in the internal array. If any of the entries are older than the
duration specified by the &lt;code&gt;-wait&lt;/code&gt; flag (defaulting to 60s), they are removed
from the array and the &lt;code&gt;websocket_missing_entries&lt;/code&gt; counter is incremented. An
additional query is then made directly to Loki for any missing entries to
determine if they are truly missing or only missing from the WebSocket. If
missing entries are not found in the direct query, the &lt;code&gt;missing_entries&lt;/code&gt; counter
is incremented.&lt;/p&gt;
&lt;h3 id=&#34;additional-queries&#34;&gt;Additional Queries&lt;/h3&gt;
&lt;h4 id=&#34;spot-check&#34;&gt;Spot Check&lt;/h4&gt;
&lt;p&gt;Starting with version 1.6.0, the canary will spot check certain results over time
to make sure they are present in Loki, this is helpful for testing the transition
of inmemory logs in the ingester to the store to make sure nothing is lost.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;-spot-check-interval&lt;/code&gt; and &lt;code&gt;-spot-check-max&lt;/code&gt; are used to tune this feature,
&lt;code&gt;-spot-check-interval&lt;/code&gt; will pull a log entry from the stream at this interval
and save it in a separate list up to &lt;code&gt;-spot-check-max&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Every &lt;code&gt;-spot-check-query-rate&lt;/code&gt;, Loki will be queried for each entry in this list and
&lt;code&gt;loki_canary_spot_check_entries_total&lt;/code&gt; will be incremented, if a result
is missing &lt;code&gt;loki_canary_spot_check_missing_entries_total&lt;/code&gt; will be incremented.&lt;/p&gt;
&lt;p&gt;The defaults of &lt;code&gt;15m&lt;/code&gt; for &lt;code&gt;spot-check-interval&lt;/code&gt; and &lt;code&gt;4h&lt;/code&gt; for &lt;code&gt;spot-check-max&lt;/code&gt;
means that after 4 hours of running the canary will have a list of 16 entries
it will query every minute (default &lt;code&gt;spot-check-query-rate&lt;/code&gt; interval is 1m),
so be aware of the query load this can put on Loki if you have a lot of canaries.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;NOTE:&lt;/strong&gt; if you are using &lt;code&gt;out-of-order-percentage&lt;/code&gt; to test ingestion of out-of-order
log lines be sure not to set the two out of order time range flags too far in the past.
The defaults are already enough to test this functionality properly, and setting them
too far in the past can cause issues with the spot check test.&lt;/p&gt;
&lt;p&gt;When using &lt;code&gt;out-of-order-percentage&lt;/code&gt; you also need to make use of pipeline stages
in your Promtail configuration in order to set the timestamps correctly as the logs are pushed
to Loki. The &lt;code&gt;client/promtail/pipelines&lt;/code&gt; docs have examples of how to do this.&lt;/p&gt;
&lt;h4 id=&#34;metric-test&#34;&gt;Metric Test&lt;/h4&gt;
&lt;p&gt;Loki Canary will run a metric query &lt;code&gt;count_over_time&lt;/code&gt; to
verify that the rate of logs being stored in Loki corresponds to the rate they are being
created by Loki Canary.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;-metric-test-interval&lt;/code&gt; and &lt;code&gt;-metric-test-range&lt;/code&gt; are used to tune this feature, but
by default every &lt;code&gt;15m&lt;/code&gt; the canary will run a &lt;code&gt;count_over_time&lt;/code&gt; instant-query to Loki
for a range of &lt;code&gt;24h&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;If the canary has not run for &lt;code&gt;-metric-test-range&lt;/code&gt; (&lt;code&gt;24h&lt;/code&gt;) the query range is adjusted
to the amount of time the canary has been running such that the rate can be calculated
since the canary was started.&lt;/p&gt;
&lt;p&gt;The canary calculates what the expected count of logs would be for the range
(also adjusting this based on canary runtime) and compares the expected result with
the actual result returned from Loki.  The &lt;em&gt;difference&lt;/em&gt; is stored as the value in
the gauge &lt;code&gt;loki_canary_metric_test_deviation&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s expected that there will be some deviation, the method of creating an expected
calculation based on the query rate compared to actual query data is imperfect
and will lead to a deviation of a few log entries.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s not expected for there to be a deviation of more than 3-4 log entries.&lt;/p&gt;
&lt;h3 id=&#34;control&#34;&gt;Control&lt;/h3&gt;
&lt;p&gt;Loki Canary responds to two endpoints to allow dynamic suspending/resuming of the
canary process.  This can be useful if you&amp;rsquo;d like to quickly disable or reenable the
canary.  To stop or start the canary issue an HTTP GET request against the &lt;code&gt;/suspend&lt;/code&gt; or
&lt;code&gt;/resume&lt;/code&gt; endpoints.&lt;/p&gt;
&lt;h2 id=&#34;installation&#34;&gt;Installation&lt;/h2&gt;
&lt;h3 id=&#34;binary&#34;&gt;Binary&lt;/h3&gt;
&lt;p&gt;Loki Canary is provided as a pre-compiled binary as part of the
&lt;a href=&#34;https://github.com/grafana/loki/releases&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;Loki Releases&lt;/a&gt; on GitHub.&lt;/p&gt;
&lt;h3 id=&#34;docker&#34;&gt;Docker&lt;/h3&gt;
&lt;p&gt;Loki Canary is also provided as a Docker container image:&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;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;# change tag to the most recent release
$ docker pull grafana/loki-canary:2.9.4&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;h3 id=&#34;kubernetes&#34;&gt;Kubernetes&lt;/h3&gt;
&lt;p&gt;To run on Kubernetes, you can do something simple like:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;kubectl run loki-canary --generator=run-pod/v1 --image=grafana/loki-canary:latest --restart=Never --image-pull-policy=IfNotPresent --labels=name=loki-canary -- -addr=loki:3100&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Or you can do something more complex like deploy it as a DaemonSet, there is a
Tanka setup for this in the &lt;code&gt;production&lt;/code&gt; folder, you can import it using
&lt;code&gt;jsonnet-bundler&lt;/code&gt;:&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;shell&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-shell&#34;&gt;jb install github.com/grafana/loki-canary/production/ksonnet/loki-canary&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Then in your Tanka environment&amp;rsquo;s &lt;code&gt;main.jsonnet&lt;/code&gt; you&amp;rsquo;ll want something like
this:&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;jsonnet&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-jsonnet&#34;&gt;local loki_canary = import &amp;#39;loki-canary/loki-canary.libsonnet&amp;#39;;

loki_canary {
  loki_canary_args&amp;#43;:: {
    addr: &amp;#34;loki:3100&amp;#34;,
    port: 80,
    labelname: &amp;#34;instance&amp;#34;,
    interval: &amp;#34;100ms&amp;#34;,
    size: 1024,
    wait: &amp;#34;3m&amp;#34;,
  },
  _config&amp;#43;:: {
    namespace: &amp;#34;default&amp;#34;,
  }
}&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;h4 id=&#34;examples&#34;&gt;Examples&lt;/h4&gt;
&lt;p&gt;Standalone Pod Implementation of loki-canary&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;---
apiVersion: v1
kind: Pod
metadata:
  labels:
    app: loki-canary
    name: loki-canary
  name: loki-canary
spec:
  containers:
  - args:
    - -addr=loki:3100
    image: grafana/loki-canary:latest
    imagePullPolicy: IfNotPresent
    name: loki-canary
    resources: {}
---
apiVersion: v1
kind: Service
metadata:
  name: loki-canary
  labels:
    app: loki-canary
spec:
  type: ClusterIP
  selector:
    app: loki-canary
  ports:
  - name: metrics
    protocol: TCP
    port: 3500
    targetPort: 3500&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;DaemonSet Implementation of loki-canary&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;---
kind: DaemonSet
apiVersion: extensions/v1beta1
metadata:
  labels:
    app: loki-canary
    name: loki-canary
  name: loki-canary
spec:
  template:
    metadata:
      name: loki-canary
      labels:
        app: loki-canary
    spec:
      containers:
      - args:
        - -addr=loki:3100
        image: grafana/loki-canary:latest
        imagePullPolicy: IfNotPresent
        name: loki-canary
        resources: {}
---
apiVersion: v1
kind: Service
metadata:
  name: loki-canary
  labels:
    app: loki-canary
spec:
  type: ClusterIP
  selector:
    app: loki-canary
  ports:
  - name: metrics
    protocol: TCP
    port: 3500
    targetPort: 3500&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;h3 id=&#34;from-source&#34;&gt;From Source&lt;/h3&gt;
&lt;p&gt;If the other options are not sufficient for your use case, you can compile
&lt;code&gt;loki-canary&lt;/code&gt; yourself:&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;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;# clone the source tree
$ git clone https://github.com/grafana/loki

# build the binary
$ make loki-canary

# (optionally build the container image)
$ make loki-canary-image&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;h2 id=&#34;configuration&#34;&gt;Configuration&lt;/h2&gt;
&lt;p&gt;The address of Loki must be passed in with the &lt;code&gt;-addr&lt;/code&gt; flag or by setting the
environment variable &lt;code&gt;LOKI_ADDRESS&lt;/code&gt;, and if your Loki server uses TLS, &lt;code&gt;-tls=true&lt;/code&gt;
must also be provided. Note that using TLS will cause the WebSocket connection
to use &lt;code&gt;wss://&lt;/code&gt; instead of &lt;code&gt;ws://&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;-labelname&lt;/code&gt; and &lt;code&gt;-labelvalue&lt;/code&gt; flags should also be provided, as these are
used by Loki Canary to filter the log stream to only process logs for the
current instance of the canary. Ensure that the values provided to the flags are
unique to each instance of Loki Canary. Grafana Labs&amp;rsquo; Tanka config
accomplishes this by passing in the Pod name as the label value.&lt;/p&gt;
&lt;p&gt;If Loki Canary reports a high number of &lt;code&gt;unexpected_entries&lt;/code&gt;, Loki Canary may
not be waiting long enough and the value for the &lt;code&gt;-wait&lt;/code&gt; flag should be
increased to a larger value than 60s.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Be aware&lt;/strong&gt; of the relationship between &lt;code&gt;pruneinterval&lt;/code&gt; and the &lt;code&gt;interval&lt;/code&gt;.
For example, with an interval of 10ms (100 logs per second) and a prune interval
of 60s, you will write 6000 logs per minute. If those logs were not received
over the WebSocket, the canary will attempt to query Loki directly to see if
they are completely lost. &lt;strong&gt;However&lt;/strong&gt; the query return is limited to 1000
results so you will not be able to return all the logs even if they did make it
to Loki.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Likewise&lt;/strong&gt;, if you lower the &lt;code&gt;pruneinterval&lt;/code&gt; you risk causing a denial of
service attack as all your canaries attempt to query for missing logs at
whatever your &lt;code&gt;pruneinterval&lt;/code&gt; is defined at.&lt;/p&gt;
&lt;p&gt;All options:&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;  -addr string
    	The Loki server URL:Port, e.g. loki:3100. Loki address can also be set using the environment variable LOKI_ADDRESS.
  -buckets int
    	Number of buckets in the response_latency histogram (default 10)
  -ca-file string
    	Client certificate authority for optional use with TLS connection to Loki
  -cert-file string
    	Client PEM encoded X.509 certificate for optional use with TLS connection to Loki
  -insecure
    	Allow insecure TLS connections
  -interval duration
    	Duration between log entries (default 1s)
  -key-file string
    	Client PEM encoded X.509 key for optional use with TLS connection to Loki
  -labelname string
    	The label name for this instance of loki-canary to use in the log selector (default &amp;#34;name&amp;#34;)
  -labelvalue string
    	The unique label value for this instance of loki-canary to use in the log selector (default &amp;#34;loki-canary&amp;#34;)
  -max-wait duration
    	Duration to keep querying Loki for missing websocket entries before reporting them missing (default 5m0s)
  -metric-test-interval duration
    	The interval the metric test query should be run (default 1h0m0s)
  -metric-test-range duration
    	The range value [24h] used in the metric test instant-query. Note: this value is truncated to the running time of the canary until this value is reached (default 24h0m0s)
  -out-of-order-max duration
    	Maximum amount of time to go back for out of order entries (in seconds). (default 1m0s)
  -out-of-order-min duration
    	Minimum amount of time to go back for out of order entries (in seconds). (default 30s)
  -out-of-order-percentage int
    	Percentage (0-100) of log entries that should be sent out of order.
  -pass string
    	Loki password. This credential should have both read and write permissions to Loki endpoints
  -port int
    	Port which loki-canary should expose metrics (default 3500)
  -pruneinterval duration
    	Frequency to check sent vs received logs, also the frequency which queries for missing logs will be dispatched to loki (default 1m0s)
  -push
    	Push the logs directly to given Loki address
  -query-timeout duration
    	How long to wait for a query response from Loki (default 10s)
  -size int
    	Size in bytes of each log line (default 100)
  -spot-check-initial-wait duration
    	How long should the spot check query wait before starting to check for entries (default 10s)
  -spot-check-interval duration
    	Interval that a single result will be kept from sent entries and spot-checked against Loki, e.g. 15min default one entry every 15 min will be saved and then queried again every 15min until spot-check-max is reached (default 15m0s)
  -spot-check-max duration
    	How far back to check a spot check entry before dropping it (default 4h0m0s)
  -spot-check-query-rate duration
    	Interval that the canary will query Loki for the current list of all spot check entries (default 1m0s)
  -streamname string
    	The stream name for this instance of loki-canary to use in the log selector (default &amp;#34;stream&amp;#34;)
  -streamvalue string
    	The unique stream value for this instance of loki-canary to use in the log selector (default &amp;#34;stdout&amp;#34;)
  -tenant-id string
    	Tenant ID to be set in X-Scope-OrgID header.
  -tls
    	Does the loki connection use TLS?
  -user string
    	Loki username.
  -version
    	Print this builds version information
  -wait duration
    	Duration to wait for log entries on websocket before querying loki for them (default 1m0s)
  -write-max-backoff duration
    	Maximum backoff time between retries  (default 5m0s)
  -write-max-retries int
    	Maximum number of retries when push a log entry  (default 10)
  -write-min-backoff duration
    	Initial backoff time before first retry  (default 500ms)
  -write-timeout duration
    	How long to wait write response from Loki (default 10s)&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
]]></content><description>&lt;h1 id="loki-canary">Loki Canary&lt;/h1>
&lt;p>Loki Canary is a standalone app that audits the log-capturing performance of
a Grafana Loki cluster.&lt;/p>
&lt;p>Loki Canary generates artificial log lines.
These log lines are sent to the Loki cluster.
Loki Canary communicates with the Loki cluster to capture metrics about the
artificial log lines,
such that Loki Canary forms information about the performance of the
Loki cluster.
The information is available as Prometheus time series metrics.&lt;/p></description></item><item><title>Troubleshooting</title><link>https://grafana.com/docs/loki/v2.9.x/operations/troubleshooting/</link><pubDate>Thu, 10 Apr 2025 12:15:54 +0000</pubDate><guid>https://grafana.com/docs/loki/v2.9.x/operations/troubleshooting/</guid><content><![CDATA[&lt;h1 id=&#34;troubleshooting&#34;&gt;Troubleshooting&lt;/h1&gt;
&lt;h2 id=&#34;loki-bad-gateway-502&#34;&gt;&amp;ldquo;Loki: Bad Gateway. 502&amp;rdquo;&lt;/h2&gt;
&lt;p&gt;This error can appear in Grafana when Grafana Loki is added as a
datasource, indicating that Grafana in unable to connect to Loki. There may
one of many root causes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;If Loki is deployed with Docker, and Grafana and Loki are not running in the
same node, check your firewall to make sure the nodes can connect.&lt;/li&gt;
&lt;li&gt;If Loki is deployed with Kubernetes:
&lt;ul&gt;
&lt;li&gt;If Grafana and Loki are in the same namespace, set the Loki URL as
&lt;code&gt;http://$LOKI_SERVICE_NAME:$LOKI_PORT&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Otherwise, set the Loki URL as
&lt;code&gt;http://$LOKI_SERVICE_NAME.$LOKI_NAMESPACE:$LOKI_PORT&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;data-source-connected-but-no-labels-received-verify-that-loki-and-promtail-is-configured-properly&#34;&gt;&amp;ldquo;Data source connected, but no labels received. Verify that Loki and Promtail is configured properly.&amp;rdquo;&lt;/h2&gt;
&lt;p&gt;This error can appear in Grafana when Loki is added as a datasource, indicating
that although Grafana has connected to Loki, Loki hasn&amp;rsquo;t received any logs from
Promtail yet. There may be one of many root causes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Promtail is running and collecting logs but is unable to connect to Loki to
send the logs. Check Promtail&amp;rsquo;s output.&lt;/li&gt;
&lt;li&gt;Promtail started sending logs to Loki before Loki was ready. This can
happen in test environment where Promtail has already read all logs and sent
them off. Here is what you can do:
&lt;ul&gt;
&lt;li&gt;Start Promtail after Loki, e.g., 60 seconds later.&lt;/li&gt;
&lt;li&gt;To force Promtail to re-send log messages, delete the positions file
(default location &lt;code&gt;/tmp/positions.yaml&lt;/code&gt;).&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Promtail is ignoring targets and isn&amp;rsquo;t reading any logs because of a
configuration issue.
&lt;ul&gt;
&lt;li&gt;This can be detected by turning on debug logging in Promtail and looking
for &lt;code&gt;dropping target, no labels&lt;/code&gt; or &lt;code&gt;ignoring target&lt;/code&gt; messages.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Promtail cannot find the location of your log files. Check that the
&lt;code&gt;scrape_configs&lt;/code&gt; contains valid path settings for finding the logs on your
worker nodes.&lt;/li&gt;
&lt;li&gt;Your pods are running with different labels than the ones Promtail is
configured to read. Check &lt;code&gt;scrape_configs&lt;/code&gt; to validate.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;loki-timeout-errors&#34;&gt;Loki timeout errors&lt;/h2&gt;
&lt;p&gt;Loki 504 errors, context canceled, and error processing requests
can have many possible causes.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Review Loki configuration&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Loki configuration &lt;code&gt;limits_config.query_timeout&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;server.http_server_read_timeout&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;server.http_server_write_timeout&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;server.http_server_idle_timeout&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Check your Loki deployment.
If you have a reverse proxy in front of Loki, that is, between Loki and Grafana, then check any configured timeouts, such as an NGINX proxy read timeout.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Other causes.  To determine if the issue is related to Loki itself or another system such as Grafana or a client-side error,
attempt to run a &lt;a href=&#34;../../query/logcli/&#34;&gt;LogCLI&lt;/a&gt; query in as direct a manner as you can. For example, if running on virtual machines, run the query on the local machine. If running in a Kubernetes cluster, then port forward the Loki HTTP port, and attempt to run the query there. If you do not get a timeout, then consider these causes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Adjust the &lt;a href=&#34;/docs/grafana/latest/administration/configuration/#dataproxy&#34;&gt;Grafana dataproxy timeout&lt;/a&gt;. Configure Grafana with a large enough dataproxy timeout.&lt;/li&gt;
&lt;li&gt;Check timeouts for reverse proxies or load balancers between your client and Grafana. Queries to Grafana are made from the your local browser with Grafana serving as a proxy (a dataproxy). Therefore, connections from your client to Grafana must have their timeout configured as well.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;cache-generation-errors&#34;&gt;Cache Generation errors&lt;/h2&gt;
&lt;p&gt;Loki cache generation number errors(Loki &amp;gt;= 2.6)&lt;/p&gt;
&lt;h3 id=&#34;error-loading-cache-generation-numbers&#34;&gt;error loading cache generation numbers&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Symptom:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Loki exposed errors on log with &lt;code&gt;msg=&amp;quot;error loading cache generation numbers&amp;quot; err=&amp;quot;unexpected status code: 403&amp;quot;&lt;/code&gt; or &lt;code&gt;msg=&amp;quot;error getting cache gen numbers from the store&amp;quot;&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Investigation:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Check the metric &lt;code&gt;loki_delete_cache_gen_load_failures_total&lt;/code&gt; on &lt;code&gt;/metrics&lt;/code&gt;, which is an indicator for the occurrence of the problem. If the value is greater than 1, it means that there is a problem with that component.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Try Http GET request to route: /loki/api/v1/cache/generation_numbers&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;If response is equal as &lt;code&gt;&amp;quot;deletion is not available for this tenant&amp;quot;&lt;/code&gt;, this means the deletion API is not enabled for the tenant. To enable this api, set &lt;code&gt;allow_deletes: true&lt;/code&gt; for this tenant via the configuration settings. Check more docs: /docs/loki/&amp;lt;LOKI_VERSION&amp;gt;/operations/storage/logs-deletion/&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;troubleshooting-targets&#34;&gt;Troubleshooting targets&lt;/h2&gt;
&lt;p&gt;Promtail exposes two web pages that can be used to understand how its service
discovery works.&lt;/p&gt;
&lt;p&gt;The service discovery page (&lt;code&gt;/service-discovery&lt;/code&gt;) shows all
discovered targets with their labels before and after relabeling as well as
the reason why the target has been dropped.&lt;/p&gt;
&lt;p&gt;The targets page (&lt;code&gt;/targets&lt;/code&gt;) displays only targets that are being actively
scraped and their respective labels, files, and positions.&lt;/p&gt;
&lt;p&gt;On Kubernetes, you can access those two pages by port-forwarding the Promtail
port (&lt;code&gt;9080&lt;/code&gt; or &lt;code&gt;3101&lt;/code&gt; if using Helm) locally:&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;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;$ kubectl port-forward loki-promtail-jrfg7 9080
# Then, in a web browser, visit http://localhost:9080/service-discovery&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;h2 id=&#34;debug-output&#34;&gt;Debug output&lt;/h2&gt;
&lt;p&gt;Both Loki and Promtail support a log level flag with the addition of
a command-line option:&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;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;loki -log.level=debug&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;promtail -log.level=debug&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;h2 id=&#34;failed-to-create-target-ioutilreaddir-readdirent-not-a-directory&#34;&gt;Failed to create target, &lt;code&gt;ioutil.ReadDir: readdirent: not a directory&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;The Promtail configuration contains a &lt;code&gt;__path__&lt;/code&gt; entry to a directory that
Promtail cannot find.&lt;/p&gt;
&lt;h2 id=&#34;connecting-to-a-promtail-pod-to-troubleshoot&#34;&gt;Connecting to a Promtail Pod to troubleshoot&lt;/h2&gt;
&lt;p&gt;First check &lt;a href=&#34;#troubleshooting-targets&#34;&gt;Troubleshooting targets&lt;/a&gt; section above.
If that doesn&amp;rsquo;t help answer your questions, you can connect to the Promtail Pod
to investigate further.&lt;/p&gt;
&lt;p&gt;If you are running Promtail as a DaemonSet in your cluster, you will have a
Promtail Pod on each node, so figure out which Promtail you need to debug first:&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;shell&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-shell&#34;&gt;$ kubectl get pods --all-namespaces -o wide
NAME                                   READY   STATUS    RESTARTS   AGE   IP             NODE        NOMINATED NODE
...
nginx-7b6fb56fb8-cw2cm                 1/1     Running   0          41d   10.56.4.12     node-ckgc   &amp;lt;none&amp;gt;
...
promtail-bth9q                         1/1     Running   0          3h    10.56.4.217    node-ckgc   &amp;lt;none&amp;gt;&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;That output is truncated to highlight just the two pods we are interested in,
you can see with the &lt;code&gt;-o wide&lt;/code&gt; flag the NODE on which they are running.&lt;/p&gt;
&lt;p&gt;You&amp;rsquo;ll want to match the node for the Pod you are interested in, in this example
NGINX, to the Promtail running on the same node.&lt;/p&gt;
&lt;p&gt;To debug you can connect to the Promtail Pod:&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;shell&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-shell&#34;&gt;kubectl exec -it promtail-bth9q -- /bin/sh&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Once connected, verify the config in &lt;code&gt;/etc/promtail/promtail.yml&lt;/code&gt; has the
contents you expect.&lt;/p&gt;
&lt;p&gt;Also check &lt;code&gt;/var/log/positions.yaml&lt;/code&gt; (&lt;code&gt;/run/promtail/positions.yaml&lt;/code&gt; when
deployed by Helm or whatever value is specified for &lt;code&gt;positions.file&lt;/code&gt;) and make
sure Promtail is tailing the logs you would expect.&lt;/p&gt;
&lt;p&gt;You can check the Promtail log by looking in &lt;code&gt;/var/log/containers&lt;/code&gt; at the
Promtail container log.&lt;/p&gt;
&lt;h2 id=&#34;enable-tracing-for-loki&#34;&gt;Enable tracing for Loki&lt;/h2&gt;
&lt;p&gt;Loki can be traced using &lt;a href=&#34;https://www.jaegertracing.io/&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;Jaeger&lt;/a&gt; by setting
the environment variable &lt;code&gt;JAEGER_AGENT_HOST&lt;/code&gt; to the hostname and port where
Jaeger is running.&lt;/p&gt;
&lt;p&gt;If you deploy with Helm, use the following command:&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;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;$ helm upgrade --install loki loki/loki --set &amp;#34;loki.tracing.jaegerAgentHost=YOUR_JAEGER_AGENT_HOST&amp;#34;&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;h2 id=&#34;running-loki-with-istio-sidecars&#34;&gt;Running Loki with Istio Sidecars&lt;/h2&gt;
&lt;p&gt;An Istio sidecar runs alongside a Pod. It intercepts all traffic to and from the Pod.
When a Pod tries to communicate with another Pod using a given protocol, Istio inspects the destination&amp;rsquo;s service using &lt;a href=&#34;https://istio.io/latest/docs/ops/configuration/traffic-management/protocol-selection/&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;Protocol Selection&lt;/a&gt;.
This mechanism uses a convention on the port name (for example, &lt;code&gt;http-my-port&lt;/code&gt; or &lt;code&gt;grpc-my-port&lt;/code&gt;)
to determine how to handle this outgoing traffic. Istio can then do operations such as authorization and smart routing.&lt;/p&gt;
&lt;p&gt;This works fine when one Pod communicates with another Pod using a hostname. But,
Istio does not allow pods to communicate with other pods using IP addresses,
unless the traffic type is &lt;code&gt;tcp&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Loki internally uses DNS to resolve the IP addresses of the different components.
Loki attempts to send a request to the IP address of those pods. The
Loki services have a &lt;code&gt;grpc&lt;/code&gt; (:9095/:9096) port defined, so Istio will consider
this to be &lt;code&gt;grpc&lt;/code&gt; traffic. It will not allow Loki components to reach each other using
an IP address. So, the traffic will fail, and the ring will remain unhealthy.&lt;/p&gt;
&lt;p&gt;The solution to this issue is to add &lt;code&gt;appProtocol: tcp&lt;/code&gt; to all of the &lt;code&gt;grpc&lt;/code&gt;
(:9095) and &lt;code&gt;grpclb&lt;/code&gt; (:9096) service ports of Loki components. This
overrides the Istio protocol selection, and it force Istio to consider this traffic raw &lt;code&gt;tcp&lt;/code&gt;, which allows pods to communicate using raw ip addresses.&lt;/p&gt;
&lt;p&gt;This disables part of the Istio traffic interception mechanism,
but still enables mTLS. This allows pods to communicate between themselves
using IP addresses over grpc.&lt;/p&gt;
]]></content><description>&lt;h1 id="troubleshooting">Troubleshooting&lt;/h1>
&lt;h2 id="loki-bad-gateway-502">&amp;ldquo;Loki: Bad Gateway. 502&amp;rdquo;&lt;/h2>
&lt;p>This error can appear in Grafana when Grafana Loki is added as a
datasource, indicating that Grafana in unable to connect to Loki. There may
one of many root causes:&lt;/p></description></item><item><title>Caching</title><link>https://grafana.com/docs/loki/v2.9.x/operations/caching/</link><pubDate>Wed, 22 Nov 2023 21:23:05 +0000</pubDate><guid>https://grafana.com/docs/loki/v2.9.x/operations/caching/</guid><content><![CDATA[&lt;h1 id=&#34;caching&#34;&gt;Caching&lt;/h1&gt;
&lt;p&gt;Loki supports caching of index writes and lookups, chunks and query results to
speed up query performance. This sections describes the recommended Memcached
configuration to enable caching for chunks and query results. The index lookup
cache is configured to be in-memory by default.&lt;/p&gt;
&lt;h2 id=&#34;before-you-begin&#34;&gt;Before you begin&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;It is recommended to deploy three, or in the case of the Helm chart two, dedicated Memcached clusters.&lt;/li&gt;
&lt;li&gt;As of 2023-02-01, the &lt;code&gt;memcached:1.6.17-alpine&lt;/code&gt; version of the library is recommended.&lt;/li&gt;
&lt;li&gt;Consult the Loki ksonnet &lt;a href=&#34;https://github.com/grafana/loki/blob/main/production/ksonnet/loki/memcached.libsonnet&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;memcached&lt;/a&gt; deployment and the ksonnet &lt;a href=&#34;https://github.com/grafana/jsonnet-libs/tree/master/memcached&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;memcached library&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;steps&#34;&gt;Steps&lt;/h2&gt;
&lt;p&gt;To enable and configure Memcached:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Deploy each Memcached service with at least three replicas and configure
each as follows:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Chunk cache

&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;--memory-limit=4096 --max-item-size=2m --conn-limit=1024&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;Query result and index queries cache

&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;--memory-limit=1024 --max-item-size=5m --conn-limit=1024&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Configure Loki to use the cache.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;If the Helm chart is used&lt;/p&gt;
&lt;p&gt;Set &lt;code&gt;memcached.chunk_cache.host&lt;/code&gt; to the Memcached address for the chunk cache, &lt;code&gt;memcached.results_cache.host&lt;/code&gt; to the Memcached address for the query result cache, &lt;code&gt;memcached.chunk_cache.enabled=true&lt;/code&gt; and &lt;code&gt;memcached.results_cache.enabled=true&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Ensure that the connection limit of Memcached is at least &lt;code&gt;number_of_clients * max_idle_conns&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The options &lt;code&gt;host&lt;/code&gt; and &lt;code&gt;service&lt;/code&gt; depend on the type of installation. For example, using the &lt;code&gt;bitnami/memcached&lt;/code&gt; Helm Charts with the following commands, the &lt;code&gt;service&lt;/code&gt; values are always &lt;code&gt;memcached&lt;/code&gt;.&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;helm upgrade --install chunk-cache -n loki bitnami/memcached -f memcached-overrides-chunk.yaml
helm upgrade --install results-cache -n loki bitnami/memcached -f memcached-overrides-results.yaml&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;The current Helm Chart only supports the chunk and results cache.&lt;/p&gt;
&lt;p&gt;In this case, the Loki configuration would be&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;loki:
  memcached:
    chunk_cache:
      enabled: true
      host: chunk-cache-memcached.loki.svc
      service: memcached-client
      batch_size: 256
      parallelism: 10
    results_cache:
      enabled: true
      host: results-cache-memcached.loki.svc
      service: memcached-client
      default_validity: 12h&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;If the Loki configuration is used, modify the following three sections in
the Loki configuration file.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Configure the chunk and index write cache

&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;chunk_store_config:
  chunk_cache_config:
    memcached:
      batch_size: 256
      parallelism: 10
    memcached_client:
      host: &amp;lt;chunk cache memcached host&amp;gt;
      service: &amp;lt;port name of memcached service&amp;gt;&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;Configure the query result cache

&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;query_range:
  cache_results: true
  results_cache:
    cache:
      memcached_client:
        consistent_hash: true
        host: &amp;lt;memcached host&amp;gt;
        service: &amp;lt;port name of memcached service&amp;gt;
        max_idle_conns: 16
        timeout: 500ms
        update_interval: 1m&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;Configure the index queries cache

&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;storage_config:
  index_queries_cache_config:
    memcached:
      batch_size: 100
      parallelism: 100
    memcached_client:
      host: &amp;lt;memcached host&amp;gt;
      service: &amp;lt;port name of memcached service&amp;gt;
      consistent_hash: true&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ol&gt;
]]></content><description>&lt;h1 id="caching">Caching&lt;/h1>
&lt;p>Loki supports caching of index writes and lookups, chunks and query results to
speed up query performance. This sections describes the recommended Memcached
configuration to enable caching for chunks and query results. The index lookup
cache is configured to be in-memory by default.&lt;/p></description></item><item><title>Shuffle sharding</title><link>https://grafana.com/docs/loki/v2.9.x/operations/shuffle-sharding/</link><pubDate>Wed, 06 Sep 2023 12:47:18 +0000</pubDate><guid>https://grafana.com/docs/loki/v2.9.x/operations/shuffle-sharding/</guid><content><![CDATA[&lt;h1 id=&#34;shuffle-sharding&#34;&gt;Shuffle sharding&lt;/h1&gt;
&lt;p&gt;Shuffle sharding is a resource-management technique used to isolate tenant workloads from other tenant workloads, to give each tenant more of a single-tenant experience when running in a shared cluster.
This technique is explained by AWS in their article &lt;a href=&#34;https://aws.amazon.com/builders-library/workload-isolation-using-shuffle-sharding/&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;Workload isolation using shuffle-sharding&lt;/a&gt;.
A reference implementation has been shown in the &lt;a href=&#34;https://github.com/awslabs/route53-infima/blob/master/src/main/java/com/amazonaws/services/route53/infima/SimpleSignatureShuffleSharder.java&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;Route53 Infima library&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&#34;the-issues-that-shuffle-sharding-mitigates&#34;&gt;The issues that shuffle sharding mitigates&lt;/h2&gt;
&lt;p&gt;Shuffle sharding can be configured for the query path.&lt;/p&gt;
&lt;p&gt;The query path is sharded by default, and the default does not use shuffle sharding.
Each tenant’s query is sharded across all queriers, so the workload uses all querier instances.&lt;/p&gt;
&lt;p&gt;In a multi-tenant cluster, sharding across all instances of a component may exhibit these issues:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Any outage of a component instance affects all tenants&lt;/li&gt;
&lt;li&gt;A misbehaving tenant affects all other tenants&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;An individual query may create issues for all tenants.
A single tenant or a group of tenants may issue an expensive query:
one that causes a querier component to hit an out-of-memory error,
or one that causes a querier component to crash.
Once the error occurs,
the tenant or tenants issuing the error-causing query will be reassigned
to other running queriers(remember all tenants can use all available queriers),
This, in turn, may affect the queriers that have been reassigned.&lt;/p&gt;
&lt;h2 id=&#34;how-shuffle-sharding-works&#34;&gt;How shuffle sharding works&lt;/h2&gt;
&lt;p&gt;The idea of shuffle sharding is to assign each tenant to a shard composed by a subset of the Loki queriers, aiming to minimize the overlapping instances between distinct tenants.&lt;/p&gt;
&lt;p&gt;A misbehaving tenant will affect only its shard&amp;rsquo;s queriers. Due to the low overlap of queriers among tenants, only a small subset of tenants will be affected by the misbehaving tenant.
Shuffle sharding requires no more resources than the default sharding strategy.&lt;/p&gt;
&lt;p&gt;Shuffle sharding does not fix all issues.
If a tenant repeatedly sends a problematic query, the crashed querier
will be disconnected from the query-frontend, and a new querier
will be immediately assigned to the tenant’s shard.
This invalidates the positive effects of shuffle sharding.
In this case,
configuring a delay between when a querier disconnects because of a crash,
and when the crashed querier is actually removed from the tenant’s shard
and another healthy querier is added as a replacement improves the situation.
A delay of 1 minute may be a reasonable value in
the query-frontend with configuration parameter
&lt;code&gt;-query-frontend.querier-forget-delay=1m&lt;/code&gt;, and in the query-scheduler with configuration parameter
&lt;code&gt;-query-scheduler.querier-forget-delay=1m&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id=&#34;low-probability-of-overlapping-instances&#34;&gt;Low probability of overlapping instances&lt;/h3&gt;
&lt;p&gt;If an example Loki cluster runs 50 queriers and assigns each tenant 4 out of 50 queriers, shuffling instances between each tenant, there are 230K possible combinations.&lt;/p&gt;
&lt;p&gt;Statistically, randomly picking two distinct tenants, there is:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;a 71% chance that they will not share any instance&lt;/li&gt;
&lt;li&gt;a 26% chance that they will share only 1 instance&lt;/li&gt;
&lt;li&gt;a 2.7% chance that they will share 2 instances&lt;/li&gt;
&lt;li&gt;a 0.08% chance that they will share 3 instances&lt;/li&gt;
&lt;li&gt;only a 0.0004% chance that their instances will fully overlap&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img
  class=&#34;lazyload d-inline-block&#34;
  data-src=&#34;./shuffle-sharding-probability.png&#34;
  alt=&#34;overlapping instances probability&#34;/&gt;&lt;/p&gt;
&lt;h2 id=&#34;configuration&#34;&gt;Configuration&lt;/h2&gt;
&lt;p&gt;Enable shuffle sharding by setting &lt;code&gt;-frontend.max-queriers-per-tenant&lt;/code&gt; to a value higher than 0 and lower than the number of available queriers.
The value of the per-tenant configuration
&lt;code&gt;max_queriers_per_tenant&lt;/code&gt; sets the quantity of allocated queriers.
This option is only available when using the query-frontend, with or without a scheduler.&lt;/p&gt;
&lt;p&gt;The per-tenant configuration parameter
&lt;code&gt;max_query_parallelism&lt;/code&gt; describes how many sub queries, after query splitting and query sharding, can be scheduled to run at the same time for each request of any tenant.&lt;/p&gt;
&lt;p&gt;Configuration parameter
&lt;code&gt;querier.concurrency&lt;/code&gt; controls the quantity of worker threads (goroutines) per single querier.&lt;/p&gt;
&lt;p&gt;The maximum number of queriers can be overridden on a per-tenant basis in the limits overrides configuration by &lt;code&gt;max_queriers_per_tenant&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&#34;shuffle-sharding-metrics&#34;&gt;Shuffle sharding metrics&lt;/h2&gt;
&lt;p&gt;These metrics reveal information relevant to shuffle sharding:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;the overall query-scheduler queue duration,  &lt;code&gt;cortex_query_scheduler_queue_duration_seconds_*&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;the query-scheduler queue length per tenant, &lt;code&gt;cortex_query_scheduler_queue_length&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;the query-scheduler queue duration per tenant can be found with this query:&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;max_over_time({cluster=&amp;#34;$cluster&amp;#34;,container=&amp;#34;query-frontend&amp;#34;, namespace=&amp;#34;$namespace&amp;#34;} |= &amp;#34;metrics.go&amp;#34; |logfmt | unwrap duration(queue_time) | __error__=&amp;#34;&amp;#34; [5m]) by (org_id)&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Too many spikes in any of these metrics may imply:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A particular tenant is trying to use more query resources than they were allocated.&lt;/li&gt;
&lt;li&gt;That tenant may need an increase in the value of &lt;code&gt;max_queriers_per_tenant&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Loki instances may be under provisioned.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;A useful query checks how many queriers are being used by each tenant:&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;count by (org_id) (sum by (org_id, pod) (count_over_time({job=&amp;#34;$namespace/querier&amp;#34;, cluster=&amp;#34;$cluster&amp;#34;} |= &amp;#34;metrics.go&amp;#34; | logfmt [$__interval])))&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
]]></content><description>&lt;h1 id="shuffle-sharding">Shuffle sharding&lt;/h1>
&lt;p>Shuffle sharding is a resource-management technique used to isolate tenant workloads from other tenant workloads, to give each tenant more of a single-tenant experience when running in a shared cluster.
This technique is explained by AWS in their article &lt;a href="https://aws.amazon.com/builders-library/workload-isolation-using-shuffle-sharding/" target="_blank" rel="noopener noreferrer">Workload isolation using shuffle-sharding&lt;/a>.
A reference implementation has been shown in the &lt;a href="https://github.com/awslabs/route53-infima/blob/master/src/main/java/com/amazonaws/services/route53/infima/SimpleSignatureShuffleSharder.java" target="_blank" rel="noopener noreferrer">Route53 Infima library&lt;/a>.&lt;/p></description></item><item><title>Query fairness within tenants</title><link>https://grafana.com/docs/loki/v2.9.x/operations/query-fairness/</link><pubDate>Thu, 10 Apr 2025 12:15:54 +0000</pubDate><guid>https://grafana.com/docs/loki/v2.9.x/operations/query-fairness/</guid><content><![CDATA[&lt;h1 id=&#34;query-fairness-within-tenants&#34;&gt;Query fairness within tenants&lt;/h1&gt;
&lt;p&gt;Loki uses &lt;a href=&#34;../shuffle-sharding/&#34;&gt;shuffle sharding&lt;/a&gt;
to minimize impact across tenants in case of querier failures or misbehaving
neighboring tenants.&lt;/p&gt;
&lt;p&gt;When there are potentially a lot of different actors using the same tenant to
query logs, such as users accessing Loki from Grafana or via LogCLI or other
applications using the HTTP API, it can lead to contention between queries of
different users, because they all share the same resources for a tenant.&lt;/p&gt;
&lt;p&gt;In that case, as an operator, you would also want to ensure some sort of query
fairness across these actors within the tenants. An actor could be a Grafana user,
a CLI user, or an application accessing the API. To achieve that, Loki
introduces hierarchical scheduler queues in version 2.9 based on
&lt;a href=&#34;../../community/lids/0003-QueryFairnessInScheduler/&#34;&gt;LID 0003: Query fairness across users within tenants&lt;/a&gt;
and they are enabled by default.&lt;/p&gt;
&lt;h2 id=&#34;what-are-hierarchical-queues-and-how-do-they-work&#34;&gt;What are hierarchical queues and how do they work&lt;/h2&gt;
&lt;p&gt;To understand hierarchical queues, we first need to know that in the scheduler
component each tenant has its own first in first out (FIFO) queue where
sub-queries are enqueued. Sub-queries are queries that result from splitting
and sharding of a query sent by a client using HTTP.&lt;/p&gt;
&lt;p&gt;Tenant queues are the first level of the queue hierarchy. When a tenant
executes a query without any further controls, all of its sub-queries are
enqueued to the first level queue.&lt;/p&gt;
&lt;p&gt;The second level of the queue hierarchy is that the tenant can have sub-queues.&lt;/p&gt;
&lt;p&gt;Similar to how shuffle sharding assigns queries at the tenant level, each time
the Loki Scheduler makes a round-robin pick at the second level of the query
hierarchy, it selects a query from the tenant’s local queue and subqueues.&lt;/p&gt;
&lt;p&gt;&lt;img
  class=&#34;lazyload d-inline-block&#34;
  data-src=&#34;./hierarchical-queues.png&#34;
  alt=&#34;Hierarchical queues&#34;/&gt;&lt;/p&gt;
&lt;p&gt;The figure above shows that a tenant queue has a local queue, which is a leaf
node in the queue tree, and a set of sub-queues. Each sub-queue, again like the
tenant queue, consists of a local queue, and possible sub-queues, resulting in
a recursive tree structure.&lt;/p&gt;
&lt;p&gt;So, how can we make use of these tree-like queue structures to achieve query fairness?&lt;/p&gt;
&lt;h2 id=&#34;how-to-control-query-fairness&#34;&gt;How to control query fairness&lt;/h2&gt;
&lt;p&gt;As already mentioned, by default, sub-queries are only enqueued at the first
(tenant) level of the queue tree. The tenant is provided by the &lt;code&gt;X-Scope-OrgID&lt;/code&gt;
header that is required when running Loki in multi-tenant mode.&lt;/p&gt;
&lt;p&gt;You use the HTTP header &lt;code&gt;X-Loki-Actor-Path&lt;/code&gt; to control to which sub-queue a
query (or more correctly its sub-queries) is enqueued.&lt;/p&gt;
&lt;p&gt;The following example shows a &lt;code&gt;curl&lt;/code&gt; command that invokes the HTTP endpoint for range queries
and passes both the &lt;code&gt;X-Scope-OrgID&lt;/code&gt; and the &lt;code&gt;X-Loki-Actor-Path&lt;/code&gt; headers.&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;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;curl -s http://localhost:3100/loki/api/v1/query_range?xxx \
    -H &amp;#39;X-Scope-OrgID: grafana&amp;#39; \
    -H &amp;#39;X-Loki-Actor-Path: joe&amp;#39;&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;The query that this request invokes ends up in the sub-queue &lt;code&gt;joe&lt;/code&gt; of the
tenant queue &lt;code&gt;grafana&lt;/code&gt;. Another user can use their own name in the actor path
header to enqueue their queries to their own sub-queue.&lt;/p&gt;
&lt;p&gt;Since the scheduler chooses the next task for a tenant in a round-robin manner,
both actors (in our case human users) get their 50% share when the scheduler
dequeues a sub-query to send to the querier.&lt;/p&gt;
&lt;p&gt;With N actors, each actor gets 1/Nth of their share. In our example with two
users, even when there are sub-queries in the local queue of the tenant, the
local queue gets 1/3 and each sub-queue gets 1/3 of their share.&lt;/p&gt;
&lt;p&gt;As the explained implementation and the header name already suggest, it is
possible to enqueue queries several levels deep. To do so, you can construct a
path to the sub-queue using the &lt;code&gt;|&lt;/code&gt; delimiter in the header value, as shown in
the following examples.&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;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;curl -s http://localhost:3100/loki/api/v1/query_range?xxx \
    -H &amp;#39;X-Scope-OrgID: grafana&amp;#39; \
    -H &amp;#39;X-Loki-Actor-Path: users|joe&amp;#39;

curl -s http://localhost:3100/loki/api/v1/query_range?xxx \
    -H &amp;#39;X-Scope-OrgID: grafana&amp;#39; \
    -H &amp;#39;X-Loki-Actor-Path: apps|logcli&amp;#39;&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;There is a limit to how deep a path and thus the queue tree can be. This is
controlled by Loki&amp;rsquo;s &lt;code&gt;-query-scheduler.max-queue-hierarchy-levels&lt;/code&gt; CLI argument
or its respective YAML configuration block:&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;query_scheduler:
  max_queue_hierarchy_levels: 2  # defaults to 3&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;It is advised to keep the levels at a reasonable level (ideally 1 to 3 levels),
both for performance reasons as well as for the understanding of how query
fairness is ensured across all sub-queues.&lt;/p&gt;
&lt;h2 id=&#34;enforcing-headers&#34;&gt;Enforcing headers&lt;/h2&gt;
&lt;p&gt;In the examples above the client that invoked the query directly against Loki also provided the
HTTP header that controls where in the queue tree the sub-queries are enqueued. However, as an operator,
you would usually want to avoid this scenario and control yourself where the header is set.&lt;/p&gt;
&lt;p&gt;When using Grafana as the Loki user interface, you can, for example, create multiple datasources
with the same tenant, but with a different additional HTTP header
&lt;code&gt;X-Loki-Scope-Actor&lt;/code&gt; and restrict which Grafana user can use which datasource.&lt;/p&gt;
&lt;p&gt;Alternatively, if you have a proxy for authentication in front of Loki, you can
pass the (hashed) user from the authentication as downstream header to Loki.&lt;/p&gt;
]]></content><description>&lt;h1 id="query-fairness-within-tenants">Query fairness within tenants&lt;/h1>
&lt;p>Loki uses &lt;a href="../shuffle-sharding/">shuffle sharding&lt;/a>
to minimize impact across tenants in case of querier failures or misbehaving
neighboring tenants.&lt;/p>
&lt;p>When there are potentially a lot of different actors using the same tenant to
query logs, such as users accessing Loki from Grafana or via LogCLI or other
applications using the HTTP API, it can lead to contention between queries of
different users, because they all share the same resources for a tenant.&lt;/p></description></item><item><title>Automatic stream sharding</title><link>https://grafana.com/docs/loki/v2.9.x/operations/automatic-stream-sharding/</link><pubDate>Mon, 29 Apr 2024 15:52:20 +0000</pubDate><guid>https://grafana.com/docs/loki/v2.9.x/operations/automatic-stream-sharding/</guid><content><![CDATA[&lt;h1 id=&#34;automatic-stream-sharding&#34;&gt;Automatic stream sharding&lt;/h1&gt;
&lt;p&gt;Automatic stream sharding will attempt to keep streams under a &lt;code&gt;desired_rate&lt;/code&gt; by adding new labels and values to
existing streams. When properly tuned, this should eliminate issues where log producers are rate limited due to the
per-stream rate limit.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;To enable automatic stream sharding:&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Edit the global 
    &lt;a href=&#34;/docs/loki/v2.9.x/configure/#limits_config&#34;&gt;&lt;code&gt;limits_config&lt;/code&gt;&lt;/a&gt; of the Loki configuration file:&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;limits_config:
  shard_streams:
      enabled: true&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Optionally lower the &lt;code&gt;desired_rate&lt;/code&gt; in bytes if you find that the system is still hitting the &lt;code&gt;per_stream_rate_limit&lt;/code&gt;:&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;limits_config:
  shard_streams:
    enabled: true
    desired_rate: 2097152 #2MiB&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Optionally enable &lt;code&gt;logging_enabled&lt;/code&gt; for debugging stream sharding.


&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;This may affect the ingestion performance of Loki.&lt;/p&gt;&lt;/blockquote&gt;&lt;/div&gt;
&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;limits_config:
  shard_streams:
    enabled: true
    logging_enabled: true&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&#34;when-to-use-automatic-stream-sharding&#34;&gt;When to use automatic stream sharding&lt;/h2&gt;
&lt;p&gt;Large log streams present several problems for Loki, namely increased and uneven resource usage on Ingesters and
Distributors. The general recommendation is to explore existing log streams for additional label values that are both
useful for querying and sufficiently low cardinality. There are many cases, however, where no more labels can
be extracted, or cardinality for a label is dangerously large. To protect itself from such volume leading to operational failure, Loki implements per-stream rate limits;
but the result is that some data is lost. The per-stream limit also needs human intervention to change, which is not ideal when log volumes increase and decrease.&lt;/p&gt;
&lt;p&gt;Loki uses automatic stream sharding to avoid rate limiting and large streams for any log stream by ensuring it is close
to a configured &lt;code&gt;desired_rate&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&#34;how-automatic-stream-sharding-works&#34;&gt;How automatic stream sharding works&lt;/h2&gt;
&lt;p&gt;Automatic stream sharding works by adding a new label, &lt;code&gt;__stream_shard__&lt;/code&gt;, to streams and incrementing its value to try
and keep all streams below a configured &lt;code&gt;desired_rate&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The feature adds a new API to Ingesters that reports the size of all existing log streams. Once per second, Distributors
query the API to get a picture of all stream rates in the system. Distributors use the existing stream-rate data and a
configured &lt;code&gt;desired_rate&lt;/code&gt; to determine how many shards a given stream should have. The desired number of new log streams
are created with the label &lt;code&gt;__stream_shard__&lt;/code&gt; and logs are divided evenly among the streams.&lt;/p&gt;
&lt;p&gt;Because automatic stream sharding is reactive and relies on successive calls to Ingesters, the view of current rates is
always somewhat behind. As a result, the actual size of sharded streams will always be higher than the &lt;code&gt;desired_rate&lt;/code&gt;.
In practice, this is still sufficient to keep log producers from being rate limited by per-stream rate limits.&lt;/p&gt;
&lt;h2 id=&#34;automatic-stream-sharding-metrics&#34;&gt;Automatic stream sharding metrics&lt;/h2&gt;
&lt;p&gt;Use these metrics to help tune Loki so that it is sharding streams aggressively enough to avoid the per-stream rate
limit:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;loki_rate_store_refresh_failures_total&lt;/code&gt;: The total number of failed attempts to refresh the distributor&amp;rsquo;s view of
stream rates.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;loki_rate_store_streams&lt;/code&gt;: The number of unique streams reported by all Ingesters. Sharded streams are reported as if
they were unsharded.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;loki_rate_store_max_stream_shards&lt;/code&gt;: The maximum number of shards for any tenant of the system.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;loki_rate_store_stream_shards&lt;/code&gt;: A histogram of the distribution of shard counts across all streams.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;loki_rate_store_max_stream_rate_bytes&lt;/code&gt;: The maximum stream size in bytes/second for any tenant of the system. Sharded
streams are reported as if they are unsharded.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;loki_rate_store_max_unique_stream_rate_bytes&lt;/code&gt;: The maximum size of any stream across all tenants. Stream shards are
individually reported.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;loki_rate_store_stream_rate_bytes&lt;/code&gt;: A histogram of the distribution of stream sizes across all tenants in
bytes/second.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;loki_stream_sharding_count&lt;/code&gt;: The total number of times that streams have been sharded. Useful for calculating the
sharding rate.&lt;/li&gt;
&lt;/ul&gt;
]]></content><description>&lt;h1 id="automatic-stream-sharding">Automatic stream sharding&lt;/h1>
&lt;p>Automatic stream sharding will attempt to keep streams under a &lt;code>desired_rate&lt;/code> by adding new labels and values to
existing streams. When properly tuned, this should eliminate issues where log producers are rate limited due to the
per-stream rate limit.&lt;/p></description></item><item><title>Authentication</title><link>https://grafana.com/docs/loki/v2.9.x/operations/authentication/</link><pubDate>Thu, 10 Apr 2025 12:15:54 +0000</pubDate><guid>https://grafana.com/docs/loki/v2.9.x/operations/authentication/</guid><content><![CDATA[&lt;h1 id=&#34;authentication&#34;&gt;Authentication&lt;/h1&gt;
&lt;p&gt;Grafana Loki does not come with any included authentication layer. Operators are
expected to run an authenticating reverse proxy in front of your services.&lt;/p&gt;
&lt;p&gt;The simple scalable &lt;a href=&#34;../../get-started/deployment-modes/&#34;&gt;deployment mode&lt;/a&gt; requires a reverse proxy to be deployed in front of Loki, to direct client API requests to either the read or write nodes. The Loki Helm chart includes a default reverse proxy configuration, using Nginx.&lt;/p&gt;
&lt;p&gt;A list of open-source reverse proxies you can use:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://www.pomerium.com/docs&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;Pomerium&lt;/a&gt;, which has a &lt;a href=&#34;https://www.pomerium.com/docs/guides/grafana&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;guide for securing Grafana&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://docs.nginx.com/nginx/&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;NGINX&lt;/a&gt; using their &lt;a href=&#34;https://docs.nginx.com/nginx/admin-guide/security-controls/configuring-http-basic-authentication/&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;guide on restricting access with HTTP basic authentication&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/oauth2-proxy/oauth2-proxy&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;OAuth2 proxy&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://www.haproxy.org/&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;HAProxy&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Note that when using Loki in multi-tenant mode, Loki requires the HTTP header
&lt;code&gt;X-Scope-OrgID&lt;/code&gt; to be set to a string identifying the tenant; the responsibility
of populating this value should be handled by the authenticating reverse proxy.
For more information, read the &lt;a href=&#34;../multi-tenancy/&#34;&gt;multi-tenancy&lt;/a&gt; documentation.&lt;/p&gt;
&lt;p&gt;For information on authenticating Promtail, see the documentation for &lt;a href=&#34;../../send-data/promtail/configuration/&#34;&gt;how to
configure Promtail&lt;/a&gt;.&lt;/p&gt;
]]></content><description>&lt;h1 id="authentication">Authentication&lt;/h1>
&lt;p>Grafana Loki does not come with any included authentication layer. Operators are
expected to run an authenticating reverse proxy in front of your services.&lt;/p></description></item><item><title>Recording Rules</title><link>https://grafana.com/docs/loki/v2.9.x/operations/recording-rules/</link><pubDate>Mon, 29 Apr 2024 15:52:20 +0000</pubDate><guid>https://grafana.com/docs/loki/v2.9.x/operations/recording-rules/</guid><content><![CDATA[&lt;h1 id=&#34;recording-rules&#34;&gt;Recording Rules&lt;/h1&gt;
&lt;p&gt;Recording rules are evaluated by the &lt;code&gt;ruler&lt;/code&gt; component. Each &lt;code&gt;ruler&lt;/code&gt; acts as its own &lt;code&gt;querier&lt;/code&gt;, in the sense that it
executes queries against the store without using the &lt;code&gt;query-frontend&lt;/code&gt; or &lt;code&gt;querier&lt;/code&gt; components. It will respect all query

    &lt;a href=&#34;/docs/loki/v2.9.x/configure/#limits_config&#34;&gt;limits&lt;/a&gt; put in place for the &lt;code&gt;querier&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Loki&amp;rsquo;s implementation of recording rules largely reuses Prometheus&amp;rsquo; code.&lt;/p&gt;
&lt;p&gt;Samples generated by recording rules are sent to Prometheus using Prometheus&amp;rsquo; &lt;strong&gt;remote-write&lt;/strong&gt; feature.&lt;/p&gt;
&lt;h2 id=&#34;write-ahead-log-wal&#34;&gt;Write-Ahead Log (WAL)&lt;/h2&gt;
&lt;p&gt;All samples generated by recording rules are written to a WAL. The WALs main benefit is that it persists the samples
generated by recording rules to disk, which means that if your &lt;code&gt;ruler&lt;/code&gt; crashes, you won&amp;rsquo;t lose any data.
We are trading off extra memory usage and slower start-up times for this functionality.&lt;/p&gt;
&lt;p&gt;A WAL is created per tenant; this is done to prevent cross-tenant interactions. If all samples were to be written
to a single WAL, this would increase the chances that one tenant could cause data-loss for others. A typical scenario here
is that Prometheus will, for example, reject a remote-write request with 100 samples if just 1 of those samples is invalid in some way.&lt;/p&gt;
&lt;h3 id=&#34;start-up&#34;&gt;Start-up&lt;/h3&gt;
&lt;p&gt;When the &lt;code&gt;ruler&lt;/code&gt; starts up, it will load the WALs for the tenants who have recording rules. These WAL files are stored
on disk and are loaded into memory.&lt;/p&gt;
&lt;p&gt;Note: WALs are loaded one at a time upon start-up. This is a current limitation of the Loki ruler.
For this reason, it is adviseable that the number of rule groups serviced by a ruler be kept to a reasonable size, since
&lt;em&gt;no rule evaluation occurs while WAL replay is in progress (this includes alerting rules)&lt;/em&gt;.&lt;/p&gt;
&lt;h3 id=&#34;truncation&#34;&gt;Truncation&lt;/h3&gt;
&lt;p&gt;WAL files are regularly truncated to reduce their size on disk.
&lt;a href=&#34;https://ganeshvernekar.com/blog/prometheus-tsdb-wal-and-checkpoint/#wal-truncation-and-checkpointing&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;This guide&lt;/a&gt;
from one of the Prometheus maintainers (Ganesh Vernekar) gives an excellent overview of the truncation, checkpointing,
and replaying of the WAL.&lt;/p&gt;
&lt;h3 id=&#34;cleaner&#34;&gt;Cleaner&lt;/h3&gt;
&lt;p&gt;&lt;span style=&#34;background-color:#f3f973;&#34;&gt;WAL Cleaner is an experimental feature.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;The WAL Cleaner watches for abandoned WALs (tenants who no longer have recording rules associated) and deletes them.
Enable this feature only if you are running into storage concerns with WALs that are too large. WALs should not grow
excessively large due to truncation.&lt;/p&gt;
&lt;h2 id=&#34;scaling&#34;&gt;Scaling&lt;/h2&gt;
&lt;p&gt;See Mimir&amp;rsquo;s guide for &lt;a href=&#34;/docs/mimir/latest/configure/configure-hash-rings/&#34;&gt;configuring Grafana Mimir hash rings&lt;/a&gt; for scaling the ruler using a ring.&lt;/p&gt;
&lt;p&gt;Note: the &lt;code&gt;ruler&lt;/code&gt; shards by rule &lt;em&gt;group&lt;/em&gt;, not by individual rules. This is an artifact of the fact that Prometheus
recording rules need to run in order since one recording rule can reuse another - but this is not possible in Loki.&lt;/p&gt;
&lt;h2 id=&#34;deployment&#34;&gt;Deployment&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;ruler&lt;/code&gt; needs to persist its WAL files to disk, and it incurs a bit of a start-up cost by reading these WALs into memory.
As such, it is recommended that you try to minimize churn of individual &lt;code&gt;ruler&lt;/code&gt; instances since rule evaluation is blocked
while the WALs are being read from disk.&lt;/p&gt;
&lt;h3 id=&#34;kubernetes&#34;&gt;Kubernetes&lt;/h3&gt;
&lt;p&gt;It is recommended that you run the &lt;code&gt;rulers&lt;/code&gt; using &lt;code&gt;StatefulSets&lt;/code&gt;. The &lt;code&gt;ruler&lt;/code&gt; will write its WAL files to persistent storage,
so a &lt;code&gt;Persistent Volume&lt;/code&gt; should be utilised.&lt;/p&gt;
&lt;h2 id=&#34;remote-write&#34;&gt;Remote-Write&lt;/h2&gt;
&lt;h3 id=&#34;per-tenant-limits&#34;&gt;Per-Tenant Limits&lt;/h3&gt;
&lt;p&gt;Remote-write can be configured at a global level in the base configuration, and certain parameters tuned specifically on
a per-tenant basis. Most of the configuration options 
    &lt;a href=&#34;/docs/loki/v2.9.x/configure/#ruler&#34;&gt;defined here&lt;/a&gt;
have 
    &lt;a href=&#34;/docs/loki/v2.9.x/configure/#limits_config&#34;&gt;override options&lt;/a&gt; (which can be also applied at runtime!).&lt;/p&gt;
&lt;h3 id=&#34;tuning&#34;&gt;Tuning&lt;/h3&gt;
&lt;p&gt;Remote-write can be tuned if the default configuration is insufficient (see &lt;a href=&#34;#failure-modes&#34;&gt;Failure Modes&lt;/a&gt; below).&lt;/p&gt;
&lt;p&gt;There is a &lt;a href=&#34;https://prometheus.io/docs/practices/remote_write/&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;guide&lt;/a&gt; on the Prometheus website, all of which applies to Loki, too.&lt;/p&gt;
&lt;p&gt;Rules can be evenly distributed across available rulers by using &lt;code&gt;-ruler.enable-sharding=true&lt;/code&gt; and &lt;code&gt;-ruler.sharding-strategy=&amp;quot;by-rule&amp;quot;&lt;/code&gt;.
Rule groups execute in order; this is a feature inherited from Prometheus&amp;rsquo; rule engine (which Loki uses), but Loki has no
need for this constraint because rules cannot depend on each other. The default sharding strategy will shard by rule groups,
but this may be undesirable as some rule groups could contain more expensive rules, which can lead to subsequent rules missing evaluations.
The &lt;code&gt;by-rule&lt;/code&gt; sharding strategy creates one rule group for each rule the ruler instance &amp;ldquo;owns&amp;rdquo; (based on its hash ring), and these rings
are all executed concurrently.&lt;/p&gt;
&lt;h2 id=&#34;observability&#34;&gt;Observability&lt;/h2&gt;
&lt;p&gt;Since Loki reuses the Prometheus code for recording rules and WALs, it also gains all of Prometheus&amp;rsquo; observability.&lt;/p&gt;
&lt;p&gt;Prometheus exposes a number of metrics for its WAL implementation, and these have all been prefixed with &lt;code&gt;loki_ruler_wal_&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;For example: &lt;code&gt;prometheus_remote_storage_bytes_total&lt;/code&gt; → &lt;code&gt;loki_ruler_wal_prometheus_remote_storage_bytes_total&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Additional metrics are exposed, also with the prefix &lt;code&gt;loki_ruler_wal_&lt;/code&gt;. All per-tenant metrics contain a &lt;code&gt;tenant&lt;/code&gt;
label, so be aware that cardinality could begin to be a concern if the number of tenants grows sufficiently large.&lt;/p&gt;
&lt;p&gt;Some key metrics to note are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;loki_ruler_wal_appender_ready&lt;/code&gt;: whether a WAL appender is ready to accept samples (1) or not (0)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;loki_ruler_wal_prometheus_remote_storage_samples_total&lt;/code&gt;: number of samples sent per tenant to remote storage&lt;/li&gt;
&lt;li&gt;&lt;code&gt;loki_ruler_wal_prometheus_remote_storage_samples...&lt;/code&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;loki_ruler_wal_prometheus_remote_storage_samples_pending_total&lt;/code&gt;: samples buffered in memory, waiting to be sent to remote storage&lt;/li&gt;
&lt;li&gt;&lt;code&gt;loki_ruler_wal_prometheus_remote_storage_samples_failed_total&lt;/code&gt;: samples that failed when sent to remote storage&lt;/li&gt;
&lt;li&gt;&lt;code&gt;loki_ruler_wal_prometheus_remote_storage_samples_dropped_total&lt;/code&gt;: samples dropped by relabel configurations&lt;/li&gt;
&lt;li&gt;&lt;code&gt;loki_ruler_wal_prometheus_remote_storage_samples_retried_total&lt;/code&gt;: samples re-resent to remote storage&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;loki_ruler_wal_prometheus_remote_storage_highest_timestamp_in_seconds&lt;/code&gt;: highest timestamp of sample appended to WAL&lt;/li&gt;
&lt;li&gt;&lt;code&gt;loki_ruler_wal_prometheus_remote_storage_queue_highest_sent_timestamp_seconds&lt;/code&gt;: highest timestamp of sample sent to remote storage.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We&amp;rsquo;ve created a basic &lt;a href=&#34;https://github.com/grafana/loki/tree/main/production/loki-mixin/dashboards/recording-rules.libsonnet&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;dashboard in our loki-mixin&lt;/a&gt;
which you can use to administer recording rules.&lt;/p&gt;
&lt;h2 id=&#34;failure-modes&#34;&gt;Failure Modes&lt;/h2&gt;
&lt;h3 id=&#34;remote-write-lagging&#34;&gt;Remote-Write Lagging&lt;/h3&gt;
&lt;p&gt;Remote-write can lag behind for many reasons:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Remote-write storage (Prometheus) is temporarily unavailable&lt;/li&gt;
&lt;li&gt;A tenant is producing samples too quickly from a recording rule&lt;/li&gt;
&lt;li&gt;Remote-write is tuned too low, creating backpressure&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;It can be determined by subtracting
&lt;code&gt;loki_ruler_wal_prometheus_remote_storage_queue_highest_sent_timestamp_seconds&lt;/code&gt; from
&lt;code&gt;loki_ruler_wal_prometheus_remote_storage_highest_timestamp_in_seconds&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;In case 1, the &lt;code&gt;ruler&lt;/code&gt; will continue to retry sending these samples until the remote storage becomes available again. Be
aware that if the remote storage is down for longer than &lt;code&gt;ruler.wal.max-age&lt;/code&gt;, data loss may occur after truncation occurs.&lt;/p&gt;
&lt;p&gt;In cases 2 &amp;amp; 3, you should consider &lt;a href=&#34;#tuning&#34;&gt;tuning&lt;/a&gt; remote-write appropriately.&lt;/p&gt;
&lt;p&gt;Further reading: see &lt;a href=&#34;/blog/2021/04/12/how-to-troubleshoot-remote-write-issues-in-prometheus/&#34;&gt;this blog post&lt;/a&gt;
by Prometheus maintainer Callum Styan.&lt;/p&gt;
&lt;h3 id=&#34;appender-not-ready&#34;&gt;Appender Not Ready&lt;/h3&gt;
&lt;p&gt;Each tenant&amp;rsquo;s WAL has an &amp;ldquo;appender&amp;rdquo; internally; this appender is used to &lt;em&gt;append&lt;/em&gt; samples to the WAL. The appender is marked
as &lt;em&gt;not ready&lt;/em&gt; until the WAL replay is complete upon startup. If the WAL is corrupted for some reason, or is taking a long
time to replay, you can determine this by alerting on &lt;code&gt;loki_ruler_wal_appender_ready &amp;lt; 1&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id=&#34;corrupt-wal&#34;&gt;Corrupt WAL&lt;/h3&gt;
&lt;p&gt;If a disk fails or the &lt;code&gt;ruler&lt;/code&gt; does not terminate correctly, there&amp;rsquo;s a chance one or more tenant WALs can become corrupted.
A mechanism exists for automatically repairing the WAL, but this cannot handle every conceivable scenario. In this case,
the &lt;code&gt;loki_ruler_wal_corruptions_repair_failed_total&lt;/code&gt; metric will be incremented.&lt;/p&gt;
&lt;h3 id=&#34;found-another-failure-mode&#34;&gt;Found another failure mode?&lt;/h3&gt;
&lt;p&gt;Open an &lt;a href=&#34;https://github.com/grafana/loki/issues&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;issue&lt;/a&gt; and tell us about it!&lt;/p&gt;
]]></content><description>&lt;h1 id="recording-rules">Recording Rules&lt;/h1>
&lt;p>Recording rules are evaluated by the &lt;code>ruler&lt;/code> component. Each &lt;code>ruler&lt;/code> acts as its own &lt;code>querier&lt;/code>, in the sense that it
executes queries against the store without using the &lt;code>query-frontend&lt;/code> or &lt;code>querier&lt;/code> components. It will respect all query
&lt;a href="/docs/loki/v2.9.x/configure/#limits_config">limits&lt;/a> put in place for the &lt;code>querier&lt;/code>.&lt;/p></description></item><item><title>Upgrade</title><link>https://grafana.com/docs/loki/v2.9.x/operations/upgrade/</link><pubDate>Mon, 08 Apr 2024 17:50:08 +0000</pubDate><guid>https://grafana.com/docs/loki/v2.9.x/operations/upgrade/</guid><content><![CDATA[&lt;h1 id=&#34;upgrade&#34;&gt;Upgrade&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;
    &lt;a href=&#34;/docs/loki/v2.9.x/setup/upgrade/&#34;&gt;Upgrade&lt;/a&gt; from one Loki version to a newer version.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;
    &lt;a href=&#34;/docs/loki/v2.9.x/setup/upgrade/&#34;&gt;Upgrade Helm&lt;/a&gt; from Helm v2.x to Helm v3.x.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
]]></content><description>&lt;h1 id="upgrade">Upgrade&lt;/h1>
&lt;ul>
&lt;li>
&lt;p>
&lt;a href="/docs/loki/v2.9.x/setup/upgrade/">Upgrade&lt;/a> from one Loki version to a newer version.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>
&lt;a href="/docs/loki/v2.9.x/setup/upgrade/">Upgrade Helm&lt;/a> from Helm v2.x to Helm v3.x.&lt;/p>
&lt;/li>
&lt;/ul></description></item></channel></rss>