diff --git a/AUTHORS b/AUTHORS index 47c4075c..4879fb04 100644 --- a/AUTHORS +++ b/AUTHORS @@ -44,3 +44,4 @@ Cristian Chiru Peter Schiffer Pedro Pombeiro Fredrik Forséll +Tobias Gruetzmacher diff --git a/NEWS.md b/NEWS.md index 4dca2985..7f7cdae0 100644 --- a/NEWS.md +++ b/NEWS.md @@ -11,8 +11,12 @@ Features: `line-format` array that indicates that the width of the field should automatically be determined by the observed values. -* Number fields used in a `line-format` now default to - being right-aligned. +* Added bunyan log format from Tobias Gruetzmacher. +* Number fields used in a JSON log format `line-format` + array now default to being right-aligned. Also, added + `prefix` and `suffix` to `line-format` elements so a + string can optionally be prepended/appended if the value + is not empty. Bug Fixes: * Hidden values in JSON logs are now hidden by default. diff --git a/docs/schemas/format-v1.schema.json b/docs/schemas/format-v1.schema.json index 6e7c1243..fe781f95 100644 --- a/docs/schemas/format-v1.schema.json +++ b/docs/schemas/format-v1.schema.json @@ -512,6 +512,16 @@ "lowercase", "capitalize" ] + }, + "prefix": { + "title": "//line-format/prefix", + "description": "Text to prepend to the value", + "type": "string" + }, + "suffix": { + "title": "//line-format/suffix", + "description": "Text to append to the value", + "type": "string" } }, "additionalProperties": false diff --git a/docs/source/formats.rst b/docs/source/formats.rst index d805cf7f..15fb1d57 100644 --- a/docs/source/formats.rst +++ b/docs/source/formats.rst @@ -185,6 +185,10 @@ object with the following fields: in the current log message. The built-in default is "-". :text-transform: Transform the text in the field. Supported options are: none, uppercase, lowercase, capitalize + :prefix: Text to prepend to the value. If the value is empty, this prefix + will not be added. + :suffix: Text to append to the value. If the value is empty, this suffix + will not be added. :timestamp-field: The name of the field that contains the log message timestamp. Defaults to "timestamp". diff --git a/src/formats/bunyan_log.json b/src/formats/bunyan_log.json index 1bf966cd..4902d194 100644 --- a/src/formats/bunyan_log.json +++ b/src/formats/bunyan_log.json @@ -1,73 +1,105 @@ { - "$schema": "https://lnav.org/schemas/format-v1.schema.json", - "bunyan": { - "title": "Bunyan log", - "url": "https://github.com/trentm/node-bunyan", - "description": "Bunyan JSON logging library for node.js", - "json": true, - "line-format": [ - { - "field": "time" - }, - " ", - { - "field": "name" - }, - "[", - { - "field": "pid" - }, - "] ", - { - "field": "__level__", - "text-transform": "uppercase" - }, - " ", - { - "field": "msg" - } - ], - "level-field": "level", - "level": { - "fatal": "60", - "error": "50", - "warning": "40", - "info": "30", - "debug": "20", - "trace": "10" - }, - "value": { - "pid": { - "kind": "integer", - "identifier": true - }, - "name": { - "kind": "string", - "identifier": true - }, - "hostname": { - "kind": "string", - "identifier": true, - "hidden": true - }, - "time": { - "kind": "string", - "identifier": false - }, - "level": { - "kind": "integer", - "identifier": true, - "foreign-key": true - }, - "v": { - "kind": "integer", - "hidden": true - }, - "msg": { - "kind": "string" - } - }, - "timestamp-field": "time", - "body-field": "msg" - } + "$schema": "https://lnav.org/schemas/format-v1.schema.json", + "bunyan": { + "title": "Bunyan log", + "url": "https://github.com/trentm/node-bunyan", + "description": "Bunyan JSON logging library for node.js", + "json": true, + "line-format": [ + { + "field": "time" + }, + " ", + { + "field": "name" + }, + "[", + { + "field": "pid" + }, + "] ", + { + "field": "__level__", + "text-transform": "uppercase", + "auto-width": true + }, + { + "field": "src/file", + "default-value": "", + "prefix": "[" + }, + { + "field": "src/line", + "default-value": "", + "prefix": ":" + }, + { + "field": "src/func", + "default-value": "", + "prefix": ":", + "suffix": "]" + }, + " ", + { + "field": "msg" + } + ], + "level-field": "level", + "level": { + "fatal": 60, + "error": 50, + "warning": 40, + "info": 30, + "debug": 20, + "trace": 10 + }, + "value": { + "pid": { + "kind": "integer", + "identifier": true + }, + "name": { + "kind": "string", + "identifier": true + }, + "hostname": { + "kind": "string", + "identifier": true, + "hidden": true + }, + "time": { + "kind": "string", + "identifier": false + }, + "level": { + "kind": "integer", + "identifier": true, + "foreign-key": true + }, + "v": { + "kind": "integer", + "hidden": true + }, + "msg": { + "kind": "string" + }, + "src": { + "kind": "json", + "hidden": true + }, + "src/file": { + "kind": "string", + "identifier": true + }, + "src/line": { + "kind": "integer" + }, + "src/func": { + "kind": "string", + "identifier": true + } + }, + "timestamp-field": "time", + "body-field": "msg" + } } diff --git a/src/log_format.cc b/src/log_format.cc index c0dc4cb0..4f7598aa 100644 --- a/src/log_format.cc +++ b/src/log_format.cc @@ -1551,6 +1551,9 @@ external_log_format::get_subline(const logline& ll, auto str = lv_iter->to_string(); size_t nl_pos = str.find('\n'); + if (!jfe.jfe_prefix.empty()) { + this->json_append_to_cache(jfe.jfe_prefix); + } lr.lr_start = this->jlf_cached_line.size(); if ((int) str.size() > jfe.jfe_max_width) { @@ -1631,6 +1634,10 @@ external_log_format::get_subline(const logline& ll, this->jlf_line_values.lvv_values.begin(), lv_iter)] = true; + + if (!jfe.jfe_suffix.empty()) { + this->json_append_to_cache(jfe.jfe_suffix); + } } else if (jfe.jfe_value.pp_value == ts_field) { struct line_range lr; ssize_t ts_len; @@ -1670,13 +1677,25 @@ external_log_format::get_subline(const logline& ll, || jfe.jfe_value.pp_value == this->elf_level_field) { + const auto* level_name = ll.get_level_name(); + auto level_len = strlen(level_name); this->json_append( - jfe, nullptr, ll.get_level_name(), -1); - } else { + jfe, nullptr, level_name, level_len); + if (jfe.jfe_auto_width) { + this->json_append_to_cache(MAX_LEVEL_NAME_LEN + - level_len); + } + } else if (!jfe.jfe_default_value.empty()) { + if (!jfe.jfe_prefix.empty()) { + this->json_append_to_cache(jfe.jfe_prefix); + } this->json_append(jfe, nullptr, jfe.jfe_default_value.c_str(), jfe.jfe_default_value.size()); + if (!jfe.jfe_suffix.empty()) { + this->json_append_to_cache(jfe.jfe_suffix); + } } switch (jfe.jfe_text_transform) { diff --git a/src/log_format_ext.hh b/src/log_format_ext.hh index 99b70e9d..c475d967 100644 --- a/src/log_format_ext.hh +++ b/src/log_format_ext.hh @@ -256,6 +256,8 @@ public: overflow_t jfe_overflow{overflow_t::ABBREV}; transform_t jfe_text_transform{transform_t::NONE}; std::string jfe_ts_format; + std::string jfe_prefix; + std::string jfe_suffix; }; struct json_field_cmp { diff --git a/src/log_format_loader.cc b/src/log_format_loader.cc index 8db9f99c..4e75b0bb 100644 --- a/src/log_format_loader.cc +++ b/src/log_format_loader.cc @@ -511,6 +511,16 @@ static const struct json_path_container line_format_handlers = { .with_enum_values(TRANSFORM_ENUM) .for_field( &external_log_format::json_format_element::jfe_text_transform), + + yajlpp::property_handler("prefix") + .with_synopsis("") + .with_description("Text to prepend to the value") + .for_field(&external_log_format::json_format_element::jfe_prefix), + + yajlpp::property_handler("suffix") + .with_synopsis("") + .with_description("Text to append to the value") + .for_field(&external_log_format::json_format_element::jfe_suffix), }; static const json_path_handler_base::enum_value_t KIND_ENUM[] = { diff --git a/src/log_level.hh b/src/log_level.hh index 1c6cb32b..65d56452 100644 --- a/src/log_level.hh +++ b/src/log_level.hh @@ -38,6 +38,8 @@ extern const char* level_names[LEVEL__MAX + 1]; +constexpr size_t MAX_LEVEL_NAME_LEN = 8; + log_level_t string2level(const char* levelstr, ssize_t len = -1, bool exact = false); diff --git a/test/expected/expected.am b/test/expected/expected.am index 94fe4fa9..efb2cfc2 100644 --- a/test/expected/expected.am +++ b/test/expected/expected.am @@ -260,6 +260,8 @@ EXPECTED_FILES = \ $(srcdir)/%reldir%/test_json_format.sh_4315a3d6124c14cbe3c474b6dbf4cc8720a9859f.out \ $(srcdir)/%reldir%/test_json_format.sh_469f005b0708d629bc95f0c48a5e390f440c1fef.err \ $(srcdir)/%reldir%/test_json_format.sh_469f005b0708d629bc95f0c48a5e390f440c1fef.out \ + $(srcdir)/%reldir%/test_json_format.sh_6767b91d715338c24c67e928b59c560c84ddf4be.err \ + $(srcdir)/%reldir%/test_json_format.sh_6767b91d715338c24c67e928b59c560c84ddf4be.out \ $(srcdir)/%reldir%/test_json_format.sh_6fbe20faa161ab9fa77df7568fff84bf3e47e920.err \ $(srcdir)/%reldir%/test_json_format.sh_6fbe20faa161ab9fa77df7568fff84bf3e47e920.out \ $(srcdir)/%reldir%/test_json_format.sh_7724d1a96d74d4418dd44d7416270f9bb64b2564.err \ diff --git a/test/expected/test_json_format.sh_6767b91d715338c24c67e928b59c560c84ddf4be.err b/test/expected/test_json_format.sh_6767b91d715338c24c67e928b59c560c84ddf4be.err new file mode 100644 index 00000000..e69de29b diff --git a/test/expected/test_json_format.sh_6767b91d715338c24c67e928b59c560c84ddf4be.out b/test/expected/test_json_format.sh_6767b91d715338c24c67e928b59c560c84ddf4be.out new file mode 100644 index 00000000..19672db2 --- /dev/null +++ b/test/expected/test_json_format.sh_6767b91d715338c24c67e928b59c560c84ddf4be.out @@ -0,0 +1,42 @@ +2023-03-24T14:26:16.243 renovate[7] DEBUG Found gitlabci-include package files + logContext: qjifsaDDI + repository: webgui/custom-icons-transformer +2023-03-24T14:26:16.243 renovate[7] DEBUG Found npm package files + logContext: qjifsaDDI + repository: webgui/custom-icons-transformer +2023-03-24T14:26:16.243 renovate[7] DEBUG [/Users/trentm/tm/node-bunyan/examples/src.js:20:Wuzzle.woos] Found 3 package file(s) + logContext: qjifsaDDI + repository: webgui/custom-icons-transformer +2023-03-24T14:26:16.243 renovate[7] INFO Dependency extraction complete + logContext: qjifsaDDI + repository: webgui/custom-icons-transformer + baseBranch: main + stats: {"managers":{"gitlabci":{"fileCount":1,"depCount":1},"gitlabci-include":{"fileCount":1,"depCount":1},"npm":{"fileCount":1,"depCount":15}},"total":{"fileCount":3,"depCount":17}} +2023-03-24T14:26:16.390 renovate[7] DEBUG Dependency node has unsupported/unversioned value lts-bullseye-slim (versioning=docker) + logContext: qjifsaDDI + repository: webgui/custom-icons-transformer +2023-03-24T14:26:17.493 renovate[7] DEBUG Release 2.8.7 is pending status checks + logContext: qjifsaDDI + repository: webgui/custom-icons-transformer + depName: prettier + check: stabilityDays +2023-03-24T14:26:17.897 renovate[7] DEBUG Release 4.4.1 is pending status checks + logContext: qjifsaDDI + repository: webgui/custom-icons-transformer + depName: rimraf + check: stabilityDays +2023-03-24T14:26:17.897 renovate[7] DEBUG All releases are pending - using latest + logContext: qjifsaDDI + repository: webgui/custom-icons-transformer + depName: rimraf + bucket: non-major +2023-03-24T14:26:18.330 renovate[7] DEBUG Release 2.10.0 is pending status checks + logContext: qjifsaDDI + repository: webgui/custom-icons-transformer + depName: prettier-plugin-svelte + check: stabilityDays +2023-03-24T14:26:18.331 renovate[7] DEBUG All releases are pending - using latest + logContext: qjifsaDDI + repository: webgui/custom-icons-transformer + depName: prettier-plugin-svelte + bucket: non-major diff --git a/test/logfile_bunyan.0 b/test/logfile_bunyan.0 index a1013ad8..facc246e 100644 --- a/test/logfile_bunyan.0 +++ b/test/logfile_bunyan.0 @@ -1,6 +1,6 @@ {"name":"renovate","hostname":"renovate-gitlab-67c4bcb5-9ggbv","pid":7,"level":20,"logContext":"qjifsaDDI","repository":"webgui/custom-icons-transformer","msg":"Found gitlabci-include package files","time":"2023-03-24T14:26:16.243Z","v":0} {"name":"renovate","hostname":"renovate-gitlab-67c4bcb5-9ggbv","pid":7,"level":20,"logContext":"qjifsaDDI","repository":"webgui/custom-icons-transformer","msg":"Found npm package files","time":"2023-03-24T14:26:16.243Z","v":0} -{"name":"renovate","hostname":"renovate-gitlab-67c4bcb5-9ggbv","pid":7,"level":20,"logContext":"qjifsaDDI","repository":"webgui/custom-icons-transformer","msg":"Found 3 package file(s)","time":"2023-03-24T14:26:16.243Z","v":0} +{"name":"renovate","hostname":"renovate-gitlab-67c4bcb5-9ggbv","pid":7,"level":20,"logContext":"qjifsaDDI","repository":"webgui/custom-icons-transformer","msg":"Found 3 package file(s)","time":"2023-03-24T14:26:16.243Z","v":0,"src":{"file": "/Users/trentm/tm/node-bunyan/examples/src.js","line": 20,"func": "Wuzzle.woos"}} {"name":"renovate","hostname":"renovate-gitlab-67c4bcb5-9ggbv","pid":7,"level":30,"logContext":"qjifsaDDI","repository":"webgui/custom-icons-transformer","baseBranch":"main","stats":{"managers":{"gitlabci":{"fileCount":1,"depCount":1},"gitlabci-include":{"fileCount":1,"depCount":1},"npm":{"fileCount":1,"depCount":15}},"total":{"fileCount":3,"depCount":17}},"msg":"Dependency extraction complete","time":"2023-03-24T14:26:16.243Z","v":0} {"name":"renovate","hostname":"renovate-gitlab-67c4bcb5-9ggbv","pid":7,"level":20,"logContext":"qjifsaDDI","repository":"webgui/custom-icons-transformer","msg":"Dependency node has unsupported/unversioned value lts-bullseye-slim (versioning=docker)","time":"2023-03-24T14:26:16.390Z","v":0} {"name":"renovate","hostname":"renovate-gitlab-67c4bcb5-9ggbv","pid":7,"level":20,"logContext":"qjifsaDDI","repository":"webgui/custom-icons-transformer","depName":"prettier","check":"stabilityDays","msg":"Release 2.8.7 is pending status checks","time":"2023-03-24T14:26:17.493Z","v":0} diff --git a/test/test_json_format.sh b/test/test_json_format.sh index ace7bf6c..496f4d78 100644 --- a/test/test_json_format.sh +++ b/test/test_json_format.sh @@ -144,3 +144,6 @@ run_cap_test ${lnav_test} -n \ run_cap_test ${lnav_test} -n \ -I ${test_dir} \ ${test_dir}/logfile_json_subsec.json + +run_cap_test ${lnav_test} -n \ + ${test_dir}/logfile_bunyan.0