2021-08-02 19:51:53 +08:00
< template >
< div class = "explore list-page" >
< div class = "main-list" >
2021-12-31 15:27:55 +08:00
< div class = "main-container explore-split-box" >
2021-08-02 19:51:53 +08:00
<!-- 关闭按钮 -- >
< div v-if = "closable" class="explore-close" >
2022-07-01 09:39:20 +08:00
< span @click ="split" > < i class = "nz-icon nz-icon-close" :title = "$t('overall.close')" > < / i > < / span >
2021-08-02 19:51:53 +08:00
< / div >
<!-- 顶部工具栏 -- >
< div class = "top-tools" style = "z-index: 1" >
2021-08-25 10:39:26 +08:00
< el-row class = "block-col-2" style = "width: 300px;" >
< el-col >
< el-dropdown trigger = "click" >
2021-11-17 18:21:56 +08:00
< span class = "el-dropdown-link explore-dropdown__item" >
2021-08-25 10:39:26 +08:00
< span > < i :class = "selectIcon" > < / i > < / span >
< span > { { selectValue } } < / span >
< span > < i class = "el-icon-arrow-down el-icon--right" > < / i > < / span >
< / span >
2021-10-29 12:02:34 +08:00
< el-dropdown-menu style = "width: 118px" class = "el-dropdown__width right-box-select-top right-public-box-dropdown-top" placement = "bottom-end" slot = "dropdown" >
2021-08-25 10:39:26 +08:00
< el-dropdown-item
2021-08-25 13:57:04 +08:00
@ click . native = "selectMetricsLogs(item.label,item.icon, item.value)"
2021-08-25 10:39:26 +08:00
v - for = "item in searchMetrics"
2021-09-06 16:22:05 +08:00
: key = "item.value" > < i class = "nz-icon" :class = "item.icon" style = "margin-right: 5px" / > { { item . label } } < / el-dropdown-item >
2021-08-25 10:39:26 +08:00
< / el-dropdown-menu >
< / el-dropdown >
< / el-col >
< / el-row >
2021-08-02 19:51:53 +08:00
< div class = "top-tool-right" >
2022-03-24 16:37:44 +08:00
< button v-if = "!closable" class="top-tool-btn top-tool-btn--text margin-r-10" style="cursor: pointer;" type="button" @click="split" >
{ { $t ( 'overall.split' ) } } < / button >
2021-08-02 19:51:53 +08:00
< pick-time id = "explore" ref = "pickTime" v-model = "filterTime" :class="{'margin-r-10': showMetrics}" :refresh-data-func="expressionChange" @unitChange="chartUnitChange" >
2022-07-01 15:42:08 +08:00
<!-- < template slot = "added-text" > { { $t ( 'dashboard.metricPreview.runQuery' ) } } < / template > -- >
2021-08-02 19:51:53 +08:00
< template slot = "added-text" > { { $t ( 'overall.query' ) } } < / template >
< / pick-time >
< button v-if = "showMetrics"
2022-07-01 15:42:08 +08:00
id = "explore-save-chart"
v - has = "'main_add'"
: class = "{'nz-btn-disabled btn-disabled-cursor-not-allowed' : saveDisabled}"
: disabled = "saveDisabled"
class = "top-tool-btn top-tool-btn--text"
type = "button"
@ click = "saveChart" >
2021-08-02 19:51:53 +08:00
{ { $t ( 'dashboard.metric.saveChart' ) } }
< / button >
2022-05-19 15:42:06 +08:00
< button v-else
2022-07-01 15:42:08 +08:00
id = "explore-save-chart-logs"
v - has = "'main_add'"
: class = "{'nz-btn-disabled btn-disabled-cursor-not-allowed' : saveDisabled}"
: disabled = "saveDisabled"
class = "top-tool-btn top-tool-btn--text"
type = "button"
@ click = "saveChartLogs" >
2022-05-19 15:42:06 +08:00
{ { $t ( 'dashboard.metric.saveChart' ) } }
< / button >
2021-08-02 19:51:53 +08:00
< / div >
< / div >
< div id = "explore-promql-box" class = "top-tools" style = "padding-top: 0; flex-wrap: wrap" >
< template v-if = "showMetrics" >
< promql-input
2021-09-28 16:54:07 +08:00
: from - father - data = "true"
: metricOptionsParent = "metricOptions"
2021-08-02 19:51:53 +08:00
v - for = "index of promqlKeys.length"
2022-07-01 15:42:08 +08:00
: id = "promqlKeys[index-1].id"
2022-06-10 11:42:44 +08:00
: pqid = "tabIndex.toString()"
2022-07-01 15:42:08 +08:00
: key = "promqlKeys[index-1].id"
2021-08-02 19:51:53 +08:00
: ref = "'promql-'+(index-1)"
: expression - list = "expressions"
2022-07-01 17:34:28 +08:00
: state = "promqlKeys[index-1].state"
2021-08-02 19:51:53 +08:00
: index = "index-1"
2022-07-01 15:42:08 +08:00
: plugins = "['metric-selector', 'metric-input', 'add', 'remove', 'copy','enable']"
2021-08-02 19:51:53 +08:00
: styleType = "1"
type = "metric"
2022-07-01 15:42:08 +08:00
@ enableExpression = "enableExpression"
2021-08-02 19:51:53 +08:00
@ addExpression = "addExpression"
@ copyExpression = "copyExpression"
@ removeExpression = "removeExpression"
@ resetExpression = "resetExpression"
> < / promql-input >
< / template >
< template v-else >
< promql-input
v - for = "index of promqlKeys.length"
2022-07-01 15:42:08 +08:00
: id = "promqlKeys[index-1].id"
2022-06-10 11:42:44 +08:00
: pqid = "tabIndex.toString()"
2022-07-01 15:42:08 +08:00
: key = "promqlKeys[index-1].id"
2021-08-02 19:51:53 +08:00
: ref = "'promql-'+(index-1)"
: expression - list = "expressions"
2022-07-01 17:34:28 +08:00
: state = "promqlKeys[index-1].state"
2021-08-02 19:51:53 +08:00
: index = "index-1"
2022-07-01 15:42:08 +08:00
: plugins = "['metric-selector', 'metric-input', 'add', 'remove', 'copy','enable']"
2021-08-02 19:51:53 +08:00
: styleType = "1"
type = "log"
2022-07-01 15:42:08 +08:00
@ enableExpression = "enableExpression"
2021-08-02 19:51:53 +08:00
@ addExpression = "addExpression"
@ copyExpression = "copyExpression"
@ removeExpression = "removeExpression"
@ resetExpression = "resetExpression"
> < / promql-input >
< / template >
< / div >
< div ref = "scrollWrap" style = "height: auto; padding: 0 20px 4px;" >
2021-09-13 15:06:54 +08:00
< el-collapse v-show = "!showIntroduce" v-model="collapseValue" class="explore-collapse" @change="logsCollapseChange" >
2021-08-02 19:51:53 +08:00
<!-- metric -- >
< template v-if = "showMetrics" >
2022-05-30 11:19:51 +08:00
< el-collapse-item name = "1" :title = "$t('explore.graph')" class = "el-collapse-item__height" >
2021-08-02 19:51:53 +08:00
< div class = "chart-room" >
2022-06-01 12:03:23 +08:00
< chart ref = "exploreChart" :unit = "chartUnit" :timeRange = "filterTime" > < / chart >
2021-08-02 19:51:53 +08:00
< / div >
< / el-collapse-item >
2021-09-10 16:44:04 +08:00
< el-collapse-item class = "el-collapse-item__height" name = "2" title = "Table" >
2021-12-27 15:37:14 +08:00
< div slot = "title" class = "explore-table-title" >
2022-05-30 11:19:51 +08:00
{ { $t ( 'dashboard.panel.chartForm.typeVal.table.label' ) } }
2021-12-27 17:59:57 +08:00
< i
class = "nz-icon-gear nz-icon"
style = "position: absolute;right: 10px;top: 8px"
@ click . stop = "tools.showCustomTableTitle = true"
2022-07-01 09:39:20 +08:00
: title = "$t('overall.selectColumns')"
2021-12-27 17:59:57 +08:00
> < / i >
<!-- 自定义table列 -- >
2021-12-27 15:37:14 +08:00
< transition name = "el-zoom-in-top" >
< element-set
@ click . stop
v - if = "tools.showCustomTableTitle"
: tableId = "tableId"
ref = "customTableTitle"
: custom - table - title = "tools.customTableTitle"
: original - table - title = "tableTitle"
: operation - true = "true"
@ close = "tools.showCustomTableTitle = false"
@ update = "updateCustomTableTitle"
> < / element-set >
< / transition >
< / div >
2021-08-04 17:10:35 +08:00
<!-- 自定义table列 -- >
2022-06-09 13:18:09 +08:00
< div class = "nz-table-list explore-table" >
2021-08-02 19:51:53 +08:00
< el-table ref = "exploreTable"
2022-07-01 15:42:08 +08:00
v - my - loading = "tools.loading"
class = "metric-table"
: data = "tableData"
border
: header - cell - class - name = "({ column }) => column.property === 'gear' ? 'explore-table-gear' : ''"
tooltip - effect = "light" >
2021-08-02 19:51:53 +08:00
< el-table-column
v - for = "(item, index) in tools.customTableTitle"
v - if = "item.show"
: key = "`col-${index}`"
: label = "item.label"
: prop = "item.prop"
: resizable = "false"
min - width = "110px"
show - overflow - tooltip
2021-12-30 19:17:22 +08:00
>
< template slot -scope = " scope " :column = "item" >
2022-03-31 09:55:22 +08:00
< template v-if = "item.prop === 'time'" > {{ timeFormate ( scope.row.time ) }} < / template >
2021-12-30 19:17:22 +08:00
< span v-else-if = "scope.row[item.prop]" > {{ scope.row [ item.prop ] }} < / span >
< template v-else > - < / template >
< / template >
< / el-table-column >
2021-10-25 14:38:43 +08:00
< template slot = "empty" >
2021-10-25 14:45:28 +08:00
< div v-if = "!tools.loading" class="table-no-data" >
2021-10-25 14:38:43 +08:00
< svg class = "icon" aria -hidden = " true " >
< use xlink :href = "#nz-icon-no-data-list" > < / use >
< / svg >
2021-10-28 17:53:27 +08:00
< div class = "table-no-data__title" > No results found < / div >
2021-10-25 14:38:43 +08:00
< / div >
< div v-else > & nbsp ; < / div >
< / template >
2021-08-02 19:51:53 +08:00
< / el-table >
< / div >
2021-12-24 16:31:31 +08:00
< pagination ref = "Pagination" :page-obj = "pageObj" @ pageNo = 'pageNo'
2021-08-04 17:10:35 +08:00
@ pageSize = 'pageSize' > < / pagination >
2021-08-02 19:51:53 +08:00
< / el-collapse-item >
< / template >
<!-- log -- >
2021-09-17 15:49:15 +08:00
< template v-else >
2022-05-30 11:19:51 +08:00
< el-collapse-item v-if = "showTab.indexOf('1') > -1" name="1" :title="$t('explore.graph')" class="el-collapse-item__height" >
2021-08-02 19:51:53 +08:00
< div class = "chart-room" >
2022-06-01 12:03:23 +08:00
< chart ref = "logChart" :unit = "chartUnit" v-my-loading = "chartLoading" :timeRange="filterTime" > < / chart >
2021-08-02 19:51:53 +08:00
< / div >
< / el-collapse-item >
2021-08-04 17:10:35 +08:00
< el-collapse-item v-if = "showTab.indexOf('2') > -1" name="2" title="Logs" >
2022-08-03 11:15:36 +08:00
< log-tab ref = "logDetail" :timeRange = "filterTime" :log-data = "logData" :explore-log-table = "logTabNoData" :explore-item = "true" :tab-index = "tabIndex" @exportLog ="exportLog" @limitChange ="queryLogData" v-my-loading = "chartLoading" > < / log -tab >
2021-08-02 19:51:53 +08:00
< / el-collapse-item >
< / template >
< / el-collapse >
< div v-if = "showMetrics" v-show="showIntroduce" class="introduce-view" >
< div class = "info-room" >
< div class = "col-md-9 doc-content" >
2022-07-01 09:39:20 +08:00
< h1 class = "page-header" > Query examples < a class = "header-anchor" href = "https://prometheus.io/docs/prometheus/latest/querying/examples/" rel = "noopener noreferrer" target = "_blank" > < i class = "nz-icon nz-icon-link1" style = "font-size: 16px;" :title = "$t('overall.link')" > < / i > < / a > < / h1 >
2021-08-02 19:51:53 +08:00
< div class = "content-divider" > < / div >
< h2 >
Simple time series selection
< / h2 >
< p > Return all time series with the metric < code > http _requests _total < / code > : < / p >
< pre > < code > http _requests _total < / code > < / pre >
< p > Return all time series with the metric < code > http _requests _total < / code > and the given < code > job < / code > and < code > handler < / code > labels : < / p >
< pre > < code > http _requests _total { job = "apiserver" , handler = "/api/comments" } < / code > < / pre >
< p > Return a whole range of time ( in this case 5 minutes ) for the same vector ,
making it a range vector : < / p >
< pre > < code > http _requests _total { job = "apiserver" , handler = "/api/comments" } [ 5 m ] < / code > < / pre >
< p > Note that an expression resulting in a range vector cannot be graphed directly ,
but viewed in the tabular ( "Console" ) view of the expression browser . < / p >
< p > Using regular expressions , you could select time series only for jobs whose
name match a certain pattern , in this case , all jobs that end with < code > server < / code > : < / p >
< pre > < code > http _requests _total { job = ~ ".*server" } < / code > < / pre >
< p > All regular expressions in Prometheus use RE2 syntax . < / p >
< p > To select all HTTP status codes except 4 xx ones , you could run : < / p >
< pre > < code > http _requests _total { status ! ~ "4.." } < / code > < / pre >
< h2 >
Subquery
< / h2 >
< p > Return the 5 - minute rate of the < code > http _requests _total < / code > metric for the past 30 minutes , with a resolution of 1 minute . < / p >
< pre > < code > rate ( http _requests _total [ 5 m ] ) [ 30 m : 1 m ] < / code > < / pre >
< p > This is an example of a nested subquery . The subquery for the < code > deriv < / code > function uses the default resolution . Note that using subqueries unnecessarily is unwise . < / p >
< pre > < code > max _over _time ( deriv ( rate ( distance _covered _total [ 5 s ] ) [ 30 s : 5 s ] ) [ 10 m : ] ) < / code > < / pre >
< h2 >
Using functions , operators , etc .
< / h2 >
< p > Return the per - second rate for all time series with the < code > http _requests _total < / code >
metric name , as measured over the last 5 minutes : < / p >
< pre > < code > rate ( http _requests _total [ 5 m ] ) < / code > < / pre >
< p > Assuming that the < code > http _requests _total < / code > time series all have the labels < code > job < / code >
( fanout by job name ) and < code > instance < / code > ( fanout by instance of the job ) , we might
want to sum over the rate of all instances , so we get fewer output time series ,
but still preserve the < code > job < / code > dimension : < / p >
< pre > < code > sum by ( job ) ( rate ( http _requests _total [ 5 m ] ) ) < / code > < / pre >
< p > If we have two different metrics with the same dimensional labels , we can apply
binary operators to them and elements on both sides with the same label set
will get matched and propagated to the output . For example , this expression
returns the unused memory in MiB for every instance ( on a fictional cluster
scheduler exposing these metrics about the instances it runs ) : < / p >
< pre > < code > ( instance _memory _limit _bytes - instance _memory _usage _bytes ) / 1024 / 1024 < / code > < / pre >
< p > The same expression , but summed by application , could be written like this : < / p >
< pre > < code > sum by ( app , proc ) ( instance _memory _limit _bytes - instance _memory _usage _bytes ) / 1024 / 1024 < / code > < / pre >
< p > If the same fictional cluster scheduler exposed CPU usage metrics like the following for every instance : < / p >
< pre > < code > instance _cpu _time _ns { app = "lion" , proc = "web" , rev = "34d0f99" , env = "prod" , job = "cluster-manager" }
instance _cpu _time _ns { app = "elephant" , proc = "worker" , rev = "34d0f99" , env = "prod" , job = "cluster-manager" }
instance _cpu _time _ns { app = "turtle" , proc = "api" , rev = "4d3a513" , env = "prod" , job = "cluster-manager" }
instance _cpu _time _ns { app = "fox" , proc = "widget" , rev = "4d3a513" , env = "prod" , job = "cluster-manager" }
...
< / code > < / pre >
< p > ... we could get the top 3 CPU users grouped by application ( < code > app < / code > ) and process type ( < code > proc < / code > ) like this : < / p >
< pre > < code > topk ( 3 , sum by ( app , proc ) ( rate ( instance _cpu _time _ns [ 5 m ] ) ) ) < / code > < / pre >
< p > Assuming this metric contains one time series per running instance , you could count the number of running instances per application like this : < / p >
< pre > < code > count by ( app ) ( instance _cpu _time _ns ) < / code > < / pre >
< / div >
< / div >
< / div >
< div v-else v-show = "showIntroduce" class="introduce-view" >
< div class = "info-room title-heard" >
< div class = "col-md-9 logs-content" >
2022-08-26 13:50:04 +08:00
<!-- LogQL : Log query language -- >
2022-08-31 16:16:21 +08:00
< h1 class = "page-header" style = "margin-Top:0px" id = "log-query-language" > LogQL : Log query language < a class = "header-anchor" href = "https://grafana.com/docs/loki/latest/logql/" rel = "noopener noreferrer" target = "_blank" > < i class = "nz-icon nz-icon-link1" style = "font-size: 16px;" :title = "$t('overall.link')" > < / i > < / a > < / h1 >
2021-08-02 19:51:53 +08:00
< div class = "title-heard__divider" > < / div >
2022-08-26 13:50:04 +08:00
< div class = "page-header-label" >
< p > LogQL is Grafana Loki ’ s PromQL - inspired query language . Queries act as if they are a distributed grep to aggregate log sources . LogQL uses labels and operators for filtering . < / p >
< p > There are two types of LogQL queries : < / p >
< ul >
2022-09-01 11:06:16 +08:00
< li > < b style = "color: #3C92F1" @click ="jumpClick('#log-queries')" class = "log-link" > Log queries < / b > return the contents of log lines . < / li >
< li > < b style = "color: #3C92F1" @click ="jumpClick('#metric-queries')" class = "log-link" > Metric queries < / b > extend log queries to calculate values based on query results . < / li >
2022-08-26 13:50:04 +08:00
< / ul >
< / div >
<!-- Log queries -- >
<!-- start -- >
< div class = "introduce-view__content" >
2022-09-01 11:06:16 +08:00
< h1 class = "page-header-one" id = "log-queries" > Log queries < / h1 >
2022-08-26 13:50:04 +08:00
< p > All LogQL queries contain a < b > log stream selector . < / b > < / p >
2022-09-01 11:06:16 +08:00
< div class = "img-hidden" > < img src = " data : image / png ; base64 , iVBORw0KGgoAAAANSUhEUgAAAkYAAAIPCAYAAACWihHIAABZK0lEQVR4XuzdC7ylU / 3H8Z9b5JJE7pdBSDepSAmDoitdpFBModCFVFL / MhJKiopIxSil + 82llDLjTq6JEJlBuadE7tX / 953fWmevvc6zz + wzc2Zm77M / 79fr9zJ7Pes8 + 9n7nP / / + bbWep7HDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHNiHa9V60aMudW81q4b55GVvNarGwEAQDudMB / zus9rwWrb / HSJ17F1Y + UlXtO9Xl1v6EGLeP3D61Gv5attY0371 / fynqLtb15PeE0o2gAAGGjXeh1RtS3m9Suvk6v2 + U0n8h / UjZVXeP3P6831hh71Ha8zvJ5SbxhjK1t8Lx8p2r7mdbbXkkUbAAAD7V9e36wbe9R4DEbzSlMwAgCgby3ktZ3Xoam2T221g7wmei1qMW3yea8DvNYs + sgWXpMtpnGuSP / evdj + Aa + ditfZM7z2thhl + rjXBu2bZ3q2xf5W8HqWRT8dxySL42qiKbAPeR3l9VFrXvsyp8FoQ69PWBz7Xl5Pb9 / cZiOvT1kc9w6pbRevPYd6NNPoiz77i72e5vV + i33sbzFFWdvV4vvMyu9uDa + PWfz8Hl6LF / 1q23od5nWk1zstRv1KTcFoR6 / 9itf6G9F7r27x3gfarN97GYvv8ksW3 + 0m7ZsBABh7OqEqvOjEpnCg0r + vtDjhlf7j9VmvCy1Gg6Z7PWyxlqQ8qX8wbVP / 3O + nxfabvE4vXotOvg9Y7Ev9tT5Gx3GMta9FemNq13vovWd43ZvaLrX2cKRwd1Ladr / XVRb7fdJrt6KfzG4w0rEdn9q1bx27PsM / vV5Z9JMFrNVX29VX6610jJpevKzVtdGKFj + rMPona73f4xbfhb6b0m + 9rile5 + / uHRbvf5fXrantdq / ntLrOpMDyC4vt6jsj / fsvFqE0awpGP7Hon21l0UchTn8Tt3ndndr + aMOn3F5qsT3 / PWhNmvpqBFLfIwAAY04nmN95Pei1TdGuE7pCyrmpT6ag84jF2pV8IlvO4qSuE5hGTUqdptLqYLSGRd / fW1xJJVo8 / BmLk2E58pBP7jqR5 / dTONFIl9rLERKNmKhNIw45MD3V63yLz6d / Z7MbjHSiV9shFscsGhVRyNF75M8jCo / qq5GrfDy6Ok99NbrWbTDS76D8TNqHQuFDFt9l1ikYqd + 7inaNYCn4XGetzyBajK730mhWpu9c35WCc / 7bGE0wUth5edGuUSu1awQp00jRnRaBfa3UtrBFH / XdJ7UBADCm9L / KdaLRyb2m6S5t27RoUzC6x4ZPfSgc / dtrStXebTDSlIqCVdOl5VMtTsR51Cif3Pcd6hF04tQoyKlV + wts + NVvkyz28cKibXaCkUakdAI / Z6hHi0ZUNDKlEbZMozwatapHPNb3 + q91H4xusOGfaR2L34 + mvLJOwejbRVumUSRte0N6vazFSNSnh3q07GzRNwec0QQjTSGW9F3ou9ci8ezD1vmKNv09aFE / AABjTus2dLJapd5gsQ5F27SuKNOJtw4e2Vlet1Rt3Qajyy0ul2 + iESAdx3PT63xy14m2dr3F6FUTjUK8zGLtyy8t9rFZsX12gtHz0 + v3DvVopxEwlShoqO / k1uY2CjvdBqMv1BsShaALitedglFe11RayuL3m / f9Jou + r7MIKGXpe9Q2hWcZTTDSfmv63Wt6NtPvR8c9oaG + bBEiy9E + AADGhE4y + l / mnWga5avFa504NQ3U5ESLKZpSt8FI02KdQolOzDqhbp1e55P75kM9WjQio4CWaVpIwW66xc + oNLKlADUWwehV6fVrhnq0 + 5HFGh7Rwmf17RSipln3wagMICWFwj8XrzsFI32OJlqzlEeTciAdqXJoHk0wen3Rll3sdVHxWtN09XvV1bTYHACAOZLX5SxRb7BYv6L / Zf65ok3B6BvF65JCwB1VW7fBSIGm00iPrl7TMWodjIwmGH3Rou / JFlNEWvsjeTRkToNRnop821CPdr + 21rSP1hqpr0bpmmgBcrfBqGl6SzTqotG3rFMwagpymhbU1JnWFck7Lfpua8NHbXI93cJYB6NzLcLRhBGq6apJAADmyFstTlYalalpMba2aT1JpmCkBbo1re9RsPhN1a5gpJGkWh2Mvm9x1VjT9MgJFguTdXm6jCYYafREJ9mabjUwFsFIwUDHdvxQjxZ9lnLN0wIWwVEhoDbB4rvtNhidXW + wmArTlWknF22dgpEWtdfyZ9Pl8aL1V3o9KXcYwVgHI41S6m + nvi0AAABzlUaKdDWSFgSXo0ZaXK2TtK4g0gk308lbJ7fdijbJVwtplKGkfddhSepglENYOTolugIqXwWXjSYYafREIzbl6II + j8LCWAQjUfBRICkXcssRFn3LS / a1qFhtWpCtETnR + i6N9ChgdRuMNJJXXkUomhbVtjzlKJ2CkS59n1C061jOs7g6cfmiXaM2Wjem9y0dbBF61k2vxzoYbWzxt6bPVNLvTsH8u1U7AABj5tUW4WO6xcLbIy3uU6O2eiRJJ6vTLE6smjqbbDEFphOerirSqEhJ02japlEbXc6e1cFIjrHoO81iqkg / q / VAN1r7yXo0wWhPi7468epYFVZuTa / VPhbBSKFBn0fHqmPW59TnVb + ji36iK8lOStu0Hkvfub7Tr1kEk26D0Q8tRlS + ZfFd6WfVXk9bdgpGp1jc + + krFmFUa64UtnSzxZKu6FM / lUZxyt / 3j232LtfvJhiJ3kv9L7WY8tWViwppmu7TtCgAAHONRjv0v8J1gr / ZYhSkHgERncS / ZHHn5XMsTuwabdKIkRY61zQVomkbnfB1Ms80AnR48TrbxeJybO33DxYn7byOJVM4UR + dtGsnW5xAS5Msrgy7zSIk6EoqhSrt4 / mtbjNP9DoZj0T99XN1KHuGxftq / zp2fTdvb + vRTuul9J3p / XJQGE0wOsAiaGikSe + nz6d7 + 9TBVGusyqnMHIy2sAiNWtekn9fUXNO6I1nDYl3ZDRZ9FWLeZ + 2jcLriTt9LudZKAVFTpNmLLProiraaQlde21TSWjAdm95Xpf3ltWYAAMx3ORhh9jWtoxKN2jSthyqVwWh2lMEIAADMIYLRnHmvxQ0hNXpS2tUisBxWtdcIRgAA9BCC0ZxZ3 + KeTbp31PkWdwq / 2iKsaI1NudC9CcEIAIAecpDFYm3MPt12QGt0tI5Li6N1OwLdq6mb + / IsabEuadN6Q5d0k0n9 / Br1BgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB9aUGvG70WqDcAAAAMmqUtnmq / RL0BAABg0KzodYnFyBEAAAAAAAAAAACAIS / w2sbrd / UGAACAQbKY1 / 1eq6X / Lt + + GQAAYHDs4PXb9O + ve32y2AYAADBQLvB6S / r3 + l4 / L7YBAAAMjEW8DjMu0wcAAAAAAIAsWzcUVvD6jdfT6g0AAADjjRZcX + e1UL2hcIzXmRbTbQAAAOPS1l53e21Qb6gs7PUTi8XYIwUoAACAvrS6111em9YbOlA42rVuBAAAGC / 0wNjZpWAFAADQt / Soj13qxtmg6bQ / eP3Ma71qGwAAQM / Sgukdvc7zut7rde2bZ9tTvA6wmI47utoGAAAw3 + nmjBO8NvNaILUt4XWGxR2ttU5orOn5auXi7c29PmBxDBOMxdoAAAw50WLK5W9e / / N6YbHt6tSWS091z66ptj2v2HZtte05xTZddl5u02MtMo2WlNvKKaAbq23rFNtuqratXWy7udq2VrHtlmrbhGLbjGpbuV7ntmrbqsW226ttqxTb9B0 / 4TXd63yvZYpt85IC0bEWjxfRsXyk2PY5az / + jxbbjqi2lT93ZLVt / 2LbF6tt + xXbNJJVbvtgse1L1TaFuewr1
2022-08-26 15:05:51 +08:00
< p > Optionally , the log stream selector can be followed by < b > a log pipeline < / b > . A log pipeline is a set of stage expressions that are chained together and applied to the selected log streams . Each expression can filter out , parse , or mutate log lines and their respective labels . < / p >
2022-08-26 13:50:04 +08:00
< p > The following example shows a full log query in action : < / p >
< p > < code class = "fillbox" > { container = "query-frontend" , namespace = "loki-dev" } |= "metrics.go" | logfmt | duration > 10 s and throughput _mb < 500 < / code > < / p >
< p > The query is composed of : < / p >
< ul >
< li > a log stream selector < code > { container = "query-frontend" , namespace = "loki-dev" } < / code > which targets the < code > query - frontend < / code > container in the < code > loki - dev < / code > namespace . < / li >
< li > a log pipeline < code > |= "metrics.go" | logfmt | duration > 10 s and throughput _mb < 500 < / code > which will filter out log that contains the word < code > metrics . go < / code > , then parses each log line to extract more labels and filter with them . < / li >
< / ul >
< pre > To avoid escaping special characters you can use the < code > ` </code>(backtick) instead of<code> " </code>when quoting strings. For example <code> ` \ w + ` </code> is the same as <code>" \\ w+"</code>. This is specially useful when writing a regular expression which contains multiple <br/>backslashes that require escaping.</pre>
< / div >
<!-- Log stream selector -- >
2021-08-02 19:51:53 +08:00
< div class = "introduce-view__content" >
2022-08-31 16:16:21 +08:00
< h1 class = "page-header-two" id = "log-stream-selector" > Log stream selector < / h1 >
2021-08-02 19:51:53 +08:00
< div class = "introduce-view__content-label" >
2022-08-26 13:50:04 +08:00
< p > The stream selector determines which log streams to include in a query ’ s results . A log stream is a unique source of log content , such as a file . A more granular log stream selector then reduces the number of searched streams to a manageable volume . This means that the labels passed to the log stream selector will affect the relative performance of the query ’ s execution . < / p >
2022-08-26 15:05:51 +08:00
< p > The log stream selector is specified by one or more comma - separated key - value pairs . Each key is a log label and each value is that label ’ s value . Curly braces ( < code > { < / code > and < code > } < / code > ) delimit the stream selector . < / p >
2022-08-26 13:50:04 +08:00
< p > Consider this stream selector : < / p >
< p > < code class = "fillbox" > { app = "mysql" , name = "mysql-backup" } < / code > < / p >
< p > All log streams that have both a label of < code > app < / code > whose value is < code > mysql < / code > and a label of < code > name < / code > whose value is < code > mysql - backup < / code > will be included in the query results . A stream may contain other pairs of labels and values , but only the specified pairs within the stream selector are used to determine which streams will be included within the query results . < / p >
< p > The same rules that apply for Prometheus Label Selectors apply for Grafana Loki log stream selectors . < / p >
2022-08-26 15:05:51 +08:00
< p > The < code > = < / code > operator after the label name is a < b > label matching operator < / b > . The following label matching operators are supported : < / p >
2022-08-26 13:50:04 +08:00
< ul >
< li > < code > = < / code > : exactly equal < / li >
< li > < code > != < / code > : not equal < / li >
< li > < code > = ~ < / code > : regex matches < / li >
< li > < code > ! ~ < / code > : regex does not match < / li >
< / ul >
< p > Regex log stream examples : < / p >
< ul >
< li > < code > { name = ~ "mysql.+" } < / code > < / li >
< li > < code > { name ! ~ "mysql.+" } < / code > < / li >
< li > < code > { name ! ~ ` mysql- \ d+ ` } < / code > < / li >
< / ul >
< p > < b > Note : < / b > The < code > = ~ < / code > regex operator is fully anchored , meaning regex must match against the entire string , including newlines . The regex < code > . < / code > character does not match newlines by default . If you want the regex dot character to match newlines you can use the single - line flag , like so : < code > ( ? s ) search _term . + < / code > matches < code > search _term \ n . < / code > < / p >
2021-08-02 19:51:53 +08:00
< / div >
< / div >
2022-08-26 13:50:04 +08:00
<!-- Log pipeline -- >
2021-08-02 19:51:53 +08:00
< div class = "introduce-view__content" >
2022-08-31 16:16:21 +08:00
< h1 class = "page-header-two" id = "log-pipeline" > Log pipeline < / h1 >
2021-08-02 19:51:53 +08:00
< div class = "introduce-view__content-label" >
2022-08-26 13:50:04 +08:00
< p > A log pipeline can be appended to a log stream selector to further process and filter log streams . It is composed of a set of expressions . Each expression is executed in left to right sequence for each log line . If an expression filters out a log line , the pipeline will stop processing the current log line and start processing the next log line . < / p >
< p > Some expressions can mutate the log content and respective labels , which will be then be available for further filtering and processing in subsequent expressions . An example that mutates is the expression < / p >
< p > < code v-pre class = "fillbox" > | line _format "{{.status_code}}" < / code > < / p >
< p > Log pipeline expressions fall into one of three categories : < / p >
< ul >
2022-09-01 11:06:16 +08:00
< li > Filtering expressions : < b style = "color: #3C92F1" @click ="jumpClick('#line-filter-expressions')" class = "log-link" > line filter expressions < / b > and < b style = "color: #3C92F1" @click ="jumpClick('#label-filter-expressions')" class = "log-link" > label filter expressions < / b > < / li >
< li > < b style = "color: #3C92F1" @click ="jumpClick('#parsing-expressions')" class = "log-link" > Parsing expressions < / b > < / li >
< li > Formatting expressions : < b style = "color: #3C92F1" @click ="jumpClick('#line-format-expressions')" class = "log-link" > line format expressions < / b > and < b style = "color: #3C92F1" @click ="jumpClick('#label-format-expressions')" class = "log-link" > label format expressions < / b > < / li >
2022-08-26 13:50:04 +08:00
< / ul >
2021-08-02 19:51:53 +08:00
< / div >
< / div >
2022-08-26 13:50:04 +08:00
<!-- Line filter expression -- >
2021-08-02 19:51:53 +08:00
< div class = "introduce-view__content" >
2022-08-26 13:50:04 +08:00
< h2 id = "line-filter-expressions" > Line filter expression < / h2 >
2021-08-02 19:51:53 +08:00
< div class = "introduce-view__content-label" >
2022-08-26 15:05:51 +08:00
< p > The line filter expression does a distributed < code > grep < / code > over the aggregated logs from the matching log streams . It searches the contents of the log line , discarding those lines that do not match the case sensitive expression . < / p >
2022-08-26 13:50:04 +08:00
< p > Each line filter expression has a < b > filter operator < / b > followed by text or a regular expression . These filter operators are supported : < / p >
< ul >
< li > < code > |= < / code > : Log line contains string < / li >
< li > < code > != < / code > : Log line does not contain string < / li >
< li > < code > | ~ < / code > : Log line contains a match to the regular expression < / li >
< li > < code > ! ~ < / code > : Log line does not contain a match to the regular expression < / li >
< / ul >
< p > Line filter expression examples : < / p >
< ul >
< li >
< p > Keep log lines that have the substring “ error ” : < / p >
< p > < code class = "fillbox" > |= "error" < / code > < / p >
< p > A complete query using this example : < / p >
< p > < code class = "fillbox" > { job = "mysql" } |= "error" < / code > < / p >
< / li >
< li >
< p > Discard log lines that have the substring “ kafka . server : type = ReplicaManager ” : < / p >
< p > < code class = "fillbox" > != "kafka.server:type=ReplicaManager" < / code > < / p >
< p > A complete query using this example : < / p >
< p > < code class = "fillbox" > { instance = ~ "kafka-[23]" , name = "kafka" } != "kafka.server:type=ReplicaManager" < / code > < / p >
< / li >
< li >
2022-09-01 11:46:31 +08:00
< p > Keep log lines that contain a substring that starts with < code > tsdb - ops < / code > and ends with < code > io : 2003 < / code > . A complete query with a regular expression : < / p >
2022-08-26 13:50:04 +08:00
< p > < code class = "fillbox" > { name = "kafka" } | ~ "tsdb-ops.*io:2003" < / code > < / p >
< / li >
< li >
2022-09-01 11:46:31 +08:00
< p > Keep log lines that contain a substring that starts with < code > error = < / code > , and is followed by 1 or more word characters . A complete query with a regular expression : < / p >
2022-08-26 13:50:04 +08:00
< p > < code class = "fillbox" > { name = "cassandra" } | ~ ` error= \ w+ ` < / code > < / p >
< / li >
< / ul >
< p > Filter operators can be chained . Filters are applied sequentially . Query results will have satisfied every filter . This complete query example will give results that include the string < code > error < / code > , and do not include the string < code > timeout < / code > . < / p >
< p > < code class = "fillbox" > { job = "mysql" } |= "error" != "timeout" < / code > < / p >
< p > When using < code > | ~ < / code > and < code > ! ~ < / code > , Go ( as in Golang ) RE2 syntax regex may be used . The matching is case - sensitive by default . Switch to case - insensitive matching by prefixing the regular expression with < code > ( ? i ) < / code > . < / p >
< p > While line filter expressions could be placed anywhere within a log pipeline , it is almost always better to have them at the beginning . Placing them at the beginning improves the performance of the query , as it only does further processing when a line matches . For example , while the results will be the same , the query specified with < / p >
< p > < code class = "fillbox" v-pre > { job = "mysql" } | = " error " | json | line_format " {{ .err }} " < / code > < / p >
< p > will always run faster than < / p >
< p > < code class = "fillbox" v-pre > { job = "mysql" } | json | line_format " {{ .message }} " | = " error " < / code > < / p >
< p > Line filter expressions are the fastest way to filter logs once the log stream selectors have been applied . < / p >
< p > Line filter expressions have support matching IP addresses . See Matching IP addresses for details . < / p >
2021-08-02 19:51:53 +08:00
< / div >
< / div >
2022-08-26 13:50:04 +08:00
<!-- Label filter expression -- >
2021-08-02 19:51:53 +08:00
< div class = "introduce-view__content" >
2022-08-26 13:50:04 +08:00
< h2 id = "label-filter-expressions" > Label filter expression < / h2 >
2021-08-02 19:51:53 +08:00
< div class = "introduce-view__content-label" >
2022-08-26 13:50:04 +08:00
< p > Label filter expression allows filtering log line using their original and extracted labels . It can contain multiple predicates . < / p >
< p > A predicate contains a < b > label identifier < / b > , an < b > operation < / b > and a < b > value < / b > to compare the label with . < / p >
< p > For example with < code > cluster = "namespace" < / code > the cluster is the label identifier , the operation is < code > = < / code > and the value is “ namespace ” . The label identifier is always on the right side of the operation . < / p >
< p > We support multiple < b > value < / b > types which are automatically inferred from the query input . < / p >
< ul >
< li > < b > String < / b > is double quoted or backticked such as < code > "200" < / code > or ` <code>us-central1</code> ` . < / li >
< li > Duration is a sequence of decimal numbers , each with optional fraction and a unit suffix , such as “ 300 ms ” , “ 1.5 h ” or “ 2 h45m ” . Valid time units are “ ns ” , “ us ” ( or “ µs ” ) , “ ms ” , “ s ” , “ m ” , “ h ” . < / li >
< li > < b > Number < / b > are floating - point number ( 64 bits ) , such as < code > 250 < / code > , < code > 89.923 < / code > . < / li >
< li > < b > Bytes < / b > is a sequence of decimal numbers , each with optional fraction and a unit suffix , such as “ 42 MB ” , “ 1.5 Kib ” or “ 20 b ” . Valid bytes units are “ b ” , “ kib ” , “ kb ” , “ mib ” , “ mb ” , “ gib ” , “ gb ” , “ tib ” , “ tb ” , “ pib ” , “ pb ” , “ eib ” , “ eb ” . < / li >
< / ul >
2022-09-01 11:06:16 +08:00
< p > String type work exactly like Prometheus label matchers use in < b style = "color: #3C92F1" @click ="jumpClick('#log-stream-selector')" class = "log-link" > log stream selector < / b > . This means you can use the same operations ( < code > = < / code > , < code > != < / code > , < code > = ~ < / code > , < code > ! ~ < / code > ) . < / p >
2022-08-26 13:50:04 +08:00
< pre > The string type is the only one that can filter out a log line with a label < code > _ _error _ _ < / code > . < / pre >
< p > Using Duration , Number and Bytes will convert the label value prior to comparision and support the following comparators : < / p >
< ul >
< li > < code > == < / code > or < code > = < / code > for equality . < / li >
< li > < code > != < / code > for inequality . < / li >
< li > < code > > < / code > and < code > >= < / code > for greater than and greater than or equal . < / li >
< li > < code > & lt ; < / code > and < code > & lt ; = < / code > for lesser than and lesser than or equal . < / li >
< / ul >
< p > For instance , < code > logfmt | duration > 1 m and bytes _consumed > 20 MB < / code > < / p >
< p > If the conversion of the label value fails , the log line is not filtered and an < code > _ _error _ _ < / code > label is added . To filters those errors see the pipeline errors section . < / p >
< p > You can chain multiple predicates using < code > and < / code > and < code > or < / code > which respectively express the < code > and < / code > and < code > or < / code > binary operations . < code > and < / code > can be equivalently expressed by a comma , a space or another pipe . Label filters can be place anywhere in a log pipeline . < / p >
< p > This means that all the following expressions are equivalent : < / p >
< p > < code class = "fillbox" >
| duration >= 20 ms or size == 20 kb and method ! ~ "2.." < br / >
| duration >= 20 ms or size == 20 kb | method ! ~ "2.." < br / >
| duration >= 20 ms or size == 20 kb , method ! ~ "2.." < br / >
| duration >= 20 ms or size == 20 kb method ! ~ "2.."
< / code > < / p >
< p > By default the precedence of multiple predicates is right to left . You can wrap predicates with parenthesis to force a different precedence left to right . < / p >
< p > For example the following are equivalent . < / p >
< p > < code class = "fillbox" >
| duration >= 20 ms or method = "GET" and size & lt ; = 20 KB < br / >
| ( ( duration >= 20 ms or method = "GET" ) and size & lt ; = 20 KB )
< / code > < / p >
< p > It will evaluate first < code > duration >= 20 ms or method = "GET" < / code > . To evaluate first < code > method = "GET" and size & lt ; = 20 KB < / code > , make sure to use proper parenthesis as shown below . < / p >
< p > < code class = "fillbox" > | duration >= 20 ms or ( method = "GET" and size & lt ; = 20 KB ) < / code > < / p >
< pre > Label filter expressions are the only expression allowed after the unwrap expression . This is mainly to allow filtering errors from the metric extraction . < / pre >
< p > Label filter expressions have support matching IP addresses . See Matching IP addresses for details . < / p >
2021-08-02 19:51:53 +08:00
< / div >
< / div >
2022-08-26 13:50:04 +08:00
<!-- Parser expression -- >
2021-08-02 19:51:53 +08:00
< div class = "introduce-view__content" >
2022-08-26 13:50:04 +08:00
< h2 id = "parsing-expressions" > Parser expression < / h2 >
2021-08-02 19:51:53 +08:00
< div class = "introduce-view__content-label" >
2022-09-01 11:06:16 +08:00
< p > Parser expression can parse and extract labels from the log content . Those extracted labels can then be used for filtering using < b style = "color: #3C92F1" @click ="jumpClick('#label-filter-expressions')" class = "log-link" > label filter expressions < / b > or for < b style = "color: #3C92F1" @click ="jumpClick('#metric-queries')" class = "log-link" > metric aggregations < / b > . < / p >
2022-08-26 13:50:04 +08:00
< p > Extracted label keys are automatically sanitized by all parsers , to follow Prometheus metric name convention . ( They can only contain ASCII letters and digits , as well as underscores and colons . They cannot start with a digit . ) < / p >
< p > For instance , the pipeline < code > | json < / code > will produce the following mapping : < / p >
< p > < code class = "fillbox" > { "a.b" : { c : "d" } , e : "f" } < / code > < / p >
< p > - > < / p >
< p > < code class = "fillbox" > { a _b _c = "d" , e = "f" } < / code > < / p >
< p > In case of errors , for instance if the line is not in the expected format , the log line won ’ t be filtered but instead will get a new < code > _ _error _ _ < / code > label added . < / p >
2022-09-01 11:06:16 +08:00
< p > If an extracted label key name already exists in the original log stream , the extracted label key will be suffixed with the < code > _extracted < / code > keyword to make the distinction between the two labels . You can forcefully override the original label using a < b style = "color: #3C92F1" @click ="jumpClick('#label-format-expressions')" class = "log-link" > label formatter expression < / b > . However if an extracted key appears twice , only the latest label value will be kept . < / p >
< p > Loki supports < b style = "color: #3C92F1" @click ="jumpClick('#JSON')" class = "log-link" > JSON < / b > , < b style = "color: #3C92F1" @click ="jumpClick('#logfmt')" class = "log-link" > logfmt < / b > , < b style = "color: #3C92F1" @click ="jumpClick('#pattern')" class = "log-link" > pattern < / b > , < b style = "color: #3C92F1" @click ="jumpClick('#regexp')" class = "log-link" > regexp < / b > and < b style = "color: #3C92F1" @click ="jumpClick('#unpack')" class = "log-link" > unpack < / b > parsers . < / p >
< p > It ’ s easier to use the predefined parsers < code > json < / code > and < code > logfmt < / code > when you can . If you can ’ t , the < code > pattern < / code > and < code > regexp < / code > parsers can be used for log lines with an unusual structure . The < code > pattern < / code > parser is easier and faster to write ; it also outperforms the < code > regexp < / code > parser . Multiple parsers can be used by a single log pipeline . This is useful for parsing complex logs . There are examples in < b style = "color: #3C92F1" @click ="jumpClick('#multiple-parsers')" class = "log-link" > Multiple parsers < / b > . < / p >
2021-08-02 19:51:53 +08:00
< / div >
< / div >
2022-08-26 13:50:04 +08:00
<!-- JSON -- >
2021-08-02 19:51:53 +08:00
< div class = "introduce-view__content" >
2022-08-26 13:50:04 +08:00
< h2 id = "JSON" > JSON < / h2 >
< div class = "introduce-view__content-label" >
< p > The < b > json < / b > parser operates in two modes : < / p >
< p > 1. < b > without < / b > parameters : < / p >
< div class = "json-module" >
< p > Adding < code > | json < / code > to your pipeline will extract all json properties as labels if the log line is a valid json document . Nested properties are flattened into label keys using the < code > _ < / code > separator . < / p >
< p > Note : < b > Arrays are skipped < / b > . < / p >
< p > For example the json parsers will extract from the following document : < / p >
< pre > {
"protocol" : "HTTP/2.0" ,
"servers" : [ "129.0.1.1" , "10.2.1.3" ] ,
"request" : {
"time" : "6.032" ,
"method" : "GET" ,
"host" : "foo.grafana.net" ,
"size" : "55" ,
"headers" : {
"Accept" : "*/*" ,
"User-Agent" : "curl/7.68.0"
}
} ,
"response" : {
"status" : 401 ,
"size" : "228" ,
"latency_seconds" : "6.031"
}
} < / pre >
< p > The following list of labels : < / p >
< pre > "protocol" => "HTTP/2.0"
"request_time" => "6.032"
"request_method" => "GET"
"request_host" => "foo.grafana.net"
"request_size" => "55"
"response_status" => "401"
"response_size" => "228"
"response_latency_seconds" => "6.031" < / pre >
< / div >
< p > 2. < b > with < / b > parameters : < / p >
< div class = "json-module" >
< p > Using < code > | json label = "expression" , another = "expression" < / code > in your pipeline will extract only the specified json fields to labels . You can specify one or more expressions in this way , the same as < code > label _format < / code > ; all expressions must be quoted . < / p >
< p > Currently , we only support field access ( < code > my . field < / code > , < code > my [ "field" ] < / code > ) and array access ( < code > list [ 0 ] < / code > ) , and any combination of these in any level of nesting ( < code > my . list [ 0 ] [ "field" ] < / code > ) . < / p >
< p > For example , < code > | json first _server = "servers[0]" , ua = "request.headers[\"User-Agent\" ] < / code > will extract from the following document : < / p >
< pre > {
"protocol" : "HTTP/2.0" ,
"servers" : [ "129.0.1.1" , "10.2.1.3" ] ,
"request" : {
"time" : "6.032" ,
"method" : "GET" ,
"host" : "foo.grafana.net" ,
"size" : "55" ,
"headers" : {
"Accept" : "*/*" ,
"User-Agent" : "curl/7.68.0"
}
} ,
"response" : {
"status" : 401 ,
"size" : "228" ,
"latency_seconds" : "6.031"
}
}
< / pre >
< p > The following list of labels : < / p >
< pre > "first_server" => "129.0.1.1"
"ua" => "curl/7.68.0" < / pre >
< p > If an array or an object returned by an expression , it will be assigned to the label in json format . < / p >
< p > For example , < code > | json server _list = "servers" , headers = "request.headers" < / code > will extract : < / p >
< pre > "server_list" => ` ["129.0.1.1","10.2.1.3"] `
"headers" => ` {"Accept": "*/*", "User-Agent": "curl/7.68.0"} ` < / pre >
< p > If the label to be extracted is same as the original JSON field , expression can be written as just < code > | json & lt ; label & gt ; < / code > < / p >
< p > For example , to extract < code > servers < / code > fields as label , expression can be written as following < / p >
< p > < code > | json servers < / code > will extract : < / p >
< pre > "servers" => ` ["129.0.1.1","10.2.1.3"] ` < / pre >
< p > Note that < code > | json servers < / code > is same as < code > | json servers = "servers" < / code > < / p >
< / div >
< / div >
< / div >
<!-- logfmt -- >
< div class = "introduce-view__content" >
< h2 id = "logfmt" > logfmt < / h2 >
< div class = "introduce-view__content-label logfmt-module" >
< p > The < b > logfmt < / b > parser can be added using the < code > | logfmt < / code > and will extract all keys and values from the logfmt formatted log line . < / p >
< p > For example the following log line : < / p >
< pre > at = info method = GET path = / h o s t = g r a f a n a . n e t f w d = " 1 2 4 . 1 3 3 . 1 2 4 . 1 6 1 " s e r v i c e = 8 m s s t a t u s = 2 0 0 < / p r e >
< p > will get those labels extracted : < / p >
< pre > "at" => "info"
"method" => "GET"
"path" => "/"
"host" => "grafana.net"
"fwd" => "124.133.124.161"
"service" => "8ms"
"status" => "200" < / pre >
< / div >
< / div >
<!-- Pattern -- >
< div class = "introduce-view__content" >
< h2 id = "pattern" > Pattern < / h2 >
< div class = "introduce-view__content-label pattern-module" >
< p > The pattern parser allows the explicit extraction of fields from log lines by defining a pattern expression ( < code > | pattern "<pattern-expression>" < / code > ) . The expression matches the structure of a log line . < / p >
< p > Consider this NGINX log line . < / p >
< p > < code class = "fillbox" > 0.191 .12 .2 - - [ 10 / Jun / 2021 : 09 : 14 : 29 + 0000 ] "GET /api/plugins/versioncheck HTTP/1.1" 200 2 "-" "Go-http-client/2.0" "13.76.247.102, 34.120.177.193" "TLSv1.2" "US" ""
< / code > < / p >
< p > This log line can be parsed with the expression < / p >
< p > < code > & lt ; ip & gt ; - - & lt ; _ & gt ; "<method> <uri> <_>" & lt ; status & gt ; & lt ; size & gt ; & lt ; _ & gt ; "<agent>" & lt ; _ & gt ; < / code > < / p >
< p > to extract these fields : < / p >
< pre > "ip" => "0.191.12.2"
"method" => "GET"
"uri" => "/api/plugins/versioncheck"
"status" => "200"
"size" => "2"
"agent" => "Go-http-client/2.0" < / pre >
< p > A pattern expression is composed of captures and literals . < / p >
< p > A capture is a field name delimited by the < code > & lt ; < / code > and < code > & gt ; < / code > characters . < code > & lt ; example & gt ; < / code > defines the field name < code > example < / code > . An unnamed capture appears as < code > & lt ; _ & gt ; < / code > . < br / > The unnamed capture skips matched content . < / p >
< p > Captures are matched from the line beginning or the previous set of literals , to the line end or the next set of literals . If a capture is not matched , the pattern parser will stop . < / p >
< p > Literals can be any sequence of UTF - 8 characters , including whitespace characters . < / p >
< p > By default , a pattern expression is anchored at the start of the log line . If the expression starts with literals , then the log line must also start with the same set of literals . Use < code > & lt ; _ & gt ; < / code > at the beginning of the expression if you don ’ t want to anchor the expression at the start . < / p >
< p > Consider the log line < / p >
< pre > level = debug ts = 2021 - 06 - 10 T09 : 24 : 13.472094048 Z caller = logging . go : 66 traceID = 0568 b66ad2d9294c msg = "POST /loki/api/v1/push (204) 16.652862ms" < / pre >
< p > To match < code > msg = " < / code > , use the expression : < / p >
< pre > & lt ; _ & gt ; msg = "<method> <path> (<status>) <latency>" < / pre >
< p > A pattern expression is invalid if < / p >
< ul >
< li > It does not contain any named capture . < / li >
< li > It contains two consecutive captures not separated by whitespace characters . < / li >
< / ul >
< / div >
< / div >
<!-- Regular expression -- >
< div class = "introduce-view__content" >
< h2 id = "regexp" > Regular expression < / h2 >
< div class = "introduce-view__content-label regular-module" >
< p > Unlike the logfmt and json , which extract implicitly all values and takes no parameters , the regexp parser takes a single parameter < code > | regexp "<re>" < / code > which is the regular expression using the Golang RE2 syntax . < / p >
< p > The regular expression must contain a least one named sub - match ( e . g < code > ( ? P & lt ; name & gt ; re ) < / code > ) , each sub - match will extract a different label . < / p >
< p > For example the parser | regexp "(?P<method>\\w+) (?P<path>[\\w|/]+) \\((?P<status>\\d+?)\\) (?P<duration>.*)" will extract from the following line : < / p >
< pre > POST / api / prom / api / v1 / query _range ( 200 ) 1.5 s < / pre >
< p > those labels : < / p >
< pre > "method" => "POST"
"path" => "/api/prom/api/v1/query_range"
"status" => "200"
"duration" => "1.5s" < / pre >
< / div >
< / div >
<!-- unpack -- >
< div class = "introduce-view__content" >
< h2 id = "unpack" > unpack < / h2 >
< div class = "introduce-view__content-label unpack-module" >
< p > The < code > unpack < / code > parser parses a JSON log line , unpacking all embedded labels from Promtail ’ s < code > pack < / code > stage . < b > A special property < code > _entry < / code > will also be used to replace the original log line < / b > . < / p >
< p > For example , using < code > | unpack < / code > with the log line : < / p >
< pre > {
"container" : "myapp" ,
"pod" : "pod-3223f" ,
"_entry" : "original log message"
} < / pre >
< p > extracts the < code > container < / code > and < code > pod < / code > labels ; it sets < code > original log message < / code > as the new log line . < / p >
< p > You can combine the < code > unpack < / code > and < code > json < / code > parsers ( or any other parsers ) if the original embedded log line is of a specific format . < / p >
< / div >
< / div >
<!-- Line format expression -- >
< div class = "introduce-view__content" >
< h2 id = "line-format-expressions" > Line format expression < / h2 >
< div class = "introduce-view__content-label line-module" >
< p v-pre > The line format expression can rewrite the log line content by using the text / template format. It takes a single string parameter < code > | line _format "{{.label_name}}" < / code > , which is the template format . All labels are injected variables into the template and are available to use with the < code > { { . label _name } } < / code > notation . < / p >
< p > For example the following expression : < / p >
< pre v-pre > { container = "frontend" } | logfmt | line_format " {{ .query }} {{ .duration }} " < / pre >
< p > Will extract and rewrite the log line to only contains the query and the duration of a request . < / p >
< p v-pre > You can use double quoted string for the template or backticks < code > ` {{.label_name}} ` < / code > to avoid the need to escape special characters . < / p >
< p > < code > line _format < / code > also supports < code > math < / code > functions . Example : < / p >
< p > If we have the following labels < code > ip = 1.1 .1 .1 < / code > , < code > status = 200 < / code > and < code > duration = 3000 ( ms ) < / code > , we can divide the duration by < code > 1000 < / code > to get the value in seconds . < / p >
< pre v-pre > { container = "frontend" } | logfmt | line_format " {{ .ip }} {{ .status }} {{ div .duration 1000 }} " < / pre >
< p > The above query will give us the < code > line < / code > as < code > 1.1 .1 .1 200 3 < / code > < / p >
< p > See template functions to learn about available functions in the template format . < / p >
< / div >
< / div >
<!-- Labels format expression -- >
< div class = "introduce-view__content" >
< h2 id = "label-format-expressions" > Labels format expression < / h2 >
2021-08-02 19:51:53 +08:00
< div class = "introduce-view__content-label" >
2022-08-26 13:50:04 +08:00
< p > The < code > | label _format < / code > expression can rename , modify or add labels . It takes as parameter a comma separated list of equality operations , enabling multiple operations at once . < / p >
< p > When both side are label identifiers , for example < code > dst = src < / code > , the operation will rename the < code > src < / code > label into < code > dst < / code > . < / p >
2022-08-26 15:05:51 +08:00
< p > The right side can alternatively be a template string ( double quoted or backtick ) , for example dst = "<code v-pre>{{.status}} {{.query}}</code>" , in which case the < code > dst < / code > label value is replaced by the result of the text / template evaluation . This is the same template engine as the < code > | line _format < / code > expression , which means labels are available as variables and you can use the same list of functions . < / p >
2022-08-26 13:50:04 +08:00
< p > In both cases , if the destination label doesn ’ t exist , then a new one is created . < / p >
2022-08-26 15:05:51 +08:00
< p > The renaming form < code > dst = src < / code > will drop the < code > src < / code > label after remapping it to the < code > dst < / code > label . However , the template form will preserve the referenced labels , such that < code v-pre > dst = "{{.src}}" < / code > results in both < code > dst < / code > and < code > src < / code > having the same value . < / p >
2022-08-26 13:50:04 +08:00
< pre > A single label name can only appear once per expression . This means < code > | label _format foo = bar , foo = "new" < / code > is not allowed but you can use two expressions for the desired effect : < code > | label _format foo = bar | label _format foo = "new" < / code > < / pre >
< / div >
< / div >
<!-- Log queries examples -- >
2022-08-31 16:16:21 +08:00
< h1 class = "page-header-two" id = "log-queries-examples" > Log queries examples < / h1 >
2022-08-26 13:50:04 +08:00
<!-- Multiple filtering -- >
< div class = "introduce-view__content" >
2022-08-31 16:16:21 +08:00
< h2 id = "multiple-filtering" > Multiple filtering < / h2 >
2022-08-26 13:50:04 +08:00
< div class = "introduce-view__content-label" >
< p > Filtering should be done first using label matchers , then line filters ( when possible ) and finally using label filters . The following query demonstrate this . < / p >
< p > < code v-pre class = "fillbox" > { cluster = "ops-tools1" , namespace = "loki-dev" , job = "loki-dev/query-frontend" } |= "metrics.go" != "out of order" | logfmt | duration > 30 s or status _code != "200" < / code > < / p >
< / div >
< / div >
<!-- Multiple parsers -- >
< div class = "introduce-view__content" >
< h2 id = "multiple-parsers" > Multiple parsers < / h2 >
< div class = "introduce-view__content-label" >
< p > To extract the method and the path of the following logfmt log line : < / p >
< p > < code v-pre class = "fillbox" > level = debug ts = 2020 - 10 - 02 T10 : 10 : 42.092268913 Z caller = logging . go : 66 traceID = a9d4d8a928d8db1 msg = "POST /api/prom/api/v1/query_range (200) 1.5s" < / code > < / p >
< p > You can use multiple parsers ( logfmt and regexp ) like this . < / p >
< p > < code v-pre class = "fillbox" > { job = "loki-ops/query-frontend" } | logfmt | line _format "{{.msg}}" | regexp "(?P<method>\\w+) (?P<path>[\\w|/]+) \\((?P<status>\\d+?)\\) (?P<duration>.*)" < / code > < / p >
2022-08-26 15:05:51 +08:00
< p > This is possible because the < code > | line _format < / code > reformats the log line to become < code > POST / api / prom / api / v1 / query _range ( 200 ) 1.5 s < / code > which can then be parsed with the < code > | regexp ... < / code > parser . < / p >
2022-08-26 13:50:04 +08:00
< / div >
< / div >
<!-- Formatting -- >
< div class = "introduce-view__content" >
2022-08-31 16:16:21 +08:00
< h2 id = "formatting" > Formatting < / h2 >
2022-08-26 13:50:04 +08:00
< div class = "introduce-view__content-label formatting-module" >
< p > The following query shows how you can reformat a log line to make it easier to read on screen . < / p >
< pre v-pre > { cluster = "ops-tools1" , name = "querier" , namespace = "loki-dev" }
|= "metrics.go" != "loki-canary"
| logfmt
| query != ""
2022-08-31 16:16:21 +08:00
| label _format query = "{{ Replace .query \" & # 92 ; & # 92 ; n & # 92 ; " \" & # 92 ; " -1 }}"
| line _format "{{ .ts}}\t{{.duration}}\ttraceID = {{.traceID}}\t{{ printf \" % - 100.100 s & # 92 ; " .query }} " < / pre >
2022-08-26 13:50:04 +08:00
< p > Label formatting is used to sanitize the query while the line format reduce the amount of information and creates a tabular output . < / p >
< p > For these given log lines : < / p >
< pre class = "box-overflow" > level = info ts = 2020 - 10 - 23 T20 : 32 : 18.094668233 Z caller = metrics . go : 81 org _id = 29 traceID = 1980 d41501b57b68 latency = fast query = "{cluster=\"ops-tools1\", job=\"loki-ops/query-frontend\"} |= \"query_range\"" query _type = filter range _type = range length = 15 m0s step = 7 s duration = 650.22401 ms status = 200 throughput _mb = 1.529717 total _bytes _mb = 0.994659
level = info ts = 2020 - 10 - 23 T20 : 32 : 18.068866235 Z caller = metrics . go : 81 org _id = 29 traceID = 1980 d41501b57b68 latency = fast query = "{cluster=\"ops-tools1\", job=\"loki-ops/query-frontend\"} |= \"query_range\"" query _type = filter range _type = range length = 15 m0s step = 7 s duration = 624.008132 ms status = 200 throughput _mb = 0.693449 total _bytes _mb = 0.432718 < / pre >
< p > The result would be : < / p >
< pre > 2020 - 10 - 23 T20 : 32 : 18.094668233 Z 650.22401 ms traceID = 1980 d41501b57b68 { cluster = "ops-tools1" , job = "loki-ops/query-frontend" } |= "query_range"
2020 - 10 - 23 T20 : 32 : 18.068866235 Z 624.008132 ms traceID = 1980 d41501b57b68 { cluster = "ops-tools1" , job = "loki-ops/query-frontend" } |= "query_range" < / pre >
< / div >
< / div >
<!-- Metric queries -- >
< div class = "introduce-view__content" >
2022-09-01 11:06:16 +08:00
< h1 class = "page-header-one" id = "metric-queries" > Metric queries < / h1 >
2022-08-26 13:50:04 +08:00
< div class = "introduce-view__content-label" >
< p > Metric queries extend log queries by applying a function to log query results . This powerful feature creates metrics from logs . < / p >
< p > Metric queries can be used to calculate the rate of error messages or the top N log sources with the greatest quantity of logs over the last 3 hours . < / p >
< p > Combined with parsers , metric queries can also be used to calculate metrics from a sample value within the log line , such as latency or request size . All labels , including extracted ones , will be available for aggregations and generation of new series . < / p >
< / div >
< / div >
<!-- Range Vector aggregation -- >
< div class = "introduce-view__content" >
2022-08-31 16:16:21 +08:00
< h1 class = "page-header-two" id = "range-vector-aggregation" > Range Vector aggregation < / h1 >
2022-08-26 13:50:04 +08:00
< div class = "introduce-view__content-label" >
< p > LogQL shares the range vector concept of Prometheus . In Grafana Loki , the selected range of samples is a range of selected log or label values . < / p >
< p > The aggregation is applied over a time duration . Loki defines Time Durations with the same syntax as Prometheus . < / p >
< p > Loki supports two types of range vector aggregations : log range aggregations and unwrapped range aggregations . < / p >
< / div >
< / div >
<!-- Log range aggregations -- >
< div class = "introduce-view__content" >
2022-08-31 16:16:21 +08:00
< h2 id = "log-range-aggregations" > Log range aggregations < / h2 >
2022-08-26 13:50:04 +08:00
< div class = "introduce-view__content-label" >
< p > A log range aggregation is a query followed by a duration . A function is applied to aggregate the query over the duration . The duration can be placed after the log stream selector or at end of the log pipeline . < / p >
< p > The functions : < / p >
< ul >
< li > < code > rate ( log - range ) < / code > : calculates the number of entries per second < / li >
< li > < code > count _over _time ( log - range ) < / code > : counts the entries for each log stream within the given range . < / li >
< li > < code > bytes _rate ( log - range ) < / code > : calculates the number of bytes per second for each stream . < / li >
< li > < code > bytes _over _time ( log - range ) < / code > : counts the amount of bytes used by each log stream for a given range . < / li >
< li > < code > absent _over _time ( log - range ) < / code > : returns an empty vector if the range vector passed to it has any elements and a 1 - element vector with the value 1 if the range vector passed to it has no elements . ( < code > absent _over _time < / code > is useful for alerting on when no time series and logs stream exist for label combination for a certain amount of time . ) < / li >
< / ul >
< p > Examples : < / p >
< ul >
< li >
< p > Count all the log lines within the last five minutes for the MySQL job . < / p >
< p > < code class = "fillbox" > count _over _time ( { job = "mysql" } [ 5 m ] ) < / code > < / p >
< / li >
< li >
< p > This aggregation includes filters and parsers . It returns the per - second rate of all non - timeout errors within the last minutes per host for the MySQL job and only includes errors whose duration is above ten seconds . < / p >
< p > < code class = "fillbox" > sum by ( host ) ( rate ( { job = "mysql" } |= "error" != "timeout" | json | duration > 10 s [ 1 m ] ) ) < / code > < / p >
< / li >
< / ul >
< / div >
< / div >
<!-- Unwrapped range aggregations -- >
< div class = "introduce-view__content" >
2022-08-31 16:16:21 +08:00
< h2 id = "unwrapped-range-aggregations" > Unwrapped range aggregations < / h2 >
2022-08-26 13:50:04 +08:00
< div class = "introduce-view__content-label" >
< p > Unwrapped ranges uses extracted labels as sample values instead of log lines . However to select which label will be used within the aggregation , the log query must end with an unwrap expression and optionally a label filter expression to discard errors . < / p >
< p > The unwrap expression is noted < code > | unwrap label _identifier < / code > where the label identifier is the label name to use for extracting sample values . < / p >
< p > Since label values are string , by default a conversion into a float ( 64 bits ) will be attempted , in case of failure the < code > _ _error _ _ < / code > label is added to the sample . Optionally the label identifier can be wrapped by a conversion function < code > | unwrap & lt ; function & gt ; ( label _identifier ) < / code > , which will attempt to convert the label value from a specific format . < / p >
< p > We currently support the functions : < / p >
< ul >
< li > < code > duration _seconds ( label _identifier ) < / code > ( or its short equivalent < code > duration < / code > ) which will convert the label value in seconds from the go duration format ( e . g < code > 5 m < / code > , < code > 24 s30ms < / code > ) . < / li >
< li > < code > bytes ( label _identifier ) < / code > which will convert the label value to raw bytes applying the bytes unit ( e . g . < code > 5 MiB < / code > , < code > 3 k < / code > , < code > 1 G < / code > ) . < / li >
< / ul >
< p > Supported function for operating over unwrapped ranges are : < / p >
< ul >
2022-08-26 15:05:51 +08:00
< li > < code > rate ( unwrapped - range ) < / code > : calculates per second rate of the sum of all values in the specified interval . < / li >
2022-08-26 13:50:04 +08:00
< li > < code > rate _counter ( unwrapped - range ) < / code > : calculates per second rate of the values in the specified interval and treating them as “ counter metric ” < / li >
< li > < code > sum _over _time ( unwrapped - range ) < / code > : the sum of all values in the specified interval . < / li >
< li > < code > avg _over _time ( unwrapped - range ) < / code > : the average value of all points in the specified interval . < / li >
< li > < code > max _over _time ( unwrapped - range ) < / code > : the maximum value of all points in the specified interval . < / li >
< li > < code > min _over _time ( unwrapped - range ) < / code > : the minimum value of all points in the specified interval < / li >
< li > < code > first _over _time ( unwrapped - range ) < / code > : the first value of all points in the specified interval < / li >
< li > < code > last _over _time ( unwrapped - range ) < / code > : the last value of all points in the specified interval < / li >
< li > < code > stdvar _over _time ( unwrapped - range ) < / code > : the population standard variance of the values in the specified interval . < / li >
< li > < code > stddev _over _time ( unwrapped - range ) < / code > : the population standard deviation of the values in the specified interval . < / li >
< li > < code > quantile _over _time ( scalar , unwrapped - range ) < / code > : the φ - quantile ( 0 ≤ φ ≤ 1 ) of the values in the specified interval . < / li >
< li > < code > absent _over _time ( unwrapped - range ) < / code > : returns an empty vector if the range vector passed to it has any elements and a 1 - element vector with the value 1 if the range vector passed to it has no elements . ( < code > absent _over _time < / code > is useful for alerting on when no time series and logs stream exist for label combination for a certain amount of time . ) < / li >
< / ul >
< p > Except for < code > sum _over _time < / code > , < code > absent _over _time < / code > and < code > rate < / code > , unwrapped range aggregations support grouping . < / p >
< p > < code class = "fillbox" > & lt ; aggr - op & gt ; ( [ parameter , ] & lt ; unwrapped - range & gt ; ) [ without | by ( & lt ; label list & gt ; ) ] < / code > < / p >
< p > Which can be used to aggregate over distinct labels dimensions by including a < code > without < / code > or < code > by < / code > clause . < / p >
< p > < code > without < / code > removes the listed labels from the result vector , while all other labels are preserved the output . < code > by < / code > does the opposite and drops labels that are not listed in the < code > by < / code > clause , even if their label values are identical between all elements of the vector . < / p >
< / div >
< / div >
<!-- Unwrapped examples -- >
< div class = "introduce-view__content" >
2022-08-31 16:16:21 +08:00
< h2 id = "unwrapped-examples" > Unwrapped examples < / h2 >
2022-08-26 13:50:04 +08:00
< div class = "introduce-view__content-label unwrapped-module" >
< pre > quantile _over _time ( 0.99 ,
{ cluster = "ops-tools1" , container = "ingress-nginx" }
| json
| _ _error _ _ = ""
| unwrap request _time [ 1 m ] ) by ( path ) < / pre >
< p > This example calculates the p99 of the nginx - ingress latency by path . < / p >
< pre > sum by ( org _id ) (
sum _over _time (
{ cluster = "ops-tools1" , container = "loki-dev" }
|= "metrics.go"
| logfmt
| unwrap bytes _processed [ 1 m ] )
) < / pre >
< p > This calculates the amount of bytes processed per organization ID . < / p >
< / div >
< / div >
<!-- Built - in aggregation operators -- >
< div class = "introduce-view__content" >
2022-08-31 16:16:21 +08:00
< h1 class = "page-header-two" id = "built-in-aggregation-operators" > Built - in aggregation operators < / h1 >
2022-08-26 13:50:04 +08:00
< div class = "introduce-view__content-label" >
< p > Like PromQL , LogQL supports a subset of built - in aggregation operators that can be used to aggregate the element of a single vector , resulting in a new vector of fewer elements but with aggregated values : < / p >
< ul >
< li > < code > sum < / code > : Calculate sum over labels < / li >
< li > < code > avg < / code > : Calculate the average over labels < / li >
< li > < code > min < / code > : Select minimum over labels < / li >
< li > < code > max < / code > : Select maximum over labels < / li >
< li > < code > stddev < / code > : Calculate the population standard deviation over labels < / li >
< li > < code > stdvar < / code > : Calculate the population standard variance over labels < / li >
< li > < code > count < / code > : Count number of elements in the vector < / li >
< li > < code > topk < / code > : Select largest k elements by sample value < / li >
< li > < code > bottomk < / code > : Select smallest k elements by sample value < / li >
< / ul >
< p > The aggregation operators can either be used to aggregate over all label values or a set of distinct label values by including a < code > without < / code > or a < code > by < / code > clause : < / p >
2022-08-26 15:05:51 +08:00
< p > < code class = "fillbox" > & lt ; aggr - op & gt ; ( [ parameter , ] & lt ; vector expression & gt ; ) [ without | by ( & lt ; label list & gt ; ) ] < / code > < / p >
2022-08-26 13:50:04 +08:00
< p > < code > parameter < / code > is required when using < code > topk < / code > and < code > bottomk < / code > . < code > topk < / code > and < code > bottomk < / code > are different from other aggregators in that a subset of the input samples , including the original labels , are returned in the result vector . < / p >
< p > < code > by < / code > and < code > without < / code > are only used to group the input vector . The < code > without < / code > clause removes the listed labels from the resulting vector , keeping all others . The < code > by < / code > clause does the opposite , dropping labels that are not listed in the clause , even if their label values are identical between all elements of the vector . < / p >
< / div >
< / div >
<!-- Vector aggregation examples -- >
< div class = "introduce-view__content" >
2022-08-31 16:16:21 +08:00
< h2 id = "vector-aggregation-examples" > Vector aggregation examples < / h2 >
2022-08-26 13:50:04 +08:00
< div class = "introduce-view__content-label" >
< p > Get the top 10 applications by the highest log throughput : < / p >
< p > < code class = "fillbox" > topk ( 10 , sum ( rate ( { region = "us-east1" } [ 5 m ] ) ) by ( name ) ) < / code > < / p >
< p > Get the count of log lines for the last five minutes for a specified job , grouping by level : < / p >
< p > < code class = "fillbox" > sum ( count _over _time ( { job = "mysql" } [ 5 m ] ) ) by ( level ) < / code > < / p >
< p > Get the rate of HTTP GET requests to the < code > / home < / code > endpoint for NGINX logs by region : < / p >
< p > < code class = "fillbox" > avg ( rate ( ( { job = "nginx" } |= "GET" | json | path = "/home" ) [ 10 s ] ) ) by ( region ) < / code > < / p >
2021-08-02 19:51:53 +08:00
< / div >
< / div >
< / div >
2022-08-31 16:16:21 +08:00
< div class = "catalog" >
< ul >
< li style = "list-style:square;" >
2022-09-01 11:06:16 +08:00
< div @click ="jumpClick('#log-queries')" > < span > Log queries < / span > < / div >
2022-08-31 16:16:21 +08:00
< ul >
2022-09-01 11:06:16 +08:00
< li style = "list-style:disc;" @click ="jumpClick('#log-stream-selector')" > < span > Log stream selector < / span > < / li >
2022-08-31 16:16:21 +08:00
< li style = "list-style:disc;" >
2022-09-01 11:06:16 +08:00
< div @click ="jumpClick('#log-pipeline')" > < span > Log pipeline < / span > < / div >
2022-08-31 16:16:21 +08:00
< ul >
2022-09-01 11:06:16 +08:00
< li @click ="jumpClick('#line-filter-expressions')" > < span > Line filter expression < / span > < / li >
< li @click ="jumpClick('#label-filter-expressions')" > < span > Label filter expression < / span > < / li >
< li @click ="jumpClick('#parsing-expressions')" > < span > Parser expression < / span > < / li >
< li @click ="jumpClick('#JSON')" > < span > JSON < / span > < / li >
< li @click ="jumpClick('#logfmt')" > < span > logfmt < / span > < / li >
< li @click ="jumpClick('#pattern')" > < span > Pattern < / span > < / li >
< li @click ="jumpClick('#regexp')" > < span > Regular expression < / span > < / li >
< li @click ="jumpClick('#unpack')" > < span > unpack < / span > < / li >
< li @click ="jumpClick('#line-format-expressions')" > < span > Line format expression < / span > < / li >
< li @click ="jumpClick('#label-format-expressions')" > < span > Labels format expression < / span > < / li >
2022-08-31 16:16:21 +08:00
< / ul >
< / li >
< li style = "list-style:disc;" >
2022-09-01 11:06:16 +08:00
< div @click ="jumpClick('#log-queries-examples')" > < span > Log queries examples < / span > < / div >
2022-08-31 16:16:21 +08:00
< ul >
2022-09-01 11:06:16 +08:00
< li @click ="jumpClick('#multiple-filtering')" > < span > Multiple filtering < / span > < / li >
< li @click ="jumpClick('#multiple-parsers')" > < span > Multiple parsers < / span > < / li >
< li @click ="jumpClick('#formatting')" > < span > Formatting < / span > < / li >
2022-08-31 16:16:21 +08:00
< / ul >
< / li >
< / ul >
< / li >
< li style = "list-style:square;" >
2022-09-01 11:06:16 +08:00
< div @click ="jumpClick('#metric-queries')" > < span > Metric queries < / span > < / div >
2022-08-31 16:16:21 +08:00
< ul >
< li style = "list-style:disc;" >
2022-09-01 11:06:16 +08:00
< div @click ="jumpClick('#range-vector-aggregation')" > < span > Range Vector aggregation < / span > < / div >
2022-08-31 16:16:21 +08:00
< ul >
2022-09-01 11:06:16 +08:00
< li @click ="jumpClick('#log-range-aggregations')" > < span > Log range aggregations < / span > < / li >
< li @click ="jumpClick('#unwrapped-range-aggregations')" > < span > Unwrapped range aggregations < / span > < / li >
< li @click ="jumpClick('#unwrapped-examples')" > < span > Unwrapped examples < / span > < / li >
2022-08-31 16:16:21 +08:00
< / ul >
< / li >
< li style = "list-style:disc;" >
2022-09-01 11:06:16 +08:00
< div @click ="jumpClick('#built-in-aggregation-operators')" > < span > Built - in aggregation operators < / span > < / div >
< ul > < li @click ="jumpClick('#vector-aggregation-examples')" > < span > Vector aggregation examples < / span > < / li > < / ul >
2022-08-31 16:16:21 +08:00
< / li >
< / ul >
< / li >
< / ul >
< / div >
2021-08-02 19:51:53 +08:00
< / div >
< / div >
< / div >
< / div >
< / div >
< transition name = "right-box" >
2022-03-08 11:35:22 +08:00
< chartRightBox v-if = "rightBox.show" ref="addChartModal" :chart="chartData" :from="$CONSTANTS.fromRoute.explore" :panel-data="panelData" :show-panel="{id: -1, name: '', type: 'explore'}" @close="handleBox(false)" @on-create-success="createSuccess" > < / chartRightBox >
2021-08-02 19:51:53 +08:00
< / transition >
< / div >
< / template >
< script >
import bus from '../../../../libs/bus'
import promqlInput from './promqlInput'
import chart from '../overview/chart'
import axios from 'axios'
import { getUUID } from '../../../common/js/common'
2022-04-12 10:30:33 +08:00
import chartDataFormat from '../../../chart/chartDataFormat'
2021-08-02 19:51:53 +08:00
import logTab from './logTab'
2021-09-28 16:54:07 +08:00
import promqlInputMixin from '@/components/common/mixin/promqlInput'
2021-12-03 16:58:25 +08:00
import chartRightBox from '@/components/common/rightBox/chart/chartRightBox'
2021-08-02 19:51:53 +08:00
export default {
name : 'exploreItem' ,
components : {
'promql-input' : promqlInput ,
2021-12-03 16:58:25 +08:00
chartRightBox ,
2021-08-02 19:51:53 +08:00
chart ,
logTab
} ,
props : {
tabIndex : Number ,
closable : Boolean
} ,
2021-09-28 16:54:07 +08:00
mixins : [ promqlInputMixin ] ,
2021-08-02 19:51:53 +08:00
data ( ) {
return {
2021-09-09 16:00:37 +08:00
chartLoading : false ,
2021-10-28 09:53:24 +08:00
logTabNoData : false ,
2021-08-02 19:51:53 +08:00
rightBox : { // 面板弹出框相关
show : false
} ,
value : true ,
tabPosition : 'none' ,
2021-12-27 15:37:14 +08:00
tableId : 'explore' ,
2021-08-02 19:51:53 +08:00
searchMetrics : [
{
value : 'Metrics' ,
2022-06-21 18:14:21 +08:00
label : this . $t ( 'overall.metric' ) ,
2021-08-25 10:39:26 +08:00
icon : 'nz-icon nz-icon-Metrics'
2021-08-02 19:51:53 +08:00
} ,
{
value : 'Logs' ,
2022-07-12 14:06:14 +08:00
label : this . $t ( 'dashboard.panel.chartForm.typeVal.log.label' ) ,
2021-08-25 10:39:26 +08:00
icon : 'nz-icon nz-icon-logs'
2021-08-02 19:51:53 +08:00
}
] ,
2022-06-21 18:14:21 +08:00
selectValue : this . $t ( 'overall.metric' ) ,
2021-08-25 13:57:04 +08:00
selectIcon : 'nz-icon nz-icon-Metrics' ,
2021-08-02 19:51:53 +08:00
showMetrics : true ,
promqlCount : 1 ,
promqlKeys : [ ] ,
expressions : [ '' ] ,
filterTime : [
2021-12-31 17:14:26 +08:00
bus . timeFormate ( bus . getOffsetTimezoneData ( - 1 ) ) ,
bus . timeFormate ( bus . getOffsetTimezoneData ( ) )
2021-08-02 19:51:53 +08:00
] ,
/* 工具参数 */
tools : {
loading : false , // 是否显示table加载动画
showCustomTableTitle : false , // 自定义列弹框是否显示
customTableTitle : [ ] // 自定义列工具的数据
} ,
tableTitle : [ ] ,
showIntroduce : true ,
defaultChartVisible : true ,
defaultTableVisible : true ,
chartVisible : true ,
tableVisible : true ,
pageObj : {
pageNo : 1 ,
pageSize : this . $CONSTANTS . defaultPageSize ,
total : 0
} ,
tableData : [ ] ,
saveDisabled : true ,
panelData : [ ] ,
chartUnit : 0 ,
historyParam : { useHistory : true , key : 'expore-history' } ,
2022-03-08 11:35:22 +08:00
chartData : { } ,
2021-08-02 19:51:53 +08:00
collapseValue : [ '1' , '2' ] ,
2021-08-04 17:10:35 +08:00
showTab : [ '1' , '2' ] ,
2022-08-05 10:26:43 +08:00
logData : [ ] ,
letter : [
'A' , 'B' , 'C' , 'D' , 'E' , 'F' , 'G' ,
'H' , 'I' , 'J' , 'K' , 'L' , 'M' , 'N' ,
'O' , 'P' , 'Q' , 'R' , 'S' , 'T' ,
'U' , 'V' , 'W' , 'X' , 'Y' , 'Z'
]
2021-08-02 19:51:53 +08:00
}
} ,
created ( ) {
this . getPanelData ( )
2022-07-01 15:42:08 +08:00
this . promqlKeys . push ( {
id : getUUID ( ) ,
2022-07-01 17:34:28 +08:00
state : 1
2022-07-01 15:42:08 +08:00
} )
2021-08-25 10:39:26 +08:00
this . selectMetricsLogs ( )
2021-08-02 19:51:53 +08:00
} ,
methods : {
2022-08-26 13:50:04 +08:00
jumpClick ( id ) {
document . querySelector ( id ) . scrollIntoView ( true )
} ,
2021-08-25 13:57:04 +08:00
selectMetricsLogs ( val , icon , label ) {
if ( val ) {
2021-08-25 10:39:26 +08:00
this . selectIcon = icon
this . selectValue = val
} else {
2021-08-25 13:57:04 +08:00
label = 'Metrics'
2021-08-25 10:39:26 +08:00
}
2021-08-25 13:57:04 +08:00
this . changeType ( label )
2021-08-25 10:39:26 +08:00
} ,
2021-08-02 19:51:53 +08:00
changeType ( value ) {
this . showMetrics = value === 'Metrics'
this . showIntroduce = true
this . resetExpression ( )
} ,
split ( ) {
this . $emit ( 'split' , this . tabIndex )
} ,
pageNo ( val ) {
this . pageObj . pageNo = val
this . tableData = this . filterShowData ( this . storedTableData , this . pageObj )
} ,
pageSize ( val ) {
this . pageObj . pageSize = val
this . tableData = this . filterShowData ( this . storedTableData , this . pageObj )
} ,
filterShowData ( source , pageObj ) {
2022-03-24 09:46:14 +08:00
if ( source ) return source . slice ( ( pageObj . pageNo - 1 ) * pageObj . pageSize , pageObj . pageNo * pageObj . pageSize )
2021-08-02 19:51:53 +08:00
} ,
chartUnitChange ( unit ) {
this . chartUnit = unit
this . $nextTick ( ( ) => {
this . expressionChange ( )
} )
} ,
exportLog ( { limit , descending } ) {
const params = {
2021-08-05 12:42:18 +08:00
logql : this . expressions ,
2021-12-31 17:14:26 +08:00
start : this . $stringTimeParseToUnix ( bus . formateTimeToTime ( this . filterTime [ 0 ] ) ) ,
end : this . $stringTimeParseToUnix ( bus . formateTimeToTime ( this . filterTime [ 1 ] ) ) ,
2021-08-02 19:51:53 +08:00
direction : descending ? 'backward' : 'forward' ,
limit
}
axios . get ( '/logs/loki/export' , { responseType : 'blob' , params : params } ) . then ( res => {
if ( window . navigator . msSaveOrOpenBlob ) {
// 兼容ie11
const blobObject = new Blob ( [ res . data ] )
window . navigator . msSaveOrOpenBlob ( blobObject , 'log' )
} else {
const url = URL . createObjectURL ( new Blob ( [ res . data ] ) )
const a = document . createElement ( 'a' )
document . body . appendChild ( a ) // 此处增加了将创建的添加到body当中
a . href = url
a . download = 'log'
a . target = '_blank'
a . click ( )
a . remove ( ) // 将a标签移除
}
} , error => {
const $self = this
const reader = new FileReader ( )
reader . onload = function ( event ) {
const responseText = reader . result
const exception = JSON . parse ( responseText )
if ( exception . message ) {
$self . $message . error ( exception . message )
} else {
console . error ( error )
}
}
reader . readAsText ( error . response . data )
} )
} ,
2021-08-04 19:22:32 +08:00
queryLogData ( limit ) { // log的chart和table是一个请求
2021-09-09 16:00:37 +08:00
this . chartLoading = true
2021-08-04 19:22:32 +08:00
if ( ! limit ) {
2021-09-23 10:51:53 +08:00
limit = this . $refs . logDetail ? this . $refs . logDetail . getLimit ( ) : 100
2021-08-04 19:22:32 +08:00
}
2021-11-27 19:28:01 +08:00
this . $refs . logDetail && this . $refs . logDetail . resetOperation ( )
2021-08-02 19:51:53 +08:00
if ( this . expressions . length > 0 ) {
const requestArr = [ ]
2022-07-01 17:34:28 +08:00
// 过滤掉state为0的元素
2021-08-02 19:51:53 +08:00
this . expressions . forEach ( ( item , index ) => {
2022-07-01 17:34:28 +08:00
if ( item != '' && this . promqlKeys [ index ] . state ) {
2021-12-31 17:14:26 +08:00
requestArr . push ( this . $get ( '/logs/loki/api/v1/query_range?format=1&query=' + encodeURIComponent ( item ) + '&start=' + this . $stringTimeParseToUnix ( bus . formateTimeToTime ( this . filterTime [ 0 ] ) ) + '&end=' + this . $stringTimeParseToUnix ( bus . formateTimeToTime ( this . filterTime [ 1 ] ) ) + '&limit=' + limit ) )
2021-08-02 19:51:53 +08:00
}
} )
if ( requestArr . length > 0 ) {
this . showIntroduce = false
this . saveDisabled = false
}
axios . all ( requestArr ) . then ( res => {
2021-09-09 16:00:37 +08:00
this . chartLoading = false
2021-08-21 17:34:01 +08:00
const errorRowIndex = [ ]
res . forEach ( ( r , i ) => {
if ( typeof r === 'string' ) {
errorRowIndex . push ( i )
2021-08-04 17:10:35 +08:00
}
2021-08-21 17:34:01 +08:00
} )
if ( errorRowIndex . length > 0 ) {
this . $message . error ( this . $t ( 'tip.errorInRow' ) + ': ' + errorRowIndex . map ( e => e + 1 ) . join ( ' ,' ) )
res = res . filter ( ( r , i ) => errorRowIndex . indexOf ( i ) === - 1 )
2021-08-04 17:10:35 +08:00
}
2021-08-21 17:34:01 +08:00
if ( res . length > 0 ) {
2021-08-24 18:33:32 +08:00
const logData = res . map ( r => r . data )
2021-10-28 09:53:24 +08:00
if ( logData [ 0 ] . result . length > 0 ) {
this . logTabNoData = false
} else {
this . logTabNoData = true
}
2021-08-24 18:33:32 +08:00
const hasGraph = logData . some ( d => d . resultType === 'matrix' )
const hasLog = logData . some ( d => d . resultType === 'streamsFormat' )
2021-08-21 17:34:01 +08:00
const graphTabIndex = this . showTab . indexOf ( '1' )
if ( hasGraph ) {
if ( graphTabIndex === - 1 ) {
this . showTab . push ( '1' )
}
} else {
if ( graphTabIndex > - 1 ) {
this . showTab . splice ( graphTabIndex , 1 )
}
2021-08-04 17:10:35 +08:00
}
2021-08-21 17:34:01 +08:00
const logTabIndex = this . showTab . indexOf ( '2' )
if ( hasLog ) {
if ( logTabIndex === - 1 ) {
2021-08-24 18:33:32 +08:00
this . showTab . push ( '2' )
2021-08-21 17:34:01 +08:00
}
} else {
if ( logTabIndex > - 1 ) {
this . showTab . splice ( logTabIndex , 1 )
}
2021-08-04 17:10:35 +08:00
}
2021-08-21 17:34:01 +08:00
this . $nextTick ( ( ) => {
2021-08-24 18:33:32 +08:00
this . logData = logData
2021-09-02 17:31:03 +08:00
hasGraph && this . loadLogGraph ( )
2021-08-21 17:34:01 +08:00
} )
2021-08-04 17:10:35 +08:00
}
2021-08-21 17:34:01 +08:00
} ) . catch ( e => {
2021-09-09 16:00:37 +08:00
this . chartLoading = false
2021-08-21 17:34:01 +08:00
this . $message . error ( this . $t ( 'terminallog.statusItem.unknownError' ) )
2021-08-02 19:51:53 +08:00
} )
}
} ,
loadLogGraph ( ) {
const graphData = this . logData . filter ( l => l . resultType === 'matrix' )
if ( graphData && graphData . length > 0 ) {
this . $refs . logChart . startLoading ( )
const promqlInputIndexs = [ ]
const queryExpression = [ ]
2021-09-03 16:30:55 +08:00
let series = [ ]
2021-08-02 19:51:53 +08:00
const legend = [ ]
this . expressions . forEach ( ( item , index ) => {
if ( item !== '' ) {
promqlInputIndexs . push ( index )
queryExpression . push ( item )
}
2021-09-02 17:31:03 +08:00
} )
this . logData . forEach ( ( response , index ) => {
if ( response . resultType === 'matrix' ) {
const promqlIndex = promqlInputIndexs [ index ]
const data = response . result
if ( ! data || data . length < 1 ) {
return
}
data . forEach ( ( result , i ) => {
const seriesItem = {
name : '' ,
symbol : 'emptyCircle' , // 去掉点
symbolSize : [ 2 , 2 ] ,
showSymbol : false ,
smooth : 0.2 , // 曲线变平滑
data : [ ] ,
lineStyle : {
width : 1 ,
opacity : 0.9
} ,
type : 'line'
2021-08-02 19:51:53 +08:00
}
2021-09-02 17:31:03 +08:00
let legendName = ''
seriesItem . data = result . values . map ( ( item ) => {
return [ item [ 0 ] * 1000 , item [ 1 ] ]
2021-08-02 19:51:53 +08:00
} )
2021-09-02 17:31:03 +08:00
if ( result . metric && Object . keys ( result . metric ) . length > 0 ) {
const metric = Object . assign ( { } , result . metric )
seriesItem . name += metric . _ _name _ _ ? metric . _ _name _ _ : ''
seriesItem . name += '{'
delete metric . _ _name _ _
for ( const key in metric ) {
seriesItem . name += key + '=' + '"' + metric [ key ] + '",'
}
legendName = seriesItem . name . substr ( 0 , seriesItem . name . length - 1 )
legendName += '}'
} else {
legendName = queryExpression [ index ]
}
seriesItem . name = legendName + '-' + index
series . push ( seriesItem )
legend . push ( { name : seriesItem . name , alias : legendName , isGray : false } )
} )
2021-08-02 19:51:53 +08:00
2021-09-02 17:31:03 +08:00
this . $refs [ 'promql-' + promqlIndex ] [ 0 ] . setError ( '' )
}
2021-08-02 19:51:53 +08:00
} )
this . defaultChartVisible = true
this . $nextTick ( ( ) => {
2021-09-02 17:31:03 +08:00
this . $refs . logChart . setLegend ( legend )
this . $refs . logChart . setRandomColors ( series . length )
2021-09-03 16:30:55 +08:00
if ( ! series . length ) {
series = ''
}
2021-09-02 17:31:03 +08:00
this . $refs . logChart . setSeries ( series )
2021-08-02 19:51:53 +08:00
this . $refs . logChart . endLoading ( )
} )
}
} ,
queryChartData ( ) {
this . $refs . exploreChart . startLoading ( )
2022-07-01 15:42:08 +08:00
if ( this . expressions . length > 0 ) {
const requestArr = [ ]
const promqlInputIndexs = [ ]
const queryExpression = [ ]
2022-07-01 17:34:28 +08:00
// 过滤掉state为0的元素
2022-07-01 15:42:08 +08:00
this . expressions . forEach ( ( item , index ) => {
2022-07-01 17:34:28 +08:00
if ( item != '' && this . promqlKeys [ index ] . state ) {
2022-07-01 15:42:08 +08:00
const step = bus . getStep ( bus . formateTimeToTime ( this . filterTime [ 0 ] ) , bus . formateTimeToTime ( this . filterTime [ 1 ] ) )
promqlInputIndexs . push ( index )
queryExpression . push ( item )
requestArr . push ( this . $get ( '/prom/api/v1/query_range?query=' + encodeURIComponent ( item ) + '&start=' + this . $stringTimeParseToUnix ( bus . formateTimeToTime ( this . filterTime [ 0 ] ) ) + '&end=' + this . $stringTimeParseToUnix ( bus . formateTimeToTime ( this . filterTime [ 1 ] ) ) + '&step=' + step + '&nullType=null' ) )
2021-08-02 19:51:53 +08:00
}
2022-07-01 15:42:08 +08:00
} )
if ( requestArr . length > 0 ) {
this . showIntroduce = false
this . saveDisabled = false
}
axios . all ( requestArr ) . then ( res => {
let series = [ ]
const legend = [ ]
if ( res . length > 0 ) {
res . forEach ( ( response , index ) => {
const promqlIndex = promqlInputIndexs [ index ]
if ( response . data && response . status == 'success' ) {
const data = response . data . result
if ( ( ! data || data . length < 1 ) && response . message ) {
this . $refs [ 'promql-' + promqlIndex ] [ 0 ] . setError ( response . message )
return
}
data . forEach ( ( result , i ) => {
const seriesItem = {
name : '' ,
symbol : 'emptyCircle' , // 去掉点
symbolSize : [ 2 , 2 ] ,
showSymbol : false ,
smooth : 0.2 , // 曲线变平滑
data : [ ] ,
lineStyle : {
width : 1 ,
opacity : 0.9
} ,
type : 'line'
2021-08-02 19:51:53 +08:00
}
2022-07-01 15:42:08 +08:00
let legendName = ''
seriesItem . data = result . values . map ( ( item ) => {
return [ item [ 0 ] * 1000 , item [ 1 ] ]
2021-08-02 19:51:53 +08:00
} )
2022-07-01 15:42:08 +08:00
if ( result . metric && Object . keys ( result . metric ) . length > 0 ) {
const metric = Object . assign ( { } , result . metric )
seriesItem . name += metric . _ _name _ _ ? metric . _ _name _ _ : ''
seriesItem . name += '{'
delete metric . _ _name _ _
for ( const key in metric ) {
seriesItem . name += key + '=' + '"' + metric [ key ] + '",'
}
legendName = seriesItem . name . substr ( 0 , seriesItem . name . length - 1 )
legendName += '}'
2021-11-22 15:39:30 +08:00
} else {
2022-07-01 15:42:08 +08:00
legendName = queryExpression [ index ]
2021-11-22 15:39:30 +08:00
}
2022-07-01 15:42:08 +08:00
seriesItem . name = legendName + '-' + index
series . push ( seriesItem )
legend . push ( { name : seriesItem . name , alias : legendName , isGray : false } )
} )
2021-08-02 19:51:53 +08:00
2022-07-01 15:42:08 +08:00
this . $refs [ 'promql-' + promqlIndex ] [ 0 ] . setError ( '' )
} else {
if ( response . error ) {
this . $refs [ 'promql-' + promqlIndex ] [ 0 ] . setError ( response . error )
} else {
this . $refs [ 'promql-' + promqlIndex ] [ 0 ] . setError ( response )
}
2021-09-03 16:30:55 +08:00
}
2022-07-01 15:42:08 +08:00
} )
this . $refs . exploreChart . setLegend ( legend )
this . $refs . exploreChart . setRandomColors ( series . length )
if ( ! series . length ) {
series = ''
2021-08-02 19:51:53 +08:00
}
2022-07-01 15:42:08 +08:00
this . $refs . exploreChart . setSeries ( series )
this . defaultChartVisible = true
}
this . $refs . exploreChart . endLoading ( )
} )
}
2021-08-02 19:51:53 +08:00
} ,
queryTableData ( ) {
this . tools . loading = true
2022-07-01 15:42:08 +08:00
if ( this . expressions . length > 0 ) {
const requestArr = [ ]
this . expressions . forEach ( ( item , index ) => {
2022-07-01 17:34:28 +08:00
// 过滤掉state为0的元素
if ( item !== '' && this . promqlKeys [ index ] . state ) {
2022-07-01 15:42:08 +08:00
requestArr . push ( this . $get ( '/prom/api/v1/query?query=' + encodeURIComponent ( item ) ) )
2021-08-02 19:51:53 +08:00
}
2022-07-01 15:42:08 +08:00
} )
if ( requestArr . length > 0 ) {
this . showIntroduce = false
}
axios . all ( requestArr ) . then ( res => {
const tData = [ ]
let tLabels = [ ]
if ( res . length > 0 ) {
this . tableData = [ ]
this . tableTitle = [ ]
res . forEach ( ( response , index ) => {
if ( response . data && response . status === 'success' ) {
const data = response . data . result
if ( data ) {
data . forEach ( ( result , i ) => {
const metrics = Object . assign ( { } , result . metric )
if ( ! Array . isArray ( result . value ) ) {
const val = result
result = {
value : [ '' , val ]
2021-08-02 19:51:53 +08:00
}
2022-07-01 15:42:08 +08:00
}
this . $set ( metrics , 'value#' + index , chartDataFormat . getUnit ( this . chartUnit || 2 ) . compute ( result . value [ 1 ] , null , 2 ) )
2021-12-27 17:59:57 +08:00
2022-07-01 15:42:08 +08:00
this . $set ( metrics , 'time' , bus . timeFormate ( bus . computeTimezone ( result . value [ 0 ] * 1000 ) ) )
for ( const key in metrics ) {
const label = {
label : key ,
prop : key ,
show : true
2021-08-02 19:51:53 +08:00
}
2022-07-01 15:42:08 +08:00
const temp = tLabels . find ( ( item , index ) => {
return item . prop == label . prop
} )
if ( ! temp ) {
tLabels . push ( label )
}
}
tData . push ( metrics )
} )
2021-08-02 19:51:53 +08:00
}
}
2022-07-01 15:42:08 +08:00
tLabels . sort ( ( a , b ) => {
return a . prop . charCodeAt ( 0 ) - b . prop . charCodeAt ( 0 )
} )
tLabels = tLabels . filter ( label => label . prop !== 'time' )
tLabels . unshift ( {
label : this . $t ( 'overall.time' ) ,
prop : 'time' ,
show : true
} )
const filterArr = [ 'alertname' , 'severity_id' , 'severity' , 'rule_type' , 'asset_id' , 'endpoint_id' , 'project_id' , 'datacenter_id' , 'module_id' , 'nz_agent_id' , 'parent_asset_id' ]
tLabels . forEach ( tLabel => {
if ( filterArr . indexOf ( tLabel . prop ) !== - 1 ) {
tLabel . show = false
}
} )
} )
if ( tData . length > 0 ) {
this . storedTableData = Object . assign ( [ ] , tData )
this . pageObj . total = this . storedTableData . length
this . tableData = this . filterShowData ( this . storedTableData , this . pageObj )
this . tableTitle = Object . assign ( [ ] , tLabels )
this . tools . customTableTitle = Object . assign ( [ ] , tLabels )
this . defaultTableVisible = true
} else {
// this.defaultTableVisible = false;
this . pageObj . total = 0
this . pageObj . pageNo = 1
2021-08-02 19:51:53 +08:00
}
2022-07-01 15:42:08 +08:00
}
this . tools . loading = false
} )
}
2021-08-02 19:51:53 +08:00
} ,
expressionChange ( ) {
const nowTimeType = this . $refs . pickTime . $refs . timePicker . nowTimeType
2021-11-22 14:42:17 +08:00
this . pageObj . pageNo = 1
2021-08-02 19:51:53 +08:00
this . setSearchTime ( nowTimeType . type , nowTimeType . value )
if ( this . showMetrics ) {
if ( this . expressions && this . expressions . length >= 1 ) {
this . queryTableData ( )
this . queryChartData ( )
this . storeHistory ( )
}
} else {
if ( this . expressions && this . expressions . length >= 1 ) {
this . queryLogData ( )
}
}
} ,
setSearchTime ( type , val ) { // 设置searchTime
if ( type === 'minute' ) {
2021-12-31 17:14:26 +08:00
const startTime = bus . timeFormate ( new Date ( bus . computeTimezone ( new Date ( ) . getTime ( ) ) ) . setMinutes ( new Date ( bus . computeTimezone ( new Date ( ) . getTime ( ) ) ) . getMinutes ( ) - val ) )
const endTime = bus . timeFormate ( new Date ( bus . computeTimezone ( new Date ( ) . getTime ( ) ) ) )
2021-08-02 19:51:53 +08:00
this . $set ( this . filterTime , 0 , startTime )
this . $set ( this . filterTime , 1 , endTime )
this . $set ( this . filterTime , 2 , val + 'm' )
} else if ( type === 'hour' ) {
2021-12-31 17:14:26 +08:00
const startTime = bus . timeFormate ( new Date ( bus . computeTimezone ( new Date ( ) . getTime ( ) ) ) . setHours ( new Date ( bus . computeTimezone ( new Date ( ) . getTime ( ) ) ) . getHours ( ) - val ) )
const endTime = bus . timeFormate ( new Date ( bus . computeTimezone ( new Date ( ) . getTime ( ) ) ) )
2021-08-02 19:51:53 +08:00
this . $set ( this . filterTime , 0 , startTime )
this . $set ( this . filterTime , 1 , endTime )
this . $set ( this . filterTime , 2 , val + 'h' )
} else if ( type === 'date' ) {
2021-12-31 17:14:26 +08:00
const startTime = bus . timeFormate ( new Date ( bus . computeTimezone ( new Date ( ) . getTime ( ) ) ) . setDate ( new Date ( bus . computeTimezone ( new Date ( ) . getTime ( ) ) ) . getDate ( ) - val ) )
const endTime = bus . timeFormate ( new Date ( bus . computeTimezone ( new Date ( ) . getTime ( ) ) ) )
2021-08-02 19:51:53 +08:00
this . $set ( this . filterTime , 0 , startTime )
this . $set ( this . filterTime , 1 , endTime )
this . $set ( this . filterTime , 2 , val + 'd' )
}
this . $refs . pickTime . $refs . timePicker . searchTime = this . filterTime
} ,
storeHistory ( ) {
const expire = 24
const historyJson = localStorage . getItem ( this . historyParam . key )
2022-07-01 17:34:28 +08:00
// 过滤掉state为0的元素
2022-07-01 15:42:08 +08:00
const expressions = this . expressions . filter ( ( item , index ) => {
2022-07-01 17:34:28 +08:00
return item && item != '' && this . promqlKeys [ index ] . state
2021-08-02 19:51:53 +08:00
} )
2021-11-26 18:47:00 +08:00
const username = localStorage . getItem ( 'nz-username' )
2021-08-02 19:51:53 +08:00
if ( historyJson && historyJson != 'undefined' && historyJson != '' ) {
const historyObj = JSON . parse ( historyJson )
let history = historyObj [ username ]
if ( history ) {
// 过滤过期表达式
history = history . filter ( item => {
return item . time + item . expire >= new Date ( ) . getTime ( )
} )
let repeat = history . filter ( item => {
return expressions . includes ( item . insertText )
} )
const old = history . filter ( item => {
return ! expressions . includes ( item . insertText )
} )
const freshExpression = expressions . filter ( item => {
const find = history . find ( t => { return t . insertText == item } )
return ! find
} )
repeat = repeat . map ( item => {
item . time = new Date ( ) . getTime ( )
item . num += 1
item . documentation = this . $t ( 'dashboard.metricPreview.historyTip' , { time : item . num , hour : 24 } )
return item
} )
const fresh = freshExpression . map ( item => {
return {
label : item ,
insertText : item ,
documentation : this . $t ( 'dashboard.metricPreview.historyTip' , { time : 1 , hour : 24 } ) ,
num : 1 ,
time : new Date ( ) . getTime ( ) ,
expire : expire * 60 * 60 * 1000
}
} )
historyObj [ username ] = fresh . concat ( repeat ) . concat ( old )
} else {
const history = expressions . map ( item => {
return {
label : item ,
insertText : item ,
documentation : this . $t ( 'dashboard.metricPreview.historyTip' , { time : 1 , hour : 24 } ) ,
num : 1 ,
time : new Date ( ) . getTime ( ) ,
expire : expire * 60 * 60 * 1000
}
} )
historyObj [ username ] = history
}
localStorage . setItem ( this . historyParam . key , JSON . stringify ( historyObj ) )
} else {
const history = expressions . map ( item => {
return {
label : item ,
insertText : item ,
documentation : this . $t ( 'dashboard.metricPreview.historyTip' , { time : 1 , hour : 24 } ) ,
num : 1 ,
time : new Date ( ) . getTime ( ) ,
expire : expire * 60 * 60 * 1000
}
} )
if ( history && history . length > 0 ) {
const stored = { }
stored [ username ] = history
localStorage . setItem ( this . historyParam . key , JSON . stringify ( stored ) )
}
}
} ,
2022-07-01 15:42:08 +08:00
// 禁用启用表达式
enableExpression ( index ) {
2022-07-01 17:34:28 +08:00
this . promqlKeys [ index ] . state = this . promqlKeys [ index ] . state ? 0 : 1
2022-07-01 15:42:08 +08:00
const temp = this . expressions . some ( ( item , index ) => {
2022-07-01 17:34:28 +08:00
return item != '' && this . promqlKeys [ index ] . state
2022-07-01 15:42:08 +08:00
} )
if ( ! temp ) {
this . showIntroduce = true
}
this . expressionChange ( )
} ,
2021-08-02 19:51:53 +08:00
addExpression ( index ) {
this . expressions . splice ( index + 1 , 0 , '' )
2022-07-01 15:42:08 +08:00
this . promqlKeys . splice ( index + 1 , 0 , {
id : getUUID ( ) ,
2022-07-01 17:34:28 +08:00
state : 1
2022-07-01 15:42:08 +08:00
} )
2021-08-02 19:51:53 +08:00
this . promqlCount ++
} ,
copyExpression ( index ) {
this . expressions . push ( this . expressions [ index ] )
2022-07-01 15:42:08 +08:00
this . promqlKeys . push ( {
id : getUUID ( ) ,
2022-07-01 17:34:28 +08:00
state : 1
2022-07-01 15:42:08 +08:00
} )
2021-08-02 19:51:53 +08:00
this . promqlCount ++
} ,
removeExpression ( index ) {
if ( this . promqlCount > 1 ) {
this . expressions . splice ( index , 1 )
this . promqlKeys . splice ( index , 1 )
this . promqlCount --
}
} ,
resetExpression ( ) {
this . expressions = [ '' ]
2022-07-01 15:42:08 +08:00
this . promqlKeys = [ {
id : getUUID ( ) ,
2022-07-01 17:34:28 +08:00
state : 1
2022-07-01 15:42:08 +08:00
} ]
2021-08-02 19:51:53 +08:00
this . promqlCount = 1
} ,
changeChartVisible ( ) {
this . chartVisible = ! this . chartVisible
} ,
changeTableVisible ( ) {
this . tableVisible = ! this . tableVisible
} ,
handleBox ( show ) {
this . rightBox . show = show
} ,
saveChart ( ) {
const chart = {
2021-12-03 16:58:25 +08:00
id : '' ,
2021-08-02 19:51:53 +08:00
name : '' ,
2021-12-03 16:58:25 +08:00
panelName : '' ,
2021-08-02 19:51:53 +08:00
type : 'line' ,
2021-12-03 16:58:25 +08:00
span : 4 ,
2022-03-08 11:35:22 +08:00
datasource : 'metrics' ,
2021-12-03 16:58:25 +08:00
height : 4 ,
unit : 2 ,
2021-08-02 19:51:53 +08:00
param : {
2021-12-03 16:58:25 +08:00
stack : 0 ,
nullType : 'null' ,
legend : { placement : 'bottom' , values : [ ] , show : true } ,
thresholdShow : true ,
thresholds : [ { value : undefined , color : '#eeeeeeff' } ]
2021-08-02 19:51:53 +08:00
} ,
elements : [ ] ,
panel : '' ,
sync : 0 ,
2021-12-03 16:58:25 +08:00
remark : '' ,
groupId : - 1
2021-08-02 19:51:53 +08:00
}
2021-12-03 16:58:25 +08:00
this . expressions . forEach ( ( exp , index ) => {
2022-08-05 10:26:43 +08:00
chart . elements . push ( { state : this . promqlKeys [ index ] . state , expression : exp , legend : '' , type : 'expert' , id : '' , name : this . transformNumToLetter ( index ) } )
2021-08-02 19:51:53 +08:00
} )
2022-03-08 11:35:22 +08:00
this . chartData = chart
2021-08-02 19:51:53 +08:00
this . rightBox . show = true
} ,
2022-05-19 15:42:06 +08:00
saveChartLogs ( ) {
const chart = {
id : '' ,
name : '' ,
panelName : '' ,
span : 4 ,
height : 4 ,
unit : 2 ,
groupId : - 1 ,
updateBy : 1 ,
updateAt : '2022-05-18 07:51:45' ,
type : 'log' ,
weight : 6 ,
param : { limit : 100 } ,
pid : null ,
buildIn : 0 ,
seq : null ,
x : 0 ,
y : 1.93 ,
elements : [ ] ,
children : null ,
chartNums : null ,
asset : null ,
varType : null ,
varId : null ,
varName : null ,
datasource : 'logs' ,
enable : { thresholds : false , legend : true , valueMapping : false } ,
sync : 0 ,
remark : ''
}
this . expressions . forEach ( ( exp , index ) => {
2022-08-05 10:26:43 +08:00
chart . elements . push ( { state : this . promqlKeys [ index ] . state , expression : exp , legend : '' , type : 'expert' , id : '' , name : this . transformNumToLetter ( index ) } )
2022-05-19 15:42:06 +08:00
} )
this . chartData = chart
this . rightBox . show = true
} ,
2022-03-18 09:39:04 +08:00
createSuccess ( panel ) { // 添加chart成功
2021-08-02 19:51:53 +08:00
this . $confirm ( this . $t ( 'dashboard.metric.goPanelTip' ) , this . $t ( 'tip.saveSuccess' ) , {
confirmButtonText : this . $t ( 'tip.yes' ) ,
cancelButtonText : this . $t ( 'tip.no' ) ,
type : 'success'
} ) . then ( ( ) => {
bus . $emit ( 'menu-change' , 'panel' )
this . $store . commit ( 'panelShowPanelChange' , panel )
this . $router . push ( {
path : '/panel' ,
query : {
t : + new Date ( )
}
} )
} )
} ,
getPanelData ( ) { // 获取panel数据
this . $get ( 'visual/panel?pageNo=1&pageSize=-1' ) . then ( response => {
if ( response . code === 200 ) {
this . panelData = response . data . list
}
} )
} ,
jumpTo ( data , id ) {
bus . $emit ( 'menu-change' , data )
this . $router . push ( {
path : '/' + data ,
query : {
t : + new Date ( )
}
} )
} ,
2021-09-13 15:06:54 +08:00
logsCollapseChange ( activeNames , a , b ) {
this . $nextTick ( ( ) => {
if ( this . $refs . exploreChart ) {
this . $refs . exploreChart . resize ( )
}
if ( this . $refs . logChart ) {
this . $refs . logChart . resize ( )
}
if ( this . $refs . logDetail ) {
this . $refs . logDetail . myChart . resize ( )
}
} )
2021-12-27 15:37:14 +08:00
} ,
updateCustomTableTitle ( custom ) {
this . tools . customTableTitle = custom
this . $refs . exploreTable . doLayout ( )
2022-08-05 10:26:43 +08:00
} ,
transformNumToLetter ( num ) { // 相当于26进制 获取idaddExpression
const self = this
let letter = ''
const loopNum = parseInt ( num / 26 )
if ( loopNum > 0 ) {
letter += this . transformNumToLetter ( loopNum - 1 )
}
letter += self . letter [ num % 26 ]
return letter
2021-08-02 19:51:53 +08:00
}
} ,
watch : {
promqlCount ( n , o ) {
this . expressionChange ( )
} ,
showMetrics ( n , o ) { // 更换type后, 将折叠面板都置为展开状态
if ( n !== o ) {
this . collapseValue = [ '1' , '2' ]
}
} ,
expressions : {
immediate : true ,
handler ( n , o ) {
2022-07-01 15:42:08 +08:00
// if (n.length == 1 && (!n[0] || n[0] == '')) {
// this.showIntroduce = true
// } else if (n.length > 1) {
// const temp = n.find((item, index) => {
// return item != ''
// })
// if (!temp) {
// this.showIntroduce = true
// } else {
// // this.showIntroduce = false
// }
// } else {
// // this.showIntroduce = false
// }
const temp = n . some ( ( item , index ) => {
2022-07-01 17:34:28 +08:00
return item != '' && this . promqlKeys [ index ] . state
2022-07-01 15:42:08 +08:00
} )
if ( ! temp ) {
2021-08-02 19:51:53 +08:00
this . showIntroduce = true
}
}
}
}
}
< / script >