Log Analytics workspaces — design, table plans, retention, access control, daily cap, and querying from TypeScript
How to set up and query a Log Analytics workspace — start with one workspace, Analytics vs Basic vs Auxiliary table plans and what each gives up, 30-day default retention to two years interactive and 12 years total, workspace vs resource-context access and the Log Analytics roles, the daily cap that stops collection, query limits that return 429, partial results, and @azure/monitor-query-logs with Entra.
markdownreferencev1T4F5-GBEPstage: designtrust: officialservice: monitorinterface: sdkinterface: iacLog Analytics workspace designsingle workspacetable plans Analytics Basic AuxiliaryBasic logs 30 days queryretention 30 days default90-day default tablesanalytics retention two yearstotal retention 12 yearslong-term retention search jobsaccess control modeenableLogAccessUsingOnlyResourcePermissionsresource-contextworkspace-contextLog Analytics ReaderLog Analytics ContributorLog Analytics Data Readerworkspaces/query/readtable-level RBACdaily capdailyQuotaGbreset hourquery concurrency 5query 429200 queries per 30 secondsquery timeout 3 minutes 10 minutesPartialError@azure/monitor-query-logsLogsQueryClient queryWorkspaceLogsQueryResultStatus PartialFailureworkspace customerIdMicrosoft.OperationalInsights/workspaces disableLocalAuth
Log Analytics workspaces
Design
Start with one workspace (per Entra tenant); data volume doesn't slow queries. Split only for a real reason: data residency per region, egress cost, separate billing or commitment tiers, or resilience (ingest into workspaces in two regions). Workspace-based Application Insights, diagnostic settings and agents all write into it.
Table plans and retention
| Plan | For | Trade-offs |
|---|---|---|
| Analytics | Monitoring, alerts, latency-sensitive queries | Full features; highest ingestion cost |
| Basic | Troubleshooting data | Queries billed; 30 days of query access; summary rules and resource-context queries limited |
| Auxiliary / Lake | High-volume, verbose data kept cheaply | Alerts stop working on the table; queries billed |
- Tables keep data 30 days by default (some built-in tables 90). Analytics retention extends to two years; total retention (including low-cost long-term retention, reached through search jobs) to 12 years.
- Shortening total retention waits 30 days before deleting, so a mistake can be reverted; lengthening applies to data not yet removed.
Access
- Access control mode (
features.enableLogAccessUsingOnlyResourcePermissions): Use resource or workspace permissions (true; the default since March 2019) lets anyone withreadon a resource query that resource's logs in resource context; Require workspace permissions (false) needs a workspace or table role. - Roles: Log Analytics Reader (read data and monitoring settings), Log Analytics Contributor (also configure), Log Analytics Data Reader (minimum query rights; for granular, condition-based RBAC). Querying needs
Microsoft.OperationalInsights/workspaces/query/read; resource-context needsMicrosoft.Insights/logs/<table>/read. - Resource context works only for records with
_ResourceId, only on Analytics tables, and for Application Insights only when it's workspace-based. features.disableLocalAuthturns off the workspace's shared-key authentication.
Daily cap
A daily cap stops collecting billable data in Analytics and Basic tables until the workspace's reset hour (fixed per workspace, not configurable); some data beyond the cap still lands. It's a budget guard, not a cost-saving tool: alert on it, because monitoring and alerts go blind once it's reached.
Querying from TypeScript
import { DefaultAzureCredential } from "@azure/identity";
import { LogsQueryClient, LogsQueryResultStatus, Durations } from "@azure/monitor-query-logs";
const credential = new DefaultAzureCredential();
// Query: needs Log Analytics Reader (or table/resource-level read) on the workspace.
export async function failedRequests(workspaceId: string): Promise<void> {
const logs = new LogsQueryClient(credential);
const kql = "AppRequests | where Success == false | summarize failures = count() by bin(TimeGenerated, 5m) | order by TimeGenerated desc";
const result = await logs.queryWorkspace(workspaceId, kql, { duration: Durations.oneHour }, { serverTimeoutInSeconds: 120 });
if (result.status === LogsQueryResultStatus.Success) {
for (const row of result.tables[0]?.rows ?? []) console.log(row);
} else {
// PartialFailure: some data came back (limits, timeouts); don't treat it as complete. Full failures throw.
console.warn(result.partialError.code, result.partialError.message);
}
}
workspaceId is the workspace's customer ID (a GUID), not its resource ID; use queryResource with a resource ID for resource-context queries.
| Limit or error | Detail |
|---|---|
| Concurrency | 5 concurrent Analytics queries per user (2 for Basic/Auxiliary); more wait in a FIFO queue |
| 429 | Queued over 3 minutes, more than 200 queued, or over 200 queries per 30 seconds per user |
| Timeout | 3 minutes by default, up to 10 (serverTimeoutInSeconds) |
| Partial results | A non-fatal error during execution: HTTP 200 with tables plus an error of code PartialError; the SDK returns PartialFailure |
| Cross-resource | At most 100 workspaces and Application Insights resources in one query |