[formats] add logfmt

pull/932/head
Timothy Stack 3 years ago
parent 782878b05c
commit 19fd336e9a

@ -9,9 +9,18 @@ lnav v0.10.1:
the "tuning" section of the configuration. the "tuning" section of the configuration.
* Added an "lnav_version()" SQL function that returns the current * Added an "lnav_version()" SQL function that returns the current
version string. 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: Interface changes:
* The xclip implementation for accessing the system clipboard now writes * The xclip implementation for accessing the system clipboard now writes
to the "clipboard" selection instead of the "primary" selection. 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: Bug Fixes:
* The text "send-input" would show up on some terminals instead of * The text "send-input" would show up on some terminals instead of
ignoring the escape sequence. This control sequence was only ignoring the escape sequence. This control sequence was only

@ -1,6 +1,6 @@
# aminclude_static.am generated automatically by Autoconf # 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 # Code coverage

@ -312,6 +312,7 @@ AC_CONFIG_FILES([Makefile])
AC_CONFIG_FILES([TESTS_ENVIRONMENT]) AC_CONFIG_FILES([TESTS_ENVIRONMENT])
AC_CONFIG_FILES([src/Makefile]) AC_CONFIG_FILES([src/Makefile])
AC_CONFIG_FILES([src/base/Makefile]) AC_CONFIG_FILES([src/base/Makefile])
AC_CONFIG_FILES([src/formats/logfmt/Makefile])
AC_CONFIG_FILES([src/fmtlib/Makefile]) AC_CONFIG_FILES([src/fmtlib/Makefile])
AC_CONFIG_FILES([src/pcrepp/Makefile]) AC_CONFIG_FILES([src/pcrepp/Makefile])
AC_CONFIG_FILES([src/pugixml/Makefile]) AC_CONFIG_FILES([src/pugixml/Makefile])

@ -34,6 +34,14 @@ supported:
self-describing, so **lnav** will read the header to determine the shape of self-describing, so **lnav** will read the header to determine the shape of
the file. the file.
There is also basic support for the `logfmt <https://brandur.org/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 Defining a New Format
--------------------- ---------------------

@ -89,10 +89,6 @@ Spatial Navigation
- :kbd:`Shift` + :kbd:`o` - :kbd:`Shift` + :kbd:`o`
- -
- Forward/backward through log messages with a matching "opid" field - Forward/backward through log messages with a matching "opid" field
* - :kbd:`y`
- :kbd:`Shift` + :kbd:`y`
-
- Next/previous SQL result
* - :kbd:`s` * - :kbd:`s`
- :kbd:`Shift` + :kbd:`s` - :kbd:`Shift` + :kbd:`s`
- -

@ -17,6 +17,7 @@ add_subdirectory(base)
add_subdirectory(pcrepp) add_subdirectory(pcrepp)
add_subdirectory(remote) add_subdirectory(remote)
add_subdirectory(tailer) add_subdirectory(tailer)
add_subdirectory(formats/logfmt)
add_executable(bin2c bin2c.hh tools/bin2c.c) add_executable(bin2c bin2c.hh tools/bin2c.c)
target_link_libraries(bin2c ZLIB::zlib) target_link_libraries(bin2c ZLIB::zlib)
@ -460,6 +461,7 @@ target_link_libraries(
tailerservice tailerservice
tailerpp tailerpp
tailercommon tailercommon
logfmt
${lnav_LIBS}) ${lnav_LIBS})
target_compile_definitions(diag PRIVATE SQLITE_OMIT_LOAD_EXTENSION) target_compile_definitions(diag PRIVATE SQLITE_OMIT_LOAD_EXTENSION)

@ -1,7 +1,7 @@
include $(top_srcdir)/aminclude_static.am 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 bin_PROGRAMS = lnav
@ -128,6 +128,7 @@ AM_CPPFLAGS = \
LDADD = \ LDADD = \
libdiag.a \ libdiag.a \
base/libbase.a \ base/libbase.a \
formats/logfmt/liblogfmt.a \
fmtlib/libcppfmt.a \ fmtlib/libcppfmt.a \
pcrepp/libpcrepp.a \ pcrepp/libpcrepp.a \
pugixml/libpugixml.a \ pugixml/libpugixml.a \

@ -46,6 +46,7 @@ add_executable(
humanize.file_size.tests.cc humanize.file_size.tests.cc
humanize.network.tests.cc humanize.network.tests.cc
humanize.time.tests.cc humanize.time.tests.cc
intern_string.tests.cc
lnav.gzip.tests.cc lnav.gzip.tests.cc
string_util.tests.cc string_util.tests.cc
network.tcp.tests.cc network.tcp.tests.cc

@ -69,6 +69,7 @@ test_base_SOURCES = \
humanize.file_size.tests.cc \ humanize.file_size.tests.cc \
humanize.network.tests.cc \ humanize.network.tests.cc \
humanize.time.tests.cc \ humanize.time.tests.cc \
intern_string.tests.cc \
lnav.gzip.tests.cc \ lnav.gzip.tests.cc \
string_util.tests.cc \ string_util.tests.cc \
test_base.cc test_base.cc

@ -44,7 +44,7 @@
struct string_fragment { struct string_fragment {
using iterator = const char *; 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) { : sf_string(str), sf_begin(begin), sf_end(end == -1 ? strlen(str) : end) {
}; };
@ -80,7 +80,7 @@ struct string_fragment {
} }
bool empty() const { bool empty() const {
return length() == 0; return !this->is_valid() || length() == 0;
}; };
char operator[](int index) const { char operator[](int index) const {
@ -154,6 +154,111 @@ struct string_fragment {
return nonstd::nullopt; return nonstd::nullopt;
} }
template<typename P>
nonstd::optional<string_fragment> 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<string_fragment> 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<typename P>
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<std::pair<string_fragment, string_fragment>>;
template<typename P>
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 { const char *to_string(char *buf) const {
memcpy(buf, this->data(), this->length()); memcpy(buf, this->data(), this->length());
buf[this->length()] = '\0'; buf[this->length()] = '\0';

@ -216,3 +216,34 @@ std::string center_str(const std::string &subject, size_t width)
return retval; return retval;
} }
template<typename T>
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>(long long &num_out, const char *string, size_t len);
template
size_t strtonum<long>(long &num_out, const char *string, size_t len);
template
size_t strtonum<int>(int &num_out, const char *string, size_t len);

@ -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); std::string center_str(const std::string& subject, size_t width);
template<typename T>
size_t strtonum(T &num_out, const char *data, size_t len);
#endif #endif

@ -51,8 +51,6 @@ using namespace std;
exec_context INIT_EXEC_CONTEXT; exec_context INIT_EXEC_CONTEXT;
bookmark_type_t BM_QUERY("query");
static const string MSG_FORMAT_STMT = R"( static const string MSG_FORMAT_STMT = R"(
SELECT count(*) AS total, min(log_line) AS log_line, log_msg_format SELECT count(*) AS total, min(log_line) AS log_line, log_msg_format
FROM all_logs FROM all_logs
@ -345,8 +343,6 @@ Result<string, string> execute_sql(exec_context &ec, const string &sql, string &
retval = ec.ec_accumulator.get_string(); retval = ec.ec_accumulator.get_string();
} }
else if (!dls.dls_rows.empty()) { 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 (lnav_data.ld_flags & LNF_HEADLESS) {
if (ec.ec_local_vars.size() == 1) { if (ec.ec_local_vars.size() == 1) {
ensure_view(&lnav_data.ld_views[LNV_DB]); ensure_view(&lnav_data.ld_views[LNV_DB]);
@ -355,13 +351,6 @@ Result<string, string> execute_sql(exec_context &ec, const string &sql, string &
retval = ""; retval = "";
alt_msg = ""; 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) { else if (dls.dls_rows.size() == 1) {
auto &row = dls.dls_rows[0]; auto &row = dls.dls_rows[0];
@ -727,7 +716,6 @@ int sql_callback(exec_context &ec, sqlite3_stmt *stmt)
if (!sqlite3_stmt_busy(stmt)) { if (!sqlite3_stmt_busy(stmt)) {
dls.clear(); dls.clear();
lnav_data.ld_log_source.text_clear_marks(&BM_QUERY);
return 0; return 0;
} }
@ -774,16 +762,6 @@ int sql_callback(exec_context &ec, sqlite3_stmt *stmt)
break; 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; return retval;

@ -177,6 +177,4 @@ void sql_progress_finished();
void add_global_vars(exec_context &ec); void add_global_vars(exec_context &ec);
extern bookmark_type_t BM_QUERY;
#endif //LNAV_COMMAND_EXECUTOR_H #endif //LNAV_COMMAND_EXECUTOR_H

@ -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)

@ -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

@ -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{""}});
}
}
}

@ -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<string_fragment, value_type>;
using step_result = mapbox::util::variant<
end_of_input,
kvpair,
error
>;
step_result step();
private:
string_fragment p_next_input;
};
}
#endif

@ -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 <iostream>
#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<logfmt::parser::kvpair>());
CHECK(pair1.get<logfmt::parser::kvpair>().first == "abc");
CHECK(pair1.get<logfmt::parser::kvpair>().second
.get<logfmt::parser::unquoted_value>().uv_value == "def");
auto pair2 = p.step();
CHECK(pair2.is<logfmt::parser::kvpair>());
CHECK(pair2.get<logfmt::parser::kvpair>().first == "ghi");
CHECK(pair2.get<logfmt::parser::kvpair>().second
.get<logfmt::parser::quoted_value>().qv_value == "\"1 2 3 4\"");
auto pair3 = p.step();
CHECK(pair3.is<logfmt::parser::kvpair>());
CHECK(pair3.get<logfmt::parser::kvpair>().first == "time");
CHECK(pair3.get<logfmt::parser::kvpair>().second
.get<logfmt::parser::int_value>().iv_value == 333);
auto pair4 = p.step();
CHECK(pair4.is<logfmt::parser::kvpair>());
CHECK(pair4.get<logfmt::parser::kvpair>().first == "empty1");
CHECK(pair4.get<logfmt::parser::kvpair>().second
.get<logfmt::parser::unquoted_value>().uv_value == "");
auto pair5 = p.step();
CHECK(pair5.is<logfmt::parser::kvpair>());
CHECK(pair5.get<logfmt::parser::kvpair>().first == "tf");
CHECK(pair5.get<logfmt::parser::kvpair>().second
.get<logfmt::parser::bool_value>().bv_value);
auto pair6 = p.step();
CHECK(pair6.is<logfmt::parser::kvpair>());
CHECK(pair6.get<logfmt::parser::kvpair>().first == "empty2");
CHECK(pair6.get<logfmt::parser::kvpair>().second
.get<logfmt::parser::unquoted_value>().uv_value == "");
auto eoi = p.step();
CHECK(eoi.is<logfmt::parser::end_of_input>());
}
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<logfmt::parser::kvpair>());
CHECK(pair1.get<logfmt::parser::kvpair>().first == "f1");
CHECK(pair1.get<logfmt::parser::kvpair>().second
.get<logfmt::parser::float_value>().fv_value == 1.0);
auto pair2 = p.step();
CHECK(pair2.is<logfmt::parser::kvpair>());
CHECK(pair2.get<logfmt::parser::kvpair>().first == "f2");
CHECK(pair2.get<logfmt::parser::kvpair>().second
.get<logfmt::parser::float_value>().fv_value == -2.0);
auto pair3 = p.step();
CHECK(pair3.is<logfmt::parser::kvpair>());
CHECK(pair3.get<logfmt::parser::kvpair>().first == "f3");
CHECK(pair3.get<logfmt::parser::kvpair>().second
.get<logfmt::parser::float_value>().fv_value == 1200);
auto pair4 = p.step();
CHECK(pair4.is<logfmt::parser::kvpair>());
CHECK(pair4.get<logfmt::parser::kvpair>().first == "f4");
CHECK(pair4.get<logfmt::parser::kvpair>().second
.get<logfmt::parser::float_value>().fv_value == 0.012);
auto pair5 = p.step();
CHECK(pair5.is<logfmt::parser::kvpair>());
CHECK(pair5.get<logfmt::parser::kvpair>().first == "f5");
CHECK(pair5.get<logfmt::parser::kvpair>().second
.get<logfmt::parser::float_value>().fv_value == 20);
auto pair6 = p.step();
CHECK(pair6.is<logfmt::parser::kvpair>());
CHECK(pair6.get<logfmt::parser::kvpair>().first == "f6");
CHECK(pair6.get<logfmt::parser::kvpair>().second
.get<logfmt::parser::float_value>().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<logfmt::parser::kvpair>());
CHECK(pair1.get<logfmt::parser::kvpair>().first == "bf1");
CHECK(pair1.get<logfmt::parser::kvpair>().second
.get<logfmt::parser::unquoted_value>().uv_value == "-");
auto pair2 = p.step();
CHECK(pair2.is<logfmt::parser::kvpair>());
CHECK(pair2.get<logfmt::parser::kvpair>().first == "bf2");
CHECK(pair2.get<logfmt::parser::kvpair>().second
.get<logfmt::parser::unquoted_value>().uv_value == "-1.2e");
auto pair3 = p.step();
CHECK(pair3.is<logfmt::parser::kvpair>());
CHECK(pair3.get<logfmt::parser::kvpair>().first == "bf3");
CHECK(pair3.get<logfmt::parser::kvpair>().second
.get<logfmt::parser::unquoted_value>().uv_value == "1.2.3");
auto pair4 = p.step();
CHECK(pair4.is<logfmt::parser::kvpair>());
CHECK(pair4.get<logfmt::parser::kvpair>().first == "bf4");
CHECK(pair4.get<logfmt::parser::kvpair>().second
.get<logfmt::parser::unquoted_value>().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<logfmt::parser::error>());
CHECK(pair1.get<logfmt::parser::error>().e_offset == 9);
CHECK(pair1.get<logfmt::parser::error>().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<logfmt::parser::error>());
CHECK(pair1.get<logfmt::parser::error>().e_offset == 3);
CHECK(pair1.get<logfmt::parser::error>().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<logfmt::parser::error>());
CHECK(pair1.get<logfmt::parser::error>().e_offset == 0);
CHECK(pair1.get<logfmt::parser::error>().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<logfmt::parser::end_of_input>());
}

@ -234,9 +234,6 @@ Spatial Navigation
also jump to the start of any log partitions that have also jump to the start of any log partitions that have
been created with the 'partition-name' command. 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 s/S Move to the next/previous "slow down" in the log message
rate. A slow down is detected by measuring how quickly rate. A slow down is detected by measuring how quickly
the message rate has changed over the previous several the message rate has changed over the previous several

@ -771,7 +771,7 @@ char(*X*)
HI HI
**See Also** **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 0
**See Also** **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 0
**See Also** **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} {"col_0":1.0,"col_1":2.0}
**See Also** **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 hw,gw
**See Also** **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 4e7a190aead058cb123c94290f29c34a
**See Also** **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 10.0MB
**See Also** **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 2
**See Also** **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 abc
**See Also** **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 3
**See Also** **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:
lower(*str*) lower(*str*)
@ -2009,7 +2033,7 @@ lower(*str*)
abc abc
**See Also** **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 c
**See Also** **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 abcdef ghi
**See Also** **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 abcdef
**See Also** **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 abcdefghi
**See Also** **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 value: 00011
**See Also** **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! Hello, World!
**See Also** **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 1 2 3 8 9 2
**See Also** **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"} {"num":123,"str":"four"}
**See Also** **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> <abc> <123> <abc>
**See Also** **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 zbc
**See Also** **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 abcabcabc
**See Also** **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 cba
**See Also** **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 abc
**See Also** **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 a
**See Also** **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** **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 f96b3d9c1a19f4394c97a1b79b1880df
**See Also** **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 0
**See Also** **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 bcbc
**See Also** **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 b
**See Also** **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 abc
**See Also** **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 97
**See Also** **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 ABC
**See Also** **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 ★ Hello ★ /abc/def/text() {} Hello ★
**See Also** **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`
---- ----

@ -103,12 +103,6 @@
"command": ":next-mark warning", "command": ":next-mark warning",
"alt-msg": "${keymap_def_alt_hour_boundary}" "alt-msg": "${keymap_def_alt_hour_boundary}"
}, },
"x59": {
"command": ":prev-mark query"
},
"x79": {
"command": ":next-mark query"
},
"x63": { "x63": {
"command": ":write-to /dev/clipboard", "command": ":write-to /dev/clipboard",
"alt-msg": "${keymap_def_clear}" "alt-msg": "${keymap_def_clear}"

@ -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")); lnav_log_file = make_optional_from_nullable(fopen(lnav_data.ld_debug_log_name, "a"));
log_info("lnav started"); log_info("lnav started");
{
static auto builtin_formats =
injector::get<std::vector<std::shared_ptr<log_format>>>();
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); load_config(lnav_data.ld_config_paths, config_errors);
if (!config_errors.empty()) { if (!config_errors.empty()) {
print_errors(config_errors); print_errors(config_errors);

@ -65,37 +65,6 @@ bool change_to_parent_dir()
return retval; return retval;
} }
template<typename T>
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>(long long &num_out, const char *string, size_t len);
template
size_t strtonum<long>(long &num_out, const char *string, size_t len);
template
size_t strtonum<int>(int &num_out, const char *string, size_t len);
string build_path(const vector<ghc::filesystem::path> &paths) string build_path(const vector<ghc::filesystem::path> &paths)
{ {
string retval; string retval;

@ -127,9 +127,6 @@ std::string build_path(const std::vector<ghc::filesystem::path> &paths);
Result<std::string, std::string> read_file(const ghc::filesystem::path &path); Result<std::string, std::string> read_file(const ghc::filesystem::path &path);
template<typename T>
size_t strtonum(T &num_out, const char *data, size_t len);
inline short pollfd_revents(const std::vector<struct pollfd> &pollfds, int fd) { inline short pollfd_revents(const std::vector<struct pollfd> &pollfds, int fd) {
auto iter = std::find_if(pollfds.begin(), pollfds.end(), [fd](const auto& entry) { auto iter = std::find_if(pollfds.begin(), pollfds.end(), [fd](const auto& entry) {
return entry.fd == fd; return entry.fd == fd;

@ -306,18 +306,6 @@ public:
*/ */
static std::vector<std::shared_ptr<log_format>> &get_root_formats(); static std::vector<std::shared_ptr<log_format>> &get_root_formats();
/**
* Template used to register log formats during initialization.
*/
template<class T>
class register_root_format {
public:
register_root_format()
{
log_format::lf_root_formats.push_back(std::make_shared<T>());
};
};
static std::shared_ptr<log_format> find_root_format(const char *name) { static std::shared_ptr<log_format> find_root_format(const char *name) {
auto& fmts = get_root_formats(); auto& fmts = get_root_formats();
for (auto& lf : fmts) { for (auto& lf : fmts) {

@ -41,6 +41,9 @@
#include "log_format.hh" #include "log_format.hh"
#include "log_vtab_impl.hh" #include "log_vtab_impl.hh"
#include "base/opt_util.hh" #include "base/opt_util.hh"
#include "base/injector.bind.hh"
#include "yajlpp/yajlpp.hh"
#include "formats/logfmt/logfmt.parser.hh"
using namespace std; using namespace std;
@ -688,7 +691,7 @@ public:
} }
void get_columns(vector<vtab_column> &cols) const { void get_columns(vector<vtab_column> &cols) const override {
for (const auto &fd : this->blt_format.blf_field_defs) { for (const auto &fd : this->blt_format.blf_field_defs) {
std::pair<int, unsigned int> type_pair = log_vtab_impl::logline_value_to_sqlite_type(fd.fd_meta.lvm_kind); std::pair<int, unsigned int> 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<std::string> &keys_inout) const { void get_foreign_keys(std::vector<std::string> &keys_inout) const override {
this->log_vtab_impl::get_foreign_keys(keys_inout); this->log_vtab_impl::get_foreign_keys(keys_inout);
for (const auto &fd : this->blt_format.blf_field_defs) { for (const auto &fd : this->blt_format.blf_field_defs) {
@ -1404,6 +1407,247 @@ const std::vector<w3c_log_format::field_to_struct_t> w3c_log_format::KNOWN_STRUC
{"sr(", "sr_headers"}, {"sr(", "sr_headers"},
}; };
log_format::register_root_format<bro_log_format> bro_log_instance; struct logfmt_pair_handler {
log_format::register_root_format<w3c_log_format> w3c_log_instance; explicit logfmt_pair_handler(date_time_scanner &dts) : lph_dt_scanner(dts)
log_format::register_root_format<generic_log_format> generic_log_instance; {
}
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<vtab_column> &cols) const override {
static const auto FIELDS = std::string("fields");
cols.emplace_back(FIELDS);
};
};
shared_ptr<log_vtab_impl> get_vtab_impl() const override
{
static auto retval = std::make_shared<logfmt_log_table>(*this);
return retval;
}
scan_result_t scan(logfile &lf, vector<logline> &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<yajl_handle_t> 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<logline_value> &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<logfmt::parser::int_value>() &&
!kvp.second.is<logfmt::parser::bool_value>()) {
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<log_format> specialized(int fmt_lock) override
{
return std::make_shared<logfmt_format>(*this);
};
};
static auto format_binder = injector::bind_multiple<log_format>()
.add<logfmt_format>()
.add<bro_log_format>()
.add<w3c_log_format>()
.add<generic_log_format>();

@ -48,9 +48,6 @@ public:
next = bm[&textview_curses::BM_SEARCH].next(vis_line_t(start)); next = bm[&textview_curses::BM_SEARCH].next(vis_line_t(start));
search_hit = (next != -1 && next <= end); 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)); next = bm[&textview_curses::BM_USER].next(vis_line_t(start));
if (next == -1) { if (next == -1) {
next = bm[&textview_curses::BM_META].next(vis_line_t(start)); next = bm[&textview_curses::BM_META].next(vis_line_t(start));

@ -33,6 +33,7 @@
#include "base/string_util.hh" #include "base/string_util.hh"
#include "sql_util.hh" #include "sql_util.hh"
#include "log_vtab_impl.hh" #include "log_vtab_impl.hh"
#include "yajlpp/json_op.hh"
#include "yajlpp/yajlpp_def.hh" #include "yajlpp/yajlpp_def.hh"
#include "vtab_module.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: case value_kind_t::VALUE_FLOAT:
root.gen(lv_struct.lv_value.d); root.gen(lv_struct.lv_value.d);
break; break;
case value_kind_t::VALUE_JSON: {
auto_mem<yajl_handle_t> 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: default:
root.gen(lv_struct.to_string()); root.gen(lv_struct.to_string());
break; break;

@ -19,7 +19,9 @@
#include "base/humanize.hh" #include "base/humanize.hh"
#include "base/string_util.hh" #include "base/string_util.hh"
#include "formats/logfmt/logfmt.parser.hh"
#include "yajlpp/yajlpp.hh" #include "yajlpp/yajlpp.hh"
#include "yajlpp/json_op.hh"
#include "column_namer.hh" #include "column_namer.hh"
#include "yajl/api/yajl_gen.h" #include "yajl/api/yajl_gen.h"
#include "sqlite-extension-func.hh" #include "sqlite-extension-func.hh"
@ -183,6 +185,70 @@ json_string extract(const char *str)
return json_string(gen); 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<yajl_handle_t> 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 static
string regexp_replace(const char *str, const char *re, const char *repl) 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<decltype(&logfmt2json), logfmt2json>::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<decltype( sqlite_func_adapter<decltype(
static_cast<bool (*)(const char *, const char *)>(&startswith)), static_cast<bool (*)(const char *, const char *)>(&startswith)),
startswith>::builder( startswith>::builder(

@ -140,6 +140,17 @@ struct from_sqlite<const char *> {
} }
}; };
template<>
struct from_sqlite<string_fragment> {
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<> template<>
struct from_sqlite<std::string> { struct from_sqlite<std::string> {
inline std::string operator()(int argc, sqlite3_value **val, int argi) { inline std::string operator()(int argc, sqlite3_value **val, int argi) {

@ -424,6 +424,11 @@ public:
return yajl_gen_string(this->yg_handle, (const unsigned char *)str.get(), str.size()); 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) yajl_gen_status operator()(bool value)
{ {
return yajl_gen_bool(this->yg_handle, value); return yajl_gen_bool(this->yg_handle, value);

@ -58,7 +58,7 @@ target_link_libraries(test_reltime diag PkgConfig::libpcre)
add_test(NAME test_reltime COMMAND test_reltime) add_test(NAME test_reltime COMMAND test_reltime)
add_executable(test_top_status test_top_status.cc) 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_test(NAME test_top_status COMMAND test_top_status)
add_executable(drive_view_colors drive_view_colors.cc) 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) target_link_libraries(drive_sql_anno diag testdummy PkgConfig::libpcre)
add_executable(drive_data_scanner drive_data_scanner.cc) 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) add_executable(scripty scripty.cc)
target_link_libraries(scripty diag PkgConfig::ncursesw) target_link_libraries(scripty diag PkgConfig::ncursesw)

@ -101,6 +101,7 @@ LDADD = \
$(TEXT2C_OBJS) \ $(TEXT2C_OBJS) \
$(DUMMY_OBJS) \ $(DUMMY_OBJS) \
$(top_builddir)/src/libdiag.a \ $(top_builddir)/src/libdiag.a \
$(top_builddir)/src/formats/logfmt/liblogfmt.a \
$(top_builddir)/src/fmtlib/libcppfmt.a \ $(top_builddir)/src/fmtlib/libcppfmt.a \
$(top_builddir)/src/pcrepp/libpcrepp.a \ $(top_builddir)/src/pcrepp/libpcrepp.a \
$(top_builddir)/src/yajl/libyajl.a \ $(top_builddir)/src/yajl/libyajl.a \
@ -277,6 +278,7 @@ dist_noinst_DATA = \
logfile_json2.json \ logfile_json2.json \
logfile_json3.json \ logfile_json3.json \
logfile_leveltest.0 \ logfile_leveltest.0 \
logfile_logfmt.0 \
logfile_multiline.0 \ logfile_multiline.0 \
logfile_nested_json.json \ logfile_nested_json.json \
logfile_openam.0 \ logfile_openam.0 \

@ -35,6 +35,7 @@
#include <fstream> #include <fstream>
#include <iostream> #include <iostream>
#include "base/injector.hh"
#include "textview_curses.hh" #include "textview_curses.hh"
#include "data_scanner.hh" #include "data_scanner.hh"
#include "data_parser.hh" #include "data_parser.hh"
@ -55,6 +56,16 @@ int main(int argc, char *argv[])
int c, retval = EXIT_SUCCESS; int c, retval = EXIT_SUCCESS;
bool prompt = false, is_log = false, pretty_print = false; bool prompt = false, is_log = false, pretty_print = false;
{
static auto builtin_formats =
injector::get<std::vector<std::shared_ptr<log_format>>>();
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<ghc::filesystem::path> paths; std::vector<ghc::filesystem::path> paths;
vector<string> errors; vector<string> errors;

@ -39,6 +39,8 @@
#include <algorithm> #include <algorithm>
#include "base/injector.hh"
#include "base/opt_util.hh"
#include "logfile.hh" #include "logfile.hh"
#include "log_format.hh" #include "log_format.hh"
#include "log_format_loader.hh" #include "log_format_loader.hh"
@ -64,13 +66,23 @@ int main(int argc, char *argv[])
dl_mode_t mode = MODE_NONE; dl_mode_t mode = MODE_NONE;
string expected_format; string expected_format;
{
static auto builtin_formats =
injector::get<std::vector<std::shared_ptr<log_format>>>();
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<std::string> errors; std::vector<std::string> errors;
vector<ghc::filesystem::path> paths; vector<ghc::filesystem::path> paths;
if (getenv("test_dir") != NULL) { getenv_opt("test_dir") | [&paths](auto value) {
paths.push_back(getenv("test_dir")); paths.template emplace_back(value);
} };
load_formats(paths, errors); load_formats(paths, errors);
} }

@ -234,9 +234,6 @@ Spatial Navigation
also jump to the start of any log partitions that have also jump to the start of any log partitions that have
been created with the 'partition-name' command. 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 s/S Move to the next/previous "slow down" in the log message
rate. A slow down is detected by measuring how quickly rate. A slow down is detected by measuring how quickly
the message rate has changed over the previous several the message rate has changed over the previous several
@ -1236,11 +1233,11 @@ Parameter
X The unicode code point values X The unicode code point values
See Also See Also
charindex(), endswith(), extract(), group_concat(), group_spooky_hash(), charindex(), endswith(), extract(), group_concat(), group_spooky_hash(),
humanize_file_size(), instr(), leftstr(), length(), lower(), ltrim(), padc(), humanize_file_size(), instr(), leftstr(), length(), logfmt2json(), lower(),
padl(), padr(), printf(), proper(), regexp_capture(), regexp_match(), ltrim(), padc(), padl(), padr(), printf(), proper(), regexp_capture(),
regexp_replace(), replace(), replicate(), reverse(), rightstr(), rtrim(), regexp_match(), regexp_replace(), replace(), replicate(), reverse(), rightstr(),
sparkline(), spooky_hash(), startswith(), strfilter(), substr(), trim(), rtrim(), sparkline(), spooky_hash(), startswith(), strfilter(), substr(),
unicode(), upper(), xpath() trim(), unicode(), upper(), xpath()
Example Example
#1 To get a string with the code points 0x48 and 0x49: #1 To get a string with the code points 0x48 and 0x49:
;SELECT char(0x48, 0x49) ;SELECT char(0x48, 0x49)
@ -1257,11 +1254,11 @@ Parameters
start The one-based index within the haystack to start the search start The one-based index within the haystack to start the search
See Also See Also
char(), endswith(), extract(), group_concat(), group_spooky_hash(), char(), endswith(), extract(), group_concat(), group_spooky_hash(),
humanize_file_size(), instr(), leftstr(), length(), lower(), ltrim(), padc(), humanize_file_size(), instr(), leftstr(), length(), logfmt2json(), lower(),
padl(), padr(), printf(), proper(), regexp_capture(), regexp_match(), ltrim(), padc(), padl(), padr(), printf(), proper(), regexp_capture(),
regexp_replace(), replace(), replicate(), reverse(), rightstr(), rtrim(), regexp_match(), regexp_replace(), replace(), replicate(), reverse(), rightstr(),
sparkline(), spooky_hash(), startswith(), strfilter(), substr(), trim(), rtrim(), sparkline(), spooky_hash(), startswith(), strfilter(), substr(),
unicode(), upper(), xpath() trim(), unicode(), upper(), xpath()
Examples Examples
#1 To search for the string 'abc' within 'abcabc' and starting at position 2: #1 To search for the string 'abc' within 'abcabc' and starting at position 2:
;SELECT charindex('abc', 'abcabc', 2) ;SELECT charindex('abc', 'abcabc', 2)
@ -1408,11 +1405,11 @@ Parameters
suffix The suffix to check in the string suffix The suffix to check in the string
See Also See Also
char(), charindex(), extract(), group_concat(), group_spooky_hash(), char(), charindex(), extract(), group_concat(), group_spooky_hash(),
humanize_file_size(), instr(), leftstr(), length(), lower(), ltrim(), padc(), humanize_file_size(), instr(), leftstr(), length(), logfmt2json(), lower(),
padl(), padr(), printf(), proper(), regexp_capture(), regexp_match(), ltrim(), padc(), padl(), padr(), printf(), proper(), regexp_capture(),
regexp_replace(), replace(), replicate(), reverse(), rightstr(), rtrim(), regexp_match(), regexp_replace(), replace(), replicate(), reverse(), rightstr(),
sparkline(), spooky_hash(), startswith(), strfilter(), substr(), trim(), rtrim(), sparkline(), spooky_hash(), startswith(), strfilter(), substr(),
unicode(), upper(), xpath() trim(), unicode(), upper(), xpath()
Examples Examples
#1 To test if the string 'notbad.jpg' ends with '.jpg': #1 To test if the string 'notbad.jpg' ends with '.jpg':
;SELECT endswith('notbad.jpg', '.jpg') ;SELECT endswith('notbad.jpg', '.jpg')
@ -1443,11 +1440,11 @@ Parameter
str The string to parse str The string to parse
See Also See Also
char(), charindex(), endswith(), group_concat(), group_spooky_hash(), char(), charindex(), endswith(), group_concat(), group_spooky_hash(),
humanize_file_size(), instr(), leftstr(), length(), lower(), ltrim(), padc(), humanize_file_size(), instr(), leftstr(), length(), logfmt2json(), lower(),
padl(), padr(), printf(), proper(), regexp_capture(), regexp_match(), ltrim(), padc(), padl(), padr(), printf(), proper(), regexp_capture(),
regexp_replace(), replace(), replicate(), reverse(), rightstr(), rtrim(), regexp_match(), regexp_replace(), replace(), replicate(), reverse(), rightstr(),
sparkline(), spooky_hash(), startswith(), strfilter(), substr(), trim(), rtrim(), sparkline(), spooky_hash(), startswith(), strfilter(), substr(),
unicode(), upper(), xpath() trim(), unicode(), upper(), xpath()
Examples Examples
#1 To extract key/value pairs from a string: #1 To extract key/value pairs from a string:
;SELECT extract('foo=1 bar=2 name="Rolo Tomassi"') ;SELECT extract('foo=1 bar=2 name="Rolo Tomassi"')
@ -1550,11 +1547,11 @@ Parameters
sep The separator to place between the values. sep The separator to place between the values.
See Also See Also
char(), charindex(), endswith(), extract(), group_spooky_hash(), char(), charindex(), endswith(), extract(), group_spooky_hash(),
humanize_file_size(), instr(), leftstr(), length(), lower(), ltrim(), padc(), humanize_file_size(), instr(), leftstr(), length(), logfmt2json(), lower(),
padl(), padr(), printf(), proper(), regexp_capture(), regexp_match(), ltrim(), padc(), padl(), padr(), printf(), proper(), regexp_capture(),
regexp_replace(), replace(), replicate(), reverse(), rightstr(), rtrim(), regexp_match(), regexp_replace(), replace(), replicate(), reverse(), rightstr(),
sparkline(), spooky_hash(), startswith(), strfilter(), substr(), trim(), rtrim(), sparkline(), spooky_hash(), startswith(), strfilter(), substr(),
unicode(), upper(), xpath() trim(), unicode(), upper(), xpath()
Examples Examples
#1 To concatenate the values of the column 'ex_procname' from the table 'lnav_example_log' #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 str The string to hash
See Also See Also
char(), charindex(), endswith(), extract(), group_concat(), char(), charindex(), endswith(), extract(), group_concat(),
humanize_file_size(), instr(), leftstr(), length(), lower(), ltrim(), padc(), humanize_file_size(), instr(), leftstr(), length(), logfmt2json(), lower(),
padl(), padr(), printf(), proper(), regexp_capture(), regexp_match(), ltrim(), padc(), padl(), padr(), printf(), proper(), regexp_capture(),
regexp_replace(), replace(), replicate(), reverse(), rightstr(), rtrim(), regexp_match(), regexp_replace(), replace(), replicate(), reverse(), rightstr(),
sparkline(), spooky_hash(), startswith(), strfilter(), substr(), trim(), rtrim(), sparkline(), spooky_hash(), startswith(), strfilter(), substr(),
unicode(), upper(), xpath() trim(), unicode(), upper(), xpath()
Example Example
#1 To produce a hash of all of the values of 'column1': #1 To produce a hash of all of the values of 'column1':
;SELECT group_spooky_hash(column1) FROM (VALUES ('abc'), ('123')) ;SELECT group_spooky_hash(column1) FROM (VALUES ('abc'), ('123'))
@ -1608,8 +1605,8 @@ Parameter
value The file size to format value The file size to format
See Also See Also
char(), charindex(), endswith(), extract(), group_concat(), group_spooky_hash(), char(), charindex(), endswith(), extract(), group_concat(), group_spooky_hash(),
instr(), leftstr(), length(), lower(), ltrim(), padc(), padl(), padr(), instr(), leftstr(), length(), logfmt2json(), lower(), ltrim(), padc(), padl(),
printf(), proper(), regexp_capture(), regexp_match(), regexp_replace(), padr(), printf(), proper(), regexp_capture(), regexp_match(), regexp_replace(),
replace(), replicate(), reverse(), rightstr(), rtrim(), sparkline(), replace(), replicate(), reverse(), rightstr(), rtrim(), sparkline(),
spooky_hash(), startswith(), strfilter(), substr(), trim(), unicode(), upper(), spooky_hash(), startswith(), strfilter(), substr(), trim(), unicode(), upper(),
xpath() xpath()
@ -1641,11 +1638,11 @@ Parameters
needle The string to look for in the haystack needle The string to look for in the haystack
See Also See Also
char(), charindex(), endswith(), extract(), group_concat(), group_spooky_hash(), char(), charindex(), endswith(), extract(), group_concat(), group_spooky_hash(),
humanize_file_size(), leftstr(), length(), lower(), ltrim(), padc(), padl(), humanize_file_size(), leftstr(), length(), logfmt2json(), lower(), ltrim(),
padr(), printf(), proper(), regexp_capture(), regexp_match(), regexp_replace(), padc(), padl(), padr(), printf(), proper(), regexp_capture(), regexp_match(),
replace(), replicate(), reverse(), rightstr(), rtrim(), sparkline(), regexp_replace(), replace(), replicate(), reverse(), rightstr(), rtrim(),
spooky_hash(), startswith(), strfilter(), substr(), trim(), unicode(), upper(), sparkline(), spooky_hash(), startswith(), strfilter(), substr(), trim(),
xpath() unicode(), upper(), xpath()
Example Example
#1 To test get the position of 'b' in the string 'abc': #1 To test get the position of 'b' in the string 'abc':
;SELECT instr('abc', 'b') ;SELECT instr('abc', 'b')
@ -1847,11 +1844,11 @@ Parameters
N The number of characters from the left side of the string to return. N The number of characters from the left side of the string to return.
See Also See Also
char(), charindex(), endswith(), extract(), group_concat(), group_spooky_hash(), char(), charindex(), endswith(), extract(), group_concat(), group_spooky_hash(),
humanize_file_size(), instr(), length(), lower(), ltrim(), padc(), padl(), humanize_file_size(), instr(), length(), logfmt2json(), lower(), ltrim(),
padr(), printf(), proper(), regexp_capture(), regexp_match(), regexp_replace(), padc(), padl(), padr(), printf(), proper(), regexp_capture(), regexp_match(),
replace(), replicate(), reverse(), rightstr(), rtrim(), sparkline(), regexp_replace(), replace(), replicate(), reverse(), rightstr(), rtrim(),
spooky_hash(), startswith(), strfilter(), substr(), trim(), unicode(), upper(), sparkline(), spooky_hash(), startswith(), strfilter(), substr(), trim(),
xpath() unicode(), upper(), xpath()
Examples Examples
#1 To get the first character of the string 'abc': #1 To get the first character of the string 'abc':
;SELECT leftstr('abc', 1) ;SELECT leftstr('abc', 1)
@ -1869,11 +1866,11 @@ Parameter
str The string to determine the length of str The string to determine the length of
See Also See Also
char(), charindex(), endswith(), extract(), group_concat(), group_spooky_hash(), char(), charindex(), endswith(), extract(), group_concat(), group_spooky_hash(),
humanize_file_size(), instr(), leftstr(), lower(), ltrim(), padc(), padl(), humanize_file_size(), instr(), leftstr(), logfmt2json(), lower(), ltrim(),
padr(), printf(), proper(), regexp_capture(), regexp_match(), regexp_replace(), padc(), padl(), padr(), printf(), proper(), regexp_capture(), regexp_match(),
replace(), replicate(), reverse(), rightstr(), rtrim(), sparkline(), regexp_replace(), replace(), replicate(), reverse(), rightstr(), rtrim(),
spooky_hash(), startswith(), strfilter(), substr(), trim(), unicode(), upper(), sparkline(), spooky_hash(), startswith(), strfilter(), substr(), trim(),
xpath() unicode(), upper(), xpath()
Example Example
#1 To get the length of the string 'abc': #1 To get the length of the string 'abc':
;SELECT length('abc') ;SELECT length('abc')
@ -1923,7 +1920,7 @@ Synopsis
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. given shared library file using the given entry point.
Parameters Parameters
path The path to the shared library containing the extension. 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. 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 Synopsis
lower(str) -- Returns a copy of the given string with all ASCII characters lower(str) -- Returns a copy of the given string with all ASCII characters
converted to lower case. converted to lower case.
@ -1974,11 +1988,11 @@ Parameter
str The string to convert. str The string to convert.
See Also See Also
char(), charindex(), endswith(), extract(), group_concat(), group_spooky_hash(), char(), charindex(), endswith(), extract(), group_concat(), group_spooky_hash(),
humanize_file_size(), instr(), leftstr(), length(), ltrim(), padc(), padl(), humanize_file_size(), instr(), leftstr(), length(), logfmt2json(), ltrim(),
padr(), printf(), proper(), regexp_capture(), regexp_match(), regexp_replace(), padc(), padl(), padr(), printf(), proper(), regexp_capture(), regexp_match(),
replace(), replicate(), reverse(), rightstr(), rtrim(), sparkline(), regexp_replace(), replace(), replicate(), reverse(), rightstr(), rtrim(),
spooky_hash(), startswith(), strfilter(), substr(), trim(), unicode(), upper(), sparkline(), spooky_hash(), startswith(), strfilter(), substr(), trim(),
xpath() unicode(), upper(), xpath()
Example Example
#1 To lowercase the string 'AbC': #1 To lowercase the string 'AbC':
;SELECT lower('AbC') ;SELECT lower('AbC')
@ -1994,11 +2008,11 @@ Parameters
chars The characters to trim. Defaults to spaces. chars The characters to trim. Defaults to spaces.
See Also See Also
char(), charindex(), endswith(), extract(), group_concat(), group_spooky_hash(), char(), charindex(), endswith(), extract(), group_concat(), group_spooky_hash(),
humanize_file_size(), instr(), leftstr(), length(), lower(), padc(), padl(), humanize_file_size(), instr(), leftstr(), length(), logfmt2json(), lower(),
padr(), printf(), proper(), regexp_capture(), regexp_match(), regexp_replace(), padc(), padl(), padr(), printf(), proper(), regexp_capture(), regexp_match(),
replace(), replicate(), reverse(), rightstr(), rtrim(), sparkline(), regexp_replace(), replace(), replicate(), reverse(), rightstr(), rtrim(),
spooky_hash(), startswith(), strfilter(), substr(), trim(), unicode(), upper(), sparkline(), spooky_hash(), startswith(), strfilter(), substr(), trim(),
xpath() unicode(), upper(), xpath()
Examples Examples
#1 To trim the leading whitespace from the string ' abc': #1 To trim the leading whitespace from the string ' abc':
;SELECT ltrim(' abc') ;SELECT ltrim(' abc')
@ -2093,11 +2107,11 @@ Parameters
len The minimum desired length of the output string len The minimum desired length of the output string
See Also See Also
char(), charindex(), endswith(), extract(), group_concat(), group_spooky_hash(), char(), charindex(), endswith(), extract(), group_concat(), group_spooky_hash(),
humanize_file_size(), instr(), leftstr(), length(), lower(), ltrim(), padl(), humanize_file_size(), instr(), leftstr(), length(), logfmt2json(), lower(),
padr(), printf(), proper(), regexp_capture(), regexp_match(), regexp_replace(), ltrim(), padl(), padr(), printf(), proper(), regexp_capture(), regexp_match(),
replace(), replicate(), reverse(), rightstr(), rtrim(), sparkline(), regexp_replace(), replace(), replicate(), reverse(), rightstr(), rtrim(),
spooky_hash(), startswith(), strfilter(), substr(), trim(), unicode(), upper(), sparkline(), spooky_hash(), startswith(), strfilter(), substr(), trim(),
xpath() unicode(), upper(), xpath()
Examples Examples
#1 To pad the string 'abc' to a length of six characters: #1 To pad the string 'abc' to a length of six characters:
;SELECT padc('abc', 6) || 'def' ;SELECT padc('abc', 6) || 'def'
@ -2116,11 +2130,11 @@ Parameters
len The minimum desired length of the output string len The minimum desired length of the output string
See Also See Also
char(), charindex(), endswith(), extract(), group_concat(), group_spooky_hash(), char(), charindex(), endswith(), extract(), group_concat(), group_spooky_hash(),
humanize_file_size(), instr(), leftstr(), length(), lower(), ltrim(), padc(), humanize_file_size(), instr(), leftstr(), length(), logfmt2json(), lower(),
padr(), printf(), proper(), regexp_capture(), regexp_match(), regexp_replace(), ltrim(), padc(), padr(), printf(), proper(), regexp_capture(), regexp_match(),
replace(), replicate(), reverse(), rightstr(), rtrim(), sparkline(), regexp_replace(), replace(), replicate(), reverse(), rightstr(), rtrim(),
spooky_hash(), startswith(), strfilter(), substr(), trim(), unicode(), upper(), sparkline(), spooky_hash(), startswith(), strfilter(), substr(), trim(),
xpath() unicode(), upper(), xpath()
Examples Examples
#1 To pad the string 'abc' to a length of six characters: #1 To pad the string 'abc' to a length of six characters:
;SELECT padl('abc', 6) ;SELECT padl('abc', 6)
@ -2139,11 +2153,11 @@ Parameters
len The minimum desired length of the output string len The minimum desired length of the output string
See Also See Also
char(), charindex(), endswith(), extract(), group_concat(), group_spooky_hash(), char(), charindex(), endswith(), extract(), group_concat(), group_spooky_hash(),
humanize_file_size(), instr(), leftstr(), length(), lower(), ltrim(), padc(), humanize_file_size(), instr(), leftstr(), length(), logfmt2json(), lower(),
padl(), printf(), proper(), regexp_capture(), regexp_match(), regexp_replace(), ltrim(), padc(), padl(), printf(), proper(), regexp_capture(), regexp_match(),
replace(), replicate(), reverse(), rightstr(), rtrim(), sparkline(), regexp_replace(), replace(), replicate(), reverse(), rightstr(), rtrim(),
spooky_hash(), startswith(), strfilter(), substr(), trim(), unicode(), upper(), sparkline(), spooky_hash(), startswith(), strfilter(), substr(), trim(),
xpath() unicode(), upper(), xpath()
Examples Examples
#1 To pad the string 'abc' to a length of six characters: #1 To pad the string 'abc' to a length of six characters:
;SELECT padr('abc', 6) || 'def' ;SELECT padr('abc', 6) || 'def'
@ -2196,11 +2210,11 @@ Parameters
X The argument to substitute at a given position in the format. X The argument to substitute at a given position in the format.
See Also See Also
char(), charindex(), endswith(), extract(), group_concat(), group_spooky_hash(), char(), charindex(), endswith(), extract(), group_concat(), group_spooky_hash(),
humanize_file_size(), instr(), leftstr(), length(), lower(), ltrim(), padc(), humanize_file_size(), instr(), leftstr(), length(), logfmt2json(), lower(),
padl(), padr(), proper(), regexp_capture(), regexp_match(), regexp_replace(), ltrim(), padc(), padl(), padr(), proper(), regexp_capture(), regexp_match(),
replace(), replicate(), reverse(), rightstr(), rtrim(), sparkline(), regexp_replace(), replace(), replicate(), reverse(), rightstr(), rtrim(),
spooky_hash(), startswith(), strfilter(), substr(), trim(), unicode(), upper(), sparkline(), spooky_hash(), startswith(), strfilter(), substr(), trim(),
xpath() unicode(), upper(), xpath()
Examples Examples
#1 To substitute 'World' into the string 'Hello, %s!': #1 To substitute 'World' into the string 'Hello, %s!':
;SELECT printf('Hello, %s!', 'World') ;SELECT printf('Hello, %s!', 'World')
@ -2221,11 +2235,11 @@ Parameter
str The string to capitalize. str The string to capitalize.
See Also See Also
char(), charindex(), endswith(), extract(), group_concat(), group_spooky_hash(), char(), charindex(), endswith(), extract(), group_concat(), group_spooky_hash(),
humanize_file_size(), instr(), leftstr(), length(), lower(), ltrim(), padc(), humanize_file_size(), instr(), leftstr(), length(), logfmt2json(), lower(),
padl(), padr(), printf(), regexp_capture(), regexp_match(), regexp_replace(), ltrim(), padc(), padl(), padr(), printf(), regexp_capture(), regexp_match(),
replace(), replicate(), reverse(), rightstr(), rtrim(), sparkline(), regexp_replace(), replace(), replicate(), reverse(), rightstr(), rtrim(),
spooky_hash(), startswith(), strfilter(), substr(), trim(), unicode(), upper(), sparkline(), spooky_hash(), startswith(), strfilter(), substr(), trim(),
xpath() unicode(), upper(), xpath()
Example Example
#1 To capitalize the words in the string 'hello, world!': #1 To capitalize the words in the string 'hello, world!':
;SELECT proper('hello, world!') ;SELECT proper('hello, world!')
@ -2326,10 +2340,11 @@ Results
content The captured value from the string. content The captured value from the string.
See Also See Also
char(), charindex(), endswith(), extract(), group_concat(), group_spooky_hash(), char(), charindex(), endswith(), extract(), group_concat(), group_spooky_hash(),
humanize_file_size(), instr(), leftstr(), length(), lower(), ltrim(), padc(), humanize_file_size(), instr(), leftstr(), length(), logfmt2json(), lower(),
padl(), padr(), printf(), proper(), regexp_match(), regexp_replace(), replace(), ltrim(), padc(), padl(), padr(), printf(), proper(), regexp_match(),
replicate(), reverse(), rightstr(), rtrim(), sparkline(), spooky_hash(), regexp_replace(), replace(), replicate(), reverse(), rightstr(), rtrim(),
startswith(), strfilter(), substr(), trim(), unicode(), upper(), xpath() sparkline(), spooky_hash(), startswith(), strfilter(), substr(), trim(),
unicode(), upper(), xpath()
Example Example
#1 To extract the key/value pairs 'a'/1 and 'b'/2 from the string 'a=1; b=2': #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+)') ;SELECT * FROM regexp_capture('a=1; b=2', '(\w+)=(\d+)')
@ -2344,11 +2359,11 @@ Parameters
str The string to test against the regular expression str The string to test against the regular expression
See Also See Also
char(), charindex(), endswith(), extract(), group_concat(), group_spooky_hash(), char(), charindex(), endswith(), extract(), group_concat(), group_spooky_hash(),
humanize_file_size(), instr(), leftstr(), length(), lower(), ltrim(), padc(), humanize_file_size(), instr(), leftstr(), length(), logfmt2json(), lower(),
padl(), padr(), printf(), proper(), regexp_capture(), regexp_replace(), ltrim(), padc(), padl(), padr(), printf(), proper(), regexp_capture(),
regexp_replace(), replace(), replicate(), reverse(), rightstr(), rtrim(), regexp_replace(), regexp_replace(), replace(), replicate(), reverse(),
sparkline(), spooky_hash(), startswith(), strfilter(), substr(), trim(), rightstr(), rtrim(), sparkline(), spooky_hash(), startswith(), strfilter(),
unicode(), upper(), xpath() substr(), trim(), unicode(), upper(), xpath()
Examples Examples
#1 To capture the digits from the string '123': #1 To capture the digits from the string '123':
;SELECT regexp_match('(\d+)', '123') ;SELECT regexp_match('(\d+)', '123')
@ -2375,11 +2390,11 @@ Parameters
backslash followed by the number of the group, starting with 1. backslash followed by the number of the group, starting with 1.
See Also See Also
char(), charindex(), endswith(), extract(), group_concat(), group_spooky_hash(), char(), charindex(), endswith(), extract(), group_concat(), group_spooky_hash(),
humanize_file_size(), instr(), leftstr(), length(), lower(), ltrim(), padc(), humanize_file_size(), instr(), leftstr(), length(), logfmt2json(), lower(),
padl(), padr(), printf(), proper(), regexp_capture(), regexp_match(), ltrim(), padc(), padl(), padr(), printf(), proper(), regexp_capture(),
regexp_match(), replace(), replicate(), reverse(), rightstr(), rtrim(), regexp_match(), regexp_match(), replace(), replicate(), reverse(), rightstr(),
sparkline(), spooky_hash(), startswith(), strfilter(), substr(), trim(), rtrim(), sparkline(), spooky_hash(), startswith(), strfilter(), substr(),
unicode(), upper(), xpath() trim(), unicode(), upper(), xpath()
Examples Examples
#1 To replace the word at the start of the string 'Hello, World!' with 'Goodbye': #1 To replace the word at the start of the string 'Hello, World!' with 'Goodbye':
;SELECT regexp_replace('Hello, World!', '^(\w+)', '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. replacement The string to replace any occurrences of the old string with.
See Also See Also
char(), charindex(), endswith(), extract(), group_concat(), group_spooky_hash(), char(), charindex(), endswith(), extract(), group_concat(), group_spooky_hash(),
humanize_file_size(), instr(), leftstr(), length(), lower(), ltrim(), padc(), humanize_file_size(), instr(), leftstr(), length(), logfmt2json(), lower(),
padl(), padr(), printf(), proper(), regexp_capture(), regexp_match(), ltrim(), padc(), padl(), padr(), printf(), proper(), regexp_capture(),
regexp_replace(), replicate(), reverse(), rightstr(), rtrim(), sparkline(), regexp_match(), regexp_replace(), replicate(), reverse(), rightstr(), rtrim(),
spooky_hash(), startswith(), strfilter(), substr(), trim(), unicode(), upper(), sparkline(), spooky_hash(), startswith(), strfilter(), substr(), trim(),
xpath() unicode(), upper(), xpath()
Examples Examples
#1 To replace the string 'x' with 'z' in 'abc': #1 To replace the string 'x' with 'z' in 'abc':
;SELECT replace('abc', 'x', 'z') ;SELECT replace('abc', 'x', 'z')
@ -2422,11 +2437,11 @@ Parameters
N The number of times to replicate the string. N The number of times to replicate the string.
See Also See Also
char(), charindex(), endswith(), extract(), group_concat(), group_spooky_hash(), char(), charindex(), endswith(), extract(), group_concat(), group_spooky_hash(),
humanize_file_size(), instr(), leftstr(), length(), lower(), ltrim(), padc(), humanize_file_size(), instr(), leftstr(), length(), logfmt2json(), lower(),
padl(), padr(), printf(), proper(), regexp_capture(), regexp_match(), ltrim(), padc(), padl(), padr(), printf(), proper(), regexp_capture(),
regexp_replace(), replace(), reverse(), rightstr(), rtrim(), sparkline(), regexp_match(), regexp_replace(), replace(), reverse(), rightstr(), rtrim(),
spooky_hash(), startswith(), strfilter(), substr(), trim(), unicode(), upper(), sparkline(), spooky_hash(), startswith(), strfilter(), substr(), trim(),
xpath() unicode(), upper(), xpath()
Example Example
#1 To repeat the string 'abc' three times: #1 To repeat the string 'abc' three times:
;SELECT replicate('abc', 3) ;SELECT replicate('abc', 3)
@ -2439,11 +2454,11 @@ Parameter
str The string to reverse. str The string to reverse.
See Also See Also
char(), charindex(), endswith(), extract(), group_concat(), group_spooky_hash(), char(), charindex(), endswith(), extract(), group_concat(), group_spooky_hash(),
humanize_file_size(), instr(), leftstr(), length(), lower(), ltrim(), padc(), humanize_file_size(), instr(), leftstr(), length(), logfmt2json(), lower(),
padl(), padr(), printf(), proper(), regexp_capture(), regexp_match(), ltrim(), padc(), padl(), padr(), printf(), proper(), regexp_capture(),
regexp_replace(), replace(), replicate(), rightstr(), rtrim(), sparkline(), regexp_match(), regexp_replace(), replace(), replicate(), rightstr(), rtrim(),
spooky_hash(), startswith(), strfilter(), substr(), trim(), unicode(), upper(), sparkline(), spooky_hash(), startswith(), strfilter(), substr(), trim(),
xpath() unicode(), upper(), xpath()
Example Example
#1 To reverse the string 'abc': #1 To reverse the string 'abc':
;SELECT reverse('abc') ;SELECT reverse('abc')
@ -2458,11 +2473,11 @@ Parameters
N The number of characters from the right side of the string to return. N The number of characters from the right side of the string to return.
See Also See Also
char(), charindex(), endswith(), extract(), group_concat(), group_spooky_hash(), char(), charindex(), endswith(), extract(), group_concat(), group_spooky_hash(),
humanize_file_size(), instr(), leftstr(), length(), lower(), ltrim(), padc(), humanize_file_size(), instr(), leftstr(), length(), logfmt2json(), lower(),
padl(), padr(), printf(), proper(), regexp_capture(), regexp_match(), ltrim(), padc(), padl(), padr(), printf(), proper(), regexp_capture(),
regexp_replace(), replace(), replicate(), reverse(), rtrim(), sparkline(), regexp_match(), regexp_replace(), replace(), replicate(), reverse(), rtrim(),
spooky_hash(), startswith(), strfilter(), substr(), trim(), unicode(), upper(), sparkline(), spooky_hash(), startswith(), strfilter(), substr(), trim(),
xpath() unicode(), upper(), xpath()
Examples Examples
#1 To get the last character of the string 'abc': #1 To get the last character of the string 'abc':
;SELECT rightstr('abc', 1) ;SELECT rightstr('abc', 1)
@ -2519,11 +2534,11 @@ Parameters
chars The characters to trim. Defaults to spaces. chars The characters to trim. Defaults to spaces.
See Also See Also
char(), charindex(), endswith(), extract(), group_concat(), group_spooky_hash(), char(), charindex(), endswith(), extract(), group_concat(), group_spooky_hash(),
humanize_file_size(), instr(), leftstr(), length(), lower(), ltrim(), padc(), humanize_file_size(), instr(), leftstr(), length(), logfmt2json(), lower(),
padl(), padr(), printf(), proper(), regexp_capture(), regexp_match(), ltrim(), padc(), padl(), padr(), printf(), proper(), regexp_capture(),
regexp_replace(), replace(), replicate(), reverse(), rightstr(), sparkline(), regexp_match(), regexp_replace(), replace(), replicate(), reverse(), rightstr(),
spooky_hash(), startswith(), strfilter(), substr(), trim(), unicode(), upper(), sparkline(), spooky_hash(), startswith(), strfilter(), substr(), trim(),
xpath() unicode(), upper(), xpath()
Examples Examples
#1 To trim the whitespace from the end of the string 'abc ': #1 To trim the whitespace from the end of the string 'abc ':
;SELECT rtrim('abc ') ;SELECT rtrim('abc ')
@ -2568,11 +2583,11 @@ Parameters
inputs. inputs.
See Also See Also
char(), charindex(), endswith(), extract(), group_concat(), group_spooky_hash(), char(), charindex(), endswith(), extract(), group_concat(), group_spooky_hash(),
humanize_file_size(), instr(), leftstr(), length(), lower(), ltrim(), padc(), humanize_file_size(), instr(), leftstr(), length(), logfmt2json(), lower(),
padl(), padr(), printf(), proper(), regexp_capture(), regexp_match(), ltrim(), padc(), padl(), padr(), printf(), proper(), regexp_capture(),
regexp_replace(), replace(), replicate(), reverse(), rightstr(), rtrim(), regexp_match(), regexp_replace(), replace(), replicate(), reverse(), rightstr(),
spooky_hash(), startswith(), strfilter(), substr(), trim(), unicode(), upper(), rtrim(), spooky_hash(), startswith(), strfilter(), substr(), trim(), unicode(),
xpath() upper(), xpath()
Examples Examples
#1 To get the unicode block element for the value 32 in the range of 0-128: #1 To get the unicode block element for the value 32 in the range of 0-128:
;SELECT sparkline(32, 128) ;SELECT sparkline(32, 128)
@ -2589,11 +2604,11 @@ Parameter
str The string to hash str The string to hash
See Also See Also
char(), charindex(), endswith(), extract(), group_concat(), group_spooky_hash(), char(), charindex(), endswith(), extract(), group_concat(), group_spooky_hash(),
humanize_file_size(), instr(), leftstr(), length(), lower(), ltrim(), padc(), humanize_file_size(), instr(), leftstr(), length(), logfmt2json(), lower(),
padl(), padr(), printf(), proper(), regexp_capture(), regexp_match(), ltrim(), padc(), padl(), padr(), printf(), proper(), regexp_capture(),
regexp_replace(), replace(), replicate(), reverse(), rightstr(), rtrim(), regexp_match(), regexp_replace(), replace(), replicate(), reverse(), rightstr(),
sparkline(), startswith(), strfilter(), substr(), trim(), unicode(), upper(), rtrim(), sparkline(), startswith(), strfilter(), substr(), trim(), unicode(),
xpath() upper(), xpath()
Examples Examples
#1 To produce a hash for the string 'Hello, World!': #1 To produce a hash for the string 'Hello, World!':
;SELECT spooky_hash('Hello, World!') ;SELECT spooky_hash('Hello, World!')
@ -2662,11 +2677,11 @@ Parameters
prefix The prefix to check in the string prefix The prefix to check in the string
See Also See Also
char(), charindex(), endswith(), extract(), group_concat(), group_spooky_hash(), char(), charindex(), endswith(), extract(), group_concat(), group_spooky_hash(),
humanize_file_size(), instr(), leftstr(), length(), lower(), ltrim(), padc(), humanize_file_size(), instr(), leftstr(), length(), logfmt2json(), lower(),
padl(), padr(), printf(), proper(), regexp_capture(), regexp_match(), ltrim(), padc(), padl(), padr(), printf(), proper(), regexp_capture(),
regexp_replace(), replace(), replicate(), reverse(), rightstr(), rtrim(), regexp_match(), regexp_replace(), replace(), replicate(), reverse(), rightstr(),
sparkline(), spooky_hash(), strfilter(), substr(), trim(), unicode(), upper(), rtrim(), sparkline(), spooky_hash(), strfilter(), substr(), trim(), unicode(),
xpath() upper(), xpath()
Examples Examples
#1 To test if the string 'foobar' starts with 'foo': #1 To test if the string 'foobar' starts with 'foo':
;SELECT startswith('foobar', 'foo') ;SELECT startswith('foobar', 'foo')
@ -2685,11 +2700,11 @@ Parameters
include The characters to include in the result include The characters to include in the result
See Also See Also
char(), charindex(), endswith(), extract(), group_concat(), group_spooky_hash(), char(), charindex(), endswith(), extract(), group_concat(), group_spooky_hash(),
humanize_file_size(), instr(), leftstr(), length(), lower(), ltrim(), padc(), humanize_file_size(), instr(), leftstr(), length(), logfmt2json(), lower(),
padl(), padr(), printf(), proper(), regexp_capture(), regexp_match(), ltrim(), padc(), padl(), padr(), printf(), proper(), regexp_capture(),
regexp_replace(), replace(), replicate(), reverse(), rightstr(), rtrim(), regexp_match(), regexp_replace(), replace(), replicate(), reverse(), rightstr(),
sparkline(), spooky_hash(), startswith(), substr(), trim(), unicode(), upper(), rtrim(), sparkline(), spooky_hash(), startswith(), substr(), trim(), unicode(),
xpath() upper(), xpath()
Example Example
#1 To get the 'b', 'c', and 'd' characters from the string 'abcabc': #1 To get the 'b', 'c', and 'd' characters from the string 'abcabc':
;SELECT strfilter('abcabc', 'bcd') ;SELECT strfilter('abcabc', 'bcd')
@ -2734,11 +2749,11 @@ Parameters
the characters before the start are returned. the characters before the start are returned.
See Also See Also
char(), charindex(), endswith(), extract(), group_concat(), group_spooky_hash(), char(), charindex(), endswith(), extract(), group_concat(), group_spooky_hash(),
humanize_file_size(), instr(), leftstr(), length(), lower(), ltrim(), padc(), humanize_file_size(), instr(), leftstr(), length(), logfmt2json(), lower(),
padl(), padr(), printf(), proper(), regexp_capture(), regexp_match(), ltrim(), padc(), padl(), padr(), printf(), proper(), regexp_capture(),
regexp_replace(), replace(), replicate(), reverse(), rightstr(), rtrim(), regexp_match(), regexp_replace(), replace(), replicate(), reverse(), rightstr(),
sparkline(), spooky_hash(), startswith(), strfilter(), trim(), unicode(), rtrim(), sparkline(), spooky_hash(), startswith(), strfilter(), trim(),
upper(), xpath() unicode(), upper(), xpath()
Examples Examples
#1 To get the substring starting at the second character until the end of the string 'abc' #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. chars The characters to trim. Defaults to spaces.
See Also See Also
char(), charindex(), endswith(), extract(), group_concat(), group_spooky_hash(), char(), charindex(), endswith(), extract(), group_concat(), group_spooky_hash(),
humanize_file_size(), instr(), leftstr(), length(), lower(), ltrim(), padc(), humanize_file_size(), instr(), leftstr(), length(), logfmt2json(), lower(),
padl(), padr(), printf(), proper(), regexp_capture(), regexp_match(), ltrim(), padc(), padl(), padr(), printf(), proper(), regexp_capture(),
regexp_replace(), replace(), replicate(), reverse(), rightstr(), rtrim(), regexp_match(), regexp_replace(), replace(), replicate(), reverse(), rightstr(),
sparkline(), spooky_hash(), startswith(), strfilter(), substr(), unicode(), rtrim(), sparkline(), spooky_hash(), startswith(), strfilter(), substr(),
upper(), xpath() unicode(), upper(), xpath()
Examples Examples
#1 To trim whitespace from the start and end of the string ' abc ': #1 To trim whitespace from the start and end of the string ' abc ':
;SELECT trim(' abc ') ;SELECT trim(' abc ')
@ -2905,11 +2920,11 @@ Parameter
X The string to examine. X The string to examine.
See Also See Also
char(), charindex(), endswith(), extract(), group_concat(), group_spooky_hash(), char(), charindex(), endswith(), extract(), group_concat(), group_spooky_hash(),
humanize_file_size(), instr(), leftstr(), length(), lower(), ltrim(), padc(), humanize_file_size(), instr(), leftstr(), length(), logfmt2json(), lower(),
padl(), padr(), printf(), proper(), regexp_capture(), regexp_match(), ltrim(), padc(), padl(), padr(), printf(), proper(), regexp_capture(),
regexp_replace(), replace(), replicate(), reverse(), rightstr(), rtrim(), regexp_match(), regexp_replace(), replace(), replicate(), reverse(), rightstr(),
sparkline(), spooky_hash(), startswith(), strfilter(), substr(), trim(), rtrim(), sparkline(), spooky_hash(), startswith(), strfilter(), substr(),
upper(), xpath() trim(), upper(), xpath()
Example Example
#1 To get the unicode code point for the first character of 'abc': #1 To get the unicode code point for the first character of 'abc':
;SELECT unicode('abc') ;SELECT unicode('abc')
@ -2929,11 +2944,11 @@ Parameter
str The string to convert. str The string to convert.
See Also See Also
char(), charindex(), endswith(), extract(), group_concat(), group_spooky_hash(), char(), charindex(), endswith(), extract(), group_concat(), group_spooky_hash(),
humanize_file_size(), instr(), leftstr(), length(), lower(), ltrim(), padc(), humanize_file_size(), instr(), leftstr(), length(), logfmt2json(), lower(),
padl(), padr(), printf(), proper(), regexp_capture(), regexp_match(), ltrim(), padc(), padl(), padr(), printf(), proper(), regexp_capture(),
regexp_replace(), replace(), replicate(), reverse(), rightstr(), rtrim(), regexp_match(), regexp_replace(), replace(), replicate(), reverse(), rightstr(),
sparkline(), spooky_hash(), startswith(), strfilter(), substr(), trim(), rtrim(), sparkline(), spooky_hash(), startswith(), strfilter(), substr(),
unicode(), xpath() trim(), unicode(), xpath()
Example Example
#1 To uppercase the string 'aBc': #1 To uppercase the string 'aBc':
;SELECT upper('aBc') ;SELECT upper('aBc')
@ -2953,11 +2968,11 @@ Results
node_text The node's text value. node_text The node's text value.
See Also See Also
char(), charindex(), endswith(), extract(), group_concat(), group_spooky_hash(), char(), charindex(), endswith(), extract(), group_concat(), group_spooky_hash(),
humanize_file_size(), instr(), leftstr(), length(), lower(), ltrim(), padc(), humanize_file_size(), instr(), leftstr(), length(), logfmt2json(), lower(),
padl(), padr(), printf(), proper(), regexp_capture(), regexp_match(), ltrim(), padc(), padl(), padr(), printf(), proper(), regexp_capture(),
regexp_replace(), replace(), replicate(), reverse(), rightstr(), rtrim(), regexp_match(), regexp_replace(), replace(), replicate(), reverse(), rightstr(),
sparkline(), spooky_hash(), startswith(), strfilter(), substr(), trim(), rtrim(), sparkline(), spooky_hash(), startswith(), strfilter(), substr(),
unicode(), upper() trim(), unicode(), upper()
Examples Examples
#1 To select the XML nodes on the path '/abc/def': #1 To select the XML nodes on the path '/abc/def':
;SELECT * FROM xpath('/abc/def', '<abc><def a="b">Hello</def><def>Bye</def></abc>') ;SELECT * FROM xpath('/abc/def', '<abc><def a="b">Hello</def><def>Bye</def></abc>')

@ -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

@ -498,6 +498,26 @@ info 0x0
error 0x0 error 0x0
EOF EOF
run_test ./drive_logfile -t -f logfmt_log ${srcdir}/logfile_logfmt.0
check_output "logfmt_log time interpreted incorrectly?" <<EOF
Sep 15 21:17:10 2021 -- 220
Sep 15 21:17:11 2021 -- 674
Sep 15 21:17:11 2021 -- 678
Sep 15 21:17:11 2021 -- 679
Sep 15 21:18:20 2021 -- 335
EOF
run_test ./drive_logfile -v -f logfmt_log ${srcdir}/logfile_logfmt.0
check_output "logfmt_log level interpreted incorrectly?" <<EOF
error 0x0
warning 0x0
info 0x0
info 0x0
error 0x0
EOF
run_test ${lnav_test} -d /tmp/lnav.err -nt -w logfile_stdin.log <<EOF run_test ${lnav_test} -d /tmp/lnav.err -nt -w logfile_stdin.log <<EOF
Hi Hi
EOF EOF

@ -46,6 +46,55 @@ log_line,log_part,log_time,log_idle_msecs,log_level,log_mark,log_comment,log_tag
EOF EOF
run_test ${lnav_test} -n \
-c ";SELECT fields FROM logfmt_log" \
-c ":write-json-to -" \
${test_dir}/logfile_logfmt.0
check_output "logfmt fields are not handled correctly?" <<EOF
[
{
"fields": {
"namespace": "inc-1-enh-domain-c14-ns-2",
"pod": "hello-inc-1-enh-domain-c14-ns-2-3-d8f465685-k75gp",
"reason": "",
"status": "Pending"
}
},
{
"fields": {
"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"
}
},
{
"fields": {
"namespace": "inc-1-domain-c14-ns-6",
"pod": "fe-inc-1-domain-c14-ns-6-5-656d9bb695-4584b",
"uid": "be2def59-3a08-42fd-8f84-6f64cfcefa93"
}
},
{
"fields": {
"namespace": "inc-1-domain-c14-ns-6",
"pod": "fe-inc-1-domain-c14-ns-6-5-656d9bb695-4584b",
"uid": "be2def59-3a08-42fd-8f84-6f64cfcefa93"
}
},
{
"fields": {
"namespace": "inc-1-enh-domain-c14-ns-2",
"pod": "hello-inc-1-enh-domain-c14-ns-2-7-5ddd6bcd69-6rqct",
"reason": "",
"status": "Pending"
}
}
]
EOF
run_test ${lnav_test} -n \ run_test ${lnav_test} -n \
-c ";SELECT sc_substatus FROM w3c_log" \ -c ";SELECT sc_substatus FROM w3c_log" \
-c ":write-json-to -" \ -c ":write-json-to -" \
@ -715,18 +764,6 @@ log_line,log_part,log_time,log_idle_msecs,log_level,log_mark,log_comment,log_tag
1,<NULL>,2009-07-20 22:59:29.000,3000,error,0,<NULL>,<NULL>,<NULL>,192.168.202.254,GET,-,<NULL>,/vmw/vSphere/default/vmkboot.gz,gPXE/0.9.7,-,HTTP/1.0,46210,404,<NULL> 1,<NULL>,2009-07-20 22:59:29.000,3000,error,0,<NULL>,<NULL>,<NULL>,192.168.202.254,GET,-,<NULL>,/vmw/vSphere/default/vmkboot.gz,gPXE/0.9.7,-,HTTP/1.0,46210,404,<NULL>
EOF 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?" <<EOF
192.168.202.254 - - [20/Jul/2009:22:59:29 +0000] "GET /vmw/vSphere/default/vmkboot.gz HTTP/1.0" 404 46210 "-" "gPXE/0.9.7"
192.168.202.254 - - [20/Jul/2009:22:59:29 +0000] "GET /vmw/vSphere/default/vmkernel.gz HTTP/1.0" 200 78929 "-" "gPXE/0.9.7"
EOF
# XXX The timestamp on the file is used to determine the year for syslog files. # XXX The timestamp on the file is used to determine the year for syslog files.
touch -t 200711030923 ${test_dir}/logfile_syslog.0 touch -t 200711030923 ${test_dir}/logfile_syslog.0

@ -233,6 +233,12 @@ Row 0:
Column result: {"col_0":1} Column result: {"col_0":1}
EOF EOF
run_test ./drive_sql "select logfmt2json('foo=1 bar=2 baz=2e1 msg=hello') as result"
check_output "logfmt2json is not working?" <<EOF
Row 0:
Column result: {"foo":1,"bar":2,"baz":20.0,"msg":"hello"}
EOF
run_test ./drive_sql "SELECT substr('#foo', range_start) AS value FROM regexp_capture('#foo', '(\w+)') WHERE capture_index = 1" run_test ./drive_sql "SELECT substr('#foo', range_start) AS value FROM regexp_capture('#foo', '(\w+)') WHERE capture_index = 1"

Loading…
Cancel
Save