Exemples de requêtes pour Azure Data Explorer et Azure MonitorSamples for queries for Azure Data Explorer and Azure Monitor

Cet article identifie les besoins courants en matière de requêtes dans Azure Data Explorer et explique comment utiliser le langage de requête Kusto afin d’y répondre.This article identifies common query needs in Azure Data Explorer and how you can use the Kusto Query Language to meet them.

Afficher un histogrammeDisplay a column chart

Pour projeter plusieurs colonnes, puis utiliser les colonnes en tant qu’axe X et qu’axe Y d’un graphique :To project two or more columns, and then use the columns as the x-axis and y-axis of a chart:

StormEvents
| where isnotempty(EndLocation) 
| summarize event_count=count() by EndLocation
| top 10 by event_count
| render columnchart
  • La première colonne forme l’axe X.The first column forms the x-axis. Il peut s’agir de valeurs numériques, de date et d’heure ou de chaîne.It can be numeric, date-time, or string.
  • Utilisez where, summarize et top pour limiter le volume de données que vous affichez.Use where, summarize, and top to limit the volume of data you display.
  • Triez les résultats pour définir l’ordre de l’axe X.Sort the results to define the order of the x-axis.

Capture d’écran d’un histogramme, avec dix colonnes de couleur décrivant les valeurs respectives de 10 emplacements.

Obtenir des sessions à partir d’événements de démarrage et d’arrêtGet sessions from start and stop events

Dans un journal d’événements, certains événements marquent le début ou la fin d’une session ou d’une activité étendue.In a log of events, some events mark the start or end of an extended activity or session.

NomName CityCity SessionIdSessionId TimestampTimestamp
DémarrerStart LondonLondon 28173302817330 2015-12-09T10:12:02.322015-12-09T10:12:02.32
JeuGame LondonLondon 28173302817330 2015-12-09T10:12:52.452015-12-09T10:12:52.45
DémarrerStart ManchesterManchester 42676674267667 2015-12-09T10:14:02.232015-12-09T10:14:02.23
ArrêterStop LondonLondon 28173302817330 2015-12-09T10:23:43.182015-12-09T10:23:43.18
AnnulerCancel ManchesterManchester 42676674267667 2015-12-09T10:27:26.292015-12-09T10:27:26.29
ArrêterStop ManchesterManchester 42676674267667 2015-12-09T10:28:31.722015-12-09T10:28:31.72

Chaque événement possède un ID de session (SessionId).Every event has a session ID (SessionId). La difficulté consiste à faire correspondre les événements de début et de fin avec un ID de session.The challenge is to match start and stop events with a session ID.

Exemple :Example:

let Events = MyLogTable | where ... ;

Events
| where Name == "Start"
| project Name, City, SessionId, StartTime=timestamp
| join (Events 
        | where Name="Stop"
        | project StopTime=timestamp, SessionId) 
    on SessionId
| project City, SessionId, StartTime, StopTime, Duration = StopTime - StartTime

Pour faire correspondre les événements de début et de fin avec un ID de session :To match start and stop events with a session ID:

  1. Utilisez let pour nommer une projection de table réduite au minimum avant d’initier la jointure.Use let to name a projection of the table that's pared down as far as possible before starting the join.
  2. Utilisez project pour modifier le nom des timestamps afin d’afficher les heures de début et de fin dans les résultats.Use project to change the names of the timestamps so that both the start time and the stop time appear in the results. project sélectionne également les autres colonnes à afficher dans les résultats.project also selects the other columns to view in the results.
  3. Utilisez join pour mettre en correspondance les entrées de début et de fin pour une même activité.Use join to match the start and stop entries for the same activity. Une ligne est créée pour chaque activité.A row is created for each activity.
  4. Utilisez à nouveau project pour ajouter une colonne et afficher la durée de l’activité.Use project again to add a column to show the duration of the activity.

Voici le format :Here's the output:

CityCity SessionIdSessionId StartTimeStartTime StopTimeStopTime DurationDuration
LondonLondon 28173302817330 2015-12-09T10:12:02.322015-12-09T10:12:02.32 2015-12-09T10:23:43.182015-12-09T10:23:43.18 00:11:40.4600:11:40.46
ManchesterManchester 42676674267667 2015-12-09T10:14:02.232015-12-09T10:14:02.23 2015-12-09T10:28:31.722015-12-09T10:28:31.72 00:14:29.4900:14:29.49

Obtenir des sessions sans utiliser d’ID de sessionGet sessions without using a session ID

Supposons que les événements de début et de fin n’aient d’ID de session facilitant une mise en correspondance,Suppose that the start and stop events don't conveniently have a session ID that we can match with. mais que nous disposions de l’adresse IP du client où la session a eu lieu.But, we do have the IP address of the client in which the session took place. En supposant que chaque adresse client effectue une seule session à la fois, nous pouvons établir une correspondance entre chaque événement de début et l’événement de fin suivant à partir de la même adresse IP :Assuming each client address conducts only one session at a time, we can match each start event to the next stop event from the same IP address:

Exemple :Example:

Events 
| where Name == "Start" 
| project City, ClientIp, StartTime = timestamp
| join  kind=inner
    (Events
    | where Name == "Stop" 
    | project StopTime = timestamp, ClientIp)
    on ClientIp
| extend duration = StopTime - StartTime 
    // Remove matches with earlier stops:
| where  duration > 0  
    // Pick out the earliest stop for each start and client:
| summarize arg_min(duration, *) by bin(StartTime,1s), ClientIp

La join fait correspondre chaque heure de début à toutes les heures de fin à partir de la même adresse IP du client.The join matches every start time with all the stop times from the same client IP address. L’exemple de code :The sample code:

  • Supprime les correspondances comportant des heures de fin antérieures.Removes matches with earlier stop times.
  • Effectue des regroupements par heure de début et adresse IP pour obtenir un groupe par session.Groups by start time and IP address to get a group for each session.
  • Fournit une fonction bin pour le paramètre StartTime.Supplies a bin function for the StartTime parameter. Si vous ne suivez pas cette étape, Kusto utilise automatiquement des classes d’une heure correspondant à certaines heures de début avec des heures de fin erronées.If you don't do this step, Kusto automatically uses one-hour bins that match some start times with the wrong stop times.

arg_min recherche la ligne présentant la plus petite durée dans chaque groupe, et le paramètre * est transmis à toutes les autres colonnes.arg_min finds the row with the smallest duration in each group, and the * parameter passes through all the other columns.

L’argument ajoute le préfixe min_ à chaque nom de colonne.The argument prefixes min_ to each column name.

Capture d’écran d’une table répertoriant les résultats, avec des colonnes pour l’heure de début, l’adresse IP du client, la durée, la ville et l’arrêt le plus ancien pour chaque combinaison client/heure de début.

Ajoutez du code pour compter les durées dans des classes correctement dimensionnées. Dans cet exemple, en raison d’une préférence pour un graphique à barres, divisez par 1s pour convertir les intervalles de temps en nombres :Add code to count the durations in conveniently sized bins. In this example, because of a preference for a bar chart, divide by 1s to convert the timespans to numbers:

    // Count the frequency of each duration:
    | summarize count() by duration=bin(min_duration/1s, 10) 
      // Cut off the long tail:
    | where duration < 300
      // Display in a bar chart:
    | sort by duration asc | render barchart 

Capture d’écran d’un histogramme représentant le nombre de sessions, avec des durées dans les plages spécifiées.

Exemple completFull example

Logs  
| filter ActivityId == "ActivityId with Blablabla" 
| summarize max(Timestamp), min(Timestamp)  
| extend Duration = max_Timestamp - min_Timestamp 

wabitrace  
| filter Timestamp >= datetime(2015-01-12 11:00:00Z)  
| filter Timestamp < datetime(2015-01-12 13:00:00Z)  
| filter EventText like "NotifyHadoopApplicationJobPerformanceCounters"      
| extend Tenant = extract("tenantName=([^,]+),", 1, EventText) 
| extend Environment = extract("environmentName=([^,]+),", 1, EventText)  
| extend UnitOfWorkId = extract("unitOfWorkId=([^,]+),", 1, EventText)  
| extend TotalLaunchedMaps = extract("totalLaunchedMaps=([^,]+),", 1, EventText, typeof(real))  
| extend MapsSeconds = extract("mapsMilliseconds=([^,]+),", 1, EventText, typeof(real)) / 1000 
| extend TotalMapsSeconds = MapsSeconds  / TotalLaunchedMaps 
| filter Tenant == 'DevDiv' and Environment == 'RollupDev2'  
| filter TotalLaunchedMaps > 0 
| summarize sum(TotalMapsSeconds) by UnitOfWorkId  
| extend JobMapsSeconds = sum_TotalMapsSeconds * 1 
| project UnitOfWorkId, JobMapsSeconds 
| join ( 
wabitrace  
| filter Timestamp >= datetime(2015-01-12 11:00:00Z)  
| filter Timestamp < datetime(2015-01-12 13:00:00Z)  
| filter EventText like "NotifyHadoopApplicationJobPerformanceCounters"  
| extend Tenant = extract("tenantName=([^,]+),", 1, EventText) 
| extend Environment = extract("environmentName=([^,]+),", 1, EventText)  
| extend UnitOfWorkId = extract("unitOfWorkId=([^,]+),", 1, EventText)   
| extend TotalLaunchedReducers = extract("totalLaunchedReducers=([^,]+),", 1, EventText, typeof(real)) 
| extend ReducesSeconds = extract("reducesMilliseconds=([^,]+)", 1, EventText, typeof(real)) / 1000 
| extend TotalReducesSeconds = ReducesSeconds / TotalLaunchedReducers 
| filter Tenant == 'DevDiv' and Environment == 'RollupDev2'  
| filter TotalLaunchedReducers > 0 
| summarize sum(TotalReducesSeconds) by UnitOfWorkId  
| extend JobReducesSeconds = sum_TotalReducesSeconds * 1 
| project UnitOfWorkId, JobReducesSeconds ) 
on UnitOfWorkId 
| join ( 
wabitrace  
| filter Timestamp >= datetime(2015-01-12 11:00:00Z)  
| filter Timestamp < datetime(2015-01-12 13:00:00Z)  
| filter EventText like "NotifyHadoopApplicationJobPerformanceCounters"  
| extend Tenant = extract("tenantName=([^,]+),", 1, EventText) 
| extend Environment = extract("environmentName=([^,]+),", 1, EventText)  
| extend JobName = extract("jobName=([^,]+),", 1, EventText)  
| extend StepName = extract("stepName=([^,]+),", 1, EventText)  
| extend UnitOfWorkId = extract("unitOfWorkId=([^,]+),", 1, EventText)  
| extend LaunchTime = extract("launchTime=([^,]+),", 1, EventText, typeof(datetime))  
| extend FinishTime = extract("finishTime=([^,]+),", 1, EventText, typeof(datetime)) 
| extend TotalLaunchedMaps = extract("totalLaunchedMaps=([^,]+),", 1, EventText, typeof(real))  
| extend TotalLaunchedReducers = extract("totalLaunchedReducers=([^,]+),", 1, EventText, typeof(real)) 
| extend MapsSeconds = extract("mapsMilliseconds=([^,]+),", 1, EventText, typeof(real)) / 1000 
| extend ReducesSeconds = extract("reducesMilliseconds=([^,]+)", 1, EventText, typeof(real)) / 1000 
| extend TotalMapsSeconds = MapsSeconds  / TotalLaunchedMaps  
| extend TotalReducesSeconds = (ReducesSeconds / TotalLaunchedReducers / ReducesSeconds) * ReducesSeconds  
| extend CalculatedDuration = (TotalMapsSeconds + TotalReducesSeconds) * time(1s) 
| filter Tenant == 'DevDiv' and Environment == 'RollupDev2') 
on UnitOfWorkId 
| extend MapsFactor = TotalMapsSeconds / JobMapsSeconds 
| extend ReducesFactor = TotalReducesSeconds / JobReducesSeconds 
| extend CurrentLoad = 1536 + (768 * TotalLaunchedMaps) + (1536 * TotalLaunchedMaps) 
| extend NormalizedLoad = 1536 + (768 * TotalLaunchedMaps * MapsFactor) + (1536 * TotalLaunchedMaps * ReducesFactor) 
| summarize sum(CurrentLoad), sum(NormalizedLoad) by  JobName  
| extend SaveFactor = sum_NormalizedLoad / sum_CurrentLoad 

Sessions simultanées du graphique au fil du tempsChart concurrent sessions over time

Supposons que vous disposiez d’une table d’activités et des heures de début et de fin de chacune d’entre elles.Suppose you have a table of activities and their start and end times. Vous pouvez afficher un graphique répertoriant le nombre d’activités exécutées simultanément au fil du temps.You can show a chart that displays how many activities run concurrently over time.

Voici un exemple d’entrée, appelé X :Here's a sample input, called X:

SessionIdSessionId StartTimeStartTime StopTimeStopTime
aa 10:01:0310:01:03 10:10:0810:10:08
bb 10:01:2910:01:29 10:03:1010:03:10
cc 10:03:0210:03:02 10:05:2010:05:20

Pour un graphique des classes d’une minute, vous souhaitez compter chaque activité en cours d’exécution à chaque intervalle d’une minute.For a chart in one-minute bins, you want to count each running activity at each one-minute interval.

Voici un résultat intermédiaire :Here's an intermediate result:

X | extend samples = range(bin(StartTime, 1m), StopTime, 1m)

range génère un tableau de valeurs à des intervalles spécifiés :range generates an array of values at the specified intervals:

SessionIdSessionId StartTimeStartTime StopTimeStopTime exemplessamples
aa 10:01:3310:01:33 10:06:3110:06:31 [10:01:00,10:02:00,...10:06:00][10:01:00,10:02:00,...10:06:00]
bb 10:02:2910:02:29 10:03:4510:03:45 [10:02:00,10:03:00][10:02:00,10:03:00]
cc 10:03:1210:03:12 10:04:3010:04:30 [10:03:00,10:04:00][10:03:00,10:04:00]

Plutôt que de conserver ces tableaux, développez-les à l’aide de mv-expand :Instead of keeping those arrays, expand them by using mv-expand:

X | mv-expand samples = range(bin(StartTime, 1m), StopTime , 1m)
SessionIdSessionId StartTimeStartTime StopTimeStopTime exemplessamples
aa 10:01:3310:01:33 10:06:3110:06:31 10:01:0010:01:00
aa 10:01:3310:01:33 10:06:3110:06:31 10:02:0010:02:00
aa 10:01:3310:01:33 10:06:3110:06:31 10:03:0010:03:00
aa 10:01:3310:01:33 10:06:3110:06:31 10:04:0010:04:00
aa 10:01:3310:01:33 10:06:3110:06:31 10:05:0010:05:00
aa 10:01:3310:01:33 10:06:3110:06:31 10:06:0010:06:00
bb 10:02:2910:02:29 10:03:4510:03:45 10:02:0010:02:00
bb 10:02:2910:02:29 10:03:4510:03:45 10:03:0010:03:00
cc 10:03:1210:03:12 10:04:3010:04:30 10:03:0010:03:00
cc 10:03:1210:03:12 10:04:3010:04:30 10:04:0010:04:00

Regroupez maintenant les résultats par heure d’échantillonnage et comptez les occurrences de chaque activité :Now, group the results by sample time and count the occurrences of each activity:

X
| mv-expand samples = range(bin(StartTime, 1m), StopTime , 1m)
| summarize count_SessionId = count() by bin(todatetime(samples),1m)
  • Utilisez todatetime() car mv-expand génère une colonne de type dynamique.Use todatetime() because mv-expand results in a column of dynamic type.
  • Utilisez bin() car, pour les valeurs numériques et les dates, si vous n’indiquez pas d’intervalle, summarize applique systématiquement une fonction bin() avec un intervalle par défaut.Use bin() because, for numeric values and dates, if you don't supply an interval, summarize always applies a bin() function by using a default interval.

Voici le format :Here's the output:

count_SessionIdcount_SessionId exemplessamples
11 10:01:0010:01:00
22 10:02:0010:02:00
33 10:03:0010:03:00
22 10:04:0010:04:00
11 10:05:0010:05:00
11 10:06:0010:06:00

Vous pouvez utiliser un graphique à barres ou un graphique temporel pour afficher les résultats.You can use a bar chart or timechart to render the results.

Introduire des classes null dans summarizeIntroduce null bins into summarize

Lorsque l’opérateur summarize est appliqué sur une clé de groupe se composant d’une colonne date/heure, compartimentez ces valeurs dans des classes à largeur fixe :When the summarize operator is applied over a group key that consists of a date-time column, bin those values to fixed-width bins:

let StartTime=ago(12h);
let StopTime=now()
T
| where Timestamp > StartTime and Timestamp <= StopTime 
| where ...
| summarize Count=count() by bin(Timestamp, 5m)

Cet exemple génère une table composée d’une seule ligne par groupe de lignes dans T se retrouvant dans chaque classe de cinq minutes.This example produces a table that has a single row per group of rows in T that fall into each bin of five minutes.

En revanche, le code n’ajoute pas de classes null - lignes pour les valeurs de classes de temps situées entre StartTime et StopTime pour lesquelles il n’existe pas de ligne correspondante dans T.What the code doesn't do is add "null bins"—rows for time bin values between StartTime and StopTime for which there's no corresponding row in T. Il est judicieux de « remplir » la table avec ces classes. Pour ce faire, vous pouvez procéder comme suit :It's a good idea to "pad" the table with those bins. Here's one way to do it:

let StartTime=ago(12h);
let StopTime=now()
T
| where Timestamp > StartTime and Timestamp <= StopTime 
| summarize Count=count() by bin(Timestamp, 5m)
| where ...
| union ( // 1
  range x from 1 to 1 step 1 // 2
  | mv-expand Timestamp=range(StartTime, StopTime, 5m) to typeof(datetime) // 3
  | extend Count=0 // 4
  )
| summarize Count=sum(Count) by bin(Timestamp, 5m) // 5 

Voici une explication pas à pas de la requête précédente :Here's a step-by-step explanation of the preceding query:

  1. Utilisez l’opérateur union pour ajouter des lignes à une table.Use the union operator to add more rows to a table. Ces lignes sont générées par l’expression union.Those rows are produced by the union expression.
  2. L’opérateur range génère une table comportant une seule ligne et une seule colonne.The range operator produces a table that has a single row and column. La table est exclusivement utilisée pour mv-expand.The table isn't used for anything other than for mv-expand to work on.
  3. L’opérateur mv-expand sur la fonction range crée autant de lignes qu’il existe de classes de cinq minutes entre StartTime et EndTime.The mv-expand operator over the range function creates as many rows as there are five-minute bins between StartTime and EndTime.
  4. Utilisez une Count sur 0.Use a Count of 0.
  5. L’opérateur summarize regroupe les classes de l’argument d’origine (gauche ou externe) vers union.The summarize operator groups together bins from the original (left, or outer) argument to union. L’opérateur compartimente également à partir de l’argument interne (lignes de classe null).The operator also bins from the inner argument to it (the null bin rows). Ce processus permet de veiller à ce que la sortie comporte une ligne par classe dont la valeur est égale à zéro ou au nombre d’origine.This process ensures that the output has one row per bin whose value is either zero or the original count.

Exploiter au mieux vos données en utilisant Kusto avec le Machine LearningGet more from your data by using Kusto with machine learning

De nombreux cas d’usage intéressants font appel à des algorithmes de Machine Learning et dérivent de précieux insights à partir des données de télémétrie.Many interesting use cases use machine learning algorithms and derive interesting insights from telemetry data. Ces algorithmes requièrent souvent un jeu de données strictement structuré comme entrée.Often, these algorithms require a strictly structured dataset as their input. En règle générale, les données de journal brutes ne correspondent ni à la structure ni à la taille requises.The raw log data usually doesn't match the required structure and size.

Commencez par rechercher les anomalies dans le taux d’erreurs d’un service d’inférences Bing spécifique.Start by looking for anomalies in the error rate of a specific Bing inferences service. La table des journaux contient 65 milliards d’enregistrements.The logs table has 65 billion records. La requête de base suivante filtre les 250 000 erreurs, puis crée une série chronologique du nombre d’erreurs utilisant la fonction de détection des anomalies series_decompose_anomalies.The following basic query filters 250,000 errors, and then creates a time series of error count that uses the anomaly detection function series_decompose_anomalies. Les anomalies sont détectées par le service Kusto et mises en évidence sous forme de points rouges sur le graphique de la série chronologique.The anomalies are detected by the Kusto service and are highlighted as red dots on the time series chart.

Logs
| where Timestamp >= datetime(2015-08-22) and Timestamp < datetime(2015-08-23) 
| where Level == "e" and Service == "Inferences.UnusualEvents_Main" 
| summarize count() by bin(Timestamp, 5min)
| render anomalychart 

Le service a identifié plusieurs compartiments affichant des taux d’erreurs suspects.The service identified few time buckets that had suspicious error rates. Utilisez Kusto pour faire un zoom avant sur ce laps de temps.Use Kusto to zoom into this timeframe. Exécutez ensuite une requête qui agrège sur la colonne Message.Then, run a query that aggregates on the Message column. Essayez de trouver les erreurs principales.Try to find the top errors.

Les parties pertinentes de l’ensemble de la trace de pile du message sont découpées pour une meilleure adaptation des résultats à la page.The relevant parts of the entire stack trace of the message are trimmed out, so the results fit better on the page.

Vous pouvez noter que l’identification des huit erreurs principales a abouti.You can see successful identification of the top eight errors. Toutefois, une longue série d’erreurs s’ensuit, car le message d’erreur a été créé à l’aide d’une chaîne de format contenant des données modifiées :However, next is a long series of errors, because the error message was created by using a format string that contained changing data:

Logs
| where Timestamp >= datetime(2015-08-22 05:00) and Timestamp < datetime(2015-08-22 06:00)
| where Level == "e" and Service == "Inferences.UnusualEvents_Main"
| summarize count() by Message 
| top 10 by count_ 
| project count_, Message 
count_count_ MessageMessage
71257125 Échec de ExecuteAlgorithmMethod pour la méthode « RunCycleFromInterimData »...ExecuteAlgorithmMethod for method 'RunCycleFromInterimData' has failed...
71257125 Échec d’appel de InferenceHostService..System.NullReferenceException : Référence d'objet non définie sur une instance d’objet...InferenceHostService call failed..System.NullReferenceException: Object reference not set to an instance of an object...
71247124 Erreur système d’inférence inattendue..System.NullReferenceException : Référence d'objet non définie sur une instance d’objet...Unexpected Inference System error..System.NullReferenceException: Object reference not set to an instance of an object...
51125112 Erreur système d’inférence inattendue..System.NullReferenceException : La référence d’objet n’est pas définie sur une instance d’objet..Unexpected Inference System error..System.NullReferenceException: Object reference not set to an instance of an object..
174174 Échec d’appel InferenceHostService..System.ServiceModel.CommunicationException: Une erreur d’écriture s’est produite dans le canal :...InferenceHostService call failed..System.ServiceModel.CommunicationException: There was an error writing to the pipe:...
1010 Échec de ExecuteAlgorithmMethod pour la méthode « RunCycleFromInterimData »...ExecuteAlgorithmMethod for method 'RunCycleFromInterimData' has failed...
1010 Erreur système d’inférence..Microsoft.Bing.Platform.Inferences.Service.Managers.UserInterimDataManagerException :...Inference System error..Microsoft.Bing.Platform.Inferences.Service.Managers.UserInterimDataManagerException:...
33 Échec d’appel InferenceHostService..System.ServiceModel.CommunicationObjectFaultedException :...InferenceHostService call failed..System.ServiceModel.CommunicationObjectFaultedException:...
11 Erreur système d’inférence... SocialGraph.BOSS.OperationResponse...AIS TraceId:8292FC561AC64BED8FA243808FE74EFD...Inference System error... SocialGraph.BOSS.OperationResponse...AIS TraceId:8292FC561AC64BED8FA243808FE74EFD...
11 Erreur système d’inférence... SocialGraph.BOSS.OperationResponse...AIS TraceId : 5F79F7587FF943EC9B641E02E701AFBF...Inference System error... SocialGraph.BOSS.OperationResponse...AIS TraceId: 5F79F7587FF943EC9B641E02E701AFBF...

À ce stade, l’utilisation de l’opérateur reduce peut aider.At this point, using the reduce operator helps. L’opérateur a identifié 63 erreurs différentes provenant du même point d’instrumentation de trace dans le code.The operator identified 63 different errors that originated at the same trace instrumentation point in the code. reduce permet de se concentrer sur des traces d’erreur plus explicites dans cette fenêtre de temps.reduce helps focus on additional meaningful error traces in that time window.

Logs
| where Timestamp >= datetime(2015-08-22 05:00) and Timestamp < datetime(2015-08-22 06:00)
| where Level == "e" and Service == "Inferences.UnusualEvents_Main"
| reduce by Message with threshold=0.35
| project Count, Pattern
NombreCount ModèlePattern
71257125 Échec de ExecuteAlgorithmMethod pour la méthode « RunCycleFromInterimData »...ExecuteAlgorithmMethod for method 'RunCycleFromInterimData' has failed...
71257125 Échec d’appel de InferenceHostService..System.NullReferenceException : Référence d'objet non définie sur une instance d’objet...InferenceHostService call failed..System.NullReferenceException: Object reference not set to an instance of an object...
71247124 Erreur système d’inférence inattendue..System.NullReferenceException : Référence d'objet non définie sur une instance d’objet...Unexpected Inference System error..System.NullReferenceException: Object reference not set to an instance of an object...
51125112 Erreur système d’inférence inattendue..System.NullReferenceException : Référence d'objet non définie sur une instance d’objet...Unexpected Inference System error..System.NullReferenceException: Object reference not set to an instance of an object...
174174 Échec d’appel InferenceHostService..System.ServiceModel.CommunicationException: Une erreur d’écriture s’est produite dans le canal :...InferenceHostService call failed..System.ServiceModel.CommunicationException: There was an error writing to the pipe:...
6363 Erreur système d’inférence..Microsoft.Bing.Platform.Inferences.* : Write * pour écrire dans l’objet BOSS.* : SocialGraph.BOSS.Reques...Inference System error..Microsoft.Bing.Platform.Inferences.*: Write * to write to the Object BOSS.*: SocialGraph.BOSS.Reques...
1010 Échec de ExecuteAlgorithmMethod pour la méthode « RunCycleFromInterimData »...ExecuteAlgorithmMethod for method 'RunCycleFromInterimData' has failed...
1010 Erreur système d’inférence..Microsoft.Bing.Platform.Inferences.Service.Managers.UserInterimDataManagerException :...Inference System error..Microsoft.Bing.Platform.Inferences.Service.Managers.UserInterimDataManagerException:...
33 Échec d’appel InferenceHostService..System.ServiceModel.*: L’objet, System.ServiceModel.Channels.*+*, pour ** est *... pour Syst...InferenceHostService call failed..System.ServiceModel.*: The object, System.ServiceModel.Channels.*+*, for ** is the *... at Syst...

Vous disposez à présent d’un meilleur aperçu des erreurs principales qui ont contribué aux anomalies détectées.Now, you have a good view into the top errors that contributed to the detected anomalies.

Pour comprendre l’effet de ces erreurs dans l’exemple de système, prenez en compte les points suivants :To understand the effect of these errors across the sample system, consider that:

  • La table Logs contient des données dimensionnelles supplémentaires, telles que Component et Cluster.The Logs table contains additional dimensional data, like Component and Cluster.
  • Le nouveau plug-in autocluster contribue à dériver les détails des composants et des clusters avec une requête simple.The new autocluster plugin can help derive component and cluster insight with a simple query.

Dans l’exemple suivant, vous pouvez clairement voir que chacune des quatre erreurs principales relève d’un composant spécifique.In the following example, you can clearly see that each of the top four errors is specific to a component. En outre, bien que les trois erreurs principales soient spécifiques au cluster DB4, la quatrième erreur se produit sur tous les clusters.Also, although the top three errors are specific to the DB4 cluster, the fourth error happens across all clusters.

Logs
| where Timestamp >= datetime(2015-08-22 05:00) and Timestamp < datetime(2015-08-22 06:00)
| where Level == "e" and Service == "Inferences.UnusualEvents_Main"
| evaluate autocluster()
NombreCount Pourcentage (%)Percentage (%) ComposantComponent ClusterCluster MessageMessage
71257125 26.6426.64 InferenceHostServiceInferenceHostService DB4DB4 ExecuteAlgorithmMethod pour la méthode...ExecuteAlgorithmMethod for method...
71257125 26.6426.64 Composant inconnuUnknown Component DB4DB4 Échec d’appel InferenceHostService...InferenceHostService call failed...
71247124 26.6426.64 InferenceAlgorithmExecutorInferenceAlgorithmExecutor DB4DB4 Erreur système d’inférence inattendue...Unexpected Inference System error...
51125112 19.1119.11 InferenceAlgorithmExecutorInferenceAlgorithmExecutor * Erreur système d’inférence inattendue...Unexpected Inference System error...

Mapper des valeurs d’un jeu à un autreMap values from one set to another

Le mappage statique de valeurs constitue un cas d’usage de requête courant.A common query use case is static mapping of values. Le mappage statique permet de mieux présenter les résultats.Static mapping can help make results more presentable.

Par exemple, dans la table suivante, DeviceModel spécifie un modèle d’appareil.For example, in the next table, DeviceModel specifies a device model. L’utilisation du modèle d’appareil ne s’avère pas pratique pour référencer le nom de l’appareil.Using the device model isn't a convenient form of referencing the device name.

DeviceModelDeviceModel NombreCount
iPhone5,1iPhone5,1 3232
iPhone3,2iPhone3,2 432432
iPhone7,2iPhone7,2 5555
iPhone5,2iPhone5,2 6666

L’utilisation d’un nom convivial s’avère plus pratique :Using a friendly name is more convenient:

FriendlyNameFriendlyName NombreCount
iPhone 5iPhone 5 3232
iPhone 4iPhone 4 432432
iPhone 6iPhone 6 5555
iPhone 5iPhone5 6666

Les deux exemples suivants montrent comment passer d’un modèle d’appareil à un nom convivial pour identifier un appareil.The next two examples demonstrate how to change from using a device model to a friendly name to identify a device.

Mapper à l’aide d’un dictionnaire dynamiqueMap by using a dynamic dictionary

Vous pouvez obtenir un mappage en utilisant un dictionnaire et des accesseurs dynamiques.You can achieve mapping by using a dynamic dictionary and dynamic accessors. Par exemple :For example:

// Dataset definition
let Source = datatable(DeviceModel:string, Count:long)
[
  'iPhone5,1', 32,
  'iPhone3,2', 432,
  'iPhone7,2', 55,
  'iPhone5,2', 66,
];
// Query start here
let phone_mapping = dynamic(
  {
    "iPhone5,1" : "iPhone 5",
    "iPhone3,2" : "iPhone 4",
    "iPhone7,2" : "iPhone 6",
    "iPhone5,2" : "iPhone5"
  });
Source 
| project FriendlyName = phone_mapping[DeviceModel], Count
FriendlyNameFriendlyName NombreCount
iPhone 5iPhone 5 3232
iPhone 4iPhone 4 432432
iPhone 6iPhone 6 5555
iPhone 5iPhone5 6666

Mapper à l’aide d’une table statiqueMap by using a static table

Vous pouvez également obtenir un mappage à l’aide d’une table persistante et d’un opérateur join.You also can achieve mapping by using a persistent table and a join operator.

  1. Créez la table de mappage une seule fois :Create the mapping table only once:

    .create table Devices (DeviceModel: string, FriendlyName: string) 
    
    .ingest inline into table Devices 
        ["iPhone5,1","iPhone 5"]["iPhone3,2","iPhone 4"]["iPhone7,2","iPhone 6"]["iPhone5,2","iPhone5"]
    
  2. Créez une table du contenu de l’appareil :Create a table of the device contents:

    DeviceModelDeviceModel FriendlyNameFriendlyName
    iPhone5,1iPhone5,1 iPhone 5iPhone 5
    iPhone3,2iPhone3,2 iPhone 4iPhone 4
    iPhone7,2iPhone7,2 iPhone 6iPhone 6
    iPhone5,2iPhone5,2 iPhone 5iPhone5
  3. Créez une source de table de test :Create a test table source:

    .create table Source (DeviceModel: string, Count: int)
    
    .ingest inline into table Source ["iPhone5,1",32]["iPhone3,2",432]["iPhone7,2",55]["iPhone5,2",66]
    
  4. Joignez les tables et exécutez le projet :Join the tables and run the project:

    Devices  
    | join (Source) on DeviceModel  
    | project FriendlyName, Count
    

Voici le format :Here's the output:

FriendlyNameFriendlyName NombreCount
iPhone 5iPhone 5 3232
iPhone 4iPhone 4 432432
iPhone 6iPhone 6 5555
iPhone 5iPhone5 6666

Créer et utiliser des tables de dimension de durée de requêteCreate and use query-time dimension tables

Vous souhaiterez souvent joindre les résultats d’une requête avec une table de dimension ad hoc non stockée dans la base de données.Often, you'll want to join the results of a query with an ad-hoc dimension table that isn't stored in the database. Vous pouvez définir une expression dont le résultat correspond à une table limitée à une seule requête.You can define an expression whose result is a table scoped to a single query.

Par exemple :For example:

// Create a query-time dimension table using datatable
let DimTable = datatable(EventType:string, Code:string)
  [
    "Heavy Rain", "HR",
    "Tornado",    "T"
  ]
;
DimTable
| join StormEvents on EventType
| summarize count() by Code

Voici un exemple de définition un peu plus complexe :Here's a slightly more complex example:

// Create a query-time dimension table using datatable
let TeamFoundationJobResult = datatable(Result:int, ResultString:string)
  [
    -1, 'None', 0, 'Succeeded', 1, 'PartiallySucceeded', 2, 'Failed',
    3, 'Stopped', 4, 'Killed', 5, 'Blocked', 6, 'ExtensionNotFound',
    7, 'Inactive', 8, 'Disabled', 9, 'JobInitializationError'
  ]
;
JobHistory
  | where PreciseTimeStamp > ago(1h)
  | where Service  != "AX"
  | where Plugin has "Analytics"
  | sort by PreciseTimeStamp desc
  | join kind=leftouter TeamFoundationJobResult on Result
  | extend ExecutionTimeSpan = totimespan(ExecutionTime)
  | project JobName, StartTime, ExecutionTimeSpan, ResultString, ResultMessage

Récupérer les derniers enregistrements (par timestamp) par identitéRetrieve the latest records (by timestamp) per identity

Supposons que vous disposiez d’une table comprenant ce qui suit :Suppose you have a table that includes:

  • Une colonne ID identifiant l’entité à laquelle chaque ligne est associée, comme un ID d’utilisateur ou un ID de nœudAn ID column that identifies the entity with which each row is associated, such as a user ID or a node ID
  • Une colonne timestamp fournissant la référence temporelle pour la ligneA timestamp column that provides the time reference for the row
  • D’autres colonnesOther columns

Vous pouvez utiliser l’opérateur top-nested pour effectuer une requête qui renvoie les deux derniers enregistrements pour chaque valeur de la colonne ID, où latest est défini comme ayant la valeur la plus élevée de timestamp  :You can use the top-nested operator to make a query that returns the latest two records for each value of the ID column, where latest is defined as having the highest value of timestamp:

datatable(id:string, timestamp:datetime, bla:string)           // #1
  [
  "Barak",  datetime(2015-01-01), "1",
  "Barak",  datetime(2016-01-01), "2",
  "Barak",  datetime(2017-01-20), "3",
  "Donald", datetime(2017-01-20), "4",
  "Donald", datetime(2017-01-18), "5",
  "Donald", datetime(2017-01-19), "6"
  ]
| top-nested   of id        by dummy0=max(1),                  // #2
  top-nested 2 of timestamp by dummy1=max(timestamp),          // #3
  top-nested   of bla       by dummy2=max(1)                   // #4
| project-away dummy0, dummy1, dummy2                          // #5

Voici une explication pas à pas de la requête précédente (la numérotation fait référence aux nombres des commentaires de code) :Here's a step-by-step explanation of the preceding query (the numbering refers to the numbers in the code comments):

  1. datatable permet de générer des données de test à des fins de démonstration.The datatable is a way to produce some test data for demonstration purposes. Normalement, vous utilisez des données réelles ici.Normally, you'd use real data here.
  2. Cette ligne a vocation à renvoyer toutes les valeurs distinctes de id .This line essentially means return all distinct values of id.
  3. Cette ligne renvoie ensuite ce qui suit pour les deux premiers enregistrements qui optimisent :This line then returns, for the top two records that maximize:
    • La colonne timestampThe timestamp column
    • Les colonnes du niveau précédent (ici, id)The columns of the preceding level (here, just id)
    • La colonne spécifiée à ce niveau (ici, timestamp)The column specified at this level (here, timestamp)
  4. Cette ligne ajoute les valeurs de la colonne bla pour chacun des enregistrements renvoyés par le niveau précédent.This line adds the values of the bla column for each of the records returned by the preceding level. Si la table contient d’autres colonnes qui vous intéressent, vous pouvez répéter cette ligne pour chacune de ces colonnes.If the table has other columns you're interested in, you can repeat this line for each of those columns.
  5. La dernière ligne utilise l’opérateur project-away pour supprimer les colonnes « supplémentaires » introduites par top-nested.The final line uses the project-away operator to remove the "extra" columns that are introduced by top-nested.

Étendre une table à hauteur d’un pourcentage du calcul totalExtend a table by a percentage of the total calculation

Une expression tabulaire incluant une colonne numérique s’avère plus utile lorsqu’elle est accompagnée de sa valeur sous forme de pourcentage du total.A tabular expression that includes a numeric column is more useful to the user when it's accompanied by its value as a percentage of the total.

Par exemple, supposons qu’une requête génère la table suivante :For example, assume that a query produces the following table:

SomeSeriesSomeSeries SomeIntSomeInt
AppleApple 100100
BananeBanana 200200

Vous souhaitez afficher la table comme suit :You want to show the table like this:

SomeSeriesSomeSeries SomeIntSomeInt PctPct
AppleApple 100100 33,333.3
BananeBanana 200200 66,666.6

Pour modifier la manière dont la table s’affiche, calculez le total (somme) de la colonne SomeInt, puis divisez chaque valeur de cette colonne par le total.To change the way the table appears, calculate the total (sum) of the SomeInt column, and then divide each value of this column by the total. Pour obtenir des résultats arbitraires, utilisez l’opérateur as.For arbitrary results, use the as operator.

Par exemple :For example:

// The following table literally represents a long calculation
// that ends up with an anonymous tabular value:
datatable (SomeInt:int, SomeSeries:string) [
  100, "Apple",
  200, "Banana",
]
// We now give this calculation a name ("X"):
| as X
// Having this name we can refer to it in the sub-expression
// "X | summarize sum(SomeInt)":
| extend Pct = 100 * bin(todouble(SomeInt) / toscalar(X | summarize sum(SomeInt)), 0.001)

Effectuer des agrégations sur une fenêtre glissantePerform aggregations over a sliding window

L’exemple suivant montre comment synthétiser des colonnes à l’aide d’une fenêtre glissante.The following example shows how to summarize columns by using a sliding window. Pour la requête, utilisez la table suivante contenant les prix des fruits par timestamps.For the query, use the following table, which contains prices of fruits by timestamps.

Calculez les coûts minimaux, maximaux et totaux de chaque fruit par jour à l’aide d’une fenêtre glissante de sept jours.Calculate the minimum, maximum, and sum costs of each fruit per day by using a sliding window of seven days. Chaque enregistrement du jeu de résultats regroupe les sept jours précédents et les résultats contiennent un enregistrement par jour dans la période d’analyse.Each record in the result set aggregates the preceding seven days, and the results contain a record per day in the analysis period.

Table des fruits :Fruit table:

TimestampTimestamp FruitFruit PricePrice
2018-09-24 21:00:00.00000002018-09-24 21:00:00.0000000 BananesBananas 33
2018-09-25 20:00:00.00000002018-09-25 20:00:00.0000000 PommesApples 99
2018-09-26 03:00:00.00000002018-09-26 03:00:00.0000000 BananesBananas 44
2018-09-27 10:00:00.00000002018-09-27 10:00:00.0000000 PrunesPlums 88
2018-09-28 07:00:00.00000002018-09-28 07:00:00.0000000 BananesBananas 66
2018-09-29 21:00:00.00000002018-09-29 21:00:00.0000000 BananesBananas 88
2018-09-30 01:00:00.00000002018-09-30 01:00:00.0000000 PrunesPlums 22
2018-10-01 05:00:00.00000002018-10-01 05:00:00.0000000 BananesBananas 00
2018-10-02 02:00:00.00000002018-10-02 02:00:00.0000000 BananesBananas 00
2018-10-03 13:00:00.00000002018-10-03 13:00:00.0000000 PrunesPlums 44
2018-10-04 14:00:00.00000002018-10-04 14:00:00.0000000 PommesApples 88
2018-10-05 05:00:00.00000002018-10-05 05:00:00.0000000 BananesBananas 22
2018-10-06 08:00:00.00000002018-10-06 08:00:00.0000000 PrunesPlums 88
2018-10-07 12:00:00.00000002018-10-07 12:00:00.0000000 BananesBananas 00

Voici la requête d’agrégation de fenêtre glissante.Here's the sliding window aggregation query. Consultez l’explication suivant le résultat de la requête.See the explanation after the query result.

let _start = datetime(2018-09-24);
let _end = _start + 13d; 
Fruits 
| extend _bin = bin_at(Timestamp, 1d, _start) // #1 
| extend _endRange = iif(_bin + 7d > _end, _end, 
                            iff( _bin + 7d - 1d < _start, _start,
                                iff( _bin + 7d - 1d < _bin, _bin,  _bin + 7d - 1d)))  // #2
| extend _range = range(_bin, _endRange, 1d) // #3
| mv-expand _range to typeof(datetime) limit 1000000 // #4
| summarize min(Price), max(Price), sum(Price) by Timestamp=bin_at(_range, 1d, _start) ,  Fruit // #5
| where Timestamp >= _start + 7d; // #6

Voici le format :Here's the output:

TimestampTimestamp FruitFruit min_Pricemin_Price max_Pricemax_Price sum_Pricesum_Price
2018-10-01 00:00:00.00000002018-10-01 00:00:00.0000000 PommesApples 99 99 99
2018-10-01 00:00:00.00000002018-10-01 00:00:00.0000000 BananesBananas 00 88 1818
2018-10-01 00:00:00.00000002018-10-01 00:00:00.0000000 PrunesPlums 22 88 1010
2018-10-02 00:00:00.00000002018-10-02 00:00:00.0000000 BananesBananas 00 88 1818
2018-10-02 00:00:00.00000002018-10-02 00:00:00.0000000 PrunesPlums 22 88 1010
2018-10-03 00:00:00.00000002018-10-03 00:00:00.0000000 PrunesPlums 22 88 1414
2018-10-03 00:00:00.00000002018-10-03 00:00:00.0000000 BananesBananas 00 88 1414
2018-10-04 00:00:00.00000002018-10-04 00:00:00.0000000 BananesBananas 00 88 1414
2018-10-04 00:00:00.00000002018-10-04 00:00:00.0000000 PrunesPlums 22 44 66
2018-10-04 00:00:00.00000002018-10-04 00:00:00.0000000 PommesApples 88 88 88
2018-10-05 00:00:00.00000002018-10-05 00:00:00.0000000 BananesBananas 00 88 1010
2018-10-05 00:00:00.00000002018-10-05 00:00:00.0000000 PrunesPlums 22 44 66
2018-10-05 00:00:00.00000002018-10-05 00:00:00.0000000 PommesApples 88 88 88
2018-10-06 00:00:00.00000002018-10-06 00:00:00.0000000 PrunesPlums 22 88 1414
2018-10-06 00:00:00.00000002018-10-06 00:00:00.0000000 BananesBananas 00 22 22
2018-10-06 00:00:00.00000002018-10-06 00:00:00.0000000 PommesApples 88 88 88
2018-10-07 00:00:00.00000002018-10-07 00:00:00.0000000 BananesBananas 00 22 22
2018-10-07 00:00:00.00000002018-10-07 00:00:00.0000000 PrunesPlums 44 88 1212
2018-10-07 00:00:00.00000002018-10-07 00:00:00.0000000 PommesApples 88 88 88

La requête « s’étend » (duplique) chaque enregistrement de la table d’entrée pendant les sept jours suivant son apparition réelle.The query "stretches" (duplicates) each record in the input table throughout the seven days after its actual appearance. Chaque enregistrement apparaît en réalité sept fois.Each record actually appears seven times. Par conséquent, l’agrégation quotidienne comprend tous les enregistrements des sept jours précédents.As a result, the daily aggregation includes all records of the preceding seven days.

Voici une explication pas à pas de la requête précédente :Here's a step-by-step explanation of the preceding query:

  1. Compartimentez chaque enregistrement sur un jour (par rapport à _start).Bin each record to one day (relative to _start).
  2. Déterminez la fin de la plage par enregistrement : _bin + 7d, sauf si la valeur n’est pas comprise dans la plage de _start et _end, auquel cas elle est ajustée.Determine the end of the range per record: _bin + 7d, unless the value is out of the range of _start and _end, in which case, it's adjusted.
  3. Pour chaque enregistrement, créez une table de sept jours (timestamps), en commençant par le jour de l’enregistrement actuel.For each record, create an array of seven days (timestamps), starting at the current record's day.
  4. mv-expand le tableau, en dupliquant chaque enregistrement dans sept enregistrements distants d’une journée.mv-expand the array, thus duplicating each record to seven records, one day apart from each other.
  5. Exécutez la fonction d’agrégation pour chaque jour.Perform the aggregation function for each day. En raison de #4, cette étape synthétise les sept jours passés.Due to #4, this step actually summarizes the past seven days.
  6. Les données des sept premiers jours sont incomplètes en l’absence de période de recherche arrière pour les sept premiers jours.The data for the first seven days is incomplete because there's no seven-day lookback period for the first seven days. Les sept premiers jours sont exclus du résultat final.The first seven days are excluded from the final result. Dans l’exemple, ils participent uniquement à l’agrégation pour 2018-10-01.In the example, they participate only in the aggregation for 2018-10-01.

Rechercher l’événement précédentFind the preceding event

L’exemple suivant montre comment rechercher un événement précédent entre deux jeux de données.The next example demonstrates how to find a preceding event between two datasets.

Vous disposez de deux jeux de données, A et B. Pour chaque enregistrement du jeu de données B, recherchez son événement précédent dans le jeu de données A (à savoir, l’enregistrement arg_max dans A toujours antérieur à B).You have two datasets, A and B. For each record in dataset B, find its preceding event in dataset A (that is, the arg_max record in A that is still older than B).

Vous trouverez ci-dessous les exemples de jeux de données :Here are the sample datasets:

let A = datatable(Timestamp:datetime, ID:string, EventA:string)
[
    datetime(2019-01-01 00:00:00), "x", "Ax1",
    datetime(2019-01-01 00:00:01), "x", "Ax2",
    datetime(2019-01-01 00:00:02), "y", "Ay1",
    datetime(2019-01-01 00:00:05), "y", "Ay2",
    datetime(2019-01-01 00:00:00), "z", "Az1"
];
let B = datatable(Timestamp:datetime, ID:string, EventB:string)
[
    datetime(2019-01-01 00:00:03), "x", "B",
    datetime(2019-01-01 00:00:04), "x", "B",
    datetime(2019-01-01 00:00:04), "y", "B",
    datetime(2019-01-01 00:02:00), "z", "B"
];
A; B
TimestampTimestamp IDID EventBEventB
2019-01-01 00:00:00.00000002019-01-01 00:00:00.0000000 xx Ax1Ax1
2019-01-01 00:00:00.00000002019-01-01 00:00:00.0000000 zz Az1Az1
2019-01-01 00:00:01.00000002019-01-01 00:00:01.0000000 xx Ax2Ax2
2019-01-01 00:00:02.00000002019-01-01 00:00:02.0000000 oy Ay1Ay1
2019-01-01 00:00:05.00000002019-01-01 00:00:05.0000000 oy Ay2Ay2

TimestampTimestamp IDID EventAEventA
2019-01-01 00:00:03.00000002019-01-01 00:00:03.0000000 xx BB
2019-01-01 00:00:04.00000002019-01-01 00:00:04.0000000 xx BB
2019-01-01 00:00:04.00000002019-01-01 00:00:04.0000000 oy BB
2019-01-01 00:02:00.00000002019-01-01 00:02:00.0000000 zz BB

Sortie attendue :Expected output:

IDID TimestampTimestamp EventBEventB A_TimestampA_Timestamp EventAEventA
xx 2019-01-01 00:00:03.00000002019-01-01 00:00:03.0000000 BB 2019-01-01 00:00:01.00000002019-01-01 00:00:01.0000000 Ax2Ax2
xx 2019-01-01 00:00:04.00000002019-01-01 00:00:04.0000000 BB 2019-01-01 00:00:01.00000002019-01-01 00:00:01.0000000 Ax2Ax2
oy 2019-01-01 00:00:04.00000002019-01-01 00:00:04.0000000 BB 2019-01-01 00:00:02.00000002019-01-01 00:00:02.0000000 Ay1Ay1
zz 2019-01-01 00:02:00.00000002019-01-01 00:02:00.0000000 BB 2019-01-01 00:00:00.00000002019-01-01 00:00:00.0000000 Az1Az1

Nous vous recommandons deux approches différentes pour résoudre ce problème.We recommend two different approaches for this problem. Vous pouvez les tester sur votre jeu de données spécifique afin de trouver celle qui convient le mieux à votre scénario.You can test both on your specific dataset to find the one that is most suitable for your scenario.

Notes

Chaque approche peut s’exécuter de manière différente sur des jeux de données différents.Each approach might run differently on different datasets.

Approche 1Approach 1

Cette approche sérialise les deux jeux de données par ID et timestamp.This approach serializes both datasets by ID and timestamp. Elle regroupe ensuite tous les événements du jeu de données B avec leurs événements précédents dans le jeu de données A. Enfin, elle sélectionne arg_max parmi tous les événements du jeu de données A dans le groupe.Then, it groups all events in dataset B with all their preceding events in dataset A. Finally, it picks the arg_max out of all the events in dataset A in the group.

A
| extend A_Timestamp = Timestamp, Kind="A"
| union (B | extend B_Timestamp = Timestamp, Kind="B")
| order by ID, Timestamp asc 
| extend t = iff(Kind == "A" and (prev(Kind) != "A" or prev(Id) != ID), 1, 0)
| extend t = row_cumsum(t)
| summarize Timestamp=make_list(Timestamp), EventB=make_list(EventB), arg_max(A_Timestamp, EventA) by t, ID
| mv-expand Timestamp to typeof(datetime), EventB to typeof(string)
| where isnotempty(EventB)
| project-away t

Approche 2Approach 2

Cette approche de résolution du problème requiert une période de recherche arrière maximale.This approach to solving the problem requires a maximum lookback period. Cette approche examine l’ancienneté de l’enregistrement du jeu de données A par rapport au jeu de données B. Elle joint ensuite les deux jeux de données en fonction de l’ID et de cette période de recherche arrière.The approach looks at how much older the record in dataset A might be compared to dataset B. The method then joins the two datasets based on ID and this lookback period.

join génère tous les candidats possibles, tous les enregistrements du jeu de données A plus anciens que les enregistrements du jeu de données B et ce, au cours de la période de recherche arrière.The join produces all possible candidates, all dataset A records that are older than records in dataset B and within the lookback period. Ensuite, l’élément le plus proche du jeu de données B est filtré par arg_min (TimestampB - TimestampA).Then, the closest one to dataset B is filtered by arg_min (TimestampB - TimestampA). Plus la période de recherche arrière est courte, meilleurs sont les résultats de la requête.The shorter the lookback period is, the better the query results will be.

Dans l’exemple suivant, la limite de recherche arrière est définie sur 1m.In the following example, the lookback period is set to 1m. L’enregistrement avec l’ID z n’a pas d’événement A correspondant, car son événement A est antérieur de deux minutes.The record with ID z doesn't have a corresponding A event because its A event is older by two minutes.

let _maxLookbackPeriod = 1m;  
let _internalWindowBin = _maxLookbackPeriod / 2;
let B_events = B 
    | extend ID = new_guid()
    | extend _time = bin(Timestamp, _internalWindowBin)
    | extend _range = range(_time - _internalWindowBin, _time + _maxLookbackPeriod, _internalWindowBin) 
    | mv-expand _range to typeof(datetime) 
    | extend B_Timestamp = Timestamp, _range;
let A_events = A 
    | extend _time = bin(Timestamp, _internalWindowBin)
    | extend _range = range(_time - _internalWindowBin, _time + _maxLookbackPeriod, _internalWindowBin) 
    | mv-expand _range to typeof(datetime) 
    | extend A_Timestamp = Timestamp, _range;
B_events
    | join kind=leftouter (
        A_events
) on ID, _range
| where isnull(A_Timestamp) or (A_Timestamp <= B_Timestamp and B_Timestamp <= A_Timestamp + _maxLookbackPeriod)
| extend diff = coalesce(B_Timestamp - A_Timestamp, _maxLookbackPeriod*2)
| summarize arg_min(diff, *) by ID
| project ID, B_Timestamp, A_Timestamp, EventB, EventA
IDID B_TimestampB_Timestamp A_TimestampA_Timestamp EventBEventB EventAEventA
xx 2019-01-01 00:00:03.00000002019-01-01 00:00:03.0000000 2019-01-01 00:00:01.00000002019-01-01 00:00:01.0000000 BB Ax2Ax2
xx 2019-01-01 00:00:04.00000002019-01-01 00:00:04.0000000 2019-01-01 00:00:01.00000002019-01-01 00:00:01.0000000 BB Ax2Ax2
oy 2019-01-01 00:00:04.00000002019-01-01 00:00:04.0000000 2019-01-01 00:00:02.00000002019-01-01 00:00:02.0000000 BB Ay1Ay1
zz 2019-01-01 00:02:00.00000002019-01-01 00:02:00.0000000 BB

Étapes suivantesNext steps

Cet article identifie les besoins courants en matière de requêtes dans Azure Monitor et explique comment utiliser le langage de requête Kusto afin d’y répondre.This article identifies common query needs in Azure Monitor and how you can use the Kusto Query Language to meet them.

Opérations de chaîneString operations

Les sections suivantes fournissent des exemples montrant comment utiliser les chaînes avec le langage de requête Kusto.The following sections give examples of how to work with strings when using the Kusto Query Language.

Chaînes et échappementStrings and how to escape them

Les valeurs de chaîne sont entourées de guillemets simples ou doubles.String values are wrapped with either single or double quotes. Ajoutez la barre oblique inverse (\) à gauche d’un caractère pour placer le caractère dans une séquence d’échappement : \t pour tabulation, \n pour saut de ligne et \" pour un guillemet simple.Add the backslash (\) to the left of a character to escape the character: \t for tab, \n for newline, and \" for the single quote character.

print "this is a 'string' literal in double \" quotes"
print 'this is a "string" literal in single \' quotes'

Pour empêcher « \ » d’agir comme un caractère d’échappement, ajoutez « @ » en tant que préfixe à la chaîne :To prevent "\" from acting as an escape character, add "@" as a prefix to the string:

print @"C:\backslash\not\escaped\with @ prefix"

Comparaisons de chaînesString comparisons

OpérateurOperator DescriptionDescription Respect de la casseCase-sensitive Exemple (génère true)Example (yields true)
== Égal àEquals OuiYes "aBc" == "aBc"
!= Non égal àNot equals OuiYes "abc" != "ABC"
=~ Égal àEquals NonNo "abc" =~ "ABC"
!~ Non égal àNot equals NonNo "aBc" !~ "xyz"
has La valeur de droite correspond à un terme entier dans la valeur de gaucheRight-side value is a whole term in left-side value NonNo "North America" has "america"
!has La valeur de droite ne correspond pas à un terme entier dans la valeur de gaucheRight-side value isn't a full term in left-side value NonNo "North America" !has "amer"
has_cs La valeur de droite correspond à un terme entier dans la valeur de gaucheRight-side value is a whole term in left-side value OuiYes "North America" has_cs "America"
!has_cs La valeur de droite ne correspond pas à un terme entier dans la valeur de gaucheRight-side value isn't a full term in left-side value OuiYes "North America" !has_cs "amer"
hasprefix La valeur de droite correspond à un préfixe de terme dans la valeur de gaucheRight-side value is a term prefix in left-side value NonNo "North America" hasprefix "ame"
!hasprefix La valeur de droite ne correspond pas à un préfixe de terme dans la valeur de gaucheRight-side value isn't a term prefix in left-side value NonNo "North America" !hasprefix "mer"
hasprefix_cs La valeur de droite correspond à un préfixe de terme dans la valeur de gaucheRight-side value is a term prefix in left-side value OuiYes "North America" hasprefix_cs "Ame"
!hasprefix_cs La valeur de droite ne correspond pas à un préfixe de terme dans la valeur de gaucheRight-side value isn't a term prefix in left-side value OuiYes "North America" !hasprefix_cs "CA"
hassuffix La valeur de droite correspond à un suffixe de terme dans la valeur de gaucheRight-side value is a term suffix in left-side value NonNo "North America" hassuffix "ica"
!hassuffix La valeur de droite ne correspond pas à un suffixe de terme dans la valeur de gaucheRight-side value isn't a term suffix in left-side value NonNo "North America" !hassuffix "americ"
hassuffix_cs La valeur de droite correspond à un suffixe de terme dans la valeur de gaucheRight-side value is a term suffix in left-side value OuiYes "North America" hassuffix_cs "ica"
!hassuffix_cs La valeur de droite ne correspond pas à un suffixe de terme dans la valeur de gaucheRight-side value isn't a term suffix in left-side value OuiYes "North America" !hassuffix_cs "icA"
contains La valeur de droite correspond à une sous-séquence de la valeur de gaucheRight-side value occurs as a subsequence of left-side value NonNo "FabriKam" contains "BRik"
!contains La valeur de droite n’intervient pas dans la valeur de gaucheRight-side value doesn't occur in left-side value NonNo "Fabrikam" !contains "xyz"
contains_cs La valeur de droite correspond à une sous-séquence de la valeur de gaucheRight-side value occurs as a subsequence of left-side value OuiYes "FabriKam" contains_cs "Kam"
!contains_cs La valeur de droite n’intervient pas dans la valeur de gaucheRight-side value doesn't occur in left-side value OuiYes "Fabrikam" !contains_cs "Kam"
startswith La valeur de droite correspond à une sous-séquence initiale de la valeur de gaucheRight-side value is an initial subsequence of left-side value NonNo "Fabrikam" startswith "fab"
!startswith La valeur de droite ne correspond pas à une sous-séquence initiale de la valeur de gaucheRight-side value isn't an initial subsequence of left-side value NonNo "Fabrikam" !startswith "kam"
startswith_cs La valeur de droite correspond à une sous-séquence initiale de la valeur de gaucheRight-side value is an initial subsequence of left-side value OuiYes "Fabrikam" startswith_cs "Fab"
!startswith_cs La valeur de droite ne correspond pas à une sous-séquence initiale de la valeur de gaucheRight-side value isn't an initial subsequence of left-side value OuiYes "Fabrikam" !startswith_cs "fab"
endswith La valeur de droite correspond à une sous-séquence fermante de la valeur de gaucheRight-side value is a closing subsequence of left-side value NonNo "Fabrikam" endswith "Kam"
!endswith La valeur de droite ne correspond pas à une sous-séquence fermante de la valeur de gaucheRight-side value isn't a closing subsequence of left-side value NonNo "Fabrikam" !endswith "brik"
endswith_cs La valeur de droite correspond à une sous-séquence fermante de la valeur de gaucheRight-side value is a closing subsequence of left-side value OuiYes "Fabrikam" endswith "Kam"
!endswith_cs La valeur de droite ne correspond pas à une sous-séquence fermante de la valeur de gaucheRight-side value isn't a closing subsequence of left-side value OuiYes "Fabrikam" !endswith "brik"
matches regex La valeur de gauche contient une correspondance pour la valeur de droiteLeft-side value contains a match for right-side value OuiYes "Fabrikam" matches regex "b.*k"
in Égal à l’un des élémentsEquals to one of the elements OuiYes "abc" in ("123", "345", "abc")
!in N’est égal à aucun des élémentsNot equals to any of the elements OuiYes "bca" !in ("123", "345", "abc")

countofcountof

Compte les occurrences d’une sous-chaîne dans une chaîne.Counts occurrences of a substring within a string. Peut correspondre à des chaînes simples ou utiliser une expression régulière (regex).Can match plain strings or use a regular expression (regex). Les correspondances de chaînes simples peuvent se chevaucher, mais pas les correspondances d’expression régulière.Plain string matches might overlap, but regex matches don't overlap.

countof(text, search [, kind])
  • text : Chaîne d’entréetext: The input string
  • search : Chaîne simple ou expression régulière à faire correspondre au textesearch: Plain string or regex to match inside text
  • kind : normal | regex (par défaut : normal).kind: normal | regex (default: normal).

Renvoie le nombre de fois où la chaîne de recherche peut être mise en correspondance dans le conteneur.Returns the number of times that the search string can be matched in the container. Les correspondances de chaînes simples peuvent se chevaucher, mais pas les correspondances d’expression régulière.Plain string matches might overlap, but regex matches don't overlap.

Correspondances de chaînes simplesPlain string matches

print countof("The cat sat on the mat", "at");  //result: 3
print countof("aaa", "a");  //result: 3
print countof("aaaa", "aa");  //result: 3 (not 2!)
print countof("ababa", "ab", "normal");  //result: 2
print countof("ababa", "aba");  //result: 2

Correspondances d’expression régulièreRegex matches

print countof("The cat sat on the mat", @"\b.at\b", "regex");  //result: 3
print countof("ababa", "aba", "regex");  //result: 1
print countof("abcabc", "a.c", "regex");  // result: 2

extractextract

Obtient une correspondance pour une expression régulière à partir d’une chaîne spécifique.Gets a match for a regular expression from a specific string. Peut également convertir la sous-chaîne extraite selon le type indiqué.Optionally, can convert the extracted substring to the specified type.

extract(regex, captureGroup, text [, typeLiteral])
  • regex : Expression régulière.regex: A regular expression.
  • captureGroup : Constante entière positive indiquant le groupe de capture à extraire.captureGroup: A positive integer constant that indicates the capture group to extract. Utilisez 0 pour la correspondance entière, 1 pour la valeur mise en correspondance par la première parenthèse () dans l’expression régulière et 2 ou plus pour les parenthèses suivantes.Use 0 for the entire match, 1 for the value matched by the first parenthesis () in the regular expression, and 2 or more for subsequent parentheses.
  • text - Chaîne à rechercher.text - The string to search.
  • typeLiteral - Littéral de type facultatif (par exemple, typeof(long)).typeLiteral - An optional type literal (for example, typeof(long)). Si elle est fournie, la sous-chaîne extraite est convertie dans ce type.If provided, the extracted substring is converted to this type.

Renvoie la sous-chaîne correspondant au groupe de capture indiqué captureGroup, éventuellement convertie en typeLiteral.Returns the substring matched against the indicated capture group captureGroup, optionally converted to typeLiteral. Si aucune correspondance n’est trouvée ou si la conversion de type échoue, renvoie la valeur null.If there's no match or the type conversion fails, returns null.

L’exemple suivant extrait le dernier octet de ComputerIP à partir d’un enregistrement de pulsation :The following example extracts the last octet of ComputerIP from a heartbeat record:

Heartbeat
| where ComputerIP != "" 
| take 1
| project ComputerIP, last_octet=extract("([0-9]*$)", 1, ComputerIP) 

L’exemple suivant extrait le dernier octet, le caste en type real (nombre), puis calcule la valeur d’adresse IP suivante :The following example extracts the last octet, casts it to a real type (number), and then calculates the next IP value:

Heartbeat
| where ComputerIP != "" 
| take 1
| extend last_octet=extract("([0-9]*$)", 1, ComputerIP, typeof(real)) 
| extend next_ip=(last_octet+1)%255
| project ComputerIP, last_octet, next_ip

Dans l’exemple suivant, une définition de Duration est recherchée dans la chaîne Trace.In the next example, the string Trace is searched for a definition of Duration. La correspondance est castée en real et multipliée par une constante de temps (1 s) qui caste Duration en type timespan.The match is cast to real and multiplied by a time constant (1 s), which then casts Duration to type timespan.

let Trace="A=12, B=34, Duration=567, ...";
print Duration = extract("Duration=([0-9.]+)", 1, Trace, typeof(real));  //result: 567
print Duration_seconds =  extract("Duration=([0-9.]+)", 1, Trace, typeof(real)) * time(1s);  //result: 00:09:27

isempty, isnotempty, notemptyisempty, isnotempty, notempty

  • isempty renvoie true si l’argument est une chaîne vide ou null (voir isnull).isempty returns true if the argument is an empty string or null (see isnull).
  • isnotempty renvoie true si l’argument n’est pas une chaîne vide ou null (voir isnotnull).isnotempty returns true if the argument isn't an empty string or null (see isnotnull). Alias : notempty.Alias: notempty.
isempty(value)
isnotempty(value)

ExempleExample

print isempty("");  // result: true

print isempty("0");  // result: false

print isempty(0);  // result: false

print isempty(5);  // result: false

Heartbeat | where isnotempty(ComputerIP) | take 1  // return 1 Heartbeat record in which ComputerIP isn't empty

parseurlparseurl

Fractionne une URL en plusieurs parties, comme protocole, hôte et port, puis renvoie un objet dictionnaire contenant les parties en tant que chaînes.Splits a URL into its parts, like protocol, host, and port, and then returns a dictionary object that contains the parts as strings.

parseurl(urlstring)

ExempleExample

print parseurl("http://user:pass@contoso.com/icecream/buy.aspx?a=1&b=2#tag")

Voici le format :Here's the output:

{
    "Scheme" : "http",
    "Host" : "contoso.com",
    "Port" : "80",
    "Path" : "/icecream/buy.aspx",
    "Username" : "user",
    "Password" : "pass",
    "Query Parameters" : {"a":"1","b":"2"},
    "Fragment" : "tag"
}

replacereplace

Remplace toutes les correspondances d’expression régulière par une autre chaîne.Replaces all regex matches with another string.

replace(regex, rewrite, input_text)
  • regex : Expression régulière à mettre en correspondance.regex: The regular expression to match by. Elle peut contenir des groupes de capture entre parenthèses ().It can contain capture groups in parentheses ().
  • rewrite : Expression régulière de remplacement pour toute correspondance trouvée par une expression régulière correspondante.rewrite: The replacement regex for any match made by matching a regex. Utilisez \0 pour faire référence à la correspondance complète, \1 pour le premier groupe de capture, \2 et ainsi de suite pour les groupes de capture suivants.Use \0 to refer to the whole match, \1 for the first capture group, \2, and so on, for subsequent capture groups.
  • input_text : Chaîne d’entrée dans laquelle effectuer une recherche.input_text: The input string to search in.

Renvoie le texte après le remplacement de toutes les correspondances de l’expression régulière par les évaluations de réécriture.Returns the text after replacing all matches of regex with evaluations of rewrite. Les correspondances ne se chevauchent pas.Matches don't overlap.

ExempleExample

SecurityEvent
| take 1
| project Activity 
| extend replaced = replace(@"(\d+) -", @"Activity ID \1: ", Activity) 

Voici le format :Here's the output:

ActivitéActivity RemplacéReplaced
4663 - Une tentative d’accès à un objet a été effectuée4663 - An attempt was made to access an object ID d’activité 4663 : Une tentative d’accès à un objet a été effectuée.Activity ID 4663: An attempt was made to access an object.

splitsplit

Fractionne une chaîne spécifique en fonction d’un délimiteur spécifié, puis renvoie un tableau des sous-chaînes obtenues.Splits a specific string according to a specified delimiter, and then returns an array of the resulting substrings.

split(source, delimiter [, requestedIndex])
  • source : Chaîne à fractionner en fonction du délimiteur spécifié.source: The string to be split according to the specified delimiter.
  • delimiter : Délimiteur utilisé pour fractionner la chaîne source.delimiter: The delimiter that will be used to split the source string.
  • requestedIndex : Index de base zéro facultatif.requestedIndex: An optional zero-based index. S’il est fourni, le tableau de chaînes renvoyé contient uniquement cet élément (s’il existe).If provided, the returned string array holds only that item (if it exists).

ExempleExample

print split("aaa_bbb_ccc", "_");    // result: ["aaa","bbb","ccc"]
print split("aa_bb", "_");          // result: ["aa","bb"]
print split("aaa_bbb_ccc", "_", 1); // result: ["bbb"]
print split("", "_");               // result: [""]
print split("a__b", "_");           // result: ["a","","b"]
print split("aabbcc", "bb");        // result: ["aa","cc"]

strcatstrcat

Concatène les arguments de chaîne (prend en charge 1 à 16 arguments).Concatenates string arguments (supports 1-16 arguments).

strcat("string1", "string2", "string3")

ExempleExample

print strcat("hello", " ", "world") // result: "hello world"

strlenstrlen

Retourne la longueur d'une chaîne.Returns the length of a string.

strlen("text_to_evaluate")

ExempleExample

print strlen("hello")   // result: 5

substringsubstring

Extrait une sous-chaîne d’une chaîne source spécifique à partir de l’index indiqué.Extracts a substring from a specific source string, starting at the specified index. Vous pouvez spécifier la longueur de la sous-chaîne demandée.Optionally, you can specify the length of the requested substring.

substring(source, startingIndex [, length])
  • source : Chaîne source dont la sous-chaîne est extraite.source: The source string that the substring is taken from.
  • startingIndex : Position du caractère de départ (base zéro) de la sous-chaîne demandée.startingIndex: The zero-based starting character position of the requested substring.
  • length : Paramètre facultatif permettant de spécifier la longueur demandée de la sous-chaîne renvoyée.length: An optional parameter that you can use to specify the requested length of the returned substring.

ExempleExample

print substring("abcdefg", 1, 2);   // result: "bc"
print substring("123456", 1);       // result: "23456"
print substring("123456", 2, 2);    // result: "34"
print substring("ABCD", 0, 2);  // result: "AB"

tolower, touppertolower, toupper

Convertit une chaîne spécifique en minuscules ou en majuscules.Converts a specific string to all lowercase or all uppercase.

tolower("value")
toupper("value")

ExempleExample

print tolower("HELLO"); // result: "hello"
print toupper("hello"); // result: "HELLO"

Opérations Date/heureDate and time operations

Les sections suivantes fournissent des exemples montrant comment utiliser les valeurs de date et d’heure avec le langage de requête Kusto.The following sections give examples of how to work with date and time values when using the Kusto Query Language.

Notions de base de date et d’heureDate-time basics

Le langage de requête Kusto comporte deux principaux types de données associés aux dates et heures : datetime et timespan.The Kusto Query Language has two main data types associated with dates and times: datetime and timespan. Toutes les dates sont exprimées au format UTC.All dates are expressed in UTC. Même si plusieurs formats date/heure sont pris en charge, le format ISO-8601 est préféré.Although multiple date-time formats are supported, the ISO-8601 format is preferred.

Les types timespan sont exprimés en tant que valeur décimale suivie d’une unité de temps :Timespans are expressed as a decimal followed by a time unit:

RaccourciShorthand Unité de tempsTime unit
dd dayday
hh hourhour
mm minuteminute
ss secondsecond
msms millisecondemillisecond
microsecondemicrosecond microsecondemicrosecond
graduationtick nanosecondenanosecond

Vous pouvez créer des valeurs date/heure en castant une chaîne à l’aide de l’opérateur todatetime.You can create date-time values by casting a string using the todatetime operator. Par exemple, pour passer en revue les pulsations de machine virtuelle envoyées dans un laps de temps spécifique, utilisez l’opérateur between afin de spécifier une plage de temps :For example, to review the VM heartbeats sent in a specific timeframe, use the between operator to specify a time range:

Heartbeat
| where TimeGenerated between(datetime("2018-06-30 22:46:42") .. datetime("2018-07-01 00:57:27"))

Un autre scénario courant consiste à comparer une valeur date/heure au présent.Another common scenario is comparing a date-time value to the present. Par exemple, pour afficher toutes les pulsations au cours des deux dernières minutes, vous pouvez utiliser l’opérateur now avec une valeur timespan qui représente deux minutes :For example, to see all heartbeats over the last two minutes, you can use the now operator together with a timespan that represents two minutes:

Heartbeat
| where TimeGenerated > now() - 2m

Un raccourci est également disponible pour cette fonction :A shortcut is also available for this function:

Heartbeat
| where TimeGenerated > now(-2m)

La méthode la plus rapide et la plus lisible consiste à utiliser l’opérateur ago :The shortest and most readable method is using the ago operator:

Heartbeat
| where TimeGenerated > ago(2m)

Supposons qu’au lieu de connaître l’heure de début et de fin, vous connaissiez l’heure de début et la durée.Suppose that instead of knowing the start and end times, you know the start time and the duration. Vous pouvez réécrire la requête :You can rewrite the query:

let startDatetime = todatetime("2018-06-30 20:12:42.9");
let duration = totimespan(25m);
Heartbeat
| where TimeGenerated between(startDatetime .. (startDatetime+duration) )
| extend timeFromStart = TimeGenerated - startDatetime

Convertir les unités de tempsConvert time units

Il peut être utile d’exprimer une valeur de date/heure ou d’intervalle de temps dans une unité de temps autre que celle par défaut.You might want to express a date-time or timespan value in a time unit other than the default. Par exemple, si vous examinez les événements d’erreur des 30 dernières minutes et avez besoin d’une colonne calculée qui affiche le temps écoulé depuis que l’événement s’est produit, vous pouvez utiliser cette requête :For example, if you're reviewing error events from the past 30 minutes and need a calculated column that shows how long ago the event happened, you can use this query:

Event
| where TimeGenerated > ago(30m)
| where EventLevelName == "Error"
| extend timeAgo = now() - TimeGenerated 

La colonne timeAgo contient des valeurs comme 00:09:31.5118992, qui se présentent au format hh:mm:ss.fffffff.The timeAgo column holds values like 00:09:31.5118992, which are formatted as hh:mm:ss.fffffff. Si vous souhaitez mettre en forme ces valeurs selon le number de minutes depuis l’heure de début, divisez cette valeur par 1m :If you want to format these values to the number of minutes since the start time, divide that value by 1m:

Event
| where TimeGenerated > ago(30m)
| where EventLevelName == "Error"
| extend timeAgo = now() - TimeGenerated
| extend timeAgoMinutes = timeAgo/1m 

Agrégations et création de compartiments par intervalles de tempsAggregations and bucketing by time intervals

Un autre scénario courant se caractérise par la nécessité d’obtenir des statistiques pour une période de temps spécifique dans une unité de temps spécifique.Another common scenario is the need to obtain statistics for a specific time period in a specific time unit. Pour ce scénario, vous pouvez utiliser un opérateur bin dans le cadre d’une clause summarize.For this scenario, you can use a bin operator as part of a summarize clause.

Utilisez la requête suivante pour obtenir le nombre d’événements qui se sont produits toutes les cinq minutes au cours de la dernière demi-heure :Use the following query to get the number of events that occurred every five minutes during the past half-hour:

Event
| where TimeGenerated > ago(30m)
| summarize events_count=count() by bin(TimeGenerated, 5m) 

Cette requête produit le tableau suivant :This query produces the following table:

TimeGenerated(UTC)TimeGenerated(UTC) events_countevents_count
2018-08-01T09:30:00.0002018-08-01T09:30:00.000 5454
2018-08-01T09:35:00.0002018-08-01T09:35:00.000 4141
2018-08-01T09:40:00.0002018-08-01T09:40:00.000 4242
2018-08-01T09:45:00.0002018-08-01T09:45:00.000 4141
2018-08-01T09:50:00.0002018-08-01T09:50:00.000 4141
2018-08-01T09:55:00.0002018-08-01T09:55:00.000 1616

Une autre façon de créer des compartiments de résultats consiste à utiliser des fonctions telles que startofday :Another way to create buckets of results is to use functions like startofday:

Event
| where TimeGenerated > ago(4d)
| summarize events_count=count() by startofday(TimeGenerated) 

Voici le format :Here's the output:

timestamptimestamp count_count_
2018-07-28T00:00:00.0002018-07-28T00:00:00.000 7 1367,136
2018-07-29T00:00:00.0002018-07-29T00:00:00.000 12 31512,315
2018-07-30T00:00:00.0002018-07-30T00:00:00.000 16 84716,847
2018-07-31T00:00:00.0002018-07-31T00:00:00.000 12 61612,616
2018-08-01T00:00:00.0002018-08-01T00:00:00.000 5 4165,416

Fuseaux horairesTime zones

Toutes les valeurs date/heure étant exprimées au format UTC, il est souvent utile de convertir ces valeurs dans le fuseau horaire local.Because all date-time values are expressed in UTC, it's often useful to convert these values into the local time zone. Par exemple, utilisez ce calcul pour convertir les heures UTC en PST :For example, use this calculation to convert UTC to PST times:

Event
| extend localTimestamp = TimeGenerated - 8h

AgrégationsAggregations

Les sections suivantes fournissent des exemples montrant comment agréger les résultats d’une requête avec le langage de requête Kusto.The following sections give examples of how to aggregate the results of a query when using the Kusto Query Language.

countcount

Comptez le nombre de lignes du jeu de résultats une fois que tous les filtres sont appliqués.Count the number of rows in the result set after any filters are applied. L’exemple suivant retourne le nombre total de lignes dans la table Perf au cours des 30 dernières minutes.The following example returns the total number of rows in the Perf table from the last 30 minutes. Les résultats sont renvoyés dans une colonne nommée count_, sauf si vous lui attribuez un nom spécifique :The results are returned in a column named count_ unless you assign a specific name to the column:

Perf
| where TimeGenerated > ago(30m) 
| summarize count()
Perf
| where TimeGenerated > ago(30m) 
| summarize num_of_records=count() 

Une visualisation de graphique temporel peut être utile pour voir une tendance au fil du temps :A timechart visualization might be useful to see a trend over time:

Perf 
| where TimeGenerated > ago(30m) 
| summarize count() by bin(TimeGenerated, 5m)
| render timechart

La sortie de cet exemple affiche la courbe de tendance du nombre d’enregistrements Perf dans des intervalles de cinq minutes :The output from this example shows the Perf record count trend line in five-minute intervals:

Capture d’écran d’un graphique en courbes affichant la courbe de tendance du nombre d’enregistrements Perf dans des intervalles de cinq minutes.

dcount, dcountifdcount, dcountif

Utilisez dcount et dcountif pour compter des valeurs distinctes dans une colonne spécifique.Use dcount and dcountif to count distinct values in a specific column. La requête suivante évalue le nombre d’ordinateurs distincts qui ont envoyé des pulsations dans la dernière heure :The following query evaluates how many distinct computers sent heartbeats in the last hour:

Heartbeat 
| where TimeGenerated > ago(1h) 
| summarize dcount(Computer)

Pour compter uniquement les ordinateurs Linux qui ont envoyé des pulsations, utilisez dcountif :To count only the Linux computers that sent heartbeats, use dcountif:

Heartbeat 
| where TimeGenerated > ago(1h) 
| summarize dcountif(Computer, OSType=="Linux")

Évaluer les sous-groupesEvaluate subgroups

Pour effectuer un compte ou d’autres agrégations de sous-groupes dans vos données, utilisez le mot clé by.To perform a count or other aggregations on subgroups in your data, use the by keyword. Par exemple, pour compter le nombre d’ordinateurs Linux distincts qui ont envoyé des pulsations dans chaque pays ou région, utilisez cette requête :For example, to count the number of distinct Linux computers that sent heartbeats in each country or region, use this query:

Heartbeat 
| where TimeGenerated > ago(1h) 
| summarize distinct_computers=dcountif(Computer, OSType=="Linux") by RemoteIPCountry
RemoteIPCountryRemoteIPCountry ordinateurs_distinctsdistinct_computers
États-UnisUnited States 1919
CanadaCanada 33
IrlandeIreland 00
Royaume-UniUnited Kingdom 00
Pays-BasNetherlands 22

Pour analyser des sous-groupes encore plus petits de vos données, ajoutez des noms de colonnes à la section by.To analyze even smaller subgroups of your data, add column names to the by section. Par exemple, vous pouvez peut-être compter le nombre d’ordinateurs distincts de chaque pays ou région par type de système d’exploitation (OSType) :For example, you might want to count the distinct computers from each country or region per type of operating system (OSType):

Heartbeat 
| where TimeGenerated > ago(1h) 
| summarize distinct_computers=dcountif(Computer, OSType=="Linux") by RemoteIPCountry, OSType

PercentilePercentile

Pour rechercher la valeur médiane, utilisez la fonction percentile avec une valeur pour spécifier le centile :To find the median value, use the percentile function with a value to specify the percentile:

Perf
| where TimeGenerated > ago(30m) 
| where CounterName == "% Processor Time" and InstanceName == "_Total" 
| summarize percentiles(CounterValue, 50) by Computer

Vous pouvez également spécifier différents centiles afin d’obtenir un résultat agrégé pour chacun :You also can specify different percentiles to get an aggregated result for each:

Perf
| where TimeGenerated > ago(30m) 
| where CounterName == "% Processor Time" and InstanceName == "_Total" 
| summarize percentiles(CounterValue, 25, 50, 75, 90) by Computer

Les résultats peuvent indiquer que certains processeurs de l’ordinateur présentent des valeurs médianes similaires.The results might show that some computer CPUs have similar median values. Toutefois, si certains ordinateurs sont stables autour de la valeur médiane, d’autres ont signalé des valeurs de processeur plus faibles et plus élevées.However, although some computers are steady around the median, others have reported much lower and higher CPU values. Les valeurs élevées et faibles indiquent que les ordinateurs ont enregistré des pics.The high and low values mean that the computers have experienced spikes.

VarianceVariance

Pour évaluer directement la variance d’une valeur, utilisez les méthodes de variance et d’écart type :To directly evaluate the variance of a value, use the standard deviation and variance methods:

Perf
| where TimeGenerated > ago(30m) 
| where CounterName == "% Processor Time" and InstanceName == "_Total" 
| summarize stdev(CounterValue), variance(CounterValue) by Computer

Un bon moyen d’analyser la stabilité de l’utilisation de l’UC consiste à combiner stdev avec le calcul de la valeur médiane :A good way to analyze the stability of CPU usage is to combine stdev with the median calculation:

Perf
| where TimeGenerated > ago(130m) 
| where CounterName == "% Processor Time" and InstanceName == "_Total" 
| summarize stdev(CounterValue), percentiles(CounterValue, 50) by Computer

Générer des listes et des ensemblesGenerate lists and sets

Vous pouvez utiliser makelist pour déplacer des données selon l’ordre des valeurs dans une colonne spécifique.You can use makelist to pivot data by the order of values in a specific column. Par exemple, vous voulez explorer l’ordre le plus courant dans lequel les événements se produisent sur vos ordinateurs.For example, you might want to explore the most common order events that take place on your computers. Vous pouvez avant tout déplacer les données selon l’ordre des valeurs EventID sur chaque ordinateur :You can essentially pivot the data by the order of EventID values on each computer:

Event
| where TimeGenerated > ago(12h)
| order by TimeGenerated desc
| summarize makelist(EventID) by Computer

Voici le format :Here's the output:

ComputerComputer list_EventIDlist_EventID
computer1computer1 [704,701,1501,1500,1085,704,704,701][704,701,1501,1500,1085,704,704,701]
computer2computer2 [326,105,302,301,300,102][326,105,302,301,300,102]
...... ......

makelist génère une liste dans l’ordre dans lequel les données lui ont été passées.makelist generates a list in the order that data was passed into it. Pour trier les événements du plus ancien au plus récent, utilisez asc dans l’instruction order plutôt que desc.To sort events from oldest to newest, use asc in the order statement instead of desc.

Il peut s’avérer utile de créer une liste de valeurs distinctes uniquement.You might find it useful to create a list only of distinct values. Cette liste est appelée jeu, et vous pouvez la générer à l’aide de la commande makeset :This list is called a set, and you can generate it by using the makeset command:

Event
| where TimeGenerated > ago(12h)
| order by TimeGenerated desc
| summarize makeset(EventID) by Computer

Voici le format :Here's the output:

ComputerComputer list_EventIDlist_EventID
computer1computer1 [704,701,1501,1500,1085][704,701,1501,1500,1085]
computer2computer2 [326,105,302,301,300,102][326,105,302,301,300,102]
...... ......

Comme makelist, makeset fonctionne avec des données ordonnées.Like makelist, makeset also works with ordered data. La commande makeset génère des tableaux en fonction de l’ordre des lignes qui lui sont transmises.The makeset command generates arrays based on the order of the rows that are passed into it.

Développer les listesExpand lists

L’opération inverse de makelist ou makeset est mv-expand.The inverse operation of makelist or makeset is mv-expand. La commande mv-expand développe une liste de valeurs pour séparer les lignes.The mv-expand command expands a list of values to separate rows. L’extension peut porter sur n’importe quel nombre de colonnes dynamiques, y compris des colonnes JSON et de tableau.It can expand across any number of dynamic columns, including JSON and array columns. Par exemple, vous pouvez rechercher dans la table Heartbeat les solutions qui ont envoyé des données à partir d’ordinateurs qui ont envoyé une pulsation dans la dernière heure :For example, you can check the Heartbeat table for solutions that sent data from computers that sent a heartbeat in the past hour:

Heartbeat
| where TimeGenerated > ago(1h)
| project Computer, Solutions

Voici le format :Here's the output:

ComputerComputer SolutionsSolutions
computer1computer1 "security", "updates", "changeTracking""security", "updates", "changeTracking"
computer2computer2 "security", "updates""security", "updates"
computer3computer3 "antiMalware", "changeTracking""antiMalware", "changeTracking"
...... ......

Utilisez mv-expand pour afficher chaque valeur dans une ligne distincte plutôt que dans une liste séparée par des virgules :Use mv-expand to show each value in a separate row instead of in a comma-separated list:

Heartbeat
| where TimeGenerated > ago(1h)
| project Computer, split(Solutions, ",")
| mv-expand Solutions

Voici le format :Here's the output:

ComputerComputer SolutionsSolutions
computer1computer1 "security""security"
computer1computer1 "updates""updates"
computer1computer1 "changeTracking""changeTracking"
computer2computer2 "security""security"
computer2computer2 "updates""updates"
computer3computer3 "antiMalware""antiMalware"
computer3computer3 "changeTracking""changeTracking"
...... ......

Vous pouvez utiliser makelist pour regrouper des éléments.You can use makelist to group items together. Dans la sortie, vous pouvez voir la liste d’ordinateurs par solution :In the output, you can see the list of computers per solution:

Heartbeat
| where TimeGenerated > ago(1h)
| project Computer, split(Solutions, ",")
| mv-expand Solutions
| summarize makelist(Computer) by tostring(Solutions) 

Voici le format :Here's the output:

SolutionsSolutions list_Computerlist_Computer
"security""security" ["computer1", "computer2"]["computer1", "computer2"]
"updates""updates" ["computer1", "computer2"]["computer1", "computer2"]
"changeTracking""changeTracking" ["computer1", "computer3"]["computer1", "computer3"]
"antiMalware""antiMalware" ["computer3"]["computer3"]
...... ......

Classes manquantesMissing bins

Une application utile de mv-expand consiste à renseigner les valeurs par défaut des classes manquantes. Par exemple, supposons que vous recherchiez la durée de bon fonctionnement d’un ordinateur spécifique en explorant sa pulsation.A useful application of mv-expand is filling in default values for missing bins. For example, suppose you're looking for the uptime of a specific computer by exploring its heartbeat. Vous souhaitez également voir la source de la pulsation, qui se trouve dans la colonne Category.You also want to see the source of the heartbeat, which is in the Category column. En règle générale, nous utilisons une instruction summarize de base :Normally, we would use a basic summarize statement:

Heartbeat
| where TimeGenerated > ago(12h)
| summarize count() by Category, bin(TimeGenerated, 1h)

Voici le format :Here's the output:

CategoryCategory TimeGeneratedTimeGenerated count_count_
Agent directDirect Agent 2017-06-06T17:00:00Z2017-06-06T17:00:00Z 1515
Agent directDirect Agent 2017-06-06T18:00:00Z2017-06-06T18:00:00Z 6060
Agent directDirect Agent 2017-06-06T20:00:00Z2017-06-06T20:00:00Z 5555
Agent directDirect Agent 2017-06-06T21:00:00Z2017-06-06T21:00:00Z 5757
Agent directDirect Agent 2017-06-06T22:00:00Z2017-06-06T22:00:00Z 6060
...... ...... ......

Dans la sortie, le compartiment associé à « 2017-06-06T19:00:00Z » est manquant, car il n’existe pas de données de pulsation pour cette heure.In the output, the bucket that's associated with "2017-06-06T19:00:00Z" is missing because there isn't any heartbeat data for that hour. Utilisez la fonction make-series pour affecter une valeur par défaut aux compartiments vides.Use the make-series function to assign a default value to empty buckets. Une ligne est générée pour chaque catégorie.A row is generated for each category. La sortie inclut deux colonnes de tableau supplémentaires, une pour les valeurs et une pour les intervalles de temps correspondants :The output includes two extra array columns, one for values and one for matching time buckets:

Heartbeat
| make-series count() default=0 on TimeGenerated in range(ago(1d), now(), 1h) by Category 

Voici le format :Here's the output:

CategoryCategory count_count_ TimeGeneratedTimeGenerated
Agent directDirect Agent [15,60,0,55,60,57,60,...][15,60,0,55,60,57,60,...] ["2017-06-06T17:00:00.0000000Z","2017-06-06T18:00:00.0000000Z","2017-06-06T19:00:00.0000000Z","2017-06-06T20:00:00.0000000Z","2017-06-06T21:00:00.0000000Z",...]["2017-06-06T17:00:00.0000000Z","2017-06-06T18:00:00.0000000Z","2017-06-06T19:00:00.0000000Z","2017-06-06T20:00:00.0000000Z","2017-06-06T21:00:00.0000000Z",...]
...... ...... ......

Le troisième élément du tableau count_ est 0, comme prévu.The third element of the count_ array is 0, as expected. Le tableau TimeGenerated présente un timestamp correspondant « 2017-06-06T19:00:00.0000000 Z ».The TimeGenerated array has a matching time stamp of "2017-06-06T19:00:00.0000000Z". Ce format de tableau s’avère cependant difficile à lire.But, this array format is difficult to read. Utilisez mv-expand pour étendre les tableaux et produire le même format de sortie que celui généré par summarize :Use mv-expand to expand the arrays and produce the same format output as generated by summarize:

Heartbeat
| make-series count() default=0 on TimeGenerated in range(ago(1d), now(), 1h) by Category 
| mv-expand TimeGenerated, count_
| project Category, TimeGenerated, count_

Voici le format :Here's the output:

CategoryCategory TimeGeneratedTimeGenerated count_count_
Agent directDirect Agent 2017-06-06T17:00:00Z2017-06-06T17:00:00Z 1515
Agent directDirect Agent 2017-06-06T18:00:00Z2017-06-06T18:00:00Z 6060
Agent directDirect Agent 2017-06-06T19:00:00Z2017-06-06T19:00:00Z 00
Agent directDirect Agent 2017-06-06T20:00:00Z2017-06-06T20:00:00Z 5555
Agent directDirect Agent 2017-06-06T21:00:00Z2017-06-06T21:00:00Z 5757
Agent directDirect Agent 2017-06-06T22:00:00Z2017-06-06T22:00:00Z 6060
...... ...... ......

Limiter les résultats à un ensemble d’éléments : let, makeset, toscalar, inNarrow results to a set of elements: let, makeset, toscalar, in

Un scénario courant consiste à sélectionner les noms d’entités spécifiques selon un ensemble de critères, puis à filtrer un autre jeu de données selon cet ensemble d’entités.A common scenario is to select the names of specific entities based on a set of criteria, and then filter a different dataset down to that set of entities. Par exemple, vous pouvez rechercher les ordinateurs qui sont connus comme ayant des mises à jour manquantes et identifier les adresses IP auxquelles ces ordinateurs font appel.For example, you might find computers that are known to have missing updates and identify IP addresses that these computers called out to.

Voici un exemple :Here's an example:

let ComputersNeedingUpdate = toscalar(
    Update
    | summarize makeset(Computer)
    | project set_Computer
);
WindowsFirewall
| where Computer in (ComputersNeedingUpdate)

JointuresJoins

Vous pouvez utiliser des jointures pour analyser les données provenant de plusieurs tables dans la même requête.You can use joins to analyze data from multiple tables in the same query. Une jointure fusionne les lignes de deux jeux de données en faisant correspondre les valeurs des colonnes spécifiées.A join merges the rows of two datasets by matching values of specified columns.

Voici un exemple :Here's an example:

SecurityEvent 
| where EventID == 4624     // sign-in events
| project Computer, Account, TargetLogonId, LogonTime=TimeGenerated
| join kind= inner (
    SecurityEvent 
    | where EventID == 4634     // sign-out events
    | project TargetLogonId, LogoffTime=TimeGenerated
) on TargetLogonId
| extend Duration = LogoffTime-LogonTime
| project-away TargetLogonId1 
| top 10 by Duration desc

Dans l’exemple, le premier jeu de données filtre tous les événements de connexion.In the example, the first dataset filters for all sign-in events. Ce jeu de données est joint au premier, qui filtre tous les événements de déconnexion.That dataset is joined with a second dataset that filters for all sign-out events. Les colonnes projetées sont Computer, Account, TargetLogonId et TimeGenerated.The projected columns are Computer, Account, TargetLogonId, and TimeGenerated. Les jeux de données sont corrélés par une colonne partagée, TargetLogonId.The datasets are correlated by a shared column, TargetLogonId. La sortie correspond à un seul enregistrement par corrélation, avec à la fois l’heure de connexion et de déconnexion.The output is a single record per correlation that has both the sign-in and sign-out time.

Si les deux jeux de données présentent des colonnes portant le même nom, un numéro d’index est attribué aux colonnes du jeu de données de droite.If both datasets have columns that have the same name, the columns of the right-side dataset are given an index number. Dans cet exemple, les résultats affichent TargetLogonId avec les valeurs de la table de gauche et TargetLogonId1 avec les valeurs de la table de droite.In this example, the results would show TargetLogonId with values from the left-side table and TargetLogonId1 with values from the right-side table. Dans ce cas, la deuxième colonne TargetLogonId1 a été supprimée à l’aide de l’opérateur project-away.In this case, the second TargetLogonId1 column was removed by using the project-away operator.

Notes

Pour améliorer les performances, conservez uniquement les colonnes appropriées des jeux de données joints à l’aide de l’opérateur project.To improve performance, keep only the relevant columns of the joined datasets by using the project operator.

Utilisez la syntaxe suivante pour joindre deux jeux de données dans lesquels la clé jointe porte un nom différent entre les deux tables :Use the following syntax to join two datasets in which the joined key has a different name between the two tables:

Table1
| join ( Table2 ) 
on $left.key1 == $right.key2

Tables de rechercheLookup tables

Une utilisation courante des jointures consiste à recourir à datatable pour le mappage des valeurs statiques.A common use of joins is to use datatable for static value mapping. L’utilisation de datatable améliore la présentation des résultats.Using datatable can help make results more presentable. Par exemple, vous pouvez enrichir les données d’événements de sécurité avec le nom de l’événement pour chaque ID d’événement :For example, you can enrich security event data with the event name for each event ID:

let DimTable = datatable(EventID:int, eventName:string)
  [
    4625, "Account activity",
    4688, "Process action",
    4634, "Account activity",
    4658, "The handle to an object was closed",
    4656, "A handle to an object was requested",
    4690, "An attempt was made to duplicate a handle to an object",
    4663, "An attempt was made to access an object",
    5061, "Cryptographic operation",
    5058, "Key file operation"
  ];
SecurityEvent
| join kind = inner
 DimTable on EventID
| summarize count() by eventName

Voici le format :Here's the output:

eventNameeventName count_count_
Un descripteur d’objet a été ferméThe handle to an object was closed 290,995290,995
Un descripteur d’objet a été demandéA handle to an object was requested 154,157154,157
Un descripteur d’objet a fait l’objet d’une tentative de duplicationAn attempt was made to duplicate a handle to an object 144,305144,305
Une tentative d’accès à un objet a eu lieuAn attempt was made to access an object 123,669123,669
Opération de chiffrementCryptographic operation 153,495153,495
Opération de fichier de cléKey file operation 153,496153,496

JSON et les structures de donnéesJSON and data structures

Les objets imbriqués sont des objets qui contiennent d’autres objets dans un tableau ou un mappage de paires clé-valeur.Nested objects are objects that contain other objects in an array or in a map of key-value pairs. Ces objets sont représentés sous forme de chaînes JSON.The objects are represented as JSON strings. Cette section explique comment utiliser JSON pour récupérer des données et analyser les objets imbriqués.This section describes how you can use JSON to retrieve data and analyze nested objects.

Utiliser des chaînes JSONWork with JSON strings

Utilisez extractjson pour accéder à un élément JSON spécifique dans un chemin connu.Use extractjson to access a specific JSON element in a known path. Cette fonction nécessite une expression de chemin qui utilise les conventions suivantes :This function requires a path expression that uses the following conventions:

  • Utilisez $ pour faire référence au dossier racine.Use $ to refer to the root folder.
  • Utilisez la notation entre crochets ou sous forme de points pour faire référence aux index et aux éléments, comme illustré dans les exemples suivants.Use the bracket or dot notation to refer to indexes and elements as illustrated in the following examples.

Utilisez des crochets pour les index et des points pour séparer les éléments :Use brackets for indexes and dots to separate elements:

let hosts_report='{"hosts": [{"location":"North_DC", "status":"running", "rate":5},{"location":"South_DC", "status":"stopped", "rate":3}]}';
print hosts_report
| extend status = extractjson("$.hosts[0].status", hosts_report)

Cet exemple est similaire, mais il utilise uniquement la notation entre crochets :This example is similar, but it uses only the brackets notation:

let hosts_report='{"hosts": [{"location":"North_DC", "status":"running", "rate":5},{"location":"South_DC", "status":"stopped", "rate":3}]}';
print hosts_report 
| extend status = extractjson("$['hosts'][0]['status']", hosts_report)

Pour un seul élément, vous pouvez uniquement utiliser la notation sous forme de points :For only one element, you can use only the dot notation:

let hosts_report=dynamic({"location":"North_DC", "status":"running", "rate":5});
print hosts_report 
| extend status = hosts_report.status

parsejsonparsejson

Il est plus facile d’accéder à plusieurs éléments de votre structure JSON en tant qu’objet dynamique.It's easiest to access multiple elements in your JSON structure as a dynamic object. Utilisez parsejson pour caster des données de texte en objet dynamique.Use parsejson to cast text data to a dynamic object. Après avoir converti le code JSON en type dynamique, vous pouvez utiliser des fonctions supplémentaires pour analyser les données.After you convert the JSON to a dynamic type, you can use additional functions to analyze the data.

let hosts_object = parsejson('{"hosts": [{"location":"North_DC", "status":"running", "rate":5},{"location":"South_DC", "status":"stopped", "rate":3}]}');
print hosts_object 
| extend status0=hosts_object.hosts[0].status, rate1=hosts_object.hosts[1].rate

arraylengtharraylength

Utilisez arraylength pour compter le nombre d’éléments dans un tableau :Use arraylength to count the number of elements in an array:

let hosts_object = parsejson('{"hosts": [{"location":"North_DC", "status":"running", "rate":5},{"location":"South_DC", "status":"stopped", "rate":3}]}');
print hosts_object 
| extend hosts_num=arraylength(hosts_object.hosts)

mv-expandmv-expand

Utilisez mv-expand pour répartir les propriétés d’un objet dans des lignes séparées :Use mv-expand to break the properties of an object into separate rows:

let hosts_object = parsejson('{"hosts": [{"location":"North_DC", "status":"running", "rate":5},{"location":"South_DC", "status":"stopped", "rate":3}]}');
print hosts_object 
| mv-expand hosts_object.hosts[0]

Capture d’écran montrant hosts_0 avec des valeurs pour l’emplacement, l’état et le taux.

buildschemabuildschema

Utilisez buildschema pour obtenir le schéma qui admet toutes les valeurs d’un objet :Use buildschema to get the schema that admits all values of an object:

let hosts_object = parsejson('{"hosts": [{"location":"North_DC", "status":"running", "rate":5},{"location":"South_DC", "status":"stopped", "rate":3}]}');
print hosts_object 
| summarize buildschema(hosts_object)

Le résultat correspond à un schéma au format JSON :The result is a schema in JSON format:

{
    "hosts":
    {
        "indexer":
        {
            "location": "string",
            "rate": "int",
            "status": "string"
        }
    }
}

Le schéma décrit les noms des champs d’objets et leurs types de données correspondants.The schema describes the names of the object fields and their matching data types.

Les objets imbriqués peuvent avoir des schémas différents, comme dans l’exemple suivant :Nested objects might have different schemas, as in the following example:

let hosts_object = parsejson('{"hosts": [{"location":"North_DC", "status":"running", "rate":5},{"status":"stopped", "rate":"3", "range":100}]}');
print hosts_object 
| summarize buildschema(hosts_object)

GraphiquesCharts

Les sections suivantes fournissent des exemples montrant comment utiliser les graphiques avec le langage de requête Kusto.The following sections give examples of how to work with charts when using the Kusto Query Language.

Présenter les résultats sous forme de graphiqueChart the results

Commencez par examiner le nombre d’ordinateurs par système d’exploitation au cours de la dernière heure :Begin by reviewing the number of computers per operating system during the past hour:

Heartbeat
| where TimeGenerated > ago(1h)
| summarize count(Computer) by OSType  

Par défaut, les résultats s’affichent sous forme de table :By default, the results display as a table:

Capture d’écran montrant les résultats de la requête dans une table.

Pour un meilleur affichage, sélectionnez Graphique, puis l’option Secteurs afin de visualiser les résultats :For a more useful view, select Chart, and then select the Pie option to visualize the results:

Capture d’écran montrant les résultats de la requête dans un graphique à secteurs.

Graphiques temporelsTimecharts

Affichez la moyenne, le 50e et le 95e centiles de temps processeur dans des classes d’une heure.Show the average and the 50th and 95th percentiles of processor time in bins of one hour.

La requête suivante génère plusieurs séries.The following query generates multiple series. Dans les résultats, vous pouvez choisir la série à afficher dans le graphique temporel.In the results, you can choose which series to show in the timechart.

Perf
| where TimeGenerated > ago(1d) 
| where CounterName == "% Processor Time" 
| summarize avg(CounterValue), percentiles(CounterValue, 50, 95)  by bin(TimeGenerated, 1h)

Sélectionnez l’option d’affichage de graphique Courbes :Select the Line chart display option:

Capture d’écran montrant un graphique en courbes à plusieurs séries.

Ligne de référenceReference line

Une ligne de référence peut vous aider à identifier facilement si la métrique a dépassé un seuil spécifique.A reference line can help you easily identify whether the metric exceeded a specific threshold. Pour ajouter une ligne à un graphique, étendez le jeu de données en ajoutant une colonne constante :To add a line to a chart, extend the dataset by adding a constant column:

Perf
| where TimeGenerated > ago(1d) 
| where CounterName == "% Processor Time" 
| summarize avg(CounterValue), percentiles(CounterValue, 50, 95)  by bin(TimeGenerated, 1h)
| extend Threshold = 20

Capture d’écran montrant un graphique en courbes à plusieurs séries avec une ligne de référence de seuil.

Plusieurs dimensionsMultiple dimensions

Plusieurs expressions de la clause by de summarize créent plusieurs lignes dans les résultats.Multiple expressions in the by clause of summarize create multiple rows in the results. Une ligne est créée pour chaque combinaison de valeurs.One row is created for each combination of values.

SecurityEvent
| where TimeGenerated > ago(1d)
| summarize count() by tostring(EventID), AccountType, bin(TimeGenerated, 1h)

Lorsque vous affichez les résultats sous forme de graphique, celui-ci utilise la première colonne de la clause by.When you view the results as a chart, the chart uses the first column from the by clause. L’exemple suivant montre un histogramme empilé créé avec la valeur EventID.The following example shows a stacked column chart that's created by using the EventID value. Les dimensions doivent être de type string.Dimensions must be of the string type. Dans cet exemple, la valeur EventID est castée sur string :In this example, the EventID value is cast to string:

Capture d’écran montrant un graphique à barres basé sur la colonne EventID.

Vous pouvez changer de colonne en sélectionnant la flèche déroulante du nom de la colonne souhaitée :You can switch between columns by selecting the drop-down arrow for the column name:

Capture d’écran montrant un graphique à barres basé sur la colonne AccountType, avec le sélecteur de colonne visible.

Analytique intelligenteSmart analytics

Cette section contient des exemples qui utilisent des fonctions d’analytique intelligente dans Azure Log Analytics pour analyser l’activité des utilisateurs.This section includes examples that use smart analytics functions in Azure Log Analytics to analyze user activity. Vous pouvez utiliser ces exemples pour analyser vos propres applications supervisées par Azure Application Insights, ou utiliser les concepts de ces requêtes pour effectuer une analyse similaire sur d’autres données.You can use these examples to analyze your own applications that are monitored by Azure Application Insights, or use the concepts in these queries for similar analysis on other data.

Analytique des cohortesCohorts analytics

L’analyse des cohortes effectue le suivi de l’activité de groupes d’utilisateurs spécifiques, appelés cohortes.Cohort analysis tracks the activity of specific groups of users, known as cohorts. L’analyse des cohortes tente de mesurer le degré d’attractivité d’un service en mesurant le taux de retour des utilisateurs.Cohort analytics attempts to measure how appealing a service is by measuring the rate of returning users. Les utilisateurs sont regroupés d’après le moment où ils ont utilisé le service pour la première fois.Users are grouped by the time they first used the service. Lors de l’analyse des cohortes, nous nous attendons à observer une baisse d’activité sur les premières périodes suivies.When analyzing cohorts, we expect to find a decrease in activity over the first tracked periods. Le titre de chaque cohorte correspond à la semaine durant laquelle ses membres ont été observés pour la première fois.Each cohort is titled by the week its members were observed for the first time.

L’exemple suivant analyse le nombre d’activités que les utilisateurs ont effectuées durant les cinq semaines suivant leur première utilisation du service :The following example analyzes the number of activities users completed during five weeks after their first use of the service:

let startDate = startofweek(bin(datetime(2017-01-20T00:00:00Z), 1d));
let week = range Cohort from startDate to datetime(2017-03-01T00:00:00Z) step 7d;
// For each user, we find the first and last timestamp of activity
let FirstAndLastUserActivity = (end:datetime) 
{
    customEvents
    | where customDimensions["sourceapp"]=="ai-loganalyticsui-prod"
    // Check 30 days back to see first time activity.
    | where timestamp > startDate - 30d
    | where timestamp < end      
    | summarize min=min(timestamp), max=max(timestamp) by user_AuthenticatedId
};
let DistinctUsers = (cohortPeriod:datetime, evaluatePeriod:datetime) {
    toscalar (
    FirstAndLastUserActivity(evaluatePeriod)
    // Find members of the cohort: only users that were observed in this period for the first time.
    | where min >= cohortPeriod and min < cohortPeriod + 7d  
    // Pick only the members that were active during the evaluated period or after.
    | where max > evaluatePeriod - 7d
    | summarize dcount(user_AuthenticatedId)) 
};
week 
| where Cohort == startDate
// Finally, calculate the desired metric for each cohort. In this sample, we calculate distinct users but you can change
// this to any other metric that would measure the engagement of the cohort members.
| extend 
    r0 = DistinctUsers(startDate, startDate+7d),
    r1 = DistinctUsers(startDate, startDate+14d),
    r2 = DistinctUsers(startDate, startDate+21d),
    r3 = DistinctUsers(startDate, startDate+28d),
    r4 = DistinctUsers(startDate, startDate+35d)
| union (week | where Cohort == startDate + 7d 
| extend 
    r0 = DistinctUsers(startDate+7d, startDate+14d),
    r1 = DistinctUsers(startDate+7d, startDate+21d),
    r2 = DistinctUsers(startDate+7d, startDate+28d),
    r3 = DistinctUsers(startDate+7d, startDate+35d) )
| union (week | where Cohort == startDate + 14d 
| extend 
    r0 = DistinctUsers(startDate+14d, startDate+21d),
    r1 = DistinctUsers(startDate+14d, startDate+28d),
    r2 = DistinctUsers(startDate+14d, startDate+35d) )
| union (week | where Cohort == startDate + 21d 
| extend 
    r0 = DistinctUsers(startDate+21d, startDate+28d),
    r1 = DistinctUsers(startDate+21d, startDate+35d) ) 
| union (week | where Cohort == startDate + 28d 
| extend 
    r0 = DistinctUsers (startDate+28d, startDate+35d) )
// Calculate the retention percentage for each cohort by weeks
| project Cohort, r0, r1, r2, r3, r4,
          p0 = r0/r0*100,
          p1 = todouble(r1)/todouble (r0)*100,
          p2 = todouble(r2)/todouble(r0)*100,
          p3 = todouble(r3)/todouble(r0)*100,
          p4 = todouble(r4)/todouble(r0)*100 
| sort by Cohort asc

Voici le format :Here's the output:

Capture d’écran montrant une table de cohortes basée sur l’activité.

Cumul des utilisateurs actifs mensuels et de l’adhérence utilisateurRolling monthly active users and user stickiness

L’exemple suivant utilise l’analyse des séries chronologiques avec la fonction series_fir.The following example uses time-series analysis with the series_fir function. Vous pouvez utiliser la fonction series_fir pour les calculs de fenêtre glissante.You can use the series_fir function for sliding window computations. L’exemple d’application supervisée est un magasin en ligne qui assure le suivi de l’activité des utilisateurs par le biais d’événements personnalisés.The sample application being monitored is an online store that tracks users' activity through custom events. La requête suit deux types d’activités utilisateur : AddToCart et Checkout.The query tracks two types of user activities: AddToCart and Checkout. Elle définit un utilisateur actif en tant qu’utilisateur ayant effectué une extraction au moins une fois un jour spécifique.It defines an active user as a user who completed a checkout at least once on a specific day.

let endtime = endofday(datetime(2017-03-01T00:00:00Z));
let window = 60d;
let starttime = endtime-window;
let interval = 1d;
let user_bins_to_analyze = 28;
// Create an array of filters coefficients for series_fir(). A list of '1' in our case will produce a simple sum.
let moving_sum_filter = toscalar(range x from 1 to user_bins_to_analyze step 1 | extend v=1 | summarize makelist(v)); 
// Level of engagement. Users will be counted as engaged if they completed at least this number of activities.
let min_activity = 1;
customEvents
| where timestamp > starttime  
| where customDimensions["sourceapp"] == "ai-loganalyticsui-prod"
// We want to analyze users who actually checked out in our website.
| where (name == "Checkout") and user_AuthenticatedId <> ""
// Create a series of activities per user.
| make-series UserClicks=count() default=0 on timestamp 
    in range(starttime, endtime-1s, interval) by user_AuthenticatedId
// Create a new column that contains a sliding sum. 
// Passing 'false' as the last parameter to series_fir() prevents normalization of the calculation by the size of the window.
// For each time bin in the *RollingUserClicks* column, the value is the aggregation of the user activities in the 
// 28 days that preceded the bin. For example, if a user was active once on 2016-12-31 and then inactive throughout 
// January, then the value will be 1 between 2016-12-31 -> 2017-01-28 and then 0s. 
| extend RollingUserClicks=series_fir(UserClicks, moving_sum_filter, false)
// Use the zip() operator to pack the timestamp with the user activities per time bin.
| project User_AuthenticatedId=user_AuthenticatedId , RollingUserClicksByDay=zip(timestamp, RollingUserClicks)
// Transpose the table and create a separate row for each combination of user and time bin (1 day).
| mv-expand RollingUserClicksByDay
| extend Timestamp=todatetime(RollingUserClicksByDay[0])
// Mark the users that qualify according to min_activity.
| extend RollingActiveUsersByDay=iff(toint(RollingUserClicksByDay[1]) >= min_activity, 1, 0)
// And finally, count the number of users per time bin.
| summarize sum(RollingActiveUsersByDay) by Timestamp
// First 28 days contain partial data, so we filter them out.
| where Timestamp > starttime + 28d
// Render as timechart.
| render timechart

Voici le format :Here's the output:

Capture d’écran d’un graphique montrant le cumul d’utilisateurs actifs par jour sur un mois.

L’exemple suivant convertit la requête précédente en fonction réutilisable.The following example turns the preceding query into a reusable function. L’exemple utilise ensuite la requête pour calculer le cumul d’adhérence utilisateur.The example then uses the query to calculate rolling user stickiness. Un utilisateur actif de cette requête est défini en tant qu’utilisateur ayant effectué une extraction au moins une fois un jour spécifique.An active user in this query is defined as a user who completed a checkout at least once on a specific day.

let rollingDcount = (sliding_window_size: int, event_name:string)
{
    let endtime = endofday(datetime(2017-03-01T00:00:00Z));
    let window = 90d;
    let starttime = endtime-window;
    let interval = 1d;
    let moving_sum_filter = toscalar(range x from 1 to sliding_window_size step 1 | extend v=1| summarize makelist(v));    
    let min_activity = 1;
    customEvents
    | where timestamp > starttime
    | where customDimensions["sourceapp"]=="ai-loganalyticsui-prod"
    | where (name == event_name)
    | where user_AuthenticatedId <> ""
    | make-series UserClicks=count() default=0 on timestamp 
        in range(starttime, endtime-1s, interval) by user_AuthenticatedId
    | extend RollingUserClicks=fir(UserClicks, moving_sum_filter, false)
    | project User_AuthenticatedId=user_AuthenticatedId , RollingUserClicksByDay=zip(timestamp, RollingUserClicks)
    | mv-expand RollingUserClicksByDay
    | extend Timestamp=todatetime(RollingUserClicksByDay[0])
    | extend RollingActiveUsersByDay=iff(toint(RollingUserClicksByDay[1]) >= min_activity, 1, 0)
    | summarize sum(RollingActiveUsersByDay) by Timestamp
    | where Timestamp > starttime + 28d
};
// Use the moving_sum_filter with bin size of 28 to count MAU.
rollingDcount(28, "Checkout")
| join
(
    // Use the moving_sum_filter with bin size of 1 to count DAU.
    rollingDcount(1, "Checkout")
)
on Timestamp
| project sum_RollingActiveUsersByDay1 *1.0 / sum_RollingActiveUsersByDay, Timestamp
| render timechart

Voici le format :Here's the output:

Capture d’écran d’un graphique montrant l’adhérence utilisateur au fil du temps.

Analyse de régressionRegression analysis

Cet exemple montre comment créer un détecteur automatisé pour les interruptions de service basé exclusivement sur les journaux d’activité des traces d’une application.This example demonstrates how to create an automated detector for service disruptions based exclusively on an application's trace logs. Le détecteur recherche les augmentations soudaines et anormales dans la quantité relative de traces d’erreur et d’avertissement dans l’application.The detector seeks abnormal, sudden increases in the relative amount of error and warning traces in the application.

Deux techniques sont utilisées pour évaluer l’état du service en fonction des données de journaux d’activité des traces :Two techniques are used to evaluate the service status based on trace logs data:

  • Utilisez make-series pour convertir les journaux d’activité des traces textuels semi-structurés en une métrique qui représente le rapport entre les lignes de traces positives et négatives.Use make-series to convert semi-structured textual trace logs into a metric that represents the ratio between positive and negative trace lines.
  • Utilisez series_fit_2lines et series_fit_line pour effectuer une détection de saut avancée à l’aide d’une analyse des séries chronologiques avec une régression linéaire de deux lignes.Use series_fit_2lines and series_fit_line for advanced step-jump detection by using time-series analysis with a two-line linear regression.
let startDate = startofday(datetime("2017-02-01"));
let endDate = startofday(datetime("2017-02-07"));
let minRsquare = 0.8;  // Tune the sensitivity of the detection sensor. Values close to 1 indicate very low sensitivity.
// Count all Good (Verbose + Info) and Bad (Error + Fatal + Warning) traces, per day.
traces
| where timestamp > startDate and timestamp < endDate
| summarize 
    Verbose = countif(severityLevel == 0),
    Info = countif(severityLevel == 1), 
    Warning = countif(severityLevel == 2),
    Error = countif(severityLevel == 3),
    Fatal = countif(severityLevel == 4) by bin(timestamp, 1d)
| extend Bad = (Error + Fatal + Warning), Good = (Verbose + Info)
// Determine the ratio of bad traces, from the total.
| extend Ratio = (todouble(Bad) / todouble(Good + Bad))*10000
| project timestamp , Ratio
// Create a time series.
| make-series RatioSeries=any(Ratio) default=0 on timestamp in range(startDate , endDate -1d, 1d) by 'TraceSeverity' 
// Apply a 2-line regression to the time series.
| extend (RSquare2, SplitIdx, Variance2,RVariance2,LineFit2)=series_fit_2lines(RatioSeries)
// Find out if our 2-line is trending up or down.
| extend (Slope,Interception,RSquare,Variance,RVariance,LineFit)=series_fit_line(LineFit2)
// Check whether the line fit reaches the threshold, and if the spike represents an increase (rather than a decrease).
| project PatternMatch = iff(RSquare2 > minRsquare and Slope>0, "Spike detected", "No Match")

Étapes suivantesNext steps