Consultas de referencia para Azure Log Analytics · Sentinel · ADX
// ✅ Siempre filtrar primero, luego summarize CultivaUsageLogs | where Timestamp > ago(24h) | where ErrorCode == "" // sólo peticiones OK | summarize TotalTokens = sum(TokensUsed), TotalReqs = count(), P95LatencyMs = percentile(LatencyMs, 95) by ServiceName | top 10 by TotalTokens desc
ago(24h) antes de summarize — seguro en tablas grandes.// ⚠ ErrorCode es dynamic → cast antes de summarize by CultivaUsageLogs | where Timestamp > ago(7d) | where isnotempty(tostring(ErrorCode)) | extend ErrorStr = tostring(ErrorCode), // cast dinámico → string ClientStr = tostring(ClientId) | summarize ErrorCount = count() by ClientStr, ErrorStr | top 20 by ErrorCount desc
tostring() obtendrías "is of a 'dynamic' type" en el by.CultivaUsageLogs | where Timestamp between (datetime(2026-06-13) .. datetime(2026-06-14)) | extend Hour = bin(Timestamp, 1h) | summarize P50 = percentile(LatencyMs, 50), P95 = percentile(LatencyMs, 95), P99 = percentile(LatencyMs, 99) by Hour, ServiceName | order by Hour asc
bin(Timestamp, 1h) para agrupar por hora — más explícito que startofhour().// Metadata es dynamic — extraer subcampo con indexación CultivaUsageLogs | where Timestamp > ago(3d) | extend Model = tostring(Metadata["model"]), Version = tostring(Metadata["api_version"]) | summarize Requests = count(), AvgTokens = avg(TokensUsed) by Model, Version, ActionType | order by Requests desc
Metadata["model"] indexa el campo JSON dinámico; el tostring() evita errores en by.// SIEMPRE verificar antes de unir tablas grandes CultivaUsageLogs | where Timestamp > ago(30d) | summarize dcount(ClientId) // → ¿cuántos clientes distintos? BillingEvents | where Timestamp > ago(30d) | summarize dcount(ClientId) // → comparar ambas cifras
E_RUNAWAY_QUERY. Comproba primero.// ✅ Especificar ambos lados del on: $left.X == $right.Y let usage_30d = CultivaUsageLogs | where Timestamp > ago(30d) | summarize TotalTokens = sum(TokensUsed) by ClientId; BillingEvents | where Timestamp > ago(30d) | summarize TotalEur = sum(AmountEur) by ClientId, PlanTier | join kind=leftouter (usage_30d) on $left.ClientId == $right.ClientId | extend EurPerToken = round(TotalEur / TotalTokens, 6) | project ClientId, PlanTier, TotalEur, TotalTokens, EurPerToken | order by TotalEur desc
let pre-agrega cada lado antes del join — reduce filas de miles a decenas.CultivaUsageLogs | where Timestamp > ago(90d) | make-series DailyReqs = count() default=0 on Timestamp step 1d | extend anomalies = series_decompose_anomalies(DailyReqs), forecast = series_decompose_forecast(DailyReqs, 7) | project Timestamp, DailyReqs, anomalies, forecast
make-series + default=0 rellena huecos — ideal para gráficas sin baches.// ❌ SIN serialize → "row set must be serialized" // ✅ | order by serializa implícitamente CultivaUsageLogs | where Timestamp > ago(30d) | summarize DailyTokens = sum(TokensUsed) by bin(Timestamp, 1d) | order by Timestamp asc // serializa implícitamente | extend CumulativeTokens = row_cumsum(DailyTokens), RowNum = row_number()
row_cumsum(), row_number(), prev(), next() requieren serialización previa.SecurityAlerts | where TimeGenerated > ago(48h) | where Severity in ("High", "Critical") | extend ClientStr = tostring(ClientId), // dynamic → string TacticStr = tostring(ExtendedProperties["tactic"]) | summarize AlertCount = count(), Tactics = make_set(TacticStr, 10) // limitar make_set a 10 by ClientStr, AlertName | top 20 by AlertCount desc
make_set(..., 10) limita la cardinalidad — evita celdas enormes en la columna de arrays.// ❌ Sin () → "matching groups" error // ✅ Con () capturando el grupo deseado SecurityAlerts | where TimeGenerated > ago(7d) | extend AlertDesc = tostring(ExtendedProperties["description"]) | extend // ✅ paréntesis alrededor del patrón IPv4 IPs = extract_all(@"(\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b)", AlertDesc) | where array_length(IPs) > 0 | mv-expand IP = IPs to typeof(string) | summarize count() by IP | top 15 by count_ desc
extract_all(@"\b\d+\.\d+\.\d+\.\d+\b", ...) falla — siempre envuelve en ().| Error KQL | Causa probable | Fix |
|---|---|---|
| is of a 'dynamic' type | Columna dynamic en by / on / order by |
tostring() / tolong() / todouble() |
| Only equality is allowed | Predicado de rango en condición de join | Pre-bucket con bin() o S2 cells, luego join |
| extractall(): matching groups | Regex sin grupo de captura () |
Añadir (): @"(\w+)" no @"\w+" |
| row set must be serialized | row_cumsum / row_number sin orden previo | | order by antes, o | serialize |
| Cannot compare values of types string and string | Valor computado en comparación | tostring() en ambos lados |
| for each left attribute, right attribute | Cláusula on incompleta | on $left.X == $right.Y explícito |
| E_LOW_MEMORY_CONDITION | Scan sin filtro previo en tabla grande | | where de rango temporal antes del summarize |
| E_RUNAWAY_QUERY | Join/agregación → demasiadas filas de salida | Comprobar dcount() en ambas tablas antes del join |
| Expected string literal in datetime() | Año suelto: datetime(2026) | datetime(2026-01-01) siempre con fecha completa |
| Unexpected token after by | Expresión compleja en summarize by | | extend primero, luego summarize by la columna |
| where Timestamp > ago(Nd) antes de cualquier | summarize
| take 20 o | top 20 by ...
by/on/order by → envuelta en tostring() / tolong()
extract_all → patrón tiene () obligatorios
dcount(ClientId) comprobado en ambas antes de ejecutar
row_cumsum, prev, next) → precedidas de | order by o | serialize
datetime(2026-06-14), nunca datetime(2026)
| project para excluirla si no es necesaria (30 KB/fila)