mirror of https://github.com/tstack/lnav.git
parent
49dce3cec2
commit
1a08eb0d12
5
NEWS
5
NEWS
|
@ -9,6 +9,8 @@ lnav v0.8.5:
|
|||
programmatically manipulate filters.
|
||||
* The ":write-*" commands will now accept "/dev/clipboard" as a file name
|
||||
that writes to the system clipboard.
|
||||
* The ":write-to" and ":write-raw-to" commands will now print out comments
|
||||
and tags attached to the lines.
|
||||
* Added a ":redirect-to <path>" command to redirect command output to the
|
||||
given file. This command is mostly useful in scripts where one might
|
||||
want to redirect all output from commands like ":echo" and ":write-to -"
|
||||
|
@ -24,6 +26,9 @@ lnav v0.8.5:
|
|||
strong fuzzy match. If there are multiple matches, as would happen
|
||||
with ":dfil", readline's menu-complete behavior will be engaged and
|
||||
you can press TAB cycle through the options.
|
||||
* When entering the ":comment" command for a line with a comment, the
|
||||
command prompt will be filled in with the existing comment to make
|
||||
editing easier.
|
||||
|
||||
Fixes:
|
||||
* The ":write-json-to" command will now pass through JSON cells as their
|
||||
|
|
|
@ -541,6 +541,35 @@ static void json_write_row(yajl_gen handle, int row)
|
|||
}
|
||||
}
|
||||
|
||||
static void write_line_to(FILE *outfile, attr_line_t &al)
|
||||
{
|
||||
auto al_attrs = al.get_attrs();
|
||||
struct line_range lr = find_string_attr_range(
|
||||
al_attrs, &textview_curses::SA_ORIGINAL_LINE);
|
||||
auto line_meta = find_string_attr(
|
||||
al_attrs, &logline::L_META);
|
||||
|
||||
fwrite(lr.substr(al.get_string()),
|
||||
1,
|
||||
lr.sublen(al.get_string()),
|
||||
outfile);
|
||||
fwrite("\n", 1, 1, outfile);
|
||||
|
||||
if (line_meta != al_attrs.end()) {
|
||||
auto bm = static_cast<const bookmark_metadata *>(line_meta->sa_value.sav_ptr);
|
||||
|
||||
if (!bm->bm_comment.empty()) {
|
||||
fprintf(outfile, " // %s\n", bm->bm_comment.c_str());
|
||||
}
|
||||
if (!bm->bm_tags.empty()) {
|
||||
fprintf(outfile, " -- %s\n",
|
||||
join(bm->bm_tags.begin(),
|
||||
bm->bm_tags.end(),
|
||||
" ").c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static string com_save_to(exec_context &ec, string cmdline, vector<string> &args)
|
||||
{
|
||||
FILE *outfile = nullptr, *toclose = nullptr;
|
||||
|
@ -773,13 +802,7 @@ static string com_save_to(exec_context &ec, string cmdline, vector<string> &args
|
|||
|
||||
tc->listview_value_for_rows(*tc, top, rows);
|
||||
for (auto &al : rows) {
|
||||
struct line_range lr = find_string_attr_range(
|
||||
al.get_attrs(), &textview_curses::SA_ORIGINAL_LINE);
|
||||
fwrite(lr.substr(al.get_string()),
|
||||
1,
|
||||
lr.sublen(al.get_string()),
|
||||
outfile);
|
||||
fwrite("\n", 1, 1, outfile);
|
||||
write_line_to(outfile, al);
|
||||
|
||||
line_count += 1;
|
||||
}
|
||||
|
@ -789,6 +812,7 @@ static string com_save_to(exec_context &ec, string cmdline, vector<string> &args
|
|||
}
|
||||
}
|
||||
else {
|
||||
vector<attr_line_t> rows(1);
|
||||
bookmark_vector<vis_line_t>::iterator iter;
|
||||
size_t count = 0;
|
||||
string line;
|
||||
|
@ -797,8 +821,8 @@ static string com_save_to(exec_context &ec, string cmdline, vector<string> &args
|
|||
if (ec.ec_dry_run && count > 10) {
|
||||
break;
|
||||
}
|
||||
tc->grep_value_for_line(*iter, line);
|
||||
fprintf(outfile, "%s\n", line.c_str());
|
||||
tc->listview_value_for_rows(*tc, *iter, rows);
|
||||
write_line_to(outfile, rows[0]);
|
||||
|
||||
line_count += 1;
|
||||
}
|
||||
|
@ -1938,6 +1962,25 @@ static string com_comment(exec_context &ec, string cmdline, vector<string> &args
|
|||
return retval;
|
||||
}
|
||||
|
||||
static string com_comment_prompt(exec_context &ec, const string &cmdline)
|
||||
{
|
||||
textview_curses *tc = *lnav_data.ld_view_stack.top();
|
||||
|
||||
if (tc != &lnav_data.ld_views[LNV_LOG]) {
|
||||
return "";
|
||||
}
|
||||
logfile_sub_source &lss = lnav_data.ld_log_source;
|
||||
std::map<content_line_t, bookmark_metadata> &bm = lss.get_user_bookmark_metadata();
|
||||
|
||||
auto line_meta = bm.find(lss.at(tc->get_top()));
|
||||
|
||||
if (line_meta != bm.end() && !line_meta->second.bm_comment.empty()) {
|
||||
return trim(cmdline) + " " + trim(line_meta->second.bm_comment);
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
static string com_clear_comment(exec_context &ec, string cmdline, vector<string> &args)
|
||||
{
|
||||
string retval;
|
||||
|
@ -4030,7 +4073,9 @@ readline_context::command_t STD_COMMANDS[] = {
|
|||
.with_summary("Attach a comment to the top log line")
|
||||
.with_parameter(help_text("text", "The comment text"))
|
||||
.with_example({"This is where it all went wrong"})
|
||||
.with_tags({"metadata"})
|
||||
.with_tags({"metadata"}),
|
||||
|
||||
com_comment_prompt,
|
||||
},
|
||||
{
|
||||
"clear-comment",
|
||||
|
@ -4050,7 +4095,7 @@ readline_context::command_t STD_COMMANDS[] = {
|
|||
.with_parameter(help_text("tag", "The tags to attach")
|
||||
.one_or_more())
|
||||
.with_example({"#BUG123 #needs-review"})
|
||||
.with_tags({"metadata"})
|
||||
.with_tags({"metadata"}),
|
||||
},
|
||||
{
|
||||
"untag",
|
||||
|
|
|
@ -66,6 +66,7 @@ string_attr_type logline::L_FILE("file");
|
|||
string_attr_type logline::L_PARTITION("partition");
|
||||
string_attr_type logline::L_MODULE("module");
|
||||
string_attr_type logline::L_OPID("opid");
|
||||
string_attr_type logline::L_META("meta");
|
||||
|
||||
external_log_format::mod_map_t external_log_format::MODULE_FORMATS;
|
||||
std::vector<external_log_format *> external_log_format::GRAPH_ORDERED_FORMATS;
|
||||
|
|
|
@ -76,6 +76,7 @@ public:
|
|||
static string_attr_type L_PARTITION;
|
||||
static string_attr_type L_MODULE;
|
||||
static string_attr_type L_OPID;
|
||||
static string_attr_type L_META;
|
||||
|
||||
/**
|
||||
* Construct a logline object with the given values.
|
||||
|
|
|
@ -505,6 +505,14 @@ void logfile_sub_source::text_attrs_for_line(textview_curses &lv,
|
|||
value_out.emplace_back(lr, &logline::L_PARTITION, &bm_iter->second);
|
||||
}
|
||||
}
|
||||
|
||||
auto bm_iter = this->lss_user_mark_metadata.find(this->at(vis_line_t(row)));
|
||||
|
||||
if (bm_iter != this->lss_user_mark_metadata.end()) {
|
||||
lr.lr_start = 0;
|
||||
lr.lr_end = -1;
|
||||
value_out.emplace_back(lr, &logline::L_META, &bm_iter->second);
|
||||
}
|
||||
}
|
||||
|
||||
if (this->lss_token_file->is_time_adjusted()) {
|
||||
|
|
|
@ -58,12 +58,24 @@ void rl_change(void *dummy, readline_curses *rc)
|
|||
|
||||
switch (lnav_data.ld_mode) {
|
||||
case LNM_COMMAND: {
|
||||
static string last_command;
|
||||
static int generation = 0;
|
||||
|
||||
string line = rc->get_line_buffer();
|
||||
vector<string> args;
|
||||
auto iter = lnav_commands.end();
|
||||
|
||||
split_ws(line, args);
|
||||
|
||||
if (args.empty()) {
|
||||
generation = 0;
|
||||
} else if (args[0] != last_command) {
|
||||
last_command = args[0];
|
||||
generation = 0;
|
||||
} else {
|
||||
generation += 1;
|
||||
}
|
||||
|
||||
if (!args.empty()) {
|
||||
iter = lnav_commands.find(args[0]);
|
||||
}
|
||||
|
@ -120,6 +132,16 @@ void rl_change(void *dummy, readline_curses *rc)
|
|||
lnav_data.ld_example_source.replace_with(al);
|
||||
}
|
||||
|
||||
if (cmd.c_prompt != nullptr && generation == 0 &&
|
||||
trim(line) == args[0]) {
|
||||
string new_prompt = cmd.c_prompt(
|
||||
lnav_data.ld_exec_context, line);
|
||||
|
||||
if (!new_prompt.empty()) {
|
||||
rc->rewrite_line(line.length(), new_prompt);
|
||||
}
|
||||
}
|
||||
|
||||
lnav_data.ld_bottom_source.grep_error("");
|
||||
lnav_data.ld_status[LNS_BOTTOM].window_change();
|
||||
}
|
||||
|
|
|
@ -176,7 +176,7 @@ static int recvall(int sock, char *buf, size_t len)
|
|||
off_t offset = 0;
|
||||
|
||||
while (len > 0) {
|
||||
int rc = recv(sock, &buf[offset], len, 0);
|
||||
ssize_t rc = recv(sock, &buf[offset], len, 0);
|
||||
|
||||
if (rc == -1) {
|
||||
switch (errno) {
|
||||
|
@ -600,12 +600,11 @@ void readline_curses::start()
|
|||
char type[32];
|
||||
|
||||
msg[rc] = '\0';
|
||||
if (msg[0] == 'i') {
|
||||
const char *initial = &msg[2];
|
||||
if (sscanf(msg, "i:%d:%n", &rl_point, &prompt_start) == 1) {
|
||||
const char *initial = &msg[prompt_start];
|
||||
|
||||
rl_extend_line_buffer(strlen(initial) + 1);
|
||||
strcpy(rl_line_buffer, initial);
|
||||
rl_point = 0;
|
||||
rl_end = strlen(initial);
|
||||
rl_redisplay();
|
||||
}
|
||||
|
@ -923,7 +922,7 @@ void readline_curses::focus(int context, const char *prompt, const char *initial
|
|||
wmove(this->vc_window, this->get_actual_y(), this->vc_left);
|
||||
wclrtoeol(this->vc_window);
|
||||
if (initial != nullptr) {
|
||||
snprintf(buffer, sizeof(buffer), "i:%s", initial);
|
||||
snprintf(buffer, sizeof(buffer), "i:0:%s", initial);
|
||||
if (sendstring(this->rc_command_pipe[RCF_MASTER],
|
||||
buffer,
|
||||
strlen(buffer) + 1) == -1) {
|
||||
|
@ -932,6 +931,18 @@ void readline_curses::focus(int context, const char *prompt, const char *initial
|
|||
}
|
||||
}
|
||||
|
||||
void readline_curses::rewrite_line(int pos, std::string value)
|
||||
{
|
||||
char buffer[1024];
|
||||
|
||||
snprintf(buffer, sizeof(buffer), "i:%d:%s", pos, value.c_str());
|
||||
if (sendstring(this->rc_command_pipe[RCF_MASTER],
|
||||
buffer,
|
||||
strlen(buffer) + 1) == -1) {
|
||||
perror("focus: write failed");
|
||||
}
|
||||
}
|
||||
|
||||
void readline_curses::abort()
|
||||
{
|
||||
char buffer[1024];
|
||||
|
|
|
@ -71,11 +71,14 @@ class readline_context {
|
|||
public:
|
||||
typedef std::string (*command_func_t)(exec_context &ec,
|
||||
std::string cmdline, std::vector<std::string> &args);
|
||||
typedef std::string (*prompt_func_t)(exec_context &ec,
|
||||
const std::string &cmdline);
|
||||
typedef struct _command_t {
|
||||
const char *c_name;
|
||||
command_func_t c_func;
|
||||
|
||||
struct help_text c_help;
|
||||
prompt_func_t c_prompt{nullptr};
|
||||
|
||||
void operator=(command_func_t func) {
|
||||
this->c_name = "anon";
|
||||
|
@ -312,6 +315,8 @@ public:
|
|||
|
||||
void focus(int context, const char *prompt, const char *initial = nullptr);
|
||||
|
||||
void rewrite_line(int pos, std::string value);
|
||||
|
||||
readline_context *get_active_context() const {
|
||||
require(this->rc_active_context != -1);
|
||||
|
||||
|
|
|
@ -117,6 +117,8 @@ static bool bind_line(sqlite3 *db,
|
|||
|
||||
char timestamp[64];
|
||||
|
||||
sqlite3_clear_bindings(stmt);
|
||||
|
||||
sql_strftime(timestamp, sizeof(timestamp),
|
||||
lf->original_line_time(line_iter), 'T');
|
||||
if (sqlite3_bind_text(stmt, 1,
|
||||
|
@ -499,9 +501,7 @@ static void load_time_bookmarks(void)
|
|||
continue;
|
||||
}
|
||||
|
||||
line_iter = lower_bound(lf->begin(),
|
||||
lf->end(),
|
||||
log_tv);
|
||||
line_iter = lower_bound(lf->begin(), lf->end(), log_tv);
|
||||
while (line_iter != lf->end()) {
|
||||
struct timeval line_tv = line_iter->get_timeval();
|
||||
|
||||
|
@ -523,12 +523,12 @@ static void load_time_bookmarks(void)
|
|||
base_content_line + std::distance(lf->begin(), line_iter));
|
||||
bool meta = false;
|
||||
|
||||
if (part_name != NULL && part_name[0] != '\0') {
|
||||
if (part_name != nullptr && part_name[0] != '\0') {
|
||||
lss.set_user_mark(&textview_curses::BM_META, line_cl);
|
||||
bm_meta[line_cl].bm_name = part_name;
|
||||
meta = true;
|
||||
}
|
||||
if (comment != NULL && comment[0] != '\0') {
|
||||
if (comment != nullptr && comment[0] != '\0') {
|
||||
lss.set_user_mark(&textview_curses::BM_META,
|
||||
line_cl);
|
||||
bm_meta[line_cl].bm_comment = comment;
|
||||
|
|
|
@ -10,24 +10,27 @@ run_test ${lnav_test} -n \
|
|||
-c ":comment Hello, World!" \
|
||||
-c ":tag foo" \
|
||||
-c ":save-session" \
|
||||
-c ":write-raw-to -" \
|
||||
${test_dir}/logfile_access_log.0
|
||||
|
||||
check_output ":tag did not work?" <<EOF
|
||||
192.168.202.254 - - [20/Jul/2009:22:59:26 +0000] "GET /vmw/cgi/tramp HTTP/1.0" 200 134 "-" "gPXE/0.9.7"
|
||||
+ Hello, World!
|
||||
+ #foo
|
||||
// Hello, World!
|
||||
-- #foo
|
||||
192.168.202.254 - - [20/Jul/2009:22:59:29 +0000] "GET /vmw/vSphere/default/vmkboot.gz HTTP/1.0" 404 46210 "-" "gPXE/0.9.7"
|
||||
192.168.202.254 - - [20/Jul/2009:22:59:29 +0000] "GET /vmw/vSphere/default/vmkernel.gz HTTP/1.0" 200 78929 "-" "gPXE/0.9.7"
|
||||
EOF
|
||||
|
||||
run_test ${lnav_test} -n \
|
||||
-c ":load-session" \
|
||||
-c ";UPDATE access_log SET log_mark = 1" \
|
||||
-c ":write-to -" \
|
||||
${test_dir}/logfile_access_log.0
|
||||
|
||||
check_output "tag was not saved in session?" <<EOF
|
||||
192.168.202.254 - - [20/Jul/2009:22:59:26 +0000] "GET /vmw/cgi/tramp HTTP/1.0" 200 134 "-" "gPXE/0.9.7"
|
||||
+ Hello, World!
|
||||
+ #foo
|
||||
// Hello, World!
|
||||
-- #foo
|
||||
192.168.202.254 - - [20/Jul/2009:22:59:29 +0000] "GET /vmw/vSphere/default/vmkboot.gz HTTP/1.0" 404 46210 "-" "gPXE/0.9.7"
|
||||
192.168.202.254 - - [20/Jul/2009:22:59:29 +0000] "GET /vmw/vSphere/default/vmkernel.gz HTTP/1.0" 200 78929 "-" "gPXE/0.9.7"
|
||||
EOF
|
||||
|
|
Loading…
Reference in New Issue