Cut the Coralogix log bill per pattern

Share
Coralogix prices a gigabyte by the priority class it lands in, and a policy set on the account decides that class. 10x gives each line a stable pattern ID, computed from the log statement that printed it rather than by clustering, and records each ID's volume. Two levers move a chosen pattern and keep every line: tier down leaves the engine's routing decision inside the event so a policy moves that pattern from High to Medium, and offload sends it to a bucket the account owns, retrievable by pattern ID. Deciding which pattern to move is what 10x adds.

Coralogix bills a gigabyte at the rate of the priority class it lands in, and a message printed in a loop, a health check or a retry, pays that rate every time it repeats. Data matching no TCO policy lands in High (Frequent Search), the class that carries fast queries and real-time alerting, so a message nobody searches arrives in the class priced for searching.

10x lowers that bill by moving the messages that cost the most to a cheaper class, and holding back the ones an alert or dashboard depends on. Nothing is deleted: every line stays retrievable, and the alerts and dashboards keep working.

10x runs as a sidecar to your log forwarder (Fluent Bit, the OpenTelemetry Collector, Fluentd, and more) and reads each line before Coralogix receives it. The engine gives each line a stable pattern ID, computed from the log statement that printed it, so lines from one statement share an ID and each pattern carries its own volume. Two levers then move a chosen pattern, to a cheaper priority class or to a bucket the account owns, and both keep the line. The levers cover the sources a forwarder fronts; logs a cloud integration ships straight into Coralogix stay on the standard bill.

The 10x engine gives each line a stable pattern ID

Here are two lines from an API gateway.

2026-07-12 14:03:11 INFO api-gateway request complete route=/v1/charge status=200 bytes=1420 duration_ms=183
2026-07-12 14:03:12 INFO api-gateway request complete route=/v1/refund status=200 bytes=644 duration_ms=97

Both come from one logging statement; only the values differ. Some tokens are the same in every line the statement prints: INFO, request complete, and the field names route, status, bytes. The rest change each time: the route, the status code, the byte count, the timestamp. The engine keeps the fixed tokens, the structure, and drops the changing values; what remains is the pattern, and its hash is the pattern ID.

The engine identifies the fixed tokens from a vocabulary, the strings the code can print, compiled from application and framework code ahead of time. It does not decide by how often a token repeats, and that is what makes the ID stable: the same statement gives the same ID next week and on every host. A line with no code behind it, a raw access log, is the exception, where a changing value can pass for a fixed token and one statement yields a few IDs.

Coralogix groups logs into shared shapes too. Templates, the feature formerly called Loggregation, cluster incoming logs by their constant structure, replace the values with placeholders, and expose a template ID to filter on. The clusters come from the traffic: a template is created once a shape crosses an occurrence threshold, and an ID derived from clustering moves with the history that produced it, measured against Drain, a common clustering algorithm, in its own post.

The pattern ID does not move that way, because it comes from the vocabulary rather than the traffic. It also exists before Coralogix has seen the line, which is what lets it drive the routing decision below. A template is formed inside Coralogix, from events that have already arrived.

Each pattern has its own numbers. One high-volume INFO line from the cart service, ValkeyCartStore GetCartAsync called with userId, ID FU1__vh8hbY, ran at 16.2M lines and 24.2 GB a day over a week in a demo environment running the OpenTelemetry demo shop, about 1.5 KB per event.

At the Coralogix list rate for High (Frequent Search), $1.15 per GB, that one message is $835 over 30 days. High is where anything no policy matches lands, so that is what the pattern pays until a policy moves it. The traffic is synthetic and the rate is a list rate, so the number shows the accounting, not a saving.

Two levers that keep every line

Tier down moves a pattern from High (Frequent Search) to Medium (Monitoring), where the raw data is held in storage the account owns and stays queryable with DataPrime, alerting, and dashboarding. A query reaches those lines where they sit, with no rehydration step in front of them, which is what makes the move a down-tier. Nothing forks to get there: the events ship to the same ingest endpoint as everything else, and a policy assigns the class.

Offload sends a pattern's events out of Coralogix, so they never reach it. The forwarder writes them to an S3 bucket the account owns, as plain JSON lines any client can read, with a pattern-ID index beside them. A retrieval by pattern ID reads only the matching objects and returns the events, with nothing re-ingested. An operator who has a raw line instead of an ID pastes it into the same tool, which finds its pattern first.

The policy reads a marker the forwarder leaves in the event

On Datadog the down-tiered slice goes to a Flex index, on CloudWatch to an Infrequent Access log group, on Azure Monitor to a Basic or Auxiliary table. Each of those is a second destination, and the forwarder splits the stream to reach it. Coralogix has no second destination for a down-tier, so the forwarder's job here is narrower: leave the engine's routing decision inside the event, where a policy can match it.

That decision cannot be derived from the event's text afterwards. Whether a pattern has passed its byte budget for the current window is a fact about a stream, counted across events in the sidecar, and a rule reading one event at a time has nothing to compute it from. So the engine writes the outcome onto the event and the forwarder ships it without stripping it.

The engine's default output hands the forwarder that marker as its own field, which is what the forwarder's filters branch on. Here is what arrives.

{"applicationName": "checkout",
 "subsystemName": "tier_down",
 "text": {"message": "request complete route=/v1/charge status=200",
          "routeState": "tier_down"}}

routeState is the engine's decision for this event, and it stays inside text, the event body. Coralogix policies match on any field in the event body, nested fields included, so the marker alone is enough to key a policy on, with no label mapping. The forwarder also copies the decision to subsystemName, which an operator can see in the Coralogix UI and a policy can key on instead.

The chain was run end to end against a live Coralogix tenant, the engine feeding a Fluent Bit running the shipped recipe, with the same 160 events going through twice. With no policy in place all 160 arrived, 114 of them marked tier_down. With one policy keyed on the marker and set to Medium, 46 were left in Frequent Search, and those 46 are exactly the events the engine never marked: 6 from the capped container before its cap was crossed, and all 40 from a second container that had no cap and so was never routed.

A second test rules out the label. Two events went up in one request, same application and same subsystem, differing only in the marker. The one marked pass stayed visible and the one marked tier_down did not, which leaves the body field as the only thing the policy could have matched.

One note for anyone reproducing this: a policy is not in force the moment it is applied. A check a minute after the apply showed nothing moved, and the same query at six minutes showed the slice gone from Frequent Search.

The decision is what 10x adds

Neither lever is new: Coralogix sells the cheaper classes, and any forwarder can write to a bucket. The open part is deciding which pattern is safe to move, and keeping that choice right as the logs change.

Nothing moves by default. An operator sets a monthly budget; the per-container limits from it are proposed by the log10x MCP in a pull request that a person merges, and a container with no budget is left alone. Inside a budgeted container, only a pattern that dominates recent volume, above a fifth of it over a few-minute window, has its excess moved.

Everything under that share stays in High, along with the first events of each window and, from severity floors, at least half of a pattern's Error events. A wrong choice loses no data: every moved line is queryable in Medium or retrievable from the bucket by ID.

Tier down is the first lever on Coralogix and offload the fallback, because Medium keeps alerting and dashboarding, so a rule that reads a down-tiered pattern keeps working. Offload takes the events out of Coralogix entirely, so anything reading them stops seeing them, and a pattern behind an alert is exempted from that lever. The engine also reports each pattern's pre-move volume as a metric series to the account's own metrics backend, so the volume stays on a dashboard either way.

The budgets and exemptions are committed files, keyed by pattern ID, each with a reason and an expiry, and with the per-pattern history they are the record behind every move. To see it on real data, the log10x MCP server installs into an AI client and runs against a live demo with no account; the table above came from there. 10x decides which pattern to move, and the platforms enforce it.