mirror of
https://github.com/VictoriaMetrics/VictoriaMetrics.git
synced 2024-12-31 15:06:26 +00:00
wip
This commit is contained in:
parent
79787ce25a
commit
2ff9cf9f43
7 changed files with 68 additions and 17 deletions
|
@ -1273,7 +1273,7 @@ See also:
|
||||||
### format pipe
|
### format pipe
|
||||||
|
|
||||||
`| format "pattern" as result_field` [pipe](#format-pipe) combines [log fields](https://docs.victoriametrics.com/victorialogs/keyconcepts/#data-model)
|
`| format "pattern" as result_field` [pipe](#format-pipe) combines [log fields](https://docs.victoriametrics.com/victorialogs/keyconcepts/#data-model)
|
||||||
according to the `pattern` and stores it to the `result_field`.
|
according to the `pattern` and stores it to the `result_field`. All the other fields remain unchanged after the `| format ...` pipe.
|
||||||
|
|
||||||
For example, the following query stores `request from <ip>:<port>` text into [`_msg` field](https://docs.victoriametrics.com/victorialogs/keyconcepts/#message-field),
|
For example, the following query stores `request from <ip>:<port>` text into [`_msg` field](https://docs.victoriametrics.com/victorialogs/keyconcepts/#message-field),
|
||||||
by substituting `<ip>` and `<port>` with the corresponding [log field](https://docs.victoriametrics.com/victorialogs/keyconcepts/#data-model) names:
|
by substituting `<ip>` and `<port>` with the corresponding [log field](https://docs.victoriametrics.com/victorialogs/keyconcepts/#data-model) names:
|
||||||
|
@ -1289,12 +1289,12 @@ then `as _msg` part can be omitted. The following query is equivalent to the pre
|
||||||
_time:5m | format "request from <ip>:<port>"
|
_time:5m | format "request from <ip>:<port>"
|
||||||
```
|
```
|
||||||
|
|
||||||
If some field values must be put into double quotes before formatting, then add `:q` after the corresponding field name.
|
If some field values must be put into double quotes before formatting, then add `q:` in front of the corresponding field name.
|
||||||
For example, the following command generates properly encoded JSON object from `_msg` and `stacktrace` [log fields](https://docs.victoriametrics.com/victorialogs/keyconcepts/#data-model)
|
For example, the following command generates properly encoded JSON object from `_msg` and `stacktrace` [log fields](https://docs.victoriametrics.com/victorialogs/keyconcepts/#data-model)
|
||||||
and stores it into `my_json` output field:
|
and stores it into `my_json` output field:
|
||||||
|
|
||||||
```logsql
|
```logsql
|
||||||
_time:5m | format '{"_msg":<_msg:q>,"stacktrace":<stacktrace:q>}' as my_json
|
_time:5m | format '{"_msg":<q:_msg>,"stacktrace":<q:stacktrace>}' as my_json
|
||||||
```
|
```
|
||||||
|
|
||||||
See also:
|
See also:
|
||||||
|
@ -1302,6 +1302,7 @@ See also:
|
||||||
- [Conditional format](#conditional-format)
|
- [Conditional format](#conditional-format)
|
||||||
- [`extract` pipe](#extract-pipe)
|
- [`extract` pipe](#extract-pipe)
|
||||||
|
|
||||||
|
|
||||||
#### Conditional format
|
#### Conditional format
|
||||||
|
|
||||||
If the [`format` pipe](#format-pipe) musn't be applied to every [log entry](https://docs.victoriametrics.com/victorialogs/keyconcepts/#data-model),
|
If the [`format` pipe](#format-pipe) musn't be applied to every [log entry](https://docs.victoriametrics.com/victorialogs/keyconcepts/#data-model),
|
||||||
|
|
|
@ -29,6 +29,7 @@ type patternField struct {
|
||||||
type patternStep struct {
|
type patternStep struct {
|
||||||
prefix string
|
prefix string
|
||||||
field string
|
field string
|
||||||
|
opt string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ptn *pattern) clone() *pattern {
|
func (ptn *pattern) clone() *pattern {
|
||||||
|
@ -154,6 +155,31 @@ func tryUnquoteString(s string) (string, int) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func parsePatternSteps(s string) ([]patternStep, error) {
|
func parsePatternSteps(s string) ([]patternStep, error) {
|
||||||
|
steps, err := parsePatternStepsInternal(s)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unescape prefixes
|
||||||
|
for i := range steps {
|
||||||
|
step := &steps[i]
|
||||||
|
step.prefix = html.UnescapeString(step.prefix)
|
||||||
|
}
|
||||||
|
|
||||||
|
// extract options part from fields
|
||||||
|
for i := range steps {
|
||||||
|
step := &steps[i]
|
||||||
|
field := step.field
|
||||||
|
if n := strings.IndexByte(field, ':'); n >= 0 {
|
||||||
|
step.opt = field[:n]
|
||||||
|
step.field = field[n+1:]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return steps, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func parsePatternStepsInternal(s string) ([]patternStep, error) {
|
||||||
if len(s) == 0 {
|
if len(s) == 0 {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
@ -163,7 +189,7 @@ func parsePatternSteps(s string) ([]patternStep, error) {
|
||||||
n := strings.IndexByte(s, '<')
|
n := strings.IndexByte(s, '<')
|
||||||
if n < 0 {
|
if n < 0 {
|
||||||
steps = append(steps, patternStep{
|
steps = append(steps, patternStep{
|
||||||
prefix: html.UnescapeString(s),
|
prefix: s,
|
||||||
})
|
})
|
||||||
return steps, nil
|
return steps, nil
|
||||||
}
|
}
|
||||||
|
@ -199,10 +225,5 @@ func parsePatternSteps(s string) ([]patternStep, error) {
|
||||||
s = s[n+1:]
|
s = s[n+1:]
|
||||||
}
|
}
|
||||||
|
|
||||||
for i := range steps {
|
|
||||||
step := &steps[i]
|
|
||||||
step.prefix = html.UnescapeString(step.prefix)
|
|
||||||
}
|
|
||||||
|
|
||||||
return steps, nil
|
return steps, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -205,6 +205,21 @@ func TestParsePatternStepsSuccess(t *testing.T) {
|
||||||
prefix: ">",
|
prefix: ">",
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
f("<q:foo>bar<abc:baz:c:y>f<:foo:bar:baz>", []patternStep{
|
||||||
|
{
|
||||||
|
field: "foo",
|
||||||
|
opt: "q",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
prefix: "bar",
|
||||||
|
field: "baz:c:y",
|
||||||
|
opt: "abc",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
prefix: "f",
|
||||||
|
field: "foo:bar:baz",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -125,7 +125,7 @@ func parsePipeExtract(lex *lexer) (*pipeExtract, error) {
|
||||||
fromField: fromField,
|
fromField: fromField,
|
||||||
ptn: ptn,
|
ptn: ptn,
|
||||||
patternStr: patternStr,
|
patternStr: patternStr,
|
||||||
iff: iff,
|
iff: iff,
|
||||||
}
|
}
|
||||||
|
|
||||||
return pe, nil
|
return pe, nil
|
||||||
|
|
|
@ -2,6 +2,7 @@ package logstorage
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"strconv"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
|
|
||||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/bytesutil"
|
"github.com/VictoriaMetrics/VictoriaMetrics/lib/bytesutil"
|
||||||
|
@ -136,7 +137,11 @@ func (shard *pipeFormatProcessorShard) formatRow(pf *pipeFormat, br *blockResult
|
||||||
if step.field != "" {
|
if step.field != "" {
|
||||||
c := br.getColumnByName(step.field)
|
c := br.getColumnByName(step.field)
|
||||||
v := c.getValueAtRow(br, rowIdx)
|
v := c.getValueAtRow(br, rowIdx)
|
||||||
b = append(b, v...)
|
if step.opt == "q" {
|
||||||
|
b = strconv.AppendQuote(b, v)
|
||||||
|
} else {
|
||||||
|
b = append(b, v...)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
bb.B = b
|
bb.B = b
|
||||||
|
|
|
@ -39,6 +39,20 @@ func TestPipeFormat(t *testing.T) {
|
||||||
expectPipeResults(t, pipeStr, rows, rowsExpected)
|
expectPipeResults(t, pipeStr, rows, rowsExpected)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// plain string into a single field
|
||||||
|
f(`format '{"foo":<q:foo>,"bar":"<bar>"}' as x`, [][]Field{
|
||||||
|
{
|
||||||
|
{"foo", `"abc"`},
|
||||||
|
{"bar", `cde`},
|
||||||
|
},
|
||||||
|
}, [][]Field{
|
||||||
|
{
|
||||||
|
{"foo", `"abc"`},
|
||||||
|
{"bar", `cde`},
|
||||||
|
{"x", `{"foo":"\"abc\"","bar":"cde"}`},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
// plain string into a single field
|
// plain string into a single field
|
||||||
f(`format foo as x`, [][]Field{
|
f(`format foo as x`, [][]Field{
|
||||||
{
|
{
|
||||||
|
@ -95,7 +109,7 @@ func TestPipeFormat(t *testing.T) {
|
||||||
})
|
})
|
||||||
|
|
||||||
// format into existing field
|
// format into existing field
|
||||||
f(`format "a<foo>aa<_msg>xx<a>x" as _msg`, [][]Field{
|
f(`format "a<foo>aa<_msg>xx<a>x"`, [][]Field{
|
||||||
{
|
{
|
||||||
{"_msg", `foobar`},
|
{"_msg", `foobar`},
|
||||||
{"a", "b"},
|
{"a", "b"},
|
||||||
|
|
|
@ -233,11 +233,6 @@ func expectPipeResults(t *testing.T, pipeStr string, rows, rowsExpected [][]Fiel
|
||||||
t.Fatalf("unexpected error when parsing %q: %s", pipeStr, err)
|
t.Fatalf("unexpected error when parsing %q: %s", pipeStr, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
pipeStrResult := p.String()
|
|
||||||
if pipeStrResult != pipeStr {
|
|
||||||
t.Fatalf("unexpected string representation for the pipe; got\n%s\nwant\n%s", pipeStrResult, pipeStr)
|
|
||||||
}
|
|
||||||
|
|
||||||
workersCount := 5
|
workersCount := 5
|
||||||
stopCh := make(chan struct{})
|
stopCh := make(chan struct{})
|
||||||
cancel := func() {}
|
cancel := func() {}
|
||||||
|
|
Loading…
Reference in a new issue