@@ -233,6 +233,105 @@ json_write_row(exec_context& ec,
233233 }
234234}
235235
236+ static void
237+ json_write_logmsg (yajl_gen handle,
238+ const logline_window::logmsg_info& li,
239+ lnav::text_anonymizer& ta,
240+ bool anonymize)
241+ {
242+ const auto & values = li.get_values ();
243+ const auto & ll = li.get_logline ();
244+
245+ {
246+ yajlpp_map obj_map (handle);
247+
248+ obj_map.gen (" log_path" );
249+ obj_map.gen (li.get_file_ptr ()->get_filename ());
250+
251+ obj_map.gen (" log_time" );
252+ {
253+ char ts[64 ];
254+ auto ts_len = sql_strftime (ts, sizeof (ts), ll.get_timeval (), ' T' );
255+ obj_map.gen (ts, ts_len);
256+ }
257+
258+ obj_map.gen (" log_level" );
259+ obj_map.gen (ll.get_level_name ());
260+
261+ if (values.lvv_opid_value ) {
262+ obj_map.gen (" log_opid" );
263+ obj_map.gen (*values.lvv_opid_value );
264+ }
265+
266+ auto hash_res = li.get_line_hash ();
267+ if (hash_res.isOk ()) {
268+ auto hash = hash_res.unwrap ();
269+ auto link
270+ = fmt::format (FMT_STRING (" #msg{:016x}-{}" ),
271+ ll.get_time <std::chrono::microseconds>().count (),
272+ hash);
273+ obj_map.gen (" log_line_link" );
274+ obj_map.gen (link);
275+ }
276+
277+ auto meta_opt = li.get_metadata ();
278+ if (meta_opt) {
279+ auto * meta = *meta_opt;
280+ if (!meta->bm_comment .empty ()) {
281+ obj_map.gen (" log_comment" );
282+ obj_map.gen (meta->bm_comment );
283+ }
284+ if (!meta->bm_tags .empty ()) {
285+ obj_map.gen (" log_tags" );
286+ {
287+ yajlpp_array arr (handle);
288+ for (const auto & tag : meta->bm_tags ) {
289+ arr.gen (tag);
290+ }
291+ }
292+ }
293+ }
294+
295+ for (const auto & lv : values.lvv_values ) {
296+ if (lv.lv_meta .lvm_hidden ) {
297+ continue ;
298+ }
299+
300+ obj_map.gen (lv.lv_meta .lvm_name .get ());
301+ switch (lv.lv_meta .lvm_kind ) {
302+ case value_kind_t ::VALUE_NULL :
303+ obj_map.gen ();
304+ break ;
305+ case value_kind_t ::VALUE_BOOLEAN :
306+ obj_map.gen (lv.lv_value .i ? true : false );
307+ break ;
308+ case value_kind_t ::VALUE_INTEGER :
309+ obj_map.gen (lv.lv_value .i );
310+ break ;
311+ case value_kind_t ::VALUE_FLOAT :
312+ obj_map.gen (lv.lv_value .d );
313+ break ;
314+ case value_kind_t ::VALUE_JSON : {
315+ auto tv = lv.text_value ();
316+ yajl_gen_number (handle, tv, lv.text_length ());
317+ break ;
318+ }
319+ default : {
320+ if (anonymize) {
321+ auto anon = ta.next (
322+ std::string (lv.text_value (), lv.text_length ()));
323+ obj_map.gen (anon);
324+ } else {
325+ obj_map.gen (lv.text_value (), lv.text_length ());
326+ }
327+ break ;
328+ }
329+ }
330+ }
331+ }
332+ yajl_gen_reset (handle, " \n " );
333+ }
334+
236335static Result<std::string, lnav::console::user_message>
237336com_save_to (exec_context& ec,
238337 std::string cmdline,
@@ -272,6 +371,12 @@ com_save_to(exec_context& ec,
272371 split_args.erase (anon_iter);
273372 anonymize = true ;
274373 }
374+ bool write_all = false ;
375+ auto all_iter = std::find (split_args.begin (), split_args.end (), " --all" );
376+ if (all_iter != split_args.end ()) {
377+ split_args.erase (all_iter);
378+ write_all = true ;
379+ }
275380
276381 auto * tc = *lnav_data.ld_view_stack .top ();
277382 auto opt_view_name = find_arg (split_args, " --view" );
@@ -319,10 +424,12 @@ com_save_to(exec_context& ec,
319424 " no query result to write, use ';' to execute a query" );
320425 }
321426 } else if (tc == &lnav_data.ld_views [LNV_LOG ]) {
322- all_user_marks = combined_user_marks (tc->get_bookmarks ());
323- if (all_user_marks.empty ()) {
324- return ec.make_error (
325- " no lines marked to write, use 'm' to mark lines" );
427+ if (!write_all) {
428+ all_user_marks = combined_user_marks (tc->get_bookmarks ());
429+ if (all_user_marks.empty ()) {
430+ return ec.make_error (
431+ " no lines marked to write, use 'm' to mark lines" );
432+ }
326433 }
327434 } else {
328435 return ec.make_error (
@@ -719,126 +826,66 @@ com_save_to(exec_context& ec,
719826 yajlpp_gen gen;
720827
721828 yajl_gen_config (gen, yajl_gen_beautify, 0 );
722- yajl_gen_config (gen, yajl_gen_print_callback, yajl_writer, outfile);
829+
830+ auto flush_gen = [&gen, outfile]() {
831+ const unsigned char * buf;
832+ size_t len;
833+
834+ yajl_gen_get_buf (gen, &buf, &len);
835+ if (len > 0 ) {
836+ fwrite (buf, 1 , len, outfile);
837+ yajl_gen_clear (gen);
838+ }
839+ };
723840
724841 if (tc == &lnav_data.ld_views [LNV_LOG ]) {
725842 auto & lss = lnav_data.ld_log_source ;
726843
727- for (auto iter = all_user_marks.bv_tree .begin ();
728- iter != all_user_marks.bv_tree .end ();
729- ++iter)
730- {
731- if (ec.ec_dry_run && line_count > 10 ) {
732- break ;
733- }
734-
735- auto lw = lss.window_at (*iter);
844+ if (write_all) {
845+ auto inner_height = tc->get_inner_height ();
846+ auto lw = lss.window_to_end (0_vl);
736847
737848 for (const auto & li : *lw) {
738- const auto & values = li. get_values ();
739- const auto & ll = li. get_logline () ;
740-
741- {
742- yajlpp_map obj_map (gen) ;
743-
744- obj_map. gen ( " log_path " );
745- obj_map. gen (li. get_file_ptr ()-> get_filename ());
746-
747- obj_map. gen ( " log_time " );
849+ if (ec. ec_dry_run && line_count > 10 ) {
850+ break ;
851+ }
852+ json_write_logmsg (gen, li, ta, anonymize);
853+ line_count += 1 ;
854+ if (line_count % 1000 == 0 ) {
855+ flush_gen ( );
856+ if ( write_progress (line_count,
857+ static_cast < size_t >(inner_height))
858+ == lnav:: progress_result_t ::interrupt)
748859 {
749- char ts[64 ];
750- auto ts_len = sql_strftime (
751- ts, sizeof (ts), ll.get_timeval (), ' T' );
752- obj_map.gen (ts, ts_len);
753- }
754-
755- obj_map.gen (" log_level" );
756- obj_map.gen (ll.get_level_name ());
757-
758- if (values.lvv_opid_value ) {
759- obj_map.gen (" log_opid" );
760- obj_map.gen (*values.lvv_opid_value );
761- }
762-
763- auto hash_res = li.get_line_hash ();
764- if (hash_res.isOk ()) {
765- auto hash = hash_res.unwrap ();
766- auto link = fmt::format (
767- FMT_STRING (" #msg{:016x}-{}" ),
768- ll.get_time <std::chrono::microseconds>()
769- .count (),
770- hash);
771- obj_map.gen (" log_line_link" );
772- obj_map.gen (link);
773- }
774-
775- auto meta_opt = li.get_metadata ();
776- if (meta_opt) {
777- auto * meta = *meta_opt;
778- if (!meta->bm_comment .empty ()) {
779- obj_map.gen (" log_comment" );
780- obj_map.gen (meta->bm_comment );
781- }
782- if (!meta->bm_tags .empty ()) {
783- obj_map.gen (" log_tags" );
784- {
785- yajlpp_array arr (gen);
786- for (const auto & tag : meta->bm_tags ) {
787- arr.gen (tag);
788- }
789- }
790- }
860+ break ;
791861 }
862+ }
863+ }
864+ } else {
865+ for (auto iter = all_user_marks.bv_tree .begin ();
866+ iter != all_user_marks.bv_tree .end ();
867+ ++iter)
868+ {
869+ if (ec.ec_dry_run && line_count > 10 ) {
870+ break ;
871+ }
792872
793- for (const auto & lv : values.lvv_values ) {
794- if (lv.lv_meta .lvm_hidden ) {
795- continue ;
796- }
873+ auto lw = lss.window_at (*iter);
797874
798- obj_map.gen (lv.lv_meta .lvm_name .get ());
799- switch (lv.lv_meta .lvm_kind ) {
800- case value_kind_t ::VALUE_NULL :
801- obj_map.gen ();
802- break ;
803- case value_kind_t ::VALUE_BOOLEAN :
804- obj_map.gen (lv.lv_value .i ? true : false );
805- break ;
806- case value_kind_t ::VALUE_INTEGER :
807- obj_map.gen (lv.lv_value .i );
808- break ;
809- case value_kind_t ::VALUE_FLOAT :
810- obj_map.gen (lv.lv_value .d );
811- break ;
812- case value_kind_t ::VALUE_JSON : {
813- auto tv = lv.text_value ();
814- yajl_gen_number (gen, tv, lv.text_length ());
815- break ;
816- }
817- default : {
818- if (anonymize) {
819- auto anon = ta.next (std::string (
820- lv.text_value (), lv.text_length ()));
821- obj_map.gen (anon);
822- } else {
823- obj_map.gen (lv.text_value (),
824- lv.text_length ());
825- }
826- break ;
827- }
828- }
829- }
875+ for (const auto & li : *lw) {
876+ json_write_logmsg (gen, li, ta, anonymize);
877+ line_count += 1 ;
830878 }
831- yajl_gen_reset (gen, " \n " );
832- }
833879
834- if (line_count > 0 && line_count % 1000 == 0 ) {
835- if (write_progress (line_count, all_user_marks.size ())
836- == lnav::progress_result_t ::interrupt)
837- {
838- break ;
880+ if (line_count > 0 && line_count % 1000 == 0 ) {
881+ flush_gen ();
882+ if (write_progress (line_count, all_user_marks.size ())
883+ == lnav::progress_result_t ::interrupt)
884+ {
885+ break ;
886+ }
839887 }
840888 }
841- line_count += 1 ;
842889 }
843890 } else {
844891 for (size_t row = 0 ; row < dls.dls_row_cursors .size (); row++) {
@@ -849,6 +896,7 @@ com_save_to(exec_context& ec,
849896 json_write_row (ec, gen, row, ta, anonymize);
850897 yajl_gen_reset (gen, " \n " );
851898 if (row > 0 && row % 1000 == 0 ) {
899+ flush_gen ();
852900 if (write_progress (row, dls.dls_row_cursors .size ())
853901 == lnav::progress_result_t ::interrupt)
854902 {
@@ -858,6 +906,7 @@ com_save_to(exec_context& ec,
858906 line_count += 1 ;
859907 }
860908 }
909+ flush_gen ();
861910 } else if (args[0 ] == " write-screen-to" ) {
862911 bool wrapped = tc->get_word_wrap ();
863912 auto orig_top = tc->get_top ();
@@ -2224,8 +2273,12 @@ static readline_context::command_t IO_COMMANDS[] = {
22242273 com_save_to,
22252274
22262275 help_text (" :write-jsonlines-to" )
2227- .with_summary (" Write SQL results or marked log lines to "
2276+ .with_summary (" Write SQL results or log lines to "
22282277 " the given file in JSON Lines format" )
2278+ .with_parameter (help_text (" --all" ,
2279+ " Write all visible log lines instead of "
2280+ " only marked lines" )
2281+ .flag ())
22292282 .with_parameter (
22302283 help_text (" --anonymize" , " Anonymize the JSON values" ).flag ())
22312284 .with_parameter (
0 commit comments