Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
N
nexpie-grafana-theme
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Registry
Registry
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Kornkitt Poolsup
nexpie-grafana-theme
Commits
23202ab1
Commit
23202ab1
authored
Jan 22, 2019
by
Hugo Häggmark
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Refactored out LogRow to a separate file
parent
9c8dea06
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
216 additions
and
215 deletions
+216
-215
public/app/core/logs_model.ts
+10
-10
public/app/features/explore/LogLabels.tsx
+3
-3
public/app/features/explore/LogRow.tsx
+193
-0
public/app/features/explore/Logs.tsx
+6
-198
public/app/plugins/datasource/loki/result_transformer.ts
+4
-4
No files found.
public/app/core/logs_model.ts
View file @
23202ab1
...
@@ -42,7 +42,7 @@ export interface LogSearchMatch {
...
@@ -42,7 +42,7 @@ export interface LogSearchMatch {
text
:
string
;
text
:
string
;
}
}
export
interface
LogRow
{
export
interface
LogRow
Model
{
duplicates
?:
number
;
duplicates
?:
number
;
entry
:
string
;
entry
:
string
;
key
:
string
;
// timestamp + labels
key
:
string
;
// timestamp + labels
...
@@ -78,7 +78,7 @@ export interface LogsMetaItem {
...
@@ -78,7 +78,7 @@ export interface LogsMetaItem {
export
interface
LogsModel
{
export
interface
LogsModel
{
id
:
string
;
// Identify one logs result from another
id
:
string
;
// Identify one logs result from another
meta
?:
LogsMetaItem
[];
meta
?:
LogsMetaItem
[];
rows
:
LogRow
[];
rows
:
LogRow
Model
[];
series
?:
TimeSeries
[];
series
?:
TimeSeries
[];
}
}
...
@@ -188,13 +188,13 @@ export const LogsParsers: { [name: string]: LogsParser } = {
...
@@ -188,13 +188,13 @@ export const LogsParsers: { [name: string]: LogsParser } = {
},
},
};
};
export
function
calculateFieldStats
(
rows
:
LogRow
[],
extractor
:
RegExp
):
LogsLabelStat
[]
{
export
function
calculateFieldStats
(
rows
:
LogRow
Model
[],
extractor
:
RegExp
):
LogsLabelStat
[]
{
// Consider only rows that satisfy the matcher
// Consider only rows that satisfy the matcher
const
rowsWithField
=
rows
.
filter
(
row
=>
extractor
.
test
(
row
.
entry
));
const
rowsWithField
=
rows
.
filter
(
row
=>
extractor
.
test
(
row
.
entry
));
const
rowCount
=
rowsWithField
.
length
;
const
rowCount
=
rowsWithField
.
length
;
// Get field value counts for eligible rows
// Get field value counts for eligible rows
const
countsByValue
=
_
.
countBy
(
rowsWithField
,
row
=>
(
row
as
LogRow
).
entry
.
match
(
extractor
)[
1
]);
const
countsByValue
=
_
.
countBy
(
rowsWithField
,
row
=>
(
row
as
LogRow
Model
).
entry
.
match
(
extractor
)[
1
]);
const
sortedCounts
=
_
.
chain
(
countsByValue
)
const
sortedCounts
=
_
.
chain
(
countsByValue
)
.
map
((
count
,
value
)
=>
({
count
,
value
,
proportion
:
count
/
rowCount
}))
.
map
((
count
,
value
)
=>
({
count
,
value
,
proportion
:
count
/
rowCount
}))
.
sortBy
(
'count'
)
.
sortBy
(
'count'
)
...
@@ -204,13 +204,13 @@ export function calculateFieldStats(rows: LogRow[], extractor: RegExp): LogsLabe
...
@@ -204,13 +204,13 @@ export function calculateFieldStats(rows: LogRow[], extractor: RegExp): LogsLabe
return
sortedCounts
;
return
sortedCounts
;
}
}
export
function
calculateLogsLabelStats
(
rows
:
LogRow
[],
label
:
string
):
LogsLabelStat
[]
{
export
function
calculateLogsLabelStats
(
rows
:
LogRow
Model
[],
label
:
string
):
LogsLabelStat
[]
{
// Consider only rows that have the given label
// Consider only rows that have the given label
const
rowsWithLabel
=
rows
.
filter
(
row
=>
row
.
labels
[
label
]
!==
undefined
);
const
rowsWithLabel
=
rows
.
filter
(
row
=>
row
.
labels
[
label
]
!==
undefined
);
const
rowCount
=
rowsWithLabel
.
length
;
const
rowCount
=
rowsWithLabel
.
length
;
// Get label value counts for eligible rows
// Get label value counts for eligible rows
const
countsByValue
=
_
.
countBy
(
rowsWithLabel
,
row
=>
(
row
as
LogRow
).
labels
[
label
]);
const
countsByValue
=
_
.
countBy
(
rowsWithLabel
,
row
=>
(
row
as
LogRow
Model
).
labels
[
label
]);
const
sortedCounts
=
_
.
chain
(
countsByValue
)
const
sortedCounts
=
_
.
chain
(
countsByValue
)
.
map
((
count
,
value
)
=>
({
count
,
value
,
proportion
:
count
/
rowCount
}))
.
map
((
count
,
value
)
=>
({
count
,
value
,
proportion
:
count
/
rowCount
}))
.
sortBy
(
'count'
)
.
sortBy
(
'count'
)
...
@@ -221,7 +221,7 @@ export function calculateLogsLabelStats(rows: LogRow[], label: string): LogsLabe
...
@@ -221,7 +221,7 @@ export function calculateLogsLabelStats(rows: LogRow[], label: string): LogsLabe
}
}
const
isoDateRegexp
=
/
\d{4}
-
[
01
]\d
-
[
0-3
]\d
T
[
0-2
]\d
:
[
0-5
]\d
:
[
0-6
]\d[
,
\.]\d
+
([
+-
][
0-2
]\d
:
[
0-5
]\d
|Z
)
/g
;
const
isoDateRegexp
=
/
\d{4}
-
[
01
]\d
-
[
0-3
]\d
T
[
0-2
]\d
:
[
0-5
]\d
:
[
0-6
]\d[
,
\.]\d
+
([
+-
][
0-2
]\d
:
[
0-5
]\d
|Z
)
/g
;
function
isDuplicateRow
(
row
:
LogRow
,
other
:
LogRow
,
strategy
:
LogsDedupStrategy
):
boolean
{
function
isDuplicateRow
(
row
:
LogRow
Model
,
other
:
LogRowModel
,
strategy
:
LogsDedupStrategy
):
boolean
{
switch
(
strategy
)
{
switch
(
strategy
)
{
case
LogsDedupStrategy
.
exact
:
case
LogsDedupStrategy
.
exact
:
// Exact still strips dates
// Exact still strips dates
...
@@ -243,7 +243,7 @@ export function dedupLogRows(logs: LogsModel, strategy: LogsDedupStrategy): Logs
...
@@ -243,7 +243,7 @@ export function dedupLogRows(logs: LogsModel, strategy: LogsDedupStrategy): Logs
return
logs
;
return
logs
;
}
}
const
dedupedRows
=
logs
.
rows
.
reduce
((
result
:
LogRow
[],
row
:
LogRow
,
index
,
list
)
=>
{
const
dedupedRows
=
logs
.
rows
.
reduce
((
result
:
LogRow
Model
[],
row
:
LogRowModel
,
index
,
list
)
=>
{
const
previous
=
result
[
result
.
length
-
1
];
const
previous
=
result
[
result
.
length
-
1
];
if
(
index
>
0
&&
isDuplicateRow
(
row
,
previous
,
strategy
))
{
if
(
index
>
0
&&
isDuplicateRow
(
row
,
previous
,
strategy
))
{
previous
.
duplicates
++
;
previous
.
duplicates
++
;
...
@@ -278,7 +278,7 @@ export function filterLogLevels(logs: LogsModel, hiddenLogLevels: Set<LogLevel>)
...
@@ -278,7 +278,7 @@ export function filterLogLevels(logs: LogsModel, hiddenLogLevels: Set<LogLevel>)
return
logs
;
return
logs
;
}
}
const
filteredRows
=
logs
.
rows
.
reduce
((
result
:
LogRow
[],
row
:
LogRow
,
index
,
list
)
=>
{
const
filteredRows
=
logs
.
rows
.
reduce
((
result
:
LogRow
Model
[],
row
:
LogRowModel
,
index
,
list
)
=>
{
if
(
!
hiddenLogLevels
.
has
(
row
.
logLevel
))
{
if
(
!
hiddenLogLevels
.
has
(
row
.
logLevel
))
{
result
.
push
(
row
);
result
.
push
(
row
);
}
}
...
@@ -291,7 +291,7 @@ export function filterLogLevels(logs: LogsModel, hiddenLogLevels: Set<LogLevel>)
...
@@ -291,7 +291,7 @@ export function filterLogLevels(logs: LogsModel, hiddenLogLevels: Set<LogLevel>)
};
};
}
}
export
function
makeSeriesForLogs
(
rows
:
LogRow
[],
intervalMs
:
number
):
TimeSeries
[]
{
export
function
makeSeriesForLogs
(
rows
:
LogRow
Model
[],
intervalMs
:
number
):
TimeSeries
[]
{
// currently interval is rangeMs / resolution, which is too low for showing series as bars.
// currently interval is rangeMs / resolution, which is too low for showing series as bars.
// need at least 10px per bucket, so we multiply interval by 10. Should be solved higher up the chain
// need at least 10px per bucket, so we multiply interval by 10. Should be solved higher up the chain
// when executing queries & interval calculated and not here but this is a temporary fix.
// when executing queries & interval calculated and not here but this is a temporary fix.
...
...
public/app/features/explore/LogLabels.tsx
View file @
23202ab1
import
React
,
{
PureComponent
}
from
'react'
;
import
React
,
{
PureComponent
}
from
'react'
;
import
classnames
from
'classnames'
;
import
classnames
from
'classnames'
;
import
{
calculateLogsLabelStats
,
LogsLabelStat
,
LogsStreamLabels
,
LogRow
}
from
'app/core/logs_model'
;
import
{
calculateLogsLabelStats
,
LogsLabelStat
,
LogsStreamLabels
,
LogRow
Model
}
from
'app/core/logs_model'
;
function
StatsRow
({
active
,
count
,
proportion
,
value
}:
LogsLabelStat
)
{
function
StatsRow
({
active
,
count
,
proportion
,
value
}:
LogsLabelStat
)
{
const
percent
=
`
${
Math
.
round
(
proportion
*
100
)}
%`
;
const
percent
=
`
${
Math
.
round
(
proportion
*
100
)}
%`
;
...
@@ -68,7 +68,7 @@ export class Stats extends PureComponent<{
...
@@ -68,7 +68,7 @@ export class Stats extends PureComponent<{
class
Label
extends
PureComponent
<
class
Label
extends
PureComponent
<
{
{
getRows
?:
()
=>
LogRow
[];
getRows
?:
()
=>
LogRow
Model
[];
label
:
string
;
label
:
string
;
plain
?:
boolean
;
plain
?:
boolean
;
value
:
string
;
value
:
string
;
...
@@ -133,7 +133,7 @@ class Label extends PureComponent<
...
@@ -133,7 +133,7 @@ class Label extends PureComponent<
}
}
export
default
class
LogLabels
extends
PureComponent
<
{
export
default
class
LogLabels
extends
PureComponent
<
{
getRows
?:
()
=>
LogRow
[];
getRows
?:
()
=>
LogRow
Model
[];
labels
:
LogsStreamLabels
;
labels
:
LogsStreamLabels
;
plain
?:
boolean
;
plain
?:
boolean
;
onClickLabel
?:
(
label
:
string
,
value
:
string
)
=>
void
;
onClickLabel
?:
(
label
:
string
,
value
:
string
)
=>
void
;
...
...
public/app/features/explore/LogRow.tsx
0 → 100644
View file @
23202ab1
import
React
,
{
PureComponent
}
from
'react'
;
import
_
from
'lodash'
;
import
Highlighter
from
'react-highlight-words'
;
import
classnames
from
'classnames'
;
import
{
LogRowModel
,
LogsLabelStat
,
LogsParser
,
calculateFieldStats
,
getParser
}
from
'app/core/logs_model'
;
import
LogLabels
,
{
Stats
}
from
'./LogLabels'
;
import
{
findHighlightChunksInText
}
from
'app/core/utils/text'
;
interface
RowProps
{
highlighterExpressions
?:
string
[];
row
:
LogRowModel
;
showDuplicates
:
boolean
;
showLabels
:
boolean
|
null
;
// Tristate: null means auto
showLocalTime
:
boolean
;
showUtc
:
boolean
;
getRows
:
()
=>
LogRowModel
[];
onClickLabel
?:
(
label
:
string
,
value
:
string
)
=>
void
;
}
interface
RowState
{
fieldCount
:
number
;
fieldLabel
:
string
;
fieldStats
:
LogsLabelStat
[];
fieldValue
:
string
;
parsed
:
boolean
;
parser
?:
LogsParser
;
parsedFieldHighlights
:
string
[];
showFieldStats
:
boolean
;
}
/**
* Renders a highlighted field.
* When hovering, a stats icon is shown.
*/
const
FieldHighlight
=
onClick
=>
props
=>
{
return
(
<
span
className=
{
props
.
className
}
style=
{
props
.
style
}
>
{
props
.
children
}
<
span
className=
"logs-row__field-highlight--icon fa fa-signal"
onClick=
{
()
=>
onClick
(
props
.
children
)
}
/>
</
span
>
);
};
/**
* Renders a log line.
*
* When user hovers over it for a certain time, it lazily parses the log line.
* Once a parser is found, it will determine fields, that will be highlighted.
* When the user requests stats for a field, they will be calculated and rendered below the row.
*/
export
class
LogRow
extends
PureComponent
<
RowProps
,
RowState
>
{
mouseMessageTimer
:
NodeJS
.
Timer
;
state
=
{
fieldCount
:
0
,
fieldLabel
:
null
,
fieldStats
:
null
,
fieldValue
:
null
,
parsed
:
false
,
parser
:
undefined
,
parsedFieldHighlights
:
[],
showFieldStats
:
false
,
};
componentWillUnmount
()
{
clearTimeout
(
this
.
mouseMessageTimer
);
}
onClickClose
=
()
=>
{
this
.
setState
({
showFieldStats
:
false
});
};
onClickHighlight
=
(
fieldText
:
string
)
=>
{
const
{
getRows
}
=
this
.
props
;
const
{
parser
}
=
this
.
state
;
const
allRows
=
getRows
();
// Build value-agnostic row matcher based on the field label
const
fieldLabel
=
parser
.
getLabelFromField
(
fieldText
);
const
fieldValue
=
parser
.
getValueFromField
(
fieldText
);
const
matcher
=
parser
.
buildMatcher
(
fieldLabel
);
const
fieldStats
=
calculateFieldStats
(
allRows
,
matcher
);
const
fieldCount
=
fieldStats
.
reduce
((
sum
,
stat
)
=>
sum
+
stat
.
count
,
0
);
this
.
setState
({
fieldCount
,
fieldLabel
,
fieldStats
,
fieldValue
,
showFieldStats
:
true
});
};
onMouseOverMessage
=
()
=>
{
// Don't parse right away, user might move along
this
.
mouseMessageTimer
=
setTimeout
(
this
.
parseMessage
,
500
);
};
onMouseOutMessage
=
()
=>
{
clearTimeout
(
this
.
mouseMessageTimer
);
this
.
setState
({
parsed
:
false
});
};
parseMessage
=
()
=>
{
if
(
!
this
.
state
.
parsed
)
{
const
{
row
}
=
this
.
props
;
const
parser
=
getParser
(
row
.
entry
);
if
(
parser
)
{
// Use parser to highlight detected fields
const
parsedFieldHighlights
=
parser
.
getFields
(
this
.
props
.
row
.
entry
);
this
.
setState
({
parsedFieldHighlights
,
parsed
:
true
,
parser
});
}
}
};
render
()
{
const
{
getRows
,
highlighterExpressions
,
onClickLabel
,
row
,
showDuplicates
,
showLabels
,
showLocalTime
,
showUtc
,
}
=
this
.
props
;
const
{
fieldCount
,
fieldLabel
,
fieldStats
,
fieldValue
,
parsed
,
parsedFieldHighlights
,
showFieldStats
,
}
=
this
.
state
;
const
previewHighlights
=
highlighterExpressions
&&
!
_
.
isEqual
(
highlighterExpressions
,
row
.
searchWords
);
const
highlights
=
previewHighlights
?
highlighterExpressions
:
row
.
searchWords
;
const
needsHighlighter
=
highlights
&&
highlights
.
length
>
0
;
const
highlightClassName
=
classnames
(
'logs-row__match-highlight'
,
{
'logs-row__match-highlight--preview'
:
previewHighlights
,
});
return
(
<
div
className=
"logs-row"
>
{
showDuplicates
&&
(
<
div
className=
"logs-row__duplicates"
>
{
row
.
duplicates
>
0
?
`${row.duplicates + 1}x`
:
null
}
</
div
>
)
}
<
div
className=
{
row
.
logLevel
?
`logs-row__level logs-row__level--${row.logLevel}`
:
''
}
/>
{
showUtc
&&
(
<
div
className=
"logs-row__time"
title=
{
`Local: ${row.timeLocal} (${row.timeFromNow})`
}
>
{
row
.
timestamp
}
</
div
>
)
}
{
showLocalTime
&&
(
<
div
className=
"logs-row__time"
title=
{
`${row.timestamp} (${row.timeFromNow})`
}
>
{
row
.
timeLocal
}
</
div
>
)
}
{
showLabels
&&
(
<
div
className=
"logs-row__labels"
>
<
LogLabels
getRows=
{
getRows
}
labels=
{
row
.
uniqueLabels
}
onClickLabel=
{
onClickLabel
}
/>
</
div
>
)
}
<
div
className=
"logs-row__message"
onMouseEnter=
{
this
.
onMouseOverMessage
}
onMouseLeave=
{
this
.
onMouseOutMessage
}
>
{
parsed
&&
(
<
Highlighter
autoEscape
highlightTag=
{
FieldHighlight
(
this
.
onClickHighlight
)
}
textToHighlight=
{
row
.
entry
}
searchWords=
{
parsedFieldHighlights
}
highlightClassName=
"logs-row__field-highlight"
/>
)
}
{
!
parsed
&&
needsHighlighter
&&
(
<
Highlighter
textToHighlight=
{
row
.
entry
}
searchWords=
{
highlights
}
findChunks=
{
findHighlightChunksInText
}
highlightClassName=
{
highlightClassName
}
/>
)
}
{
!
parsed
&&
!
needsHighlighter
&&
row
.
entry
}
{
showFieldStats
&&
(
<
div
className=
"logs-row__stats"
>
<
Stats
stats=
{
fieldStats
}
label=
{
fieldLabel
}
value=
{
fieldValue
}
onClickClose=
{
this
.
onClickClose
}
rowCount=
{
fieldCount
}
/>
</
div
>
)
}
</
div
>
</
div
>
);
}
}
public/app/features/explore/Logs.tsx
View file @
23202ab1
import
_
from
'lodash'
;
import
_
from
'lodash'
;
import
React
,
{
PureComponent
}
from
'react'
;
import
React
,
{
PureComponent
}
from
'react'
;
import
Highlighter
from
'react-highlight-words'
;
import
classnames
from
'classnames'
;
import
*
as
rangeUtil
from
'app/core/utils/rangeutil'
;
import
*
as
rangeUtil
from
'app/core/utils/rangeutil'
;
import
{
RawTimeRange
}
from
'@grafana/ui'
;
import
{
RawTimeRange
}
from
'@grafana/ui'
;
...
@@ -11,20 +9,16 @@ import {
...
@@ -11,20 +9,16 @@ import {
LogsModel
,
LogsModel
,
dedupLogRows
,
dedupLogRows
,
filterLogLevels
,
filterLogLevels
,
getParser
,
LogLevel
,
LogLevel
,
LogsMetaKind
,
LogsMetaKind
,
LogsLabelStat
,
LogsParser
,
LogRow
,
calculateFieldStats
,
}
from
'app/core/logs_model'
;
}
from
'app/core/logs_model'
;
import
{
findHighlightChunksInText
}
from
'app/core/utils/text'
;
import
{
Switch
}
from
'app/core/components/Switch/Switch'
;
import
{
Switch
}
from
'app/core/components/Switch/Switch'
;
import
ToggleButtonGroup
,
{
ToggleButton
}
from
'app/core/components/ToggleButtonGroup/ToggleButtonGroup'
;
import
ToggleButtonGroup
,
{
ToggleButton
}
from
'app/core/components/ToggleButtonGroup/ToggleButtonGroup'
;
import
Graph
from
'./Graph'
;
import
Graph
from
'./Graph'
;
import
LogLabels
,
{
Stats
}
from
'./LogLabels'
;
import
LogLabels
from
'./LogLabels'
;
import
{
LogRow
}
from
'./LogRow'
;
const
PREVIEW_LIMIT
=
100
;
const
PREVIEW_LIMIT
=
100
;
...
@@ -43,191 +37,6 @@ const graphOptions = {
...
@@ -43,191 +37,6 @@ const graphOptions = {
},
},
};
};
/**
* Renders a highlighted field.
* When hovering, a stats icon is shown.
*/
const
FieldHighlight
=
onClick
=>
props
=>
{
return
(
<
span
className=
{
props
.
className
}
style=
{
props
.
style
}
>
{
props
.
children
}
<
span
className=
"logs-row__field-highlight--icon fa fa-signal"
onClick=
{
()
=>
onClick
(
props
.
children
)
}
/>
</
span
>
);
};
interface
RowProps
{
highlighterExpressions
?:
string
[];
row
:
LogRow
;
showDuplicates
:
boolean
;
showLabels
:
boolean
|
null
;
// Tristate: null means auto
showLocalTime
:
boolean
;
showUtc
:
boolean
;
getRows
:
()
=>
LogRow
[];
onClickLabel
?:
(
label
:
string
,
value
:
string
)
=>
void
;
}
interface
RowState
{
fieldCount
:
number
;
fieldLabel
:
string
;
fieldStats
:
LogsLabelStat
[];
fieldValue
:
string
;
parsed
:
boolean
;
parser
?:
LogsParser
;
parsedFieldHighlights
:
string
[];
showFieldStats
:
boolean
;
}
/**
* Renders a log line.
*
* When user hovers over it for a certain time, it lazily parses the log line.
* Once a parser is found, it will determine fields, that will be highlighted.
* When the user requests stats for a field, they will be calculated and rendered below the row.
*/
class
Row
extends
PureComponent
<
RowProps
,
RowState
>
{
mouseMessageTimer
:
NodeJS
.
Timer
;
state
=
{
fieldCount
:
0
,
fieldLabel
:
null
,
fieldStats
:
null
,
fieldValue
:
null
,
parsed
:
false
,
parser
:
undefined
,
parsedFieldHighlights
:
[],
showFieldStats
:
false
,
};
componentWillUnmount
()
{
clearTimeout
(
this
.
mouseMessageTimer
);
}
onClickClose
=
()
=>
{
this
.
setState
({
showFieldStats
:
false
});
};
onClickHighlight
=
(
fieldText
:
string
)
=>
{
const
{
getRows
}
=
this
.
props
;
const
{
parser
}
=
this
.
state
;
const
allRows
=
getRows
();
// Build value-agnostic row matcher based on the field label
const
fieldLabel
=
parser
.
getLabelFromField
(
fieldText
);
const
fieldValue
=
parser
.
getValueFromField
(
fieldText
);
const
matcher
=
parser
.
buildMatcher
(
fieldLabel
);
const
fieldStats
=
calculateFieldStats
(
allRows
,
matcher
);
const
fieldCount
=
fieldStats
.
reduce
((
sum
,
stat
)
=>
sum
+
stat
.
count
,
0
);
this
.
setState
({
fieldCount
,
fieldLabel
,
fieldStats
,
fieldValue
,
showFieldStats
:
true
});
};
onMouseOverMessage
=
()
=>
{
// Don't parse right away, user might move along
this
.
mouseMessageTimer
=
setTimeout
(
this
.
parseMessage
,
500
);
};
onMouseOutMessage
=
()
=>
{
clearTimeout
(
this
.
mouseMessageTimer
);
this
.
setState
({
parsed
:
false
});
};
parseMessage
=
()
=>
{
if
(
!
this
.
state
.
parsed
)
{
const
{
row
}
=
this
.
props
;
const
parser
=
getParser
(
row
.
entry
);
if
(
parser
)
{
// Use parser to highlight detected fields
const
parsedFieldHighlights
=
parser
.
getFields
(
this
.
props
.
row
.
entry
);
this
.
setState
({
parsedFieldHighlights
,
parsed
:
true
,
parser
});
}
}
};
render
()
{
const
{
getRows
,
highlighterExpressions
,
onClickLabel
,
row
,
showDuplicates
,
showLabels
,
showLocalTime
,
showUtc
,
}
=
this
.
props
;
const
{
fieldCount
,
fieldLabel
,
fieldStats
,
fieldValue
,
parsed
,
parsedFieldHighlights
,
showFieldStats
,
}
=
this
.
state
;
const
previewHighlights
=
highlighterExpressions
&&
!
_
.
isEqual
(
highlighterExpressions
,
row
.
searchWords
);
const
highlights
=
previewHighlights
?
highlighterExpressions
:
row
.
searchWords
;
const
needsHighlighter
=
highlights
&&
highlights
.
length
>
0
;
const
highlightClassName
=
classnames
(
'logs-row__match-highlight'
,
{
'logs-row__match-highlight--preview'
:
previewHighlights
,
});
return
(
<
div
className=
"logs-row"
>
{
showDuplicates
&&
(
<
div
className=
"logs-row__duplicates"
>
{
row
.
duplicates
>
0
?
`${row.duplicates + 1}x`
:
null
}
</
div
>
)
}
<
div
className=
{
row
.
logLevel
?
`logs-row__level logs-row__level--${row.logLevel}`
:
''
}
/>
{
showUtc
&&
(
<
div
className=
"logs-row__time"
title=
{
`Local: ${row.timeLocal} (${row.timeFromNow})`
}
>
{
row
.
timestamp
}
</
div
>
)
}
{
showLocalTime
&&
(
<
div
className=
"logs-row__time"
title=
{
`${row.timestamp} (${row.timeFromNow})`
}
>
{
row
.
timeLocal
}
</
div
>
)
}
{
showLabels
&&
(
<
div
className=
"logs-row__labels"
>
<
LogLabels
getRows=
{
getRows
}
labels=
{
row
.
uniqueLabels
}
onClickLabel=
{
onClickLabel
}
/>
</
div
>
)
}
<
div
className=
"logs-row__message"
onMouseEnter=
{
this
.
onMouseOverMessage
}
onMouseLeave=
{
this
.
onMouseOutMessage
}
>
{
parsed
&&
(
<
Highlighter
autoEscape
highlightTag=
{
FieldHighlight
(
this
.
onClickHighlight
)
}
textToHighlight=
{
row
.
entry
}
searchWords=
{
parsedFieldHighlights
}
highlightClassName=
"logs-row__field-highlight"
/>
)
}
{
!
parsed
&&
needsHighlighter
&&
(
<
Highlighter
textToHighlight=
{
row
.
entry
}
searchWords=
{
highlights
}
findChunks=
{
findHighlightChunksInText
}
highlightClassName=
{
highlightClassName
}
/>
)
}
{
!
parsed
&&
!
needsHighlighter
&&
row
.
entry
}
{
showFieldStats
&&
(
<
div
className=
"logs-row__stats"
>
<
Stats
stats=
{
fieldStats
}
label=
{
fieldLabel
}
value=
{
fieldValue
}
onClickClose=
{
this
.
onClickClose
}
rowCount=
{
fieldCount
}
/>
</
div
>
)
}
</
div
>
</
div
>
);
}
}
function
renderMetaItem
(
value
:
any
,
kind
:
LogsMetaKind
)
{
function
renderMetaItem
(
value
:
any
,
kind
:
LogsMetaKind
)
{
if
(
kind
===
LogsMetaKind
.
LabelsMap
)
{
if
(
kind
===
LogsMetaKind
.
LabelsMap
)
{
return
(
return
(
...
@@ -441,10 +250,9 @@ export default class Logs extends PureComponent<LogsProps, LogsState> {
...
@@ -441,10 +250,9 @@ export default class Logs extends PureComponent<LogsProps, LogsState> {
<
div
className=
"logs-rows"
>
<
div
className=
"logs-rows"
>
{
hasData
&&
{
hasData
&&
!
deferLogs
&&
!
deferLogs
&&
// Only inject highlighterExpression in the first set for performance reasons
// Only inject highlighterExpression in the first set for performance reasons
firstRows
.
map
(
row
=>
(
firstRows
.
map
(
row
=>
(
<
Row
<
Log
Row
key=
{
row
.
key
+
row
.
duplicates
}
key=
{
row
.
key
+
row
.
duplicates
}
getRows=
{
getRows
}
getRows=
{
getRows
}
highlighterExpressions=
{
highlighterExpressions
}
highlighterExpressions=
{
highlighterExpressions
}
...
@@ -460,7 +268,7 @@ export default class Logs extends PureComponent<LogsProps, LogsState> {
...
@@ -460,7 +268,7 @@ export default class Logs extends PureComponent<LogsProps, LogsState> {
!
deferLogs
&&
!
deferLogs
&&
renderAll
&&
renderAll
&&
lastRows
.
map
(
row
=>
(
lastRows
.
map
(
row
=>
(
<
Row
<
Log
Row
key=
{
row
.
key
+
row
.
duplicates
}
key=
{
row
.
key
+
row
.
duplicates
}
getRows=
{
getRows
}
getRows=
{
getRows
}
row=
{
row
}
row=
{
row
}
...
...
public/app/plugins/datasource/loki/result_transformer.ts
View file @
23202ab1
...
@@ -5,7 +5,7 @@ import {
...
@@ -5,7 +5,7 @@ import {
LogLevel
,
LogLevel
,
LogsMetaItem
,
LogsMetaItem
,
LogsModel
,
LogsModel
,
LogRow
,
LogRow
Model
,
LogsStream
,
LogsStream
,
LogsStreamEntry
,
LogsStreamEntry
,
LogsStreamLabels
,
LogsStreamLabels
,
...
@@ -115,7 +115,7 @@ export function processEntry(
...
@@ -115,7 +115,7 @@ export function processEntry(
parsedLabels
:
LogsStreamLabels
,
parsedLabels
:
LogsStreamLabels
,
uniqueLabels
:
LogsStreamLabels
,
uniqueLabels
:
LogsStreamLabels
,
search
:
string
search
:
string
):
LogRow
{
):
LogRow
Model
{
const
{
line
}
=
entry
;
const
{
line
}
=
entry
;
const
ts
=
entry
.
ts
||
entry
.
timestamp
;
const
ts
=
entry
.
ts
||
entry
.
timestamp
;
// Assumes unique-ness, needs nanosec precision for timestamp
// Assumes unique-ness, needs nanosec precision for timestamp
...
@@ -156,9 +156,9 @@ export function mergeStreamsToLogs(streams: LogsStream[], limit = DEFAULT_MAX_LI
...
@@ -156,9 +156,9 @@ export function mergeStreamsToLogs(streams: LogsStream[], limit = DEFAULT_MAX_LI
}));
}));
// Merge stream entries into single list of log rows
// Merge stream entries into single list of log rows
const
sortedRows
:
LogRow
[]
=
_
.
chain
(
streams
)
const
sortedRows
:
LogRow
Model
[]
=
_
.
chain
(
streams
)
.
reduce
(
.
reduce
(
(
acc
:
LogRow
[],
stream
:
LogsStream
)
=>
[
(
acc
:
LogRow
Model
[],
stream
:
LogsStream
)
=>
[
...
acc
,
...
acc
,
...
stream
.
entries
.
map
(
entry
=>
...
stream
.
entries
.
map
(
entry
=>
processEntry
(
entry
,
stream
.
labels
,
stream
.
parsedLabels
,
stream
.
uniqueLabels
,
stream
.
search
)
processEntry
(
entry
,
stream
.
labels
,
stream
.
parsedLabels
,
stream
.
uniqueLabels
,
stream
.
search
)
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment