• 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

83.93
/src/log_format_ext.hh
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
 * @file log_format_ext.hh
30
 */
31

32
#ifndef lnav_log_format_ext_hh
33
#define lnav_log_format_ext_hh
34

35
#include <list>
36
#include <string>
37
#include <unordered_map>
38
#include <vector>
39

40
#include "log_format.hh"
41
#include "log_search_table_fwd.hh"
42
#include "yajlpp/yajlpp.hh"
43

44
class module_format;
45

46
class external_log_format : public log_format {
47
public:
48
    struct value_def {
49
        value_def(intern_string_t name,
531,842✔
50
                  value_kind_t kind,
51
                  logline_value_meta::column_t col,
52
                  log_format* format)
53
            : vd_meta(name, kind, col, format)
531,842✔
54
        {
55
        }
531,842✔
56

57
        void set_rewrite_src_name()
408,039✔
58
        {
59
            this->vd_rewrite_src_name = intern_string::lookup(
408,039✔
60
                fmt::format(FMT_STRING("{}:{}"),
2,040,195✔
61
                            this->vd_meta.lvm_format.value()->get_name(),
408,039✔
62
                            this->vd_meta.lvm_name));
408,039✔
63
        }
408,039✔
64

65
        logline_value_meta vd_meta;
66
        std::string vd_collate;
67
        intern_string_t vd_unit_field;
68
        std::map<const intern_string_t, scaling_factor> vd_unit_scaling;
69
        bool vd_internal{false};
70
        std::vector<std::string> vd_action_list;
71
        std::string vd_rewriter;
72
        std::string vd_description;
73
        intern_string_t vd_rewrite_src_name;
74
        std::optional<size_t> vd_line_format_index;
75
        bool vd_is_desc_field{false};
76
    };
77

78
    struct indexed_value_def {
79
        indexed_value_def(int index = -1,
673,324✔
80
                          int unit_index = -1,
81
                          std::shared_ptr<value_def> vd = nullptr)
82
            : ivd_index(index), ivd_unit_field_index(unit_index),
673,324✔
83
              ivd_value_def(std::move(vd))
673,324✔
84
        {
85
        }
673,324✔
86

87
        int ivd_index;
88
        int ivd_unit_field_index;
89
        std::shared_ptr<value_def> ivd_value_def;
90

91
        bool operator<(const indexed_value_def& rhs) const
1,656,367✔
92
        {
93
            return this->ivd_index < rhs.ivd_index;
1,656,367✔
94
        }
95
    };
96

97
    struct pattern {
98
        intern_string_t p_name;
99
        std::string p_config_path;
100
        factory_container<lnav::pcre2pp::code,
101
                          int>::with_default_args<PCRE2_DOTALL>
102
            p_pcre;
103
        std::vector<indexed_value_def> p_value_by_index;
104
        std::map<intern_string_t, int> p_value_name_to_index;
105
        std::vector<int> p_numeric_value_indexes;
106
        int p_timestamp_field_index{-1};
107
        int p_time_field_index{-1};
108
        int p_level_field_index{-1};
109
        int p_module_field_index{-1};
110
        int p_opid_field_index{-1};
111
        int p_subid_field_index{-1};
112
        int p_body_field_index{-1};
113
        int p_src_file_field_index{-1};
114
        int p_src_line_field_index{-1};
115
        int p_timestamp_end{-1};
116
        bool p_module_format{false};
117
        std::set<size_t> p_matched_samples;
118
    };
119

120
    struct level_pattern {
121
        factory_container<lnav::pcre2pp::code> lp_pcre;
122
    };
123

124
    external_log_format(const intern_string_t name)
46,785✔
125
        : elf_level_field(intern_string::lookup("level", -1)),
46,785✔
126
          elf_body_field(intern_string::lookup("body", -1)),
46,785✔
127
          jlf_yajl_handle(nullptr, yajl_handle_deleter()), elf_name(name)
140,355✔
128
    {
129
        this->jlf_line_offsets.reserve(128);
46,785✔
130
    }
46,785✔
131

132
    const intern_string_t get_name() const override { return this->elf_name; }
1,446,144✔
133

134
    match_name_result match_name(const std::string& filename) override;
135

136
    scan_result_t test_line(
137
        sample_t& sample,
138
        std::vector<lnav::console::user_message>& msgs) override;
139

140
    scan_result_t scan(logfile& lf,
141
                       std::vector<logline>& dst,
142
                       const line_info& offset,
143
                       shared_buffer_ref& sbr,
144
                       scan_batch_context& sbc) override;
145

146
    bool scan_for_partial(shared_buffer_ref& sbr,
147
                          size_t& len_out) const override;
148

149
    void annotate(logfile* lf,
150
                  uint64_t line_number,
151
                  string_attrs_t& sa,
152
                  logline_value_vector& values,
153
                  bool annotate_module = true) const override;
154

155
    void rewrite(exec_context& ec,
156
                 shared_buffer_ref& line,
157
                 string_attrs_t& sa,
158
                 std::string& value_out) override;
159

160
    void build(std::vector<lnav::console::user_message>& errors);
161

162
    void register_vtabs(log_vtab_manager* vtab_manager,
163
                        std::vector<lnav::console::user_message>& errors);
164

165
    bool match_samples(const std::vector<sample_t>& samples) const;
166

167
    bool hide_field(const intern_string_t field_name, bool val) override;
168

169
    std::map<intern_string_t, logline_value_meta> get_field_states() override
8,185✔
170
    {
171
        std::map<intern_string_t, logline_value_meta> retval;
8,185✔
172

173
        for (const auto& vd : this->elf_value_defs) {
118,166✔
174
            retval.emplace(vd.first, vd.second->vd_meta);
109,981✔
175
        }
176

177
        return retval;
8,185✔
UNCOV
178
    }
×
179

180
    std::shared_ptr<log_format> specialized(int fmt_lock) override;
181

182
    const logline_value_stats* stats_for_value(
183
        const intern_string_t& name) const override;
184

185
    void get_subline(const logline& ll,
186
                     shared_buffer_ref& sbr,
187
                     subline_options opts) override;
188

189
    std::shared_ptr<log_vtab_impl> get_vtab_impl() const override;
190

UNCOV
191
    const std::vector<std::string>* get_actions(
×
192
        const logline_value& lv) const override
193
    {
UNCOV
194
        const std::vector<std::string>* retval = nullptr;
×
195

UNCOV
196
        const auto iter = this->elf_value_defs.find(lv.lv_meta.lvm_name);
×
UNCOV
197
        if (iter != this->elf_value_defs.end()) {
×
UNCOV
198
            retval = &iter->second->vd_action_list;
×
199
        }
200

UNCOV
201
        return retval;
×
202
    }
203

204
    bool format_changed() override;
205

206
    std::set<std::string> get_source_path() const override
5✔
207
    {
208
        return this->elf_source_path;
5✔
209
    }
210

211
    std::vector<logline_value_meta> get_value_metadata() const override;
212

213
    enum class json_log_field {
214
        CONSTANT,
215
        VARIABLE
216
    };
217

218
    struct json_format_element {
219
        enum class align_t {
220
            NONE,
221
            LEFT,
222
            RIGHT,
223
        };
224

225
        enum class overflow_t {
226
            ABBREV,
227
            TRUNCATE,
228
            DOTDOT,
229
            LASTWORD,
230
        };
231

232
        enum class transform_t {
233
            NONE,
234
            UPPERCASE,
235
            LOWERCASE,
236
            CAPITALIZE,
237
        };
238

239
        json_log_field jfe_type{json_log_field::CONSTANT};
240
        positioned_property<intern_string_t> jfe_value;
241
        std::string jfe_default_value{"-"};
242
        long long jfe_min_width{0};
243
        bool jfe_auto_width{false};
244
        long long jfe_max_width{LLONG_MAX};
245
        align_t jfe_align{align_t::NONE};
246
        overflow_t jfe_overflow{overflow_t::ABBREV};
247
        transform_t jfe_text_transform{transform_t::NONE};
248
        std::string jfe_ts_format;
249
        std::string jfe_prefix;
250
        std::string jfe_suffix;
251
    };
252

253
    struct json_field_cmp {
254
        json_field_cmp(json_log_field type, const intern_string_t name)
255
            : jfc_type(type), jfc_field_name(name)
256
        {
257
        }
258

259
        bool operator()(const json_format_element& jfe) const
260
        {
261
            return (this->jfc_type == jfe.jfe_type
262
                    && this->jfc_field_name == jfe.jfe_value.pp_value);
263
        }
264

265
        json_log_field jfc_type;
266
        const intern_string_t jfc_field_name;
267
    };
268

269
    struct highlighter_def {
270
        factory_container<lnav::pcre2pp::code> hd_pattern;
271
        positioned_property<std::string> hd_color;
272
        positioned_property<std::string> hd_background_color;
273
        bool hd_underline{false};
274
        bool hd_blink{false};
275
    };
276

277
    struct value_line_count_result {
278
        size_t vlcr_count{1};
279
        size_t vlcr_line_format_count{0};
280
        bool vlcr_has_ansi{false};
281
        bool vlcr_valid_utf{true};
282
        std::optional<size_t> vlcr_line_format_index;
283
    };
284

285
    value_line_count_result value_line_count(const value_def* vd,
286
                                             bool top_level,
287
                                             std::optional<double> val,
288
                                             const unsigned char* str,
289
                                             ssize_t len,
290
                                             yajl_string_props_t* props);
291

292
    bool has_value_def(const intern_string_t ist) const
293
    {
294
        const auto iter = this->elf_value_defs.find(ist);
295

296
        return iter != this->elf_value_defs.end();
297
    }
298

299
    std::string get_pattern_path(uint64_t line_number) const override
2✔
300
    {
301
        if (this->elf_type != elf_type_t::ELF_TYPE_TEXT) {
2✔
UNCOV
302
            return "structured";
×
303
        }
304
        int pat_index = this->pattern_index_for_line(line_number);
2✔
305
        return this->elf_pattern_order[pat_index]->p_config_path;
2✔
306
    }
307

308
    intern_string_t get_pattern_name(uint64_t line_number) const override;
309

310
    std::string get_pattern_regex(uint64_t line_number) const override;
311

312
    log_level_t convert_level(string_fragment str,
313
                              scan_batch_context* sbc) const;
314

315
    using mod_map_t = std::map<intern_string_t, module_format>;
316
    static mod_map_t MODULE_FORMATS;
317
    static std::vector<std::shared_ptr<external_log_format>>
318
        GRAPH_ORDERED_FORMATS;
319

320
    std::set<std::string> elf_source_path;
321
    std::vector<std::filesystem::path> elf_format_source_order;
322
    std::map<intern_string_t, int> elf_format_sources;
323
    std::list<intern_string_t> elf_collision;
324
    factory_container<lnav::pcre2pp::code> elf_filename_pcre;
325
    std::map<std::string, std::shared_ptr<pattern>> elf_patterns;
326
    std::vector<std::shared_ptr<pattern>> elf_pattern_order;
327
    std::vector<sample_t> elf_samples;
328
    std::unordered_map<const intern_string_t, std::shared_ptr<value_def>>
329
        elf_value_defs;
330

331
    struct value_defs_state {
332
        size_t vds_generation{0};
333
    };
334

335
    std::shared_ptr<value_defs_state> elf_value_defs_state{
336
        std::make_shared<value_defs_state>()};
337
    value_defs_state elf_specialized_value_defs_state;
338

339
    std::vector<std::shared_ptr<value_def>> elf_value_def_order;
340
    robin_hood::unordered_map<string_fragment, value_def*, frag_hasher>
341
        elf_value_def_frag_map;
342
    std::vector<std::pair<string_fragment, value_def*>>
343
        elf_value_def_read_order;
344
    ArenaAlloc::Alloc<char> elf_allocator{4096};
345
    std::vector<std::shared_ptr<value_def>> elf_numeric_value_defs;
346
    size_t elf_column_count{0};
347
    double elf_timestamp_divisor{1.0};
348
    intern_string_t elf_level_field;
349
    factory_container<lnav::pcre2pp::code> elf_level_pointer;
350
    intern_string_t elf_body_field;
351
    intern_string_t elf_module_id_field;
352
    intern_string_t elf_opid_field;
353
    intern_string_t elf_subid_field;
354
    intern_string_t elf_src_file_field;
355
    intern_string_t elf_src_line_field;
356
    std::map<log_level_t, level_pattern> elf_level_patterns;
357
    std::vector<std::pair<int64_t, log_level_t>> elf_level_pairs;
358
    bool elf_container{false};
359
    bool elf_has_module_format{false};
360
    bool elf_builtin_format{false};
361

362
    struct header_exprs {
363
        std::map<std::string, std::string> he_exprs;
364
    };
365

366
    struct header {
367
        header_exprs h_exprs;
368
        size_t h_size{32};
369
    };
370

371
    struct converter {
372
        std::string c_type;
373
        header c_header;
374
        positioned_property<std::string> c_command;
375
    };
376

377
    converter elf_converter;
378

379
    using search_table_pcre2pp
380
        = factory_container<lnav::pcre2pp::code, int>::with_default_args<
381
            log_search_table_ns::PATTERN_OPTIONS>;
382

383
    struct search_table_def {
384
        search_table_pcre2pp std_pattern;
385
        std::string std_glob;
386
        log_level_t std_level{LEVEL_UNKNOWN};
387
    };
388

389
    std::map<intern_string_t, search_table_def> elf_search_tables;
390
    std::map<const intern_string_t, highlighter_def> elf_highlighter_patterns;
391

392
    enum class elf_type_t {
393
        ELF_TYPE_TEXT,
394
        ELF_TYPE_JSON,
395
        ELF_TYPE_CSV,
396
    };
397

398
    elf_type_t elf_type{elf_type_t::ELF_TYPE_TEXT};
399

400
    scan_result_t scan_json(std::vector<logline>& dst,
401
                            const line_info& li,
402
                            shared_buffer_ref& sbr,
403
                            scan_batch_context& sbc);
404

405
    void update_op_description(
406
        const std::map<intern_string_t, opid_descriptors>& desc_def,
407
        log_op_description& lod,
408
        const pattern* fpat,
409
        const lnav::pcre2pp::match_data& md);
410

411
    void update_op_description(
412
        const std::map<intern_string_t, opid_descriptors>& desc_def,
413
        log_op_description& lod);
414

415
    void json_append_to_cache(const char* value, ssize_t len)
18,095✔
416
    {
417
        if (len <= 0) {
18,095✔
418
            return;
14✔
419
        }
420

421
        const auto old_size = this->jlf_cached_line.size();
18,081✔
422
        this->jlf_cached_line.resize(old_size + len);
18,081✔
423
        memcpy(&this->jlf_cached_line[old_size], value, len);
18,081✔
424
    }
425

426
    void json_append_to_cache(const string_fragment& sf)
4,231✔
427
    {
428
        this->json_append_to_cache(sf.data(), sf.length());
4,231✔
429
    }
4,231✔
430

431
    void json_append_to_cache(ssize_t len)
723✔
432
    {
433
        if (len <= 0) {
723✔
UNCOV
434
            return;
×
435
        }
436
        const size_t old_size = this->jlf_cached_line.size();
723✔
437
        this->jlf_cached_line.resize(old_size + len);
723✔
438
        memset(&this->jlf_cached_line[old_size], ' ', len);
723✔
439
    }
440

441
    void json_append(const json_format_element& jfe,
442
                     const value_def* vd,
443
                     const string_fragment& sf);
444

445
    logline_value_meta get_value_meta(intern_string_t field_name,
446
                                      value_kind_t kind);
447

448
    logline_value_meta get_value_meta(yajlpp_parse_context* ypc,
449
                                      const value_def* vd,
450
                                      value_kind_t kind);
451

452
    std::vector<lnav::console::snippet> get_snippets() const;
453

454
    bool jlf_hide_extra{false};
455
    std::vector<json_format_element> jlf_line_format;
456
    int jlf_line_format_init_count{0};
457
    logline_value_vector jlf_line_values;
458

459
    off_t jlf_cached_offset{-1};
460
    line_range jlf_cached_sub_range;
461
    subline_options jlf_cached_opts{};
462
    std::vector<off_t> jlf_line_offsets;
463
    std::vector<char> jlf_cached_line;
464
    string_attrs_t jlf_line_attrs;
465
    std::shared_ptr<yajlpp_parse_context> jlf_parse_context;
466
    std::shared_ptr<yajl_handle_t> jlf_yajl_handle;
467
    shared_buffer jlf_share_manager;
468

469
private:
470
    const intern_string_t elf_name;
471

472
    static uint8_t module_scan(string_fragment body_cap,
473
                               const intern_string_t& mod_name);
474
};
475

476
class module_format {
477
public:
478
    std::shared_ptr<log_format> mf_mod_format;
479
};
480

481
#endif
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