OpenObserve vs. Loki: query behavior, retention, and operating costs
Compare Loki and OpenObserve using recorded query examples, then assess labels, field types, cold queries, retention, and the work of running each backend.
Loki is a natural fit for a team that already works in Grafana and usually starts an investigation with a known service and namespace. Labels narrow the search, and the engineer looks for errors in a recent time window. Replacing the backend solely to change that familiar workflow may not offer much value.
OpenObserve is attractive to teams that frequently analyze structured business logs: find an order across services, add a few fields, and write a SQL aggregation. Both teams might generate 1 TiB a day, yet place very different demands on indexing, scanning, and the people writing queries.
Start with the existing stack and identify the queries that repeatedly cause trouble. One slow historical search may not justify a migration. A daily investigation that requires awkward workarounds can. The small query examples below show how those differences emerge before capacity and cost enter the comparison.
The original October 9, 2026 validation used Loki 3.7.8 and OpenObserve OSS 1.0.4. It tested query semantics on a small local dataset, not production load or a performance ranking. The full limitations and saved results are linked at the end.
Published by Guance. Product documentation was checked again on October 13, 2026; the recorded test results below retain their original version and scope.
Overview
| Decision | Loki | Self-hosted OpenObserve |
|---|---|---|
| Starting a search | Select streams with stable labels, then filter their contents or metadata | Query structured event fields with SQL |
| Request identifiers | Prefer structured metadata to high-cardinality stream labels | Store identifiers as fields; check whether the configured indexes accelerate the query |
| Invalid numeric values | Pipeline errors need an explicit policy; metric queries cannot contain errors | TRY_CAST can keep a query running, but invalid values become NULL |
| Operating work | Maintain labels, storage, query capacity, and Grafana integrations | Maintain ingestion, storage, indexes, query capacity, and migrated workflows |
Find a log by trace ID
Suppose an engineer receives a trace_id. In Loki, a selector such as {service="checkout",env="prod"} can narrow the relevant streams before filtering the ID. Where the ID is stored matters.
Making every trace ID a stream label creates a large number of streams. Loki recommends structured metadata for high-cardinality fields of this kind. Those values can still be filtered, but they do not define streams in the label index. Structured metadata also requires the appropriate storage configuration: the recorded test used TSDB with schema v13. Changing the collector's field mapping alone does not establish that the backend is configured to handle it. See Loki labels and structured metadata.
The test stored service and env as stream labels and trace_id as structured metadata. This query returned the target event without parsing JSON from the log body:
{service="checkout",env="prod"}
| trace_id="00000000000000000000000000000001"
OpenObserve stored trace_id as a field. The corresponding SQL query found the same event:
SELECT event_id FROM query_samples
WHERE trace_id = '00000000000000000000000000000001'
Both queries worked on the small dataset. The service and environment selector matters to the Loki workflow: if the engineer already knows the service, it is a useful starting point. If the only available clue is an order ID with an unknown source, the search scope widens. When that happens frequently, broad-query latency and resource use deserve more weight in selection.
OpenObserve's field and full-text indexes can accelerate eligible SQL queries. That does not mean every predicate uses an index. Expressions, time ranges, and data distribution affect the amount scanned. Familiar SQL reduces the learning burden, but query resources still need to be sized for actual usage. See OpenObserve's Tantivy index documentation.
Preserve matching and error semantics
The fixtures contained upstream timeout, upstream timed out, and connection timeout. Loki's |= "timeout" returned two events. OpenObserve's message LIKE '%timeout%' also returned two. Neither substring expression matched timed out.
That result compares the specified substring matches, not the products' general full-text tokenization capabilities. If an existing alert relies on tokenized search and a migration replaces it with a substring filter, the screen can still display plausible results while the alert's coverage changes. Preserve the intended set of errors, not just the text typed into the old search box.
Now consider counting requests slower than 900 milliseconds. The seven shared JSON fixtures included string values such as "1200", "1600", and "n/a", as well as a missing duration. Loki received one additional malformed JSON line to test its parser error path.
This LogQL query returned HTTP 400 in the recorded test because a pipeline error occurred during numeric filtering:
sum(count_over_time(
{service="checkout",env="prod"}
| json | duration_ms > 900 [1h]
))
Filtering errors after the numeric operation produced the expected count of two:
sum(count_over_time(
{service="checkout",env="prod"}
| json | duration_ms > 900 | __error__="" [1h]
))
The position of | __error__="" matters. Filtering immediately after json cannot remove an error introduced by a later numeric conversion. Count excluded events separately; a drop in “slow requests” should not conceal a rise in invalid duration fields. See LogQL query pipelines and errors.
With OpenObserve, a direct CAST(duration_ms AS DOUBLE) returned HTTP 200 but an empty hits array, is_partial=true, and a function_error describing the failed conversion of n/a. The following query returned the two expected events with is_partial=false:
SELECT event_id FROM query_samples
WHERE TRY_CAST(duration_ms AS DOUBLE) > 900
ORDER BY event_id
TRY_CAST turns a failed conversion into NULL, allowing this query to continue. It does not repair the event. Invalid and missing values do not satisfy the threshold, so data-quality counts remain necessary.
This is also a choice about where to perform maintenance. For stable formats, normalizing numbers and status values during ingestion keeps common queries simpler. With many frequently changing formats, preserving the original and parsing at query time offers flexibility but leaves processing cost and error handling in the query path. Both products support processing; the team has to decide which layer it wants to maintain.
Test concurrency and cold reads separately
A few fixtures can establish semantics. They cannot determine which platform best serves 1 TiB/day, 30-day retention, and ten engineers investigating at once.
Looking for a recent service error, finding a request across one day, and aggregating endpoints over seven days are different workloads. The last may scan substantially more historical data. A first query against remote objects also differs from a repeated query served largely from cache.
Production adds ongoing ingestion and scheduled queries. Ten seconds of latency might be spent waiting for a query slot, reading objects, decompressing data, or aggregating results. Resource contention calls for a different response from excessive scanning. Low-frequency historical analysis may tolerate a slower result; interactive incident investigation often cannot. Those workloads need not share the same response-time target.
A low object-storage rate explains only part of the cost. Keeping additional compute and cache capacity online to accelerate a rare broad query may be hard to justify. If that query becomes a daily workflow for several engineers, the same investment can make sense. Apply that reasoning to both Loki and OpenObserve.
Apply the same retention policy before comparing storage
Consider a product-independent model: 1 TiB of raw logs per day, with 80% of routine logs retained for three days and 20% of important logs retained for 30 days. At steady state, the raw retained volume is:
1 TiB × (80% × 3 + 20% × 30) = 8.4 TiB
Keeping everything for 30 days would retain 30 TiB of raw data. Neither figure includes compression, indexes, caches, write-ahead logs (WALs), or replicas. Multiplying both by one disk price does not produce comparable platform costs. The calculation shows that identifying data value can change the capacity requirement before an engine is selected.
A retention-policy change also takes time to affect the physical footprint. Storage charges do not instantly reach the new steady state while older objects remain. Changing the interface's default search window to three days does not delete everything older than three days.
Existing skills and configuration have value too. A team with established Grafana dashboards, alert rules, label conventions, and operational experience should include the cost of moving those assets in an OpenObserve proposal. A team whose primary users already analyze structured data in SQL should count the work needed to learn and maintain LogQL, label policies, and parsing pipelines. Familiarity is a cost advantage when the existing assets are named and valued explicitly.
When each option fits
Loki remains a reasonable choice when Grafana is deeply embedded, labels are consistent, and most investigation starts with recent events from a known service. Existing dashboards, rules, and operator experience continue to serve that workflow.
OpenObserve deserves attention when structured-field analysis is frequent, SQL is already familiar, and the team is prepared to run the query and storage backend. The advantage comes from matching the analysis workflow to the team's skills. Object storage does not make every possible query inexpensive.
Revisit the decision when the workload changes. A system built around service-local troubleshooting may later need daily cross-service business analysis. That can justify different indexing, a new query design, or another backend. The frequency and importance of the new work should determine how much change is worthwhile.
Compare Guance with the full self-hosted cost
Both self-hosted options require field processing, retention, queries, and alert configuration. Someone must also own backend upgrades, capacity, and recovery. Guance supplies Pipeline processing, multiple log indexes, and related investigation features as part of its platform. With Guance SaaS, backend operation moves to the provider, which can reduce the work of maintaining ingestion, storage, and query clusters. See the Guance Pipeline guide and log documentation.
The application team still needs correct event time, service identity, and trace IDs. Related traces must actually be collected and retained. A managed backend removes infrastructure responsibilities; it does not remove ownership of the data's meaning.
If an established Loki deployment needs little additional maintenance, keeping it may cost less. If application operations staff would need to build and support a log platform alongside their existing work, compare Guance's service fee with machines, implementation time, and ongoing support together. Ready-to-configure queries and alerts can let that team start using the data earlier, provided collection and field mapping are validated.
Saved samples, versions, and query results (Chinese explanatory notes).
The original tests ran locally on single-node instances. Loki's WAL protection was triggered by local disk usage; query semantics were subsequently checked in a disposable instance with WAL disabled. The results establish neither durability nor throughput. Both products received the same seven valid JSON records; only Loki received the additional malformed-JSON record for its error-path test.
Contact us
Join the community
to join the community
Try Guance
Start online and pay only for what you use.
Get startedChoose a Guance plan