mirror of
https://github.com/tstack/lnav.git
synced 2024-08-16 16:30:25 +03:00
[files] add a detail view to the files panel
This commit is contained in:
parent
e6aac98193
commit
9694d93ff3
4
NEWS.md
4
NEWS.md
@ -8,6 +8,10 @@ Features:
|
||||
so that you can manually set an opid on log messages that
|
||||
don't have one. Setting an opid allows messages to show
|
||||
up in the timeline view.
|
||||
* The Files panel now has a details view on the right side
|
||||
that shows extra information about the selected file.
|
||||
You can look here for details of why lnav selected a
|
||||
particular log format.
|
||||
* Add support for GitHub Markdown Alerts.
|
||||
* Added the `:xopen` command that will open the given paths
|
||||
using an external opener like `open` or `xdg-open`.
|
||||
|
@ -104,14 +104,19 @@ To hide the panels again, press :kbd:`q`.
|
||||
Screenshot of the files panel showing the loaded files.
|
||||
|
||||
The Files panel is open initially to display progress in loading files.
|
||||
The following information can be displayed for each file:
|
||||
The following information is displayed for each file:
|
||||
|
||||
* the "unique" portion of the path relative to the other files and
|
||||
* the amount of data that has been indexed.
|
||||
|
||||
To the right of the file list is a panel that shows details for each
|
||||
file, such as:
|
||||
|
||||
* the "unique" portion of the path relative to the other files;
|
||||
* the amount of data that has been indexed;
|
||||
* the date range of log messages contained in the file;
|
||||
* the errors that were encountered while trying to index the file;
|
||||
* the notes recorded for files where some automatic action was taken,
|
||||
like hiding the file if it was seen as a duplicate of another file.
|
||||
like hiding the file if it was seen as a duplicate of another file;
|
||||
* the details of the demultiplexing and log format matching process.
|
||||
|
||||
.. figure:: lnav-filters-panel.png
|
||||
:align: center
|
||||
|
@ -301,7 +301,7 @@ public:
|
||||
|
||||
size_t start_len = this->al_string.length();
|
||||
|
||||
this->al_string.append(std::move(value.first));
|
||||
this->append(std::move(value.first));
|
||||
|
||||
line_range lr{(int) start_len, (int) this->al_string.length()};
|
||||
|
||||
|
@ -180,6 +180,30 @@ string_fragment::trim() const
|
||||
return this->trim(" \t\r\n");
|
||||
}
|
||||
|
||||
string_fragment
|
||||
string_fragment::rtrim(const char* tokens) const
|
||||
{
|
||||
string_fragment retval = *this;
|
||||
|
||||
while (retval.sf_begin < retval.sf_end) {
|
||||
bool found = false;
|
||||
|
||||
for (int lpc = 0; tokens[lpc] != '\0'; lpc++) {
|
||||
if (retval.sf_string[retval.sf_end - 1] == tokens[lpc]) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
break;
|
||||
}
|
||||
|
||||
retval.sf_end -= 1;
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
std::optional<string_fragment>
|
||||
string_fragment::consume_n(int amount) const
|
||||
{
|
||||
|
@ -281,6 +281,12 @@ struct string_fragment {
|
||||
|
||||
string_fragment sub_range(int begin, int end) const
|
||||
{
|
||||
if (this->sf_begin + begin > this->sf_end) {
|
||||
begin = this->sf_end - this->sf_begin;
|
||||
}
|
||||
if (this->sf_begin + end > this->sf_end) {
|
||||
end = this->sf_end - this->sf_begin;
|
||||
}
|
||||
return string_fragment{
|
||||
this->sf_string, this->sf_begin + begin, this->sf_begin + end};
|
||||
}
|
||||
@ -575,6 +581,7 @@ struct string_fragment {
|
||||
}
|
||||
|
||||
string_fragment trim(const char* tokens) const;
|
||||
string_fragment rtrim(const char* tokens) const;
|
||||
string_fragment trim() const;
|
||||
|
||||
string_fragment prepend(const char* str, int amount) const
|
||||
|
@ -35,7 +35,11 @@
|
||||
#include "base/injector.hh"
|
||||
#include "base/lnav_log.hh"
|
||||
#include "base/paths.hh"
|
||||
#include "base/snippet_highlighters.hh"
|
||||
#include "piper.looper.cfg.hh"
|
||||
#include "readline_highlighters.hh"
|
||||
|
||||
using namespace lnav::roles::literals;
|
||||
|
||||
namespace lnav {
|
||||
namespace piper {
|
||||
@ -88,7 +92,7 @@ multiplex_matcher::match(const string_fragment& line)
|
||||
for (const auto& demux_pair : cfg.c_demux_definitions) {
|
||||
const auto& df = demux_pair.second;
|
||||
|
||||
if (!df.dd_valid || !df.dd_enabled) {
|
||||
if (!df.dd_valid) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -109,24 +113,86 @@ multiplex_matcher::match(const string_fragment& line)
|
||||
log_info(" demuxer pattern matched");
|
||||
if (!md[df.dd_muxid_capture_index].has_value()) {
|
||||
log_info(" however, mux_id was not captured");
|
||||
|
||||
auto match_um = lnav::console::user_message::warning(
|
||||
attr_line_t("demuxer ")
|
||||
.append_quoted(demux_pair.first)
|
||||
.append(" matched, however the ")
|
||||
.append("mux_id"_symbol)
|
||||
.append(" was not captured"));
|
||||
this->mm_details.emplace_back(match_um);
|
||||
continue;
|
||||
}
|
||||
if (!md[df.dd_body_capture_index].has_value()) {
|
||||
log_info(" however, body was not captured");
|
||||
auto match_um = lnav::console::user_message::warning(
|
||||
attr_line_t("demuxer ")
|
||||
.append_quoted(demux_pair.first)
|
||||
.append(" matched, however the ")
|
||||
.append("body"_symbol)
|
||||
.append(" was not captured"));
|
||||
this->mm_details.emplace_back(match_um);
|
||||
continue;
|
||||
}
|
||||
log_info(" and required captures were found, using demuxer");
|
||||
return found{demux_pair.first};
|
||||
|
||||
if (df.dd_enabled) {
|
||||
auto match_um = lnav::console::user_message::ok(
|
||||
attr_line_t("demuxer ")
|
||||
.append_quoted(demux_pair.first)
|
||||
.append(" matched line ")
|
||||
.append(lnav::roles::number(
|
||||
fmt::to_string(this->mm_line_count))));
|
||||
this->mm_details.emplace_back(match_um);
|
||||
return found{demux_pair.first};
|
||||
}
|
||||
auto config_al = attr_line_t().append(
|
||||
fmt::format(FMT_STRING(":config /log/demux/{}/enabled "
|
||||
"true"),
|
||||
demux_pair.first));
|
||||
readline_lnav_highlighter(config_al, -1);
|
||||
auto match_um
|
||||
= lnav::console::user_message::info(
|
||||
attr_line_t("demuxer ")
|
||||
.append_quoted(demux_pair.first)
|
||||
.append(" matched line ")
|
||||
.append(lnav::roles::number(
|
||||
fmt::to_string(this->mm_line_count)))
|
||||
.append(", however, it is disabled"))
|
||||
.with_help(
|
||||
attr_line_t("Use ")
|
||||
.append_quoted(
|
||||
lnav::roles::quoted_code(config_al))
|
||||
.append(" to enable this demuxer"));
|
||||
this->mm_details.emplace_back(match_um);
|
||||
}
|
||||
|
||||
auto partial_size = df.dd_pattern.pp_value->match_partial(
|
||||
line.sub_range(0, 1024));
|
||||
if (partial_size > 0) {
|
||||
log_info(" only partial match by pattern: %s",
|
||||
df.dd_pattern.pp_value->get_pattern().c_str());
|
||||
log_info(" %s",
|
||||
line.sub_range(0, partial_size).to_string().c_str());
|
||||
auto regex_al = attr_line_t(df.dd_pattern.pp_value->get_pattern());
|
||||
lnav::snippets::regex_highlighter(
|
||||
regex_al, -1, line_range{0, (int) regex_al.length()});
|
||||
auto in_line = line.sub_range(0, 1024).rtrim("\n").to_string();
|
||||
auto esc_res
|
||||
= fmt::v10::detail::find_escape(&in_line[0], &(*in_line.end()));
|
||||
if (esc_res.end != nullptr) {
|
||||
in_line = fmt::format(FMT_STRING("{:?}"), in_line);
|
||||
}
|
||||
auto note = attr_line_t("pattern: ")
|
||||
.append(regex_al)
|
||||
.append("\n ")
|
||||
.append(lnav::roles::quoted_code(in_line))
|
||||
.append("\n")
|
||||
.append(partial_size + 2, ' ')
|
||||
.append("^ matched up to here");
|
||||
auto match_um = lnav::console::user_message::info(
|
||||
attr_line_t("demuxer ")
|
||||
.append_quoted(demux_pair.first)
|
||||
.append(" did not match line ")
|
||||
.append(lnav::roles::number(
|
||||
fmt::to_string(this->mm_line_count))))
|
||||
.with_note(note);
|
||||
this->mm_details.emplace_back(match_um);
|
||||
}
|
||||
if (df.dd_control_pattern.pp_value) {
|
||||
md = df.dd_control_pattern.pp_value->create_match_data();
|
||||
@ -141,6 +207,7 @@ multiplex_matcher::match(const string_fragment& line)
|
||||
}
|
||||
}
|
||||
|
||||
this->mm_line_count += 1;
|
||||
if (this->mm_partial_match_ids.empty()) {
|
||||
return not_found{};
|
||||
}
|
||||
|
@ -30,6 +30,7 @@
|
||||
#ifndef lnav_piper_file_hh
|
||||
#define lnav_piper_file_hh
|
||||
|
||||
#include <filesystem>
|
||||
#include <map>
|
||||
#include <optional>
|
||||
#include <set>
|
||||
@ -39,7 +40,7 @@
|
||||
|
||||
#include "auto_mem.hh"
|
||||
#include "base/intern_string.hh"
|
||||
#include <filesystem>
|
||||
#include "lnav.console.hh"
|
||||
#include "mapbox/variant_io.hpp"
|
||||
#include "time_util.hh"
|
||||
|
||||
@ -96,8 +97,11 @@ public:
|
||||
|
||||
match_result match(const string_fragment& line);
|
||||
|
||||
std::vector<lnav::console::user_message> mm_details;
|
||||
|
||||
private:
|
||||
std::set<std::string> mm_partial_match_ids;
|
||||
size_t mm_line_count{0};
|
||||
};
|
||||
|
||||
} // namespace piper
|
||||
|
@ -79,6 +79,16 @@ strftime_rfc3339(
|
||||
return index;
|
||||
}
|
||||
|
||||
std::string
|
||||
to_rfc3339_string(time64_t tim, int millis, char sep)
|
||||
{
|
||||
char buf[64];
|
||||
|
||||
strftime_rfc3339(buf, sizeof(buf), tim, millis, sep);
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
static std::optional<Posix::time_zone>
|
||||
get_posix_zone(const char* name)
|
||||
{
|
||||
|
@ -46,11 +46,16 @@ namespace lnav {
|
||||
|
||||
using time64_t = uint64_t;
|
||||
|
||||
ssize_t strftime_rfc3339(char* buffer,
|
||||
size_t buffer_size,
|
||||
lnav::time64_t tim,
|
||||
int millis,
|
||||
char sep = ' ');
|
||||
ssize_t strftime_rfc3339(
|
||||
char* buffer, size_t buffer_size, time64_t tim, int millis, char sep = ' ');
|
||||
|
||||
std::string to_rfc3339_string(time64_t tim, int millis, char sep = ' ');
|
||||
|
||||
inline std::string
|
||||
to_rfc3339_string(struct timeval tv, char sep = ' ')
|
||||
{
|
||||
return to_rfc3339_string(tv.tv_sec, tv.tv_usec / 1000, sep);
|
||||
}
|
||||
|
||||
date::sys_info sys_time_to_info(date::sys_seconds secs);
|
||||
|
||||
|
@ -245,13 +245,11 @@ bind_sql_parameters(exec_context& ec, sqlite3_stmt* stmt)
|
||||
static void
|
||||
execute_search(const std::string& search_cmd)
|
||||
{
|
||||
lnav_data.ld_view_stack.top() | [&search_cmd](auto tc) {
|
||||
auto search_term
|
||||
= string_fragment(search_cmd)
|
||||
.find_right_boundary(0, string_fragment::tag1{'\n'})
|
||||
.to_string();
|
||||
tc->execute_search(search_term);
|
||||
};
|
||||
textview_curses* tc = get_textview_for_mode(lnav_data.ld_mode);
|
||||
auto search_term = string_fragment(search_cmd)
|
||||
.find_right_boundary(0, string_fragment::tag1{'\n'})
|
||||
.to_string();
|
||||
tc->execute_search(search_term);
|
||||
}
|
||||
|
||||
Result<std::string, lnav::console::user_message>
|
||||
|
@ -406,12 +406,15 @@ file_collection::watch_logfile(const std::string& filename,
|
||||
}
|
||||
}
|
||||
|
||||
auto ff = detect_file_format(filename);
|
||||
auto ff_res = detect_file_format(filename);
|
||||
|
||||
loo.loo_file_format = ff;
|
||||
switch (ff) {
|
||||
loo.loo_file_format = ff_res.dffr_file_format;
|
||||
switch (ff_res.dffr_file_format) {
|
||||
case file_format_t::SQLITE_DB:
|
||||
retval.fc_other_files[filename].ofd_format = ff;
|
||||
retval.fc_other_files[filename].ofd_format
|
||||
= ff_res.dffr_file_format;
|
||||
retval.fc_other_files[filename].ofd_details
|
||||
= ff_res.dffr_details;
|
||||
break;
|
||||
|
||||
case file_format_t::MULTIPLEXED: {
|
||||
@ -430,7 +433,10 @@ file_collection::watch_logfile(const std::string& filename,
|
||||
looper_options);
|
||||
|
||||
if (create_res.isOk()) {
|
||||
retval.fc_other_files[filename] = ff;
|
||||
auto& ofd = retval.fc_other_files[filename];
|
||||
|
||||
ofd.ofd_format = ff_res.dffr_file_format;
|
||||
ofd.ofd_details = ff_res.dffr_details;
|
||||
retval.fc_file_names[filename] = loo;
|
||||
retval.fc_file_names[filename].with_piper(
|
||||
create_res.unwrap());
|
||||
@ -498,7 +504,9 @@ file_collection::watch_logfile(const std::string& filename,
|
||||
res.unwrapErr(),
|
||||
});
|
||||
} else {
|
||||
retval.fc_other_files[filename] = ff;
|
||||
auto& ofd = retval.fc_other_files[filename];
|
||||
ofd.ofd_format = ff_res.dffr_file_format;
|
||||
ofd.ofd_details = ff_res.dffr_details;
|
||||
}
|
||||
{
|
||||
prog_iter_opt | [&prog](auto prog_iter) {
|
||||
@ -512,6 +520,7 @@ file_collection::watch_logfile(const std::string& filename,
|
||||
default: {
|
||||
auto filename_to_open = filename;
|
||||
|
||||
loo.loo_match_details = ff_res.dffr_details;
|
||||
auto eff = detect_mime_type(filename);
|
||||
|
||||
if (eff) {
|
||||
@ -790,6 +799,14 @@ file_collection::rescan_files(bool required)
|
||||
pair.second.loo_piper->get_out_pattern().string(),
|
||||
pair.second,
|
||||
required);
|
||||
if (!pair.second.loo_piper.value().get_demux_id().empty()
|
||||
&& this->fc_other_files.count(pair.first) == 0)
|
||||
{
|
||||
auto& ofd = retval.fc_other_files[pair.first];
|
||||
ofd.ofd_format = file_format_t::MULTIPLEXED;
|
||||
ofd.ofd_details
|
||||
= pair.second.loo_piper.value().get_demux_details();
|
||||
}
|
||||
} else {
|
||||
this->expand_filename(fq, pair.first, pair.second, required);
|
||||
if (this->fc_rotated) {
|
||||
@ -872,6 +889,18 @@ file_collection::copy()
|
||||
return retval;
|
||||
}
|
||||
|
||||
void
|
||||
file_collection::clear()
|
||||
{
|
||||
this->fc_name_to_errors->writeAccess()->clear();
|
||||
this->fc_file_names.clear();
|
||||
this->fc_files.clear();
|
||||
this->fc_renamed_files.clear();
|
||||
this->fc_closed_files.clear();
|
||||
this->fc_other_files.clear();
|
||||
this->fc_new_stats.clear();
|
||||
}
|
||||
|
||||
size_t
|
||||
file_collection::other_file_format_count(file_format_t ff) const
|
||||
{
|
||||
|
@ -67,6 +67,7 @@ using safe_scan_progress = safe::Safe<scan_progress>;
|
||||
struct other_file_descriptor {
|
||||
file_format_t ofd_format;
|
||||
std::string ofd_description;
|
||||
std::vector<lnav::console::user_message> ofd_details;
|
||||
|
||||
other_file_descriptor(file_format_t format = file_format_t::UNKNOWN,
|
||||
std::string description = "")
|
||||
@ -188,15 +189,7 @@ struct file_collection {
|
||||
&& this->fc_other_files.empty();
|
||||
}
|
||||
|
||||
void clear()
|
||||
{
|
||||
this->fc_name_to_errors->writeAccess()->clear();
|
||||
this->fc_file_names.clear();
|
||||
this->fc_files.clear();
|
||||
this->fc_closed_files.clear();
|
||||
this->fc_other_files.clear();
|
||||
this->fc_new_stats.clear();
|
||||
}
|
||||
void clear();
|
||||
|
||||
bool is_below_open_file_limit() const
|
||||
{
|
||||
|
@ -39,17 +39,17 @@
|
||||
#include "config.h"
|
||||
#include "line_buffer.hh"
|
||||
|
||||
file_format_t
|
||||
detect_file_format_result
|
||||
detect_file_format(const std::filesystem::path& filename)
|
||||
{
|
||||
detect_file_format_result retval = {file_format_t::UNKNOWN};
|
||||
auto describe_res = archive_manager::describe(filename);
|
||||
if (describe_res.isOk()
|
||||
&& describe_res.unwrap().is<archive_manager::archive_info>())
|
||||
{
|
||||
return file_format_t::ARCHIVE;
|
||||
return {file_format_t::ARCHIVE};
|
||||
}
|
||||
|
||||
file_format_t retval = file_format_t::UNKNOWN;
|
||||
auto open_res = lnav::filesystem::open_file(filename, O_RDONLY);
|
||||
if (open_res.isErr()) {
|
||||
log_error("unable to open file for format detection: %s -- %s",
|
||||
@ -70,7 +70,7 @@ detect_file_format(const std::filesystem::path& filename)
|
||||
|
||||
if (header_frag.startswith(SQLITE3_HEADER)) {
|
||||
log_info("%s: appears to be a SQLite DB", filename.c_str());
|
||||
retval = file_format_t::SQLITE_DB;
|
||||
retval.dffr_file_format = file_format_t::SQLITE_DB;
|
||||
} else {
|
||||
auto looping = true;
|
||||
lnav::piper::multiplex_matcher mm;
|
||||
@ -118,7 +118,8 @@ detect_file_format(const std::filesystem::path& filename)
|
||||
log_info("%s: is multiplexed using %s",
|
||||
filename.c_str(),
|
||||
f.f_id.c_str());
|
||||
retval = file_format_t::MULTIPLEXED;
|
||||
retval.dffr_file_format
|
||||
= file_format_t::MULTIPLEXED;
|
||||
return false;
|
||||
},
|
||||
[](lnav::piper::multiplex_matcher::not_found nf) {
|
||||
@ -130,6 +131,7 @@ detect_file_format(const std::filesystem::path& filename)
|
||||
|
||||
next_range = li.li_file_range;
|
||||
}
|
||||
retval.dffr_details = std::move(mm.mm_details);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -32,10 +32,11 @@
|
||||
#ifndef lnav_file_format_hh
|
||||
#define lnav_file_format_hh
|
||||
|
||||
#include <filesystem>
|
||||
#include <optional>
|
||||
|
||||
#include "base/lnav.console.hh"
|
||||
#include "fmt/format.h"
|
||||
#include <filesystem>
|
||||
|
||||
enum class file_format_t : int {
|
||||
UNKNOWN,
|
||||
@ -51,7 +52,13 @@ struct external_file_format {
|
||||
std::filesystem::path eff_source_path;
|
||||
};
|
||||
|
||||
file_format_t detect_file_format(const std::filesystem::path& filename);
|
||||
struct detect_file_format_result {
|
||||
file_format_t dffr_file_format{file_format_t::UNKNOWN};
|
||||
std::vector<lnav::console::user_message> dffr_details;
|
||||
};
|
||||
|
||||
detect_file_format_result detect_file_format(
|
||||
const std::filesystem::path& filename);
|
||||
|
||||
std::optional<external_file_format> detect_mime_type(
|
||||
const std::filesystem::path& filename);
|
||||
|
@ -34,6 +34,9 @@
|
||||
#include "base/fs_util.hh"
|
||||
#include "base/humanize.hh"
|
||||
#include "base/humanize.network.hh"
|
||||
#include "base/humanize.time.hh"
|
||||
#include "base/itertools.enumerate.hh"
|
||||
#include "base/itertools.hh"
|
||||
#include "base/opt_util.hh"
|
||||
#include "base/string_util.hh"
|
||||
#include "config.h"
|
||||
@ -48,7 +51,7 @@ files_list_selection
|
||||
from_selection(vis_line_t sel_vis)
|
||||
{
|
||||
auto& fc = lnav_data.ld_active_files;
|
||||
int sel = (int) sel_vis;
|
||||
int sel = sel_vis;
|
||||
|
||||
{
|
||||
safe::ReadAccess<safe_name_to_errors> errs(*fc.fc_name_to_errors);
|
||||
@ -57,7 +60,8 @@ from_selection(vis_line_t sel_vis)
|
||||
auto iter = errs->begin();
|
||||
|
||||
std::advance(iter, sel);
|
||||
return error_selection::build(sel, iter->first);
|
||||
return error_selection::build(
|
||||
sel, std::make_pair(iter->first, iter->second.fei_description));
|
||||
}
|
||||
|
||||
sel -= errs->size();
|
||||
@ -83,7 +87,10 @@ from_selection(vis_line_t sel_vis)
|
||||
}
|
||||
} // namespace files_model
|
||||
|
||||
files_sub_source::files_sub_source() {}
|
||||
files_sub_source::
|
||||
files_sub_source()
|
||||
{
|
||||
}
|
||||
|
||||
bool
|
||||
files_sub_source::list_input_handle_key(listview_curses& lv, int ch)
|
||||
@ -181,11 +188,11 @@ files_sub_source::list_input_handle_key(listview_curses& lv, int ch)
|
||||
[&](files_model::error_selection& es) {
|
||||
auto& fc = lnav_data.ld_active_files;
|
||||
|
||||
fc.fc_file_names.erase(es.sb_iter);
|
||||
fc.fc_file_names.erase(es.sb_iter.first);
|
||||
|
||||
auto name_iter = fc.fc_file_names.begin();
|
||||
while (name_iter != fc.fc_file_names.end()) {
|
||||
if (name_iter->first == es.sb_iter) {
|
||||
if (name_iter->first == es.sb_iter.first) {
|
||||
name_iter = fc.fc_file_names.erase(name_iter);
|
||||
continue;
|
||||
}
|
||||
@ -196,7 +203,7 @@ files_sub_source::list_input_handle_key(listview_curses& lv, int ch)
|
||||
if (rp_opt) {
|
||||
auto rp = *rp_opt;
|
||||
|
||||
if (fmt::to_string(rp.home()) == es.sb_iter) {
|
||||
if (fmt::to_string(rp.home()) == es.sb_iter.first) {
|
||||
fc.fc_other_files.erase(name_iter->first);
|
||||
name_iter = fc.fc_file_names.erase(name_iter);
|
||||
continue;
|
||||
@ -205,7 +212,8 @@ files_sub_source::list_input_handle_key(listview_curses& lv, int ch)
|
||||
++name_iter;
|
||||
}
|
||||
|
||||
fc.fc_name_to_errors->writeAccess()->erase(es.sb_iter);
|
||||
fc.fc_name_to_errors->writeAccess()->erase(
|
||||
es.sb_iter.first);
|
||||
fc.fc_invalidate_merge = true;
|
||||
lv.reload_data();
|
||||
},
|
||||
@ -246,8 +254,10 @@ files_sub_source::text_value_for_line(textview_curses& tc,
|
||||
std::string& value_out,
|
||||
text_sub_source::line_flags_t flags)
|
||||
{
|
||||
bool selected
|
||||
= lnav_data.ld_mode == ln_mode_t::FILES && line == tc.get_selection();
|
||||
bool selected = line == tc.get_selection();
|
||||
role_t cursor_role = lnav_data.ld_mode == ln_mode_t::FILES
|
||||
? role_t::VCR_CURSOR_LINE
|
||||
: role_t::VCR_DISABLED_CURSOR_LINE;
|
||||
const auto dim = tc.get_dimensions();
|
||||
const auto& fc = lnav_data.ld_active_files;
|
||||
auto filename_width
|
||||
@ -280,8 +290,7 @@ files_sub_source::text_value_for_line(textview_curses& tc,
|
||||
}
|
||||
al.append(" ").append(iter->second.fei_description);
|
||||
if (selected) {
|
||||
al.with_attr_for_all(
|
||||
VC_ROLE.value(role_t::VCR_DISABLED_FOCUSED));
|
||||
al.with_attr_for_all(VC_ROLE.value(cursor_role));
|
||||
}
|
||||
|
||||
value_out = al.get_string();
|
||||
@ -308,7 +317,7 @@ files_sub_source::text_value_for_line(textview_curses& tc,
|
||||
.append(" ")
|
||||
.append(iter->second.ofd_description);
|
||||
if (selected) {
|
||||
al.with_attr_for_all(VC_ROLE.value(role_t::VCR_DISABLED_FOCUSED));
|
||||
al.with_attr_for_all(VC_ROLE.value(cursor_role));
|
||||
}
|
||||
if (line == fc.fc_other_files.size() - 1) {
|
||||
al.with_attr_for_all(VC_STYLE.value(text_attrs{A_UNDERLINE}));
|
||||
@ -323,13 +332,8 @@ files_sub_source::text_value_for_line(textview_curses& tc,
|
||||
const auto& lf = fc.fc_files[line];
|
||||
auto ld_opt = lnav_data.ld_log_source.find_data(lf);
|
||||
auto fn = fmt::to_string(std::filesystem::path(lf->get_unique_path()));
|
||||
char start_time[64] = "", end_time[64] = "";
|
||||
std::vector<std::string> file_notes;
|
||||
|
||||
if (lf->get_format() != nullptr) {
|
||||
sql_strftime(start_time, sizeof(start_time), lf->front().get_timeval());
|
||||
sql_strftime(end_time, sizeof(end_time), lf->back().get_timeval());
|
||||
}
|
||||
truncate_to(fn, filename_width);
|
||||
for (const auto& pair : lf->get_notes()) {
|
||||
file_notes.push_back(pair.second);
|
||||
@ -355,19 +359,13 @@ files_sub_source::text_value_for_line(textview_curses& tc,
|
||||
humanize::file_size(lf->get_index_size(),
|
||||
humanize::alignment::columnar));
|
||||
}
|
||||
al.append(" ")
|
||||
.append(start_time)
|
||||
.append(" \u2014 ")
|
||||
.append(end_time)
|
||||
.append(" ")
|
||||
.appendf(FMT_STRING("{}"), fmt::join(file_notes, "; "));
|
||||
al.append(" ").appendf(FMT_STRING("{}"), fmt::join(file_notes, "; "));
|
||||
if (selected) {
|
||||
al.with_attr_for_all(VC_ROLE.value(role_t::VCR_FOCUSED));
|
||||
al.with_attr_for_all(VC_ROLE.value(cursor_role));
|
||||
}
|
||||
|
||||
value_out = al.get_string();
|
||||
this->fss_last_line_len
|
||||
= filename_width + 23 + strlen(start_time) + strlen(end_time);
|
||||
this->fss_last_line_len = value_out.length();
|
||||
}
|
||||
|
||||
void
|
||||
@ -455,3 +453,179 @@ files_sub_source::text_handle_mouse(
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
files_sub_source::text_selection_changed(textview_curses& tc)
|
||||
{
|
||||
auto sel = files_model::from_selection(tc.get_selection());
|
||||
std::vector<attr_line_t> details;
|
||||
|
||||
sel.match(
|
||||
[](files_model::no_selection) {},
|
||||
|
||||
[&details](const files_model::error_selection& es) {
|
||||
auto path = std::filesystem::path(es.sb_iter.first);
|
||||
|
||||
details.emplace_back(
|
||||
attr_line_t().appendf(FMT_STRING("Full path: {}"), path));
|
||||
details.emplace_back(attr_line_t(" ").append(
|
||||
lnav::roles::error(es.sb_iter.second)));
|
||||
},
|
||||
[&details](const files_model::other_selection& os) {
|
||||
auto path = std::filesystem::path(os.sb_iter->first);
|
||||
|
||||
details.emplace_back(
|
||||
attr_line_t().appendf(FMT_STRING("Full path: {}"), path));
|
||||
details.emplace_back(attr_line_t(" ").append("Match Details"_h3));
|
||||
for (const auto& msg : os.sb_iter->second.ofd_details) {
|
||||
auto msg_al = msg.to_attr_line();
|
||||
|
||||
for (auto& msg_line : msg_al.rtrim().split_lines()) {
|
||||
msg_line.insert(0, 4, ' ');
|
||||
details.emplace_back(msg_line);
|
||||
}
|
||||
}
|
||||
},
|
||||
[&details](const files_model::file_selection& fs) {
|
||||
static constexpr auto NAME_WIDTH = 17;
|
||||
|
||||
auto lf = *fs.sb_iter;
|
||||
auto path = lf->get_filename();
|
||||
auto actual_path = lf->get_actual_path();
|
||||
auto format = lf->get_format();
|
||||
|
||||
details.emplace_back(
|
||||
attr_line_t()
|
||||
.appendf(FMT_STRING("{}"), path)
|
||||
.with_attr_for_all(VC_ROLE.value(role_t::VCR_IDENTIFIER)));
|
||||
const auto notes = lf->get_notes();
|
||||
if (!notes.empty()) {
|
||||
details.emplace_back(
|
||||
attr_line_t(" ").append("Notes"_h2).append(":"));
|
||||
for (const auto& pair : notes) {
|
||||
details.emplace_back(attr_line_t(" ").append(
|
||||
lnav::roles::warning(pair.second)));
|
||||
}
|
||||
}
|
||||
details.emplace_back(attr_line_t(" ").append("General"_h2));
|
||||
if (actual_path) {
|
||||
if (path != actual_path.value()) {
|
||||
auto line = attr_line_t()
|
||||
.append("Actual Path"_h3)
|
||||
.right_justify(NAME_WIDTH)
|
||||
.append(": ")
|
||||
.append(lnav::roles::file(
|
||||
fmt::to_string(actual_path.value())));
|
||||
details.emplace_back(line);
|
||||
}
|
||||
} else {
|
||||
details.emplace_back(attr_line_t().append(" Piped"));
|
||||
}
|
||||
details.emplace_back(
|
||||
attr_line_t()
|
||||
.append("MIME Type"_h3)
|
||||
.right_justify(NAME_WIDTH)
|
||||
.append(": ")
|
||||
.append(fmt::to_string(lf->get_text_format())));
|
||||
details.emplace_back(
|
||||
attr_line_t()
|
||||
.append("Last Modified"_h3)
|
||||
.right_justify(NAME_WIDTH)
|
||||
.append(": ")
|
||||
.append(lnav::to_rfc3339_string(
|
||||
convert_log_time_to_local(lf->get_modified_time()),
|
||||
0,
|
||||
'T')));
|
||||
details.emplace_back(
|
||||
attr_line_t()
|
||||
.append("Size"_h3)
|
||||
.right_justify(NAME_WIDTH)
|
||||
.append(": ")
|
||||
.append(humanize::file_size(lf->get_index_size(),
|
||||
humanize::alignment::none)));
|
||||
|
||||
details.emplace_back(attr_line_t()
|
||||
.append("Lines"_h3)
|
||||
.right_justify(NAME_WIDTH)
|
||||
.append(": ")
|
||||
.append(lnav::roles::number(fmt::format(
|
||||
FMT_STRING("{:L}"), lf->size()))));
|
||||
if (format != nullptr) {
|
||||
details.emplace_back(attr_line_t()
|
||||
.append("Time Range"_h3)
|
||||
.right_justify(NAME_WIDTH)
|
||||
.append(": ")
|
||||
.append(lnav::to_rfc3339_string(
|
||||
lf->front().get_timeval(), 'T'))
|
||||
.append(" - ")
|
||||
.append(lnav::to_rfc3339_string(
|
||||
lf->back().get_timeval(), 'T')));
|
||||
details.emplace_back(
|
||||
attr_line_t()
|
||||
.append("Duration"_h3)
|
||||
.right_justify(NAME_WIDTH)
|
||||
.append(": ")
|
||||
.append(humanize::time::duration::from_tv(
|
||||
lf->back().get_timeval()
|
||||
- lf->front().get_timeval())
|
||||
.to_string()));
|
||||
}
|
||||
{
|
||||
auto line
|
||||
= attr_line_t(" ").append("Log Format"_h2).append(": ");
|
||||
|
||||
if (format != nullptr) {
|
||||
line.append(lnav::roles::identifier(
|
||||
format->get_name().to_string()));
|
||||
} else {
|
||||
line.append("(none)"_comment);
|
||||
}
|
||||
details.emplace_back(line);
|
||||
}
|
||||
auto match_msgs = lf->get_format_match_messages();
|
||||
|
||||
details.emplace_back(
|
||||
attr_line_t(" ").append("Match Details"_h3));
|
||||
for (const auto& msg : match_msgs) {
|
||||
auto msg_al = msg.to_attr_line();
|
||||
|
||||
for (auto& msg_line : msg_al.rtrim().split_lines()) {
|
||||
msg_line.insert(0, 6, ' ');
|
||||
details.emplace_back(msg_line);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
const auto& meta = lf->get_embedded_metadata();
|
||||
|
||||
if (!meta.empty()) {
|
||||
details.emplace_back(
|
||||
attr_line_t(" ").append("Embedded Metadata:"_h2));
|
||||
for (const auto& [index, meta_pair] :
|
||||
lnav::itertools::enumerate(meta))
|
||||
{
|
||||
details.emplace_back(
|
||||
attr_line_t(" ")
|
||||
.appendf(FMT_STRING("[{}]"), index)
|
||||
.append(" ")
|
||||
.append(lnav::roles::h3(meta_pair.first)));
|
||||
details.emplace_back(
|
||||
attr_line_t(" MIME Type: ")
|
||||
.append(lnav::roles::symbol(fmt::to_string(
|
||||
meta_pair.second.m_format))));
|
||||
line_range lr{6, -1};
|
||||
details.emplace_back(attr_line_t().with_attr(
|
||||
string_attr{lr, VC_GRAPHIC.value(ACS_HLINE)}));
|
||||
const auto val_sf = string_fragment::from_str(
|
||||
meta_pair.second.m_value);
|
||||
for (const auto val_line : val_sf.split_lines()) {
|
||||
details.emplace_back(
|
||||
attr_line_t(" ").append(val_line));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
this->fss_details_source->replace_with(details);
|
||||
}
|
||||
|
@ -31,6 +31,7 @@
|
||||
#define files_sub_source_hh
|
||||
|
||||
#include "file_collection.hh"
|
||||
#include "plain_text_source.hh"
|
||||
#include "textview_curses.hh"
|
||||
|
||||
class files_sub_source
|
||||
@ -65,8 +66,11 @@ public:
|
||||
const listview_curses::display_line_content_t&,
|
||||
mouse_event& me) override;
|
||||
|
||||
void text_selection_changed(textview_curses& tc) override;
|
||||
|
||||
size_t fss_last_line_len{0};
|
||||
attr_line_t fss_curr_line;
|
||||
plain_text_source* fss_details_source{nullptr};
|
||||
};
|
||||
|
||||
struct files_overlay_source : public list_overlay_source {
|
||||
@ -95,7 +99,9 @@ struct selection_base {
|
||||
}
|
||||
};
|
||||
|
||||
struct error_selection : public selection_base<error_selection, std::string> {};
|
||||
struct error_selection
|
||||
: public selection_base<error_selection,
|
||||
std::pair<std::string, std::string>> {};
|
||||
|
||||
struct other_selection
|
||||
: public selection_base<
|
||||
|
@ -47,8 +47,10 @@ static auto DELETE_HELP = ANSI_BOLD("D") ": Delete";
|
||||
static auto FILTERING_HELP = ANSI_BOLD("f") ": ";
|
||||
static auto JUMP_HELP = ANSI_BOLD("ENTER") ": Jump To";
|
||||
static auto CLOSE_HELP = ANSI_BOLD("X") ": Close";
|
||||
static auto FOCUS_DETAILS_HELP = ANSI_BOLD("CTRL-]") ": Focus on details view";
|
||||
|
||||
filter_status_source::filter_status_source()
|
||||
filter_status_source::
|
||||
filter_status_source()
|
||||
{
|
||||
this->tss_fields[TSF_TITLE].set_width(14);
|
||||
this->tss_fields[TSF_TITLE].set_role(role_t::VCR_STATUS_TITLE);
|
||||
@ -102,6 +104,7 @@ filter_status_source::statusview_fields()
|
||||
break;
|
||||
case ln_mode_t::FILTER:
|
||||
case ln_mode_t::FILES:
|
||||
case ln_mode_t::FILE_DETAILS:
|
||||
this->tss_fields[TSF_HELP].set_value(EXIT_MSG);
|
||||
break;
|
||||
default:
|
||||
@ -110,6 +113,7 @@ filter_status_source::statusview_fields()
|
||||
}
|
||||
|
||||
if (lnav_data.ld_mode == ln_mode_t::FILES
|
||||
|| lnav_data.ld_mode == ln_mode_t::FILE_DETAILS
|
||||
|| lnav_data.ld_mode == ln_mode_t::SEARCH_FILES)
|
||||
{
|
||||
this->tss_fields[TSF_FILES_TITLE].set_value(
|
||||
@ -231,7 +235,8 @@ filter_status_source::update_filtered(text_sub_source* tss)
|
||||
}
|
||||
}
|
||||
|
||||
filter_help_status_source::filter_help_status_source()
|
||||
filter_help_status_source::
|
||||
filter_help_status_source()
|
||||
{
|
||||
this->fss_help.set_min_width(10);
|
||||
this->fss_help.set_share(1);
|
||||
@ -292,10 +297,11 @@ filter_help_status_source::statusview_fields()
|
||||
tss->tss_apply_filters ? "Disable Filtering"
|
||||
: "Enable Filtering");
|
||||
}
|
||||
} else if (lnav_data.ld_mode == ln_mode_t::FILES
|
||||
} else if ((lnav_data.ld_mode == ln_mode_t::FILES
|
||||
|| lnav_data.ld_mode == ln_mode_t::FILE_DETAILS)
|
||||
&& lnav_data.ld_session_loaded)
|
||||
{
|
||||
auto& lv = lnav_data.ld_files_view;
|
||||
const auto& lv = lnav_data.ld_files_view;
|
||||
auto sel = files_model::from_selection(lv.get_selection());
|
||||
|
||||
sel.match(
|
||||
@ -313,9 +319,16 @@ filter_help_status_source::statusview_fields()
|
||||
if (ld_opt && !ld_opt.value()->ld_visible) {
|
||||
vis_help = "Show";
|
||||
}
|
||||
const auto* focus_details_help
|
||||
= lnav_data.ld_mode == ln_mode_t::FILES
|
||||
? FOCUS_DETAILS_HELP
|
||||
: "";
|
||||
|
||||
this->fss_help.set_value(
|
||||
" %s%s %s", ENABLE_HELP, vis_help, JUMP_HELP);
|
||||
this->fss_help.set_value(" %s%s %s %s",
|
||||
ENABLE_HELP,
|
||||
vis_help,
|
||||
JUMP_HELP,
|
||||
focus_details_help);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
@ -29,7 +29,6 @@ FORMAT_FILES = \
|
||||
$(srcdir)/%reldir%/openamdb_log.json \
|
||||
$(srcdir)/%reldir%/openstack_log.json \
|
||||
$(srcdir)/%reldir%/page_log.json \
|
||||
$(srcdir)/%reldir%/papertrail_log.json \
|
||||
$(srcdir)/%reldir%/pcap_log.json \
|
||||
$(srcdir)/%reldir%/procstate_log.json \
|
||||
$(srcdir)/%reldir%/redis_log.json \
|
||||
|
@ -1,52 +0,0 @@
|
||||
{
|
||||
"$schema": "https://lnav.org/schemas/format-v1.schema.json",
|
||||
"papertrail_log": {
|
||||
"title": "Papertrail Service",
|
||||
"url": "https://papertrailapp.com/",
|
||||
"description": "Log format for the papertrail log management service",
|
||||
"json": true,
|
||||
"hide-extra": true,
|
||||
"file-pattern": "pt:.*",
|
||||
"line-format": [
|
||||
{
|
||||
"field": "display_received_at"
|
||||
},
|
||||
" ",
|
||||
{
|
||||
"field": "hostname"
|
||||
},
|
||||
" ",
|
||||
{
|
||||
"field": "program"
|
||||
},
|
||||
": ",
|
||||
{
|
||||
"field": "message"
|
||||
}
|
||||
],
|
||||
"level-field": "severity",
|
||||
"level": {
|
||||
"error": "Error",
|
||||
"debug": "Debug",
|
||||
"warning": "Warning",
|
||||
"info": "Info(?:rmational)?|Notice",
|
||||
"critical": "Crit(?:ical)?",
|
||||
"fatal": "Emerg(?:ency)?|Alert"
|
||||
},
|
||||
"timestamp-field": "generated_at",
|
||||
"body-field": "message",
|
||||
"value": {
|
||||
"display_received_at": {
|
||||
"kind": "string"
|
||||
},
|
||||
"program": {
|
||||
"kind": "string",
|
||||
"identifier": true
|
||||
},
|
||||
"hostname": {
|
||||
"kind": "string",
|
||||
"identifier": true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -244,6 +244,13 @@ line_buffer::gz_indexed::open(int fd, lnav::gzip::header& hd)
|
||||
hd.h_mtime.tv_sec = gz_hd.time;
|
||||
hd.h_name = std::string((char*) name);
|
||||
hd.h_comment = std::string((char*) comment);
|
||||
log_info(
|
||||
"%d: read gzip header (mtime=%d; name='%s'; "
|
||||
"comment='%s')",
|
||||
fd,
|
||||
hd.h_mtime.tv_sec,
|
||||
hd.h_name.c_str(),
|
||||
hd.h_comment.c_str());
|
||||
break;
|
||||
default:
|
||||
log_error("%d: failed to read gzip header data", fd);
|
||||
|
@ -429,7 +429,7 @@ listview_curses::do_update()
|
||||
}
|
||||
|
||||
wrap_width = width;
|
||||
if (this->lv_show_scrollbar) {
|
||||
if (this->lv_show_scrollbar && wrap_width > 0) {
|
||||
wrap_width -= 1;
|
||||
}
|
||||
|
||||
@ -541,6 +541,7 @@ listview_curses::do_update()
|
||||
row_overlay_content[overlay_row].with_attr_for_all(
|
||||
VC_ROLE.value(role_t::VCR_CURSOR_LINE));
|
||||
}
|
||||
|
||||
this->lv_display_lines.push_back(
|
||||
overlay_content{row, overlay_row});
|
||||
mvwattrline(this->lv_window,
|
||||
|
54
src/lnav.cc
54
src/lnav.cc
@ -689,7 +689,21 @@ handle_config_ui_key(int ch, const char* keyseq)
|
||||
|
||||
switch (lnav_data.ld_mode) {
|
||||
case ln_mode_t::FILES:
|
||||
retval = lnav_data.ld_files_view.handle_key(ch);
|
||||
if (ch == KEY_CTRL(']')) {
|
||||
set_view_mode(ln_mode_t::FILE_DETAILS);
|
||||
lnav_data.ld_mode = ln_mode_t::FILE_DETAILS;
|
||||
retval = true;
|
||||
} else {
|
||||
retval = lnav_data.ld_files_view.handle_key(ch);
|
||||
}
|
||||
break;
|
||||
case ln_mode_t::FILE_DETAILS:
|
||||
if (ch == KEY_ESCAPE || ch == KEY_CTRL(']')) {
|
||||
set_view_mode(ln_mode_t::FILES);
|
||||
retval = true;
|
||||
} else {
|
||||
retval = lnav_data.ld_file_details_view.handle_key(ch);
|
||||
}
|
||||
break;
|
||||
case ln_mode_t::FILTER:
|
||||
retval = lnav_data.ld_filter_view.handle_key(ch);
|
||||
@ -727,6 +741,7 @@ handle_config_ui_key(int ch, const char* keyseq)
|
||||
}
|
||||
lnav_data.ld_mode = new_mode.value();
|
||||
lnav_data.ld_files_view.reload_data();
|
||||
lnav_data.ld_file_details_view.reload_data();
|
||||
lnav_data.ld_filter_view.reload_data();
|
||||
lnav_data.ld_status[LNS_FILTER].set_needs_update();
|
||||
} else {
|
||||
@ -760,6 +775,7 @@ handle_key(int ch, const char* keyseq)
|
||||
|
||||
case ln_mode_t::FILTER:
|
||||
case ln_mode_t::FILES:
|
||||
case ln_mode_t::FILE_DETAILS:
|
||||
return handle_config_ui_key(ch, keyseq);
|
||||
|
||||
case ln_mode_t::SPECTRO_DETAILS: {
|
||||
@ -1349,6 +1365,18 @@ VALUES ('org.lnav.mouse-support', -1, DATETIME('now', '+1 minute'),
|
||||
highlight_source_t::THEME);
|
||||
lnav_data.ld_files_view.set_overlay_source(&lnav_data.ld_files_overlay);
|
||||
|
||||
lnav_data.ld_file_details_view.set_title("File Details");
|
||||
lnav_data.ld_file_details_view.set_selectable(true);
|
||||
lnav_data.ld_file_details_view.set_window(lnav_data.ld_window);
|
||||
lnav_data.ld_file_details_view.set_show_scrollbar(true);
|
||||
lnav_data.ld_file_details_view.set_supports_marks(true);
|
||||
lnav_data.ld_file_details_view.get_disabled_highlights().insert(
|
||||
highlight_source_t::THEME);
|
||||
lnav_data.ld_file_details_view.tc_cursor_role
|
||||
= role_t::VCR_DISABLED_CURSOR_LINE;
|
||||
lnav_data.ld_file_details_view.tc_disabled_cursor_role
|
||||
= role_t::VCR_DISABLED_CURSOR_LINE;
|
||||
|
||||
lnav_data.ld_user_message_view.set_window(lnav_data.ld_window);
|
||||
|
||||
lnav_data.ld_spectro_details_view.set_title("spectro-details");
|
||||
@ -1674,8 +1702,11 @@ VALUES ('org.lnav.mouse-support', -1, DATETIME('now', '+1 minute'),
|
||||
break;
|
||||
case ln_mode_t::SEARCH_FILES:
|
||||
case ln_mode_t::FILES:
|
||||
case ln_mode_t::FILE_DETAILS:
|
||||
lnav_data.ld_files_view.set_needs_update();
|
||||
lnav_data.ld_file_details_view.set_needs_update();
|
||||
lnav_data.ld_files_view.do_update();
|
||||
lnav_data.ld_file_details_view.do_update();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
@ -1785,6 +1816,7 @@ VALUES ('org.lnav.mouse-support', -1, DATETIME('now', '+1 minute'),
|
||||
case ln_mode_t::PAGING:
|
||||
case ln_mode_t::FILTER:
|
||||
case ln_mode_t::FILES:
|
||||
case ln_mode_t::FILE_DETAILS:
|
||||
case ln_mode_t::SPECTRO_DETAILS:
|
||||
case ln_mode_t::BUSY:
|
||||
if (old_gen
|
||||
@ -2054,6 +2086,10 @@ VALUES ('org.lnav.mouse-support', -1, DATETIME('now', '+1 minute'),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (rescan_future.valid()) {
|
||||
rescan_future.get();
|
||||
}
|
||||
} catch (readline_curses::error& e) {
|
||||
log_error("error: %s", strerror(e.e_err));
|
||||
}
|
||||
@ -2303,13 +2339,19 @@ main(int argc, char* argv[])
|
||||
SELECT tbl_name FROM sqlite_master WHERE sql LIKE 'CREATE VIRTUAL TABLE%'
|
||||
)";
|
||||
|
||||
log_info("performing cleanup");
|
||||
lnav_data.ld_child_pollers.clear();
|
||||
|
||||
log_info("marking files as closed");
|
||||
for (auto& lf : lnav_data.ld_active_files.fc_files) {
|
||||
lf->close();
|
||||
}
|
||||
log_info("rebuilding after closures");
|
||||
rebuild_indexes(ui_clock::now());
|
||||
log_info("clearing file collection");
|
||||
lnav_data.ld_active_files.clear();
|
||||
|
||||
log_info("dropping tables");
|
||||
lnav_data.ld_vtab_manager = nullptr;
|
||||
|
||||
std::vector<std::string> tables_to_drop;
|
||||
@ -2367,6 +2409,8 @@ SELECT tbl_name FROM sqlite_master WHERE sql LIKE 'CREATE VIRTUAL TABLE%'
|
||||
#endif
|
||||
|
||||
lnav_data.ld_db.reset();
|
||||
|
||||
log_info("cleanup finished");
|
||||
});
|
||||
|
||||
#ifdef HAVE_LIBCURL
|
||||
@ -2882,8 +2926,14 @@ SELECT tbl_name FROM sqlite_master WHERE sql LIKE 'CREATE VIRTUAL TABLE%'
|
||||
.add_input_delegate(*filter_source);
|
||||
lnav_data.ld_files_view.set_sub_source(&lnav_data.ld_files_source)
|
||||
.add_input_delegate(lnav_data.ld_files_source);
|
||||
lnav_data.ld_file_details_view.set_sub_source(
|
||||
&lnav_data.ld_file_details_source);
|
||||
lnav_data.ld_files_source.fss_details_source
|
||||
= &lnav_data.ld_file_details_source;
|
||||
lnav_data.ld_user_message_view.set_sub_source(
|
||||
&lnav_data.ld_user_message_source);
|
||||
auto overlay_menu = std::make_shared<text_overlay_menu>();
|
||||
lnav_data.ld_file_details_view.set_overlay_source(overlay_menu.get());
|
||||
|
||||
for (int lpc = 0; lpc < LNV__MAX; lpc++) {
|
||||
lnav_data.ld_views[lpc].set_gutter_source(new log_gutter_source());
|
||||
@ -3541,6 +3591,8 @@ SELECT tbl_name FROM sqlite_master WHERE sql LIKE 'CREATE VIRTUAL TABLE%'
|
||||
|
||||
save_session();
|
||||
}
|
||||
|
||||
log_info("exiting main loop");
|
||||
} catch (const std::system_error& e) {
|
||||
if (e.code().value() != EPIPE) {
|
||||
fprintf(stderr, "error: %s\n", e.what());
|
||||
|
@ -197,8 +197,10 @@ struct lnav_data_t {
|
||||
textview_curses ld_doc_view;
|
||||
textview_curses ld_filter_view;
|
||||
files_sub_source ld_files_source;
|
||||
plain_text_source ld_file_details_source;
|
||||
files_overlay_source ld_files_overlay;
|
||||
textview_curses ld_files_view;
|
||||
textview_curses ld_file_details_view;
|
||||
plain_text_source ld_example_source;
|
||||
textview_curses ld_example_view;
|
||||
plain_text_source ld_match_source;
|
||||
|
@ -3352,7 +3352,7 @@ com_open(exec_context& ec, std::string cmdline, std::vector<std::string>& args)
|
||||
attr_line_t al;
|
||||
attr_line_builder alb(al);
|
||||
|
||||
switch (detect_res) {
|
||||
switch (detect_res.dffr_file_format) {
|
||||
case file_format_t::ARCHIVE: {
|
||||
auto describe_res = archive_manager::describe(fn);
|
||||
|
||||
@ -3450,7 +3450,7 @@ com_open(exec_context& ec, std::string cmdline, std::vector<std::string>& args)
|
||||
break;
|
||||
}
|
||||
case file_format_t::SQLITE_DB: {
|
||||
alb.append(fmt::to_string(detect_res));
|
||||
alb.append(fmt::to_string(detect_res.dffr_file_format));
|
||||
break;
|
||||
}
|
||||
case file_format_t::REMOTE: {
|
||||
|
@ -333,9 +333,10 @@ logline_value::origin_in_full_msg(const char* msg, ssize_t len) const
|
||||
return retval;
|
||||
}
|
||||
|
||||
logline_value::logline_value(logline_value_meta lvm,
|
||||
shared_buffer_ref& sbr,
|
||||
struct line_range origin)
|
||||
logline_value::
|
||||
logline_value(logline_value_meta lvm,
|
||||
shared_buffer_ref& sbr,
|
||||
struct line_range origin)
|
||||
: lv_meta(std::move(lvm)), lv_origin(origin)
|
||||
{
|
||||
if (sbr.get_data() == nullptr) {
|
||||
@ -4028,16 +4029,24 @@ external_log_format::specialized(int fmt_lock)
|
||||
return retval;
|
||||
}
|
||||
|
||||
bool
|
||||
log_format::match_name_result
|
||||
external_log_format::match_name(const std::string& filename)
|
||||
{
|
||||
if (this->elf_filename_pcre.pp_value == nullptr) {
|
||||
return true;
|
||||
return name_matched{};
|
||||
}
|
||||
|
||||
return this->elf_filename_pcre.pp_value->find_in(filename)
|
||||
.ignore_error()
|
||||
.has_value();
|
||||
if (this->elf_filename_pcre.pp_value->find_in(filename)
|
||||
.ignore_error()
|
||||
.has_value())
|
||||
{
|
||||
return name_matched{};
|
||||
}
|
||||
|
||||
return name_mismatched{
|
||||
this->elf_filename_pcre.pp_value->match_partial(filename),
|
||||
this->elf_filename_pcre.pp_value->get_pattern(),
|
||||
};
|
||||
}
|
||||
|
||||
auto
|
||||
@ -4294,8 +4303,8 @@ log_format::find_root_format(const char* name)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
log_format::pattern_for_lines::pattern_for_lines(uint32_t pfl_line,
|
||||
uint32_t pfl_pat_index)
|
||||
log_format::pattern_for_lines::
|
||||
pattern_for_lines(uint32_t pfl_line, uint32_t pfl_pat_index)
|
||||
: pfl_line(pfl_line), pfl_pat_index(pfl_pat_index)
|
||||
{
|
||||
}
|
||||
|
@ -406,7 +406,19 @@ public:
|
||||
*/
|
||||
virtual const intern_string_t get_name() const = 0;
|
||||
|
||||
virtual bool match_name(const std::string& filename) { return true; }
|
||||
struct name_matched {};
|
||||
struct name_mismatched {
|
||||
size_t nm_partial;
|
||||
std::string nm_pattern;
|
||||
};
|
||||
|
||||
using match_name_result
|
||||
= mapbox::util::variant<name_matched, name_mismatched>;
|
||||
|
||||
virtual match_name_result match_name(const std::string& filename)
|
||||
{
|
||||
return name_matched{};
|
||||
}
|
||||
|
||||
struct scan_match {
|
||||
uint32_t sm_quality;
|
||||
|
@ -140,7 +140,7 @@ public:
|
||||
|
||||
const intern_string_t get_name() const override { return this->elf_name; }
|
||||
|
||||
bool match_name(const std::string& filename) override;
|
||||
match_name_result match_name(const std::string& filename) override;
|
||||
|
||||
scan_result_t scan(logfile& lf,
|
||||
std::vector<logline>& dst,
|
||||
@ -446,7 +446,6 @@ public:
|
||||
bool jlf_hide_extra{false};
|
||||
std::vector<json_format_element> jlf_line_format;
|
||||
int jlf_line_format_init_count{0};
|
||||
shared_buffer jlf_share_manager;
|
||||
logline_value_vector jlf_line_values;
|
||||
|
||||
off_t jlf_cached_offset{-1};
|
||||
@ -457,6 +456,7 @@ public:
|
||||
string_attrs_t jlf_line_attrs;
|
||||
std::shared_ptr<yajlpp_parse_context> jlf_parse_context;
|
||||
std::shared_ptr<yajl_handle_t> jlf_yajl_handle;
|
||||
shared_buffer jlf_share_manager;
|
||||
|
||||
private:
|
||||
const intern_string_t elf_name;
|
||||
|
@ -45,6 +45,7 @@
|
||||
#include "base/date_time_scanner.cfg.hh"
|
||||
#include "base/fs_util.hh"
|
||||
#include "base/injector.hh"
|
||||
#include "base/snippet_highlighters.hh"
|
||||
#include "base/string_util.hh"
|
||||
#include "config.h"
|
||||
#include "file_options.hh"
|
||||
@ -56,6 +57,8 @@
|
||||
#include "piper.looper.hh"
|
||||
#include "yajlpp/yajlpp_def.hh"
|
||||
|
||||
using namespace lnav::roles::literals;
|
||||
|
||||
static auto intern_lifetime = intern_string::get_table_lifetime();
|
||||
|
||||
static const size_t INDEX_RESERVE_INCREMENT = 1024;
|
||||
@ -140,6 +143,7 @@ logfile::open(std::filesystem::path filename,
|
||||
lf->lf_indexing = lf->lf_options.loo_is_visible;
|
||||
lf->lf_text_format
|
||||
= lf->lf_options.loo_text_format.value_or(text_format_t::TF_UNKNOWN);
|
||||
lf->lf_format_match_messages = loo.loo_match_details;
|
||||
|
||||
const auto& hdr = lf->lf_line_buffer.get_header_data();
|
||||
if (hdr.valid()) {
|
||||
@ -149,7 +153,9 @@ logfile::open(std::filesystem::path filename,
|
||||
if (!gzhdr.empty()) {
|
||||
lf->lf_embedded_metadata["net.zlib.gzip.header"] = {
|
||||
text_format_t::TF_JSON,
|
||||
file_header_handlers.to_string(gzhdr),
|
||||
file_header_handlers.formatter_for(gzhdr)
|
||||
.with_config(yajl_gen_beautify, 1)
|
||||
.to_string(),
|
||||
};
|
||||
}
|
||||
},
|
||||
@ -159,7 +165,9 @@ logfile::open(std::filesystem::path filename,
|
||||
|
||||
lf->lf_embedded_metadata["org.lnav.piper.header"] = {
|
||||
text_format_t::TF_JSON,
|
||||
lnav::piper::header_handlers.to_string(phdr),
|
||||
lnav::piper::header_handlers.formatter_for(phdr)
|
||||
.with_config(yajl_gen_beautify, 1)
|
||||
.to_string(),
|
||||
};
|
||||
log_info("setting file name from piper header: %s",
|
||||
phdr.h_name.c_str());
|
||||
@ -196,14 +204,15 @@ logfile::open(std::filesystem::path filename,
|
||||
return Ok(lf);
|
||||
}
|
||||
|
||||
logfile::logfile(std::filesystem::path filename,
|
||||
const logfile_open_options& loo)
|
||||
logfile::
|
||||
logfile(std::filesystem::path filename, const logfile_open_options& loo)
|
||||
: lf_filename(std::move(filename)), lf_options(loo)
|
||||
{
|
||||
this->lf_opids.writeAccess()->los_opid_ranges.reserve(64);
|
||||
}
|
||||
|
||||
logfile::~logfile()
|
||||
logfile::~
|
||||
logfile()
|
||||
{
|
||||
log_info("destructing logfile: %s", this->lf_filename.c_str());
|
||||
}
|
||||
@ -360,12 +369,33 @@ logfile::process_prefix(shared_buffer_ref& sbr,
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!curr->match_name(this->lf_filename)) {
|
||||
auto match_res = curr->match_name(this->lf_filename);
|
||||
if (match_res.is<log_format::name_mismatched>()) {
|
||||
auto nm = match_res.get<log_format::name_mismatched>();
|
||||
if (li.li_file_range.fr_offset == 0) {
|
||||
log_debug("(%s) does not match file name: %s",
|
||||
curr->get_name().get(),
|
||||
this->lf_filename.c_str());
|
||||
}
|
||||
auto regex_al = attr_line_t(nm.nm_pattern);
|
||||
lnav::snippets::regex_highlighter(
|
||||
regex_al, -1, line_range{0, (int) regex_al.length()});
|
||||
auto note = attr_line_t("pattern: ")
|
||||
.append(regex_al)
|
||||
.append("\n ")
|
||||
.append(lnav::roles::quoted_code(
|
||||
fmt::to_string(this->get_filename())))
|
||||
.append("\n")
|
||||
.append(nm.nm_partial + 2, ' ')
|
||||
.append("^ matched up to here"_snippet_border);
|
||||
auto match_um = lnav::console::user_message::info(
|
||||
attr_line_t()
|
||||
.append(lnav::roles::identifier(
|
||||
curr->get_name().to_string()))
|
||||
.append(" file name pattern required "
|
||||
"by format does not match"))
|
||||
.with_note(note);
|
||||
this->lf_format_match_messages.emplace_back(match_um);
|
||||
this->lf_mismatched_formats.insert(curr->get_name());
|
||||
continue;
|
||||
}
|
||||
@ -414,6 +444,20 @@ logfile::process_prefix(shared_buffer_ref& sbr,
|
||||
" scan with format (%s) matched with quality (%d)",
|
||||
curr->get_name().c_str(),
|
||||
sm.sm_quality);
|
||||
|
||||
auto match_um
|
||||
= lnav::console::user_message::info(
|
||||
attr_line_t()
|
||||
.append(lnav::roles::identifier(
|
||||
curr->get_name().to_string()))
|
||||
.append(" matched line ")
|
||||
.append(lnav::roles::number(
|
||||
fmt::to_string(starting_index_size))))
|
||||
.with_note(
|
||||
attr_line_t("match quality is ")
|
||||
.append(lnav::roles::number(
|
||||
fmt::to_string(sm.sm_quality))));
|
||||
this->lf_format_match_messages.emplace_back(match_um);
|
||||
if (best_match) {
|
||||
auto starting_iter = std::next(
|
||||
this->lf_index.begin(), starting_index_size);
|
||||
@ -469,6 +513,14 @@ logfile::process_prefix(shared_buffer_ref& sbr,
|
||||
this->lf_index.size(),
|
||||
curr->get_name().get());
|
||||
|
||||
auto match_um = lnav::console::user_message::ok(
|
||||
attr_line_t()
|
||||
.append(lnav::roles::identifier(
|
||||
winner.first->get_name().to_string()))
|
||||
.append(" is the best match for line ")
|
||||
.append(lnav::roles::number(
|
||||
fmt::to_string(starting_index_size))));
|
||||
this->lf_format_match_messages.emplace_back(match_um);
|
||||
this->lf_text_format = text_format_t::TF_LOG;
|
||||
this->lf_format = curr->specialized();
|
||||
this->lf_format_quality = winner.second.sm_quality;
|
||||
|
@ -32,6 +32,7 @@
|
||||
#ifndef logfile_hh
|
||||
#define logfile_hh
|
||||
|
||||
#include <filesystem>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
@ -49,7 +50,6 @@
|
||||
#include "bookmarks.hh"
|
||||
#include "byte_array.hh"
|
||||
#include "file_options.hh"
|
||||
#include <filesystem>
|
||||
#include "line_buffer.hh"
|
||||
#include "log_format_fwd.hh"
|
||||
#include "logfile_fwd.hh"
|
||||
@ -420,6 +420,16 @@ public:
|
||||
return this->lf_file_options;
|
||||
}
|
||||
|
||||
const std::set<intern_string_t>& get_mismatched_formats()
|
||||
{
|
||||
return this->lf_mismatched_formats;
|
||||
}
|
||||
|
||||
const std::vector<lnav::console::user_message>& get_format_match_messages()
|
||||
{
|
||||
return this->lf_format_match_messages;
|
||||
}
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Process a line from the file.
|
||||
@ -489,6 +499,7 @@ private:
|
||||
std::map<std::string, metadata> lf_embedded_metadata;
|
||||
size_t lf_file_options_generation{0};
|
||||
std::optional<std::pair<std::string, lnav::file_options>> lf_file_options;
|
||||
std::vector<lnav::console::user_message> lf_format_match_messages;
|
||||
};
|
||||
|
||||
class logline_observer {
|
||||
|
@ -75,6 +75,7 @@ struct logfile_open_options_base {
|
||||
std::optional<text_format_t> loo_text_format;
|
||||
std::optional<lnav::piper::running_handle> loo_piper;
|
||||
file_location_t loo_init_location{mapbox::util::no_init{}};
|
||||
std::vector<lnav::console::user_message> loo_match_details;
|
||||
};
|
||||
|
||||
struct logfile_open_options : public logfile_open_options_base {
|
||||
|
@ -146,7 +146,8 @@ pretty_pipe_callback(exec_context& ec, const std::string& cmdline, auto_fd& fd)
|
||||
return retval;
|
||||
}
|
||||
|
||||
logfile_sub_source::logfile_sub_source()
|
||||
logfile_sub_source::
|
||||
logfile_sub_source()
|
||||
: text_sub_source(1), lss_meta_grepper(*this), lss_location_history(*this)
|
||||
{
|
||||
this->tss_supports_filtering = true;
|
||||
@ -2007,6 +2008,7 @@ logfile_sub_source::remove_file(std::shared_ptr<logfile> lf)
|
||||
|
||||
this->lss_force_rebuild = true;
|
||||
}
|
||||
this->lss_token_file = nullptr;
|
||||
}
|
||||
|
||||
std::optional<vis_line_t>
|
||||
@ -2303,7 +2305,8 @@ logline_window::end()
|
||||
return {this->lw_source, vl};
|
||||
}
|
||||
|
||||
logline_window::logmsg_info::logmsg_info(logfile_sub_source& lss, vis_line_t vl)
|
||||
logline_window::logmsg_info::
|
||||
logmsg_info(logfile_sub_source& lss, vis_line_t vl)
|
||||
: li_source(lss), li_line(vl)
|
||||
{
|
||||
if (this->li_line < vis_line_t(this->li_source.text_line_count())) {
|
||||
@ -2394,7 +2397,8 @@ logline_window::logmsg_info::get_metadata() const
|
||||
return &bm_iter->second;
|
||||
}
|
||||
|
||||
logline_window::logmsg_info::metadata_edit_guard::~metadata_edit_guard()
|
||||
logline_window::logmsg_info::metadata_edit_guard::~
|
||||
metadata_edit_guard()
|
||||
{
|
||||
auto line_number = std::distance(this->meg_logmsg_info.li_file->begin(),
|
||||
this->meg_logmsg_info.li_logline);
|
||||
|
@ -187,6 +187,14 @@ environ_to_map()
|
||||
return retval;
|
||||
}
|
||||
|
||||
auto_pipe&
|
||||
looper::get_wakeup_pipe()
|
||||
{
|
||||
static auto retval = auto_pipe::for_child_fd(-1).unwrap();
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
looper::
|
||||
looper(std::string name, auto_fd stdout_fd, auto_fd stderr_fd, options opts)
|
||||
: l_name(std::move(name)), l_cwd(std::filesystem::current_path().string()),
|
||||
@ -210,8 +218,11 @@ looper(std::string name, auto_fd stdout_fd, auto_fd stderr_fd, options opts)
|
||||
looper::~
|
||||
looper()
|
||||
{
|
||||
char ch = '\0';
|
||||
|
||||
log_info("piper destructed, shutting down: %s", this->l_name.c_str());
|
||||
this->l_looping = false;
|
||||
log_perror(write(get_wakeup_pipe().write_end(), &ch, 1));
|
||||
this->l_future.wait();
|
||||
}
|
||||
|
||||
@ -231,7 +242,7 @@ looper::loop()
|
||||
static constexpr auto FILE_TIMEOUT_MAX = 1000ms;
|
||||
|
||||
const auto& cfg = injector::get<const config&>();
|
||||
struct pollfd pfd[2];
|
||||
struct pollfd pfd[3];
|
||||
struct {
|
||||
line_buffer lb;
|
||||
file_range last_range;
|
||||
@ -311,6 +322,11 @@ looper::loop()
|
||||
break;
|
||||
}
|
||||
|
||||
auto& wakeup_pfd = pfd[used_pfds++];
|
||||
wakeup_pfd.fd = get_wakeup_pipe().read_end().get();
|
||||
wakeup_pfd.events = POLLIN;
|
||||
wakeup_pfd.revents = 0;
|
||||
|
||||
auto poll_rc = poll(pfd, used_pfds, poll_timeout);
|
||||
if (poll_rc == 0) {
|
||||
// update the timestamp to keep the file alive from any
|
||||
@ -329,6 +345,11 @@ looper::loop()
|
||||
} else {
|
||||
last_write = std::chrono::system_clock::now();
|
||||
}
|
||||
if (!this->l_looping.load()) {
|
||||
char ch;
|
||||
|
||||
read(get_wakeup_pipe().read_end(), &ch, 1);
|
||||
}
|
||||
for (auto& cap : captured_fds) {
|
||||
if (cap.lb.get_fd() == -1) {
|
||||
continue;
|
||||
@ -469,16 +490,20 @@ looper::loop()
|
||||
fmt::format(FMT_STRING("{:?}"), body_sf).c_str());
|
||||
|
||||
auto match_res = mmatcher.match(body_sf);
|
||||
if (!mmatcher.mm_details.empty()) {
|
||||
this->l_demux_info.writeAccess()->di_details
|
||||
= mmatcher.mm_details;
|
||||
}
|
||||
demux_attempted = match_res.match(
|
||||
[this, &curr_demux_def, &cfg](
|
||||
multiplex_matcher::found f) {
|
||||
curr_demux_def
|
||||
= cfg.c_demux_definitions.find(f.f_id)->second;
|
||||
{
|
||||
safe::WriteAccess<safe_demux_id> di(
|
||||
this->l_demux_id);
|
||||
safe::WriteAccess<safe_demux_info> di(
|
||||
this->l_demux_info);
|
||||
|
||||
di->assign(f.f_id);
|
||||
di->di_name = f.f_id;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
@ -755,7 +780,7 @@ cleanup()
|
||||
}
|
||||
|
||||
for (auto& entry : to_remove) {
|
||||
log_debug("removing piper directory: %s", entry.c_str());
|
||||
log_info("removing piper directory: %s", entry.c_str());
|
||||
std::filesystem::remove_all(entry);
|
||||
}
|
||||
});
|
||||
|
@ -30,6 +30,7 @@
|
||||
#ifndef piper_looper_hh
|
||||
#define piper_looper_hh
|
||||
|
||||
#include <filesystem>
|
||||
#include <future>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
@ -37,7 +38,6 @@
|
||||
#include "base/auto_fd.hh"
|
||||
#include "base/piper.file.hh"
|
||||
#include "base/result.h"
|
||||
#include <filesystem>
|
||||
#include "safe/safe.h"
|
||||
#include "yajlpp/yajlpp_def.hh"
|
||||
|
||||
@ -49,7 +49,12 @@ enum class state {
|
||||
finished,
|
||||
};
|
||||
|
||||
using safe_demux_id = safe::Safe<std::string>;
|
||||
struct demux_info {
|
||||
std::string di_name;
|
||||
std::vector<lnav::console::user_message> di_details;
|
||||
};
|
||||
|
||||
using safe_demux_info = safe::Safe<demux_info>;
|
||||
|
||||
struct options {
|
||||
bool o_demux{false};
|
||||
@ -86,7 +91,10 @@ public:
|
||||
return this->l_out_dir / "out.*";
|
||||
}
|
||||
|
||||
std::string get_demux_id() const { return *this->l_demux_id.readAccess(); }
|
||||
demux_info get_demux_info() const
|
||||
{
|
||||
return *this->l_demux_info.readAccess();
|
||||
}
|
||||
|
||||
std::string get_url() const
|
||||
{
|
||||
@ -94,10 +102,7 @@ public:
|
||||
this->l_out_dir.filename().string());
|
||||
}
|
||||
|
||||
int get_loop_count() const
|
||||
{
|
||||
return this->l_loop_count.load();
|
||||
}
|
||||
int get_loop_count() const { return this->l_loop_count.load(); }
|
||||
|
||||
bool is_finished() const
|
||||
{
|
||||
@ -120,6 +125,8 @@ public:
|
||||
private:
|
||||
void loop();
|
||||
|
||||
static auto_pipe& get_wakeup_pipe();
|
||||
|
||||
std::atomic<bool> l_looping{true};
|
||||
const std::string l_name;
|
||||
const std::string l_cwd;
|
||||
@ -131,7 +138,7 @@ private:
|
||||
std::future<void> l_future;
|
||||
std::atomic<int> l_finished{0};
|
||||
std::atomic<int> l_loop_count{0};
|
||||
safe_demux_id l_demux_id;
|
||||
safe_demux_info l_demux_info;
|
||||
};
|
||||
|
||||
template<state LooperState>
|
||||
@ -154,7 +161,15 @@ public:
|
||||
return this->h_looper->get_out_pattern();
|
||||
}
|
||||
|
||||
std::string get_demux_id() const { return this->h_looper->get_demux_id(); }
|
||||
std::string get_demux_id() const
|
||||
{
|
||||
return this->h_looper->get_demux_info().di_name;
|
||||
}
|
||||
|
||||
std::vector<lnav::console::user_message> get_demux_details() const
|
||||
{
|
||||
return this->h_looper->get_demux_info().di_details;
|
||||
}
|
||||
|
||||
std::string get_url() const { return this->h_looper->get_url(); }
|
||||
|
||||
|
@ -143,6 +143,7 @@ plain_text_source&
|
||||
plain_text_source::replace_with(const std::vector<attr_line_t>& text_lines)
|
||||
{
|
||||
file_off_t off = 0;
|
||||
this->tds_lines.clear();
|
||||
for (const auto& al : text_lines) {
|
||||
this->tds_lines.emplace_back(off, al);
|
||||
off += al.length() + 1;
|
||||
|
@ -784,6 +784,7 @@ rl_search_internal(readline_curses* rc, ln_mode_t mode, bool complete = false)
|
||||
case ln_mode_t::PAGING:
|
||||
case ln_mode_t::FILTER:
|
||||
case ln_mode_t::FILES:
|
||||
case ln_mode_t::FILE_DETAILS:
|
||||
case ln_mode_t::EXEC:
|
||||
case ln_mode_t::USER:
|
||||
case ln_mode_t::SPECTRO_DETAILS:
|
||||
@ -876,6 +877,7 @@ rl_callback_int(readline_curses* rc, bool is_alt)
|
||||
case ln_mode_t::PAGING:
|
||||
case ln_mode_t::FILTER:
|
||||
case ln_mode_t::FILES:
|
||||
case ln_mode_t::FILE_DETAILS:
|
||||
case ln_mode_t::SPECTRO_DETAILS:
|
||||
case ln_mode_t::BUSY:
|
||||
require(0);
|
||||
|
@ -33,6 +33,7 @@
|
||||
"pattern": "^(?<timestamp>\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}\\.\\d{3}(?:Z|[+\\-]\\d{2}:\\d{2})) source=[a-zA-Z0-9][a-zA-Z0-9_\\.\\-]* (?<body>.*) kubernetes_host=(?<k8s_host>[a-zA-Z0-9][a-zA-Z0-9_\\.\\-]*) kubernetes_pod_name=(?<mux_id>[a-zA-Z0-9][a-zA-Z0-9_\\.\\-]*)"
|
||||
},
|
||||
"container-with-type": {
|
||||
"enabled": false,
|
||||
"pattern": "^(?<mux_id>[a-zA-Z][\\w\\-]{3,}) (?<container_type>[a-zA-Z][\\w\\-]{3,}) (?<body>.*)"
|
||||
}
|
||||
}
|
||||
|
@ -33,6 +33,7 @@
|
||||
#include "config.h"
|
||||
#include "lnav.hh"
|
||||
#include "md4cpp.hh"
|
||||
#include "sysclip.hh"
|
||||
#include "textview_curses.hh"
|
||||
|
||||
using namespace md4cpp::literals;
|
||||
@ -50,7 +51,10 @@ text_overlay_menu::list_overlay_menu(const listview_curses& lv, vis_line_t row)
|
||||
return retval;
|
||||
}
|
||||
|
||||
const auto* tss = tc->get_sub_source();
|
||||
const auto& sti = tc->tc_selected_text.value();
|
||||
const auto supports_filtering
|
||||
= tss != nullptr && tss->tss_supports_filtering;
|
||||
|
||||
if (sti.sti_line != row) {
|
||||
return retval;
|
||||
@ -85,25 +89,29 @@ text_overlay_menu::list_overlay_menu(const listview_curses& lv, vis_line_t row)
|
||||
attr_line_t al;
|
||||
|
||||
int start = left;
|
||||
if (is_link) {
|
||||
al.append(":floppy_disk:"_emoji)
|
||||
.append(" Open in lnav")
|
||||
.append(" ");
|
||||
} else {
|
||||
al.append(" ").append("\u2714 Filter-in"_ok).append(" ");
|
||||
if (is_link || supports_filtering) {
|
||||
if (is_link) {
|
||||
al.append(":floppy_disk:"_emoji)
|
||||
.append(" Open in lnav")
|
||||
.append(" ");
|
||||
} else {
|
||||
al.append(" ").append("\u2714 Filter-in"_ok).append(" ");
|
||||
}
|
||||
this->los_menu_items.emplace_back(
|
||||
menu_line,
|
||||
line_range{start, start + (int) al.length()},
|
||||
[is_link, sti](const std::string& value) {
|
||||
const auto cmd = is_link
|
||||
? ":open $href"
|
||||
: fmt::format(FMT_STRING(":filter-in {}"),
|
||||
lnav::pcre2pp::quote(value));
|
||||
lnav_data.ld_exec_context
|
||||
.with_provenance(exec_context::mouse_input{})
|
||||
->execute_with(cmd,
|
||||
std::make_pair("href", sti.sti_href));
|
||||
});
|
||||
start += al.length();
|
||||
}
|
||||
this->los_menu_items.emplace_back(
|
||||
menu_line,
|
||||
line_range{start, start + (int) al.length()},
|
||||
[is_link, sti](const std::string& value) {
|
||||
auto cmd = is_link ? ":open $href"
|
||||
: fmt::format(FMT_STRING(":filter-in {}"),
|
||||
lnav::pcre2pp::quote(value));
|
||||
lnav_data.ld_exec_context
|
||||
.with_provenance(exec_context::mouse_input{})
|
||||
->execute_with(cmd, std::make_pair("href", sti.sti_href));
|
||||
});
|
||||
start += al.length();
|
||||
|
||||
if (is_link) {
|
||||
al.append(" ");
|
||||
@ -125,28 +133,32 @@ text_overlay_menu::list_overlay_menu(const listview_curses& lv, vis_line_t row)
|
||||
}
|
||||
retval.emplace_back(attr_line_t().pad_to(left).append(al));
|
||||
}
|
||||
menu_line += 1_vl;
|
||||
{
|
||||
attr_line_t al;
|
||||
|
||||
if (is_link) {
|
||||
al.append(":globe_with_meridians:"_emoji).append(" Open ");
|
||||
} else {
|
||||
al.append(" ").append("\u2718 Filter-out"_error).append(" ");
|
||||
}
|
||||
menu_line += 1_vl;
|
||||
int start = left;
|
||||
this->los_menu_items.emplace_back(
|
||||
menu_line,
|
||||
line_range{start, start + (int) al.length()},
|
||||
[is_link, sti](const std::string& value) {
|
||||
auto cmd = is_link ? ":xopen $href"
|
||||
: fmt::format(FMT_STRING(":filter-out {}"),
|
||||
lnav::pcre2pp::quote(value));
|
||||
lnav_data.ld_exec_context
|
||||
.with_provenance(exec_context::mouse_input{})
|
||||
->execute_with(cmd, std::make_pair("href", sti.sti_href));
|
||||
});
|
||||
start += al.length();
|
||||
if (is_link || supports_filtering) {
|
||||
if (is_link) {
|
||||
al.append(":globe_with_meridians:"_emoji).append(" Open ");
|
||||
} else {
|
||||
al.append(" ").append("\u2718 Filter-out"_error).append(" ");
|
||||
}
|
||||
this->los_menu_items.emplace_back(
|
||||
menu_line,
|
||||
line_range{start, start + (int) al.length()},
|
||||
[is_link, sti](const std::string& value) {
|
||||
auto cmd = is_link
|
||||
? ":xopen $href"
|
||||
: fmt::format(FMT_STRING(":filter-out {}"),
|
||||
lnav::pcre2pp::quote(value));
|
||||
lnav_data.ld_exec_context
|
||||
.with_provenance(exec_context::mouse_input{})
|
||||
->execute_with(cmd,
|
||||
std::make_pair("href", sti.sti_href));
|
||||
});
|
||||
start += al.length();
|
||||
}
|
||||
al.append(":clipboard:"_emoji)
|
||||
.append(is_link ? " Copy link " : " Copy ")
|
||||
.with_attr_for_all(VC_ROLE.value(role_t::VCR_STATUS));
|
||||
@ -154,7 +166,15 @@ text_overlay_menu::list_overlay_menu(const listview_curses& lv, vis_line_t row)
|
||||
menu_line,
|
||||
line_range{start, start + (int) al.length()},
|
||||
[](const std::string& value) {
|
||||
lnav_data.ld_exec_context.execute("|lnav-copy-text");
|
||||
auto clip_res = sysclip::open(sysclip::type_t::GENERAL);
|
||||
if (clip_res.isErr()) {
|
||||
log_error("unable to open clipboard: %s",
|
||||
clip_res.unwrapErr().c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
auto clip_pipe = clip_res.unwrap();
|
||||
fwrite(value.c_str(), 1, value.length(), clip_pipe.in());
|
||||
});
|
||||
retval.emplace_back(attr_line_t().pad_to(left).append(al));
|
||||
}
|
||||
|
@ -462,7 +462,6 @@ textview_curses::handle_mouse(mouse_event& me)
|
||||
|
||||
switch (me.me_state) {
|
||||
case mouse_button_state_t::BUTTON_STATE_PRESSED: {
|
||||
this->tc_text_selection_active = true;
|
||||
this->tc_press_line = mouse_line;
|
||||
this->tc_press_left = this->lv_left + me.me_press_x;
|
||||
if (!this->lv_selectable) {
|
||||
@ -470,6 +469,7 @@ textview_curses::handle_mouse(mouse_event& me)
|
||||
}
|
||||
mouse_line.match(
|
||||
[this, &me, sub_delegate, &mouse_line](const main_content& mc) {
|
||||
this->tc_text_selection_active = true;
|
||||
if (this->vc_enabled) {
|
||||
if (this->tc_supports_marks
|
||||
&& me.me_button == mouse_button_t::BUTTON_LEFT
|
||||
@ -492,9 +492,7 @@ textview_curses::handle_mouse(mouse_event& me)
|
||||
}
|
||||
},
|
||||
[](const overlay_menu& om) {},
|
||||
[](const static_overlay_content& soc) {
|
||||
|
||||
},
|
||||
[](const static_overlay_content& soc) {},
|
||||
[this](const overlay_content& oc) {
|
||||
this->set_overlay_selection(oc.oc_line);
|
||||
},
|
||||
@ -574,15 +572,9 @@ textview_curses::handle_mouse(mouse_event& me)
|
||||
sub_delegate->text_handle_mouse(*this, mouse_line, me);
|
||||
}
|
||||
},
|
||||
[](const static_overlay_content& soc) {
|
||||
|
||||
},
|
||||
[](const overlay_menu& om) {
|
||||
|
||||
},
|
||||
[](const overlay_content& oc) {
|
||||
|
||||
},
|
||||
[](const static_overlay_content& soc) {},
|
||||
[](const overlay_menu& om) {},
|
||||
[](const overlay_content& oc) {},
|
||||
[](const empty_space& es) {});
|
||||
break;
|
||||
}
|
||||
@ -658,19 +650,16 @@ textview_curses::handle_mouse(mouse_event& me)
|
||||
if (ov != nullptr && mouse_line.is<overlay_menu>()
|
||||
&& this->tc_selected_text)
|
||||
{
|
||||
auto* los = dynamic_cast<list_overlay_source*>(ov);
|
||||
if (los != nullptr) {
|
||||
auto& om = mouse_line.get<overlay_menu>();
|
||||
auto& sti = this->tc_selected_text.value();
|
||||
auto& om = mouse_line.get<overlay_menu>();
|
||||
auto& sti = this->tc_selected_text.value();
|
||||
|
||||
for (const auto& mi : los->los_menu_items) {
|
||||
if (om.om_line == mi.mi_line
|
||||
&& me.is_click_in(mouse_button_t::BUTTON_LEFT,
|
||||
mi.mi_range))
|
||||
{
|
||||
mi.mi_action(sti.sti_value);
|
||||
break;
|
||||
}
|
||||
for (const auto& mi : ov->los_menu_items) {
|
||||
if (om.om_line == mi.mi_line
|
||||
&& me.is_click_in(mouse_button_t::BUTTON_LEFT,
|
||||
mi.mi_range))
|
||||
{
|
||||
mi.mi_action(sti.sti_value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -848,11 +837,11 @@ void
|
||||
textview_curses::execute_search(const std::string& regex_orig)
|
||||
{
|
||||
std::string regex = regex_orig;
|
||||
std::shared_ptr<lnav::pcre2pp::code> code;
|
||||
|
||||
if ((this->tc_search_child == nullptr)
|
||||
|| (regex != this->tc_current_search))
|
||||
{
|
||||
std::shared_ptr<lnav::pcre2pp::code> code;
|
||||
this->match_reset();
|
||||
|
||||
this->tc_search_child.reset();
|
||||
|
@ -175,7 +175,7 @@ timeline_preview_overlay::list_overlay_menu(const listview_curses& lv,
|
||||
}
|
||||
|
||||
timeline_header_overlay::
|
||||
timeline_header_overlay(std::shared_ptr<timeline_source> src)
|
||||
timeline_header_overlay(const std::shared_ptr<timeline_source>& src)
|
||||
: gho_src(src)
|
||||
{
|
||||
}
|
||||
|
@ -187,7 +187,8 @@ public:
|
||||
|
||||
class timeline_header_overlay : public text_overlay_menu {
|
||||
public:
|
||||
explicit timeline_header_overlay(std::shared_ptr<timeline_source> src);
|
||||
explicit timeline_header_overlay(
|
||||
const std::shared_ptr<timeline_source>& src);
|
||||
|
||||
bool list_static_overlay(const listview_curses& lv,
|
||||
int y,
|
||||
|
@ -617,6 +617,7 @@ handle_winch()
|
||||
lnav_data.ld_match_view.set_needs_update();
|
||||
lnav_data.ld_filter_view.set_needs_update();
|
||||
lnav_data.ld_files_view.set_needs_update();
|
||||
lnav_data.ld_file_details_view.set_needs_update();
|
||||
lnav_data.ld_spectro_details_view.set_needs_update();
|
||||
lnav_data.ld_timeline_details_view.set_needs_update();
|
||||
lnav_data.ld_user_message_view.set_needs_update();
|
||||
@ -627,6 +628,9 @@ handle_winch()
|
||||
void
|
||||
layout_views()
|
||||
{
|
||||
static constexpr auto FILES_FOCUSED_WIDTH = 40;
|
||||
static constexpr auto FILES_BLURRED_WIDTH = 20;
|
||||
|
||||
static auto* breadcrumb_view = injector::get<breadcrumb_curses*>();
|
||||
int width, height;
|
||||
getmaxyx(lnav_data.ld_window, height, width);
|
||||
@ -698,13 +702,31 @@ layout_views()
|
||||
|
||||
auto config_panel_open = (lnav_data.ld_mode == ln_mode_t::FILTER
|
||||
|| lnav_data.ld_mode == ln_mode_t::FILES
|
||||
|| lnav_data.ld_mode == ln_mode_t::FILE_DETAILS
|
||||
|| lnav_data.ld_mode == ln_mode_t::SEARCH_FILTERS
|
||||
|| lnav_data.ld_mode == ln_mode_t::SEARCH_FILES);
|
||||
auto filters_open = (lnav_data.ld_mode == ln_mode_t::FILTER
|
||||
|| lnav_data.ld_mode == ln_mode_t::SEARCH_FILTERS);
|
||||
auto files_open = (lnav_data.ld_mode == ln_mode_t::FILES
|
||||
|| lnav_data.ld_mode == ln_mode_t::FILE_DETAILS
|
||||
|| lnav_data.ld_mode == ln_mode_t::SEARCH_FILES);
|
||||
int filter_height = config_panel_open ? 5 : 0;
|
||||
auto files_width = lnav_data.ld_mode == ln_mode_t::FILES
|
||||
? FILES_FOCUSED_WIDTH
|
||||
: FILES_BLURRED_WIDTH;
|
||||
int filter_height;
|
||||
|
||||
switch (lnav_data.ld_mode) {
|
||||
case ln_mode_t::FILES:
|
||||
case ln_mode_t::FILTER:
|
||||
filter_height = 5;
|
||||
break;
|
||||
case ln_mode_t::FILE_DETAILS:
|
||||
filter_height = 15;
|
||||
break;
|
||||
default:
|
||||
filter_height = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
bool breadcrumb_open = (lnav_data.ld_mode == ln_mode_t::BREADCRUMBS);
|
||||
|
||||
@ -809,9 +831,16 @@ layout_views()
|
||||
|
||||
lnav_data.ld_files_view.set_height(vis_line_t(filter_height));
|
||||
lnav_data.ld_files_view.set_y(bottom + 2);
|
||||
lnav_data.ld_files_view.set_width(width);
|
||||
lnav_data.ld_files_view.set_width(files_width);
|
||||
lnav_data.ld_files_view.set_visible(files_open && vis);
|
||||
|
||||
lnav_data.ld_file_details_view.set_height(vis_line_t(filter_height));
|
||||
lnav_data.ld_file_details_view.set_y(bottom + 2);
|
||||
lnav_data.ld_file_details_view.set_x(files_width);
|
||||
lnav_data.ld_file_details_view.set_width(
|
||||
std::clamp(width - files_width, 0, width));
|
||||
lnav_data.ld_file_details_view.set_visible(files_open && vis);
|
||||
|
||||
lnav_data.ld_status[LNS_FILTER_HELP].set_visible(config_panel_open && vis);
|
||||
lnav_data.ld_status[LNS_FILTER_HELP].set_y(bottom + 1);
|
||||
lnav_data.ld_status[LNS_FILTER_HELP].set_width(width);
|
||||
@ -1265,6 +1294,8 @@ get_textview_for_mode(ln_mode_t mode)
|
||||
case ln_mode_t::SEARCH_FILES:
|
||||
case ln_mode_t::FILES:
|
||||
return &lnav_data.ld_files_view;
|
||||
case ln_mode_t::FILE_DETAILS:
|
||||
return &lnav_data.ld_file_details_view;
|
||||
case ln_mode_t::SPECTRO_DETAILS:
|
||||
case ln_mode_t::SEARCH_SPECTRO_DETAILS:
|
||||
return &lnav_data.ld_spectro_details_view;
|
||||
@ -1435,6 +1466,11 @@ set_view_mode(ln_mode_t mode)
|
||||
lnav_data.ld_view_stack.set_needs_update();
|
||||
break;
|
||||
}
|
||||
case ln_mode_t::FILE_DETAILS: {
|
||||
lnav_data.ld_file_details_view.tc_cursor_role
|
||||
= role_t::VCR_DISABLED_CURSOR_LINE;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@ -1443,6 +1479,12 @@ set_view_mode(ln_mode_t mode)
|
||||
breadcrumb_view->focus();
|
||||
break;
|
||||
}
|
||||
case ln_mode_t::FILE_DETAILS: {
|
||||
lnav_data.ld_status[LNS_FILTER].set_needs_update();
|
||||
lnav_data.ld_file_details_view.tc_cursor_role
|
||||
= role_t::VCR_CURSOR_LINE;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@ -1464,6 +1506,7 @@ all_views()
|
||||
retval.push_back(&lnav_data.ld_example_view);
|
||||
retval.push_back(&lnav_data.ld_preview_view[0]);
|
||||
retval.push_back(&lnav_data.ld_preview_view[1]);
|
||||
retval.push_back(&lnav_data.ld_file_details_view);
|
||||
retval.push_back(&lnav_data.ld_files_view);
|
||||
retval.push_back(&lnav_data.ld_filter_view);
|
||||
retval.push_back(&lnav_data.ld_user_message_view);
|
||||
@ -1478,7 +1521,7 @@ void
|
||||
lnav_behavior::mouse_event(int button, bool release, int x, int y)
|
||||
{
|
||||
static auto* breadcrumb_view = injector::get<breadcrumb_curses*>();
|
||||
static const std::vector<view_curses*> VIEWS = all_views();
|
||||
static const auto VIEWS = all_views();
|
||||
static const auto CLICK_INTERVAL
|
||||
= std::chrono::milliseconds(mouseinterval(-1) * 2);
|
||||
|
||||
@ -1552,6 +1595,7 @@ lnav_behavior::mouse_event(int button, bool release, int x, int y)
|
||||
case ln_mode_t::PAGING:
|
||||
break;
|
||||
case ln_mode_t::FILES:
|
||||
case ln_mode_t::FILE_DETAILS:
|
||||
case ln_mode_t::FILTER:
|
||||
// Clicking on the main view when the config panels are
|
||||
// open should return us to paging.
|
||||
|
@ -64,6 +64,7 @@ enum class ln_mode_t : int {
|
||||
BREADCRUMBS,
|
||||
FILTER,
|
||||
FILES,
|
||||
FILE_DETAILS,
|
||||
SPECTRO_DETAILS,
|
||||
SEARCH_SPECTRO_DETAILS,
|
||||
COMMAND,
|
||||
@ -96,8 +97,8 @@ std::optional<vis_line_t> next_cluster(
|
||||
const,
|
||||
const bookmark_type_t* bt,
|
||||
vis_line_t top);
|
||||
bool moveto_cluster(std::optional<vis_line_t> (
|
||||
bookmark_vector<vis_line_t>::*f)(vis_line_t) const,
|
||||
bool moveto_cluster(std::optional<vis_line_t> (bookmark_vector<vis_line_t>::*f)(
|
||||
vis_line_t) const,
|
||||
const bookmark_type_t* bt,
|
||||
vis_line_t top);
|
||||
vis_line_t search_forward_from(textview_curses* tc);
|
||||
|
@ -5256,7 +5256,7 @@
|
||||
"control-pattern": ""
|
||||
},
|
||||
"container-with-type": {
|
||||
"enabled": true,
|
||||
"enabled": false,
|
||||
"pattern": "^(?<mux_id>[a-zA-Z][\\w\\-]{3,}) (?<container_type>[a-zA-Z][\\w\\-]{3,}) (?<body>.*)",
|
||||
"control-pattern": ""
|
||||
},
|
||||
|
@ -19,49 +19,50 @@
|
||||
/log/annotations/org.lnav.test/description -> {test_dir}/configs/installed/anno-test.json:6
|
||||
/log/annotations/org.lnav.test/handler -> {test_dir}/configs/installed/anno-test.json:8
|
||||
/log/date-time/convert-zoned-to-local -> root-config.json:18
|
||||
/log/demux/container-with-type/pattern -> root-config.json:36
|
||||
/log/demux/container-with-type/enabled -> root-config.json:36
|
||||
/log/demux/container-with-type/pattern -> root-config.json:37
|
||||
/log/demux/container/pattern -> root-config.json:29
|
||||
/log/demux/recv-with-pod/control-pattern -> root-config.json:32
|
||||
/log/demux/recv-with-pod/pattern -> root-config.json:33
|
||||
/tuning/archive-manager/cache-ttl -> root-config.json:43
|
||||
/tuning/archive-manager/min-free-space -> root-config.json:42
|
||||
/tuning/clipboard/impls/MacOS/find/read -> root-config.json:71
|
||||
/tuning/clipboard/impls/MacOS/find/write -> root-config.json:70
|
||||
/tuning/clipboard/impls/MacOS/general/read -> root-config.json:67
|
||||
/tuning/clipboard/impls/MacOS/general/write -> root-config.json:66
|
||||
/tuning/clipboard/impls/MacOS/test -> root-config.json:64
|
||||
/tuning/clipboard/impls/NeoVim/general/read -> root-config.json:99
|
||||
/tuning/clipboard/impls/NeoVim/general/write -> root-config.json:98
|
||||
/tuning/clipboard/impls/NeoVim/test -> root-config.json:96
|
||||
/tuning/clipboard/impls/Wayland/general/read -> root-config.json:78
|
||||
/tuning/clipboard/impls/Wayland/general/write -> root-config.json:77
|
||||
/tuning/clipboard/impls/Wayland/test -> root-config.json:75
|
||||
/tuning/clipboard/impls/Windows/general/write -> root-config.json:105
|
||||
/tuning/clipboard/impls/Windows/test -> root-config.json:103
|
||||
/tuning/clipboard/impls/X11-xclip/general/read -> root-config.json:85
|
||||
/tuning/clipboard/impls/X11-xclip/general/write -> root-config.json:84
|
||||
/tuning/clipboard/impls/X11-xclip/test -> root-config.json:82
|
||||
/tuning/clipboard/impls/tmux/general/read -> root-config.json:92
|
||||
/tuning/clipboard/impls/tmux/general/write -> root-config.json:91
|
||||
/tuning/clipboard/impls/tmux/test -> root-config.json:89
|
||||
/tuning/external-opener/impls/MacOS/command -> root-config.json:114
|
||||
/tuning/external-opener/impls/MacOS/test -> root-config.json:113
|
||||
/tuning/external-opener/impls/XDG/command -> root-config.json:118
|
||||
/tuning/external-opener/impls/XDG/test -> root-config.json:117
|
||||
/tuning/piper/max-size -> root-config.json:57
|
||||
/tuning/piper/rotations -> root-config.json:58
|
||||
/tuning/piper/ttl -> root-config.json:59
|
||||
/tuning/remote/ssh/command -> root-config.json:47
|
||||
/tuning/remote/ssh/config/BatchMode -> root-config.json:49
|
||||
/tuning/remote/ssh/config/ConnectTimeout -> root-config.json:50
|
||||
/tuning/remote/ssh/start-command -> root-config.json:52
|
||||
/tuning/remote/ssh/transfer-command -> root-config.json:53
|
||||
/tuning/url-scheme/docker-compose/handler -> root-config.json:127
|
||||
/tuning/url-scheme/docker/handler -> root-config.json:124
|
||||
/tuning/archive-manager/cache-ttl -> root-config.json:44
|
||||
/tuning/archive-manager/min-free-space -> root-config.json:43
|
||||
/tuning/clipboard/impls/MacOS/find/read -> root-config.json:72
|
||||
/tuning/clipboard/impls/MacOS/find/write -> root-config.json:71
|
||||
/tuning/clipboard/impls/MacOS/general/read -> root-config.json:68
|
||||
/tuning/clipboard/impls/MacOS/general/write -> root-config.json:67
|
||||
/tuning/clipboard/impls/MacOS/test -> root-config.json:65
|
||||
/tuning/clipboard/impls/NeoVim/general/read -> root-config.json:100
|
||||
/tuning/clipboard/impls/NeoVim/general/write -> root-config.json:99
|
||||
/tuning/clipboard/impls/NeoVim/test -> root-config.json:97
|
||||
/tuning/clipboard/impls/Wayland/general/read -> root-config.json:79
|
||||
/tuning/clipboard/impls/Wayland/general/write -> root-config.json:78
|
||||
/tuning/clipboard/impls/Wayland/test -> root-config.json:76
|
||||
/tuning/clipboard/impls/Windows/general/write -> root-config.json:106
|
||||
/tuning/clipboard/impls/Windows/test -> root-config.json:104
|
||||
/tuning/clipboard/impls/X11-xclip/general/read -> root-config.json:86
|
||||
/tuning/clipboard/impls/X11-xclip/general/write -> root-config.json:85
|
||||
/tuning/clipboard/impls/X11-xclip/test -> root-config.json:83
|
||||
/tuning/clipboard/impls/tmux/general/read -> root-config.json:93
|
||||
/tuning/clipboard/impls/tmux/general/write -> root-config.json:92
|
||||
/tuning/clipboard/impls/tmux/test -> root-config.json:90
|
||||
/tuning/external-opener/impls/MacOS/command -> root-config.json:115
|
||||
/tuning/external-opener/impls/MacOS/test -> root-config.json:114
|
||||
/tuning/external-opener/impls/XDG/command -> root-config.json:119
|
||||
/tuning/external-opener/impls/XDG/test -> root-config.json:118
|
||||
/tuning/piper/max-size -> root-config.json:58
|
||||
/tuning/piper/rotations -> root-config.json:59
|
||||
/tuning/piper/ttl -> root-config.json:60
|
||||
/tuning/remote/ssh/command -> root-config.json:48
|
||||
/tuning/remote/ssh/config/BatchMode -> root-config.json:50
|
||||
/tuning/remote/ssh/config/ConnectTimeout -> root-config.json:51
|
||||
/tuning/remote/ssh/start-command -> root-config.json:53
|
||||
/tuning/remote/ssh/transfer-command -> root-config.json:54
|
||||
/tuning/url-scheme/docker-compose/handler -> root-config.json:128
|
||||
/tuning/url-scheme/docker/handler -> root-config.json:125
|
||||
/tuning/url-scheme/hw/handler -> {test_dir}/configs/installed/hw-url-handler.json:6
|
||||
/tuning/url-scheme/journald/handler -> root-config.json:130
|
||||
/tuning/url-scheme/piper/handler -> root-config.json:133
|
||||
/tuning/url-scheme/podman/handler -> root-config.json:136
|
||||
/tuning/url-scheme/journald/handler -> root-config.json:131
|
||||
/tuning/url-scheme/piper/handler -> root-config.json:134
|
||||
/tuning/url-scheme/podman/handler -> root-config.json:137
|
||||
/ui/clock-format -> root-config.json:4
|
||||
/ui/default-colors -> root-config.json:6
|
||||
/ui/dim-text -> root-config.json:5
|
||||
|
@ -1,2 +1,2 @@
|
||||
[1m[4mbasename(filepath) [0m[1m[4m [0m[1m[4m descriptor [0m[1m[4m [0m[1m[4m mimetype [0m[1m[4m [0m[1m[4m content [0m[1m[4m [0m
|
||||
logfile_syslog.1.gz net.zlib.gzip.header application/json {"name":"logfile_syslog.1","mtime":"2007-11-03T09:23:00.000","comment":""}
|
||||
[1m[4mbasename(filepath) [0m[1m[4m [0m[1m[4m descriptor [0m[1m[4m [0m[1m[4m mimetype [0m[1m[4m [0m[1m[4m content [0m[1m[4m [0m
|
||||
logfile_syslog.1.gz net.zlib.gzip.header application/json {␊ "name": "logfile_syslog.1",␊ "mtime": "2007-11-03T09:23:00.000",␊ "comment": ""␊}␊
|
||||
|
Loading…
Reference in New Issue
Block a user