Skip to main content
emnode
Monitoring

Filter resource logs with diagnostic settings and DCR transformations

One capability across every Azure resource: collect the log categories you actually need to investigate an incident, and drop the noise before it lands in Log Analytics, so you keep the visibility that protects mean-time-to-detect without paying to ingest data nobody will ever query.

14 min·10 sections·AZURE

Last reviewed

Filtering resource logs: the basics

What does an unfiltered diagnostic setting actually cost you?

Azure resources do not collect their resource logs by default. To see what a key vault, an App Service or a SQL database is doing, you create a diagnostic setting that routes its log categories to a Log Analytics workspace. The fast way to do this is to tick the 'allLogs' category group, point it at the workspace and move on, and that is exactly what most teams do. It works, and it is also how a monitoring bill quietly triples: every category, including the high-volume, low-value ones, now streams into the workspace and is billed per gigabyte ingested, whether or not anyone ever queries it.

There are two distinct levers, and they sit at two different stages. The first is the diagnostic setting itself: it decides which whole log categories leave the resource. Choosing the categories you genuinely investigate with, rather than 'allLogs', is the blunt, free cut. The second lever is the transformation: a diagnostic setting cannot filter within a category, so when a category you do need is mostly noise (successful health-probe requests, routine informational rows), an ingestion-time transformation in a data collection rule (DCR) drops those rows with a KQL 'where' clause before they are written and billed. Microsoft's own cost guidance names exactly these two moves: collect only the categories you need, and use transformations to filter the rest.

The work, then, is not to turn logging off. Visibility is the point: when an incident hits, the team needs the logs that let them detect and resolve it quickly. The work is to separate the log data that shortens an investigation from the data that only inflates the bill, keep the first, and filter the second at the door rather than paying to store it and querying around it forever.

In this lesson you will learn how Azure diagnostic settings and data collection rule transformations decide what resource log data reaches Log Analytics, how to find the settings that are ingesting more than anyone queries, and how to filter them down without losing the visibility you rely on during an incident. You will see the two levers in order: choosing log categories at the diagnostic setting, then dropping noisy rows with an ingestion-time transformation.

Fun fact

The category nobody chose

When you create a diagnostic setting, Azure offers a category group called 'allLogs', and ticking it is genuinely the path of least resistance: one checkbox, every category, done. The catch is that 'allLogs' is a living definition. If Microsoft adds a new, chattier log category to a service, every diagnostic setting using 'allLogs' starts collecting and billing it automatically, with no change on your side and no decision by anyone. A setting that was reasonable the day it was created can grow its own ingestion months later. It is the rare bill increase that arrives entirely without a cause you can point to, which is exactly why naming the categories you actually want is the quiet, durable fix.

Finding log ingestion that nobody queries

Priya runs the platform team at a SaaS company whose Azure Monitor bill has roughly doubled over two quarters while the estate grew far less than that. Nobody changed the monitoring strategy, so the growth is coming from somewhere structural.

Rather than guess, Priya starts by asking the workspace which tables are actually driving the ingestion, so the noisy, rarely-queried categories can be separated from the logs the on-call team genuinely relies on before anything is changed.

Ask the workspace which tables ingested the most data over the last month. The 'Usage' table records billed ingestion per table, so this is the bill, not a guess.

$ az monitor log-analytics query \ --workspace "$WORKSPACE_ID" \ --analytics-query "Usage | where TimeGenerated > ago(30d) | summarize GB=sum(Quantity)/1000 by DataType | top 5 by GB desc" \ -o table
DataType GB
----------------------------- --------
AzureDiagnostics 812.4
AppServiceHTTPLogs 418.9
AppServiceConsoleLogs 27.1
# AzureDiagnostics is mostly verbose categories nobody queries. AppServiceHTTPLogs is mostly 200-OK health probes.

The top two tables are the whole story: one over-broad diagnostic setting and one category that is mostly successful health-probe traffic. These are the cut, and neither is a category the on-call team investigates with.

How a log row reaches Log Analytics, and where you can stop itdeep dive

A resource log row passes through two control points before it is stored and billed. The first is the diagnostic setting on the resource. It is a coarse, category-level switch: you enable whole categories, either by naming them individually or by selecting a category group such as 'allLogs' (every category) or 'audit' (only the categories that record customer interactions with data or settings). What you cannot do at the diagnostic setting is filter within a category. As Microsoft's documentation states plainly, diagnostic settings do not allow granular filtering within a selected category. If a category is half noise, the diagnostic setting will still send all of it.

The second control point is the transformation, and it exists precisely to fill that gap. A transformation is a Kusto Query Language (KQL) statement carried in a data collection rule, applied to each incoming record at ingestion time, after the data leaves the resource but before it is written to the workspace. Every transformation query begins with 'source', the virtual table that represents the input stream, and a 'where' clause filters it: if a record does not match the clause, it is not sent to the destination, so 'source | where ...' drops the rows that fail the test. Because resource logs from diagnostic settings do not carry their own DCR, the transformation for them lives in a special object called the workspace transformation DCR: there is exactly one per workspace, its 'kind' is 'WorkspaceTransforms', it has no data sources, it names a single workspace destination, and it carries one data flow per table you want to filter.

Two practical facts shape the design. A transformation runs against every record, so it must be fast: aim for under a second, because a transformation that takes more than twenty seconds can cause data loss. And the billing rule is worth knowing exactly: for standard Analytics tables, transformations are usually free, but if one reduces ingested data by more than fifty per cent you are charged a data-processing fee on the amount filtered above that half, and that fee does not apply when Microsoft Sentinel is enabled on the workspace. The category choice at the diagnostic setting is the free, blunt cut; the transformation is the precise, mostly-free cut for the categories you must keep but want to thin.

What is the impact of leaving diagnostic settings unfiltered?

The direct impact is spend with no return. Log Analytics charges per gigabyte ingested, so every verbose category that nobody queries is a recurring bill for data that sits unread. Unlike a one-off mistake, this compounds: the setting keeps ingesting every day, and because 'allLogs' picks up any new category Microsoft adds, an unfiltered setting can grow its own cost over time without anyone touching it.

The second-order impact is on the thing logging exists for: visibility. A workspace stuffed with routine noise is slower and more expensive to query during an incident, and a team that has watched the bill climb is tempted into the wrong fix, switching whole categories off to save money. That is the genuinely costly outcome, because it trades a predictable monthly charge for a longer mean-time-to-detect and mean-time-to-resolve when something breaks. The cost of flying blind during an outage, slower detection, slower diagnosis, more customer impact and more engineering hours, is far larger and far less predictable than the ingestion it saved.

Filtering at the door avoids both. Choosing the categories the team actually investigates with, and dropping the noisy rows before they are billed, removes the unread spend while leaving every log the team relies on in place. The estate ends up cheaper to run and faster to query, and once the remaining volume is predictable, Microsoft's commitment tiers can take a further documented discount of up to roughly thirty per cent on top. Done well, this is a cost cut that improves visibility rather than spending it.

How do you filter logs without losing visibility?

Work the two levers in order, blunt cut first, precise cut second, and let what the on-call team actually queries decide every choice. The order matters: narrow the categories before you reach for a transformation, because the free cut often removes most of the volume on its own.

1. Measure ingestion by table, against real usage

Query the workspace 'Usage' table to rank ingestion by DataType over the last month, then annotate each top table with whether on-call has queried it in an actual incident. This usage-weighted view, not a flat percentage target, is the source of truth for what is waste and what is signal.

2. Narrow the diagnostic settings to the categories you use

Replace 'allLogs' with the specific log categories the team investigates with. This is the blunt, free cut: it stops whole categories of unread data at the resource and, because you are naming categories explicitly, it also immunises the setting against silently picking up new categories later. Run 'az monitor diagnostic-settings categories list' on a resource to see exactly what it can emit before you choose.

3. Thin the categories you must keep with a transformation

For a category you need but that is mostly noise, such as web-server logs dominated by successful health probes, add an ingestion-time transformation that drops the routine rows with a KQL 'where' clause and keeps the errors. Test the query in Log Analytics against real data first, confirm it keeps every row on-call would need, then move it into the workspace transformation DCR. Keep it fast, well under a second.

4. Make filtered-by-default the standard, then commit

Bake the chosen categories into the Bicep or Policy that provisions diagnostic settings, so new resources arrive filtered rather than copying an over-broad setting. Once the remaining ingestion is predictable, review Azure Advisor and the workspace 'Usage and estimated costs' view and move to a commitment tier for the documented discount.

# 1. Diagnostic setting: collect only the categories you investigate with,
#    not the 'allLogs' category group. Example for an App Service.
az monitor diagnostic-settings create \
  --name app-logs-filtered \
  --resource "$APP_RESOURCE_ID" \
  --workspace "$WORKSPACE_ID" \
  --logs '[{"category":"AppServiceHTTPLogs","enabled":true},{"category":"AppServiceConsoleLogs","enabled":true}]'

# 2. Workspace transformation DCR: drop the noisy rows from a category you keep.
#    Here, drop successful (2xx/3xx) health-probe requests from the HTTP logs,
#    keep everything else, so on-call still sees every 4xx/5xx error.
#    transformKql must be a single line and starts with 'source'.
cat > dcr.json <<'JSON'
{
  "kind": "WorkspaceTransforms",
  "location": "eastus",
  "properties": {
    "dataSources": {},
    "destinations": {
      "logAnalytics": [
        { "name": "law", "workspaceResourceId": "__WORKSPACE_ID__" }
      ]
    },
    "dataFlows": [
      {
        "streams": [ "Microsoft-Table-AppServiceHTTPLogs" ],
        "destinations": [ "law" ],
        "transformKql": "source | where ScStatus >= 400"
      }
    ]
  }
}
JSON
sed -i "s|__WORKSPACE_ID__|$WORKSPACE_ID|" dcr.json

az monitor data-collection rule create \
  --name workspace-transforms \
  --resource-group "$RG" \
  --body @dcr.json

# Note: validate the transform query in Log Analytics against real rows first,
# confirm it keeps every error on-call needs, then deploy. There is one
# workspace transformation DCR per workspace; add a data flow per table to it.

Quick quiz

Question 1 of 5

An App Service diagnostic setting uses the 'allLogs' category group and is the largest source of ingestion in the workspace. What is the first, free lever to pull?

You can now treat resource logging as a portfolio of decisions rather than a default: measure ingestion by table against what on-call actually queries, narrow each diagnostic setting to the categories you investigate with (the free, blunt cut), thin the busy categories you keep with an ingestion-time transformation in the workspace transformation DCR (the precise cut), and bake the result into provisioning so new resources arrive filtered. The saving is real and the visibility that keeps incidents short stays intact.

Back to the library