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
6a01bab6
Unverified
Commit
6a01bab6
authored
Aug 25, 2020
by
Fredrik Enestad
Committed by
GitHub
Aug 25, 2020
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Allow toggling of derived fields (#27148)
parent
f5ee1f93
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
80 additions
and
75 deletions
+80
-75
packages/grafana-ui/src/components/Logs/LogDetails.tsx
+3
-67
packages/grafana-ui/src/components/Logs/LogRow.tsx
+1
-1
packages/grafana-ui/src/components/Logs/LogRowMessageParsed.tsx
+5
-4
packages/grafana-ui/src/components/Logs/logParser.ts
+71
-3
No files found.
packages/grafana-ui/src/components/Logs/LogDetails.tsx
View file @
6a01bab6
...
...
@@ -18,7 +18,7 @@ import { getLogRowStyles } from './getLogRowStyles';
import
{
stylesFactory
}
from
'../../themes/stylesFactory'
;
import
{
selectThemeVariant
}
from
'../../themes/selectThemeVariant'
;
import
{
parseMessage
,
FieldDef
}
from
'./logParser'
;
import
{
getAllFields
}
from
'./logParser'
;
//Components
import
{
LogDetailsRow
}
from
'./LogDetailsRow'
;
...
...
@@ -61,61 +61,6 @@ const getStyles = stylesFactory((theme: GrafanaTheme) => {
class
UnThemedLogDetails
extends
PureComponent
<
Props
>
{
getParser
=
memoizeOne
(
getParser
);
getDerivedFields
=
memoizeOne
((
row
:
LogRowModel
):
FieldDef
[]
=>
{
return
(
row
.
dataFrame
.
fields
.
map
((
field
,
index
)
=>
({
...
field
,
index
}))
// Remove Id which we use for react key and entry field which we are showing as the log message. Also remove hidden fields.
.
filter
(
(
field
,
index
)
=>
!
(
'id'
===
field
.
name
||
row
.
entryFieldIndex
===
index
||
field
.
config
.
custom
?.
hidden
)
)
// Filter out fields without values. For example in elastic the fields are parsed from the document which can
// have different structure per row and so the dataframe is pretty sparse.
.
filter
(
field
=>
{
const
value
=
field
.
values
.
get
(
row
.
rowIndex
);
// Not sure exactly what will be the empty value here. And we want to keep 0 as some values can be non
// string.
return
value
!==
null
&&
value
!==
undefined
;
})
.
map
(
field
=>
{
const
{
getFieldLinks
}
=
this
.
props
;
const
links
=
getFieldLinks
?
getFieldLinks
(
field
,
row
.
rowIndex
)
:
[];
return
{
key
:
field
.
name
,
value
:
field
.
values
.
get
(
row
.
rowIndex
).
toString
(),
links
:
links
,
fieldIndex
:
field
.
index
,
};
})
);
});
/**
* Returns all fields for log row which consists of fields we parse from the message itself and any derived fields
* setup in data source config.
*/
getAllFields
=
memoizeOne
((
row
:
LogRowModel
)
=>
{
const
fields
=
parseMessage
(
row
.
entry
);
const
derivedFields
=
this
.
getDerivedFields
(
row
);
const
fieldsMap
=
[...
derivedFields
,
...
fields
].
reduce
((
acc
,
field
)
=>
{
// Strip enclosing quotes for hashing. When values are parsed from log line the quotes are kept, but if same
// value is in the dataFrame it will be without the quotes. We treat them here as the same value.
const
value
=
field
.
value
.
replace
(
/
(
^"
)
|
(
"$
)
/g
,
''
);
const
fieldHash
=
`
${
field
.
key
}
=
${
value
}
`
;
if
(
acc
[
fieldHash
])
{
acc
[
fieldHash
].
links
=
[...(
acc
[
fieldHash
].
links
||
[]),
...(
field
.
links
||
[])];
}
else
{
acc
[
fieldHash
]
=
field
;
}
return
acc
;
},
{}
as
{
[
key
:
string
]:
FieldDef
});
const
allFields
=
Object
.
values
(
fieldsMap
);
allFields
.
sort
(
sortFieldsLinkFirst
);
return
allFields
;
});
getStatsForParsedField
=
(
key
:
string
)
=>
{
const
matcher
=
this
.
getParser
(
this
.
props
.
row
.
entry
)
!
.
buildMatcher
(
key
);
return
calculateFieldStats
(
this
.
props
.
getRows
(),
matcher
);
...
...
@@ -135,12 +80,13 @@ class UnThemedLogDetails extends PureComponent<Props> {
onClickShowParsedField
,
onClickHideParsedField
,
showParsedFields
,
getFieldLinks
,
}
=
this
.
props
;
const
style
=
getLogRowStyles
(
theme
,
row
.
logLevel
);
const
styles
=
getStyles
(
theme
);
const
labels
=
row
.
labels
?
row
.
labels
:
{};
const
labelsAvailable
=
Object
.
keys
(
labels
).
length
>
0
;
const
fields
=
this
.
getAllFields
(
row
);
const
fields
=
getAllFields
(
row
,
getFieldLinks
);
const
parsedFieldsAvailable
=
fields
&&
fields
.
length
>
0
;
return
(
...
...
@@ -219,15 +165,5 @@ class UnThemedLogDetails extends PureComponent<Props> {
}
}
function
sortFieldsLinkFirst
(
fieldA
:
FieldDef
,
fieldB
:
FieldDef
)
{
if
(
fieldA
.
links
?.
length
&&
!
fieldB
.
links
?.
length
)
{
return
-
1
;
}
if
(
!
fieldA
.
links
?.
length
&&
fieldB
.
links
?.
length
)
{
return
1
;
}
return
fieldA
.
key
>
fieldB
.
key
?
1
:
fieldA
.
key
<
fieldB
.
key
?
-
1
:
0
;
}
export
const
LogDetails
=
withTheme
(
UnThemedLogDetails
);
LogDetails
.
displayName
=
'LogDetails'
;
packages/grafana-ui/src/components/Logs/LogRow.tsx
View file @
6a01bab6
...
...
@@ -183,7 +183,7 @@ class UnThemedLogRow extends PureComponent<Props, State> {
</
td
>
)
}
{
showParsedFields
&&
showParsedFields
.
length
>
0
?
(
<
LogRowMessageParsed
row=
{
row
}
showParsedFields=
{
showParsedFields
!
}
/>
<
LogRowMessageParsed
row=
{
row
}
showParsedFields=
{
showParsedFields
!
}
getFieldLinks=
{
getFieldLinks
}
/>
)
:
(
<
LogRowMessage
highlighterExpressions=
{
highlighterExpressions
}
...
...
packages/grafana-ui/src/components/Logs/LogRowMessageParsed.tsx
View file @
6a01bab6
import
React
,
{
PureComponent
}
from
'react'
;
import
{
LogRowModel
}
from
'@grafana/data'
;
import
{
LogRowModel
,
Field
,
LinkModel
}
from
'@grafana/data'
;
import
{
Themeable
}
from
'../../types/theme'
;
import
{
withTheme
}
from
'../../themes/index'
;
import
{
parseMessage
}
from
'./logParser'
;
import
{
getAllFields
}
from
'./logParser'
;
export
interface
Props
extends
Themeable
{
row
:
LogRowModel
;
showParsedFields
:
string
[];
getFieldLinks
?:
(
field
:
Field
,
rowIndex
:
number
)
=>
Array
<
LinkModel
<
Field
>>
;
}
class
UnThemedLogRowMessageParsed
extends
PureComponent
<
Props
>
{
render
()
{
const
{
row
,
showParsedFields
}
=
this
.
props
;
const
fields
=
parseMessage
(
row
.
entry
);
const
{
row
,
showParsedFields
,
getFieldLinks
}
=
this
.
props
;
const
fields
=
getAllFields
(
row
,
getFieldLinks
);
const
line
=
showParsedFields
.
map
(
parsedKey
=>
{
...
...
packages/grafana-ui/src/components/Logs/logParser.ts
View file @
6a01bab6
import
{
Field
,
getParser
,
LinkModel
}
from
'@grafana/data'
;
import
{
Field
,
getParser
,
LinkModel
,
LogRowModel
}
from
'@grafana/data'
;
import
memoizeOne
from
'memoize-one'
;
import
{
MAX_CHARACTERS
}
from
'./LogRowMessage'
;
const
memoizedGetParser
=
memoizeOne
(
getParser
);
export
type
FieldDef
=
{
type
FieldDef
=
{
key
:
string
;
value
:
string
;
links
?:
Array
<
LinkModel
<
Field
>>
;
fieldIndex
?:
number
;
};
export
const
parseMessage
=
memoizeOne
((
rowEntry
):
FieldDef
[]
=>
{
/**
* Returns all fields for log row which consists of fields we parse from the message itself and any derived fields
* setup in data source config.
*/
export
const
getAllFields
=
memoizeOne
(
(
row
:
LogRowModel
,
getFieldLinks
?:
(
field
:
Field
,
rowIndex
:
number
)
=>
Array
<
LinkModel
<
Field
>>
)
=>
{
const
fields
=
parseMessage
(
row
.
entry
);
const
derivedFields
=
getDerivedFields
(
row
,
getFieldLinks
);
const
fieldsMap
=
[...
derivedFields
,
...
fields
].
reduce
((
acc
,
field
)
=>
{
// Strip enclosing quotes for hashing. When values are parsed from log line the quotes are kept, but if same
// value is in the dataFrame it will be without the quotes. We treat them here as the same value.
const
value
=
field
.
value
.
replace
(
/
(
^"
)
|
(
"$
)
/g
,
''
);
const
fieldHash
=
`
${
field
.
key
}
=
${
value
}
`
;
if
(
acc
[
fieldHash
])
{
acc
[
fieldHash
].
links
=
[...(
acc
[
fieldHash
].
links
||
[]),
...(
field
.
links
||
[])];
}
else
{
acc
[
fieldHash
]
=
field
;
}
return
acc
;
},
{}
as
{
[
key
:
string
]:
FieldDef
});
const
allFields
=
Object
.
values
(
fieldsMap
);
allFields
.
sort
(
sortFieldsLinkFirst
);
return
allFields
;
}
);
const
parseMessage
=
memoizeOne
((
rowEntry
):
FieldDef
[]
=>
{
if
(
rowEntry
.
length
>
MAX_CHARACTERS
)
{
return
[];
}
...
...
@@ -30,3 +58,43 @@ export const parseMessage = memoizeOne((rowEntry): FieldDef[] => {
return
fields
;
});
const
getDerivedFields
=
memoizeOne
(
(
row
:
LogRowModel
,
getFieldLinks
?:
(
field
:
Field
,
rowIndex
:
number
)
=>
Array
<
LinkModel
<
Field
>>
):
FieldDef
[]
=>
{
return
(
row
.
dataFrame
.
fields
.
map
((
field
,
index
)
=>
({
...
field
,
index
}))
// Remove Id which we use for react key and entry field which we are showing as the log message. Also remove hidden fields.
.
filter
(
(
field
,
index
)
=>
!
(
'id'
===
field
.
name
||
row
.
entryFieldIndex
===
index
||
field
.
config
.
custom
?.
hidden
)
)
// Filter out fields without values. For example in elastic the fields are parsed from the document which can
// have different structure per row and so the dataframe is pretty sparse.
.
filter
(
field
=>
{
const
value
=
field
.
values
.
get
(
row
.
rowIndex
);
// Not sure exactly what will be the empty value here. And we want to keep 0 as some values can be non
// string.
return
value
!==
null
&&
value
!==
undefined
;
})
.
map
(
field
=>
{
const
links
=
getFieldLinks
?
getFieldLinks
(
field
,
row
.
rowIndex
)
:
[];
return
{
key
:
field
.
name
,
value
:
field
.
values
.
get
(
row
.
rowIndex
).
toString
(),
links
:
links
,
fieldIndex
:
field
.
index
,
};
})
);
}
);
function
sortFieldsLinkFirst
(
fieldA
:
FieldDef
,
fieldB
:
FieldDef
)
{
if
(
fieldA
.
links
?.
length
&&
!
fieldB
.
links
?.
length
)
{
return
-
1
;
}
if
(
!
fieldA
.
links
?.
length
&&
fieldB
.
links
?.
length
)
{
return
1
;
}
return
fieldA
.
key
>
fieldB
.
key
?
1
:
fieldA
.
key
<
fieldB
.
key
?
-
1
:
0
;
}
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