diff --git a/NEWS b/NEWS index 693c8a2b..3e901598 100644 --- a/NEWS +++ b/NEWS @@ -9,9 +9,18 @@ lnav v0.10.1: the "tuning" section of the configuration. * Added an "lnav_version()" SQL function that returns the current version string. + * Added basic support for the logfmt file format. Currently, only files + whose lines are entirely logfmt-encoded are supported. The lines + must also contain either a field named "time" or "ts" that contains + the timestamp. + * Added the "logfmt2json()" SQL function to convert a string containing + a logfmt-encoded message into a JSON object that can be operated on + more easily. Interface changes: * The xclip implementation for accessing the system clipboard now writes to the "clipboard" selection instead of the "primary" selection. + * The 'query' bookmark type and y/Y hotkeys have been removed due to + performance issues and the functionality is probably rarely used. Bug Fixes: * The text "send-input" would show up on some terminals instead of ignoring the escape sequence. This control sequence was only diff --git a/aminclude_static.am b/aminclude_static.am index 783d3e50..b5ac46fa 100644 --- a/aminclude_static.am +++ b/aminclude_static.am @@ -1,6 +1,6 @@ # aminclude_static.am generated automatically by Autoconf -# from AX_AM_MACROS_STATIC on Fri Oct 1 09:22:49 PDT 2021 +# from AX_AM_MACROS_STATIC on Sat Oct 2 23:15:38 PDT 2021 # Code coverage diff --git a/configure.ac b/configure.ac index 4de14490..fcfb8261 100644 --- a/configure.ac +++ b/configure.ac @@ -312,6 +312,7 @@ AC_CONFIG_FILES([Makefile]) AC_CONFIG_FILES([TESTS_ENVIRONMENT]) AC_CONFIG_FILES([src/Makefile]) AC_CONFIG_FILES([src/base/Makefile]) +AC_CONFIG_FILES([src/formats/logfmt/Makefile]) AC_CONFIG_FILES([src/fmtlib/Makefile]) AC_CONFIG_FILES([src/pcrepp/Makefile]) AC_CONFIG_FILES([src/pugixml/Makefile]) diff --git a/docs/source/formats.rst b/docs/source/formats.rst index 14e1dde9..acc277a5 100644 --- a/docs/source/formats.rst +++ b/docs/source/formats.rst @@ -34,6 +34,14 @@ supported: self-describing, so **lnav** will read the header to determine the shape of the file. +There is also basic support for the `logfmt `_ +convention for formatting log messages. Files that use this format must +have the entire line be key/value pairs and the timestamp contained in a +field named :code:`time` or :code:`ts`. If the file you're using does not +quite follow this formatting, but wraps logfmt data with another recognized +format, you can use the :ref:`logfmt2json` SQL function to convert the data +into JSON for further analysis. + Defining a New Format --------------------- diff --git a/docs/source/hotkeys.rst b/docs/source/hotkeys.rst index f33a3ba6..e2f9c200 100644 --- a/docs/source/hotkeys.rst +++ b/docs/source/hotkeys.rst @@ -89,10 +89,6 @@ Spatial Navigation - :kbd:`Shift` + :kbd:`o` - - Forward/backward through log messages with a matching "opid" field - * - :kbd:`y` - - :kbd:`Shift` + :kbd:`y` - - - - Next/previous SQL result * - :kbd:`s` - :kbd:`Shift` + :kbd:`s` - diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index e38c09e6..d8154bb7 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -17,6 +17,7 @@ add_subdirectory(base) add_subdirectory(pcrepp) add_subdirectory(remote) add_subdirectory(tailer) +add_subdirectory(formats/logfmt) add_executable(bin2c bin2c.hh tools/bin2c.c) target_link_libraries(bin2c ZLIB::zlib) @@ -460,6 +461,7 @@ target_link_libraries( tailerservice tailerpp tailercommon + logfmt ${lnav_LIBS}) target_compile_definitions(diag PRIVATE SQLITE_OMIT_LOAD_EXTENSION) diff --git a/src/Makefile.am b/src/Makefile.am index f7febe5f..532f9d68 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,7 +1,7 @@ include $(top_srcdir)/aminclude_static.am -SUBDIRS = tools fmtlib pcrepp base tailer pugixml yajl yajlpp . +SUBDIRS = tools fmtlib pcrepp base tailer pugixml yajl yajlpp formats/logfmt . bin_PROGRAMS = lnav @@ -128,6 +128,7 @@ AM_CPPFLAGS = \ LDADD = \ libdiag.a \ base/libbase.a \ + formats/logfmt/liblogfmt.a \ fmtlib/libcppfmt.a \ pcrepp/libpcrepp.a \ pugixml/libpugixml.a \ diff --git a/src/base/CMakeLists.txt b/src/base/CMakeLists.txt index 6d4b8350..f8a39994 100644 --- a/src/base/CMakeLists.txt +++ b/src/base/CMakeLists.txt @@ -46,6 +46,7 @@ add_executable( humanize.file_size.tests.cc humanize.network.tests.cc humanize.time.tests.cc + intern_string.tests.cc lnav.gzip.tests.cc string_util.tests.cc network.tcp.tests.cc diff --git a/src/base/Makefile.am b/src/base/Makefile.am index 54b83e90..e73ade65 100644 --- a/src/base/Makefile.am +++ b/src/base/Makefile.am @@ -69,6 +69,7 @@ test_base_SOURCES = \ humanize.file_size.tests.cc \ humanize.network.tests.cc \ humanize.time.tests.cc \ + intern_string.tests.cc \ lnav.gzip.tests.cc \ string_util.tests.cc \ test_base.cc diff --git a/src/base/intern_string.hh b/src/base/intern_string.hh index 0ee5d2f4..80308d78 100644 --- a/src/base/intern_string.hh +++ b/src/base/intern_string.hh @@ -44,7 +44,7 @@ struct string_fragment { using iterator = const char *; - explicit string_fragment(const char *str, int begin = 0, int end = -1) + explicit string_fragment(const char *str = "", int begin = 0, int end = -1) : sf_string(str), sf_begin(begin), sf_end(end == -1 ? strlen(str) : end) { }; @@ -80,7 +80,7 @@ struct string_fragment { } bool empty() const { - return length() == 0; + return !this->is_valid() || length() == 0; }; char operator[](int index) const { @@ -154,6 +154,111 @@ struct string_fragment { return nonstd::nullopt; } + template + nonstd::optional consume(P predicate) const { + int consumed = 0; + while (consumed < this->length()) { + if (!predicate(this->data()[consumed])) { + break; + } + + consumed += 1; + } + + if (consumed == 0) { + return nonstd::nullopt; + } + + return string_fragment{ + this->sf_string, + this->sf_begin + consumed, + this->sf_end, + }; + } + + nonstd::optional consume_n(int amount) const { + if (amount > this->length()) { + return nonstd::nullopt; + } + + return string_fragment{ + this->sf_string, + this->sf_begin + amount, + this->sf_end, + }; + } + + template + string_fragment skip(P predicate) const { + int offset = 0; + while (offset < this->length() && predicate(this->data()[offset])) { + offset += 1; + } + + return string_fragment{ + this->sf_string, + this->sf_begin + offset, + this->sf_end, + }; + } + + using split_result = nonstd::optional>; + + template + split_result split_while(P& predicate) const { + int consumed = 0; + while (consumed < this->length()) { + if (!predicate(this->data()[consumed])) { + break; + } + + consumed += 1; + } + + if (consumed == 0) { + return nonstd::nullopt; + } + + return std::make_pair( + string_fragment{ + this->sf_string, + this->sf_begin, + this->sf_begin + consumed, + }, + string_fragment{ + this->sf_string, + this->sf_begin + consumed, + this->sf_end, + } + ); + } + + struct tag1 { + const char t_value; + + bool operator()(char ch) const { + return this->t_value == ch; + } + }; + + struct quoted_string_body { + bool qs_in_escape{false}; + + bool operator()(char ch) { + if (this->qs_in_escape) { + this->qs_in_escape = false; + return true; + } else if (ch == '\\') { + this->qs_in_escape = true; + return true; + } else if (ch == '"') { + return false; + } else { + return true; + } + } + }; + const char *to_string(char *buf) const { memcpy(buf, this->data(), this->length()); buf[this->length()] = '\0'; diff --git a/src/base/string_util.cc b/src/base/string_util.cc index eaa845a8..661add42 100644 --- a/src/base/string_util.cc +++ b/src/base/string_util.cc @@ -216,3 +216,34 @@ std::string center_str(const std::string &subject, size_t width) return retval; } + +template +size_t strtonum(T &num_out, const char *string, size_t len) +{ + size_t retval = 0; + T sign = 1; + + num_out = 0; + + for (; retval < len && isspace(string[retval]); retval++); + for (; retval < len && string[retval] == '-'; retval++) { + sign *= -1; + } + for (; retval < len && string[retval] == '+'; retval++); + for (; retval < len && isdigit(string[retval]); retval++) { + num_out *= 10; + num_out += string[retval] - '0'; + } + num_out *= sign; + + return retval; +} + +template +size_t strtonum(long long &num_out, const char *string, size_t len); + +template +size_t strtonum(long &num_out, const char *string, size_t len); + +template +size_t strtonum(int &num_out, const char *string, size_t len); diff --git a/src/base/string_util.hh b/src/base/string_util.hh index e79e9cdc..198853ad 100644 --- a/src/base/string_util.hh +++ b/src/base/string_util.hh @@ -176,4 +176,7 @@ std::string repeat(const std::string& input, size_t num); std::string center_str(const std::string& subject, size_t width); +template +size_t strtonum(T &num_out, const char *data, size_t len); + #endif diff --git a/src/command_executor.cc b/src/command_executor.cc index f59ceb5d..8c4fe2c8 100644 --- a/src/command_executor.cc +++ b/src/command_executor.cc @@ -51,8 +51,6 @@ using namespace std; exec_context INIT_EXEC_CONTEXT; -bookmark_type_t BM_QUERY("query"); - static const string MSG_FORMAT_STMT = R"( SELECT count(*) AS total, min(log_line) AS log_line, log_msg_format FROM all_logs @@ -345,8 +343,6 @@ Result execute_sql(exec_context &ec, const string &sql, string & retval = ec.ec_accumulator.get_string(); } else if (!dls.dls_rows.empty()) { - vis_bookmarks &bm = lnav_data.ld_views[LNV_LOG].get_bookmarks(); - if (lnav_data.ld_flags & LNF_HEADLESS) { if (ec.ec_local_vars.size() == 1) { ensure_view(&lnav_data.ld_views[LNV_DB]); @@ -355,13 +351,6 @@ Result execute_sql(exec_context &ec, const string &sql, string & retval = ""; alt_msg = ""; } - else if (dls.dls_headers.size() == 1 && !bm[&BM_QUERY].empty()) { - retval = ""; - alt_msg = HELP_MSG_2( - y, Y, - "to move forward/backward through query results " - "in the log view"); - } else if (dls.dls_rows.size() == 1) { auto &row = dls.dls_rows[0]; @@ -727,7 +716,6 @@ int sql_callback(exec_context &ec, sqlite3_stmt *stmt) if (!sqlite3_stmt_busy(stmt)) { dls.clear(); - lnav_data.ld_log_source.text_clear_marks(&BM_QUERY); return 0; } @@ -774,16 +762,6 @@ int sql_callback(exec_context &ec, sqlite3_stmt *stmt) break; } } - if (value != nullptr && - (dls.dls_headers[lpc].hm_name == "log_line" || - strstr(dls.dls_headers[lpc].hm_name.c_str(), "log_line"))) { - int line_number = -1; - - if (sscanf(value, "%d", &line_number) == 1) { - lnav_data.ld_views[LNV_LOG].toggle_user_mark( - &BM_QUERY, vis_line_t(line_number)); - } - } } return retval; diff --git a/src/command_executor.hh b/src/command_executor.hh index fda88b2c..e34ad49a 100644 --- a/src/command_executor.hh +++ b/src/command_executor.hh @@ -177,6 +177,4 @@ void sql_progress_finished(); void add_global_vars(exec_context &ec); -extern bookmark_type_t BM_QUERY; - #endif //LNAV_COMMAND_EXECUTOR_H diff --git a/src/formats/logfmt/CMakeLists.txt b/src/formats/logfmt/CMakeLists.txt new file mode 100644 index 00000000..0d780c7d --- /dev/null +++ b/src/formats/logfmt/CMakeLists.txt @@ -0,0 +1,36 @@ + +add_library( + logfmt + STATIC + logfmt.parser.hh + logfmt.parser.cc +) + +target_include_directories( + logfmt + PUBLIC + . + ${CMAKE_BINARY_DIR}/src + ${CMAKE_SOURCE_DIR}/src + ${CMAKE_SOURCE_DIR}/src/fmtlib +) + +add_executable( + logfmt.parser.test + logfmt.parser.test.cc +) +target_include_directories( + logfmt.parser.test + PUBLIC + . + ${CMAKE_BINARY_DIR}/src + ${CMAKE_SOURCE_DIR}/src + ${CMAKE_SOURCE_DIR}/src/fmtlib + ../../third-party/doctest-root) +target_link_libraries( + logfmt.parser.test + logfmt + base +) + +add_test(NAME logfmt.parser.test COMMAND logfmt.parser.test) diff --git a/src/formats/logfmt/Makefile.am b/src/formats/logfmt/Makefile.am new file mode 100644 index 00000000..d44e8ef4 --- /dev/null +++ b/src/formats/logfmt/Makefile.am @@ -0,0 +1,38 @@ + +include $(top_srcdir)/aminclude_static.am + +AM_CPPFLAGS = \ + $(CODE_COVERAGE_CPPFLAGS) \ + -Wall \ + -I$(top_srcdir)/src/ \ + -I$(top_srcdir)/src/third-party \ + -I$(top_srcdir)/src/fmtlib \ + $(LIBARCHIVE_CFLAGS) \ + $(READLINE_CFLAGS) \ + $(SQLITE3_CFLAGS) \ + $(LIBCURL_CPPFLAGS) + +AM_LIBS = $(CODE_COVERAGE_LIBS) +AM_CFLAGS = $(CODE_COVERAGE_CFLAGS) +AM_CXXFLAGS = $(CODE_COVERAGE_CXXFLAGS) + +noinst_LIBRARIES = liblogfmt.a + +noinst_HEADERS = \ + logfmt.parser.hh + +liblogfmt_a_SOURCES = \ + logfmt.parser.cc + +check_PROGRAMS = \ + logfmt.parser.test + +logfmt_parser_test_SOURCES = \ + logfmt.parser.test.cc + +logfmt_parser_test_LDADD = \ + $(top_builddir)/src/base/libbase.a \ + liblogfmt.a + +TESTS = \ + logfmt.parser.test diff --git a/src/formats/logfmt/logfmt.parser.cc b/src/formats/logfmt/logfmt.parser.cc new file mode 100644 index 00000000..6231c09d --- /dev/null +++ b/src/formats/logfmt/logfmt.parser.cc @@ -0,0 +1,256 @@ +/** + * Copyright (c) 2021, Timothy Stack + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Timothy Stack nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @file logfmt.parser.cc + */ + +#include "config.h" + +#include "base/string_util.hh" +#include "logfmt.parser.hh" + +logfmt::parser::parser(string_fragment sf) + : p_next_input(sf) +{ + +} + +static bool is_not_eq(char ch) +{ + return ch != '='; +} + +struct bare_value_predicate { + enum class int_state_t { + INIT, + NEED_DIGIT, + DIGITS, + INVALID, + }; + + enum class float_state_t { + INIT, + NEED_DIGIT, + DIGITS, + FRACTION_DIGIT, + EXPONENT_INIT, + EXPONENT_NEED_DIGIT, + EXPONENT_DIGIT, + INVALID, + }; + + int_state_t bvp_int_state{int_state_t::INIT}; + float_state_t bvp_float_state{float_state_t::INIT}; + size_t bvp_index{0}; + + bool is_integer() const { + return this->bvp_int_state == int_state_t::DIGITS; + } + + bool is_float() const { + switch (this->bvp_float_state) { + case float_state_t::DIGITS: + case float_state_t::FRACTION_DIGIT: + case float_state_t::EXPONENT_DIGIT: + return true; + default: + return false; + } + } + + bool operator()(char ch) { + if (ch == ' ') { + return false; + } + + bool got_digit = isdigit(ch); + switch (this->bvp_int_state) { + case int_state_t::INIT: + if (got_digit) { + this->bvp_int_state = int_state_t::DIGITS; + } else if (ch == '-') { + this->bvp_int_state = int_state_t::NEED_DIGIT; + } else { + this->bvp_int_state = int_state_t::INVALID; + } + break; + case int_state_t::DIGITS: + case int_state_t::NEED_DIGIT: + if (got_digit) { + this->bvp_int_state = int_state_t::DIGITS; + } else { + this->bvp_int_state = int_state_t::INVALID; + } + break; + case int_state_t::INVALID: + break; + } + + switch (this->bvp_float_state) { + case float_state_t::INIT: + if (got_digit) { + this->bvp_float_state = float_state_t::DIGITS; + } else if (ch == '-') { + this->bvp_float_state = float_state_t::NEED_DIGIT; + } else { + this->bvp_float_state = float_state_t::INVALID; + } + break; + case float_state_t::DIGITS: + case float_state_t::NEED_DIGIT: + if (got_digit) { + this->bvp_float_state = float_state_t::DIGITS; + } else if (ch == '.') { + this->bvp_float_state = float_state_t::FRACTION_DIGIT; + } else if (ch == 'e' || ch == 'E') { + this->bvp_float_state = float_state_t::EXPONENT_INIT; + } else { + this->bvp_float_state = float_state_t::INVALID; + } + break; + case float_state_t::FRACTION_DIGIT: + if (got_digit) { + this->bvp_float_state = float_state_t::FRACTION_DIGIT; + } else if (ch == 'e' || ch == 'E') { + this->bvp_float_state = float_state_t::EXPONENT_INIT; + } else { + this->bvp_float_state = float_state_t::INVALID; + } + break; + case float_state_t::EXPONENT_INIT: + if (got_digit) { + this->bvp_float_state = float_state_t::EXPONENT_DIGIT; + } else if (ch == '-' || ch == '+') { + this->bvp_float_state = float_state_t::EXPONENT_NEED_DIGIT; + } else { + this->bvp_float_state = float_state_t::INVALID; + } + break; + case float_state_t::EXPONENT_NEED_DIGIT: + case float_state_t::EXPONENT_DIGIT: + if (got_digit) { + this->bvp_float_state = float_state_t::EXPONENT_DIGIT; + } else { + this->bvp_float_state = float_state_t::INVALID; + } + break; + case float_state_t::INVALID: + break; + } + + this->bvp_index += 1; + + return true; + } +}; + +logfmt::parser::step_result logfmt::parser::step() +{ + const static auto IS_DQ = string_fragment::tag1{'"'}; + + auto remaining = this->p_next_input.skip(isspace); + + if (remaining.empty()) { + return end_of_input{}; + } + + auto pair_opt = remaining.split_while(is_not_eq); + + if (!pair_opt) { + return error{remaining.sf_begin, "expecting key followed by '='"}; + } + + auto key_frag = pair_opt->first; + auto after_eq = pair_opt->second.consume(string_fragment::tag1{'='}); + + if (!after_eq) { + return error{pair_opt->second.sf_begin, "expecting '='"}; + } + + auto value_start = after_eq.value(); + + if (value_start.startswith("\"")) { + string_fragment::quoted_string_body qsb; + auto quoted_pair = value_start.consume_n(1)->split_while(qsb); + + if (!quoted_pair) { + return error{value_start.sf_begin + 1, "string body missing"}; + } + + auto after_quote = quoted_pair->second.consume(IS_DQ); + + if (!after_quote) { + return error{quoted_pair->second.sf_begin, "non-terminated string"}; + } + + this->p_next_input = after_quote.value(); + return std::make_pair(key_frag, quoted_value{string_fragment{ + quoted_pair->first.sf_string, + quoted_pair->first.sf_begin - 1, + quoted_pair->first.sf_end + 1 + }}); + } else { + bare_value_predicate bvp; + auto value_pair = value_start.split_while(bvp); + + if (value_pair) { + static const auto TRUE_FRAG = string_fragment{"true"}; + static const auto FALSE_FRAG = string_fragment{"false"}; + + this->p_next_input = value_pair->second; + if (bvp.is_integer()) { + int_value retval; + + strtonum(retval.iv_value, + value_pair->first.data(), + value_pair->first.length()); + retval.iv_str_value = value_pair->first; + + return std::make_pair(key_frag, retval); + } else if (bvp.is_float()) { + char float_copy[value_pair->first.length() + 1]; + float_value retval; + + strncpy(float_copy, value_pair->first.data(), value_pair->first.length()); + float_copy[value_pair->first.length()] = '\0'; + retval.fv_value = strtod(float_copy, nullptr); + retval.fv_str_value = value_pair->first; + + return std::make_pair(key_frag, retval); + } else if (value_pair->first.iequal(TRUE_FRAG)) { + return std::make_pair(key_frag, bool_value{true, value_pair->first}); + } else if (value_pair->first.iequal(FALSE_FRAG)) { + return std::make_pair(key_frag, bool_value{false, value_pair->first}); + } + return std::make_pair(key_frag, unquoted_value{value_pair->first}); + } else { + this->p_next_input = value_start; + return std::make_pair(key_frag, unquoted_value{string_fragment{""}}); + } + } +} diff --git a/src/formats/logfmt/logfmt.parser.hh b/src/formats/logfmt/logfmt.parser.hh new file mode 100644 index 00000000..78060010 --- /dev/null +++ b/src/formats/logfmt/logfmt.parser.hh @@ -0,0 +1,91 @@ +/** + * Copyright (c) 2021, Timothy Stack + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Timothy Stack nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @file logfmt.parser.hh + */ + +#ifndef lnav_logfmt_parser_hh +#define lnav_logfmt_parser_hh + +#include "base/intern_string.hh" +#include "base/result.h" +#include "mapbox/variant.hpp" + +namespace logfmt { + +class parser { +public: + explicit parser(string_fragment sf); + + struct end_of_input {}; + struct error { + int e_offset; + const std::string e_msg; + }; + struct unquoted_value { + string_fragment uv_value; + }; + struct quoted_value { + string_fragment qv_value; + }; + struct bool_value { + bool bv_value{false}; + string_fragment bv_str_value; + }; + struct int_value { + int64_t iv_value{0}; + string_fragment iv_str_value; + }; + struct float_value { + double fv_value{0}; + string_fragment fv_str_value; + }; + using value_type = mapbox::util::variant< + bool_value, + int_value, + float_value, + unquoted_value, + quoted_value + >; + + using kvpair = std::pair; + + using step_result = mapbox::util::variant< + end_of_input, + kvpair, + error + >; + + step_result step(); +private: + string_fragment p_next_input; +}; + +} + +#endif diff --git a/src/formats/logfmt/logfmt.parser.test.cc b/src/formats/logfmt/logfmt.parser.test.cc new file mode 100644 index 00000000..2193bfed --- /dev/null +++ b/src/formats/logfmt/logfmt.parser.test.cc @@ -0,0 +1,221 @@ +/** + * Copyright (c) 2021, Timothy Stack + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Timothy Stack nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @file logfmt.parser.test.cc + */ + +#include "config.h" + +#include + +#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN +#include "doctest/doctest.h" + +#include "logfmt.parser.hh" + +TEST_CASE("basic") +{ + static const char *line = "abc=def ghi=\"1 2 3 4\" time=333 empty1= tf=true empty2="; + + auto p = logfmt::parser{string_fragment{line}}; + + auto pair1 = p.step(); + + CHECK(pair1.is()); + CHECK(pair1.get().first == "abc"); + CHECK(pair1.get().second + .get().uv_value == "def"); + + auto pair2 = p.step(); + + CHECK(pair2.is()); + CHECK(pair2.get().first == "ghi"); + CHECK(pair2.get().second + .get().qv_value == "\"1 2 3 4\""); + + auto pair3 = p.step(); + + CHECK(pair3.is()); + CHECK(pair3.get().first == "time"); + CHECK(pair3.get().second + .get().iv_value == 333); + + auto pair4 = p.step(); + + CHECK(pair4.is()); + CHECK(pair4.get().first == "empty1"); + CHECK(pair4.get().second + .get().uv_value == ""); + + auto pair5 = p.step(); + + CHECK(pair5.is()); + CHECK(pair5.get().first == "tf"); + CHECK(pair5.get().second + .get().bv_value); + + auto pair6 = p.step(); + + CHECK(pair6.is()); + CHECK(pair6.get().first == "empty2"); + CHECK(pair6.get().second + .get().uv_value == ""); + + auto eoi = p.step(); + CHECK(eoi.is()); +} + +TEST_CASE("floats") +{ + static const char *line = "f1=1.0 f2=-2.0 f3=1.2e3 f4=1.2e-2 f5=2e1 f6=2e+1"; + + auto p = logfmt::parser{string_fragment{line}}; + + auto pair1 = p.step(); + + CHECK(pair1.is()); + CHECK(pair1.get().first == "f1"); + CHECK(pair1.get().second + .get().fv_value == 1.0); + + auto pair2 = p.step(); + + CHECK(pair2.is()); + CHECK(pair2.get().first == "f2"); + CHECK(pair2.get().second + .get().fv_value == -2.0); + + auto pair3 = p.step(); + + CHECK(pair3.is()); + CHECK(pair3.get().first == "f3"); + CHECK(pair3.get().second + .get().fv_value == 1200); + + auto pair4 = p.step(); + + CHECK(pair4.is()); + CHECK(pair4.get().first == "f4"); + CHECK(pair4.get().second + .get().fv_value == 0.012); + + auto pair5 = p.step(); + + CHECK(pair5.is()); + CHECK(pair5.get().first == "f5"); + CHECK(pair5.get().second + .get().fv_value == 20); + + auto pair6 = p.step(); + + CHECK(pair6.is()); + CHECK(pair6.get().first == "f6"); + CHECK(pair6.get().second + .get().fv_value == 20); +} + +TEST_CASE("bad floats") +{ + static const char *line = "bf1=- bf2=-1.2e bf3=1.2.3 bf4=1e2e4"; + + auto p = logfmt::parser{string_fragment{line}}; + + auto pair1 = p.step(); + + CHECK(pair1.is()); + CHECK(pair1.get().first == "bf1"); + CHECK(pair1.get().second + .get().uv_value == "-"); + + auto pair2 = p.step(); + + CHECK(pair2.is()); + CHECK(pair2.get().first == "bf2"); + CHECK(pair2.get().second + .get().uv_value == "-1.2e"); + + auto pair3 = p.step(); + + CHECK(pair3.is()); + CHECK(pair3.get().first == "bf3"); + CHECK(pair3.get().second + .get().uv_value == "1.2.3"); + + auto pair4 = p.step(); + + CHECK(pair4.is()); + CHECK(pair4.get().first == "bf4"); + CHECK(pair4.get().second + .get().uv_value == "1e2e4"); +} + +TEST_CASE("non-terminated string") +{ + static const char *line = "abc=\"12 2"; + + auto p = logfmt::parser{string_fragment{line}}; + auto pair1 = p.step(); + + CHECK(pair1.is()); + CHECK(pair1.get().e_offset == 9); + CHECK(pair1.get().e_msg == "non-terminated string"); +} + +TEST_CASE("missing equals") +{ + static const char *line = "abc"; + + auto p = logfmt::parser{string_fragment{line}}; + auto pair1 = p.step(); + + CHECK(pair1.is()); + CHECK(pair1.get().e_offset == 3); + CHECK(pair1.get().e_msg == "expecting '='"); +} + +TEST_CASE("missing key") +{ + static const char *line = "=def"; + + auto p = logfmt::parser{string_fragment{line}}; + auto pair1 = p.step(); + + CHECK(pair1.is()); + CHECK(pair1.get().e_offset == 0); + CHECK(pair1.get().e_msg == "expecting key followed by '='"); +} + +TEST_CASE("empty") +{ + static const char *line = ""; + + auto p = logfmt::parser{string_fragment{line}}; + auto pair1 = p.step(); + + CHECK(pair1.is()); +} diff --git a/src/help.txt b/src/help.txt index 34abdd1b..8eed1520 100644 --- a/src/help.txt +++ b/src/help.txt @@ -234,9 +234,6 @@ Spatial Navigation also jump to the start of any log partitions that have been created with the 'partition-name' command. - y/Y Move forward/backward through the log view based on the - "log_line" column in the SQL result view. - s/S Move to the next/previous "slow down" in the log message rate. A slow down is detected by measuring how quickly the message rate has changed over the previous several diff --git a/src/internals/sql-ref.rst b/src/internals/sql-ref.rst index 9c0b2721..fac2ed9e 100644 --- a/src/internals/sql-ref.rst +++ b/src/internals/sql-ref.rst @@ -771,7 +771,7 @@ char(*X*) HI **See Also** - :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash_agg`, :ref:`humanize_file_size`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`sparkline`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`upper`, :ref:`xpath` + :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash_agg`, :ref:`humanize_file_size`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`logfmt2json`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`sparkline`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`upper`, :ref:`xpath` ---- @@ -804,7 +804,7 @@ charindex(*needle*, *haystack*, *\[start\]*) 0 **See Also** - :ref:`char`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash_agg`, :ref:`humanize_file_size`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`sparkline`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`upper`, :ref:`xpath` + :ref:`char`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash_agg`, :ref:`humanize_file_size`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`logfmt2json`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`sparkline`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`upper`, :ref:`xpath` ---- @@ -1068,7 +1068,7 @@ endswith(*str*, *suffix*) 0 **See Also** - :ref:`char`, :ref:`charindex`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash_agg`, :ref:`humanize_file_size`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`sparkline`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`upper`, :ref:`xpath` + :ref:`char`, :ref:`charindex`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash_agg`, :ref:`humanize_file_size`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`logfmt2json`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`sparkline`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`upper`, :ref:`xpath` ---- @@ -1123,7 +1123,7 @@ extract(*str*) {"col_0":1.0,"col_1":2.0} **See Also** - :ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`group_concat`, :ref:`group_spooky_hash_agg`, :ref:`humanize_file_size`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`sparkline`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`upper`, :ref:`xpath` + :ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`group_concat`, :ref:`group_spooky_hash_agg`, :ref:`humanize_file_size`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`logfmt2json`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`sparkline`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`upper`, :ref:`xpath` ---- @@ -1324,7 +1324,7 @@ group_concat(*X*, *\[sep\]*) hw,gw **See Also** - :ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_spooky_hash_agg`, :ref:`humanize_file_size`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`sparkline`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`upper`, :ref:`xpath` + :ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_spooky_hash_agg`, :ref:`humanize_file_size`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`logfmt2json`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`sparkline`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`upper`, :ref:`xpath` ---- @@ -1348,7 +1348,7 @@ group_spooky_hash(*str*) 4e7a190aead058cb123c94290f29c34a **See Also** - :ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`humanize_file_size`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`sparkline`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`upper`, :ref:`xpath` + :ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`humanize_file_size`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`logfmt2json`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`sparkline`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`upper`, :ref:`xpath` ---- @@ -1394,7 +1394,7 @@ humanize_file_size(*value*) 10.0MB **See Also** - :ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash_agg`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`sparkline`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`upper`, :ref:`xpath` + :ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash_agg`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`logfmt2json`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`sparkline`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`upper`, :ref:`xpath` ---- @@ -1442,7 +1442,7 @@ instr(*haystack*, *needle*) 2 **See Also** - :ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash_agg`, :ref:`humanize_file_size`, :ref:`leftstr`, :ref:`length`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`sparkline`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`upper`, :ref:`xpath` + :ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash_agg`, :ref:`humanize_file_size`, :ref:`leftstr`, :ref:`length`, :ref:`logfmt2json`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`sparkline`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`upper`, :ref:`xpath` ---- @@ -1795,7 +1795,7 @@ leftstr(*str*, *N*) abc **See Also** - :ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash_agg`, :ref:`humanize_file_size`, :ref:`instr`, :ref:`length`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`sparkline`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`upper`, :ref:`xpath` + :ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash_agg`, :ref:`humanize_file_size`, :ref:`instr`, :ref:`length`, :ref:`logfmt2json`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`sparkline`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`upper`, :ref:`xpath` ---- @@ -1819,7 +1819,7 @@ length(*str*) 3 **See Also** - :ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash_agg`, :ref:`humanize_file_size`, :ref:`instr`, :ref:`leftstr`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`sparkline`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`upper`, :ref:`xpath` + :ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash_agg`, :ref:`humanize_file_size`, :ref:`instr`, :ref:`leftstr`, :ref:`logfmt2json`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`sparkline`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`upper`, :ref:`xpath` ---- @@ -1990,6 +1990,30 @@ log_top_line() ---- +.. _logfmt2json: + +logfmt2json(*str*) +^^^^^^^^^^^^^^^^^^ + + Convert a logfmt-encoded string into JSON + + **Parameters** + * **str\*** --- The logfmt message to parse + + **Examples** + To extract key/value pairs from a log message: + + .. code-block:: custsqlite + + ;SELECT logfmt2json('foo=1 bar=2 name="Rolo Tomassi"') + {"foo":1,"bar":2,"name":"Rolo Tomassi"} + + **See Also** + :ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash_agg`, :ref:`humanize_file_size`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`sparkline`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`upper`, :ref:`xpath` + +---- + + .. _lower: lower(*str*) @@ -2009,7 +2033,7 @@ lower(*str*) abc **See Also** - :ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash_agg`, :ref:`humanize_file_size`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`sparkline`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`upper`, :ref:`xpath` + :ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash_agg`, :ref:`humanize_file_size`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`logfmt2json`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`sparkline`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`upper`, :ref:`xpath` ---- @@ -2041,7 +2065,7 @@ ltrim(*str*, *\[chars\]*) c **See Also** - :ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash_agg`, :ref:`humanize_file_size`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`lower`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`sparkline`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`upper`, :ref:`xpath` + :ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash_agg`, :ref:`humanize_file_size`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`logfmt2json`, :ref:`lower`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`sparkline`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`upper`, :ref:`xpath` ---- @@ -2198,7 +2222,7 @@ padc(*str*, *len*) abcdef ghi **See Also** - :ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash_agg`, :ref:`humanize_file_size`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`lower`, :ref:`ltrim`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`sparkline`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`upper`, :ref:`xpath` + :ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash_agg`, :ref:`humanize_file_size`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`logfmt2json`, :ref:`lower`, :ref:`ltrim`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`sparkline`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`upper`, :ref:`xpath` ---- @@ -2230,7 +2254,7 @@ padl(*str*, *len*) abcdef **See Also** - :ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash_agg`, :ref:`humanize_file_size`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`sparkline`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`upper`, :ref:`xpath` + :ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash_agg`, :ref:`humanize_file_size`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`logfmt2json`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`sparkline`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`upper`, :ref:`xpath` ---- @@ -2262,7 +2286,7 @@ padr(*str*, *len*) abcdefghi **See Also** - :ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash_agg`, :ref:`humanize_file_size`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`sparkline`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`upper`, :ref:`xpath` + :ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash_agg`, :ref:`humanize_file_size`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`logfmt2json`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`sparkline`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`upper`, :ref:`xpath` ---- @@ -2360,7 +2384,7 @@ printf(*format*, *X*) value: 00011 **See Also** - :ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash_agg`, :ref:`humanize_file_size`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`sparkline`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`upper`, :ref:`xpath` + :ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash_agg`, :ref:`humanize_file_size`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`logfmt2json`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`sparkline`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`upper`, :ref:`xpath` ---- @@ -2384,7 +2408,7 @@ proper(*str*) Hello, World! **See Also** - :ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash_agg`, :ref:`humanize_file_size`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`sparkline`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`upper`, :ref:`xpath` + :ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash_agg`, :ref:`humanize_file_size`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`logfmt2json`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`sparkline`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`upper`, :ref:`xpath` ---- @@ -2567,7 +2591,7 @@ regexp_capture(*string*, *pattern*) 1 2 3 8 9 2 **See Also** - :ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash_agg`, :ref:`humanize_file_size`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`sparkline`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`upper`, :ref:`xpath` + :ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash_agg`, :ref:`humanize_file_size`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`logfmt2json`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`sparkline`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`upper`, :ref:`xpath` ---- @@ -2606,7 +2630,7 @@ regexp_match(*re*, *str*) {"num":123,"str":"four"} **See Also** - :ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash_agg`, :ref:`humanize_file_size`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_replace`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`sparkline`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`upper`, :ref:`xpath` + :ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash_agg`, :ref:`humanize_file_size`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`logfmt2json`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_replace`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`sparkline`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`upper`, :ref:`xpath` ---- @@ -2639,7 +2663,7 @@ regexp_replace(*str*, *re*, *repl*) <123> **See Also** - :ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash_agg`, :ref:`humanize_file_size`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_match`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`sparkline`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`upper`, :ref:`xpath` + :ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash_agg`, :ref:`humanize_file_size`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`logfmt2json`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_match`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`sparkline`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`upper`, :ref:`xpath` ---- @@ -2672,7 +2696,7 @@ replace(*str*, *old*, *replacement*) zbc **See Also** - :ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash_agg`, :ref:`humanize_file_size`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`sparkline`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`upper`, :ref:`xpath` + :ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash_agg`, :ref:`humanize_file_size`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`logfmt2json`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`sparkline`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`upper`, :ref:`xpath` ---- @@ -2697,7 +2721,7 @@ replicate(*str*, *N*) abcabcabc **See Also** - :ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash_agg`, :ref:`humanize_file_size`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`sparkline`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`upper`, :ref:`xpath` + :ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash_agg`, :ref:`humanize_file_size`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`logfmt2json`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`sparkline`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`upper`, :ref:`xpath` ---- @@ -2721,7 +2745,7 @@ reverse(*str*) cba **See Also** - :ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash_agg`, :ref:`humanize_file_size`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`rightstr`, :ref:`rtrim`, :ref:`sparkline`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`upper`, :ref:`xpath` + :ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash_agg`, :ref:`humanize_file_size`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`logfmt2json`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`rightstr`, :ref:`rtrim`, :ref:`sparkline`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`upper`, :ref:`xpath` ---- @@ -2753,7 +2777,7 @@ rightstr(*str*, *N*) abc **See Also** - :ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash_agg`, :ref:`humanize_file_size`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rtrim`, :ref:`sparkline`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`upper`, :ref:`xpath` + :ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash_agg`, :ref:`humanize_file_size`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`logfmt2json`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rtrim`, :ref:`sparkline`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`upper`, :ref:`xpath` ---- @@ -2849,7 +2873,7 @@ rtrim(*str*, *\[chars\]*) a **See Also** - :ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash_agg`, :ref:`humanize_file_size`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`sparkline`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`upper`, :ref:`xpath` + :ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash_agg`, :ref:`humanize_file_size`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`logfmt2json`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`sparkline`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`upper`, :ref:`xpath` ---- @@ -2919,7 +2943,7 @@ sparkline(*value*, *\[upper\]*) ▁▂▃▄▅▆▇█ **See Also** - :ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash_agg`, :ref:`humanize_file_size`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`upper`, :ref:`xpath` + :ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash_agg`, :ref:`humanize_file_size`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`logfmt2json`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`upper`, :ref:`xpath` ---- @@ -2964,7 +2988,7 @@ spooky_hash(*str*) f96b3d9c1a19f4394c97a1b79b1880df **See Also** - :ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash_agg`, :ref:`humanize_file_size`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`sparkline`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`upper`, :ref:`xpath` + :ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash_agg`, :ref:`humanize_file_size`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`logfmt2json`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`sparkline`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`upper`, :ref:`xpath` ---- @@ -3078,7 +3102,7 @@ startswith(*str*, *prefix*) 0 **See Also** - :ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash_agg`, :ref:`humanize_file_size`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`sparkline`, :ref:`spooky_hash`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`upper`, :ref:`xpath` + :ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash_agg`, :ref:`humanize_file_size`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`logfmt2json`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`sparkline`, :ref:`spooky_hash`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`upper`, :ref:`xpath` ---- @@ -3103,7 +3127,7 @@ strfilter(*source*, *include*) bcbc **See Also** - :ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash_agg`, :ref:`humanize_file_size`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`sparkline`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`upper`, :ref:`xpath` + :ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash_agg`, :ref:`humanize_file_size`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`logfmt2json`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`sparkline`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`upper`, :ref:`xpath` ---- @@ -3190,7 +3214,7 @@ substr(*str*, *start*, *\[size\]*) b **See Also** - :ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash_agg`, :ref:`humanize_file_size`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`sparkline`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`trim`, :ref:`unicode`, :ref:`upper`, :ref:`xpath` + :ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash_agg`, :ref:`humanize_file_size`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`logfmt2json`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`sparkline`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`trim`, :ref:`unicode`, :ref:`upper`, :ref:`xpath` ---- @@ -3396,7 +3420,7 @@ trim(*str*, *\[chars\]*) abc **See Also** - :ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash_agg`, :ref:`humanize_file_size`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`sparkline`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`unicode`, :ref:`upper`, :ref:`xpath` + :ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash_agg`, :ref:`humanize_file_size`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`logfmt2json`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`sparkline`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`unicode`, :ref:`upper`, :ref:`xpath` ---- @@ -3449,7 +3473,7 @@ unicode(*X*) 97 **See Also** - :ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash_agg`, :ref:`humanize_file_size`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`sparkline`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`upper`, :ref:`xpath` + :ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash_agg`, :ref:`humanize_file_size`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`logfmt2json`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`sparkline`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`upper`, :ref:`xpath` ---- @@ -3487,7 +3511,7 @@ upper(*str*) ABC **See Also** - :ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash_agg`, :ref:`humanize_file_size`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`sparkline`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`xpath` + :ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash_agg`, :ref:`humanize_file_size`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`logfmt2json`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`sparkline`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`xpath` ---- @@ -3530,7 +3554,7 @@ xpath(*xpath*, *xmldoc*) Hello ★ /abc/def/text() {} Hello ★ **See Also** - :ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash_agg`, :ref:`humanize_file_size`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`sparkline`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`upper` + :ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash_agg`, :ref:`humanize_file_size`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`logfmt2json`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`sparkline`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`upper` ---- diff --git a/src/keymaps/default-keymap.json b/src/keymaps/default-keymap.json index d61f56c2..ddd35034 100644 --- a/src/keymaps/default-keymap.json +++ b/src/keymaps/default-keymap.json @@ -103,12 +103,6 @@ "command": ":next-mark warning", "alt-msg": "${keymap_def_alt_hour_boundary}" }, - "x59": { - "command": ":prev-mark query" - }, - "x79": { - "command": ":next-mark query" - }, "x63": { "command": ":write-to /dev/clipboard", "alt-msg": "${keymap_def_clear}" diff --git a/src/lnav.cc b/src/lnav.cc index b4ca7eac..9ec8d4a7 100644 --- a/src/lnav.cc +++ b/src/lnav.cc @@ -2231,6 +2231,16 @@ int main(int argc, char *argv[]) lnav_log_file = make_optional_from_nullable(fopen(lnav_data.ld_debug_log_name, "a")); log_info("lnav started"); + { + static auto builtin_formats = + injector::get>>(); + auto& root_formats = log_format::get_root_formats(); + + log_format::get_root_formats().insert( + root_formats.begin(), builtin_formats.begin(), builtin_formats.end()); + builtin_formats.clear(); + } + load_config(lnav_data.ld_config_paths, config_errors); if (!config_errors.empty()) { print_errors(config_errors); diff --git a/src/lnav_util.cc b/src/lnav_util.cc index bce2ce60..34d13c53 100644 --- a/src/lnav_util.cc +++ b/src/lnav_util.cc @@ -65,37 +65,6 @@ bool change_to_parent_dir() return retval; } -template -size_t strtonum(T &num_out, const char *string, size_t len) -{ - size_t retval = 0; - T sign = 1; - - num_out = 0; - - for (; retval < len && isspace(string[retval]); retval++); - for (; retval < len && string[retval] == '-'; retval++) { - sign *= -1; - } - for (; retval < len && string[retval] == '+'; retval++); - for (; retval < len && isdigit(string[retval]); retval++) { - num_out *= 10; - num_out += string[retval] - '0'; - } - num_out *= sign; - - return retval; -} - -template -size_t strtonum(long long &num_out, const char *string, size_t len); - -template -size_t strtonum(long &num_out, const char *string, size_t len); - -template -size_t strtonum(int &num_out, const char *string, size_t len); - string build_path(const vector &paths) { string retval; diff --git a/src/lnav_util.hh b/src/lnav_util.hh index 28918334..13fe78d2 100644 --- a/src/lnav_util.hh +++ b/src/lnav_util.hh @@ -127,9 +127,6 @@ std::string build_path(const std::vector &paths); Result read_file(const ghc::filesystem::path &path); -template -size_t strtonum(T &num_out, const char *data, size_t len); - inline short pollfd_revents(const std::vector &pollfds, int fd) { auto iter = std::find_if(pollfds.begin(), pollfds.end(), [fd](const auto& entry) { return entry.fd == fd; diff --git a/src/log_format.hh b/src/log_format.hh index bb8fb552..bbb79391 100644 --- a/src/log_format.hh +++ b/src/log_format.hh @@ -306,18 +306,6 @@ public: */ static std::vector> &get_root_formats(); - /** - * Template used to register log formats during initialization. - */ - template - class register_root_format { -public: - register_root_format() - { - log_format::lf_root_formats.push_back(std::make_shared()); - }; - }; - static std::shared_ptr find_root_format(const char *name) { auto& fmts = get_root_formats(); for (auto& lf : fmts) { diff --git a/src/log_format_impls.cc b/src/log_format_impls.cc index a52b2bea..3fee601a 100644 --- a/src/log_format_impls.cc +++ b/src/log_format_impls.cc @@ -41,6 +41,9 @@ #include "log_format.hh" #include "log_vtab_impl.hh" #include "base/opt_util.hh" +#include "base/injector.bind.hh" +#include "yajlpp/yajlpp.hh" +#include "formats/logfmt/logfmt.parser.hh" using namespace std; @@ -688,7 +691,7 @@ public: } - void get_columns(vector &cols) const { + void get_columns(vector &cols) const override { for (const auto &fd : this->blt_format.blf_field_defs) { std::pair type_pair = log_vtab_impl::logline_value_to_sqlite_type(fd.fd_meta.lvm_kind); @@ -696,7 +699,7 @@ public: } }; - void get_foreign_keys(std::vector &keys_inout) const { + void get_foreign_keys(std::vector &keys_inout) const override { this->log_vtab_impl::get_foreign_keys(keys_inout); for (const auto &fd : this->blt_format.blf_field_defs) { @@ -1404,6 +1407,247 @@ const std::vector w3c_log_format::KNOWN_STRUC {"sr(", "sr_headers"}, }; -log_format::register_root_format bro_log_instance; -log_format::register_root_format w3c_log_instance; -log_format::register_root_format generic_log_instance; +struct logfmt_pair_handler { + explicit logfmt_pair_handler(date_time_scanner &dts) : lph_dt_scanner(dts) + { + } + + bool process_value(const string_fragment& value_frag) { + if (this->lph_key_frag == "time" || + this->lph_key_frag == "ts") { + if (!this->lph_dt_scanner.scan(value_frag.data(), + value_frag.length(), + nullptr, + &this->lph_time_tm, + this->lph_tv)) { + return false; + } + this->lph_found_time = true; + } else if (this->lph_key_frag == "level") { + this->lph_level = string2level(value_frag.data(), value_frag.length()); + } + return true; + } + + date_time_scanner &lph_dt_scanner; + bool lph_found_time{false}; + struct exttm lph_time_tm{}; + struct timeval lph_tv{0, 0}; + log_level_t lph_level{log_level_t::LEVEL_INFO}; + string_fragment lph_key_frag{""}; +}; + +class logfmt_format : public log_format { +public: + const intern_string_t get_name() const override + { + const static auto NAME = intern_string::lookup("logfmt_log"); + + return NAME; + } + + class logfmt_log_table : public log_format_vtab_impl { + public: + logfmt_log_table(const log_format &format) : log_format_vtab_impl(format) {} + + void get_columns(vector &cols) const override { + static const auto FIELDS = std::string("fields"); + + cols.emplace_back(FIELDS); + }; + }; + + shared_ptr get_vtab_impl() const override + { + static auto retval = std::make_shared(*this); + + return retval; + } + + scan_result_t scan(logfile &lf, vector &dst, const line_info &li, + shared_buffer_ref &sbr) override + { + auto p = logfmt::parser(string_fragment{sbr.get_data(), 0, (int) sbr.length()}); + scan_result_t retval = scan_result_t::SCAN_NO_MATCH; + bool done = false; + logfmt_pair_handler lph(this->lf_date_time); + + while (!done) { + auto parse_result = p.step(); + + done = parse_result.match( + [](const logfmt::parser::end_of_input &) { + return true; + }, + [&lph](const logfmt::parser::kvpair &kvp) { + lph.lph_key_frag = kvp.first; + + return kvp.second.match( + [](const logfmt::parser::bool_value& bv) { + return false; + }, + [&lph](const logfmt::parser::float_value& fv) { + return lph.process_value(fv.fv_str_value); + }, + [&lph](const logfmt::parser::int_value& iv) { + return lph.process_value(iv.iv_str_value); + }, + [&lph](const logfmt::parser::quoted_value &qv) { + auto_mem handle(yajl_free); + yajl_callbacks cb; + + handle = yajl_alloc(&cb, nullptr, &lph); + memset(&cb, 0, sizeof(cb)); + cb.yajl_string = +[](void *ctx, const unsigned char* str, size_t len) -> int { + auto& lph = *((logfmt_pair_handler *)ctx); + string_fragment value_frag{str, 0, (int) len}; + + return lph.process_value(value_frag); + }; + + if (yajl_parse(handle, + (const unsigned char *) qv.qv_value.data(), + qv.qv_value.length()) != yajl_status_ok || + yajl_complete_parse(handle) != yajl_status_ok) { + log_debug("json parsing failed"); + string_fragment unq_frag{ + qv.qv_value.sf_string, + qv.qv_value.sf_begin + 1, + qv.qv_value.sf_end - 1, + }; + + return lph.process_value(unq_frag); + } + + return false; + }, + [&lph](const logfmt::parser::unquoted_value &uv) { + return lph.process_value(uv.uv_value); + } + ); + }, + [](const logfmt::parser::error &err) { + log_error("logfmt parse error: %s", err.e_msg.c_str()); + return true; + } + ); + } + + if (lph.lph_found_time) { + dst.emplace_back(li.li_file_range.fr_offset, lph.lph_tv, lph.lph_level); + retval = scan_result_t::SCAN_MATCH; + } + + return retval; + } + + void + annotate(uint64_t line_number, shared_buffer_ref &sbr, string_attrs_t &sa, + vector &values, bool annotate_module) const override + { + static const auto FIELDS_NAME = intern_string::lookup("fields"); + + auto p = logfmt::parser( + string_fragment{sbr.get_data(), 0, (int) sbr.length()}); + bool done = false; + + while (!done) { + auto parse_result = p.step(); + + done = parse_result.match( + [](const logfmt::parser::end_of_input &) { + return true; + }, + [this, &sa, &values, &sbr](const logfmt::parser::kvpair &kvp) { + auto value_frag = kvp.second.match( + [this, &kvp, &values](const logfmt::parser::bool_value& bv) { + auto lvm = logline_value_meta{ + intern_string::lookup(kvp.first), + value_kind_t::VALUE_INTEGER, + 0, + (log_format *) this + } + .with_struct_name(FIELDS_NAME); + values.emplace_back(lvm, bv.bv_value); + + return bv.bv_str_value; + }, + [this, &kvp, &values](const logfmt::parser::int_value& iv) { + auto lvm = logline_value_meta{ + intern_string::lookup(kvp.first), + value_kind_t::VALUE_INTEGER, + 0, + (log_format *) this + } + .with_struct_name(FIELDS_NAME); + values.emplace_back(lvm, iv.iv_value); + + return iv.iv_str_value; + }, + [this, &kvp, &values](const logfmt::parser::float_value& fv) { + auto lvm = logline_value_meta{ + intern_string::lookup(kvp.first), + value_kind_t::VALUE_INTEGER, + 0, + (log_format *) this + } + .with_struct_name(FIELDS_NAME); + values.emplace_back(lvm, fv.fv_value); + + return fv.fv_str_value; + }, + [](const logfmt::parser::quoted_value &qv) { + return qv.qv_value; + }, + [](const logfmt::parser::unquoted_value &uv) { + return uv.uv_value; + } + ); + auto value_lr = line_range{ + value_frag.sf_begin, value_frag.sf_end + }; + + if (kvp.first == "time" || kvp.first == "ts") { + sa.emplace_back(value_lr, &logline::L_TIMESTAMP); + } else if (kvp.first == "level") { + } else if (kvp.first == "msg") { + sa.emplace_back(value_lr, &SA_BODY); + } else if (!kvp.second.is() && + !kvp.second.is()) { + auto lvm = logline_value_meta{ + intern_string::lookup(kvp.first), + value_frag.startswith("\"") ? + value_kind_t::VALUE_JSON : + value_kind_t::VALUE_TEXT, + 0, + (log_format *) this + } + .with_struct_name(FIELDS_NAME); + shared_buffer_ref value_sbr; + + value_sbr.subset(sbr, value_frag.sf_begin, value_frag.length()); + values.emplace_back(lvm, value_sbr); + } + + return false; + }, + [line_number, &sbr](const logfmt::parser::error &err) { + log_error("bad line %.*s", sbr.length(), sbr.get_data()); + log_error("%lld:logfmt parse error: %s", line_number, err.e_msg.c_str()); + return true; + } + ); + } + } + + shared_ptr specialized(int fmt_lock) override + { + return std::make_shared(*this); + }; +}; + +static auto format_binder = injector::bind_multiple() + .add() + .add() + .add() + .add(); diff --git a/src/log_gutter_source.hh b/src/log_gutter_source.hh index b7c1a70a..37f4d161 100644 --- a/src/log_gutter_source.hh +++ b/src/log_gutter_source.hh @@ -48,9 +48,6 @@ public: next = bm[&textview_curses::BM_SEARCH].next(vis_line_t(start)); search_hit = (next != -1 && next <= end); - next = bm[&BM_QUERY].next(vis_line_t(start)); - search_hit = search_hit || (next != -1 && next <= end); - next = bm[&textview_curses::BM_USER].next(vis_line_t(start)); if (next == -1) { next = bm[&textview_curses::BM_META].next(vis_line_t(start)); diff --git a/src/log_vtab_impl.cc b/src/log_vtab_impl.cc index ed48b905..5bd2c54f 100644 --- a/src/log_vtab_impl.cc +++ b/src/log_vtab_impl.cc @@ -33,6 +33,7 @@ #include "base/string_util.hh" #include "sql_util.hh" #include "log_vtab_impl.hh" +#include "yajlpp/json_op.hh" #include "yajlpp/yajlpp_def.hh" #include "vtab_module.hh" @@ -624,6 +625,27 @@ static int vt_column(sqlite3_vtab_cursor *cur, sqlite3_context *ctx, int col) case value_kind_t::VALUE_FLOAT: root.gen(lv_struct.lv_value.d); break; + case value_kind_t::VALUE_JSON: { + auto_mem parse_handle(yajl_free); + json_ptr jp(""); + json_op jo(jp); + + jo.jo_ptr_callbacks = json_op::gen_callbacks; + jo.jo_ptr_data = gen; + parse_handle.reset(yajl_alloc(&json_op::ptr_callbacks, nullptr, &jo)); + + auto json_in = (const unsigned char *) lv_struct.text_value(); + auto json_len = lv_struct.text_length(); + + if (yajl_parse(parse_handle.in(), json_in, json_len) != yajl_status_ok || + yajl_complete_parse(parse_handle.in()) != yajl_status_ok) { + log_error("failed to parse json value: %.*s", + lv_struct.text_length(), + lv_struct.text_value()); + root.gen(lv_struct.to_string()); + } + break; + } default: root.gen(lv_struct.to_string()); break; diff --git a/src/string-extension-functions.cc b/src/string-extension-functions.cc index 1856ea5d..9c34acde 100644 --- a/src/string-extension-functions.cc +++ b/src/string-extension-functions.cc @@ -19,7 +19,9 @@ #include "base/humanize.hh" #include "base/string_util.hh" +#include "formats/logfmt/logfmt.parser.hh" #include "yajlpp/yajlpp.hh" +#include "yajlpp/json_op.hh" #include "column_namer.hh" #include "yajl/api/yajl_gen.h" #include "sqlite-extension-func.hh" @@ -183,6 +185,70 @@ json_string extract(const char *str) return json_string(gen); } +json_string logfmt2json(string_fragment line) +{ + logfmt::parser p(line); + yajlpp_gen gen; + yajl_gen_config(gen, yajl_gen_beautify, false); + + { + yajlpp_map root(gen); + bool done = false; + + while (!done) { + auto pair = p.step(); + + done = pair.match( + [](const logfmt::parser::end_of_input& eoi) { + return true; + }, + [&root, &gen](const logfmt::parser::kvpair& kvp) { + root.gen(kvp.first); + + kvp.second.match( + [&root](const logfmt::parser::bool_value& bv) { + root.gen(bv.bv_value); + }, + [&root](const logfmt::parser::int_value& iv) { + root.gen(iv.iv_value); + }, + [&root](const logfmt::parser::float_value& fv) { + root.gen(fv.fv_value); + }, + [&root, &gen](const logfmt::parser::quoted_value& qv) { + auto_mem parse_handle(yajl_free); + json_ptr jp(""); + json_op jo(jp); + + jo.jo_ptr_callbacks = json_op::gen_callbacks; + jo.jo_ptr_data = gen; + parse_handle.reset(yajl_alloc(&json_op::ptr_callbacks, nullptr, &jo)); + + auto json_in = (const unsigned char *) qv.qv_value.data(); + auto json_len = qv.qv_value.length(); + + if (yajl_parse(parse_handle.in(), json_in, json_len) != yajl_status_ok || + yajl_complete_parse(parse_handle.in()) != yajl_status_ok) { + root.gen(qv.qv_value); + } + }, + [&root](const logfmt::parser::unquoted_value& uv) { + root.gen(uv.uv_value); + } + ); + + return false; + }, + [](const logfmt::parser::error& e) -> bool { + throw sqlite_func_error("Invalid logfmt: {}", e.e_msg); + } + ); + } + } + + return json_string(gen); +} + static string regexp_replace(const char *str, const char *re, const char *repl) { @@ -413,6 +479,18 @@ int string_extension_functions(struct FuncDef **basic_funcs, }) ), + sqlite_func_adapter::builder( + help_text("logfmt2json", + "Convert a logfmt-encoded string into JSON") + .sql_function() + .with_parameter({"str", "The logfmt message to parse"}) + .with_tags({"string"}) + .with_example({ + "To extract key/value pairs from a log message", + "SELECT logfmt2json('foo=1 bar=2 name=\"Rolo Tomassi\"')" + }) + ), + sqlite_func_adapter(&startswith)), startswith>::builder( diff --git a/src/vtab_module.hh b/src/vtab_module.hh index af318e9b..8ed4baa8 100644 --- a/src/vtab_module.hh +++ b/src/vtab_module.hh @@ -140,6 +140,17 @@ struct from_sqlite { } }; +template<> +struct from_sqlite { + inline string_fragment operator()(int argc, sqlite3_value **val, int argi) { + return string_fragment { + sqlite3_value_text(val[argi]), + 0, + sqlite3_value_bytes(val[argi]), + }; + } +}; + template<> struct from_sqlite { inline std::string operator()(int argc, sqlite3_value **val, int argi) { diff --git a/src/yajlpp/yajlpp.hh b/src/yajlpp/yajlpp.hh index 957e30f7..4a0f5491 100644 --- a/src/yajlpp/yajlpp.hh +++ b/src/yajlpp/yajlpp.hh @@ -424,6 +424,11 @@ public: return yajl_gen_string(this->yg_handle, (const unsigned char *)str.get(), str.size()); }; + yajl_gen_status operator()(const string_fragment &str) + { + return yajl_gen_string(this->yg_handle, (const unsigned char *)str.data(), str.length()); + }; + yajl_gen_status operator()(bool value) { return yajl_gen_bool(this->yg_handle, value); diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 979107b2..42d028c3 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -58,7 +58,7 @@ target_link_libraries(test_reltime diag PkgConfig::libpcre) add_test(NAME test_reltime COMMAND test_reltime) add_executable(test_top_status test_top_status.cc) -target_link_libraries(test_top_status diag testdummy PkgConfig::libpcre) +target_link_libraries(test_top_status diag testdummy PkgConfig::libpcre logfmt) add_test(NAME test_top_status COMMAND test_top_status) add_executable(drive_view_colors drive_view_colors.cc) @@ -74,7 +74,7 @@ add_executable(drive_sql_anno drive_sql_anno.cc) target_link_libraries(drive_sql_anno diag testdummy PkgConfig::libpcre) add_executable(drive_data_scanner drive_data_scanner.cc) -target_link_libraries(drive_data_scanner diag testdummy PkgConfig::libpcre) +target_link_libraries(drive_data_scanner diag testdummy PkgConfig::libpcre logfmt) add_executable(scripty scripty.cc) target_link_libraries(scripty diag PkgConfig::ncursesw) diff --git a/test/Makefile.am b/test/Makefile.am index 193efc2a..cb50109c 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -101,6 +101,7 @@ LDADD = \ $(TEXT2C_OBJS) \ $(DUMMY_OBJS) \ $(top_builddir)/src/libdiag.a \ + $(top_builddir)/src/formats/logfmt/liblogfmt.a \ $(top_builddir)/src/fmtlib/libcppfmt.a \ $(top_builddir)/src/pcrepp/libpcrepp.a \ $(top_builddir)/src/yajl/libyajl.a \ @@ -277,6 +278,7 @@ dist_noinst_DATA = \ logfile_json2.json \ logfile_json3.json \ logfile_leveltest.0 \ + logfile_logfmt.0 \ logfile_multiline.0 \ logfile_nested_json.json \ logfile_openam.0 \ diff --git a/test/drive_data_scanner.cc b/test/drive_data_scanner.cc index 207c6d75..d2341612 100644 --- a/test/drive_data_scanner.cc +++ b/test/drive_data_scanner.cc @@ -35,6 +35,7 @@ #include #include +#include "base/injector.hh" #include "textview_curses.hh" #include "data_scanner.hh" #include "data_parser.hh" @@ -55,6 +56,16 @@ int main(int argc, char *argv[]) int c, retval = EXIT_SUCCESS; bool prompt = false, is_log = false, pretty_print = false; + { + static auto builtin_formats = + injector::get>>(); + auto& root_formats = log_format::get_root_formats(); + + log_format::get_root_formats().insert( + root_formats.begin(), builtin_formats.begin(), builtin_formats.end()); + builtin_formats.clear(); + } + { std::vector paths; vector errors; diff --git a/test/drive_logfile.cc b/test/drive_logfile.cc index dc01a9eb..11d49dce 100644 --- a/test/drive_logfile.cc +++ b/test/drive_logfile.cc @@ -39,6 +39,8 @@ #include +#include "base/injector.hh" +#include "base/opt_util.hh" #include "logfile.hh" #include "log_format.hh" #include "log_format_loader.hh" @@ -64,13 +66,23 @@ int main(int argc, char *argv[]) dl_mode_t mode = MODE_NONE; string expected_format; + { + static auto builtin_formats = + injector::get>>(); + auto& root_formats = log_format::get_root_formats(); + + log_format::get_root_formats().insert( + root_formats.begin(), builtin_formats.begin(), builtin_formats.end()); + builtin_formats.clear(); + } + { std::vector errors; vector paths; - if (getenv("test_dir") != NULL) { - paths.push_back(getenv("test_dir")); - } + getenv_opt("test_dir") | [&paths](auto value) { + paths.template emplace_back(value); + }; load_formats(paths, errors); } diff --git a/test/expected_help.txt b/test/expected_help.txt index 97c2b0e2..d3accccc 100644 --- a/test/expected_help.txt +++ b/test/expected_help.txt @@ -234,9 +234,6 @@ Spatial Navigation also jump to the start of any log partitions that have been created with the 'partition-name' command. - y/Y Move forward/backward through the log view based on the - "log_line" column in the SQL result view. - s/S Move to the next/previous "slow down" in the log message rate. A slow down is detected by measuring how quickly the message rate has changed over the previous several @@ -1236,11 +1233,11 @@ Parameter X The unicode code point values See Also charindex(), endswith(), extract(), group_concat(), group_spooky_hash(), - humanize_file_size(), instr(), leftstr(), length(), lower(), ltrim(), padc(), - padl(), padr(), printf(), proper(), regexp_capture(), regexp_match(), - regexp_replace(), replace(), replicate(), reverse(), rightstr(), rtrim(), - sparkline(), spooky_hash(), startswith(), strfilter(), substr(), trim(), - unicode(), upper(), xpath() + humanize_file_size(), instr(), leftstr(), length(), logfmt2json(), lower(), + ltrim(), padc(), padl(), padr(), printf(), proper(), regexp_capture(), + regexp_match(), regexp_replace(), replace(), replicate(), reverse(), rightstr(), + rtrim(), sparkline(), spooky_hash(), startswith(), strfilter(), substr(), + trim(), unicode(), upper(), xpath() Example #1 To get a string with the code points 0x48 and 0x49: ;SELECT char(0x48, 0x49) @@ -1257,11 +1254,11 @@ Parameters start The one-based index within the haystack to start the search See Also char(), endswith(), extract(), group_concat(), group_spooky_hash(), - humanize_file_size(), instr(), leftstr(), length(), lower(), ltrim(), padc(), - padl(), padr(), printf(), proper(), regexp_capture(), regexp_match(), - regexp_replace(), replace(), replicate(), reverse(), rightstr(), rtrim(), - sparkline(), spooky_hash(), startswith(), strfilter(), substr(), trim(), - unicode(), upper(), xpath() + humanize_file_size(), instr(), leftstr(), length(), logfmt2json(), lower(), + ltrim(), padc(), padl(), padr(), printf(), proper(), regexp_capture(), + regexp_match(), regexp_replace(), replace(), replicate(), reverse(), rightstr(), + rtrim(), sparkline(), spooky_hash(), startswith(), strfilter(), substr(), + trim(), unicode(), upper(), xpath() Examples #1 To search for the string 'abc' within 'abcabc' and starting at position 2: ;SELECT charindex('abc', 'abcabc', 2) @@ -1408,11 +1405,11 @@ Parameters suffix The suffix to check in the string See Also char(), charindex(), extract(), group_concat(), group_spooky_hash(), - humanize_file_size(), instr(), leftstr(), length(), lower(), ltrim(), padc(), - padl(), padr(), printf(), proper(), regexp_capture(), regexp_match(), - regexp_replace(), replace(), replicate(), reverse(), rightstr(), rtrim(), - sparkline(), spooky_hash(), startswith(), strfilter(), substr(), trim(), - unicode(), upper(), xpath() + humanize_file_size(), instr(), leftstr(), length(), logfmt2json(), lower(), + ltrim(), padc(), padl(), padr(), printf(), proper(), regexp_capture(), + regexp_match(), regexp_replace(), replace(), replicate(), reverse(), rightstr(), + rtrim(), sparkline(), spooky_hash(), startswith(), strfilter(), substr(), + trim(), unicode(), upper(), xpath() Examples #1 To test if the string 'notbad.jpg' ends with '.jpg': ;SELECT endswith('notbad.jpg', '.jpg') @@ -1443,11 +1440,11 @@ Parameter str The string to parse See Also char(), charindex(), endswith(), group_concat(), group_spooky_hash(), - humanize_file_size(), instr(), leftstr(), length(), lower(), ltrim(), padc(), - padl(), padr(), printf(), proper(), regexp_capture(), regexp_match(), - regexp_replace(), replace(), replicate(), reverse(), rightstr(), rtrim(), - sparkline(), spooky_hash(), startswith(), strfilter(), substr(), trim(), - unicode(), upper(), xpath() + humanize_file_size(), instr(), leftstr(), length(), logfmt2json(), lower(), + ltrim(), padc(), padl(), padr(), printf(), proper(), regexp_capture(), + regexp_match(), regexp_replace(), replace(), replicate(), reverse(), rightstr(), + rtrim(), sparkline(), spooky_hash(), startswith(), strfilter(), substr(), + trim(), unicode(), upper(), xpath() Examples #1 To extract key/value pairs from a string: ;SELECT extract('foo=1 bar=2 name="Rolo Tomassi"') @@ -1550,11 +1547,11 @@ Parameters sep The separator to place between the values. See Also char(), charindex(), endswith(), extract(), group_spooky_hash(), - humanize_file_size(), instr(), leftstr(), length(), lower(), ltrim(), padc(), - padl(), padr(), printf(), proper(), regexp_capture(), regexp_match(), - regexp_replace(), replace(), replicate(), reverse(), rightstr(), rtrim(), - sparkline(), spooky_hash(), startswith(), strfilter(), substr(), trim(), - unicode(), upper(), xpath() + humanize_file_size(), instr(), leftstr(), length(), logfmt2json(), lower(), + ltrim(), padc(), padl(), padr(), printf(), proper(), regexp_capture(), + regexp_match(), regexp_replace(), replace(), replicate(), reverse(), rightstr(), + rtrim(), sparkline(), spooky_hash(), startswith(), strfilter(), substr(), + trim(), unicode(), upper(), xpath() Examples #1 To concatenate the values of the column 'ex_procname' from the table 'lnav_example_log' : @@ -1578,11 +1575,11 @@ Parameter str The string to hash See Also char(), charindex(), endswith(), extract(), group_concat(), - humanize_file_size(), instr(), leftstr(), length(), lower(), ltrim(), padc(), - padl(), padr(), printf(), proper(), regexp_capture(), regexp_match(), - regexp_replace(), replace(), replicate(), reverse(), rightstr(), rtrim(), - sparkline(), spooky_hash(), startswith(), strfilter(), substr(), trim(), - unicode(), upper(), xpath() + humanize_file_size(), instr(), leftstr(), length(), logfmt2json(), lower(), + ltrim(), padc(), padl(), padr(), printf(), proper(), regexp_capture(), + regexp_match(), regexp_replace(), replace(), replicate(), reverse(), rightstr(), + rtrim(), sparkline(), spooky_hash(), startswith(), strfilter(), substr(), + trim(), unicode(), upper(), xpath() Example #1 To produce a hash of all of the values of 'column1': ;SELECT group_spooky_hash(column1) FROM (VALUES ('abc'), ('123')) @@ -1608,8 +1605,8 @@ Parameter value The file size to format See Also char(), charindex(), endswith(), extract(), group_concat(), group_spooky_hash(), - instr(), leftstr(), length(), lower(), ltrim(), padc(), padl(), padr(), - printf(), proper(), regexp_capture(), regexp_match(), regexp_replace(), + instr(), leftstr(), length(), logfmt2json(), lower(), ltrim(), padc(), padl(), + padr(), printf(), proper(), regexp_capture(), regexp_match(), regexp_replace(), replace(), replicate(), reverse(), rightstr(), rtrim(), sparkline(), spooky_hash(), startswith(), strfilter(), substr(), trim(), unicode(), upper(), xpath() @@ -1641,11 +1638,11 @@ Parameters needle The string to look for in the haystack See Also char(), charindex(), endswith(), extract(), group_concat(), group_spooky_hash(), - humanize_file_size(), leftstr(), length(), lower(), ltrim(), padc(), padl(), - padr(), printf(), proper(), regexp_capture(), regexp_match(), regexp_replace(), - replace(), replicate(), reverse(), rightstr(), rtrim(), sparkline(), - spooky_hash(), startswith(), strfilter(), substr(), trim(), unicode(), upper(), - xpath() + humanize_file_size(), leftstr(), length(), logfmt2json(), lower(), ltrim(), + padc(), padl(), padr(), printf(), proper(), regexp_capture(), regexp_match(), + regexp_replace(), replace(), replicate(), reverse(), rightstr(), rtrim(), + sparkline(), spooky_hash(), startswith(), strfilter(), substr(), trim(), + unicode(), upper(), xpath() Example #1 To test get the position of 'b' in the string 'abc': ;SELECT instr('abc', 'b') @@ -1847,11 +1844,11 @@ Parameters N The number of characters from the left side of the string to return. See Also char(), charindex(), endswith(), extract(), group_concat(), group_spooky_hash(), - humanize_file_size(), instr(), length(), lower(), ltrim(), padc(), padl(), - padr(), printf(), proper(), regexp_capture(), regexp_match(), regexp_replace(), - replace(), replicate(), reverse(), rightstr(), rtrim(), sparkline(), - spooky_hash(), startswith(), strfilter(), substr(), trim(), unicode(), upper(), - xpath() + humanize_file_size(), instr(), length(), logfmt2json(), lower(), ltrim(), + padc(), padl(), padr(), printf(), proper(), regexp_capture(), regexp_match(), + regexp_replace(), replace(), replicate(), reverse(), rightstr(), rtrim(), + sparkline(), spooky_hash(), startswith(), strfilter(), substr(), trim(), + unicode(), upper(), xpath() Examples #1 To get the first character of the string 'abc': ;SELECT leftstr('abc', 1) @@ -1869,11 +1866,11 @@ Parameter str The string to determine the length of See Also char(), charindex(), endswith(), extract(), group_concat(), group_spooky_hash(), - humanize_file_size(), instr(), leftstr(), lower(), ltrim(), padc(), padl(), - padr(), printf(), proper(), regexp_capture(), regexp_match(), regexp_replace(), - replace(), replicate(), reverse(), rightstr(), rtrim(), sparkline(), - spooky_hash(), startswith(), strfilter(), substr(), trim(), unicode(), upper(), - xpath() + humanize_file_size(), instr(), leftstr(), logfmt2json(), lower(), ltrim(), + padc(), padl(), padr(), printf(), proper(), regexp_capture(), regexp_match(), + regexp_replace(), replace(), replicate(), reverse(), rightstr(), rtrim(), + sparkline(), spooky_hash(), startswith(), strfilter(), substr(), trim(), + unicode(), upper(), xpath() Example #1 To get the length of the string 'abc': ;SELECT length('abc') @@ -1923,7 +1920,7 @@ Synopsis Synopsis - load_extension(path, [entry-point]) -- Loads SQLite extensions out of the + load_extension(path, [entry-point]) -- Loads SQLite extensions out of the given shared library file using the given entry point. Parameters path The path to the shared library containing the extension. @@ -1967,6 +1964,23 @@ Synopsis log_top_line() -- Return the line number at the top of the log view. +Synopsis + logfmt2json(str) -- Convert a logfmt-encoded string into JSON +Parameter + str The logfmt message to parse +See Also + char(), charindex(), endswith(), extract(), group_concat(), group_spooky_hash(), + humanize_file_size(), instr(), leftstr(), length(), lower(), ltrim(), padc(), + padl(), padr(), printf(), proper(), regexp_capture(), regexp_match(), + regexp_replace(), replace(), replicate(), reverse(), rightstr(), rtrim(), + sparkline(), spooky_hash(), startswith(), strfilter(), substr(), trim(), + unicode(), upper(), xpath() +Example +#1 To extract key/value pairs from a log message: + ;SELECT logfmt2json('foo=1 bar=2 name="Rolo Tomassi"') + + + Synopsis lower(str) -- Returns a copy of the given string with all ASCII characters converted to lower case. @@ -1974,11 +1988,11 @@ Parameter str The string to convert. See Also char(), charindex(), endswith(), extract(), group_concat(), group_spooky_hash(), - humanize_file_size(), instr(), leftstr(), length(), ltrim(), padc(), padl(), - padr(), printf(), proper(), regexp_capture(), regexp_match(), regexp_replace(), - replace(), replicate(), reverse(), rightstr(), rtrim(), sparkline(), - spooky_hash(), startswith(), strfilter(), substr(), trim(), unicode(), upper(), - xpath() + humanize_file_size(), instr(), leftstr(), length(), logfmt2json(), ltrim(), + padc(), padl(), padr(), printf(), proper(), regexp_capture(), regexp_match(), + regexp_replace(), replace(), replicate(), reverse(), rightstr(), rtrim(), + sparkline(), spooky_hash(), startswith(), strfilter(), substr(), trim(), + unicode(), upper(), xpath() Example #1 To lowercase the string 'AbC': ;SELECT lower('AbC') @@ -1994,11 +2008,11 @@ Parameters chars The characters to trim. Defaults to spaces. See Also char(), charindex(), endswith(), extract(), group_concat(), group_spooky_hash(), - humanize_file_size(), instr(), leftstr(), length(), lower(), padc(), padl(), - padr(), printf(), proper(), regexp_capture(), regexp_match(), regexp_replace(), - replace(), replicate(), reverse(), rightstr(), rtrim(), sparkline(), - spooky_hash(), startswith(), strfilter(), substr(), trim(), unicode(), upper(), - xpath() + humanize_file_size(), instr(), leftstr(), length(), logfmt2json(), lower(), + padc(), padl(), padr(), printf(), proper(), regexp_capture(), regexp_match(), + regexp_replace(), replace(), replicate(), reverse(), rightstr(), rtrim(), + sparkline(), spooky_hash(), startswith(), strfilter(), substr(), trim(), + unicode(), upper(), xpath() Examples #1 To trim the leading whitespace from the string ' abc': ;SELECT ltrim(' abc') @@ -2093,11 +2107,11 @@ Parameters len The minimum desired length of the output string See Also char(), charindex(), endswith(), extract(), group_concat(), group_spooky_hash(), - humanize_file_size(), instr(), leftstr(), length(), lower(), ltrim(), padl(), - padr(), printf(), proper(), regexp_capture(), regexp_match(), regexp_replace(), - replace(), replicate(), reverse(), rightstr(), rtrim(), sparkline(), - spooky_hash(), startswith(), strfilter(), substr(), trim(), unicode(), upper(), - xpath() + humanize_file_size(), instr(), leftstr(), length(), logfmt2json(), lower(), + ltrim(), padl(), padr(), printf(), proper(), regexp_capture(), regexp_match(), + regexp_replace(), replace(), replicate(), reverse(), rightstr(), rtrim(), + sparkline(), spooky_hash(), startswith(), strfilter(), substr(), trim(), + unicode(), upper(), xpath() Examples #1 To pad the string 'abc' to a length of six characters: ;SELECT padc('abc', 6) || 'def' @@ -2116,11 +2130,11 @@ Parameters len The minimum desired length of the output string See Also char(), charindex(), endswith(), extract(), group_concat(), group_spooky_hash(), - humanize_file_size(), instr(), leftstr(), length(), lower(), ltrim(), padc(), - padr(), printf(), proper(), regexp_capture(), regexp_match(), regexp_replace(), - replace(), replicate(), reverse(), rightstr(), rtrim(), sparkline(), - spooky_hash(), startswith(), strfilter(), substr(), trim(), unicode(), upper(), - xpath() + humanize_file_size(), instr(), leftstr(), length(), logfmt2json(), lower(), + ltrim(), padc(), padr(), printf(), proper(), regexp_capture(), regexp_match(), + regexp_replace(), replace(), replicate(), reverse(), rightstr(), rtrim(), + sparkline(), spooky_hash(), startswith(), strfilter(), substr(), trim(), + unicode(), upper(), xpath() Examples #1 To pad the string 'abc' to a length of six characters: ;SELECT padl('abc', 6) @@ -2139,11 +2153,11 @@ Parameters len The minimum desired length of the output string See Also char(), charindex(), endswith(), extract(), group_concat(), group_spooky_hash(), - humanize_file_size(), instr(), leftstr(), length(), lower(), ltrim(), padc(), - padl(), printf(), proper(), regexp_capture(), regexp_match(), regexp_replace(), - replace(), replicate(), reverse(), rightstr(), rtrim(), sparkline(), - spooky_hash(), startswith(), strfilter(), substr(), trim(), unicode(), upper(), - xpath() + humanize_file_size(), instr(), leftstr(), length(), logfmt2json(), lower(), + ltrim(), padc(), padl(), printf(), proper(), regexp_capture(), regexp_match(), + regexp_replace(), replace(), replicate(), reverse(), rightstr(), rtrim(), + sparkline(), spooky_hash(), startswith(), strfilter(), substr(), trim(), + unicode(), upper(), xpath() Examples #1 To pad the string 'abc' to a length of six characters: ;SELECT padr('abc', 6) || 'def' @@ -2196,11 +2210,11 @@ Parameters X The argument to substitute at a given position in the format. See Also char(), charindex(), endswith(), extract(), group_concat(), group_spooky_hash(), - humanize_file_size(), instr(), leftstr(), length(), lower(), ltrim(), padc(), - padl(), padr(), proper(), regexp_capture(), regexp_match(), regexp_replace(), - replace(), replicate(), reverse(), rightstr(), rtrim(), sparkline(), - spooky_hash(), startswith(), strfilter(), substr(), trim(), unicode(), upper(), - xpath() + humanize_file_size(), instr(), leftstr(), length(), logfmt2json(), lower(), + ltrim(), padc(), padl(), padr(), proper(), regexp_capture(), regexp_match(), + regexp_replace(), replace(), replicate(), reverse(), rightstr(), rtrim(), + sparkline(), spooky_hash(), startswith(), strfilter(), substr(), trim(), + unicode(), upper(), xpath() Examples #1 To substitute 'World' into the string 'Hello, %s!': ;SELECT printf('Hello, %s!', 'World') @@ -2221,11 +2235,11 @@ Parameter str The string to capitalize. See Also char(), charindex(), endswith(), extract(), group_concat(), group_spooky_hash(), - humanize_file_size(), instr(), leftstr(), length(), lower(), ltrim(), padc(), - padl(), padr(), printf(), regexp_capture(), regexp_match(), regexp_replace(), - replace(), replicate(), reverse(), rightstr(), rtrim(), sparkline(), - spooky_hash(), startswith(), strfilter(), substr(), trim(), unicode(), upper(), - xpath() + humanize_file_size(), instr(), leftstr(), length(), logfmt2json(), lower(), + ltrim(), padc(), padl(), padr(), printf(), regexp_capture(), regexp_match(), + regexp_replace(), replace(), replicate(), reverse(), rightstr(), rtrim(), + sparkline(), spooky_hash(), startswith(), strfilter(), substr(), trim(), + unicode(), upper(), xpath() Example #1 To capitalize the words in the string 'hello, world!': ;SELECT proper('hello, world!') @@ -2326,10 +2340,11 @@ Results content The captured value from the string. See Also char(), charindex(), endswith(), extract(), group_concat(), group_spooky_hash(), - humanize_file_size(), instr(), leftstr(), length(), lower(), ltrim(), padc(), - padl(), padr(), printf(), proper(), regexp_match(), regexp_replace(), replace(), - replicate(), reverse(), rightstr(), rtrim(), sparkline(), spooky_hash(), - startswith(), strfilter(), substr(), trim(), unicode(), upper(), xpath() + humanize_file_size(), instr(), leftstr(), length(), logfmt2json(), lower(), + ltrim(), padc(), padl(), padr(), printf(), proper(), regexp_match(), + regexp_replace(), replace(), replicate(), reverse(), rightstr(), rtrim(), + sparkline(), spooky_hash(), startswith(), strfilter(), substr(), trim(), + unicode(), upper(), xpath() Example #1 To extract the key/value pairs 'a'/1 and 'b'/2 from the string 'a=1; b=2': ;SELECT * FROM regexp_capture('a=1; b=2', '(\w+)=(\d+)') @@ -2344,11 +2359,11 @@ Parameters str The string to test against the regular expression See Also char(), charindex(), endswith(), extract(), group_concat(), group_spooky_hash(), - humanize_file_size(), instr(), leftstr(), length(), lower(), ltrim(), padc(), - padl(), padr(), printf(), proper(), regexp_capture(), regexp_replace(), - regexp_replace(), replace(), replicate(), reverse(), rightstr(), rtrim(), - sparkline(), spooky_hash(), startswith(), strfilter(), substr(), trim(), - unicode(), upper(), xpath() + humanize_file_size(), instr(), leftstr(), length(), logfmt2json(), lower(), + ltrim(), padc(), padl(), padr(), printf(), proper(), regexp_capture(), + regexp_replace(), regexp_replace(), replace(), replicate(), reverse(), + rightstr(), rtrim(), sparkline(), spooky_hash(), startswith(), strfilter(), + substr(), trim(), unicode(), upper(), xpath() Examples #1 To capture the digits from the string '123': ;SELECT regexp_match('(\d+)', '123') @@ -2375,11 +2390,11 @@ Parameters backslash followed by the number of the group, starting with 1. See Also char(), charindex(), endswith(), extract(), group_concat(), group_spooky_hash(), - humanize_file_size(), instr(), leftstr(), length(), lower(), ltrim(), padc(), - padl(), padr(), printf(), proper(), regexp_capture(), regexp_match(), - regexp_match(), replace(), replicate(), reverse(), rightstr(), rtrim(), - sparkline(), spooky_hash(), startswith(), strfilter(), substr(), trim(), - unicode(), upper(), xpath() + humanize_file_size(), instr(), leftstr(), length(), logfmt2json(), lower(), + ltrim(), padc(), padl(), padr(), printf(), proper(), regexp_capture(), + regexp_match(), regexp_match(), replace(), replicate(), reverse(), rightstr(), + rtrim(), sparkline(), spooky_hash(), startswith(), strfilter(), substr(), + trim(), unicode(), upper(), xpath() Examples #1 To replace the word at the start of the string 'Hello, World!' with 'Goodbye': ;SELECT regexp_replace('Hello, World!', '^(\w+)', 'Goodbye') @@ -2400,11 +2415,11 @@ Parameters replacement The string to replace any occurrences of the old string with. See Also char(), charindex(), endswith(), extract(), group_concat(), group_spooky_hash(), - humanize_file_size(), instr(), leftstr(), length(), lower(), ltrim(), padc(), - padl(), padr(), printf(), proper(), regexp_capture(), regexp_match(), - regexp_replace(), replicate(), reverse(), rightstr(), rtrim(), sparkline(), - spooky_hash(), startswith(), strfilter(), substr(), trim(), unicode(), upper(), - xpath() + humanize_file_size(), instr(), leftstr(), length(), logfmt2json(), lower(), + ltrim(), padc(), padl(), padr(), printf(), proper(), regexp_capture(), + regexp_match(), regexp_replace(), replicate(), reverse(), rightstr(), rtrim(), + sparkline(), spooky_hash(), startswith(), strfilter(), substr(), trim(), + unicode(), upper(), xpath() Examples #1 To replace the string 'x' with 'z' in 'abc': ;SELECT replace('abc', 'x', 'z') @@ -2422,11 +2437,11 @@ Parameters N The number of times to replicate the string. See Also char(), charindex(), endswith(), extract(), group_concat(), group_spooky_hash(), - humanize_file_size(), instr(), leftstr(), length(), lower(), ltrim(), padc(), - padl(), padr(), printf(), proper(), regexp_capture(), regexp_match(), - regexp_replace(), replace(), reverse(), rightstr(), rtrim(), sparkline(), - spooky_hash(), startswith(), strfilter(), substr(), trim(), unicode(), upper(), - xpath() + humanize_file_size(), instr(), leftstr(), length(), logfmt2json(), lower(), + ltrim(), padc(), padl(), padr(), printf(), proper(), regexp_capture(), + regexp_match(), regexp_replace(), replace(), reverse(), rightstr(), rtrim(), + sparkline(), spooky_hash(), startswith(), strfilter(), substr(), trim(), + unicode(), upper(), xpath() Example #1 To repeat the string 'abc' three times: ;SELECT replicate('abc', 3) @@ -2439,11 +2454,11 @@ Parameter str The string to reverse. See Also char(), charindex(), endswith(), extract(), group_concat(), group_spooky_hash(), - humanize_file_size(), instr(), leftstr(), length(), lower(), ltrim(), padc(), - padl(), padr(), printf(), proper(), regexp_capture(), regexp_match(), - regexp_replace(), replace(), replicate(), rightstr(), rtrim(), sparkline(), - spooky_hash(), startswith(), strfilter(), substr(), trim(), unicode(), upper(), - xpath() + humanize_file_size(), instr(), leftstr(), length(), logfmt2json(), lower(), + ltrim(), padc(), padl(), padr(), printf(), proper(), regexp_capture(), + regexp_match(), regexp_replace(), replace(), replicate(), rightstr(), rtrim(), + sparkline(), spooky_hash(), startswith(), strfilter(), substr(), trim(), + unicode(), upper(), xpath() Example #1 To reverse the string 'abc': ;SELECT reverse('abc') @@ -2458,11 +2473,11 @@ Parameters N The number of characters from the right side of the string to return. See Also char(), charindex(), endswith(), extract(), group_concat(), group_spooky_hash(), - humanize_file_size(), instr(), leftstr(), length(), lower(), ltrim(), padc(), - padl(), padr(), printf(), proper(), regexp_capture(), regexp_match(), - regexp_replace(), replace(), replicate(), reverse(), rtrim(), sparkline(), - spooky_hash(), startswith(), strfilter(), substr(), trim(), unicode(), upper(), - xpath() + humanize_file_size(), instr(), leftstr(), length(), logfmt2json(), lower(), + ltrim(), padc(), padl(), padr(), printf(), proper(), regexp_capture(), + regexp_match(), regexp_replace(), replace(), replicate(), reverse(), rtrim(), + sparkline(), spooky_hash(), startswith(), strfilter(), substr(), trim(), + unicode(), upper(), xpath() Examples #1 To get the last character of the string 'abc': ;SELECT rightstr('abc', 1) @@ -2519,11 +2534,11 @@ Parameters chars The characters to trim. Defaults to spaces. See Also char(), charindex(), endswith(), extract(), group_concat(), group_spooky_hash(), - humanize_file_size(), instr(), leftstr(), length(), lower(), ltrim(), padc(), - padl(), padr(), printf(), proper(), regexp_capture(), regexp_match(), - regexp_replace(), replace(), replicate(), reverse(), rightstr(), sparkline(), - spooky_hash(), startswith(), strfilter(), substr(), trim(), unicode(), upper(), - xpath() + humanize_file_size(), instr(), leftstr(), length(), logfmt2json(), lower(), + ltrim(), padc(), padl(), padr(), printf(), proper(), regexp_capture(), + regexp_match(), regexp_replace(), replace(), replicate(), reverse(), rightstr(), + sparkline(), spooky_hash(), startswith(), strfilter(), substr(), trim(), + unicode(), upper(), xpath() Examples #1 To trim the whitespace from the end of the string 'abc ': ;SELECT rtrim('abc ') @@ -2568,11 +2583,11 @@ Parameters inputs. See Also char(), charindex(), endswith(), extract(), group_concat(), group_spooky_hash(), - humanize_file_size(), instr(), leftstr(), length(), lower(), ltrim(), padc(), - padl(), padr(), printf(), proper(), regexp_capture(), regexp_match(), - regexp_replace(), replace(), replicate(), reverse(), rightstr(), rtrim(), - spooky_hash(), startswith(), strfilter(), substr(), trim(), unicode(), upper(), - xpath() + humanize_file_size(), instr(), leftstr(), length(), logfmt2json(), lower(), + ltrim(), padc(), padl(), padr(), printf(), proper(), regexp_capture(), + regexp_match(), regexp_replace(), replace(), replicate(), reverse(), rightstr(), + rtrim(), spooky_hash(), startswith(), strfilter(), substr(), trim(), unicode(), + upper(), xpath() Examples #1 To get the unicode block element for the value 32 in the range of 0-128: ;SELECT sparkline(32, 128) @@ -2589,11 +2604,11 @@ Parameter str The string to hash See Also char(), charindex(), endswith(), extract(), group_concat(), group_spooky_hash(), - humanize_file_size(), instr(), leftstr(), length(), lower(), ltrim(), padc(), - padl(), padr(), printf(), proper(), regexp_capture(), regexp_match(), - regexp_replace(), replace(), replicate(), reverse(), rightstr(), rtrim(), - sparkline(), startswith(), strfilter(), substr(), trim(), unicode(), upper(), - xpath() + humanize_file_size(), instr(), leftstr(), length(), logfmt2json(), lower(), + ltrim(), padc(), padl(), padr(), printf(), proper(), regexp_capture(), + regexp_match(), regexp_replace(), replace(), replicate(), reverse(), rightstr(), + rtrim(), sparkline(), startswith(), strfilter(), substr(), trim(), unicode(), + upper(), xpath() Examples #1 To produce a hash for the string 'Hello, World!': ;SELECT spooky_hash('Hello, World!') @@ -2662,11 +2677,11 @@ Parameters prefix The prefix to check in the string See Also char(), charindex(), endswith(), extract(), group_concat(), group_spooky_hash(), - humanize_file_size(), instr(), leftstr(), length(), lower(), ltrim(), padc(), - padl(), padr(), printf(), proper(), regexp_capture(), regexp_match(), - regexp_replace(), replace(), replicate(), reverse(), rightstr(), rtrim(), - sparkline(), spooky_hash(), strfilter(), substr(), trim(), unicode(), upper(), - xpath() + humanize_file_size(), instr(), leftstr(), length(), logfmt2json(), lower(), + ltrim(), padc(), padl(), padr(), printf(), proper(), regexp_capture(), + regexp_match(), regexp_replace(), replace(), replicate(), reverse(), rightstr(), + rtrim(), sparkline(), spooky_hash(), strfilter(), substr(), trim(), unicode(), + upper(), xpath() Examples #1 To test if the string 'foobar' starts with 'foo': ;SELECT startswith('foobar', 'foo') @@ -2685,11 +2700,11 @@ Parameters include The characters to include in the result See Also char(), charindex(), endswith(), extract(), group_concat(), group_spooky_hash(), - humanize_file_size(), instr(), leftstr(), length(), lower(), ltrim(), padc(), - padl(), padr(), printf(), proper(), regexp_capture(), regexp_match(), - regexp_replace(), replace(), replicate(), reverse(), rightstr(), rtrim(), - sparkline(), spooky_hash(), startswith(), substr(), trim(), unicode(), upper(), - xpath() + humanize_file_size(), instr(), leftstr(), length(), logfmt2json(), lower(), + ltrim(), padc(), padl(), padr(), printf(), proper(), regexp_capture(), + regexp_match(), regexp_replace(), replace(), replicate(), reverse(), rightstr(), + rtrim(), sparkline(), spooky_hash(), startswith(), substr(), trim(), unicode(), + upper(), xpath() Example #1 To get the 'b', 'c', and 'd' characters from the string 'abcabc': ;SELECT strfilter('abcabc', 'bcd') @@ -2734,11 +2749,11 @@ Parameters the characters before the start are returned. See Also char(), charindex(), endswith(), extract(), group_concat(), group_spooky_hash(), - humanize_file_size(), instr(), leftstr(), length(), lower(), ltrim(), padc(), - padl(), padr(), printf(), proper(), regexp_capture(), regexp_match(), - regexp_replace(), replace(), replicate(), reverse(), rightstr(), rtrim(), - sparkline(), spooky_hash(), startswith(), strfilter(), trim(), unicode(), - upper(), xpath() + humanize_file_size(), instr(), leftstr(), length(), logfmt2json(), lower(), + ltrim(), padc(), padl(), padr(), printf(), proper(), regexp_capture(), + regexp_match(), regexp_replace(), replace(), replicate(), reverse(), rightstr(), + rtrim(), sparkline(), spooky_hash(), startswith(), strfilter(), trim(), + unicode(), upper(), xpath() Examples #1 To get the substring starting at the second character until the end of the string 'abc' : @@ -2867,11 +2882,11 @@ Parameters chars The characters to trim. Defaults to spaces. See Also char(), charindex(), endswith(), extract(), group_concat(), group_spooky_hash(), - humanize_file_size(), instr(), leftstr(), length(), lower(), ltrim(), padc(), - padl(), padr(), printf(), proper(), regexp_capture(), regexp_match(), - regexp_replace(), replace(), replicate(), reverse(), rightstr(), rtrim(), - sparkline(), spooky_hash(), startswith(), strfilter(), substr(), unicode(), - upper(), xpath() + humanize_file_size(), instr(), leftstr(), length(), logfmt2json(), lower(), + ltrim(), padc(), padl(), padr(), printf(), proper(), regexp_capture(), + regexp_match(), regexp_replace(), replace(), replicate(), reverse(), rightstr(), + rtrim(), sparkline(), spooky_hash(), startswith(), strfilter(), substr(), + unicode(), upper(), xpath() Examples #1 To trim whitespace from the start and end of the string ' abc ': ;SELECT trim(' abc ') @@ -2905,11 +2920,11 @@ Parameter X The string to examine. See Also char(), charindex(), endswith(), extract(), group_concat(), group_spooky_hash(), - humanize_file_size(), instr(), leftstr(), length(), lower(), ltrim(), padc(), - padl(), padr(), printf(), proper(), regexp_capture(), regexp_match(), - regexp_replace(), replace(), replicate(), reverse(), rightstr(), rtrim(), - sparkline(), spooky_hash(), startswith(), strfilter(), substr(), trim(), - upper(), xpath() + humanize_file_size(), instr(), leftstr(), length(), logfmt2json(), lower(), + ltrim(), padc(), padl(), padr(), printf(), proper(), regexp_capture(), + regexp_match(), regexp_replace(), replace(), replicate(), reverse(), rightstr(), + rtrim(), sparkline(), spooky_hash(), startswith(), strfilter(), substr(), + trim(), upper(), xpath() Example #1 To get the unicode code point for the first character of 'abc': ;SELECT unicode('abc') @@ -2929,11 +2944,11 @@ Parameter str The string to convert. See Also char(), charindex(), endswith(), extract(), group_concat(), group_spooky_hash(), - humanize_file_size(), instr(), leftstr(), length(), lower(), ltrim(), padc(), - padl(), padr(), printf(), proper(), regexp_capture(), regexp_match(), - regexp_replace(), replace(), replicate(), reverse(), rightstr(), rtrim(), - sparkline(), spooky_hash(), startswith(), strfilter(), substr(), trim(), - unicode(), xpath() + humanize_file_size(), instr(), leftstr(), length(), logfmt2json(), lower(), + ltrim(), padc(), padl(), padr(), printf(), proper(), regexp_capture(), + regexp_match(), regexp_replace(), replace(), replicate(), reverse(), rightstr(), + rtrim(), sparkline(), spooky_hash(), startswith(), strfilter(), substr(), + trim(), unicode(), xpath() Example #1 To uppercase the string 'aBc': ;SELECT upper('aBc') @@ -2953,11 +2968,11 @@ Results node_text The node's text value. See Also char(), charindex(), endswith(), extract(), group_concat(), group_spooky_hash(), - humanize_file_size(), instr(), leftstr(), length(), lower(), ltrim(), padc(), - padl(), padr(), printf(), proper(), regexp_capture(), regexp_match(), - regexp_replace(), replace(), replicate(), reverse(), rightstr(), rtrim(), - sparkline(), spooky_hash(), startswith(), strfilter(), substr(), trim(), - unicode(), upper() + humanize_file_size(), instr(), leftstr(), length(), logfmt2json(), lower(), + ltrim(), padc(), padl(), padr(), printf(), proper(), regexp_capture(), + regexp_match(), regexp_replace(), replace(), replicate(), reverse(), rightstr(), + rtrim(), sparkline(), spooky_hash(), startswith(), strfilter(), substr(), + trim(), unicode(), upper() Examples #1 To select the XML nodes on the path '/abc/def': ;SELECT * FROM xpath('/abc/def', 'HelloBye') diff --git a/test/logfile_logfmt.0 b/test/logfile_logfmt.0 new file mode 100644 index 00000000..c453b35a --- /dev/null +++ b/test/logfile_logfmt.0 @@ -0,0 +1,5 @@ +time="2021-09-15T21:17:10.220731Z" level=error msg="error retrieving pod status: pod inc-1-enh-domain-c14-ns-2/hello-inc-1-enh-domain-c14-ns-2-3-d8f465685-k75gp is not found: PodNotFound" namespace=inc-1-enh-domain-c14-ns-2 pod=hello-inc-1-enh-domain-c14-ns-2-3-d8f465685-k75gp reason= status=Pending +time="2021-09-15T21:17:11.674149Z" level=warning msg="Failed to DeletePod in the provider" error="pod inc-1-domain-c14-ns-6/fe-inc-1-domain-c14-ns-6-5-656d9bb695-4584b is not found: PodNotFound" namespace=inc-1-domain-c14-ns-6 pod=fe-inc-1-domain-c14-ns-6-5-656d9bb695-4584b uid=be2def59-3a08-42fd-8f84-6f64cfcefa93 +time="2021-09-15T21:17:11.678991Z" level=info msg="DeletionGracePeriodSeconds set to 0" namespace=inc-1-domain-c14-ns-6 pod=fe-inc-1-domain-c14-ns-6-5-656d9bb695-4584b uid=be2def59-3a08-42fd-8f84-6f64cfcefa93 +time="2021-09-15T21:17:11.679036Z" level=info msg="Pod deleted" namespace=inc-1-domain-c14-ns-6 pod=fe-inc-1-domain-c14-ns-6-5-656d9bb695-4584b uid=be2def59-3a08-42fd-8f84-6f64cfcefa93 +time="2021-09-15T21:18:20.335825Z" level=error msg="error retrieving pod status: pod inc-1-enh-domain-c14-ns-2/hello-inc-1-enh-domain-c14-ns-2-7-5ddd6bcd69-6rqct is not found: PodNotFound" namespace=inc-1-enh-domain-c14-ns-2 pod=hello-inc-1-enh-domain-c14-ns-2-7-5ddd6bcd69-6rqct reason= status=Pending diff --git a/test/test_logfile.sh b/test/test_logfile.sh index 77fd6c5a..26c112ad 100644 --- a/test/test_logfile.sh +++ b/test/test_logfile.sh @@ -498,6 +498,26 @@ info 0x0 error 0x0 EOF +run_test ./drive_logfile -t -f logfmt_log ${srcdir}/logfile_logfmt.0 + +check_output "logfmt_log time interpreted incorrectly?" <,2009-07-20 22:59:29.000,3000,error,0,,,,192.168.202.254,GET,-,,/vmw/vSphere/default/vmkboot.gz,gPXE/0.9.7,-,HTTP/1.0,46210,404, EOF -run_test ${lnav_test} -n \ - -c ":goto 0" \ - -c ";select log_line from access_log where log_level >= 'warning'" \ - -c ":switch-to-view log" \ - -c ":next-mark query" \ - ${test_dir}/logfile_access_log.0 - -check_output "query bookmark not working?" <