Kusto query issue with title keyword - azure-data-explorer

I assume title may be reserved word or similar but query below refuses to be parsed around c.title. Not sure what exactly the issue with query itself
AzureActivity
| where CategoryValue == "ResourceHealth" and ResourceProviderValue == "MICROSOFT.COMPUTE"
| where not (ResourceGroup startswith "DATABRICKS-RG")
| extend d=parse_json(Properties)
| extend c = parse_json(tostring(d.eventProperties))
| where c.cause == "PlatformInitiated" and not(c.title == "Live Migration")
Error shown
SYNTAX ERROR
Query could not be parsed at '.' on line [6,48]
Token: .
Line: 6
Position: 48
If issue persists, please open a support ticket.
Request id: 6a4d4bae-41f6-43b4-9657-55fc435acab9

as title is a reserved keyword in the language, you could replace c.title with c['title'].
see: https://learn.microsoft.com/en-us/azure/data-explorer/kusto/query/scalar-data-types/dynamic#dynamic-object-accessors

Related

KQL ipv4_is_in_range with datatable

Good day,
Attempting to check IPAddress from SiginLogs with a datatable. I am able to perform the Scalar function ipv4_is_in_range() with a single value. Ips are changed for privacy
ex:
ipv4_is_in_range(IPAddress, '127.0.0.255/24')
When I try to use a declared datatable it does not recognize the values and returns nothing.
ex:
let srcIPs = datatable (checkIP:string) ['127.0.0.1/24'];
SigninLogs
| union srcIPs
| where ipv4_is_in_range( IPAddress, checkIP)
or
let srcIPs = datatable (checkIP:string) [
'127.0.0.1/24',
'8.8.8.8',
'1.1.1.1/16'
];
SigninLogs
| union srcIPs
| where ipv4_is_in_range( IPAddress, checkIP)
if I replace the 'where' with 'extend' I will get one IP address that does show correctly but will include another IP address that is not within that range.
My question is how do I get the function to recognize the values from srcIPs correctly?
#Michael. I went a head a revisited that document and reattempted. The workspace still shows and error when I hover ipv4_lookup stating it is not defined. YET. It still ran, something I didn't attempt. Now the code looks like.
let IP_Data = datatable(network:string)
[
"127.0.0.1",
"8.8.8.8/24",
"192.168.0.1",
"10.0.240.255/21"
];
SigninLogs
| evaluate ipv4_lookup(IP_Data, IPAddress, network)
| where UserType == "Member"
| project-reorder IPAddress, UserPrincipalName
So this code got me what I was looking for. TY all for your assistance.
Answering my own question with working code for record.

kusto query with dynamic object value without key

I have a lot of data looking like
{"tuesday":"<30, 60>"}
{"friday":"<0, 5>"}
{"saturday":"<5, 10>"}
{"friday":"<0, 5>"}
{"saturday":"<5, 10>"}
{"sunday":"0"}
{"monday":"<0, 5>"}
All i want is the value regardless of the key.
My query:
customEvents
| where name == "eventName"
| extend d = parse_json(tostring(customDimensions.['Properties']))
| project d
| take 7
d is a dynamic object and I can do d.monday for the value, but I'd like to get the value without the key. Is this possible with Kusto?
Thanks
for the case of a single-property as you've demonstrated above, using the parse operator could work:
datatable(d:dynamic)
[
,dynamic({"tuesday":"<30, 60>"})
,dynamic({"friday":"<0, 5>"})
,dynamic({"saturday":"<5, 10>"})
,dynamic({"friday":"<0, 5>"})
,dynamic({"saturday":"<5, 10>"})
,dynamic({"sunday":"0"})
,dynamic({"monday":"<0, 5>"})
]
| parse d with * ':"' value '"' *
| project value
Notes:
In case your values are not necessarily encapsulated in double quotes (e.g. are numerics), then you should be able to specify kind=regex for the parse operator, and use a conditional expression for the existence of the double quotes.
In case you have potentially more than 1 property per property bag, using extract_all() is an option.
Relevant Docs:
https://learn.microsoft.com/en-us/azure/kusto/query/parseoperator
https://learn.microsoft.com/en-us/azure/kusto/query/extractallfunction

Application Insights Extract Nested CustomDimensions

I have some data in Application Insights Analytics that has a dynamic object as a property of custom dimensions. For example:
| timestamp | name | customDimensions | etc |
|-------------------------|---------|----------------------------------|-----|
| 2017-09-11T19:56:20.000 | Spinner | { | ... |
MyCustomDimension: "hi"
Properties:
context: "ABC"
userMessage: "Some other"
}
Does that make sense? So a key/value pair inside of customDimensions.
I'm trying to bring up the context property to be a proper column in the results. So expected would be :
| timestamp | name | customDimensions | context| etc |
|-------------------------|---------|----------------------------------|--------|-----|
| 2017-09-11T19:56:20.000 | Spinner | { | ABC | ...
MyCustomDimension: "hi"
Properties:
context: "ABC"
userMessage: "Some other"
}
I've tried this:
customEvents | where name == "Spinner" | extend Context = customDimensions.Properties["context"]
and this:
customEvents | where name == "Spinner" | extend Context = customDimensions.Properties.context
but neither seem to work. They give me a column at the end named "Context" but the column is empty - no values.
Any ideas?
EDIT:
Added a picture for clarifying the format of the data:
edited to working answer:
customEvents
| where name == "Spinner"
| extend Properties = todynamic(tostring(customDimensions.Properties))
| extend Context = Properties.context
you need an extra tostring and todynamic in here to get what you expect (and what i expected!)
the explanation i was given:
Dynamic field "promises" you the upper/outer level of key / value access (this is how you access customDimensions.Properties).
Accessing internal structure of that json depends on the exact format of customDimensions.Properties content. It doesn’t have to be json by itself. Even if it looks like a well structured json, it still may be just a string that is not exactly well formatted json.
So basically, it by default won't attempt to parse strings inside of a dynamic/json block because they don't want to spend a lot of time possibly trying and failing to convert nested content to json infinitely.
I still think that extra tostring shouldn't be required inside there, since todynamic should already be allowing both string and dynamic in validly, so i'm checking to see if the team that owns the query stuff can make that step better.
Thanks sooo much.. just to expand on the answer from John. We needed to graph duration of end-points using custom events. This query made it so we could specify the duration as our Y-axis in the chart:
customEvents
| extend Properties = todynamic(tostring(customDimensions.Properties))
| extend duration = todouble(todecimal(Properties.duration))
| project timestamp, name, duration

How to create robust access logs using Apache Tomcat Valve Component?

We are working with Apache Tomcat 7 and trying to setup the Valve Component to store our access logs, ready for processing in SnowPlow.
The problem we have is how to make these logs robust. To give an example - we can separate fields with tabs and extract the user agent string like so:
pattern="%{yyyy-MM-dd}t %{hh:mm:ss}t %{User-Agent}i "
The problem is that the Valve Component does not (as far as I can see) escape %{User-Agent}i, so a stray tab in a useragent will corrupt the data (row will look like it contains four fields, not three).
As far as solutions, unless there's a way of escaping the useragent which I've missed, I can see a couple of solutions:
Use a really obscure field delimiter (or combination of field delimiters) which is very unlikely to crop up in a useragent string. We tried Ctrl-A (HTML ?) but that didn't seem to work
Write a custom AccessLogValve which either supports escaping or sanitizes tabs - perhaps similar to this post Sanitizing Tomcat access log entries
A bit puzzled that I can't find anything else about this online - does nobody parse their Tomcat access logs?
What do you recommend? We're a little stuck...
RFC2616 defines user agent string as
User-Agent = "User-Agent" ":" 1*( product | comment )
Then product is defined as
product = token ["/" product-version]
product-version = token
Following this, tokens are defined as
token = 1*<any CHAR except CTLs or separators>
and separators/CTLs as
separators = "(" | ")" | "<" | ">" | "#"
| "," | ";" | ":" | "\" | <">
| "/" | "[" | "]" | "?" | "="
| "{" | "}" | SP | HT
CTL = <any US-ASCII control character
(octets 0 - 31) and DEL (127)>
We need not to forget comment, which is defined as
comment = "(" *( ctext | quoted-pair | comment ) ")"
ctext = <any TEXT excluding "(" and ")">
quoted-pair = "\" CHAR
CHAR = <any US-ASCII character (octets 0 - 127)>
So if I understand correctly, you should be able to use any separator or CTL as long as you can distinguish comment, which is wrapped in ( and ). If ( appears inside the comment, it should be escaped with \.
In the end, I wrote a custom Tomcat AccessLogValve which:
Introduced a new pattern, 'I', to escape an incoming header
Introduced a new pattern, 'C', to fetch a cookie stored on the response
Re-implemented the pattern 'i' to ensure that "" (empty string) is replaced with "-"
Re-implemented the pattern 'q' to remove the "?" and ensure "" (empty string) is replaced with "-"
Overwrote the 'v' pattern, to write the version of this AccessLogValve, rather than the local server name
It seems to be pretty robust - I haven't had any further issues with unescaped values.

Is it possible to parse multi-line c style comments in MGrammar?

I've been hacking around with the May09 Oslo bits, experimented with tokenizing some source code. I can't seem to figure out how to correctly handle multiline C-style comments though.
For example: /*comment*/
Some cases that elude me:
/***/
or
/**//**/
I can make one or the other work, but not both.
The grammar was:
module Test {
language Comments {
token Comment =
MultiLineComment;
token MultiLineComment =
"/*" MultiLineCommentChar* "*/";
token MultiLineCommentChar =
^ "*" |
"*" PostAsteriskChar;
token PostAsteriskChar =
^ "*" |
"*" ^("*" | "/");
/*
token PostAsteriskChar =
^ "*" |
"*" PostAsteriskChar;
*/
syntax Main = Comment*;
}
}
The commented out token is what I think I want to do, however recursive tokens are not permitted.
The fact that MGrammar itself has "broken" multiline comments (it can't handle /***/) leads me to believe this isn't possible.
Does anyone know otherwise?
The way I have done it is as follows (not all my own code but I can't find a referance to the original author).
interleave Skippable = Whitespace | Comment;
interleave Comment = CommentToken;
#{Classification["Comment"]}
token CommentToken = CommentDelimited
| CommentLine;
token CommentDelimited = "/*" CommentDelimitedContent* "*/";
token CommentDelimitedContent
= ^('*')
| '*' ^('/');
token CommentLine = "//" CommentLineContent*;
token CommentLineContent
= ^(
'\u000A' // New Line
| '\u000D' // Carriage Return
| '\u0085' // Next Line
| '\u2028' // Line Separator
| '\u2029' // Paragraph Separator
);
This allows for both single line (//) comments as well as multiline (/* */) comments.

Resources