Contoh Kueri Kusto
Artikel ini mengidentifikasi kueri umum dan cara menggunakan Bahasa Kueri Kusto untuk memenuhinya.
Menampilkan bagan kolom
Untuk memproyeksikan dua kolom atau lebih, lalu menggunakan kolom tersebut sebagai sumbu-x dan sumbu-y bagan:
StormEvents
| where isnotempty(EndLocation)
| summarize event_count=count() by EndLocation
| top 10 by event_count
| render columnchart
- Kolom pertama membentuk sumbu-x. Ini bisa berupa angka, tanggal-waktu, atau string.
- Gunakan
where,summarize, dantopuntuk membatasi volume data yang Anda tampilkan. - Urutkan hasil untuk menentukan urutan sumbu-x.
Mendapatkan sesi dari peristiwa mulai dan berhenti
Dalam log peristiwa, beberapa peristiwa menandai awal atau akhir aktivitas atau sesi yang diperpanjang.
| Nama | Kota | SessionId | Tanda waktu |
|---|---|---|---|
| Mulai | London | 2817330 | 2015-12-09T10:12:02.32 |
| Permainan | London | 2817330 | 2015-12-09T10:12:52.45 |
| Mulai | Manchester | 4267667 | 2015-12-09T10:14:02.23 |
| Hentikan | London | 2817330 | 2015-12-09T10:23:43.18 |
| Batalkan | Manchester | 4267667 | 2015-12-09T10:27:26.29 |
| Hentikan | Manchester | 4267667 | 2015-12-09T10:28:31.72 |
Setiap peristiwa memiliki ID sesi (SessionId). Tantangannya adalah untuk mencocokkan peristiwa mulai dan berhenti dengan ID sesi.
Contoh:
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
Untuk mencocokkan peristiwa mulai dan berhenti dengan ID sesi:
- Gunakan let untuk memberi nama proyeksi tabel yang disederhanakan sebisa mungkin sebelum memulai penggabungan.
- Gunakan project untuk mengubah nama stempel waktu sehingga waktu mulai dan waktu berhenti muncul dalam hasil.
projectjuga memilih kolom lain yang akan ditampilkan dalam hasil. - Gunakan join untuk mencocokkan entri mulai dan berhenti untuk aktivitas yang sama. Sebuah baris dibuat untuk setiap aktivitas.
- Use
projectlagi untuk menambahkan kolom guna menampilkan durasi aktivitas.
Berikut output-nya:
| Kota | SessionId | StartTime | StopTime | Durasi |
|---|---|---|---|---|
| London | 2817330 | 2015-12-09T10:12:02.32 | 2015-12-09T10:23:43.18 | 00:11:40.46 |
| Manchester | 4267667 | 2015-12-09T10:14:02.23 | 2015-12-09T10:28:31.72 | 00:14:29.49 |
Mendapatkan sesi tanpa menggunakan ID sesi
Misalkan peristiwa mulai dan berhenti tidak memiliki ID sesi yang dapat kita cocokkan. Tetapi, kita memiliki alamat IP klien tempat sesi berlangsung. Dengan asumsi setiap alamat klien hanya melakukan satu sesi pada satu waktu, kita dapat mencocokkan setiap peristiwa mulai dengan peristiwa berhenti berikutnya dari alamat IP yang sama:
Contoh:
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
join mencocokkan setiap waktu mulai dengan semua waktu berhenti dari alamat IP klien yang sama. Kode sampel:
- Menghapus kecocokan dengan waktu berhenti sebelumnya.
- Kelompokkan berdasarkan waktu mulai dan alamat IP untuk mendapatkan grup untuk setiap sesi.
- Menyediakan fungsi
binuntuk parameterStartTime. Jika Anda tidak melakukan langkah ini, Kusto secara otomatis menggunakan bin satu jam mencocokkan beberapa waktu mulai dengan waktu berhenti yang salah.
arg_min menemukan baris dengan durasi terkecil di setiap grup, kemudian parameter * melewati semua kolom lainnya.
Argumen memberi prefiks min_ ke setiap nama kolom.
Tambahkan kode untuk menghitung durasi dalam bin berukuran konvensional. Dalam contoh ini, karena preferensi untuk bagan batang, bagi dengan 1s untuk mengubah rentang waktu menjadi angka:
// 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
Contoh lengkap
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
Sesi bersamaan bagan dari waktu ke waktu
Misalkan Anda memiliki tabel aktivitas serta waktu mulai dan berakhirnya. Anda dapat menampilkan bagan yang menampilkan jumlah aktivitas yang berjalan bersamaan dari waktu ke waktu.
Berikut adalah input sampel, yang disebut X:
| SessionId | StartTime | StopTime |
|---|---|---|
| a | 10:01:03 | 10:10:08 |
| b | 10:01:29 | 10:03:10 |
| c | 10:03:02 | 10:05:20 |
Untuk bagan dalam bin satu menit, Anda dapat menghitung setiap aktivitas yang berjalan pada setiap interval satu menit.
Berikut adalah hasil menengah:
X | extend samples = range(bin(StartTime, 1m), StopTime, 1m)
range membuat sebuah array nilai pada interval yang ditentukan:
| SessionId | StartTime | StopTime | sampel |
|---|---|---|---|
| a | 10:01:33 | 10:06:31 | [10:01:00,10:02:00,...10:06:00] |
| b | 10:02:29 | 10:03:45 | [10:02:00,10:03:00] |
| c | 10:03:12 | 10:04:30 | [10:03:00,10:04:00] |
Alih-alih menyimpannya, perluas array tersebut dengan menggunakan mv-expand:
X | mv-expand samples = range(bin(StartTime, 1m), StopTime , 1m)
| SessionId | StartTime | StopTime | sampel |
|---|---|---|---|
| a | 10:01:33 | 10:06:31 | 10:01:00 |
| a | 10:01:33 | 10:06:31 | 10:02:00 |
| a | 10:01:33 | 10:06:31 | 10:03:00 |
| a | 10:01:33 | 10:06:31 | 10:04:00 |
| a | 10:01:33 | 10:06:31 | 10:05:00 |
| a | 10:01:33 | 10:06:31 | 10:06:00 |
| b | 10:02:29 | 10:03:45 | 10:02:00 |
| b | 10:02:29 | 10:03:45 | 10:03:00 |
| c | 10:03:12 | 10:04:30 | 10:03:00 |
| c | 10:03:12 | 10:04:30 | 10:04:00 |
Sekarang, kelompokkan hasil berdasarkan waktu sampel dan hitung kemunculan setiap aktivitas:
X
| mv-expand samples = range(bin(StartTime, 1m), StopTime , 1m)
| summarize count_SessionId = count() by bin(todatetime(samples),1m)
- Gunakan
todatetime()karena mv-expand menghasilkan kolom jenis dinamis. - Gunakan
bin()karena, untuk nilai dan tanggal numerik, jika Anda tidak menyediakan interval,summarizeselalu menerapkan fungsibin()dengan menggunakan interval default.
Berikut output-nya:
| count_SessionId | sampel |
|---|---|
| 1 | 10:01:00 |
| 2 | 10:02:00 |
| 3 | 10:03:00 |
| 2 | 10:04:00 |
| 1 | 10:05:00 |
| 1 | 10:06:00 |
Anda dapat menggunakan bagan batang atau bagan waktu untuk merender hasilnya.
Memperkenalkan bin null ke summarize
Ketika operator summarize diterapkan pada kunci grup yang terdiri dari kolom tanggal-waktu, bin nilai tersebut ke bin dengan lebar tetap:
let StartTime=ago(12h);
let StopTime=now()
T
| where Timestamp > StartTime and Timestamp <= StopTime
| where ...
| summarize Count=count() by bin(Timestamp, 5m)
Contoh ini menghasilkan tabel yang memiliki satu baris per kelompok baris dalam T yang masuk ke setiap bin lima menit.
Yang tidak dilakukan kode adalah menambahkan baris "bin null" untuk nilai bin waktu antara StartTime dan StopTime yang tidak memiliki baris yang sesuai dalam T. Sebaiknya lakukan "pad" tabel dengan bin tersebut. Berikut adalah salah satu cara untuk melakukannya:
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
Berikut adalah penjelasan langkah demi langkah dari kueri sebelumnya:
- Gunakan operator
unionuntuk menambahkan lebih banyak baris ke tabel. Baris tersebut dihasilkan oleh ekspresiunion. - Operator
rangemenghasilkan tabel yang memiliki satu baris dan kolom. Tabel tidak digunakan untuk tujuan apa pun selain untukmv-expand. - Operator
mv-expandpada fungsirangemembuat baris sebanyak jumlah bin lima menit antaraStartTimedanEndTime. - Gunakan
Countdari0. - Operator
summarizemengelompokkan bin dari argumen asli (kiri, atau luar) keunion. Operator juga melakukan bin dari argumen dalam ke baris bin null. Proses ini memastikan bahwa output memiliki satu baris per bin yang nilainya nol atau jumlah asli.
Pelajari lebih lanjut data Anda menggunakan Kusto dengan pembelajaran mesin
Banyak kasus penggunaan yang menarik menggunakan algoritma pembelajaran mesin dan memperoleh wawasan menarik dari data telemetri. Seringkali, algoritma ini memerlukan himpunan data yang terstruktur secara ketat sebagai inputnya. Data log mentah biasanya tidak sesuai dengan struktur dan ukuran yang diperlukan.
Mulailah dengan mencari anomali dalam tingkat kesalahan layanan inferensi Bing tertentu. Tabel log memiliki 65 miliar rekaman. Kueri dasar berikut memfilter 250.000 kesalahan, lalu membuat seri waktu jumlah kesalahan yang menggunakan fungsi deteksi anomali series_decompose_anomalies. Anomali terdeteksi oleh layanan Kusto dan ditandai sebagai titik merah pada bagan seri waktu.
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
Layanan ini mengidentifikasi beberapa wadah waktu yang memiliki tingkat kesalahan yang mencurigakan. Gunakan Kusto untuk melihat rentang waktu ini lebih mendalam. Kemudian, jalankan kueri yang melakukan penggabungan pada kolom Message. Cobalah untuk menemukan kesalahan teratas.
Bagian yang relevan dari seluruh jejak tumpukan pesan dipangkas, sehingga hasilnya lebih pas di halaman.
Anda dapat melihat identifikasi yang berhasil dari delapan kesalahan teratas. Namun, berikutnya adalah serangkaian kesalahan yang panjang, karena pesan kesalahan dibuat dengan menggunakan string format yang berisi data yang berubah:
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_ | Pesan |
|---|---|
| 7125 | ExecuteAlgorithmMethod untuk metode 'RunCycleFromInterimData' telah gagal... |
| 7125 | Panggilan InferenceHostService gagal..System.NullReferenceException: Referensi objek tidak diatur ke instans dari objek... |
| 7124 | Kesalahan Sistem Inferensi Tidak Terduga..System.NullReferenceException: Referensi objek tidak diatur ke instans dari objek... |
| 5112 | Kesalahan Sistem Inferensi Tidak Terduga..System.NullReferenceException: Referensi objek tidak diatur ke instans dari objek.. |
| 174 | Panggilan InferenceHostService gagal.. System.ServiceModel.CommunicationException: Terjadi kesalahan saat menulis ke pipa:... |
| 10 | ExecuteAlgorithmMethod untuk metode 'RunCycleFromInterimData' telah gagal... |
| 10 | Kesalahan Sistem Inferensi..Microsoft.Bing.Platform.Inferences.Service.Managers.UserInterimDataManagerException:... |
| 3 | Panggilan InferenceHostService gagal..System.ServiceModel.CommunicationObjectFaultedException:... |
| 1 | Kesalahan Sistem Inferensi... SocialGraph.BOSS.OperationResponse...AIS TraceId:8292FC561AC64BED8FA243808FE74EFD... |
| 1 | Kesalahan Sistem Inferensi... SocialGraph.BOSS.OperationResponse...AIS TraceId: 5F79F7587FF943EC9B641E02E701AFBF... |
Di sini, menggunakan operator reduce sangat membantu. Operator ini mengidentifikasi 63 kesalahan berbeda yang berasal dari titik instrumentasi pelacakan yang sama dalam kode. reduce membantu untuk fokus pada pelacakan kesalahan bermakna lain dalam jendela waktu tersebut.
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
| Hitungan | Pola |
|---|---|
| 7125 | ExecuteAlgorithmMethod untuk metode 'RunCycleFromInterimData' telah gagal... |
| 7125 | Panggilan InferenceHostService gagal..System.NullReferenceException: Referensi objek tidak diatur ke instans dari objek... |
| 7124 | Kesalahan Sistem Inferensi Tidak Terduga..System.NullReferenceException: Referensi objek tidak diatur ke instans dari objek... |
| 5112 | Kesalahan Sistem Inferensi Tidak Terduga..System.NullReferenceException: Referensi objek tidak diatur ke instans dari objek... |
| 174 | Panggilan InferenceHostService gagal.. System.ServiceModel.CommunicationException: Terjadi kesalahan saat menulis ke pipa:... |
| 63 | Kesalahan Sistem Inferensi..Microsoft.Bing.Platform.Inferences.*: Write * to write to the Object BOSS.*: SocialGraph.BOSS.Reques... |
| 10 | ExecuteAlgorithmMethod untuk metode 'RunCycleFromInterimData' telah gagal... |
| 10 | Kesalahan Sistem Inferensi..Microsoft.Bing.Platform.Inferences.Service.Managers.UserInterimDataManagerException:... |
| 3 | Panggilan InferenceHostService gagal..System.ServiceModel.*: The object, System.ServiceModel.Channels.*+*, for ** is the *... at Syst... |
Sekarang, Anda telah mengetahui dengan baik kesalahan teratas yang berkontribusi pada anomali yang terdeteksi.
Untuk memahami efek kesalahan ini di seluruh sistem sampel, pertimbangkan bahwa:
- Tabel
Logsberisi data dimensi tambahan, sepertiComponentdanCluster. - Plugin autocluster yang baru dapat membantu memperoleh wawasan komponen dan kluster dengan kueri sederhana.
Dalam contoh berikut, Anda dapat dengan jelas melihat bahwa setiap empat kesalahan teratas bersifat khusus untuk sebuah komponen. Selain itu, meskipun tiga kesalahan teratas bersifat khusus untuk kluster DB4, kesalahan keempat terjadi di seluruh kluster.
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()
| Menghitung | Persentase (%) | Komponen | Kluster | Pesan |
|---|---|---|---|---|
| 7125 | 26.64 | InferenceHostService | DB4 | ExecuteAlgorithmMethod untuk metode... |
| 7125 | 26.64 | Komponen Tidak Diketahui | DB4 | Panggilan InferenceHostService gagal... |
| 7124 | 26.64 | InferenceAlgorithmExecutor | DB4 | Kesalahan Sistem Inferensi Tidak Terduga... |
| 5112 | 19.11 | InferenceAlgorithmExecutor | * | Kesalahan Sistem Inferensi Tidak Terduga... |
Memetakan nilai dari satu set ke yang lain
Kasus penggunaan kueri umum adalah pemetaan statis nilai. Pemetaan statis dapat membantu membuat hasil lebih rapi.
Misalnya, di tabel berikutnya, DeviceModel menentukan model perangkat. Menggunakan model perangkat bukan merupakan bentuk pereferensian nama perangkat yang mudah.
| DeviceModel | Menghitung |
|---|---|
| iPhone5,1 | 32 |
| iPhone3,2 | 432 |
| iPhone7,2 | 55 |
| iPhone5,2 | 66 |
Menggunakan nama yang mudah diingat lebih memudahkan:
| FriendlyName | Count |
|---|---|
| iPhone 5 | 32 |
| iPhone 4 | 432 |
| iPhone 6 | 55 |
| iPhone5 | 66 |
Dua contoh berikutnya menunjukkan cara beralih dari menggunakan model perangkat ke nama yang mudah diingat untuk mengidentifikasi sebuah perangkat.
Memetakan dengan menggunakan kamus dinamis
Anda dapat melakukan pemetaan dengan menggunakan kamus dinamis dan pengakses dinamis. Contohnya:
// 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
| FriendlyName | Count |
|---|---|
| iPhone 5 | 32 |
| iPhone 4 | 432 |
| iPhone 6 | 55 |
| iPhone5 | 66 |
Memetakan dengan menggunakan tabel statis
Anda juga dapat melakukan pemetaan dengan menggunakan tabel persisten dan operator join.
Membuat tabel pemetaan hanya sekali:
.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"]Membuat tabel konten perangkat:
DeviceModel FriendlyName iPhone5,1 iPhone 5 iPhone3,2 iPhone 4 iPhone7,2 iPhone 6 iPhone5,2 iPhone5 Membuat sumber tabel pengujian:
.create table Source (DeviceModel: string, Count: int) .ingest inline into table Source ["iPhone5,1",32]["iPhone3,2",432]["iPhone7,2",55]["iPhone5,2",66]Menggabungkan tabel dan menjalankan proyek:
Devices | join (Source) on DeviceModel | project FriendlyName, Count
Berikut output-nya:
| FriendlyName | Count |
|---|---|
| iPhone 5 | 32 |
| iPhone 4 | 432 |
| iPhone 6 | 55 |
| iPhone5 | 66 |
Membuat dan menggunakan tabel dimensi waktu kueri
Seringkali, Anda akan perlu menggabungkan hasil kueri dengan tabel dimensi ad-hoc yang tidak disimpan dalam database. Anda dapat menentukan ekspresi yang hasilnya adalah tabel yang dicakup ke satu kueri.
Contohnya:
// 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
Berikut adalah contoh yang sedikit lebih kompleks:
// 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
Mengambil rekaman terbaru (berdasarkan stempel waktu) per identitas
Misalkan Anda memiliki tabel yang meliputi:
- Kolom
IDyang mengidentifikasi entitas yang terkait dengan setiap baris, seperti ID pengguna atau ID node - Kolom
timestampyang memberikan referensi waktu untuk baris - Kolom lainnya
Anda dapat menggunakan operator bertumpuk teratas untuk membuat kueri yang mengembalikan dua rekaman terbaru untuk setiap nilai kolom ID, di mana terbaru ditentukan sebagai memiliki nilai tertinggi dari 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
Berikut adalah penjelasan langkah demi langkah dari kueri sebelumnya (penomoran mengacu pada angka dalam komentar kode):
datatableadalah cara untuk menghasilkan beberapa data pengujian untuk tujuan demonstrasi. Biasanya, Anda akan menggunakan data nyata di sini.- Baris ini pada dasarnya berarti mengembalikan semua nilai yang berbeda dari
id. - Baris ini kemudian mengembalikan dua rekaman teratas yang memaksimalkan:
- Kolom
timestamp - Kolom dari tingkat sebelumnya (di sini, hanya
id) - Kolom yang ditentukan pada tingkat ini (di sini,
timestamp)
- Kolom
- Baris ini menambahkan nilai dari kolom
blauntuk setiap rekaman yang dikembalikan oleh tingkat sebelumnya. Jika tabel memiliki kolom lain yang Anda minati, Anda dapat mengulangi baris ini untuk setiap kolom tersebut. - Baris terakhir menggunakan operator project-away untuk menghapus kolom "ekstra" yang diperkenalkan oleh
top-nested.
Memperluas tabel dengan persentase dari total perhitungan
Ekspresi tabular yang menyertakan kolom numerik lebih berguna bagi pengguna ketika disertai dengan nilainya sebagai persentase dari total.
Misalnya, asumsikan bahwa kueri menghasilkan tabel berikut:
| SomeSeries | SomeInt |
|---|---|
| Apple | 100 |
| Pisang | 200 |
Anda ingin menunjukkan tabel seperti ini:
| SomeSeries | SomeInt | Pct |
|---|---|---|
| Apple | 100 | 33.3 |
| Pisang | 200 | 66.6 |
Untuk mengubah tampilan tabel, hitung total (jumlah) kolom SomeInt, lalu bagi setiap nilai dari kolom ini dengan totalnya. Untuk hasil arbitrer, gunakan sebagai operator.
Contohnya:
// 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)
Melakukan agregasi melalui jendela geser
Contoh berikut menunjukkan cara meringkas kolom dengan menggunakan jendela geser. Untuk kueri, gunakan tabel berikut, yang berisi harga buah berdasarkan stempel waktu.
Hitung biaya minimum, maksimum, dan jumlah setiap buah per hari dengan menggunakan jendela geser tujuh hari. Setiap rekaman dalam hasil yang ditetapkan mengagregasi tujuh hari sebelumnya, dan hasilnya berisi rekaman per hari dalam periode analisis.
Tabel buah:
| Tanda waktu | Buah | Harga |
|---|---|---|
| 2018-09-24 21:00:00.0000000 | Pisang | 3 |
| 2018-09-25 20:00:00.0000000 | Apel | 9 |
| 2018-09-26 03:00:00.0000000 | Pisang | 4 |
| 2018-09-27 10:00:00.0000000 | Plum | 8 |
| 2018-09-28 07:00:00.0000000 | Pisang | 6 |
| 2018-09-29 21:00:00.0000000 | Pisang | 8 |
| 2018-09-30 01:00:00.0000000 | Plum | 2 |
| 2018-10-01 05:00:00.0000000 | Pisang | 0 |
| 2018-10-02 02:00:00.0000000 | Pisang | 0 |
| 2018-10-03 13:00:00.0000000 | Plum | 4 |
| 2018-10-04 14:00:00.0000000 | Apel | 8 |
| 2018-10-05 05:00:00.0000000 | Pisang | 2 |
| 2018-10-06 08:00:00.0000000 | Plum | 8 |
| 2018-10-07 12:00:00.0000000 | Pisang | 0 |
Berikut adalah kueri agregasi jendela geser. Lihat penjelasannya setelah hasil kueri.
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
Berikut output-nya:
| Tanda waktu | Buah | min_Price | max_Price | sum_Price |
|---|---|---|---|---|
| 2018-10-01 00:00:00.0000000 | Apel | 9 | 9 | 9 |
| 2018-10-01 00:00:00.0000000 | Pisang | 0 | 8 | 18 |
| 2018-10-01 00:00:00.0000000 | Plum | 2 | 8 | 10 |
| 2018-10-02 00:00:00.0000000 | Pisang | 0 | 8 | 18 |
| 2018-10-02 00:00:00.0000000 | Plum | 2 | 8 | 10 |
| 2018-10-03 00:00:00.0000000 | Plum | 2 | 8 | 14 |
| 2018-10-03 00:00:00.0000000 | Pisang | 0 | 8 | 14 |
| 2018-10-04 00:00:00.0000000 | Pisang | 0 | 8 | 14 |
| 2018-10-04 00:00:00.0000000 | Plum | 2 | 4 | 6 |
| 2018-10-04 00:00:00.0000000 | Apel | 8 | 8 | 8 |
| 2018-10-05 00:00:00.0000000 | Pisang | 0 | 8 | 10 |
| 2018-10-05 00:00:00.0000000 | Plum | 2 | 4 | 6 |
| 2018-10-05 00:00:00.0000000 | Apel | 8 | 8 | 8 |
| 2018-10-06 00:00:00.0000000 | Plum | 2 | 8 | 14 |
| 2018-10-06 00:00:00.0000000 | Pisang | 0 | 2 | 2 |
| 2018-10-06 00:00:00.0000000 | Apel | 8 | 8 | 8 |
| 2018-10-07 00:00:00.0000000 | Pisang | 0 | 2 | 2 |
| 2018-10-07 00:00:00.0000000 | Plum | 4 | 8 | 12 |
| 2018-10-07 00:00:00.0000000 | Apel | 8 | 8 | 8 |
Kueri "merenggangkan" (menduplikasi) setiap rekaman dalam tabel input selama tujuh hari setelah kemunculannya sebenarnya. Setiap rekaman benar-benar muncul tujuh kali. Akibatnya, agregasi harian mencakup semua rekaman dari tujuh hari sebelumnya.
Berikut adalah penjelasan langkah demi langkah dari kueri sebelumnya:
- Bin setiap rekaman ke satu hari (relatif terhadap
_start). - Tentukan akhir rentang per rekaman:
_bin + 7d, kecuali nilainya berada di luar rentang_startdan_end, dalam hal ini, nilai tersebut telah disesuaikan. - Untuk setiap rekaman, buat array tujuh hari (stempel waktu), mulai dari hari rekaman saat ini.
mv-expandarray, sehingga menduplikasi setiap rekaman ke tujuh hari, berbeda satu hari dengan satu sama lain.- Lakukan fungsi agregasi untuk setiap hari. Karena #4, langkah ini benar-benar meringkas tujuh hari terakhir.
- Data untuk tujuh hari pertama tidak lengkap karena tidak ada periode lookback tujuh hari selama tujuh hari pertama. Tujuh hari pertama dikecualikan dari hasil akhir. Dalam contoh ini, tujuh hari pertama hanya berpartisipasi dalam agregasi untuk 2018-10-01.
Menemukan peristiwa sebelumnya
Contoh berikutnya menunjukkan cara menemukan peristiwa sebelumnya antara dua himpunan data.
Anda memiliki dua himpunan data, A dan B. Untuk setiap rekaman dalam himpunan data B, temukan peristiwa sebelumnya dalam himpunan data A (yaitu, rekaman arg_max dalam A yang masih lebih lama dari B).
Berikut adalah contoh himpunan data:
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
| Tanda waktu | ID | EventB |
|---|---|---|
| 2019-01-01 00:00:00.0000000 | x | Ax1 |
| 2019-01-01 00:00:00.0000000 | z | Az1 |
| 2019-01-01 00:00:01.0000000 | x | Ax2 |
| 2019-01-01 00:00:02.0000000 | y | Ay1 |
| 2019-01-01 00:00:05.0000000 | y | Ay2 |
| Tanda waktu | ID | EventA |
|---|---|---|
| 2019-01-01 00:00:03.0000000 | x | B |
| 2019-01-01 00:00:04.0000000 | x | B |
| 2019-01-01 00:00:04.0000000 | y | B |
| 2019-01-01 00:02:00.0000000 | z | B |
Output yang diharapkan:
| ID | Tanda waktu | EventB | A_Timestamp | EventA |
|---|---|---|---|---|
| x | 2019-01-01 00:00:03.0000000 | B | 2019-01-01 00:00:01.0000000 | Ax2 |
| x | 2019-01-01 00:00:04.0000000 | B | 2019-01-01 00:00:01.0000000 | Ax2 |
| y | 2019-01-01 00:00:04.0000000 | B | 2019-01-01 00:00:02.0000000 | Ay1 |
| z | 2019-01-01 00:02:00.0000000 | B | 2019-01-01 00:00:00.0000000 | Az1 |
Kami merekomendasikan dua pendekatan berbeda untuk masalah ini. Anda dapat menguji keduanya pada himpunan data spesifik Anda untuk menemukan yang paling cocok untuk skenario Anda.
Catatan
Setiap pendekatan mungkin berjalan secara berbeda pada himpunan data yang berbeda.
Pendekatan 1
Pendekatan ini membuat serial himpunan data berdasarkan ID dan stempel waktu. Kemudian, mengelompokkan semua peristiwa dalam himpunan data B dengan semua peristiwa sebelumnya dalam himpunan data A. Terakhir, memilih arg_max dari semua peristiwa dalam himpunan data A dalam grup.
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
Pendekatan 2
Pendekatan untuk memecahkan masalah ini membutuhkan periode lookback maksimum. Pendekatan ini melihat seberapa lama rekaman dalam himpunan data A dibandingkan dengan himpunan data B. Metode ini kemudian menggabungkan dua himpunan data berdasarkan ID dan periode lookback ini.
join menghasilkan semua kandidat yang memungkinkan, semua rekaman himpunan data A yang lebih lama dari rekaman dalam himpunan data B dan dalam periode lookback. Kemudian, yang paling dekat dengan himpunan data B difilter oleh arg_min (TimestampB - TimestampA). Semakin pendek periode lookback, semakin baik hasil kuerinya.
Dalam contoh berikut, periode lookback diatur ke 1m. Rekaman dengan ID z tidak memiliki peristiwa A yang sesuai karena peristiwa A-nya lebih lama dua menit.
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
| ID | B_Timestamp | A_Timestamp | EventB | EventA |
|---|---|---|---|---|
| x | 2019-01-01 00:00:03.0000000 | 2019-01-01 00:00:01.0000000 | B | Ax2 |
| x | 2019-01-01 00:00:04.0000000 | 2019-01-01 00:00:01.0000000 | B | Ax2 |
| y | 2019-01-01 00:00:04.0000000 | 2019-01-01 00:00:02.0000000 | B | Ay1 |
| z | 2019-01-01 00:02:00.0000000 | B |
Langkah berikutnya
- Pelajari tutorial tentang Bahasa Kueri Kusto.
Artikel ini mengidentifikasi kebutuhan kueri umum di Azure Monitor dan cara menggunakan Bahasa Kueri Kusto untuk memenuhinya.
Operasi string
Bagian berikut memberikan contoh cara bekerja dengan string saat menggunakan Bahasa Kueri Kusto.
String dan cara melakukan escape
Nilai string dibungkus dengan tanda kutip tunggal atau ganda. Tambahkan garis miring terbalik (\) di sebelah kiri karakter untuk melakukan escape pada karakter: \t untuk tab, \n untuk baris baru, \' untuk karakter kutipan tunggal, dan \" untuk karakter kutipan ganda.
print "this is a 'string' literal in double \" quotes"
print 'this is a "string" literal in single \' quotes'
Untuk mencegah "\" bertindak sebagai karakter escape, tambahkan "@" sebagai prefiks ke string:
print @"C:\backslash\not\escaped\with @ prefix"
Perbandingan string
| Operator | Deskripsi | Peka huruf besar/kecil | Contoh (menangguhkan true) |
|---|---|---|---|
== |
Sama | Ya | "aBc" == "aBc" |
!= |
Tidak sama | Ya | "abc" != "ABC" |
=~ |
Sama | Tidak | "abc" =~ "ABC" |
!~ |
Tidak sama | Tidak | "aBc" !~ "xyz" |
has |
Nilai sisi kanan adalah seluruh istilah dalam nilai sisi kiri | Tidak | "North America" has "america" |
!has |
Nilai sisi kanan bukanlah seluruh istilah dalam nilai sisi kiri | Tidak | "North America" !has "amer" |
has_cs |
Nilai sisi kanan adalah seluruh istilah dalam nilai sisi kiri | Ya | "North America" has_cs "America" |
!has_cs |
Nilai sisi kanan bukanlah seluruh istilah dalam nilai sisi kiri | Ya | "North America" !has_cs "amer" |
hasprefix |
Nilai sisi kanan adalah prefiks istilah dalam nilai sisi kiri | Tidak | "North America" hasprefix "ame" |
!hasprefix |
Nilai sisi kanan bukanlah prefiks istilah dalam nilai sisi kiri | Tidak | "North America" !hasprefix "mer" |
hasprefix_cs |
Nilai sisi kanan adalah prefiks istilah dalam nilai sisi kiri | Ya | "North America" hasprefix_cs "Ame" |
!hasprefix_cs |
Nilai sisi kanan bukanlah prefiks istilah dalam nilai sisi kiri | Ya | "North America" !hasprefix_cs "CA" |
hassuffix |
Nilai sisi kanan adalah sufiks istilah dalam nilai sisi kiri | Tidak | "North America" hassuffix "ica" |
!hassuffix |
Nilai sisi kanan bukanlah sufiks istilah dalam nilai sisi kiri | Tidak | "North America" !hassuffix "americ" |
hassuffix_cs |
Nilai sisi kanan adalah sufiks istilah dalam nilai sisi kiri | Ya | "North America" hassuffix_cs "ica" |
!hassuffix_cs |
Nilai sisi kanan bukanlah sufiks istilah dalam nilai sisi kiri | Ya | "North America" !hassuffix_cs "icA" |
contains |
Nilai sisi kanan muncul sebagai subsequence nilai sisi kiri | Tidak | "FabriKam" contains "BRik" |
!contains |
Nilai sisi kanan tidak muncul dalam nilai sisi kiri | Tidak | "Fabrikam" !contains "xyz" |
contains_cs |
Nilai sisi kanan muncul sebagai subsequence nilai sisi kiri | Ya | "FabriKam" contains_cs "Kam" |
!contains_cs |
Nilai sisi kanan tidak muncul dalam nilai sisi kiri | Ya | "Fabrikam" !contains_cs "Kam" |
startswith |
Nilai sisi kanan adalah subsequence awal dari nilai sisi kiri | Tidak | "Fabrikam" startswith "fab" |
!startswith |
Nilai sisi kanan bukanlah subsequence awal dari nilai sisi kiri | Tidak | "Fabrikam" !startswith "kam" |
startswith_cs |
Nilai sisi kanan adalah subsequence awal dari nilai sisi kiri | Ya | "Fabrikam" startswith_cs "Fab" |
!startswith_cs |
Nilai sisi kanan bukanlah subsequence awal dari nilai sisi kiri | Ya | "Fabrikam" !startswith_cs "fab" |
endswith |
Nilai sisi kanan adalah subsequence penutup dari nilai sisi kiri | Tidak | "Fabrikam" endswith "Kam" |
!endswith |
Nilai sisi kanan bukanlah subsequence penutup dari nilai sisi kiri | Tidak | "Fabrikam" !endswith "brik" |
endswith_cs |
Nilai sisi kanan adalah subsequence penutup dari nilai sisi kiri | Ya | "Fabrikam" endswith "Kam" |
!endswith_cs |
Nilai sisi kanan bukanlah subsequence penutup dari nilai sisi kiri | Ya | "Fabrikam" !endswith "brik" |
matches regex |
Nilai sisi kiri berisi kecocokan untuk nilai sisi kanan | Ya | "Fabrikam" matches regex "b.*k" |
in |
Sama dengan salah satu elemen | Ya | "abc" in ("123", "345", "abc") |
!in |
Tidak sama dengan satu pun elemen | Ya | "bca" !in ("123", "345", "abc") |
countof
Menghitung kemunculan substring dalam string. Dapat mencocokkan string biasa atau menggunakan ekspresi reguler (regex). Kecocokan string biasa mungkin tumpang tindih, tetapi kecocokan regex tidak tumpang tindih.
countof(text, search [, kind])
text: String inputsearch: String biasa atau regex yang akan dicocokkan dalam tekskind: normal | regex (default: normal).
Mengembalikan jumlah string pencarian dapat dicocokkan dalam kontainer. Kecocokan string biasa mungkin tumpang tindih, tetapi kecocokan regex tidak tumpang tindih.
Kecocokan string biasa
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
Kecocokan regex
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
ekstrak
Mendapatkan kecocokan untuk ekspresi reguler dari string tertentu. Secara opsional, dapat mengonversi substring yang diekstrak ke jenis yang ditentukan.
extract(regex, captureGroup, text [, typeLiteral])
regex: Ekspresi reguler.captureGroup: Konstanta bilangan bulat positif yang menunjukkan grup pengambilan yang akan diekstrak. Gunakan 0 untuk keseluruhan pencocokan, 1 untuk nilai yang dicocokkan dengan kurung pertama () dalam ekspresi reguler, dan 2 atau lebih untuk kurung berikutnya.text- String yang akan dicari.typeLiteral- Jenis literal opsional (misalnya,typeof(long)). Jika disediakan, substring yang diekstraksi dikonversi ke jenis ini.
Mengembalikan substring yang dicocokkan dengan captureGroup grup pengambil yang ditunjukkan, secara opsional dikonversi ke typeLiteral. Jika tidak ada kecocokan atau konversi jenis gagal, null akan dikembalikan.
Contoh berikut mengekstrak oktet terakhir ComputerIP dari rekaman heartbeat:
Heartbeat
| where ComputerIP != ""
| take 1
| project ComputerIP, last_octet=extract("([0-9]*$)", 1, ComputerIP)
Contoh berikut mengekstrak oktet terakhir, konversikan secara eksplisit ke jenis nyata (angka), lalu menghitung nilai IP berikutnya:
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
Dalam contoh berikutnya, string Trace dicari untuk definisi Duration. Kecocokan dikonversi secara eksplisit ke real dan dikalikan dengan konstanta waktu (1 detik), yang kemudian mengonversi Duration secara eksplisit ke jenis 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, notempty
isemptymengembalikantruejika argumen adalah string kosong atau null (lihatisnull).isnotemptymengembalikantruejika argumen bukan string kosong atau null (lihatisnotnull). Alias:notempty.
isempty(value)
isnotempty(value)
Contoh
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
parseurl
Membagi URL ke bagian-bagiannya, seperti protokol, host, dan port, lalu mengembalikan objek kamus yang berisi bagian-bagian tersebut sebagai string.
parseurl(urlstring)
Contoh
print parseurl("http://user:pass@contoso.com/icecream/buy.aspx?a=1&b=2#tag")
Berikut output-nya:
{
"Scheme" : "http",
"Host" : "contoso.com",
"Port" : "80",
"Path" : "/icecream/buy.aspx",
"Username" : "user",
"Password" : "pass",
"Query Parameters" : {"a":"1","b":"2"},
"Fragment" : "tag"
}
replace
Mengganti semua kecocokan regex dengan string lain.
replace(regex, rewrite, input_text)
regex: Ekspresi reguler yang akan dicocokkan. Ini dapat berisi grup pengambil dalam tanda kurung ().rewrite: Regex pengganti untuk kecocokan apa pun yang dibuat dengan mencocokkan regex. Gunakan \0 untuk merujuk ke seluruh kecocokan, \1 untuk grup pengambil pertama, \2, dan seterusnya, untuk grup pengambil berikutnya.input_text: String input yang akan dicari.
Mengembalikan teks setelah mengganti semua kecocokan regex dengan evaluasi penulisan ulang. Kecocokan tidak tumpang tindih.
Contoh
SecurityEvent
| take 1
| project Activity
| extend replaced = replace(@"(\d+) -", @"Activity ID \1: ", Activity)
Berikut output-nya:
| Aktivitas | Diganti |
|---|---|
| 4663 - Upaya telah dilakukan untuk mengakses objek | ID Aktivitas 4663: Upaya telah dilakukan untuk mengakses objek. |
split
Memisahkan string tertentu sesuai dengan pemisah yang ditentukan, lalu mengembalikan array dari substring yang dihasilkan.
split(source, delimiter [, requestedIndex])
source: String yang akan dipisah sesuai dengan pemisah yang ditentukan.delimiter: Pemisah yang akan digunakan untuk memisah string sumber.requestedIndex: Indeks berbasis nol opsional. Jika disediakan, array string yang dikembalikan hanya menyimpan item tersebut (jika ada).
Contoh
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"]
strcat
Menggabungkan argumen string (mendukung 1-16 argumen).
strcat("string1", "string2", "string3")
Contoh
print strcat("hello", " ", "world") // result: "hello world"
strlen
Mengembalikan panjang string.
strlen("text_to_evaluate")
Contoh
print strlen("hello") // result: 5
substring
Mengekstrak substring dari string sumber tertentu, mulai dari indeks yang ditentukan. Secara opsional, Anda dapat menentukan panjang substring yang diminta.
substring(source, startingIndex [, length])
source: String sumber tempat substring akan diambil.startingIndexstartingIndex: Posisi karakter awal berbasis nol dari substring yang diminta.length: Parameter opsional yang dapat digunakan untuk menentukan panjang yang diminta dari substring yang dikembalikan.
Contoh
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, toupper
Mengonversi string tertentu ke semua huruf kecil atau semua huruf besar.
tolower("value")
toupper("value")
Contoh
print tolower("HELLO"); // result: "hello"
print toupper("hello"); // result: "HELLO"
Operasi tanggal dan waktu
Bagian berikut memberikan contoh cara bekerja dengan nilai tanggal dan waktu saat menggunakan Bahasa Kueri Kusto.
Dasar-dasar tanggal waktu
Bahasa Kueri Kusto memiliki dua jenis data utama yang terkait dengan tanggal dan waktu: datetime dan timespan. Semua tanggal dinyatakan dalam UTC. Meskipun beberapa format tanggal waktu didukung, format ISO-8601 lebih diutamakan.
Rentang waktu dinyatakan sebagai desimal yang diikuti oleh unit waktu:
| Pintasan | Unit waktu |
|---|---|
| d | hari |
| h | jam |
| m | menit |
| s | detik |
| ms | milidetik |
| mikrodetik | mikrodetik |
| detik | nanodetik |
Anda dapat membuat nilai tanggal-waktu dengan mengonversi string secara eksplisit menggunakan operator todatetime. Misalnya, untuk meninjau heartbeat mesin virtual yang dikirim dalam rentang waktu tertentu, gunakan operator between untuk menentukan rentang waktu:
Heartbeat
| where TimeGenerated between(datetime("2018-06-30 22:46:42") .. datetime("2018-07-01 00:57:27"))
Skenario umum lainnya adalah membandingkan nilai tanggal-waktu dengan saat ini. Misalnya, untuk melihat semua heartbeat selama dua menit terakhir, Anda dapat menggunakan operator now bersama dengan rentang waktu yang mewakili dua menit:
Heartbeat
| where TimeGenerated > now() - 2m
Pintasan juga tersedia untuk fungsi ini:
Heartbeat
| where TimeGenerated > now(-2m)
Metode tersingkat dan paling mudah dibaca adalah menggunakan operator ago:
Heartbeat
| where TimeGenerated > ago(2m)
Misalkan Anda tidak mengetahui waktu mulai dan berakhirnya, tetapi mengetahui waktu mulai dan durasinya. Anda dapat menulis ulang kueri:
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
Mengonversi unit waktu
Anda dapat mengekspresikan nilai tanggal-waktu atau rentang waktu dalam unit waktu selain default. Misalnya, jika Anda meninjau peristiwa kesalahan dari 30 menit terakhir dan memerlukan kolom terhitung yang menunjukkan berapa lama peristiwa itu terjadi, Anda dapat menggunakan kueri ini:
Event
| where TimeGenerated > ago(30m)
| where EventLevelName == "Error"
| extend timeAgo = now() - TimeGenerated
Kolom timeAgo menyimpan nilai seperti 00:09:31.5118992, yang diformat sebagai hh:mm:ss.fffffff. Jika Anda ingin memformat nilai-nilai ini ke number menit sejak waktu mulai, bagi nilai tersebut dengan 1m:
Event
| where TimeGenerated > ago(30m)
| where EventLevelName == "Error"
| extend timeAgo = now() - TimeGenerated
| extend timeAgoMinutes = timeAgo/1m
Agregasi dan bucketing berdasarkan interval waktu
Skenario umum lainnya adalah kebutuhan untuk mendapatkan statistik dari jangka waktu tertentu dalam unit waktu tertentu. Untuk skenario ini, Anda dapat menggunakan operator bin sebagai bagian dari klausul summarize.
Gunakan kueri berikut untuk mendapatkan jumlah peristiwa yang terjadi setiap lima menit selama setengah jam terakhir:
Event
| where TimeGenerated > ago(30m)
| summarize events_count=count() by bin(TimeGenerated, 5m)
Kueri ini menghasilkan tabel berikut:
| TimeGenerated(UTC) | events_count |
|---|---|
| 2018-08-01T09:30:00 | 54 |
| 2018-08-01T09:35:00 | 41 |
| 2018-08-01T09:40:00 | 42 |
| 2018-08-01T09:45:00 | 41 |
| 2018-08-01T09:50:00 | 41 |
| 2018-08-01T09:55:00 | 16 |
Cara lain untuk membuat wadah hasil adalah dengan menggunakan fungsi seperti startofday:
Event
| where TimeGenerated > ago(4d)
| summarize events_count=count() by startofday(TimeGenerated)
Berikut output-nya:
| rentang waktu | count_ |
|---|---|
| 2018-07-28T00:00:00 | 7,136 |
| 2018-07-29T00:00:00 | 12,315 |
| 2018-07-30T00:00:00 | 16,847 |
| 2018-07-31T00:00:00 | 12,616 |
| 2018-08-01T00:00:00 | 5,416 |
Zona waktu
Karena semua nilai tanggal waktu dinyatakan dalam UTC, sebaiknya ubah nilai-nilai ini ke zona waktu lokal. Misalnya, gunakan perhitungan ini untuk mengonversi waktu UTC ke PST:
Event
| extend localTimestamp = TimeGenerated - 8h
Agregasi
Bagian berikut memberikan contoh cara mengagregasi hasil kueri saat menggunakan Bahasa Kueri Kusto.
hitung
Hitung jumlah baris dalam tataan hasil setelah filter diterapkan. Contoh berikut mengembalikan jumlah total baris dalam tabel Perf dari 30 menit terakhir. Hasilnya dikembalikan dalam kolom bernama count_ kecuali Anda menetapkan nama tertentu ke kolom tersebut:
Perf
| where TimeGenerated > ago(30m)
| summarize count()
Perf
| where TimeGenerated > ago(30m)
| summarize num_of_records=count()
Visualisasi bagan waktu mungkin berguna untuk melihat tren dari waktu ke waktu:
Perf
| where TimeGenerated > ago(30m)
| summarize count() by bin(TimeGenerated, 5m)
| render timechart
Output dari contoh ini menunjukkan baris tren jumlah rekaman Perf dalam interval lima menit:
dcount, dcountif
Gunakan dcount dan dcountif untuk menghitung nilai berbeda dalam kolom tertentu. Kueri berikut mengevaluasi berapa banyak komputer berbeda yang mengirim heartbeat dalam satu jam terakhir:
Heartbeat
| where TimeGenerated > ago(1h)
| summarize dcount(Computer)
Untuk hanya menghitung komputer Linux yang mengirim heartbeat, gunakan dcountif:
Heartbeat
| where TimeGenerated > ago(1h)
| summarize dcountif(Computer, OSType=="Linux")
Mengevaluasi subgrup
Untuk melakukan penghitungan atau agregasi lain pada subgrup dalam data Anda, gunakan kata kunci by. Misalnya, untuk menghitung jumlah komputer Linux berbeda yang mengirim heartbeat di setiap negara atau wilayah, gunakan kueri ini:
Heartbeat
| where TimeGenerated > ago(1h)
| summarize distinct_computers=dcountif(Computer, OSType=="Linux") by RemoteIPCountry
| RemoteIPCountry | distinct_computers |
|---|---|
| Amerika Serikat | 19 |
| Kanada | 3 |
| Irlandia | 0 |
| Inggris Raya | 0 |
| Belanda | 2 |
Untuk menganalisis subgrup data Anda yang lebih kecil, tambahkan nama kolom ke bagian by tersebut. Misalnya, Anda mungkin ingin menghitung komputer berbeda dari setiap negara atau wilayah per jenis sistem operasi (OSType):
Heartbeat
| where TimeGenerated > ago(1h)
| summarize distinct_computers=dcountif(Computer, OSType=="Linux") by RemoteIPCountry, OSType
Persentil
Untuk menemukan nilai median, gunakan fungsi percentile dengan nilai untuk menentukan persentil:
Perf
| where TimeGenerated > ago(30m)
| where CounterName == "% Processor Time" and InstanceName == "_Total"
| summarize percentiles(CounterValue, 50) by Computer
Anda juga dapat menentukan persentil lain untuk mendapatkan hasil agregat dari setiap pencarian:
Perf
| where TimeGenerated > ago(30m)
| where CounterName == "% Processor Time" and InstanceName == "_Total"
| summarize percentiles(CounterValue, 25, 50, 75, 90) by Computer
Hasilnya mungkin menunjukkan bahwa beberapa CPU komputer memiliki nilai median yang sama. Namun, meskipun beberapa komputer menampilkan median yang sama, komputer yang lain melaporkan nilai CPU yang jauh lebih rendah dan lebih tinggi. Nilai tinggi dan rendah berarti bahwa komputer telah mengalami lonjakan.
Varian
Untuk mengevaluasi varians nilai secara langsung, gunakan metode varians dan simpangan baku:
Perf
| where TimeGenerated > ago(30m)
| where CounterName == "% Processor Time" and InstanceName == "_Total"
| summarize stdev(CounterValue), variance(CounterValue) by Computer
Cara yang teapt untuk menganalisis stabilitas penggunaan CPU adalah dengan menggabungkan stdev dengan perhitungan median:
Perf
| where TimeGenerated > ago(130m)
| where CounterName == "% Processor Time" and InstanceName == "_Total"
| summarize stdev(CounterValue), percentiles(CounterValue, 50) by Computer
Membuat daftar dan set
Anda dapat menggunakan makelist untuk mem-pivot data berdasarkan urutan nilai di kolom tertentu. Misalnya, Anda mungkin ingin mempelajari peristiwa pengurutan paling umum yang terjadi di komputer Anda. Anda pada dasarnya dapat memutar data berdasarkan urutan nilai EventID di setiap komputer:
Event
| where TimeGenerated > ago(12h)
| order by TimeGenerated desc
| summarize makelist(EventID) by Computer
Berikut output-nya:
| Komputer | list_EventID |
|---|---|
| computer1 | [704,701,1501,1500,1085,704,704,701] |
| computer2 | [326,105,302,301,300,102] |
| ... | ... |
makelist menghasilkan daftar dalam urutan data yang diteruskan ke dalamnya. Untuk mengurutkan peristiwa dari yang terlama ke terbaru, gunakan asc dalam pernyataan order, bukan desc.
Anda dapat membuat daftar dari nilai yang berbeda saja. Data ini disebut set, dan Anda dapat membuatnya dengan menggunakan perintah makeset:
Event
| where TimeGenerated > ago(12h)
| order by TimeGenerated desc
| summarize makeset(EventID) by Computer
Berikut output-nya:
| Komputer | list_EventID |
|---|---|
| computer1 | [704,701,1501,1500,1085] |
| computer2 | [326,105,302,301,300,102] |
| ... | ... |
Seperti halnya makelist, makeset juga dapat digunakan dengan data yang diurutkan. Perintah makeset menghasilkan array berdasarkan urutan baris yang diteruskan ke dalamnya.
Memperluas daftar
Operasi terbalik dari makelist atau makeset adalah mv-expand. Perintah mv-expand memperluas daftar nilai ke baris terpisah. Perintah ini dapat diperluas di sejumlah kolom dinamis, termasuk JSON dan kolom array. Misalnya, Anda dapat memeriksa tabel Heartbeat untuk solusi yang mengirim data dari komputer yang mengirim heartbeat dalam satu jam terakhir:
Heartbeat
| where TimeGenerated > ago(1h)
| project Computer, Solutions
Berikut output-nya:
| Komputer | Solusi |
|---|---|
| computer1 | "security", "updates", "changeTracking" |
| computer2 | "security", "updates" |
| computer3 | "antiMalware", "changeTracking" |
| ... | ... |
Gunakan mv-expand untuk menampilkan setiap nilai dalam baris terpisah, bukan dalam daftar yang dipisahkan koma:
Heartbeat
| where TimeGenerated > ago(1h)
| project Computer, split(Solutions, ",")
| mv-expand Solutions
Berikut output-nya:
| Komputer | Solusi |
|---|---|
| computer1 | "security" |
| computer1 | "updates" |
| computer1 | "changeTracking" |
| computer2 | "security" |
| computer2 | "updates" |
| computer3 | "antiMalware" |
| computer3 | "changeTracking" |
| ... | ... |
Anda dapat menggunakan makelist untuk mengelompokkan item. Dalam output, Anda dapat melihat daftar komputer per solusi:
Heartbeat
| where TimeGenerated > ago(1h)
| project Computer, split(Solutions, ",")
| mv-expand Solutions
| summarize makelist(Computer) by tostring(Solutions)
Berikut output-nya:
| Solusi | list_Computer |
|---|---|
| "security" | ["computer1", "computer2"] |
| "updates" | ["computer1", "computer2"] |
| "changeTracking" | ["computer1", "computer3"] |
| "antiMalware" | ["computer3"] |
| ... | ... |
Bin yang hilang
Aplikasi mv-expand yang berguna mengisi nilai default untuk bin yang hilang. Misalnya, misalkan Anda mencari waktu aktif komputer tertentu dengan menjelajahi heartbeat-nya. Anda dapat melihat sumber heartbeat, yang ada dalam kolom Category. Biasanya, kami akan menggunakan pernyataan summarize dasar:
Heartbeat
| where TimeGenerated > ago(12h)
| summarize count() by Category, bin(TimeGenerated, 1h)
Berikut output-nya:
| Kategori | TimeGenerated | count_ |
|---|---|---|
| Agen Langsung | 2017-06-06T17:00:00Z | 15 |
| Agen Langsung | 2017-06-06T18:00:00Z | 60 |
| Agen Langsung | 2017-06-06T20:00:00Z | 55 |
| Agen Langsung | 2017-06-06T21:00:00Z | 57 |
| Agen Langsung | 2017-06-06T22:00:00Z | 60 |
| ... | ... | ... |
Dalam output, wadah yang terkait dengan "2017-06-06T19:00:00Z" hilang karena tidak ada data heartbeat untuk jam itu. Gunakan fungsi make-series untuk menetapkan nilai default ke wadah kosong. Sebuah baris dibuat untuk setiap kategori. Output meliputi dua kolom array tambahan, satu untuk nilai dan satu untuk wadah waktu yang cocok:
Heartbeat
| make-series count() default=0 on TimeGenerated in range(ago(1d), now(), 1h) by Category
Berikut output-nya:
| Kategori | count_ | TimeGenerated |
|---|---|---|
| Agen Langsung | [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",...] |
| ... | ... | ... |
Elemen ketiga dari array count_ adalah 0, seperti yang diharapkan. Array TimeGenerated memiliki stempel waktu yang cocok dari "2017-06-06T19:00:00.0000000Z". Tetapi, format array ini sulit untuk dibaca. Gunakan mv-expand untuk memperluas array dan menghasilkan output format yang sama seperti yang dihasilkan oleh 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_
Berikut output-nya:
| Kategori | TimeGenerated | count_ |
|---|---|---|
| Agen Langsung | 2017-06-06T17:00:00Z | 15 |
| Agen Langsung | 2017-06-06T18:00:00Z | 60 |
| Agen Langsung | 2017-06-06T19:00:00Z | 0 |
| Agen Langsung | 2017-06-06T20:00:00Z | 55 |
| Agen Langsung | 2017-06-06T21:00:00Z | 57 |
| Agen Langsung | 2017-06-06T22:00:00Z | 60 |
| ... | ... | ... |
Persempit hasil ke satu set elemen: let, makeset, toscalar, in
Skenario umum adalah memilih nama entitas tertentu berdasarkan kumpulan kriteria, lalu memfilter himpunan data berbeda ke kumpulan entitas tersebut. Misalnya, Anda mungkin menemukan komputer yang diketahui tidak memiliki pembaruan, lalu mengidentifikasi alamat IP yang dipanggil komputer ini.
Berikut contohnya:
let ComputersNeedingUpdate = toscalar(
Update
| summarize makeset(Computer)
| project set_Computer
);
WindowsFirewall
| where Computer in (ComputersNeedingUpdate)
Gabungan
Anda dapat menggunakan gabung untuk menganalisis data dari beberapa tabel dalam kueri yang sama. Gabung menggabungkan baris dari dua himpunan data dengan mencocokkan nilai kolom tertentu.
Berikut contohnya:
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
Dalam contoh ini, filter himpunan data pertama untuk semua peristiwa masuk. Himpunan data tersebut digabungkan dengan himpunan data kedua yang memfilter semua peristiwa keluar. Kolom yang diproyeksikan adalah Computer, Account, TargetLogonId, dan TimeGenerated. Himpunan data ini berkorelasi dengan kolom bersama, TargetLogonId. Output-nya adalah rekaman tunggal per korelasi yang memiliki waktu masuk dan keluar.
Jika kedua himpunan data memiliki kolom dengan nama yang sama, kolom himpunan data sebelah kanan akan diberi nomor indeks. Dalam contoh ini, hasilnya akan menampilkan TargetLogonId dengan nilai dari tabel sebelah kiri dan TargetLogonId1 dengan nilai dari tabel sebelah kanan. Dalam hal ini, kolom TargetLogonId1 kedua telah dihapus dengan menggunakan operator project-away.
Catatan
Untuk meningkatkan performa, simpan kolom yang relevan saja dari himpunan data yang digabungkan dengan menggunakan operator project.
Gunakan sintaks berikut untuk menggabungkan dua himpunan data di mana kunci yang digabungkan memiliki nama yang berbeda antara dua tabel:
Table1
| join ( Table2 )
on $left.key1 == $right.key2
Tabel pencarian
Penggunaan umum gabung adalah menggunakan datatable untuk pemetaan nilai statis. Menggunakan datatable dapat membantu membuat hasil lebih rapi. Misalnya, Anda dapat memperkaya data peristiwa keamanan dengan nama peristiwa untuk setiap ID peristiwa:
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
Berikut output-nya:
| eventName | count_ |
|---|---|
| Handel ke suatu objek telah ditutup | 290,995 |
| Handel ke suatu objek telah diminta | 154,157 |
| Upaya telah dilakukan untuk menduplikasi handel ke suatu objek | 144,305 |
| Upaya telah dilakukan untuk mengakses suatu objek | 123,669 |
| Operasi kriptografi | 153,495 |
| Operasi file kunci | 153,496 |
JSON dan struktur data
Objek bertumpuk adalah objek yang berisi objek lain dalam array atau dalam peta pasangan kunci-nilai. Objek direpresentasikan sebagai string JSON. Bagian ini menjelaskan cara menggunakan JSON untuk mengambil data dan menganalisis objek bertumpuk.
Bekerja dengan string JSON
Gunakan extractjson untuk mengakses elemen JSON tertentu dalam jalur yang diketahui. Fungsi ini memerlukan ekspresi jalur yang menggunakan konvensi berikut:
- Gunakan $ untuk merujuk ke folder akar.
- Gunakan notasi tanda kurung atau titik untuk merujuk pada indeks dan elemen sebagaimana diilustrasikan dalam contoh berikut.
Gunakan tanda kurung untuk indeks dan titik untuk memisahkan elemen:
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)
Contoh ini serupa, tetapi hanya menggunakan notasi tanda kurung:
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)
Untuk satu elemen saja, Anda hanya dapat menggunakan notasi titik:
let hosts_report=dynamic({"location":"North_DC", "status":"running", "rate":5});
print hosts_report
| extend status = hosts_report.status
parsejson
Paling mudah untuk mengakses beberapa elemen dalam struktur JSON Anda sebagai objek dinamis. Gunakan parsejson untuk mengonversi data teks secara eksplisit ke objek dinamis. Setelah mengonversi JSON ke jenis dinamis, Anda dapat menggunakan fungsi tambahan untuk menganalisis 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
arraylength
Gunakan arraylength untuk menghitung jumlah elemen dalam 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-expand
Gunakan mv-expand untuk memisah properti objek ke baris terpisah:
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]
buildschema
Gunakan buildschema untuk mendapatkan skema yang menerima semua nilai dari objek:
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)
Hasilnya adalah skema dalam format JSON:
{
"hosts":
{
"indexer":
{
"location": "string",
"rate": "int",
"status": "string"
}
}
}
Skema tersebut menjelaskan nama bidang objek dan jenis data yang cocok.
Objek bertumpuk mungkin memiliki skema yang berbeda, seperti dalam contoh berikut:
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)
Diagram
Bagian berikut memberikan contoh cara menggunakan bagan saat menggunakan Bahasa Kueri Kusto.
Membuat bagan dari hasil
Mulailah dengan meninjau jumlah komputer per sistem operasi selama satu jam terakhir:
Heartbeat
| where TimeGenerated > ago(1h)
| summarize count(Computer) by OSType
Secara default, hasil ditampilkan sebagai tabel:
Untuk tampilan yang lebih berguna, pilih Bagan, lalu pilih opsi Pai untuk memvisualisasikan hasilnya:
Diagram waktu
Menampilkan rata-rata dan persentil ke-50 dan ke-95 dari waktu prosesor dalam bin satu jam.
Kueri berikut menghasilkan beberapa seri. Pada hasilnya, Anda dapat memilih seri mana yang akan ditampilkan dalam bagan waktu.
Perf
| where TimeGenerated > ago(1d)
| where CounterName == "% Processor Time"
| summarize avg(CounterValue), percentiles(CounterValue, 50, 95) by bin(TimeGenerated, 1h)
Pilih opsi tampilan bagan Baris:
Garis referensi
Garis referensi dapat membantu Anda dengan mudah mengidentifikasi apakah metrik melebihi ambang yang ditentukan. Untuk menambahkan garis ke bagan, perluas himpunan data dengan menambahkan kolom konstan:
Perf
| where TimeGenerated > ago(1d)
| where CounterName == "% Processor Time"
| summarize avg(CounterValue), percentiles(CounterValue, 50, 95) by bin(TimeGenerated, 1h)
| extend Threshold = 20
Beberapa dimensi
Beberapa ekspresi dalam klausul by dari summarize membuat beberapa baris dalam hasil. Satu baris dibuat untuk setiap kombinasi nilai.
SecurityEvent
| where TimeGenerated > ago(1d)
| summarize count() by tostring(EventID), AccountType, bin(TimeGenerated, 1h)
Saat Anda menampilkan hasil sebagai bagan, bagan menggunakan kolom pertama dari klausul by. Contoh berikut menunjukkan bagan kolom bertumpuk yang dibuat dengan menggunakan nilai EventID. Dimensi harus dari jenis string. Dalam contoh ini, nilai EventID dikonversi secara eksplisit ke string:
Anda dapat beralih antara kolom dengan memilih panah drop-down untuk nama kolom:
Analisis cerdas
Bagian ini menyertakan contoh yang menggunakan fungsi analitik cerdas di Azure Log Analytics untuk menganalisis aktivitas pengguna. Anda dapat menggunakan contoh ini untuk menganalisis aplikasi Anda sendiri yang dipantau oleh Azure Application Insights, atau menggunakan konsep dalam kueri ini untuk analisis serupa pada data lain.
Analisis kohor
Analisis kohor melacak aktivitas kelompok pengguna tertentu, yang dikenal sebagai cohorts. Analisis kohor mencoba mengukur seberapa menarik suatu layanan dengan mengukur tingkat pengguna yang kembali. Pengguna dikelompokkan berdasarkan waktu mereka pertama kali menggunakan layanan. Saat menganalisis kohor, kami berharap menemukan penurunan aktivitas selama periode pertama yang dilacak. Setiap kohor diberi judul berdasarkan minggu di mana anggotanya diamati untuk pertama kalinya.
Contoh berikut menganalisis jumlah aktivitas yang diselesaikan pengguna selama lima minggu setelah penggunaan pertama layanan mereka:
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
Berikut output-nya:
Menggulirkan pengguna aktif bulanan dan penyematan pengguna
Contoh berikut menggunakan analisis rangkaian waktu dengan fungsi series_fir. Anda dapat menggunakan fungsi series_fir untuk komputasi jendela geser. Aplikasi sampel yang dipantau adalah toko online yang melacak aktivitas pengguna melalui peristiwa kustom. Kueri melacak dua jenis aktivitas pengguna: AddToCart dan Checkout. Ini menentukan pengguna aktif sebagai pengguna yang menyelesaikan penyelesaian setidaknya sekali pada hari tertentu.
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
Berikut output-nya:
Contoh berikut mengubah kueri sebelumnya menjadi fungsi yang dapat digunakan kembali. Contoh ini kemudian menggunakan kueri untuk menghitung penyematan pengguna bergulir. Pengguna aktif dalam kueri ini ditentukan sebagai pengguna yang menyelesaikan penyelesaian setidaknya sekali pada hari tertentu.
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
Berikut output-nya:
Analisis regresi
Contoh ini menunjukkan cara membuat pendeteksi otomatis untuk gangguan layanan berdasarkan secara eksklusif pada log jejak aplikasi. Pendeteksi mencari peningkatan tiba-tiba yang bersifat abnormal dalam jumlah relatif kesalahan dan jejak peringatan dalam aplikasi.
Dua teknik digunakan untuk mengevaluasi status layanan berdasarkan data log jejak:
- Gunakan make-series untuk mengonversi log jejak tekstual semi-terstruktur menjadi metrik yang mewakili rasio antara garis jejak positif dan negatif.
- Gunakan series_fit_2lines dan series_fit_line untuk deteksi lompatan langkah lanjutan dengan menggunakan analisis time-series dengan regresi linier dua baris.
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")
Langkah berikutnya
- Pelajari tutorial tentang Bahasa Kueri Kusto.