• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

tstack / lnav / 17589970077-2502

09 Sep 2025 05:00PM UTC coverage: 65.196% (-5.0%) from 70.225%
17589970077-2502

push

github

tstack
[format] add fields for source file/line

Knowing the source file/line context in a log
message can help find log messages when using
log2src.

56 of 70 new or added lines in 2 files covered. (80.0%)

13954 existing lines in 210 files now uncovered.

45516 of 69814 relevant lines covered (65.2%)

404154.37 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

1.06
/src/files_sub_source.cc
1
/**
2
 * Copyright (c) 2020, Timothy Stack
3
 *
4
 * All rights reserved.
5
 *
6
 * Redistribution and use in source and binary forms, with or without
7
 * modification, are permitted provided that the following conditions are met:
8
 *
9
 * * Redistributions of source code must retain the above copyright notice, this
10
 * list of conditions and the following disclaimer.
11
 * * Redistributions in binary form must reproduce the above copyright notice,
12
 * this list of conditions and the following disclaimer in the documentation
13
 * and/or other materials provided with the distribution.
14
 * * Neither the name of Timothy Stack nor the names of its contributors
15
 * may be used to endorse or promote products derived from this software
16
 * without specific prior written permission.
17
 *
18
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY
19
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21
 * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
22
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
25
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28
 */
29

30
#include "files_sub_source.hh"
31

32
#include "base/ansi_scrubber.hh"
33
#include "base/attr_line.builder.hh"
34
#include "base/fs_util.hh"
35
#include "base/humanize.hh"
36
#include "base/humanize.network.hh"
37
#include "base/humanize.time.hh"
38
#include "base/itertools.enumerate.hh"
39
#include "base/itertools.hh"
40
#include "base/opt_util.hh"
41
#include "base/string_util.hh"
42
#include "config.h"
43
#include "lnav.hh"
44
#include "mapbox/variant.hpp"
45
#include "md4cpp.hh"
46
#include "sql_util.hh"
47

48
using namespace md4cpp::literals;
49
using namespace lnav::roles::literals;
50

51
namespace files_model {
52
files_list_selection
UNCOV
53
from_selection(std::optional<vis_line_t> sel_vis)
×
54
{
UNCOV
55
    if (!sel_vis) {
×
UNCOV
56
        return no_selection{};
×
57
    }
58

UNCOV
59
    auto& fc = lnav_data.ld_active_files;
×
60
    int sel = sel_vis.value();
×
61

62
    {
63
        safe::ReadAccess<safe_name_to_errors> errs(*fc.fc_name_to_errors);
×
64

UNCOV
65
        if (sel < (ssize_t) errs->size()) {
×
UNCOV
66
            auto iter = errs->begin();
×
67

68
            std::advance(iter, sel);
UNCOV
69
            return error_selection::build(
×
UNCOV
70
                sel, std::make_pair(iter->first, iter->second.fei_description));
×
71
        }
72

73
        sel -= errs->size();
×
74
    }
75

UNCOV
76
    if (sel < (ssize_t) fc.fc_other_files.size()) {
×
UNCOV
77
        auto iter = fc.fc_other_files.begin();
×
78

79
        std::advance(iter, sel);
UNCOV
80
        return other_selection::build(sel, iter);
×
81
    }
82

UNCOV
83
    sel -= fc.fc_other_files.size();
×
84

UNCOV
85
    if (sel < (ssize_t) fc.fc_files.size()) {
×
UNCOV
86
        auto iter = fc.fc_files.begin();
×
87

88
        std::advance(iter, sel);
UNCOV
89
        return file_selection::build(sel, iter);
×
90
    }
91

UNCOV
92
    return no_selection{};
×
93
}
94
}  // namespace files_model
95

96
bool
UNCOV
97
files_sub_source::list_input_handle_key(listview_curses& lv, const ncinput& ch)
×
98
{
99
    switch (ch.eff_text[0]) {
×
UNCOV
100
        case NCKEY_ENTER:
×
101
        case '\r': {
UNCOV
102
            auto sel = files_model::from_selection(lv.get_selection());
×
103

104
            sel.match(
×
105
                [](files_model::no_selection) {},
×
106
                [](files_model::error_selection) {},
×
107
                [](files_model::other_selection) {},
×
108
                [&](files_model::file_selection& fs) {
×
109
                    auto& lss = lnav_data.ld_log_source;
×
UNCOV
110
                    auto lf = *fs.sb_iter;
×
111

112
                    lf->set_indexing(true);
×
113
                    lss.find_data(lf) | [](auto ld) {
×
114
                        ld->set_visibility(true);
×
UNCOV
115
                        lnav_data.ld_log_source.text_filters_changed();
×
116
                    };
117

118
                    if (lf->get_format() != nullptr) {
×
119
                        auto& log_view = lnav_data.ld_views[LNV_LOG];
×
120
                        lss.row_for_time(lf->front().get_timeval()) |
×
121
                            [](auto row) {
×
UNCOV
122
                                lnav_data.ld_views[LNV_LOG].set_selection(row);
×
123
                            };
UNCOV
124
                        ensure_view(&log_view);
×
125
                    } else {
126
                        auto& tv = lnav_data.ld_views[LNV_TEXT];
×
UNCOV
127
                        auto& tss = lnav_data.ld_text_source;
×
128

129
                        tss.to_front(lf);
×
130
                        tv.reload_data();
×
UNCOV
131
                        ensure_view(&tv);
×
132
                    }
133

134
                    lv.reload_data();
×
135
                    set_view_mode(ln_mode_t::PAGING);
×
UNCOV
136
                });
×
137

UNCOV
138
            return true;
×
139
        }
140

141
        case ' ': {
×
UNCOV
142
            auto sel = files_model::from_selection(lv.get_selection());
×
143

144
            sel.match([](files_model::no_selection) {},
×
145
                      [](files_model::error_selection) {},
×
146
                      [](files_model::other_selection) {},
×
147
                      [&](files_model::file_selection& fs) {
×
148
                          auto& lss = lnav_data.ld_log_source;
×
UNCOV
149
                          auto lf = *fs.sb_iter;
×
150

151
                          lss.find_data(lf) | [](auto ld) {
×
152
                              ld->get_file_ptr()->set_indexing(!ld->ld_visible);
×
UNCOV
153
                              ld->set_visibility(!ld->ld_visible);
×
154
                          };
155

156
                          auto top_view = *lnav_data.ld_view_stack.top();
×
UNCOV
157
                          auto tss = top_view->get_sub_source();
×
158

159
                          if (tss != nullptr) {
×
160
                              if (tss != &lss) {
×
161
                                  lss.text_filters_changed();
×
UNCOV
162
                                  lnav_data.ld_views[LNV_LOG].reload_data();
×
163
                              }
164
                              tss->text_filters_changed();
×
UNCOV
165
                              top_view->reload_data();
×
166
                          }
167

168
                          lv.reload_data();
×
169
                      });
×
UNCOV
170
            return true;
×
171
        }
172
        case 'n': {
×
173
            execute_command(lnav_data.ld_exec_context, "next-mark search");
×
UNCOV
174
            return true;
×
175
        }
176
        case 'N': {
×
177
            execute_command(lnav_data.ld_exec_context, "prev-mark search");
×
UNCOV
178
            return true;
×
179
        }
180
        case '/': {
×
181
            execute_command(lnav_data.ld_exec_context, "prompt search-files");
×
UNCOV
182
            return true;
×
183
        }
184
        case 'X': {
×
UNCOV
185
            auto sel = files_model::from_selection(lv.get_selection());
×
186

187
            sel.match(
×
188
                [](files_model::no_selection) {},
×
189
                [&](files_model::error_selection& es) {
×
UNCOV
190
                    auto& fc = lnav_data.ld_active_files;
×
191

UNCOV
192
                    fc.fc_file_names.erase(es.sb_iter.first);
×
193

194
                    auto name_iter = fc.fc_file_names.begin();
×
195
                    while (name_iter != fc.fc_file_names.end()) {
×
196
                        if (name_iter->first == es.sb_iter.first) {
×
197
                            name_iter = fc.fc_file_names.erase(name_iter);
×
UNCOV
198
                            continue;
×
199
                        }
200

201
                        auto rp_opt = humanize::network::path::from_str(
UNCOV
202
                            name_iter->first);
×
203

204
                        if (rp_opt) {
×
UNCOV
205
                            auto rp = *rp_opt;
×
206

207
                            if (fmt::to_string(rp.home()) == es.sb_iter.first) {
×
208
                                fc.fc_other_files.erase(name_iter->first);
×
209
                                name_iter = fc.fc_file_names.erase(name_iter);
×
UNCOV
210
                                continue;
×
211
                            }
212
                        }
UNCOV
213
                        ++name_iter;
×
214
                    }
215

216
                    fc.fc_name_to_errors->writeAccess()->erase(
×
217
                        es.sb_iter.first);
×
218
                    fc.fc_invalidate_merge = true;
×
219
                    lv.reload_data();
×
220
                },
×
221
                [](files_model::other_selection) {},
×
222
                [](files_model::file_selection) {});
×
UNCOV
223
            return true;
×
224
        }
225
    }
UNCOV
226
    return false;
×
227
}
228

229
void
UNCOV
230
files_sub_source::list_input_handle_scroll_out(listview_curses& lv)
×
231
{
232
    set_view_mode(ln_mode_t::PAGING);
×
UNCOV
233
    lnav_data.ld_filter_view.reload_data();
×
234
}
235

236
size_t
237
files_sub_source::text_line_count()
3,459✔
238
{
239
    const auto& fc = lnav_data.ld_active_files;
3,459✔
240
    auto retval = fc.fc_name_to_errors->readAccess()->size()
3,459✔
241
        + fc.fc_other_files.size() + fc.fc_files.size();
3,459✔
242

243
    return retval;
3,459✔
244
}
245

246
size_t
UNCOV
247
files_sub_source::text_line_width(textview_curses& curses)
×
248
{
UNCOV
249
    return 512;
×
250
}
251

252
line_info
UNCOV
253
files_sub_source::text_value_for_line(textview_curses& tc,
×
254
                                      int line,
255
                                      std::string& value_out,
256
                                      text_sub_source::line_flags_t flags)
257
{
UNCOV
258
    bool selected = line == tc.get_selection();
×
UNCOV
259
    role_t cursor_role = lnav_data.ld_mode == ln_mode_t::FILES
×
UNCOV
260
        ? role_t::VCR_CURSOR_LINE
×
261
        : role_t::VCR_DISABLED_CURSOR_LINE;
UNCOV
262
    const auto& fc = lnav_data.ld_active_files;
×
UNCOV
263
    auto filename_width = std::min(fc.fc_largest_path_length, (size_t) 32);
×
264

UNCOV
265
    this->fss_curr_line.clear();
×
UNCOV
266
    auto& al = this->fss_curr_line;
×
UNCOV
267
    attr_line_builder alb(al);
×
268

UNCOV
269
    if (selected) {
×
UNCOV
270
        al.append(" ", VC_GRAPHIC.value(NCACS_RARROW));
×
271
    } else {
UNCOV
272
        al.append(" ");
×
273
    }
274
    {
UNCOV
275
        safe::ReadAccess<safe_name_to_errors> errs(*fc.fc_name_to_errors);
×
276

UNCOV
277
        if (line < (ssize_t) errs->size()) {
×
UNCOV
278
            auto iter = std::next(errs->begin(), line);
×
UNCOV
279
            auto path = std::filesystem::path(iter->first);
×
280
            auto fn = fmt::to_string(path.filename());
×
281

282
            truncate_to(fn, filename_width);
×
UNCOV
283
            al.append("   ");
×
284
            {
285
                auto ag = alb.with_attr(VC_ROLE.value(role_t::VCR_ERROR));
×
286

287
                al.appendf(FMT_STRING("{:<{}}"), fn, filename_width);
×
288
            }
289
            al.append("   ").append(iter->second.fei_description);
×
290
            if (selected) {
×
291
                al.with_attr_for_all(VC_ROLE.value(cursor_role));
×
292
            }
293

294
            value_out = al.get_string();
×
295
            return {};
×
296
        }
297

298
        line -= errs->size();
×
299
    }
300

UNCOV
301
    if (line < (ssize_t) fc.fc_other_files.size()) {
×
UNCOV
302
        auto iter = std::next(fc.fc_other_files.begin(), line);
×
UNCOV
303
        auto path = std::filesystem::path(iter->first);
×
UNCOV
304
        auto fn = fmt::to_string(path);
×
305

306
        truncate_to(fn, filename_width);
×
307
        al.append("   ");
×
308
        {
UNCOV
309
            auto ag = alb.with_attr(VC_ROLE.value(role_t::VCR_FILE));
×
310

311
            al.appendf(FMT_STRING("{:<{}}"), fn, filename_width);
×
312
        }
313
        al.append("   ")
×
UNCOV
314
            .appendf(FMT_STRING("{:14}"), iter->second.ofd_format)
×
315
            .append("  ")
×
316
            .append(iter->second.ofd_description);
×
317
        if (selected) {
×
UNCOV
318
            al.with_attr_for_all(VC_ROLE.value(cursor_role));
×
319
        }
320
        if (line == (ssize_t) fc.fc_other_files.size() - 1) {
×
321
            al.with_attr_for_all(VC_STYLE.value(text_attrs::with_underline()));
×
322
        }
323

324
        value_out = al.get_string();
×
325
        return {};
×
326
    }
327

328
    line -= fc.fc_other_files.size();
×
329

UNCOV
330
    const auto& lf = fc.fc_files[line];
×
UNCOV
331
    auto ld_opt = lnav_data.ld_log_source.find_data(lf);
×
332
    auto fn = fmt::to_string(std::filesystem::path(lf->get_unique_path()));
×
333

UNCOV
334
    truncate_to(fn, filename_width);
×
UNCOV
335
    al.append(" ");
×
UNCOV
336
    if (ld_opt) {
×
UNCOV
337
        if (ld_opt.value()->ld_visible) {
×
UNCOV
338
            al.append("\u25c6"_ok);
×
339
        } else {
UNCOV
340
            al.append("\u25c7"_comment);
×
341
        }
342
    } else {
UNCOV
343
        al.append("\u25c6"_comment);
×
344
    }
UNCOV
345
    al.append(" ");
×
UNCOV
346
    al.appendf(FMT_STRING("{:<{}}"), fn, filename_width);
×
UNCOV
347
    al.append("  ");
×
348
    {
UNCOV
349
        auto ag = alb.with_attr(VC_ROLE.value(role_t::VCR_NUMBER));
×
350

UNCOV
351
        al.appendf(FMT_STRING("{:>8}"),
×
UNCOV
352
                   humanize::file_size(lf->get_index_size(),
×
353
                                       humanize::alignment::columnar));
354
    }
UNCOV
355
    if (selected) {
×
UNCOV
356
        al.with_attr_for_all(VC_ROLE.value(cursor_role));
×
357
    }
358

UNCOV
359
    value_out = al.get_string();
×
UNCOV
360
    this->fss_last_line_len = value_out.length();
×
361

UNCOV
362
    return {};
×
363
}
364

365
void
UNCOV
366
files_sub_source::text_attrs_for_line(textview_curses& tc,
×
367
                                      int line,
368
                                      string_attrs_t& value_out)
369
{
UNCOV
370
    value_out = this->fss_curr_line.get_attrs();
×
371
}
372

373
size_t
UNCOV
374
files_sub_source::text_size_for_line(textview_curses& tc,
×
375
                                     int line,
376
                                     text_sub_source::line_flags_t raw)
377
{
UNCOV
378
    return 0;
×
379
}
380

381
static auto
UNCOV
382
spinner_index()
×
383
{
384
    auto now = ui_clock::now();
×
385

UNCOV
386
    return std::chrono::duration_cast<std::chrono::milliseconds>(
×
UNCOV
387
               now.time_since_epoch())
×
388
               .count()
×
UNCOV
389
        / 100;
×
390
}
391

392
bool
UNCOV
393
files_overlay_source::list_static_overlay(const listview_curses& lv,
×
394
                                          int y,
395
                                          int bottom,
396
                                          attr_line_t& value_out)
397
{
398
    if (y != 0) {
×
399
        return false;
×
400
    }
401
    static const char PROG[] = "-\\|/";
UNCOV
402
    constexpr size_t PROG_SIZE = sizeof(PROG) - 1;
×
403

UNCOV
404
    auto& fc = lnav_data.ld_active_files;
×
UNCOV
405
    auto fc_prog = fc.fc_progress;
×
UNCOV
406
    safe::WriteAccess<safe_scan_progress> sp(*fc_prog);
×
407

UNCOV
408
    if (!sp->sp_extractions.empty()) {
×
UNCOV
409
        const auto& prog = sp->sp_extractions.front();
×
410

UNCOV
411
        value_out.with_ansi_string(fmt::format(
×
412
            "{} Extracting " ANSI_COLOR(COLOR_CYAN) "{}" ANSI_NORM
413
                                                    "... {:>8}/{}",
UNCOV
414
            PROG[spinner_index() % PROG_SIZE],
×
UNCOV
415
            prog.ep_path.filename().string(),
×
UNCOV
416
            humanize::file_size(prog.ep_out_size, humanize::alignment::none),
×
UNCOV
417
            humanize::file_size(prog.ep_total_size,
×
418
                                humanize::alignment::none)));
419
        return true;
×
420
    }
421
    if (!sp->sp_tailers.empty()) {
×
UNCOV
422
        auto first_iter = sp->sp_tailers.begin();
×
423

424
        value_out.with_ansi_string(fmt::format(
×
425
            "{} Connecting to " ANSI_COLOR(COLOR_CYAN) "{}" ANSI_NORM ": {}",
426
            PROG[spinner_index() % PROG_SIZE],
×
427
            first_iter->first,
×
UNCOV
428
            first_iter->second.tp_message));
×
429
        return true;
×
430
    }
431

432
    return false;
×
433
}
434

435
bool
436
files_sub_source::text_handle_mouse(
×
437
    textview_curses& tc,
438
    const listview_curses::display_line_content_t&,
439
    mouse_event& me)
440
{
UNCOV
441
    auto nci = ncinput{};
×
UNCOV
442
    if (me.is_click_in(mouse_button_t::BUTTON_LEFT, 1, 3)) {
×
UNCOV
443
        nci.id = ' ';
×
UNCOV
444
        nci.eff_text[0] = ' ';
×
UNCOV
445
        this->list_input_handle_key(tc, nci);
×
446
    }
UNCOV
447
    if (me.is_double_click_in(mouse_button_t::BUTTON_LEFT, line_range{4, -1})) {
×
UNCOV
448
        nci.id = '\r';
×
UNCOV
449
        nci.eff_text[0] = '\r';
×
UNCOV
450
        this->list_input_handle_key(tc, nci);
×
451
    }
452

UNCOV
453
    return false;
×
454
}
455

456
void
UNCOV
457
files_sub_source::text_selection_changed(textview_curses& tc)
×
458
{
UNCOV
459
    auto sel = files_model::from_selection(tc.get_selection());
×
UNCOV
460
    std::vector<attr_line_t> details;
×
461

UNCOV
462
    this->fss_details_mtime = std::chrono::microseconds::zero();
×
UNCOV
463
    sel.match(
×
UNCOV
464
        [](files_model::no_selection) {},
×
465

UNCOV
466
        [&details](const files_model::error_selection& es) {
×
UNCOV
467
            auto path = std::filesystem::path(es.sb_iter.first);
×
468

UNCOV
469
            details.emplace_back(
×
470
                attr_line_t().appendf(FMT_STRING("Full path: {}"), path));
×
471
            details.emplace_back(attr_line_t("  ").append(
×
UNCOV
472
                lnav::roles::error(es.sb_iter.second)));
×
473
        },
×
474
        [&details](const files_model::other_selection& os) {
×
475
            auto path = std::filesystem::path(os.sb_iter->first);
×
476

477
            details.emplace_back(
×
478
                attr_line_t().appendf(FMT_STRING("Full path: {}"), path));
×
479
            details.emplace_back(attr_line_t("  ").append("Match Details"_h3));
×
480
            for (const auto& msg : os.sb_iter->second.ofd_details) {
×
481
                auto msg_al = msg.to_attr_line();
×
482

483
                for (auto& msg_line : msg_al.rtrim().split_lines()) {
×
484
                    msg_line.insert(0, 4, ' ');
×
485
                    details.emplace_back(msg_line);
×
486
                }
487
            }
488
        },
×
489
        [&details, this](const files_model::file_selection& fs) {
×
490
            static constexpr auto NAME_WIDTH = 17;
491

492
            auto lf = *fs.sb_iter;
×
493
            this->fss_details_mtime = lf->get_modified_time();
×
UNCOV
494
            auto path = lf->get_filename();
×
UNCOV
495
            auto actual_path = lf->get_actual_path();
×
496
            auto format = lf->get_format();
×
UNCOV
497
            const auto& open_opts = lf->get_open_options();
×
498

UNCOV
499
            details.emplace_back(attr_line_t()
×
UNCOV
500
                                     .appendf(FMT_STRING("{}"), path.filename())
×
UNCOV
501
                                     .with_attr_for_all(VC_STYLE.value(
×
UNCOV
502
                                         text_attrs::with_bold())));
×
UNCOV
503
            details.emplace_back(
×
UNCOV
504
                attr_line_t("  ")
×
UNCOV
505
                    .append(":open_file_folder:"_emoji)
×
UNCOV
506
                    .appendf(FMT_STRING(" {}"), path.parent_path()));
×
507

UNCOV
508
            const auto& decompress_err = lf->get_decompress_error();
×
UNCOV
509
            if (!decompress_err.empty()) {
×
UNCOV
510
                details.emplace_back(
×
UNCOV
511
                    attr_line_t("  ")
×
UNCOV
512
                        .append("Error"_h2)
×
513
                        .append(": ")
×
514
                        .append(lnav::roles::error(decompress_err)));
×
515
            }
516

517
            const auto notes = lf->get_notes();
×
UNCOV
518
            if (!notes.empty()) {
×
519
                details.emplace_back(
×
520
                    attr_line_t("  ").append("Notes"_h2).append(":"));
×
UNCOV
521
                for (const auto& [_kind, note_um] : notes) {
×
UNCOV
522
                    for (const auto& note_line :
×
UNCOV
523
                         note_um.to_attr_line().split_lines())
×
524
                    {
UNCOV
525
                        details.emplace_back(
×
UNCOV
526
                            attr_line_t("    ").append(note_line));
×
527
                    }
528
                }
529
            }
530
            details.emplace_back(attr_line_t("  ").append("General"_h2));
×
531
            if (actual_path) {
×
532
                if (path != actual_path.value()) {
×
533
                    auto line = attr_line_t()
×
534
                                    .append("Actual Path"_h3)
×
UNCOV
535
                                    .right_justify(NAME_WIDTH)
×
UNCOV
536
                                    .append(": ")
×
537
                                    .append(lnav::roles::file(
×
UNCOV
538
                                        fmt::to_string(actual_path.value())))
×
UNCOV
539
                                    .move();
×
UNCOV
540
                    details.emplace_back(line);
×
541
                }
542
            } else {
UNCOV
543
                details.emplace_back(attr_line_t().append("  Piped"));
×
544
            }
UNCOV
545
            details.emplace_back(
×
UNCOV
546
                attr_line_t()
×
UNCOV
547
                    .append("MIME Type"_h3)
×
UNCOV
548
                    .right_justify(NAME_WIDTH)
×
UNCOV
549
                    .append(": ")
×
UNCOV
550
                    .append(fmt::to_string(lf->get_text_format())));
×
UNCOV
551
            details.emplace_back(attr_line_t()
×
UNCOV
552
                                     .append("Last Modified"_h3)
×
UNCOV
553
                                     .right_justify(NAME_WIDTH)
×
UNCOV
554
                                     .append(": ")
×
UNCOV
555
                                     .append(lnav::to_rfc3339_string(
×
UNCOV
556
                                         convert_log_time_to_local(
×
557

UNCOV
558
                                             lf->get_stat().st_mtime),
×
559
                                         0,
560
                                         'T')));
UNCOV
561
            details.emplace_back(
×
UNCOV
562
                attr_line_t()
×
UNCOV
563
                    .append("Size"_h3)
×
UNCOV
564
                    .right_justify(NAME_WIDTH)
×
UNCOV
565
                    .append(": ")
×
UNCOV
566
                    .append(humanize::file_size(lf->get_index_size(),
×
567
                                                humanize::alignment::none)));
UNCOV
568
            if (lf->is_compressed()) {
×
UNCOV
569
                details.emplace_back(attr_line_t()
×
UNCOV
570
                                         .append("Compressed Size"_h3)
×
UNCOV
571
                                         .right_justify(NAME_WIDTH)
×
UNCOV
572
                                         .append(": ")
×
UNCOV
573
                                         .append(humanize::file_size(
×
UNCOV
574
                                             lf->get_stat().st_size,
×
575
                                             humanize::alignment::none)));
576
            }
577

UNCOV
578
            details.emplace_back(attr_line_t()
×
UNCOV
579
                                     .append("Lines"_h3)
×
UNCOV
580
                                     .right_justify(NAME_WIDTH)
×
UNCOV
581
                                     .append(": ")
×
UNCOV
582
                                     .append(lnav::roles::number(fmt::format(
×
UNCOV
583
                                         FMT_STRING("{:L}"), lf->size()))));
×
UNCOV
584
            if (format != nullptr && lf->size() > 0) {
×
UNCOV
585
                details.emplace_back(attr_line_t()
×
UNCOV
586
                                         .append("Time Range"_h3)
×
UNCOV
587
                                         .right_justify(NAME_WIDTH)
×
UNCOV
588
                                         .append(": ")
×
UNCOV
589
                                         .append(lnav::to_rfc3339_string(
×
UNCOV
590
                                             lf->front().get_timeval(), 'T'))
×
UNCOV
591
                                         .append(" - ")
×
UNCOV
592
                                         .append(lnav::to_rfc3339_string(
×
UNCOV
593
                                             lf->back().get_timeval(), 'T')));
×
UNCOV
594
                details.emplace_back(
×
UNCOV
595
                    attr_line_t()
×
UNCOV
596
                        .append("Duration"_h3)
×
UNCOV
597
                        .right_justify(NAME_WIDTH)
×
UNCOV
598
                        .append(": ")
×
UNCOV
599
                        .append(humanize::time::duration::from_tv(
×
UNCOV
600
                                    lf->back().get_timeval()
×
UNCOV
601
                                    - lf->front().get_timeval())
×
UNCOV
602
                                    .to_string()));
×
603
            }
604

UNCOV
605
            auto file_options_opt = lf->get_file_options();
×
UNCOV
606
            if (file_options_opt) {
×
UNCOV
607
                auto& [path, file_options] = file_options_opt.value();
×
608

UNCOV
609
                details.emplace_back(attr_line_t()
×
UNCOV
610
                                         .append("Options Path"_h3)
×
UNCOV
611
                                         .right_justify(NAME_WIDTH)
×
UNCOV
612
                                         .append(": ")
×
UNCOV
613
                                         .append(lnav::roles::file(path)));
×
UNCOV
614
                if (file_options.fo_default_zone.pp_value != nullptr) {
×
UNCOV
615
                    details.emplace_back(attr_line_t()
×
UNCOV
616
                                             .append("Timezone"_h3)
×
UNCOV
617
                                             .right_justify(NAME_WIDTH)
×
UNCOV
618
                                             .append(": ")
×
UNCOV
619
                                             .append(lnav::roles::symbol(
×
620
                                                 file_options.fo_default_zone
UNCOV
621
                                                     .pp_value->name())));
×
622
                }
623
            }
624

625
            {
UNCOV
626
                details.emplace_back(
×
UNCOV
627
                    attr_line_t()
×
UNCOV
628
                        .append("Provenance"_h3)
×
UNCOV
629
                        .right_justify(NAME_WIDTH)
×
UNCOV
630
                        .append(": ")
×
UNCOV
631
                        .append(fmt::to_string(open_opts.loo_source)));
×
UNCOV
632
                details.emplace_back(
×
UNCOV
633
                    attr_line_t()
×
UNCOV
634
                        .append("Flags"_h3)
×
UNCOV
635
                        .right_justify(NAME_WIDTH)
×
UNCOV
636
                        .append(": ")
×
UNCOV
637
                        .append(lnav::roles::for_flag(
×
UNCOV
638
                            "\u2022", open_opts.loo_include_in_session))
×
639
                        .append("include-in-session")
×
640
                        .append(", ")
×
641
                        .append(lnav::roles::for_flag(
×
642
                            "\u2022", open_opts.loo_detect_format))
×
643
                        .append("detect-format"));
644
                if (open_opts.loo_init_location.valid()) {
×
645
                    auto loc = open_opts.loo_init_location.match(
646
                        [](file_location_tail) { return std::string("tail"); },
×
647
                        [](int vl) {
×
648
                            return fmt::format(FMT_STRING("L{:L}"), vl);
×
649
                        },
650
                        [](std::string section) { return section; });
×
651
                    details.emplace_back(attr_line_t()
×
652
                                             .append("Initial Location"_h3)
×
653
                                             .right_justify(NAME_WIDTH)
×
654
                                             .append(": ")
×
655
                                             .append(loc));
656
                }
657
            }
658

659
            {
660
                auto line = attr_line_t("  ")
×
661
                                .append("Log Format"_h2)
×
662
                                .append(": ")
×
UNCOV
663
                                .move();
×
664

UNCOV
665
                if (format != nullptr) {
×
UNCOV
666
                    line.append(lnav::roles::identifier(
×
UNCOV
667
                        format->get_name().to_string()));
×
668
                } else {
UNCOV
669
                    line.append("(none)"_comment);
×
670
                }
UNCOV
671
                details.emplace_back(line);
×
672
            }
673

UNCOV
674
            if (lf->get_format_ptr() != nullptr) {
×
675
                const auto um = lnav::console::user_message::info(
UNCOV
676
                    attr_line_t("The file contents matched this log format and "
×
UNCOV
677
                                "will be shown in the LOG view"));
×
UNCOV
678
                um.to_attr_line().rtrim().split_lines()
×
UNCOV
679
                    | lnav::itertools::for_each([&details](const auto& al) {
×
UNCOV
680
                          details.emplace_back(attr_line_t("    ").append(al));
×
UNCOV
681
                      });
×
UNCOV
682
            } else {
×
683
                auto cmd
UNCOV
684
                    = attr_line_t("lnav -m format ")
×
UNCOV
685
                          .append("format-name",
×
UNCOV
686
                                  VC_STYLE.value(text_attrs::with_underline()))
×
UNCOV
687
                          .append(" test ")
×
UNCOV
688
                          .append(lf->get_filename())
×
UNCOV
689
                          .with_attr_for_all(
×
UNCOV
690
                              VC_ROLE.value(role_t::VCR_QUOTED_CODE));
×
691
                const auto um
UNCOV
692
                    = lnav::console::user_message::info(
×
693

694
                          "The file contents did not match any log "
695
                          "formats and can be accessed in the TEXT view")
UNCOV
696
                          .with_help(attr_line_t("If you expected this file to "
×
697
                                                 "match a particular "
698
                                                 "format, you can run the "
699
                                                 "following to get more "
700
                                                 "details:\n  ")
UNCOV
701
                                         .append(cmd));
×
UNCOV
702
                um.to_attr_line().rtrim().split_lines()
×
UNCOV
703
                    | lnav::itertools::for_each([&details](const auto& al) {
×
UNCOV
704
                          details.emplace_back(attr_line_t("    ").append(al));
×
UNCOV
705
                      });
×
706
            }
707

UNCOV
708
            const auto& match_msgs = lf->get_format_match_messages();
×
UNCOV
709
            details.emplace_back(
×
UNCOV
710
                attr_line_t("    ").append("Match Details"_h3));
×
UNCOV
711
            for (const auto& msg : match_msgs) {
×
UNCOV
712
                auto msg_al = msg.to_attr_line();
×
713

UNCOV
714
                for (auto& msg_line : msg_al.rtrim().split_lines()) {
×
UNCOV
715
                    msg_line.insert(0, 6, ' ');
×
UNCOV
716
                    details.emplace_back(msg_line);
×
717
                }
718
            }
719

UNCOV
720
            const auto& ili = lf->get_invalid_line_info();
×
UNCOV
721
            if (ili.ili_total > 0) {
×
UNCOV
722
                auto dotdot = ili.ili_total
×
723
                    > logfile::invalid_line_info::MAX_INVALID_LINES;
UNCOV
724
                auto um = lnav::console::user_message::error(
×
UNCOV
725
                              attr_line_t()
×
UNCOV
726
                                  .append(lnav::roles::number(
×
UNCOV
727
                                      fmt::to_string(ili.ili_total)))
×
UNCOV
728
                                  .append(" line(s) are not handled by the "
×
729
                                          "format and considered invalid"))
UNCOV
730
                              .with_note(attr_line_t("Lines: ")
×
UNCOV
731
                                             .join(ili.ili_lines, ", ")
×
UNCOV
732
                                             .append(dotdot ? ", ..." : ""))
×
UNCOV
733
                              .with_help(
×
734
                                  "The format may be need to be adjusted to "
UNCOV
735
                                  "capture these lines");
×
736

UNCOV
737
                details.emplace_back(attr_line_t());
×
UNCOV
738
                um.to_attr_line().rtrim().split_lines()
×
UNCOV
739
                    | lnav::itertools::for_each([&details](const auto& al) {
×
UNCOV
740
                          details.emplace_back(attr_line_t("    ").append(al));
×
UNCOV
741
                      });
×
742
            }
743

744
            {
UNCOV
745
                const auto& meta = lf->get_embedded_metadata();
×
746

UNCOV
747
                if (!meta.empty()) {
×
UNCOV
748
                    details.emplace_back(
×
UNCOV
749
                        attr_line_t("  ").append("Embedded Metadata:"_h2));
×
UNCOV
750
                    for (const auto& [index, meta_pair] :
×
UNCOV
751
                         lnav::itertools::enumerate(meta))
×
752
                    {
UNCOV
753
                        details.emplace_back(
×
UNCOV
754
                            attr_line_t("  ")
×
UNCOV
755
                                .appendf(FMT_STRING("[{}]"), index)
×
UNCOV
756
                                .append(" ")
×
UNCOV
757
                                .append(lnav::roles::h3(meta_pair.first)));
×
UNCOV
758
                        details.emplace_back(
×
UNCOV
759
                            attr_line_t("      MIME Type: ")
×
UNCOV
760
                                .append(lnav::roles::symbol(fmt::to_string(
×
UNCOV
761
                                    meta_pair.second.m_format))));
×
UNCOV
762
                        line_range lr{6, -1};
×
UNCOV
763
                        details.emplace_back(attr_line_t().with_attr(
×
UNCOV
764
                            string_attr{lr, VC_GRAPHIC.value(NCACS_HLINE)}));
×
UNCOV
765
                        const auto val_sf = string_fragment::from_str(
×
UNCOV
766
                            meta_pair.second.m_value);
×
UNCOV
767
                        for (const auto val_line : val_sf.split_lines()) {
×
UNCOV
768
                            details.emplace_back(
×
UNCOV
769
                                attr_line_t("      ").append(val_line));
×
770
                        }
771
                    }
772
                }
773
            }
UNCOV
774
        });
×
775

UNCOV
776
    this->fss_details_source->replace_with(details);
×
777
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc