mirror of
https://github.com/tstack/lnav.git
synced 2024-10-26 13:16:11 +03:00
[cmds] add command for setting a default time zone
This commit is contained in:
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
|
||||
|
105
src/file_options.cc
Normal file
105
src/file_options.cc
Normal file
@ -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
|
70
src/file_options.hh
Normal file
70
src/file_options.hh
Normal file
@ -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*
|
||||
|
76
src/lnav.cc
76
src/lnav.cc
@ -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 @@
|
||||
{
|
||||
[35m"foo bar"[0m: null,
|
||||
[35m"foo bar"[0m: [1m[36mnull[0m,
|
||||
[35m"array"[0m: [
|
||||
1,
|
||||
2,
|
||||
3
|
||||
[1m1[0m,
|
||||
[1m2[0m,
|
||||
[1m3[0m
|
||||
],
|
||||
[35m"obj"[0m: {
|
||||
[35m"one"[0m: 1,
|
||||
[35m"two"[0m: true
|
||||
[35m"one"[0m: [1m1[0m,
|
||||
[35m"two"[0m: [1m[36mtrue[0m
|
||||
}
|
||||
}
|
||||
|
@ -1446,6 +1446,17 @@ For support questions, email:
|
||||
|
||||
|
||||
|
||||
[4m:[0m[1m[4mset-file-timezone[0m[4m [0m[4mzone[0m[4m [0m[4mpattern[0m
|
||||
══════════════════════════════════════════════════════════════════════
|
||||
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.
|
||||
[4mParameters[0m
|
||||
[4mzone[0m The timezone name
|
||||
[4mpattern[0m The glob pattern to match against files that
|
||||
should use this timezone
|
||||
|
||||
|
||||
[4m:[0m[1m[4mset-min-log-level[0m[4m [0m[4mlog-level[0m
|
||||
══════════════════════════════════════════════════════════════════════
|
||||
Set the minimum log level to display in the log view
|
||||
|
@ -1,12 +1,12 @@
|
||||
{
|
||||
[35m "foo [0mbar" : null,
|
||||
[35m "array" : [[0m
|
||||
[35m [0m[35m [0m[35m 1,[0m
|
||||
[35m [0m[35m [0m[35m 2,[0m
|
||||
[35m [0m[35m [0m[35m 3[0m
|
||||
[35m ],[0m
|
||||
[35m "obj" : [0m{
|
||||
"one[35m" : 1[0m,
|
||||
[35m [0m[35m [0m[35m [0m"two" [35m: tru[0me
|
||||
[35m"foo bar"[0m : [1m[36mnull[0m,
|
||||
[35m"array"[0m : [
|
||||
[1m1[0m,
|
||||
[1m2[0m,
|
||||
[1m3[0m
|
||||
],
|
||||
[35m"obj"[0m : {
|
||||
[35m"one"[0m : [1m1[0m,
|
||||
[35m"two"[0m : [1m[36mtrue[0m
|
||||
}
|
||||
}
|
||||
|
@ -1,29 +1,29 @@
|
||||
|
||||
|
||||
[2013-09-06T20:00:48.124] ⋮ trace testbork bork bork
|
||||
|
||||
|
||||
[2013-09-06T20:00:49.124] ⋮ Starting up [32mservice[0mbork 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
|
||||
|
||||
|
||||
[33m[[0m[33m2013-09-06T22:01:49.124[0m[33m] [0m[33m⋮[0m[33m [0m[33mnot looking goodbork bork bork[0m
|
||||
|
||||
|
||||
[31m[[0m[31m2013-09-06T22:01:49.124[0m[31m] [0m[31m⋮[0m[31m [0m[31mlooking badbork bork bork[0m
|
||||
|
||||
|
||||
[31m[[0m[31m2013-09-06T22:01:49.124[0m[31m] [0m[31m⋮[0m[31m [0m[31msooo badbork bork bork[0m
|
||||
|
@ -1,8 +1,8 @@
|
||||
{
|
||||
[35m "wra[0mpper": [
|
||||
[35m"wrapper"[0m: [
|
||||
{[35m"message"[0m:[35m""[0m
|
||||
[35m select Id from Account where id = $sfid[0m
|
||||
[35m ^[0m
|
||||
[35m ERROR at Row:1:Column:34[0m
|
||||
[35m line 1:34 no viable alternative at character '$'[0m
|
||||
[35m""[0m}]}
|
||||
[35m [0m[35m [0m[35m select Id from Account where id = $sfid[0m
|
||||
[35m [0m[35m [0m[35m [0m[35m [0m[35m ^[0m
|
||||
[35m [0m[35m [0m[35m ERROR at Row:1:Column:34[0m
|
||||
[35m [0m[35m [0m[35m line[0m 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…
Reference in New Issue
Block a user