Azure Monitor Logs
is based on Azure Data Explorer
, and log queries are written by using the same Kusto Query Language (KQL). This rich language is designed to be easy to read and author, so you should be able to start writing queries with some basic guidance.
Kusto Query Language (KQL)
contains native support for creation, manipulation, and analysis of multiple time series. With KQL, you can create and analyze thousands of time series in seconds, enabling near real time monitoring solutions and workflows.
The Kusto Query language (KQL)
is having a few interesting functions for Anomaly detection and forecasting.
In this article I will explain how to use these functions for Azure platform resource performance metric data
stored in Logs
in a Log Analytics Workspace
.
You can export the Azure platform metrics
from the Azure monitor pipeline to Azure Monitor Logs (and thus Log Analytics).
The export can be configured using the diagnostic setting for an Azure resource
The Azure platform metrics
performance data is stored in the AzureMetrics table contains Metric data emitted by Azure services that measure their health and performance.
A regular query to display a timechart
for the Average CurrentConnections
for an Application Gateway could be:
AzureMetrics
| where ResourceId =~ "**your Application Gateway resource id**"
| where MetricName =~ "CurrentConnections"
| summarize
avg = avg(Average)
by
ResourceId=tolower(ResourceId),
bin(TimeGenerated, 1h)
| render timechart
The first step in time series analysis is to partition and transform the original telemetry table to a set of time series. The table usually contains a timestamp column, contextual dimensions, and optional metrics. The dimensions are used to partition the data. The goal is to create thousands of time series per partition at regular time intervals.
This is done by using the make-series operator.
make-series CurrentConnections=avg(avg) on TimeGenerated from ago(30d) to now(-1d) step 1h
In this example the average CurrentConnections
are calculated on the Timestamp field TimeGenerated
from 30 days ago
until yesterday
it will generate a timeseries with an interval of 1h
samples.
AzureMetrics
| where ResourceId =~ "**your Application Gateway resource id**"
| where MetricName =~ "CurrentConnections"
| summarize
avg = avg(Average)
by
ResourceId=tolower(ResourceId),
bin(TimeGenerated, 1h)
| project
avg,
ResourceId,
TimeGenerated
| make-series CurrentConnections=avg(avg) on TimeGenerated from ago(30d) to now(-1d) step 1h
The result shows a set of time series, the average
of the CurrentConnections
with an interval of 1 hour
from 30 days
ago until yesterday
.
You can add the render operator to viualize the results in a timechart:
AzureMetrics
| where ResourceId =~ "**your Application Gateway resource id**"
| where MetricName =~ "CurrentConnections"
| summarize
avg = avg(Average)
by
ResourceId=tolower(ResourceId),
bin(TimeGenerated, 1h)
| project
avg,
ResourceId,
TimeGenerated
| make-series CurrentConnections=avg(avg) on TimeGenerated from ago(30d) to now(-1d) step 1h
Now it is getting interesting. The Kusto query language has a function to decompose the timeseries using a well-known decomposition model, where each original time series is decomposed into seasonal, trend, and residual components and add predicts the values of the future timeline to it.
This is done using the series_decompose_forecast() function.
series_decompose_forecast(Series, Points, [ Seasonality, Trend, Seasonality_threshold ])
Note that I’ve added some variable declerations in the beginning: min_t, max_t, dt and horizon
The make-series
operator is changed to extend the period to 7 days in the future
(max_t+horizon).
The Forecast is added with the series_decompose_forecast() function based on the CurrentConnections
in the time series.
The number of points are calculated from the number of days
în the forecast devided
by the interval
: ‘horizon / dt’
let min_t = ago(30d);
let max_t = now(-1d);
let dt = 1h;
let horizon=7d;
AzureMetrics
| where ResourceId =~ "**your Application Gateway resource id**"
| where MetricName =~ "CurrentConnections"
| summarize
avg = avg(Average)
by
ResourceId=tolower(ResourceId),
bin(TimeGenerated, 1h)
| project
avg,
ResourceId,
TimeGenerated
| make-series CurrentConnections=avg(avg) on TimeGenerated from min_t to max_t+horizon step dt
| extend Forecast = series_decompose_forecast(CurrentConnections, toint(horizon / dt))
| render timechart
The series_decompose_forecast() function adds the series for the number of points stated in the function. Resulting in a cool timechart with a future Forecast
timeline:
Similar to the series_decompose_forecast() the function series_decompose_anomalies() is based on series decomposition. The function takes an expression containing a series (dynamic numerical array) as input, and extracts anomalous points with scores.
series_decompose_anomalies (Series, [ Threshold, Seasonality, Trend, Test_points, AD_method, Seasonality_threshold ])
let min_t = ago(30d);
let max_t = now(-1d);
let dt = 1h;
AzureMetrics
| where ResourceId =~ "**your Application Gateway resource id**"
| where MetricName =~ "CurrentConnections"
| summarize
avg = avg(Average)
by
ResourceId=tolower(ResourceId),
bin(TimeGenerated, dt)
| project
avg,
ResourceId,
TimeGenerated
| make-series CurrentConnections = avg(avg) on TimeGenerated from min_t to max_t step dt
| extend (Anomalies) = series_decompose_anomalies(CurrentConnections, 1.5, -1, 'linefit')
| render timechart
The result is nice, adding a Anomalies
timeline for the anomalies. -1
showing a lower
anomaly, +1
showing a higher
anomaly.
The only disadvantage is that the anomalies is hard to see if the values of the timeseries are higher.
You can fix the scaling of the Anomalies
timeline by taking a toscalar() of the average overtime, and apply a series_multiply() to the time series:
let grain = 1h;
let metric_scale = toscalar(
AzureMetrics
| where ResourceId =~ "**your Application Gateway resource id**"
| where MetricName =~ "CurrentConnections"
| summarize
avg = avg(Average)
| summarize avg(avg));
AzureMetrics
| where ResourceId =~ "**your Application Gateway resource id**"
| where MetricName =~ "CurrentConnections"
| summarize
avg = avg(Average)
by
ResourceId=tolower(ResourceId),
bin(TimeGenerated, 1h)
| project
avg,
ResourceId,
TimeGenerated
| make-series CurrentConnections = avg(avg) default = 0 on TimeGenerated from ago(30d) to now(-1d) step grain
| extend (anomalies) = series_decompose_anomalies(CurrentConnections, 1.5, -1, 'linefit')
| extend anomalies =
series_multiply(metric_scale, anomalies) // multiplied for visualization purposes
| render timechart
The result is showing a easier interpretable output:
The time series analysis functionality is realy easy to use and showing lost of potential.
In a future post, I will show you can dynamically visualize the data for Azure Platform Resources using Azure Workbooks.