@@ -22,6 +22,12 @@ internal static class LogsHelper
2222 {
2323 private const string CustomEventAttributeName = "microsoft.custom_event.name" ;
2424 private const string ClientIpAttributeName = "microsoft.client.ip" ;
25+ private const string AvailabilityIdAttributeName = "microsoft.availability.id" ;
26+ private const string AvailabilityNameAttributeName = "microsoft.availability.name" ;
27+ private const string AvailabilityDurationAttributeName = "microsoft.availability.duration" ;
28+ private const string AvailabilitySuccessAttributeName = "microsoft.availability.success" ;
29+ private const string AvailabilityRunLocationAttributeName = "microsoft.availability.runLocation" ;
30+ private const string AvailabilityMessageAttributeName = "microsoft.availability.message" ;
2531 private const int Version = 2 ;
2632 private static readonly Action < LogRecordScope , IDictionary < string , string > > s_processScope = ( scope , properties ) =>
2733 {
@@ -61,7 +67,7 @@ internal static (List<TelemetryItem> TelemetryItems, TelemetrySchemaTypeCounter
6167 try
6268 {
6369 var properties = new ChangeTrackingDictionary < string , string > ( ) ;
64- ProcessLogRecordProperties ( logRecord , properties , out string ? message , out string ? eventName , out string ? microsoftClientIp ) ;
70+ ProcessLogRecordProperties ( logRecord , properties , out string ? message , out string ? eventName , out string ? microsoftClientIp , out AvailabilityInfo ? availabilityInfo ) ;
6571
6672 if ( logRecord . Exception is not null )
6773 {
@@ -75,6 +81,18 @@ internal static (List<TelemetryItem> TelemetryItems, TelemetrySchemaTypeCounter
7581 } ;
7682 telemetrySchemaTypeCounter . _exceptionCount ++ ;
7783 }
84+ else if ( availabilityInfo is not null )
85+ {
86+ telemetryItem = new TelemetryItem ( "Availability" , logRecord , resource , instrumentationKey , microsoftClientIp )
87+ {
88+ Data = new MonitorBase
89+ {
90+ BaseType = "AvailabilityData" ,
91+ BaseData = new AvailabilityData ( Version , availabilityInfo . Value , properties , logRecord ) ,
92+ }
93+ } ;
94+ telemetrySchemaTypeCounter . _availabilityCount ++ ;
95+ }
7896 else if ( eventName is not null )
7997 {
8098 telemetryItem = new TelemetryItem ( "Event" , logRecord , resource , instrumentationKey , microsoftClientIp )
@@ -111,19 +129,24 @@ internal static (List<TelemetryItem> TelemetryItems, TelemetrySchemaTypeCounter
111129 return ( telemetryItems , telemetrySchemaTypeCounter ) ;
112130 }
113131
114- internal static void ProcessLogRecordProperties ( LogRecord logRecord , IDictionary < string , string > properties , out string ? message , out string ? eventName , out string ? microsoftClientIp )
132+ internal static void ProcessLogRecordProperties ( LogRecord logRecord , IDictionary < string , string > properties , out string ? message , out string ? eventName , out string ? microsoftClientIp , out AvailabilityInfo ? availabilityInfo )
115133 {
116134 eventName = null ;
135+ availabilityInfo = null ;
117136 message = logRecord . Exception ? . Message ?? logRecord . FormattedMessage ;
118137 microsoftClientIp = null ;
138+ bool hasAvailabilityData = false ;
119139
120140 foreach ( KeyValuePair < string , object ? > item in logRecord . Attributes ?? Enumerable . Empty < KeyValuePair < string , object ? > > ( ) )
121141 {
122142 if ( item . Key == CustomEventAttributeName )
123143 {
124144 eventName = item . Value ? . ToString ( ) ;
125145 }
126-
146+ else if ( item . Key == AvailabilityNameAttributeName )
147+ {
148+ hasAvailabilityData = true ;
149+ }
127150 else if ( item . Key == ClientIpAttributeName )
128151 {
129152 microsoftClientIp = item . Value ? . ToString ( ) . Truncate ( SchemaConstants . MessageData_Properties_MaxValueLength ) ;
@@ -159,9 +182,15 @@ internal static void ProcessLogRecordProperties(LogRecord logRecord, IDictionary
159182 }
160183 }
161184
185+ // If we detected availability data, do a second pass to extract all availability attributes
186+ if ( hasAvailabilityData )
187+ {
188+ availabilityInfo = ExtractAvailabilityInfo ( logRecord , message , out microsoftClientIp ) ;
189+ }
190+
162191 logRecord . ForEachScope ( s_processScope , properties ) ;
163192
164- if ( eventName is null ) // we will omit the following properties if we've detected a custom event.
193+ if ( eventName is null && availabilityInfo is null ) // we will omit the following properties if we've detected a custom event or availability .
165194 {
166195 var categoryName = logRecord . CategoryName ;
167196 if ( ! properties . ContainsKey ( "CategoryName" ) && ! string . IsNullOrEmpty ( categoryName ) )
@@ -181,6 +210,66 @@ internal static void ProcessLogRecordProperties(LogRecord logRecord, IDictionary
181210 }
182211 }
183212
213+ private static AvailabilityInfo ? ExtractAvailabilityInfo ( LogRecord logRecord , string ? message , out string ? microsoftClientIp )
214+ {
215+ string ? availabilityId = null ;
216+ string ? availabilityName = null ;
217+ string ? availabilityDuration = null ;
218+ string ? availabilitySuccess = null ;
219+ string ? availabilityRunLocation = null ;
220+ string ? availabilityMessage = null ;
221+ microsoftClientIp = null ;
222+
223+ foreach ( KeyValuePair < string , object ? > item in logRecord . Attributes ?? Enumerable . Empty < KeyValuePair < string , object ? > > ( ) )
224+ {
225+ if ( item . Key == AvailabilityIdAttributeName )
226+ {
227+ availabilityId = item . Value ? . ToString ( ) ;
228+ }
229+ else if ( item . Key == AvailabilityNameAttributeName )
230+ {
231+ availabilityName = item . Value ? . ToString ( ) ;
232+ }
233+ else if ( item . Key == AvailabilityDurationAttributeName )
234+ {
235+ availabilityDuration = item . Value ? . ToString ( ) ;
236+ }
237+ else if ( item . Key == AvailabilitySuccessAttributeName )
238+ {
239+ availabilitySuccess = item . Value ? . ToString ( ) ;
240+ }
241+ else if ( item . Key == AvailabilityRunLocationAttributeName )
242+ {
243+ availabilityRunLocation = item . Value ? . ToString ( ) ;
244+ }
245+ else if ( item . Key == AvailabilityMessageAttributeName )
246+ {
247+ availabilityMessage = item . Value ? . ToString ( ) ;
248+ }
249+ else if ( item . Key == ClientIpAttributeName )
250+ {
251+ microsoftClientIp = item . Value ? . ToString ( ) . Truncate ( SchemaConstants . MessageData_Properties_MaxValueLength ) ;
252+ }
253+ }
254+
255+ // Construct availability info if we have required fields
256+ if ( ! string . IsNullOrEmpty ( availabilityId ) && ! string . IsNullOrEmpty ( availabilityName ) &&
257+ ! string . IsNullOrEmpty ( availabilityDuration ) && ! string . IsNullOrEmpty ( availabilitySuccess ) )
258+ {
259+ return new AvailabilityInfo
260+ {
261+ Id = availabilityId ! ,
262+ Name = availabilityName ! ,
263+ Duration = availabilityDuration ! ,
264+ Success = bool . Parse ( availabilitySuccess ) ,
265+ RunLocation = availabilityRunLocation ,
266+ Message = availabilityMessage ?? message
267+ } ;
268+ }
269+
270+ return null ;
271+ }
272+
184273 internal static string GetProblemId ( Exception exception )
185274 {
186275 string methodName = "UnknownMethod" ;
@@ -244,4 +333,17 @@ internal static SeverityLevel GetSeverityLevel(LogLevel logLevel)
244333 }
245334 }
246335 }
336+
337+ /// <summary>
338+ /// Struct to hold availability test information extracted from log attributes.
339+ /// </summary>
340+ internal struct AvailabilityInfo
341+ {
342+ public string Id { get ; set ; }
343+ public string Name { get ; set ; }
344+ public string Duration { get ; set ; }
345+ public bool Success { get ; set ; }
346+ public string ? RunLocation { get ; set ; }
347+ public string ? Message { get ; set ; }
348+ }
247349}
0 commit comments