[text] pretty-printing should work in the text view

pull/254/head
Timothy Stack 9 years ago
parent b4cb092ce1
commit 0d37a8c142

@ -30,6 +30,7 @@ lnav v0.8.0:
summary of the currently entered command. summary of the currently entered command.
Fixes: Fixes:
* The pretty-print view should now work for text files.
* Nested fields in JSON logs are now supported for levels, bodies, etc... * Nested fields in JSON logs are now supported for levels, bodies, etc...
* Tab-completion should work for quoted SQL identifiers. * Tab-completion should work for quoted SQL identifiers.
* 'lo-fi' mode key shortcut changed to CTRL+L. * 'lo-fi' mode key shortcut changed to CTRL+L.

@ -183,7 +183,7 @@ Display
* - |ks| q |ke| * - |ks| q |ke|
- Return to the previous view/quit - Return to the previous view/quit
* - |ks| Shift |ke| + |ks| p |ke| * - |ks| Shift |ke| + |ks| p |ke|
- Switch to/from the pretty-printed view of the displayed log messages - Switch to/from the pretty-printed view of the displayed log or text files
* - |ks| Shift |ke| + |ks| t |ke| * - |ks| Shift |ke| + |ks| t |ke|
- Display elapsed time between lines - Display elapsed time between lines
* - |ks| t |ke| * - |ks| t |ke|

@ -50,6 +50,7 @@ set(diag_STAT_SRCS
state-extension-functions.cc state-extension-functions.cc
strnatcmp.c strnatcmp.c
textview_curses.cc textview_curses.cc
timer.cc
view_curses.cc view_curses.cc
views_vtab.cc views_vtab.cc
vt52_curses.cc vt52_curses.cc
@ -112,6 +113,7 @@ set(diag_STAT_SRCS
termios_guard.hh termios_guard.hh
textfile_sub_source.hh textfile_sub_source.hh
time_T.hh time_T.hh
timer.hh
top_status_source.hh top_status_source.hh
url_loader.hh url_loader.hh
views_vtab.hh views_vtab.hh

@ -166,9 +166,9 @@ through the file.
>/< Move horizontally to the next/previous search hit. >/< Move horizontally to the next/previous search hit.
P Switch to/from the pretty-printed view of the log messages P Switch to/from the pretty-printed view of the log or text
currently displayed. In this view, structured data, such files currently displayed. In this view, structured data,
as XML, will be reformatted to make it easier to read. such as XML, will be reformatted to make it easier to read.
t Switch to/from the text file view. The text file view is t Switch to/from the text file view. The text file view is
for any files that are not recognized as log files. for any files that are not recognized as log files.

@ -817,13 +817,7 @@ void handle_paging_key(int ch)
break; break;
case 'P': case 'P':
if (tc == &lnav_data.ld_views[LNV_PRETTY] || toggle_view(&lnav_data.ld_views[LNV_PRETTY]);
(lss && lss->text_line_count() > 0)) {
toggle_view(&lnav_data.ld_views[LNV_PRETTY]);
}
else {
lnav_data.ld_rl_view->set_value("Pretty-printed only works with log messages");
}
break; break;
case 't': case 't':

@ -757,14 +757,25 @@ static void open_schema_view(void)
static void open_pretty_view(void) static void open_pretty_view(void)
{ {
textview_curses *log_tc = &lnav_data.ld_views[LNV_LOG]; static const char *NOTHING_MSG =
"Nothing to pretty-print";
textview_curses *top_tc = lnav_data.ld_view_stack.top();
textview_curses *pretty_tc = &lnav_data.ld_views[LNV_PRETTY]; textview_curses *pretty_tc = &lnav_data.ld_views[LNV_PRETTY];
logfile_sub_source &lss = lnav_data.ld_log_source; textview_curses *log_tc = &lnav_data.ld_views[LNV_LOG];
if (lss.text_line_count() > 0) { textview_curses *text_tc = &lnav_data.ld_views[LNV_TEXT];
ostringstream stream; ostringstream stream;
delete pretty_tc->get_sub_source();
if (top_tc->get_inner_height() == 0) {
pretty_tc->set_sub_source(new plain_text_source(NOTHING_MSG));
return;
}
if (top_tc == log_tc) {
logfile_sub_source &lss = lnav_data.ld_log_source;
bool first_line = true; bool first_line = true;
delete pretty_tc->get_sub_source();
for (vis_line_t vl = log_tc->get_top(); vl <= log_tc->get_bottom(); ++vl) { for (vis_line_t vl = log_tc->get_top(); vl <= log_tc->get_bottom(); ++vl) {
content_line_t cl = lss.at(vl); content_line_t cl = lss.at(vl);
logfile *lf = lss.find(cl); logfile *lf = lss.find(cl);
@ -784,15 +795,26 @@ static void open_pretty_view(void)
stream << trim(pp.print()) << endl; stream << trim(pp.print()) << endl;
first_line = false; first_line = false;
} }
pretty_tc->set_sub_source(new plain_text_source(stream.str())); }
if (lnav_data.ld_last_pretty_print_top != log_tc->get_top()) { else if (top_tc == text_tc) {
pretty_tc->set_top(vis_line_t(0)); logfile *lf = lnav_data.ld_text_source.current_file();
for (vis_line_t vl = text_tc->get_top(); vl <= text_tc->get_bottom(); ++vl) {
logfile::iterator ll = lf->begin() + vl;
shared_buffer_ref sbr;
lf->read_full_message(ll, sbr);
data_scanner ds(sbr);
pretty_printer pp(&ds);
stream << pp.print() << endl;
} }
lnav_data.ld_last_pretty_print_top = log_tc->get_top();
} }
else { pretty_tc->set_sub_source(new plain_text_source(stream.str()));
log_warning("no log data to pretty-print"); if (lnav_data.ld_last_pretty_print_top != log_tc->get_top()) {
pretty_tc->set_top(vis_line_t(0));
} }
lnav_data.ld_last_pretty_print_top = log_tc->get_top();
redo_search(LNV_PRETTY); redo_search(LNV_PRETTY);
} }
@ -2795,7 +2817,7 @@ int main(int argc, char *argv[])
if (lnav_data.ld_flags & LNF_HEADLESS) { if (lnav_data.ld_flags & LNF_HEADLESS) {
std::vector<pair<string, string> > msgs; std::vector<pair<string, string> > msgs;
std::vector<pair<string, string> >::iterator msg_iter; std::vector<pair<string, string> >::iterator msg_iter;
textview_curses *log_tc, *tc; textview_curses *log_tc, *text_tc, *tc;
attr_line_t al; attr_line_t al;
const std::string &line = al.get_string(); const std::string &line = al.get_string();
bool found_error = false; bool found_error = false;
@ -2809,7 +2831,14 @@ int main(int argc, char *argv[])
wait_for_pipers(); wait_for_pipers();
rebuild_indexes(true); rebuild_indexes(true);
lnav_data.ld_views[LNV_LOG].set_top(vis_line_t(0)); log_tc->set_top(vis_line_t(0));
text_tc = &lnav_data.ld_views[LNV_TEXT];
text_tc->set_top(vis_line_t(0));
text_tc->set_height(vis_line_t(text_tc->get_inner_height()));
if (lnav_data.ld_log_source.text_line_count() == 0 &&
lnav_data.ld_text_source.text_line_count() > 0) {
toggle_view(&lnav_data.ld_views[LNV_TEXT]);
}
log_info("Executing initial commands"); log_info("Executing initial commands");
execute_init_commands(msgs); execute_init_commands(msgs);

@ -41,6 +41,7 @@
#include <stack> #include <stack>
#include <deque> #include <deque>
#include <sstream> #include <sstream>
#include <iomanip>
#include "timer.hh" #include "timer.hh"
#include "ansi_scrubber.hh" #include "ansi_scrubber.hh"
@ -65,7 +66,10 @@ public:
}; };
pretty_printer(data_scanner *ds) pretty_printer(data_scanner *ds)
: pp_depth(0), pp_line_length(0), pp_scanner(ds) { : pp_leading_indent(0),
pp_depth(0),
pp_line_length(0),
pp_scanner(ds) {
this->pp_body_lines.push(0); this->pp_body_lines.push(0);
}; };
@ -118,11 +122,20 @@ public:
continue; continue;
} }
break; break;
case DT_WHITE:
if (this->pp_values.empty() && this->pp_depth == 0) {
this->pp_leading_indent = el.e_capture.length();
continue;
}
break;
default: default:
break; break;
} }
this->pp_values.push_back(el); this->pp_values.push_back(el);
} }
while (this->pp_depth > 0) {
this->ascend();
}
this->flush_values(); this->flush_values();
return this->pp_stream.str(); return this->pp_stream.str();
@ -157,7 +170,7 @@ private:
break; break;
} }
if (rc == 1 && reverse_lookup_enabled) { if (rc == 1 && reverse_lookup_enabled) {
const struct timeval timeout = {3, 0}; const struct timeval timeout = {0, 500 * 1000};
{ {
timer::interrupt_timer t(timeout, sigalrm_handler); timer::interrupt_timer t(timeout, sigalrm_handler);
@ -238,13 +251,17 @@ private:
} }
void append_indent() { void append_indent() {
this->pp_stream << std::string(this->pp_leading_indent, ' ');
if (this->pp_stream.tellp() == this->pp_leading_indent) {
return;
}
for (int lpc = 0; lpc < this->pp_depth; lpc++) { for (int lpc = 0; lpc < this->pp_depth; lpc++) {
this->pp_stream << " "; this->pp_stream << " ";
} }
} }
void write_element(const element &el) { void write_element(const element &el) {
if (this->pp_depth > 0 && this->pp_line_length == 0 && el.e_token == DT_WHITE) { if (this->pp_line_length == 0 && el.e_token == DT_WHITE) {
return; return;
} }
if (this->pp_line_length == 0 && el.e_token == DT_LINE) { if (this->pp_line_length == 0 && el.e_token == DT_LINE) {
@ -270,6 +287,7 @@ private:
} }
} }
int pp_leading_indent;
int pp_depth; int pp_depth;
int pp_line_length; int pp_line_length;
std::stack<int> pp_body_lines; std::stack<int> pp_body_lines;

@ -27,10 +27,12 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
#include "config.h"
#include "timer.hh" #include "timer.hh"
#include "lnav_log.hh" #include "lnav_log.hh"
const struct itimerval disable = { static const struct itimerval DISABLE_TV = {
{ 0, 0 }, { 0, 0 },
{ 0, 0 } { 0, 0 }
}; };
@ -39,20 +41,25 @@ timer::error::error(int err):e_err(err) { }
timer::interrupt_timer::interrupt_timer(struct timeval t, timer::interrupt_timer::interrupt_timer(struct timeval t,
sighandler_t_ sighandler=SIG_IGN) : new_handler(sighandler), sighandler_t_ sighandler=SIG_IGN) : new_handler(sighandler),
old_handler(NULL), new_val((struct itimerval){{0,0},t}), new_val((struct itimerval){{0,0},t}),
old_val(disable), armed(false) { } old_val(DISABLE_TV), armed(false) {
memset(&this->old_handler, 0, sizeof(this->old_handler));
}
int timer::interrupt_timer::arm_timer() { int timer::interrupt_timer::arm_timer() {
struct sigaction sa;
// Disable the interval timer before setting the handler and arming the // Disable the interval timer before setting the handler and arming the
// interval timer or else we will have a race-condition where the timer // interval timer or else we will have a race-condition where the timer
// might fire and the appropriate handler might not be set. // might fire and the appropriate handler might not be set.
if (setitimer(ITIMER_REAL, &disable, &this->old_val) != 0) { if (setitimer(ITIMER_REAL, &DISABLE_TV, &this->old_val) != 0) {
log_error("Unable to disable the timer: %s", log_error("Unable to disable the timer: %s",
strerror(errno)); strerror(errno));
return -1; return -1;
} }
this->old_handler = signal(SIGALRM, this->new_handler); memset(&sa, 0, sizeof(sa));
if (this->old_handler == SIG_ERR) { sa.sa_handler = this->new_handler;
if (sigaction(SIGALRM, &sa, &this->old_handler) == -1) {
log_error("Unable to set the signal handler: %s", log_error("Unable to set the signal handler: %s",
strerror(errno)); strerror(errno));
if (setitimer(ITIMER_REAL, &this->old_val, NULL) != 0) { if (setitimer(ITIMER_REAL, &this->old_val, NULL) != 0) {
@ -64,7 +71,7 @@ int timer::interrupt_timer::arm_timer() {
} }
if (setitimer(ITIMER_REAL, &this->new_val, NULL) != 0) { if (setitimer(ITIMER_REAL, &this->new_val, NULL) != 0) {
if(signal(SIGALRM, this->old_handler) == SIG_ERR) { if(sigaction(SIGALRM, &this->old_handler, NULL) == -1) {
log_error("Unable to reset the signal handler: %s", log_error("Unable to reset the signal handler: %s",
strerror(errno)); strerror(errno));
throw timer::error(errno); throw timer::error(errno);
@ -86,12 +93,12 @@ void timer::interrupt_timer::disarm_timer() {
// the previous interval timer or else we will have a race-condition // the previous interval timer or else we will have a race-condition
// where the timer might fire and the appropriate handler might not be // where the timer might fire and the appropriate handler might not be
// set. // set.
if (setitimer(ITIMER_REAL, &disable, NULL) != 0) { if (setitimer(ITIMER_REAL, &DISABLE_TV, NULL) != 0) {
log_error("Failed to disable the timer: %s", log_error("Failed to disable the timer: %s",
strerror(errno)); strerror(errno));
throw timer::error(errno); throw timer::error(errno);
} }
if (signal(SIGALRM, this->old_handler) == SIG_ERR) { if (sigaction(SIGALRM, &this->old_handler, NULL) == -1) {
log_error("Failed to reinstall previous SIGALRM handler: %s", log_error("Failed to reinstall previous SIGALRM handler: %s",
strerror(errno)); strerror(errno));
throw timer::error(errno); throw timer::error(errno);
@ -102,8 +109,8 @@ void timer::interrupt_timer::disarm_timer() {
throw timer::error(errno); throw timer::error(errno);
} }
this->armed = false; this->armed = false;
this->old_val = disable; this->old_val = DISABLE_TV;
this->old_handler = NULL; memset(&this->old_handler, 0, sizeof(this->old_handler));
} }
} }

@ -56,7 +56,8 @@ class interrupt_timer {
bool is_armed(); bool is_armed();
~interrupt_timer(); ~interrupt_timer();
private: private:
sighandler_t_ new_handler, old_handler; sighandler_t_ new_handler;
struct sigaction old_handler;
struct itimerval new_val, old_val; struct itimerval new_val, old_val;
bool armed; bool armed;
}; };

@ -251,6 +251,8 @@ dist_noinst_DATA = \
logfile_with_a_really_long_name_to_test_a_bug_with_long_names.0 \ logfile_with_a_really_long_name_to_test_a_bug_with_long_names.0 \
multiline.lnav \ multiline.lnav \
mvwattrline_output.0 \ mvwattrline_output.0 \
textfile_json_indented.0 \
textfile_json_one_line.0 \
view_colors_output.0 \ view_colors_output.0 \
vt52_curses_input.0 \ vt52_curses_input.0 \
vt52_curses_input.1 \ vt52_curses_input.1 \

@ -355,6 +355,42 @@ check_output "pipe-line-to env vars are not working" <<EOF
EOF EOF
run_test ${lnav_test} -n \
-c ":switch-to-view pretty" \
${test_dir}/textfile_json_one_line.0
check_output "pretty-printer is not working for text files" <<EOF
{
"foo bar" : null,
"array" : [
1,
2,
3
],
"obj" : {
"one" : 1,
"two" : true
}
}
EOF
run_test ${lnav_test} -n \
-c ":switch-to-view pretty" \
${test_dir}/textfile_json_one_line.0
check_output "pretty-printer is not working for indented text files" <<EOF
{
"foo bar": null,
"array": [
1,
2,
3
],
"obj": {
"one": 1,
"two": true
}
}
EOF
run_test ${lnav_test} -n \ run_test ${lnav_test} -n \
-c ":switch-to-view pretty" \ -c ":switch-to-view pretty" \
${test_dir}/logfile_vami.0 ${test_dir}/logfile_vami.0

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

@ -0,0 +1 @@
{ "foo bar" : null, "array" : [1, 2, 3], "obj" : { "one" : 1, "two" : true } }
Loading…
Cancel
Save