[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.
* Added an "lnav_version()" SQL function that returns the current
version string.
* Added basic support for the logfmt file format. Currently, only files
whose lines are entirely logfmt-encoded are supported. The lines
must also contain either a field named "time" or "ts" that contains
the timestamp.
* Added the "logfmt2json()" SQL function to convert a string containing
a logfmt-encoded message into a JSON object that can be operated on
more easily.
Interface changes:
* The xclip implementation for accessing the system clipboard now writes
to the "clipboard" selection instead of the "primary" selection.
* The 'query' bookmark type and y/Y hotkeys have been removed due to
performance issues and the functionality is probably rarely used.
Bug Fixes:
* The text "send-input" would show up on some terminals instead of
ignoring the escape sequence. This control sequence was only

@ -1,6 +1,6 @@
# aminclude_static.am generated automatically by Autoconf
# from AX_AM_MACROS_STATIC on Fri Oct 1 09:22:49 PDT 2021
# from AX_AM_MACROS_STATIC on Sat Oct 2 23:15:38 PDT 2021
# Code coverage

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

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

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

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

@ -1,7 +1,7 @@
include $(top_srcdir)/aminclude_static.am
SUBDIRS = tools fmtlib pcrepp base tailer pugixml yajl yajlpp .
SUBDIRS = tools fmtlib pcrepp base tailer pugixml yajl yajlpp formats/logfmt .
bin_PROGRAMS = lnav
@ -128,6 +128,7 @@ AM_CPPFLAGS = \
LDADD = \
libdiag.a \
base/libbase.a \
formats/logfmt/liblogfmt.a \
fmtlib/libcppfmt.a \
pcrepp/libpcrepp.a \
pugixml/libpugixml.a \

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

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

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

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

@ -51,8 +51,6 @@ using namespace std;
exec_context INIT_EXEC_CONTEXT;
bookmark_type_t BM_QUERY("query");
static const string MSG_FORMAT_STMT = R"(
SELECT count(*) AS total, min(log_line) AS log_line, log_msg_format
FROM all_logs
@ -345,8 +343,6 @@ Result<string, string> execute_sql(exec_context &ec, const string &sql, string &
retval = ec.ec_accumulator.get_string();
}
else if (!dls.dls_rows.empty()) {
vis_bookmarks &bm = lnav_data.ld_views[LNV_LOG].get_bookmarks();
if (lnav_data.ld_flags & LNF_HEADLESS) {
if (ec.ec_local_vars.size() == 1) {
ensure_view(&lnav_data.ld_views[LNV_DB]);
@ -355,13 +351,6 @@ Result<string, string> execute_sql(exec_context &ec, const string &sql, string &
retval = "";
alt_msg = "";
}
else if (dls.dls_headers.size() == 1 && !bm[&BM_QUERY].empty()) {
retval = "";
alt_msg = HELP_MSG_2(
y, Y,
"to move forward/backward through query results "
"in the log view");
}
else if (dls.dls_rows.size() == 1) {
auto &row = dls.dls_rows[0];
@ -727,7 +716,6 @@ int sql_callback(exec_context &ec, sqlite3_stmt *stmt)
if (!sqlite3_stmt_busy(stmt)) {
dls.clear();
lnav_data.ld_log_source.text_clear_marks(&BM_QUERY);
return 0;
}
@ -774,16 +762,6 @@ int sql_callback(exec_context &ec, sqlite3_stmt *stmt)
break;
}
}
if (value != nullptr &&
(dls.dls_headers[lpc].hm_name == "log_line" ||
strstr(dls.dls_headers[lpc].hm_name.c_str(), "log_line"))) {
int line_number = -1;
if (sscanf(value, "%d", &line_number) == 1) {
lnav_data.ld_views[LNV_LOG].toggle_user_mark(
&BM_QUERY, vis_line_t(line_number));
}
}
}
return retval;

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

@ -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
been created with the 'partition-name' command.
y/Y Move forward/backward through the log view based on the
"log_line" column in the SQL result view.
s/S Move to the next/previous "slow down" in the log message
rate. A slow down is detected by measuring how quickly
the message rate has changed over the previous several

@ -771,7 +771,7 @@ char(*X*)
HI
**See Also**
:ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash_agg`, :ref:`humanize_file_size`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`sparkline`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`upper`, :ref:`xpath`
:ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash_agg`, :ref:`humanize_file_size`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`logfmt2json`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`sparkline`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`upper`, :ref:`xpath`
----
@ -804,7 +804,7 @@ charindex(*needle*, *haystack*, *\[start\]*)
0
**See Also**
:ref:`char`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash_agg`, :ref:`humanize_file_size`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`sparkline`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`upper`, :ref:`xpath`
:ref:`char`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash_agg`, :ref:`humanize_file_size`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`logfmt2json`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`sparkline`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`upper`, :ref:`xpath`
----
@ -1068,7 +1068,7 @@ endswith(*str*, *suffix*)
0
**See Also**
:ref:`char`, :ref:`charindex`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash_agg`, :ref:`humanize_file_size`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`sparkline`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`upper`, :ref:`xpath`
:ref:`char`, :ref:`charindex`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash_agg`, :ref:`humanize_file_size`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`logfmt2json`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`sparkline`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`upper`, :ref:`xpath`
----
@ -1123,7 +1123,7 @@ extract(*str*)
{"col_0":1.0,"col_1":2.0}
**See Also**
:ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`group_concat`, :ref:`group_spooky_hash_agg`, :ref:`humanize_file_size`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`sparkline`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`upper`, :ref:`xpath`
:ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`group_concat`, :ref:`group_spooky_hash_agg`, :ref:`humanize_file_size`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`logfmt2json`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`sparkline`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`upper`, :ref:`xpath`
----
@ -1324,7 +1324,7 @@ group_concat(*X*, *\[sep\]*)
hw,gw
**See Also**
:ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_spooky_hash_agg`, :ref:`humanize_file_size`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`sparkline`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`upper`, :ref:`xpath`
:ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_spooky_hash_agg`, :ref:`humanize_file_size`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`logfmt2json`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`sparkline`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`upper`, :ref:`xpath`
----
@ -1348,7 +1348,7 @@ group_spooky_hash(*str*)
4e7a190aead058cb123c94290f29c34a
**See Also**
:ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`humanize_file_size`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`sparkline`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`upper`, :ref:`xpath`
:ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`humanize_file_size`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`logfmt2json`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`sparkline`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`upper`, :ref:`xpath`
----
@ -1394,7 +1394,7 @@ humanize_file_size(*value*)
10.0MB
**See Also**
:ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash_agg`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`sparkline`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`upper`, :ref:`xpath`
:ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash_agg`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`logfmt2json`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`sparkline`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`upper`, :ref:`xpath`
----
@ -1442,7 +1442,7 @@ instr(*haystack*, *needle*)
2
**See Also**
:ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash_agg`, :ref:`humanize_file_size`, :ref:`leftstr`, :ref:`length`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`sparkline`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`upper`, :ref:`xpath`
:ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash_agg`, :ref:`humanize_file_size`, :ref:`leftstr`, :ref:`length`, :ref:`logfmt2json`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`sparkline`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`upper`, :ref:`xpath`
----
@ -1795,7 +1795,7 @@ leftstr(*str*, *N*)
abc
**See Also**
:ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash_agg`, :ref:`humanize_file_size`, :ref:`instr`, :ref:`length`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`sparkline`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`upper`, :ref:`xpath`
:ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash_agg`, :ref:`humanize_file_size`, :ref:`instr`, :ref:`length`, :ref:`logfmt2json`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`sparkline`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`upper`, :ref:`xpath`
----
@ -1819,7 +1819,7 @@ length(*str*)
3
**See Also**
:ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash_agg`, :ref:`humanize_file_size`, :ref:`instr`, :ref:`leftstr`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`sparkline`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`upper`, :ref:`xpath`
:ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash_agg`, :ref:`humanize_file_size`, :ref:`instr`, :ref:`leftstr`, :ref:`logfmt2json`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`sparkline`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`upper`, :ref:`xpath`
----
@ -1990,6 +1990,30 @@ log_top_line()
----
.. _logfmt2json:
logfmt2json(*str*)
^^^^^^^^^^^^^^^^^^
Convert a logfmt-encoded string into JSON
**Parameters**
* **str\*** --- The logfmt message to parse
**Examples**
To extract key/value pairs from a log message:
.. code-block:: custsqlite
;SELECT logfmt2json('foo=1 bar=2 name="Rolo Tomassi"')
{"foo":1,"bar":2,"name":"Rolo Tomassi"}
**See Also**
:ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash_agg`, :ref:`humanize_file_size`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`sparkline`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`upper`, :ref:`xpath`
----
.. _lower:
lower(*str*)
@ -2009,7 +2033,7 @@ lower(*str*)
abc
**See Also**
:ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash_agg`, :ref:`humanize_file_size`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`sparkline`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`upper`, :ref:`xpath`
:ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash_agg`, :ref:`humanize_file_size`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`logfmt2json`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`sparkline`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`upper`, :ref:`xpath`
----
@ -2041,7 +2065,7 @@ ltrim(*str*, *\[chars\]*)
c
**See Also**
:ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash_agg`, :ref:`humanize_file_size`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`lower`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`sparkline`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`upper`, :ref:`xpath`
:ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash_agg`, :ref:`humanize_file_size`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`logfmt2json`, :ref:`lower`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`sparkline`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`upper`, :ref:`xpath`
----
@ -2198,7 +2222,7 @@ padc(*str*, *len*)
abcdef ghi
**See Also**
:ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash_agg`, :ref:`humanize_file_size`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`lower`, :ref:`ltrim`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`sparkline`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`upper`, :ref:`xpath`
:ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash_agg`, :ref:`humanize_file_size`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`logfmt2json`, :ref:`lower`, :ref:`ltrim`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`sparkline`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`upper`, :ref:`xpath`
----
@ -2230,7 +2254,7 @@ padl(*str*, *len*)
abcdef
**See Also**
:ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash_agg`, :ref:`humanize_file_size`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`sparkline`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`upper`, :ref:`xpath`
:ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash_agg`, :ref:`humanize_file_size`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`logfmt2json`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`sparkline`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`upper`, :ref:`xpath`
----
@ -2262,7 +2286,7 @@ padr(*str*, *len*)
abcdefghi
**See Also**
:ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash_agg`, :ref:`humanize_file_size`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`sparkline`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`upper`, :ref:`xpath`
:ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash_agg`, :ref:`humanize_file_size`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`logfmt2json`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`sparkline`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`upper`, :ref:`xpath`
----
@ -2360,7 +2384,7 @@ printf(*format*, *X*)
value: 00011
**See Also**
:ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash_agg`, :ref:`humanize_file_size`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`sparkline`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`upper`, :ref:`xpath`
:ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash_agg`, :ref:`humanize_file_size`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`logfmt2json`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`sparkline`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`upper`, :ref:`xpath`
----
@ -2384,7 +2408,7 @@ proper(*str*)
Hello, World!
**See Also**
:ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash_agg`, :ref:`humanize_file_size`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`sparkline`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`upper`, :ref:`xpath`
:ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash_agg`, :ref:`humanize_file_size`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`logfmt2json`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`sparkline`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`upper`, :ref:`xpath`
----
@ -2567,7 +2591,7 @@ regexp_capture(*string*, *pattern*)
1 2 3 8 9 2
**See Also**
:ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash_agg`, :ref:`humanize_file_size`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`sparkline`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`upper`, :ref:`xpath`
:ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash_agg`, :ref:`humanize_file_size`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`logfmt2json`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`sparkline`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`upper`, :ref:`xpath`
----
@ -2606,7 +2630,7 @@ regexp_match(*re*, *str*)
{"num":123,"str":"four"}
**See Also**
:ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash_agg`, :ref:`humanize_file_size`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_replace`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`sparkline`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`upper`, :ref:`xpath`
:ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash_agg`, :ref:`humanize_file_size`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`logfmt2json`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_replace`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`sparkline`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`upper`, :ref:`xpath`
----
@ -2639,7 +2663,7 @@ regexp_replace(*str*, *re*, *repl*)
<123> <abc>
**See Also**
:ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash_agg`, :ref:`humanize_file_size`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_match`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`sparkline`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`upper`, :ref:`xpath`
:ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash_agg`, :ref:`humanize_file_size`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`logfmt2json`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_match`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`sparkline`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`upper`, :ref:`xpath`
----
@ -2672,7 +2696,7 @@ replace(*str*, *old*, *replacement*)
zbc
**See Also**
:ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash_agg`, :ref:`humanize_file_size`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`sparkline`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`upper`, :ref:`xpath`
:ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash_agg`, :ref:`humanize_file_size`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`logfmt2json`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`sparkline`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`upper`, :ref:`xpath`
----
@ -2697,7 +2721,7 @@ replicate(*str*, *N*)
abcabcabc
**See Also**
:ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash_agg`, :ref:`humanize_file_size`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`sparkline`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`upper`, :ref:`xpath`
:ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash_agg`, :ref:`humanize_file_size`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`logfmt2json`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`sparkline`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`upper`, :ref:`xpath`
----
@ -2721,7 +2745,7 @@ reverse(*str*)
cba
**See Also**
:ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash_agg`, :ref:`humanize_file_size`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`rightstr`, :ref:`rtrim`, :ref:`sparkline`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`upper`, :ref:`xpath`
:ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash_agg`, :ref:`humanize_file_size`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`logfmt2json`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`rightstr`, :ref:`rtrim`, :ref:`sparkline`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`upper`, :ref:`xpath`
----
@ -2753,7 +2777,7 @@ rightstr(*str*, *N*)
abc
**See Also**
:ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash_agg`, :ref:`humanize_file_size`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rtrim`, :ref:`sparkline`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`upper`, :ref:`xpath`
:ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash_agg`, :ref:`humanize_file_size`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`logfmt2json`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rtrim`, :ref:`sparkline`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`upper`, :ref:`xpath`
----
@ -2849,7 +2873,7 @@ rtrim(*str*, *\[chars\]*)
a
**See Also**
:ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash_agg`, :ref:`humanize_file_size`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`sparkline`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`upper`, :ref:`xpath`
:ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash_agg`, :ref:`humanize_file_size`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`logfmt2json`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`sparkline`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`upper`, :ref:`xpath`
----
@ -2919,7 +2943,7 @@ sparkline(*value*, *\[upper\]*)
▁▂▃▄▅▆▇█
**See Also**
:ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash_agg`, :ref:`humanize_file_size`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`upper`, :ref:`xpath`
:ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash_agg`, :ref:`humanize_file_size`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`logfmt2json`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`upper`, :ref:`xpath`
----
@ -2964,7 +2988,7 @@ spooky_hash(*str*)
f96b3d9c1a19f4394c97a1b79b1880df
**See Also**
:ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash_agg`, :ref:`humanize_file_size`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`sparkline`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`upper`, :ref:`xpath`
:ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash_agg`, :ref:`humanize_file_size`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`logfmt2json`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`sparkline`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`upper`, :ref:`xpath`
----
@ -3078,7 +3102,7 @@ startswith(*str*, *prefix*)
0
**See Also**
:ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash_agg`, :ref:`humanize_file_size`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`sparkline`, :ref:`spooky_hash`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`upper`, :ref:`xpath`
:ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash_agg`, :ref:`humanize_file_size`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`logfmt2json`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`sparkline`, :ref:`spooky_hash`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`upper`, :ref:`xpath`
----
@ -3103,7 +3127,7 @@ strfilter(*source*, *include*)
bcbc
**See Also**
:ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash_agg`, :ref:`humanize_file_size`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`sparkline`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`upper`, :ref:`xpath`
:ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash_agg`, :ref:`humanize_file_size`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`logfmt2json`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`sparkline`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`upper`, :ref:`xpath`
----
@ -3190,7 +3214,7 @@ substr(*str*, *start*, *\[size\]*)
b
**See Also**
:ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash_agg`, :ref:`humanize_file_size`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`sparkline`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`trim`, :ref:`unicode`, :ref:`upper`, :ref:`xpath`
:ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash_agg`, :ref:`humanize_file_size`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`logfmt2json`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`sparkline`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`trim`, :ref:`unicode`, :ref:`upper`, :ref:`xpath`
----
@ -3396,7 +3420,7 @@ trim(*str*, *\[chars\]*)
abc
**See Also**
:ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash_agg`, :ref:`humanize_file_size`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`sparkline`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`unicode`, :ref:`upper`, :ref:`xpath`
:ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash_agg`, :ref:`humanize_file_size`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`logfmt2json`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`sparkline`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`unicode`, :ref:`upper`, :ref:`xpath`
----
@ -3449,7 +3473,7 @@ unicode(*X*)
97
**See Also**
:ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash_agg`, :ref:`humanize_file_size`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`sparkline`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`upper`, :ref:`xpath`
:ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash_agg`, :ref:`humanize_file_size`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`logfmt2json`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`sparkline`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`upper`, :ref:`xpath`
----
@ -3487,7 +3511,7 @@ upper(*str*)
ABC
**See Also**
:ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash_agg`, :ref:`humanize_file_size`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`sparkline`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`xpath`
:ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash_agg`, :ref:`humanize_file_size`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`logfmt2json`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`sparkline`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`xpath`
----
@ -3530,7 +3554,7 @@ xpath(*xpath*, *xmldoc*)
Hello ★ /abc/def/text() {} Hello ★
**See Also**
:ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash_agg`, :ref:`humanize_file_size`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`sparkline`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`upper`
:ref:`char`, :ref:`charindex`, :ref:`endswith`, :ref:`extract`, :ref:`group_concat`, :ref:`group_spooky_hash_agg`, :ref:`humanize_file_size`, :ref:`instr`, :ref:`leftstr`, :ref:`length`, :ref:`logfmt2json`, :ref:`lower`, :ref:`ltrim`, :ref:`padc`, :ref:`padl`, :ref:`padr`, :ref:`printf`, :ref:`proper`, :ref:`regexp_capture`, :ref:`regexp_match`, :ref:`regexp_replace`, :ref:`replace`, :ref:`replicate`, :ref:`reverse`, :ref:`rightstr`, :ref:`rtrim`, :ref:`sparkline`, :ref:`spooky_hash`, :ref:`startswith`, :ref:`strfilter`, :ref:`substr`, :ref:`trim`, :ref:`unicode`, :ref:`upper`
----

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

@ -2231,6 +2231,16 @@ int main(int argc, char *argv[])
lnav_log_file = make_optional_from_nullable(fopen(lnav_data.ld_debug_log_name, "a"));
log_info("lnav started");
{
static auto builtin_formats =
injector::get<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);
if (!config_errors.empty()) {
print_errors(config_errors);

@ -65,37 +65,6 @@ bool change_to_parent_dir()
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 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);
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) {
auto iter = std::find_if(pollfds.begin(), pollfds.end(), [fd](const auto& entry) {
return entry.fd == fd;

@ -306,18 +306,6 @@ public:
*/
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) {
auto& fmts = get_root_formats();
for (auto& lf : fmts) {

@ -41,6 +41,9 @@
#include "log_format.hh"
#include "log_vtab_impl.hh"
#include "base/opt_util.hh"
#include "base/injector.bind.hh"
#include "yajlpp/yajlpp.hh"
#include "formats/logfmt/logfmt.parser.hh"
using namespace std;
@ -688,7 +691,7 @@ public:
}
void get_columns(vector<vtab_column> &cols) const {
void get_columns(vector<vtab_column> &cols) const override {
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);
@ -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);
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"},
};
log_format::register_root_format<bro_log_format> bro_log_instance;
log_format::register_root_format<w3c_log_format> w3c_log_instance;
log_format::register_root_format<generic_log_format> generic_log_instance;
struct logfmt_pair_handler {
explicit logfmt_pair_handler(date_time_scanner &dts) : lph_dt_scanner(dts)
{
}
bool process_value(const string_fragment& value_frag) {
if (this->lph_key_frag == "time" ||
this->lph_key_frag == "ts") {
if (!this->lph_dt_scanner.scan(value_frag.data(),
value_frag.length(),
nullptr,
&this->lph_time_tm,
this->lph_tv)) {
return false;
}
this->lph_found_time = true;
} else if (this->lph_key_frag == "level") {
this->lph_level = string2level(value_frag.data(), value_frag.length());
}
return true;
}
date_time_scanner &lph_dt_scanner;
bool lph_found_time{false};
struct exttm lph_time_tm{};
struct timeval lph_tv{0, 0};
log_level_t lph_level{log_level_t::LEVEL_INFO};
string_fragment lph_key_frag{""};
};
class logfmt_format : public log_format {
public:
const intern_string_t get_name() const override
{
const static auto NAME = intern_string::lookup("logfmt_log");
return NAME;
}
class logfmt_log_table : public log_format_vtab_impl {
public:
logfmt_log_table(const log_format &format) : log_format_vtab_impl(format) {}
void get_columns(vector<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));
search_hit = (next != -1 && next <= end);
next = bm[&BM_QUERY].next(vis_line_t(start));
search_hit = search_hit || (next != -1 && next <= end);
next = bm[&textview_curses::BM_USER].next(vis_line_t(start));
if (next == -1) {
next = bm[&textview_curses::BM_META].next(vis_line_t(start));

@ -33,6 +33,7 @@
#include "base/string_util.hh"
#include "sql_util.hh"
#include "log_vtab_impl.hh"
#include "yajlpp/json_op.hh"
#include "yajlpp/yajlpp_def.hh"
#include "vtab_module.hh"
@ -624,6 +625,27 @@ static int vt_column(sqlite3_vtab_cursor *cur, sqlite3_context *ctx, int col)
case value_kind_t::VALUE_FLOAT:
root.gen(lv_struct.lv_value.d);
break;
case value_kind_t::VALUE_JSON: {
auto_mem<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:
root.gen(lv_struct.to_string());
break;

@ -19,7 +19,9 @@
#include "base/humanize.hh"
#include "base/string_util.hh"
#include "formats/logfmt/logfmt.parser.hh"
#include "yajlpp/yajlpp.hh"
#include "yajlpp/json_op.hh"
#include "column_namer.hh"
#include "yajl/api/yajl_gen.h"
#include "sqlite-extension-func.hh"
@ -183,6 +185,70 @@ json_string extract(const char *str)
return json_string(gen);
}
json_string logfmt2json(string_fragment line)
{
logfmt::parser p(line);
yajlpp_gen gen;
yajl_gen_config(gen, yajl_gen_beautify, false);
{
yajlpp_map root(gen);
bool done = false;
while (!done) {
auto pair = p.step();
done = pair.match(
[](const logfmt::parser::end_of_input& eoi) {
return true;
},
[&root, &gen](const logfmt::parser::kvpair& kvp) {
root.gen(kvp.first);
kvp.second.match(
[&root](const logfmt::parser::bool_value& bv) {
root.gen(bv.bv_value);
},
[&root](const logfmt::parser::int_value& iv) {
root.gen(iv.iv_value);
},
[&root](const logfmt::parser::float_value& fv) {
root.gen(fv.fv_value);
},
[&root, &gen](const logfmt::parser::quoted_value& qv) {
auto_mem<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
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(
static_cast<bool (*)(const char *, const char *)>(&startswith)),
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<>
struct from_sqlite<std::string> {
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());
};
yajl_gen_status operator()(const string_fragment &str)
{
return yajl_gen_string(this->yg_handle, (const unsigned char *)str.data(), str.length());
};
yajl_gen_status operator()(bool value)
{
return yajl_gen_bool(this->yg_handle, value);

@ -58,7 +58,7 @@ target_link_libraries(test_reltime diag PkgConfig::libpcre)
add_test(NAME test_reltime COMMAND test_reltime)
add_executable(test_top_status test_top_status.cc)
target_link_libraries(test_top_status diag testdummy PkgConfig::libpcre)
target_link_libraries(test_top_status diag testdummy PkgConfig::libpcre logfmt)
add_test(NAME test_top_status COMMAND test_top_status)
add_executable(drive_view_colors drive_view_colors.cc)
@ -74,7 +74,7 @@ add_executable(drive_sql_anno drive_sql_anno.cc)
target_link_libraries(drive_sql_anno diag testdummy PkgConfig::libpcre)
add_executable(drive_data_scanner drive_data_scanner.cc)
target_link_libraries(drive_data_scanner diag testdummy PkgConfig::libpcre)
target_link_libraries(drive_data_scanner diag testdummy PkgConfig::libpcre logfmt)
add_executable(scripty scripty.cc)
target_link_libraries(scripty diag PkgConfig::ncursesw)

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

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

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

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

@ -46,6 +46,55 @@ log_line,log_part,log_time,log_idle_msecs,log_level,log_mark,log_comment,log_tag
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 \
-c ";SELECT sc_substatus FROM w3c_log" \
-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>
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.
touch -t 200711030923 ${test_dir}/logfile_syslog.0

@ -233,6 +233,12 @@ Row 0:
Column result: {"col_0":1}
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"

Loading…
Cancel
Save