How to build Azure Log Analytics URL with KQL Query?

How to build Azure Log Analytics URL with KQL Query?

image.png

This is a requirement I had for one of my projects where we retrieve app insights data from several azure web apps on their flow performances. I used App Insights API for posting the KQL query and the timespan to get the flow performances which are logged using custom dimensions in the specific insights' logs.

I also had a requirement where I had to give the users who are seeing the data, the ability to see the query and timespan I used for retrieving the data they are seeing. There is no direct way Azure gives you this ability. So, I researched the Log Analytics 'Share' functionality on how they are sharing the queries in the URLs.

Here is the format they are using,

https://portal.azure.com/#blade/Microsoft_Azure_Monitoring_Logs/LogsBlade/resourceId/%2Fsubscriptions%2F{subscriptionId}%2FresourceGroups%2F{rg}%2Fproviders%2Fmicrosoft.insights%2Fcomponents%2F{resourcename}/source/LogsBlade.AnalyticsShareLinkToQuery/query/{query}

The values you see inside {} are to be replaced with actuals. However, for {query}, it does not take the KQL directly.

I eventually found that they are converting the KQL into Base64 bytes and compressing them. They have this property by default set to true => isQueryBase64Compressed=true in the query string.

So, the URL would become like this,

https://portal.azure.com/#blade/Microsoft_Azure_Monitoring_Logs/LogsBlade/resourceId/%2Fsubscriptions%2F{subscriptionId}%2FresourceGroups%2F{rg}%2Fproviders%2Fmicrosoft.insights%2Fcomponents%2F{resourcename}/source/LogsBlade.AnalyticsShareLinkToQuery/query/{query}/isQueryBase64Compressed/true

OK, Here is the code for that KQL query conversion to fit into this URL.

var queryBytes = Encoding.UTF8.GetBytes(query);
var memoryStream = new MemoryStream();
var gZipStream = new GZipStream(memoryStream, CompressionMode.Compress);

gZipStream.Write(queryBytes, 0, queryBytes.Length);
gZipStream.Close();

var compressedData = memoryStream.ToArray();

var encodedText = Convert.ToBase64String(compressedData);
encodedText = encodedText.Replace("/", "%2F");
encodedText = encodedText.Replace("+", "%2B");
encodedText = encodedText.Replace("=", "%3D");

var portalUrl = "https://portal.azure.com/#@sample.onmicrosoft.com/resource/subscriptions/guid-guid-guid-guid-guid/resourceGroups/rg-name/providers/microsoft.insights/components/app-insights-name/overview";

var (subscriptionId, rg, resourcename) = GetAzureResourceDetailsFromLogUrl(portalUrl);

url = "https://portal.azure.com/#blade/" + $"Microsoft_Azure_Monitoring_Logs/LogsBlade/resourceId/%2Fsubscriptions%2F{subscriptionId}%2FresourceGroups%2F{rg}%2Fproviders%2Fmicrosoft.insights%2Fcomponents%2F{resourcename}/source/LogsBlade.AnalyticsShareLinkToQuery/query/{encodedText}/isQueryBase64Compressed/true";

Additional Perks Here is an additional perk for getting the Subscription Id, Resource Group Name, and the Resource Name from an Azure App Insights resource URL.


private static (string, string, string) GetAzureResourceDetailsFromLogUrl(string portalUrl)
{
  var uri = new Uri(portalUrl.Replace("#", ""));

  var subscriptionId = uri.Segments[uri.Segments.ToList().FindIndex(f => f.Equals(SubscriptionsUrlSegment, StringComparison.OrdinalIgnoreCase)) + 1].Replace("/", "");

  var rg = uri.Segments[uri.Segments.ToList().FindIndex(f => f.Equals(RGUrlSegment, StringComparison.OrdinalIgnoreCase)) + 1].Replace("/", "");

  var resourceName = uri.Segments[uri.Segments.ToList().FindIndex(f => f.Equals(ComponentUrlSegment, StringComparison.OrdinalIgnoreCase)) + 1].Replace("/", "");

  return (subscriptionId, rg, resourceName);
}

Did you find this article valuable?

Support Venkatesan Rethinam by becoming a sponsor. Any amount is appreciated!