Cut the Azure log bill per pattern
Azure Monitor charges most to ingest logs into the Analytics plan, billed per GB, and every repeat of a message pays again. 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 and cost. Two levers move a chosen pattern and keep every line: tier down sends it to the cheaper Basic or Auxiliary table plan, and offload sends it to a storage account the subscription owns, retrievable by pattern ID. The routing is ordinary; deciding which pattern to move is what 10x adds.
The expensive line on an Azure Monitor bill is ingestion into the Analytics plan, the table plan that keeps logs fully queryable. The rate includes the first 31 days of retention, so for a high-volume log the bill is almost all ingestion. Ingestion is billed per GB, so a message printed in a loop, a health check or a retry, pays the full rate every time it repeats, whether or not anyone reads it.
10x lowers that bill by moving the messages that cost the most to cheaper storage, and holding back the ones an alert or dashboard depends on. A large system's volume is dominated by a few messages that repeat, a health check, a retry, a request-complete line, printed millions of times each. Nothing is deleted: every line stays retrievable, and the alerts and dashboards keep working. The goal is a smaller Azure bill with the same investigation experience, and the hard part it solves is choosing which messages are safe to move.
10x runs as a sidecar to your log forwarder (Fluent Bit, the OpenTelemetry Collector, Fluentd, and more) and reads each line before Azure Monitor 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 and cost. Two levers then move a chosen pattern, to a cheaper table plan or a storage account the subscription owns, and both keep the line. The levers cover the sources a forwarder fronts; logs that Azure diagnostic settings, Application Insights, or the Azure Monitor Agent send straight to the workspace 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=97Both 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.
Azure's KQL reduce operator clusters similar lines into shared shapes too, returning a pattern, a count, and a representative line, but it clusters per query, over the table and time range the query names, with nothing durable behind the clusters and no cost history. A cluster can change when the lines it was built from change, measured against Drain, a common clustering algorithm, in its own post. The pattern ID does not change that way, because it comes from the vocabulary rather than the traffic. When a deploy adds a field or writes a value into the text, a log alert that matched on that text stops firing, but the pattern ID and its cost history continue; only rewriting the message itself gives a new ID.
Each pattern has its own numbers. Here is one, measured over a week in a demo environment running the OpenTelemetry demo shop, priced at Azure list rates for East US: Analytics ingestion $2.30 per GB, with the first 31 days of retention included. It is a high-volume INFO line from the cart service, ValkeyCartStore GetCartAsync called with userId, ID FU1__vh8hbY, about 1.5 KB per event.
| Measured | Per day | Per 30 days |
|---|---|---|
| Lines | 16.2M | 486M |
| Volume | 24.2 GB | 726 GB |
| Ingestion | $55.66 | $1,670 |
Ingestion is the whole bill for this pattern across the window, since the Analytics plan carries 31 days of retention in that rate and does not charge Analytics queries by the byte. The traffic is synthetic and the rates are list rates, so the numbers show the accounting, not a saving.
Two levers that keep every line
Tier down sends a pattern to the cheaper Basic or Auxiliary table plan, where ingestion drops from the Analytics rate of $2.30 per GB to $0.50 or $0.05. The engine marks the events to move and the forwarder writes them to a table on the cheaper plan; a workspace sets the plan per table, so the rest stays on Analytics. The lines stay queryable in the same Logs interface with single-table KQL, billed by the gigabyte scanned, and an asynchronous search job reaches data past the interactive window. Queries run fast on Basic and slower on Auxiliary, so tier down suits the patterns an incident query might still want; a pattern behind a full alert stays on Analytics.
Offload sends a pattern's events out of Azure Monitor, so they never reach the workspace and never pay ingestion. The forwarder writes them to an Azure Storage account the subscription 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 decision is what 10x adds
Neither lever is new: Azure sells the cheaper table plans, and any forwarder can write to a storage account. 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 on the Analytics plan, 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 the cheaper table or retrievable from the storage account by ID.
One exemption keeps alerts working. A pattern read by a log alert stays on the Analytics plan, since the cheaper plans carry only limited alerting, Basic a simple rule and Auxiliary none. The engine also reports each pattern's pre-move volume as a metric series, with Azure Monitor among the backends, so the real number stays on a dashboard.
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.