[cmds] add command for setting a default time zone

pull/1205/head
Tim Stack 8 months ago
parent 731066a644
commit eacbaa9d4f

@ -269,6 +269,12 @@ add_library(
)
target_include_directories(cppfmt PUBLIC fmtlib)
add_library(
datepp STATIC
third-party/date/src/tz.cpp
)
target_include_directories(datepp PUBLIC third-party/date/include)
add_library(
cppscnlib STATIC
third-party/scnlib/src/reader_float.cpp
@ -389,6 +395,7 @@ add_library(
file_collection.cc
file_converter_manager.cc
file_format.cc
file_options.cc
file_vtab.cc
files_sub_source.cc
filter_observer.cc
@ -503,6 +510,7 @@ add_library(
file_collection.hh
file_converter_manager.hh
file_format.hh
file_options.hh
files_sub_source.hh
filter_observer.hh
filter_status_source.hh
@ -665,6 +673,7 @@ target_include_directories(diag PUBLIC . fmtlib ${CMAKE_CURRENT_BINARY_DIR}
target_link_libraries(
diag
base
datepp
lnavdt
lnavfileio
pcrepp
@ -676,7 +685,8 @@ target_link_libraries(
cppfmt
base64
spookyhash
${lnav_LIBS})
${lnav_LIBS}
)
target_compile_definitions(diag PRIVATE SQLITE_OMIT_LOAD_EXTENSION)
check_library_exists(util openpty "" HAVE_LIBUTIL)

@ -210,6 +210,7 @@ noinst_HEADERS = \
file_collection.hh \
file_converter_manager.hh \
file_format.hh \
file_options.hh \
file_vtab.cfg.hh \
files_sub_source.hh \
filter_observer.hh \
@ -407,6 +408,7 @@ libdiag_a_SOURCES = \
file_collection.cc \
file_converter_manager.cc \
file_format.cc \
file_options.cc \
files_sub_source.cc \
filter_observer.cc \
filter_status_source.cc \

@ -73,7 +73,7 @@ add_library(
target_include_directories(base PUBLIC . .. ../third-party
${CMAKE_CURRENT_BINARY_DIR}/..)
target_link_libraries(base cppfmt cppscnlib pcrepp ncurses::libcurses pthread lnavdt)
target_link_libraries(base cppfmt cppscnlib pcrepp ncurses::libcurses pthread lnavdt datepp)
add_executable(
test_base

@ -7,6 +7,7 @@ AM_CPPFLAGS = \
-I$(top_srcdir)/src/ \
-I$(top_srcdir)/src/third-party \
-I$(top_srcdir)/src/fmtlib \
-I$(top_srcdir)/src/third-party/date/include \
-I$(top_srcdir)/src/third-party/scnlib/include \
$(LIBARCHIVE_CFLAGS) \
$(READLINE_CFLAGS) \

@ -157,11 +157,24 @@ date_time_scanner::scan(const char* time_dest,
if (convert_local
&& (this->dts_local_time
|| tm_out->et_flags & ETF_EPOCH_TIME
|| (tm_out->et_flags & ETF_ZONE_SET
|| ((tm_out->et_flags & ETF_ZONE_SET
|| this->dts_default_zone != nullptr)
&& this->dts_zoned_to_local)))
{
time_t gmt = tm_out->to_timeval().tv_sec;
if (!(tm_out->et_flags & ETF_ZONE_SET)
&& !(tm_out->et_flags & ETF_EPOCH_TIME)
&& this->dts_default_zone != nullptr)
{
date::local_seconds stime;
stime += std::chrono::seconds{gmt};
auto ztime
= date::make_zoned(this->dts_default_zone, stime);
gmt = std::chrono::duration_cast<std::chrono::seconds>(
ztime.get_sys_time().time_since_epoch())
.count();
}
this->to_localtime(gmt, *tm_out);
}
const auto& last_tm = this->dts_last_tm.et_tm;
@ -301,8 +314,6 @@ date_time_scanner::to_localtime(time_t t, exttm& tm_out)
if (t < this->dts_local_offset_valid || t >= this->dts_local_offset_expiry)
{
time_t new_gmt;
localtime_r(&t, &tm_out.et_tm);
// Clear the gmtoff set by localtime_r() otherwise tm2sec() will
// convert the time back again.
@ -311,8 +322,7 @@ date_time_scanner::to_localtime(time_t t, exttm& tm_out)
tm_out.et_tm.tm_zone = nullptr;
#endif
tm_out.et_tm.tm_isdst = 0;
new_gmt = tm2sec(&tm_out.et_tm);
auto new_gmt = tm2sec(&tm_out.et_tm);
this->dts_local_offset_cache = new_gmt - t;
this->dts_local_offset_valid = t;
this->dts_local_offset_expiry = t + (EXPIRE_TIME - 1);

@ -37,6 +37,7 @@
#include <sys/types.h>
#include "date/tz.h"
#include "time_util.hh"
/**
@ -105,6 +106,7 @@ struct date_time_scanner {
time_t dts_local_offset_cache{0};
time_t dts_local_offset_valid{0};
time_t dts_local_offset_expiry{0};
const date::time_zone* dts_default_zone{nullptr};
static const int EXPIRE_TIME = 15 * 60;

@ -31,6 +31,8 @@
#define lnav_itertools_hh
#include <algorithm>
#include <deque>
#include <map>
#include <memory>
#include <set>
#include <type_traits>
@ -138,8 +140,16 @@ struct max_with_init {
struct sum {};
struct to_vector {};
} // namespace details
inline details::to_vector
to_vector()
{
return details::to_vector{};
}
template<typename T>
inline details::unwrap_or<T>
unwrap_or(T value)
@ -619,12 +629,70 @@ operator|(nonstd::optional<T> in,
template<typename T, typename F>
auto
operator|(const T& in, const lnav::itertools::details::mapper<F>& mapper)
-> std::vector<std::remove_const_t<std::remove_reference_t<
decltype(mapper.m_func(std::declval<typename T::value_type>()))>>>
operator|(const std::set<T>& in,
const lnav::itertools::details::mapper<F>& mapper)
-> std::set<std::remove_const_t<
std::remove_reference_t<decltype(mapper.m_func(std::declval<T>()))>>>
{
using return_type = std::vector<std::remove_const_t<std::remove_reference_t<
decltype(mapper.m_func(std::declval<typename T::value_type>()))>>>;
using return_type = std::set<std::remove_const_t<
std::remove_reference_t<decltype(mapper.m_func(std::declval<T>()))>>>;
return_type retval;
std::transform(in.begin(),
in.end(),
std::inserter(retval, retval.begin()),
mapper.m_func);
return retval;
}
template<typename T, typename F>
auto
operator|(const std::vector<T>& in,
const lnav::itertools::details::mapper<F>& mapper)
-> std::vector<std::remove_const_t<
std::remove_reference_t<decltype(mapper.m_func(std::declval<T>()))>>>
{
using return_type = std::vector<std::remove_const_t<
std::remove_reference_t<decltype(mapper.m_func(std::declval<T>()))>>>;
return_type retval;
retval.reserve(in.size());
std::transform(
in.begin(), in.end(), std::back_inserter(retval), mapper.m_func);
return retval;
}
template<typename T, typename F>
auto
operator|(const std::deque<T>& in,
const lnav::itertools::details::mapper<F>& mapper)
-> std::vector<std::remove_const_t<
std::remove_reference_t<decltype(mapper.m_func(std::declval<T>()))>>>
{
using return_type = std::vector<std::remove_const_t<
std::remove_reference_t<decltype(mapper.m_func(std::declval<T>()))>>>;
return_type retval;
retval.reserve(in.size());
std::transform(
in.begin(), in.end(), std::back_inserter(retval), mapper.m_func);
return retval;
}
template<typename K, typename V, typename F>
auto
operator|(const std::map<K, V>& in,
const lnav::itertools::details::mapper<F>& mapper)
-> std::vector<
std::remove_const_t<std::remove_reference_t<decltype(mapper.m_func(
std::declval<typename std::map<K, V>::value_type>()))>>>
{
using return_type = std::vector<
std::remove_const_t<std::remove_reference_t<decltype(mapper.m_func(
std::declval<typename std::map<K, V>::value_type>()))>>>;
return_type retval;
retval.reserve(in.size());
@ -782,4 +850,16 @@ operator|(nonstd::optional<T> in,
return in.value_or(unwrapper.uo_value);
}
template<typename T>
std::vector<T>
operator|(std::set<T>&& in, lnav::itertools::details::to_vector tv)
{
std::vector<T> retval;
retval.reserve(in.size());
std::copy(in.begin(), in.end(), std::back_inserter(retval));
return retval;
}
#endif

@ -0,0 +1,105 @@
/**
* Copyright (c) 2023, 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.
*/
#include "file_options.hh"
#include <fnmatch.h>
#include "base/lnav_log.hh"
#include "yajlpp/yajlpp.hh"
#include "yajlpp/yajlpp_def.hh"
namespace lnav {
static const typed_json_path_container<file_options> options_handlers = {
yajlpp::property_handler("default-zone")
.with_synopsis("<zone>")
.with_description("The default zone")
.with_example("America/Los_Angeles"),
};
static const typed_json_path_container<file_options_collection>
collection_handlers = {
yajlpp::pattern_property_handler("(.*)")
.with_description("Path pattern")
.with_children(options_handlers),
};
bool
file_options::operator==(const lnav::file_options& rhs) const
{
return this->fo_default_zone == rhs.fo_default_zone;
}
nonstd::optional<file_options>
file_options_collection::match(const std::string& path) const
{
auto iter = this->foc_pattern_to_options.find(path);
if (iter != this->foc_pattern_to_options.end()) {
return iter->second;
}
for (const auto& pair : this->foc_pattern_to_options) {
auto rc = fnmatch(pair.first.c_str(), path.c_str(), FNM_PATHNAME);
if (rc == 0) {
return pair.second;
}
if (rc != FNM_NOMATCH) {
log_error("fnmatch('%s', '%s') failed -- %s",
pair.first.c_str(),
path.c_str(),
strerror(errno));
}
}
return nonstd::nullopt;
}
nonstd::optional<file_options>
file_options_hier::match(const ghc::filesystem::path& path) const
{
auto lookup_path = path.parent_path();
while (true) {
const auto iter = this->foh_path_to_collection.find(lookup_path);
if (iter != this->foh_path_to_collection.end()) {
return iter->second.match(path.string());
}
auto next_lookup_path = lookup_path.parent_path();
if (lookup_path == next_lookup_path) {
break;
}
lookup_path = next_lookup_path;
}
return nonstd::nullopt;
}
} // namespace lnav

@ -0,0 +1,70 @@
/**
* Copyright (c) 2023, 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.
*/
#ifndef lnav_file_options_hh
#define lnav_file_options_hh
#include <map>
#include "base/lnav.console.hh"
#include "base/result.h"
#include "date/tz.h"
#include "ghc/filesystem.hpp"
#include "mapbox/variant.hpp"
#include "safe/safe.h"
namespace lnav {
struct file_options {
intern_string_t fo_default_zone_name;
const date::time_zone* fo_default_zone{nullptr};
bool operator==(const file_options& rhs) const;
};
struct file_options_collection {
std::map<std::string, file_options> foc_pattern_to_options;
nonstd::optional<file_options> match(const std::string& path) const;
};
struct file_options_hier {
std::map<ghc::filesystem::path, file_options_collection>
foh_path_to_collection;
size_t foh_generation{0};
nonstd::optional<file_options> match(
const ghc::filesystem::path& path) const;
};
using safe_file_options_hier = safe::Safe<file_options_hier>;
} // namespace lnav
#endif

@ -1200,6 +1200,21 @@
----
.. _set_file_timezone:
:set-file-timezone *zone* *pattern*
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Set the timezone to use for log messages that do not include a timezone. The timezone is applied to the focused file or the given glob pattern.
**Parameters**
* **zone\*** --- The timezone name
* **pattern\*** --- The glob pattern to match against files that should use this timezone
----
.. _set_min_log_level:
:set-min-log-level *log-level*

@ -87,6 +87,7 @@
#include "dump_internals.hh"
#include "environ_vtab.hh"
#include "file_converter_manager.hh"
#include "file_options.hh"
#include "filter_sub_source.hh"
#include "fstat_vtab.hh"
#include "gantt_source.hh"
@ -248,6 +249,9 @@ static auto bound_tailer
static auto bound_main = injector::bind_multiple<static_service>()
.add_singleton<main_looper, services::main_t>();
static auto bound_file_options_hier
= injector::bind<lnav::safe_file_options_hier>::to_singleton();
namespace injector {
template<>
void
@ -1834,7 +1838,7 @@ looper()
case ln_mode_t::PAGING:
case ln_mode_t::FILTER:
case ln_mode_t::FILES:
next_rescan_time = next_status_update_time + 1s;
next_rescan_time = next_status_update_time;
next_rebuild_time = next_rescan_time;
break;
default:
@ -1953,7 +1957,8 @@ looper()
lnav::session::restore_view_states();
if (lnav_data.ld_mode == ln_mode_t::FILES) {
if (lnav_data.ld_log_source.text_line_count() == 0
&& lnav_data.ld_text_source.text_line_count() > 0)
&& lnav_data.ld_text_source.text_line_count() > 0
&& lnav_data.ld_view_stack.size() == 1)
{
log_debug("no logs, just text...");
ensure_view(&lnav_data.ld_views[LNV_TEXT]);
@ -2197,6 +2202,28 @@ main(int argc, char* argv[])
setenv("LNAV_HOME_DIR", lnav::paths::dotlnav().c_str(), 1);
setenv("LNAV_WORK_DIR", lnav::paths::workdir().c_str(), 1);
{
auto& safe_options_hier
= injector::get<lnav::safe_file_options_hier&>();
safe::WriteAccess<lnav::safe_file_options_hier> options_hier(
safe_options_hier);
options_hier->foh_generation += 1;
auto_mem<char> var_path;
var_path = realpath("/var/log", nullptr);
auto curr_tz = date::get_tzdb().current_zone();
auto options_coll = lnav::file_options_collection{};
options_coll.foc_pattern_to_options[fmt::format(FMT_STRING("{}/*"),
var_path.in())]
= lnav::file_options{
intern_string::lookup(curr_tz->name()),
curr_tz,
};
options_hier->foh_path_to_collection.emplace(ghc::filesystem::path("/"),
options_coll);
}
lnav_data.ld_exec_context.ec_sql_callback = sql_callback;
lnav_data.ld_exec_context.ec_pipe_callback = pipe_callback;
@ -2544,15 +2571,20 @@ SELECT tbl_name FROM sqlite_master WHERE sql LIKE 'CREATE VIRTUAL TABLE%'
= attr_line_t("the ")
.append("-i"_symbol)
.append(
" option expects one or more log format definition "
"files to install in your lnav configuration "
" option expects one or more log format "
"definition "
"files to install in your lnav "
"configuration "
"directory");
const auto install_help
= attr_line_t(
"log format definitions are JSON files that tell lnav "
"log format definitions are JSON files that "
"tell lnav "
"how to understand log files\n")
.append(
"See: https://docs.lnav.org/en/latest/formats.html");
"See: "
"https://docs.lnav.org/en/latest/"
"formats.html");
lnav::console::print(stderr,
lnav::console::user_message::error(
@ -2675,7 +2707,8 @@ SELECT tbl_name FROM sqlite_master WHERE sql LIKE 'CREATE VIRTUAL TABLE%'
written = write(out_fd, buffer, rc);
if (written == -1) {
fprintf(stderr,
"error: unable to install file -- %s\n",
"error: unable to install file "
"-- %s\n",
strerror(errno));
exit(EXIT_FAILURE);
}
@ -2704,9 +2737,9 @@ SELECT tbl_name FROM sqlite_master WHERE sql LIKE 'CREATE VIRTUAL TABLE%'
}
}
/* If we statically linked against an ncurses library that had a non-
* standard path to the terminfo database, we need to set this variable
* so that it will try the default path.
/* If we statically linked against an ncurses library that had a
* non- standard path to the terminfo database, we need to set this
* variable so that it will try the default path.
*/
setenv("TERMINFO_DIRS",
"/usr/share/terminfo:/lib/terminfo:/usr/share/lib/terminfo",
@ -2935,7 +2968,8 @@ SELECT tbl_name FROM sqlite_master WHERE sql LIKE 'CREATE VIRTUAL TABLE%'
file_loc = vis_line_t(scan_res.value());
} else {
log_warning(
"failed to parse line number from file path with colon: %s",
"failed to parse line number from file path "
"with colon: %s",
file_path.c_str());
}
}
@ -3073,7 +3107,8 @@ SELECT tbl_name FROM sqlite_master WHERE sql LIKE 'CREATE VIRTUAL TABLE%'
std::string partial_line(sbr.get_data(), partial_len);
fprintf(stderr,
"error:%s:%ld:line did not match format %s\n",
"error:%s:%ld:line did not match format "
"%s\n",
lf->get_filename().c_str(),
line_number,
fmt->get_pattern_path(line_number).c_str());
@ -3151,8 +3186,8 @@ SELECT tbl_name FROM sqlite_master WHERE sql LIKE 'CREATE VIRTUAL TABLE%'
}
}
} else if (S_ISREG(stdin_st.st_mode)) {
// The shell connected a file directly, just open it up and add it
// in here.
// The shell connected a file directly, just open it up
// and add it in here.
auto loo = logfile_open_options{}
.with_filename(STDIN_NAME)
.with_include_in_session(false);
@ -3190,11 +3225,10 @@ SELECT tbl_name FROM sqlite_master WHERE sql LIKE 'CREATE VIRTUAL TABLE%'
stderr,
lnav::console::user_message::error("nothing to do")
.with_reason("no files given or default files found")
.with_help(
attr_line_t("use the ")
.append_quoted(lnav::roles::keyword("-N"))
.append(
" option to open lnav without loading any files")));
.with_help(attr_line_t("use the ")
.append_quoted(lnav::roles::keyword("-N"))
.append(" option to open lnav without "
"loading any files")));
retval = EXIT_FAILURE;
}
@ -3433,8 +3467,8 @@ SELECT tbl_name FROM sqlite_master WHERE sql LIKE 'CREATE VIRTUAL TABLE%'
fprintf(stderr, "error: %s\n", e.what());
}
// When reading from stdin, tell the user where the capture file is
// stored so they can look at it later.
// When reading from stdin, tell the user where the capture
// file is stored so they can look at it later.
if (stdin_url && !(lnav_data.ld_flags & LNF_HEADLESS)
&& verbosity != verbosity_t::quiet)
{

@ -326,6 +326,70 @@ com_unix_time(exec_context& ec,
return Ok(retval);
}
static Result<std::string, lnav::console::user_message>
com_set_file_timezone(exec_context& ec,
std::string cmdline,
std::vector<std::string>& args)
{
std::string retval;
if (args.empty()) {
args.emplace_back("timezone");
return Ok(retval);
}
if (args.size() == 1) {
return ec.make_error("expecting a timezone name");
}
auto* tc = *lnav_data.ld_view_stack.top();
auto* lss = dynamic_cast<logfile_sub_source*>(tc->get_sub_source());
if (lss != nullptr) {
if (lss->text_line_count() == 0) {
return ec.make_error("no log messages to examine");
}
auto line_pair = lss->find_line_with_file(lss->at(tc->get_selection()));
if (!line_pair) {
return ec.make_error(FMT_STRING("cannot find line: {}"),
(int) tc->get_selection());
}
try {
auto* tz = date::locate_zone(args[1]);
if (!ec.ec_dry_run) {
static auto& safe_options_hier
= injector::get<lnav::safe_file_options_hier&>();
safe::WriteAccess<lnav::safe_file_options_hier> options_hier(
safe_options_hier);
options_hier->foh_generation += 1;
auto& coll = options_hier->foh_path_to_collection["/"];
log_info("setting timezone for %s to %s",
line_pair->first->get_filename().c_str(),
args[1].c_str());
coll.foc_pattern_to_options[line_pair->first->get_filename()]
= lnav::file_options{
intern_string::lookup(args[1]),
tz,
};
}
} catch (const std::runtime_error& e) {
return ec.make_error(FMT_STRING("Unable to get timezone: {} -- {}"),
args[1],
e.what());
}
} else {
return ec.make_error(
":set-file-timezone is only supported for the LOG view");
}
return Ok(retval);
}
static Result<std::string, lnav::console::user_message>
com_convert_time_to(exec_context& ec,
std::string cmdline,
@ -5442,6 +5506,18 @@ readline_context::command_t STD_COMMANDS[] = {
.with_summary("Convert the focused timestamp to the given timezone")
.with_parameter(help_text("zone", "The timezone name")),
},
{
"set-file-timezone",
com_set_file_timezone,
help_text(":set-file-timezone")
.with_summary("Set the timezone to use for log messages that do "
"not include a timezone. The timezone is applied to "
"the focused file or the given glob pattern.")
.with_parameter({"zone", "The timezone name"})
.with_parameter(help_text{"pattern",
"The glob pattern to match against "
"files that should use this timezone"}),
},
{"current-time",
com_current_time,

@ -1061,6 +1061,16 @@ external_log_format::scan(logfile& lf,
shared_buffer_ref& sbr,
scan_batch_context& sbc)
{
if (dst.empty()) {
auto file_options = lf.get_file_options();
if (file_options) {
this->lf_date_time.dts_default_zone = file_options->fo_default_zone;
} else {
this->lf_date_time.dts_default_zone = nullptr;
}
}
if (this->elf_type == elf_type_t::ELF_TYPE_JSON) {
logline ll(li.li_file_range.fr_offset, 0, 0, LEVEL_INFO);
auto line_frag = sbr.to_string_fragment();

@ -107,6 +107,17 @@ class generic_log_format : public log_format {
nonstd::optional<string_fragment> level;
const char* last_pos;
if (dst.empty()) {
auto file_options = lf.get_file_options();
if (file_options) {
this->lf_date_time.dts_default_zone
= file_options->fo_default_zone;
} else {
this->lf_date_time.dts_default_zone = nullptr;
}
}
if ((last_pos = this->log_scanf(dst.size(),
sbr.to_string_fragment(),
get_pcre_log_formats(),
@ -535,6 +546,17 @@ public:
static const auto SEP_RE
= lnav::pcre2pp::code::from_const(R"(^#separator\s+(.+))");
if (dst.empty()) {
auto file_options = lf.get_file_options();
if (file_options) {
this->lf_date_time.dts_default_zone
= file_options->fo_default_zone;
} else {
this->lf_date_time.dts_default_zone = nullptr;
}
}
if (!this->blf_format_name.empty()) {
return this->scan_int(dst, li, sbr, sbc);
}
@ -1200,6 +1222,17 @@ public:
return scan_incomplete{};
}
if (dst.empty()) {
auto file_options = lf.get_file_options();
if (file_options) {
this->lf_date_time.dts_default_zone
= file_options->fo_default_zone;
} else {
this->lf_date_time.dts_default_zone = nullptr;
}
}
if (!this->wlf_format_name.empty()) {
return this->scan_int(dst, li, sbr);
}
@ -1713,6 +1746,17 @@ public:
bool done = false;
logfmt_pair_handler lph(this->lf_date_time);
if (dst.empty()) {
auto file_options = lf.get_file_options();
if (file_options) {
this->lf_date_time.dts_default_zone
= file_options->fo_default_zone;
} else {
this->lf_date_time.dts_default_zone = nullptr;
}
}
while (!done) {
auto parse_result = p.step();

@ -47,6 +47,7 @@
#include "base/injector.hh"
#include "base/string_util.hh"
#include "config.h"
#include "file_options.hh"
#include "hasher.hh"
#include "lnav_util.hh"
#include "log.watch.hh"
@ -156,6 +157,8 @@ logfile::open(std::string filename, const logfile_open_options& loo, auto_fd fd)
});
}
lf->file_options_have_changed();
ensure(lf->invariant());
return Ok(lf);
@ -172,6 +175,37 @@ logfile::~logfile()
log_info("destructing logfile: %s", this->lf_filename.c_str());
}
bool
logfile::file_options_have_changed()
{
static auto& safe_options_hier
= injector::get<lnav::safe_file_options_hier&>();
{
safe::ReadAccess<lnav::safe_file_options_hier> options_hier(
safe_options_hier);
if (this->lf_file_options_generation == options_hier->foh_generation) {
return false;
}
auto new_options = options_hier->match(this->get_filename());
if (this->lf_file_options == new_options) {
this->lf_file_options_generation = options_hier->foh_generation;
return false;
}
this->lf_file_options = new_options;
log_info("%s: file options have changed", this->lf_filename.c_str());
if (this->lf_file_options) {
log_info(" tz=%s",
this->lf_file_options->fo_default_zone->name().c_str());
}
this->lf_file_options_generation = options_hier->foh_generation;
}
return true;
}
bool
logfile::exists() const
{
@ -497,9 +531,10 @@ logfile::rebuild_index(nonstd::optional<ui_clock::time_point> deadline)
return rebuild_result_t::NO_NEW_LINES;
}
if (this->lf_format != nullptr
&& (this->lf_zoned_to_local_state != dts_cfg.c_zoned_to_local
|| this->lf_format->format_changed()))
if (this->file_options_have_changed()
|| (this->lf_format != nullptr
&& (this->lf_zoned_to_local_state != dts_cfg.c_zoned_to_local
|| this->lf_format->format_changed())))
{
log_info("%s: format has changed, rebuilding",
this->lf_filename.c_str());

@ -48,6 +48,7 @@
#include "base/result.h"
#include "bookmarks.hh"
#include "byte_array.hh"
#include "file_options.hh"
#include "ghc/filesystem.hpp"
#include "line_buffer.hh"
#include "log_format_fwd.hh"
@ -400,6 +401,11 @@ public:
return this->lf_embedded_metadata;
}
nonstd::optional<lnav::file_options> get_file_options() const
{
return this->lf_file_options;
}
protected:
/**
* Process a line from the file.
@ -417,6 +423,8 @@ protected:
private:
logfile(std::string filename, const logfile_open_options& loo);
bool file_options_have_changed();
std::string lf_filename;
logfile_open_options lf_options;
logfile_activity lf_activity;
@ -458,6 +466,8 @@ private:
std::vector<std::shared_ptr<format_tag_def>> lf_applicable_taggers;
std::map<std::string, metadata> lf_embedded_metadata;
size_t lf_file_options_generation{0};
nonstd::optional<lnav::file_options> lf_file_options;
};
class logline_observer {

@ -275,7 +275,8 @@ logfile_sub_source::text_value_for_line(textview_curses& tc,
if (!this->lss_token_line->is_continued()
&& (this->lss_token_file->is_time_adjusted()
|| (format->lf_timestamp_flags & ETF_ZONE_SET
|| ((format->lf_timestamp_flags & ETF_ZONE_SET
|| format->lf_date_time.dts_default_zone != nullptr)
&& format->lf_date_time.dts_zoned_to_local)
|| format->lf_timestamp_flags & ETF_MACHINE_ORIENTED
|| !(format->lf_timestamp_flags & ETF_DAY_SET)
@ -496,8 +497,7 @@ logfile_sub_source::text_attrs_for_line(textview_curses& lv,
value_out.emplace_back(lr, VC_GRAPHIC.value(graph));
if (!(this->lss_token_flags & RF_FULL)) {
bookmark_vector<vis_line_t>& bv_search
= bm[&textview_curses::BM_SEARCH];
auto& bv_search = bm[&textview_curses::BM_SEARCH];
if (binary_search(std::begin(bv_search),
std::end(bv_search),
@ -2347,7 +2347,8 @@ logfile_sub_source::text_crumbs_for_line(int line,
return breadcrumb::possibility{
elem.to_string(),
};
});
})
| lnav::itertools::to_vector();
},
[ec = this->lss_exec_context](const auto& format_name) {
static const std::string MOVE_STMT = R"(;UPDATE lnav_views

@ -112,6 +112,21 @@ plain_text_source::replace_with(const std::vector<std::string>& text_lines)
return *this;
}
plain_text_source&
plain_text_source::replace_with(const std::vector<attr_line_t>& text_lines)
{
file_off_t off = 0;
for (const auto& al : text_lines) {
this->tds_lines.emplace_back(off, al);
off += al.length() + 1;
}
this->tds_longest_line = this->compute_longest_line();
if (this->tss_view != nullptr) {
this->tss_view->set_needs_update();
}
return *this;
}
void
plain_text_source::clear()
{
@ -148,6 +163,18 @@ plain_text_source::text_value_for_line(textview_curses& tc,
text_sub_source::line_flags_t flags)
{
value_out = this->tds_lines[row].tl_value.get_string();
this->tds_line_indent_size = 0;
for (const auto& ch : value_out) {
if (ch == ' ') {
this->tds_line_indent_size += 1;
} else if (ch == '\t') {
do {
this->tds_line_indent_size += 1;
} while (this->tds_line_indent_size % 8);
} else {
break;
}
}
}
void
@ -162,6 +189,18 @@ plain_text_source::text_attrs_for_line(textview_curses& tc,
value_out.emplace_back(line_range{0, -1},
VC_STYLE.value(text_attrs{A_REVERSE}));
}
for (const auto& indent : this->tds_doc_sections.m_indents) {
if (indent < this->tds_line_indent_size) {
auto guide_lr = line_range{
(int) indent,
(int) (indent + 1),
line_range::unit::codepoint,
};
value_out.emplace_back(guide_lr,
VC_BLOCK_ELEM.value(block_elem_t{
L'\u258f', role_t::VCR_INDENT_GUIDE}));
}
}
}
size_t

@ -77,6 +77,8 @@ public:
plain_text_source& replace_with(const std::vector<std::string>& text_lines);
plain_text_source& replace_with(const std::vector<attr_line_t>& text_lines);
void clear();
plain_text_source& truncate_to(size_t max_lines);
@ -131,6 +133,7 @@ protected:
text_format_t tds_text_format{text_format_t::TF_UNKNOWN};
size_t tds_longest_line{0};
bool tds_reverse_selection{false};
size_t tds_line_indent_size{0};
lnav::document::metadata tds_doc_sections;
};

@ -171,11 +171,16 @@ pretty_printer::append_to(attr_line_t& al)
void
pretty_printer::write_element(const pretty_printer::element& el)
{
ssize_t start_size = this->pp_stream.tellp();
if (this->pp_leading_indent == 0 && this->pp_line_length == 0
&& el.e_token == DT_WHITE)
{
if (this->pp_depth == 0) {
this->pp_soft_indent += el.e_capture.length();
} else {
auto shift_cover = line_range{(int) start_size, (int) start_size};
shift_string_attrs(
this->pp_attrs, shift_cover, -el.e_capture.length());
}
return;
}
@ -191,10 +196,10 @@ pretty_printer::write_element(const pretty_printer::element& el)
}
return;
}
int indent_size = 0;
if (this->pp_line_length == 0) {
this->append_indent();
indent_size = this->append_indent();
}
ssize_t start_size = this->pp_stream.tellp();
if (el.e_token == DT_QUOTED_STRING) {
auto unquoted_str = auto_mem<char>::malloc(el.e_capture.length() + 1);
const char* start
@ -230,11 +235,9 @@ pretty_printer::write_element(const pretty_printer::element& el)
}
} else {
this->pp_stream << this->pp_scanner->to_string_fragment(el.e_capture);
int shift_amount
= start_size - el.e_capture.c_begin - this->pp_shift_accum;
shift_string_attrs(this->pp_attrs, el.e_capture.c_begin, shift_amount);
this->pp_shift_accum = start_size - el.e_capture.c_begin;
}
auto shift_cover = line_range{(int) start_size, (int) start_size};
shift_string_attrs(this->pp_attrs, shift_cover, indent_size);
this->pp_line_length += el.e_capture.length();
if (el.e_token == DT_LINE) {
this->pp_line_length = 0;
@ -242,29 +245,23 @@ pretty_printer::write_element(const pretty_printer::element& el)
}
}
void
int
pretty_printer::append_indent()
{
static const auto INDENT_GUIDELINE = block_elem_t{
L'\u258f',
role_t::VCR_INDENT_GUIDE,
};
auto start_size = this->pp_stream.tellp();
this->pp_stream << std::string(
this->pp_leading_indent + this->pp_soft_indent, ' ');
this->pp_soft_indent = 0;
if (this->pp_stream.tellp() == this->pp_leading_indent) {
return;
}
for (int lpc = 0; lpc < this->pp_depth; lpc++) {
if (lpc > 0) {
int off = this->pp_stream.tellp();
this->pp_post_attrs.emplace_back(
line_range{off, off + 1},
VC_BLOCK_ELEM.value(INDENT_GUIDELINE));
if (this->pp_stream.tellp() != this->pp_leading_indent) {
for (int lpc = 0; lpc < this->pp_depth; lpc++) {
this->pp_stream << " ";
}
if (this->pp_depth > 0) {
this->pp_indents.insert(this->pp_leading_indent
+ 4 * this->pp_depth);
}
this->pp_stream << " ";
}
return (this->pp_stream.tellp() - start_size);
}
bool
@ -305,7 +302,11 @@ pretty_printer::flush_values(bool start_on_depth)
&& (el.e_token == DT_LSQUARE || el.e_token == DT_LCURLY))
{
if (this->pp_line_length > 0) {
ssize_t start_size = this->pp_stream.tellp();
this->pp_stream << std::endl;
auto shift_cover
= line_range{(int) start_size, (int) start_size};
shift_string_attrs(this->pp_attrs, shift_cover, 1);
}
this->pp_line_length = 0;
}
@ -321,13 +322,19 @@ pretty_printer::start_new_line()
{
bool has_output;
ssize_t start_size = this->pp_stream.tellp();
if (this->pp_line_length > 0) {
this->pp_stream << std::endl;
auto shift_cover = line_range{(int) start_size, (int) start_size};
shift_string_attrs(this->pp_attrs, shift_cover, 1);
this->pp_line_length = 0;
}
has_output = this->flush_values();
if (has_output && this->pp_line_length > 0) {
start_size = this->pp_stream.tellp();
this->pp_stream << std::endl;
auto shift_cover = line_range{(int) start_size, (int) start_size};
shift_string_attrs(this->pp_attrs, shift_cover, 1);
}
this->pp_line_length = 0;
this->pp_body_lines.top() += 1;

@ -94,6 +94,8 @@ public:
return std::move(this->pp_hier_stage);
}
std::set<size_t> take_indents() { return std::move(this->pp_indents); }
private:
void descend();
@ -103,7 +105,7 @@ private:
bool flush_values(bool start_on_depth = false);
void append_indent();
int append_indent();
void write_element(const element& el);
@ -130,6 +132,7 @@ private:
std::vector<lnav::document::section_interval_t> pp_intervals;
std::vector<std::unique_ptr<lnav::document::hier_node>> pp_hier_nodes;
std::unique_ptr<lnav::document::hier_node> pp_hier_stage;
std::set<size_t> pp_indents;
};
#endif

@ -43,6 +43,7 @@ libtailerpp_a_CPPFLAGS = \
-I$(srcdir)/.. \
-I$(srcdir)/../fmtlib \
-I$(srcdir)/../third-party \
-I$(top_srcdir)/src/third-party/date/include \
-I$(top_srcdir)/src/third-party/scnlib/include
libtailerpp_a_SOURCES = \
@ -56,6 +57,7 @@ libtailerservice_a_CPPFLAGS = \
-I$(srcdir)/.. \
-I$(srcdir)/../fmtlib \
-I$(srcdir)/../third-party \
-I$(top_srcdir)/src/third-party/date/include \
-I$(top_srcdir)/src/third-party/scnlib/include
libtailerservice_a_SOURCES = \

@ -244,6 +244,16 @@ setup_highlights(highlight_map_t& hm)
.with_text_format(text_format_t::TF_JAVA)
.with_role(role_t::VCR_KEYWORD);
hm[{highlight_source_t::INTERNAL, "json.keyword"}]
= highlighter(xpcre_compile(R"((?:null|true|false))"))
.with_nestable(false)
.with_text_format(text_format_t::TF_JSON)
.with_role(role_t::VCR_KEYWORD);
hm[{highlight_source_t::INTERNAL, "json.number"}]
= highlighter(xpcre_compile(R"(-?\d+(?:\.\d+(?:[eE][+\-]?\d+)?)?)"))
.with_nestable(false)
.with_text_format(text_format_t::TF_JSON)
.with_role(role_t::VCR_NUMBER);
hm[{highlight_source_t::INTERNAL, "sql.0.comment"}]
= highlighter(xpcre_compile("(?:(?<=[\\s;])|^)--.*"))
.with_text_format(text_format_t::TF_SQL)

@ -616,6 +616,7 @@ textfile_sub_source::rescan_files(
file_iterator iter;
rescan_result_t retval;
size_t files_scanned = 0;
if (this->tss_view == nullptr || this->tss_view->is_paused()) {
return retval;
@ -623,7 +624,8 @@ textfile_sub_source::rescan_files(
std::vector<std::shared_ptr<logfile>> closed_files;
for (iter = this->tss_files.begin(); iter != this->tss_files.end();) {
if (deadline && ui_clock::now() > deadline.value()) {
if (deadline && files_scanned > 0 && ui_clock::now() > deadline.value())
{
log_info("rescan_files() deadline reached, breaking...");
retval.rr_scan_completed = false;
break;
@ -644,6 +646,7 @@ textfile_sub_source::rescan_files(
++iter;
continue;
}
files_scanned += 1;
try {
const auto& st = lf->get_stat();

@ -139,6 +139,11 @@ open_gantt_view()
class pretty_sub_source : public plain_text_source {
public:
void set_indents(std::set<size_t>&& indents)
{
this->tds_doc_sections.m_indents = std::move(indents);
}
void text_crumbs_for_line(int line,
std::vector<breadcrumb::crumb>& crumbs) override
{
@ -308,11 +313,18 @@ open_pretty_view()
auto* log_tc = &lnav_data.ld_views[LNV_LOG];
auto* text_tc = &lnav_data.ld_views[LNV_TEXT];
if (top_tc == log_tc && log_tc->get_inner_height() == 0
&& text_tc->get_inner_height() > 0)
{
lnav_data.ld_view_stack.push_back(text_tc);
top_tc = text_tc;
}
if (top_tc != log_tc && top_tc != text_tc) {
return;
}
attr_line_t full_text;
std::vector<attr_line_t> full_text;
delete pretty_tc->get_sub_source();
pretty_tc->set_sub_source(nullptr);
@ -324,9 +336,11 @@ open_pretty_view()
std::vector<lnav::document::section_interval_t> all_intervals;
std::vector<std::unique_ptr<lnav::document::hier_node>> hier_nodes;
std::vector<pretty_sub_source::hier_interval_t> hier_tree_vec;
std::set<size_t> pretty_indents;
if (top_tc == log_tc) {
auto& lss = lnav_data.ld_log_source;
bool first_line = true;
auto start_off = size_t{0};
for (auto vl = log_tc->get_top(); vl <= log_tc->get_bottom(); ++vl) {
content_line_t cl = lss.at(vl);
@ -365,7 +379,6 @@ open_pretty_view()
? body_lr.lr_start - orig_lr.lr_start
: orig_lr.lr_start);
pretty_printer pp(&ds, orig_al.get_attrs());
auto start_off = full_text.length();
if (body_lr.is_valid()) {
// TODO: dump more details of the line in the output.
@ -375,9 +388,14 @@ open_pretty_view()
}
pretty_al.split_lines(pretty_lines);
auto prefix_len = prefix_al.length();
auto curr_intervals = pp.take_intervals();
auto line_hier_root = pp.take_hier_root();
auto curr_indents = pp.take_indents()
| lnav::itertools::map([&prefix_len](const auto& elem) {
return elem + prefix_len;
});
auto line_off = 0;
for (auto& pretty_line : pretty_lines) {
if (pretty_line.empty() && &pretty_line == &pretty_lines.back())
@ -385,24 +403,23 @@ open_pretty_view()
break;
}
pretty_line.insert(0, prefix_al);
pretty_line.append("\n");
for (auto& interval : curr_intervals) {
if (line_off <= interval.start) {
interval.start += prefix_al.length();
interval.stop += prefix_al.length();
interval.start += prefix_len;
interval.stop += prefix_len;
} else if (line_off < interval.stop) {
interval.stop += prefix_al.length();
interval.stop += prefix_len;
}
}
lnav::document::hier_node::depth_first(
line_hier_root.get(),
[line_off, prefix_len = prefix_al.length()](auto* hn) {
[line_off, prefix_len = prefix_len](auto* hn) {
if (line_off <= hn->hn_start) {
hn->hn_start += prefix_len;
}
});
line_off += pretty_line.length();
full_text.append(pretty_line);
line_off += pretty_line.get_string().length();
full_text.emplace_back(pretty_line);
}
first_line = false;
@ -415,15 +432,14 @@ open_pretty_view()
[start_off](auto* hn) { hn->hn_start += start_off; });
hier_nodes.emplace_back(std::move(line_hier_root));
hier_tree_vec.emplace_back(
start_off, full_text.length(), hier_nodes.back().get());
start_off, start_off + line_off, hier_nodes.back().get());
all_intervals.insert(
all_intervals.end(),
std::make_move_iterator(curr_intervals.begin()),
std::make_move_iterator(curr_intervals.end()));
}
pretty_indents.insert(curr_indents.begin(), curr_indents.end());
if (!full_text.empty()) {
full_text.erase(full_text.length() - 1, 1);
start_off += line_off;
}
} else if (top_tc == text_tc) {
if (text_tc->listview_rows(*text_tc)) {
@ -433,19 +449,36 @@ open_pretty_view()
*text_tc, text_tc->get_top(), rows);
attr_line_t orig_al;
for (const auto& row : rows) {
for (auto& row : rows) {
remove_string_attr(row.get_attrs(), &VC_BLOCK_ELEM);
for (auto& attr : row.get_attrs()) {
if (attr.sa_type == &VC_ROLE) {
auto role = attr.sa_value.get<role_t>();
if (role == text_tc->tc_cursor_role
|| role == text_tc->tc_disabled_cursor_role)
{
attr.sa_range.lr_end = attr.sa_range.lr_start;
}
}
}
orig_al.append(row);
}
data_scanner ds(orig_al.get_string());
string_attrs_t sa;
pretty_printer pp(&ds, orig_al.get_attrs());
attr_line_t pretty_al;
pp.append_to(pretty_al);
pretty_al.rtrim();
pp.append_to(full_text);
all_intervals = pp.take_intervals();
hier_nodes.emplace_back(pp.take_hier_root());
hier_tree_vec.emplace_back(
0, full_text.length(), hier_nodes.back().get());
0, pretty_al.length(), hier_nodes.back().get());
pretty_indents = pp.take_indents();
pretty_al.split_lines(full_text);
}
}
auto* pts = new pretty_sub_source();
@ -454,6 +487,8 @@ open_pretty_view()
pts->pss_hier_nods = std::move(hier_nodes);
pts->pss_hier_tree = std::make_shared<pretty_sub_source::hier_tree_t>(
std::move(hier_tree_vec));
pts->set_indents(std::move(pretty_indents));
pts->replace_with(full_text);
pretty_tc->set_sub_source(pts);
if (lnav_data.ld_last_pretty_print_top != log_tc->get_top()) {

@ -13,7 +13,7 @@ add_library(
target_include_directories(yajlpp PUBLIC . .. ../fmtlib
${CMAKE_CURRENT_BINARY_DIR}/..)
target_link_libraries(yajlpp pcrepp yajl ncurses::libcurses)
target_link_libraries(yajlpp pcrepp yajl ncurses::libcurses datepp)
add_executable(test_yajlpp test_yajlpp.cc)
target_link_libraries(test_yajlpp yajlpp base ${lnav_LIBS})

@ -11,6 +11,7 @@ AM_CPPFLAGS = \
$(PCRE_CFLAGS) \
-I$(top_srcdir)/src/ \
-I$(top_srcdir)/src/fmtlib \
-I$(top_srcdir)/src/third-party/date/include \
-I$(top_srcdir)/src/third-party/scnlib/include
AM_LDFLAGS = \

@ -17,6 +17,7 @@ AM_CPPFLAGS = \
-I$(top_srcdir)/src \
-I$(top_srcdir)/src/fmtlib \
-I$(top_srcdir)/src/third-party \
-I$(top_srcdir)/src/third-party/date/include \
-I$(top_srcdir)/src/third-party/scnlib/include \
$(CODE_COVERAGE_CPPFLAGS) \
$(LIBARCHIVE_CFLAGS) \

@ -37,6 +37,7 @@
#include <stdio.h>
#include <stdlib.h>
#include "base/injector.bind.hh"
#include "base/injector.hh"
#include "config.h"
#include "data_parser.hh"
@ -51,6 +52,9 @@
const char* TMP_NAME = "scanned.tmp";
static auto bound_file_options_hier
= injector::bind<lnav::safe_file_options_hier>::to_singleton();
int
main(int argc, char* argv[])
{

@ -37,6 +37,7 @@
#include <sys/types.h>
#include <unistd.h>
#include "base/injector.bind.hh"
#include "base/injector.hh"
#include "base/opt_util.hh"
#include "config.h"
@ -54,6 +55,9 @@ typedef enum {
MODE_LEVELS,
} dl_mode_t;
static auto bound_file_options_hier
= injector::bind<lnav::safe_file_options_hier>::to_singleton();
time_t
time(time_t* _unused)
{

@ -378,6 +378,10 @@ EXPECTED_FILES = \
$(srcdir)/%reldir%/test_logfile.sh_c18e14a26d8261c9f72747118a469266121d5459.out \
$(srcdir)/%reldir%/test_logfile.sh_ccb0d31813367c8d9dc5b5df383fac5b780711c1.err \
$(srcdir)/%reldir%/test_logfile.sh_ccb0d31813367c8d9dc5b5df383fac5b780711c1.out \
$(srcdir)/%reldir%/test_logfile.sh_d14f6d8888652321206549df8a9535399f0fd372.err \
$(srcdir)/%reldir%/test_logfile.sh_d14f6d8888652321206549df8a9535399f0fd372.out \
$(srcdir)/%reldir%/test_logfile.sh_de8d59879fe6aa5a012b0748ff77ae26c07aea89.err \
$(srcdir)/%reldir%/test_logfile.sh_de8d59879fe6aa5a012b0748ff77ae26c07aea89.out \
$(srcdir)/%reldir%/test_logfile.sh_e840b674cd65936a72bd64b1dac1524d16fe44c3.err \
$(srcdir)/%reldir%/test_logfile.sh_e840b674cd65936a72bd64b1dac1524d16fe44c3.out \
$(srcdir)/%reldir%/test_logfile.sh_f171f265d8d45a2707e8b9f53e938f574c614d25.err \

@ -1,12 +1,12 @@
{
"foo bar": null,
"foo bar": null,
"array": [
1,
2,
3
1,
2,
3
],
"obj": {
"one": 1,
"two": true
"one": 1,
"two": true
}
}

@ -1446,6 +1446,17 @@ For support questions, email:
:set-file-timezone zone pattern
══════════════════════════════════════════════════════════════════════
Set the timezone to use for log messages that do not include a
timezone. The timezone is applied to the focused file or the given
glob pattern.
Parameters
zone The timezone name
pattern The glob pattern to match against files that
should use this timezone
:set-min-log-level log-level
══════════════════════════════════════════════════════════════════════
Set the minimum log level to display in the log view

@ -1,12 +1,12 @@
{
 "foo bar" : null,
 "array" : [
   1,
   2,
   3
 ],
 "obj" : {
"one" : 1,
   "two" : true
"foo bar" : null,
"array" : [
1,
2,
3
],
"obj" : {
"one" : 1,
"two" : true
}
}

@ -1,29 +1,29 @@
[2013-09-06T20:00:48.124] ⋮ trace testbork bork bork
[2013-09-06T20:00:49.124] ⋮ Starting up servicebork bork bork
[2013-09-06T22:00:49.124] ⋮ Shutting down servicebork bork bork
user: mailto:steve@example.com
[2013-09-06T22:00:59.124] ⋮ Details...
bork bork bork
[2013-09-06T22:00:59.124] ⋮ Details...
bork bork bork
[2013-09-06T22:00:59.124] ⋮ Details...
bork bork bork
[2013-09-06T22:00:59.124] ⋮ Details...
bork bork bork
[2013-09-06T22:00:59.124] ⋮ Details...bork bork bork
[2013-09-06T22:01:49.124] ⋮ 1 beat per secondbork bork bork
[2013-09-06T22:01:49.124] ⋮ not looking goodbork bork bork
[2013-09-06T22:01:49.124] ⋮ looking badbork bork bork
[2013-09-06T22:01:49.124] ⋮ sooo badbork bork bork

@ -1,8 +1,8 @@
{
 "wrapper": [
"wrapper": [
{"message":""
 select Id from Account where id = $sfid
 ^
 ERROR at Row:1:Column:34
 line 1:34 no viable alternative at character '$'
""}]}
   select Id from Account where id = $sfid
     ^
   ERROR at Row:1:Column:34
   line 1:34 no viable alternative at character '$'
""}]}

@ -1,5 +1,5 @@
2015-04-18T13:16:30.003 {
"wrapper": {"msg": r""
Hello,
World!
""}}
""}}

@ -710,3 +710,11 @@ run_cap_test ${lnav_test} -n \
run_cap_test ${lnav_test} -n \
-c ':filter-in Air Mob' \
${test_dir}/logfile_ansi.1
run_cap_test ${lnav_test} -n \
-c ':set-file-timezone America/Los_Angeles' \
${test_dir}/logfile_syslog.0
run_cap_test ${lnav_test} -n \
-c ':set-file-timezone America/New_York' \
${test_dir}/logfile_syslog.0

Loading…
Cancel
Save