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

tstack / lnav / 19080893927-2636

04 Nov 2025 07:43PM UTC coverage: 68.931% (+0.03%) from 68.898%
19080893927-2636

push

github

tstack
[tidy] some more cleanup

9 of 29 new or added lines in 5 files covered. (31.03%)

1855 existing lines in 27 files now uncovered.

50572 of 73366 relevant lines covered (68.93%)

428466.05 hits per line

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

85.88
/src/log_format.cc
1
/**
2
 * Copyright (c) 2007-2015, 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 <algorithm>
31
#include <chrono>
32
#include <memory>
33
#include <vector>
34

35
#include <fnmatch.h>
36
#include <stdio.h>
37
#include <string.h>
38

39
#include "base/fs_util.hh"
40
#include "base/humanize.hh"
41
#include "base/intern_string.hh"
42
#include "base/is_utf8.hh"
43
#include "base/itertools.enumerate.hh"
44
#include "base/itertools.hh"
45
#include "base/map_util.hh"
46
#include "base/opt_util.hh"
47
#include "base/snippet_highlighters.hh"
48
#include "base/string_attr_type.hh"
49
#include "base/string_util.hh"
50
#include "bookmarks.hh"
51
#include "command_executor.hh"
52
#include "config.h"
53
#include "fmt/format.h"
54
#include "lnav_util.hh"
55
#include "log_format_ext.hh"
56
#include "log_search_table.hh"
57
#include "log_vtab_impl.hh"
58
#include "ptimec.hh"
59
#include "readline_highlighters.hh"
60
#include "scn/scan.h"
61
#include "sql_util.hh"
62
#include "sqlite-extension-func.hh"
63
#include "sqlitepp.hh"
64
#include "yajlpp/yajlpp.hh"
65
#include "yajlpp/yajlpp_def.hh"
66

67
using namespace lnav::roles::literals;
68
using namespace std::chrono_literals;
69

70
static auto intern_lifetime = intern_string::get_table_lifetime();
71

72
constexpr string_attr_type<void> L_PREFIX("prefix");
73
constexpr string_attr_type<void> L_TIMESTAMP("timestamp");
74
constexpr string_attr_type<void> L_LEVEL("level");
75
constexpr string_attr_type<std::shared_ptr<logfile>> L_FILE("file");
76
constexpr string_attr_type<bookmark_metadata*> L_PARTITION("partition");
77
constexpr string_attr_type<void> L_MODULE("module");
78
constexpr string_attr_type<void> L_OPID("opid");
79
constexpr string_attr_type<bookmark_metadata*> L_META("meta");
80

81
external_log_format::mod_map_t external_log_format::MODULE_FORMATS;
82
std::vector<std::shared_ptr<external_log_format>>
83
    external_log_format::GRAPH_ORDERED_FORMATS;
84

85
const intern_string_t log_format::LOG_TIME_STR
86
    = intern_string::lookup("log_time");
87
const intern_string_t log_format::LOG_LEVEL_STR
88
    = intern_string::lookup("log_level");
89
const intern_string_t log_format::LOG_OPID_STR
90
    = intern_string::lookup("log_opid");
91
const intern_string_t log_format::LOG_THREAD_ID_STR
92
    = intern_string::lookup("log_thread_id");
93

94
static constexpr uint32_t DATE_TIME_SET_FLAGS = ETF_YEAR_SET | ETF_MONTH_SET
95
    | ETF_DAY_SET | ETF_HOUR_SET | ETF_MINUTE_SET | ETF_SECOND_SET;
96

97
log_level_stats&
98
log_level_stats::operator|=(const log_level_stats& rhs)
846✔
99
{
100
    this->lls_error_count += rhs.lls_error_count;
846✔
101
    this->lls_warning_count += rhs.lls_warning_count;
846✔
102
    this->lls_total_count += rhs.lls_total_count;
846✔
103

104
    return *this;
846✔
105
}
106

107
log_op_description&
108
log_op_description::operator|=(const log_op_description& rhs)
379✔
109
{
110
    if (!this->lod_index && rhs.lod_index) {
379✔
111
        this->lod_index = rhs.lod_index;
×
112
    }
113
    if (this->lod_elements.size() < rhs.lod_elements.size()) {
379✔
114
        this->lod_elements = rhs.lod_elements;
×
115
    }
116

117
    return *this;
379✔
118
}
119

120
void
121
opid_time_range::clear()
×
122
{
123
    this->otr_range.invalidate();
×
124
    this->otr_sub_ops.clear();
×
125
    this->otr_level_stats = {};
×
126
}
127

128
opid_time_range&
129
opid_time_range::operator|=(const opid_time_range& rhs)
379✔
130
{
131
    this->otr_range |= rhs.otr_range;
379✔
132
    this->otr_description |= rhs.otr_description;
379✔
133
    this->otr_level_stats |= rhs.otr_level_stats;
379✔
134
    for (const auto& rhs_sub : rhs.otr_sub_ops) {
384✔
135
        bool found = false;
5✔
136

137
        for (auto& sub : this->otr_sub_ops) {
10✔
138
            if (sub.ostr_subid == rhs_sub.ostr_subid) {
5✔
139
                sub.ostr_range |= rhs_sub.ostr_range;
5✔
140
                found = true;
5✔
141
            }
142
        }
143
        if (!found) {
5✔
144
            this->otr_sub_ops.emplace_back(rhs_sub);
×
145
        }
146
    }
147
    std::stable_sort(this->otr_sub_ops.begin(), this->otr_sub_ops.end());
379✔
148

149
    return *this;
379✔
150
}
151

152
void
153
thread_id_time_range::clear()
×
154
{
155
    this->titr_range.invalidate();
×
156
    this->titr_level_stats = {};
×
157
}
158

159
thread_id_time_range&
160
thread_id_time_range::operator|=(const thread_id_time_range& rhs)
467✔
161
{
162
    this->titr_range |= rhs.titr_range;
467✔
163
    this->titr_level_stats |= rhs.titr_level_stats;
467✔
164

165
    return *this;
467✔
166
}
167

168
void
169
log_level_stats::update_msg_count(log_level_t lvl, int32_t amount)
22,397✔
170
{
171
    switch (lvl) {
22,397✔
172
        case LEVEL_FATAL:
1,222✔
173
        case LEVEL_CRITICAL:
174
        case LEVEL_ERROR:
175
            this->lls_error_count += amount;
1,222✔
176
            break;
1,222✔
177
        case LEVEL_WARNING:
111✔
178
            this->lls_warning_count += amount;
111✔
179
            break;
111✔
180
        default:
21,064✔
181
            break;
21,064✔
182
    }
183
    this->lls_total_count += amount;
22,397✔
184
}
22,397✔
185

186
void
187
opid_time_range::close_sub_ops(const string_fragment& subid)
×
188
{
189
    for (auto& other_sub : this->otr_sub_ops) {
×
190
        if (other_sub.ostr_subid == subid) {
×
191
            other_sub.ostr_open = false;
×
192
        }
193
    }
194
}
195

196
log_thread_id_map::iterator
197
log_thread_id_state::insert_tid(ArenaAlloc::Alloc<char>& alloc,
10,417✔
198
                                const string_fragment& tid,
199
                                const std::chrono::microseconds& us)
200
{
201
    auto retval = this->ltis_tid_ranges.find(tid);
10,417✔
202
    if (retval == this->ltis_tid_ranges.end()) {
10,417✔
203
        auto tid_copy = tid.to_owned(alloc);
5,289✔
204
        auto titr = thread_id_time_range{time_range{us, us}};
5,289✔
205
        auto emplace_res = this->ltis_tid_ranges.emplace(tid_copy, titr);
5,289✔
206
        retval = emplace_res.first;
5,289✔
207
    } else {
208
        retval->second.titr_range.extend_to(us);
5,128✔
209
    }
210

211
    return retval;
10,417✔
212
}
213

214
log_opid_map::iterator
215
log_opid_state::insert_op(ArenaAlloc::Alloc<char>& alloc,
11,916✔
216
                          const string_fragment& opid,
217
                          const std::chrono::microseconds& us,
218
                          timestamp_point_of_reference_t poref,
219
                          std::chrono::microseconds duration)
220
{
221
    auto retval = this->los_opid_ranges.find(opid);
11,916✔
222
    if (retval == this->los_opid_ranges.end()) {
11,916✔
223
        auto opid_copy = opid.to_owned(alloc);
5,995✔
224
        auto otr = opid_time_range{time_range{us, us}};
5,995✔
225
        auto emplace_res = this->los_opid_ranges.emplace(opid_copy, otr);
5,995✔
226
        retval = emplace_res.first;
5,995✔
227
    } else {
5,995✔
228
        retval->second.otr_range.extend_to(us);
5,921✔
229
    }
230
    if (duration > 0us) {
11,916✔
231
        auto other_us = us;
104✔
232
        switch (poref) {
104✔
233
            case timestamp_point_of_reference_t::send:
18✔
234
                other_us -= duration;
18✔
235
                break;
18✔
236
            case timestamp_point_of_reference_t::start:
86✔
237
                other_us += duration;
86✔
238
                break;
86✔
239
        }
240
        retval->second.otr_range.extend_to(other_us);
104✔
241
    }
242

243
    return retval;
11,916✔
244
}
245

246
opid_sub_time_range*
247
log_opid_state::sub_op_in_use(ArenaAlloc::Alloc<char>& alloc,
64✔
248
                              log_opid_map::iterator& op_iter,
249
                              const string_fragment& subid,
250
                              const std::chrono::microseconds& us,
251
                              log_level_t level)
252
{
253
    const auto& opid = op_iter->first;
64✔
254
    auto sub_iter = this->los_sub_in_use.find(subid);
64✔
255
    if (sub_iter == this->los_sub_in_use.end()) {
64✔
256
        auto emp_res
257
            = this->los_sub_in_use.emplace(subid.to_owned(alloc), opid);
43✔
258

259
        sub_iter = emp_res.first;
43✔
260
    }
261

262
    auto retval = sub_iter->first;
64✔
263
    if (sub_iter->second != opid) {
64✔
264
        auto other_otr
265
            = lnav::map::find(this->los_opid_ranges, sub_iter->second);
×
266
        if (other_otr) {
×
267
            other_otr->get().close_sub_ops(retval);
×
268
        }
269
    }
270
    sub_iter->second = opid;
64✔
271

272
    auto& otr = op_iter->second;
64✔
273
    auto sub_op_iter = otr.otr_sub_ops.rbegin();
64✔
274
    for (; sub_op_iter != otr.otr_sub_ops.rend(); ++sub_op_iter) {
64✔
275
        if (sub_op_iter->ostr_open && sub_op_iter->ostr_subid == retval) {
21✔
276
            break;
21✔
277
        }
278
    }
279
    if (sub_op_iter == otr.otr_sub_ops.rend()) {
64✔
280
        otr.otr_sub_ops.emplace_back(opid_sub_time_range{
43✔
281
            retval,
282
            time_range{us, us},
283
        });
284
        otr.otr_sub_ops.back().ostr_level_stats.update_msg_count(level);
43✔
285

286
        return &otr.otr_sub_ops.back();
43✔
287
    } else {
288
        sub_op_iter->ostr_range.extend_to(us);
21✔
289
        sub_op_iter->ostr_level_stats.update_msg_count(level);
21✔
290
        return &(*sub_op_iter);
21✔
291
    }
292
}
293

294
std::optional<std::string>
295
log_format::opid_descriptor::matches(const string_fragment& sf) const
2,557✔
296
{
297
    if (this->od_extractor.pp_value) {
2,557✔
298
        thread_local auto desc_md = lnav::pcre2pp::match_data::unitialized();
837✔
299

300
        auto desc_match_res = this->od_extractor.pp_value->capture_from(sf)
837✔
301
                                  .into(desc_md)
837✔
302
                                  .matches(PCRE2_NO_UTF_CHECK | PCRE2_ANCHORED)
1,674✔
303
                                  .ignore_error();
837✔
304
        if (desc_match_res) {
837✔
305
            return desc_md.to_string();
66✔
306
        }
307

308
        return std::nullopt;
771✔
309
    }
310
    return sf.to_string();
1,720✔
311
}
312

313
std::string
314
log_format::opid_descriptors::to_string(
1,075✔
315
    const lnav::map::small<size_t, std::string>& lod) const
316
{
317
    std::string retval;
1,075✔
318

319
    for (size_t lpc = 0; lpc < this->od_descriptors->size(); lpc++) {
2,162✔
320
        retval.append(this->od_descriptors->at(lpc).od_prefix);
1,087✔
321
        auto val = lod.value_for(lpc);
1,087✔
322
        if (val) {
1,087✔
323
            retval.append(*val.value());
1,086✔
324
        }
325
        retval.append(this->od_descriptors->at(lpc).od_suffix);
1,087✔
326
    }
327

328
    return retval;
1,075✔
329
}
×
330

331
chart_type_t
332
logline_value_meta::to_chart_type() const
38✔
333
{
334
    auto retval = chart_type_t::hist;
38✔
335
    switch (this->lvm_kind) {
38✔
336
        case value_kind_t::VALUE_NULL:
9✔
337
            retval = chart_type_t::none;
9✔
338
            break;
9✔
339
        case value_kind_t::VALUE_INTEGER:
10✔
340
            if (!this->lvm_identifier && !this->lvm_foreign_key) {
10✔
341
                retval = chart_type_t::spectro;
5✔
342
            }
343
            break;
10✔
344
        case value_kind_t::VALUE_FLOAT:
×
345
            retval = chart_type_t::spectro;
×
346
            break;
×
347
        case value_kind_t::VALUE_XML:
1✔
348
        case value_kind_t::VALUE_JSON:
349
        case value_kind_t::VALUE_BOOLEAN:
350
        case value_kind_t::VALUE_TIMESTAMP:
351
            retval = chart_type_t::none;
1✔
352
            break;
1✔
353
        default:
18✔
354
            break;
18✔
355
    }
356

357
    return retval;
38✔
358
}
359

360
struct line_range
361
logline_value::origin_in_full_msg(const char* msg, ssize_t len) const
×
362
{
363
    if (this->lv_sub_offset == 0) {
×
364
        return this->lv_origin;
×
365
    }
366

367
    if (len == -1) {
×
368
        len = strlen(msg);
×
369
    }
370

371
    struct line_range retval = this->lv_origin;
×
372
    const char *last = msg, *msg_end = msg + len;
×
373

374
    for (int lpc = 0; lpc < this->lv_sub_offset; lpc++) {
×
375
        const auto* next = (const char*) memchr(last, '\n', msg_end - last);
×
376
        require(next != nullptr);
×
377

378
        next += 1;
×
379
        int amount = (next - last);
×
380

381
        retval.lr_start += amount;
×
382
        if (retval.lr_end != -1) {
×
383
            retval.lr_end += amount;
×
384
        }
385

386
        last = next + 1;
×
387
    }
388

389
    if (retval.lr_end == -1) {
×
390
        const auto* eol = (const char*) memchr(last, '\n', msg_end - last);
×
391

392
        if (eol == nullptr) {
×
393
            retval.lr_end = len;
×
394
        } else {
395
            retval.lr_end = eol - msg;
×
396
        }
397
    }
398

399
    return retval;
×
400
}
401

402
logline_value::logline_value(logline_value_meta lvm,
612,796✔
403
                             shared_buffer_ref& sbr,
404
                             struct line_range origin)
612,796✔
405
    : lv_meta(std::move(lvm)), lv_origin(origin)
612,796✔
406
{
407
    if (sbr.get_data() == nullptr) {
612,796✔
408
        this->lv_meta.lvm_kind = value_kind_t::VALUE_NULL;
×
409
    }
410

411
    switch (this->lv_meta.lvm_kind) {
612,796✔
412
        case value_kind_t::VALUE_JSON:
309,660✔
413
        case value_kind_t::VALUE_XML:
414
        case value_kind_t::VALUE_STRUCT:
415
        case value_kind_t::VALUE_TEXT:
416
        case value_kind_t::VALUE_QUOTED:
417
        case value_kind_t::VALUE_W3C_QUOTED:
418
        case value_kind_t::VALUE_TIMESTAMP:
419
            require(origin.lr_end != -1);
309,660✔
420
            this->lv_frag = string_fragment::from_byte_range(
309,660✔
421
                sbr.get_data(), origin.lr_start, origin.lr_end);
309,660✔
422
            break;
309,660✔
423

424
        case value_kind_t::VALUE_NULL:
×
425
            break;
×
426

427
        case value_kind_t::VALUE_INTEGER: {
274,066✔
428
            auto scan_res
429
                = scn::scan_value<int64_t>(sbr.to_string_view(origin));
274,066✔
430
            if (scan_res) {
274,066✔
431
                this->lv_value.i = scan_res->value();
274,064✔
432
            } else {
433
                this->lv_value.i = 0;
2✔
434
            }
435
            break;
274,066✔
436
        }
437

438
        case value_kind_t::VALUE_FLOAT: {
29,070✔
439
            auto scan_res = scn::scan_value<double>(sbr.to_string_view(origin));
29,070✔
440
            if (scan_res) {
29,070✔
441
                this->lv_value.d = scan_res->value();
29,070✔
442
            } else {
443
                this->lv_value.d = 0;
×
444
            }
445
            break;
29,070✔
446
        }
447

448
        case value_kind_t::VALUE_BOOLEAN:
×
449
            if (strncmp(
×
450
                    sbr.get_data_at(origin.lr_start), "true", origin.length())
×
451
                    == 0
452
                || strncmp(
×
453
                       sbr.get_data_at(origin.lr_start), "yes", origin.length())
×
454
                    == 0)
455
            {
456
                this->lv_value.i = 1;
×
457
            } else {
458
                this->lv_value.i = 0;
×
459
            }
460
            break;
×
461

462
        case value_kind_t::VALUE_UNKNOWN:
×
463
        case value_kind_t::VALUE__MAX:
464
            ensure(0);
×
465
            break;
466
    }
467
}
612,796✔
468

469
std::string
470
logline_value::to_string() const
9,880✔
471
{
472
    char buffer[128];
473

474
    switch (this->lv_meta.lvm_kind) {
9,880✔
475
        case value_kind_t::VALUE_NULL:
15✔
476
            return "null";
30✔
477

478
        case value_kind_t::VALUE_JSON:
9,313✔
479
        case value_kind_t::VALUE_XML:
480
        case value_kind_t::VALUE_STRUCT:
481
        case value_kind_t::VALUE_TEXT:
482
        case value_kind_t::VALUE_TIMESTAMP:
483
            if (this->lv_str) {
9,313✔
484
                return this->lv_str.value();
1,418✔
485
            }
486
            if (this->lv_frag.empty()) {
7,895✔
487
                return this->lv_intern_string.to_string();
55✔
488
            }
489
            return this->lv_frag.to_string();
7,840✔
490

491
        case value_kind_t::VALUE_QUOTED:
7✔
492
        case value_kind_t::VALUE_W3C_QUOTED:
493
            if (this->lv_frag.empty()) {
7✔
494
                return "";
×
495
            } else {
496
                switch (this->lv_frag.data()[0]) {
7✔
497
                    case '\'':
7✔
498
                    case '"': {
499
                        auto unquote_func = this->lv_meta.lvm_kind
14✔
500
                                == value_kind_t::VALUE_W3C_QUOTED
501
                            ? unquote_w3c
7✔
502
                            : unquote;
503
                        stack_buf allocator;
7✔
504
                        auto* unquoted_str
505
                            = allocator.allocate(this->lv_frag.length());
7✔
506
                        size_t unquoted_len;
507

508
                        unquoted_len = unquote_func(unquoted_str,
7✔
509
                                                    this->lv_frag.data(),
510
                                                    this->lv_frag.length());
7✔
511
                        return {unquoted_str, unquoted_len};
14✔
512
                    }
7✔
513
                    default:
×
514
                        return this->lv_frag.to_string();
×
515
                }
516
            }
517
            break;
518

519
        case value_kind_t::VALUE_INTEGER:
507✔
520
            snprintf(buffer, sizeof(buffer), "%" PRId64, this->lv_value.i);
507✔
521
            break;
507✔
522

523
        case value_kind_t::VALUE_FLOAT:
32✔
524
            snprintf(buffer, sizeof(buffer), "%lf", this->lv_value.d);
32✔
525
            break;
32✔
526

527
        case value_kind_t::VALUE_BOOLEAN:
6✔
528
            if (this->lv_value.i) {
6✔
529
                return "true";
×
530
            } else {
531
                return "false";
12✔
532
            }
533
            break;
534
        case value_kind_t::VALUE_UNKNOWN:
×
535
        case value_kind_t::VALUE__MAX:
536
            ensure(0);
×
537
            break;
538
    }
539

540
    return {buffer};
1,078✔
541
}
542

543
string_fragment
544
logline_value::to_string_fragment(ArenaAlloc::Alloc<char>& alloc) const
1,095✔
545
{
546
    char buffer[128];
547

548
    switch (this->lv_meta.lvm_kind) {
1,095✔
549
        case value_kind_t::VALUE_NULL:
×
550
            return "null"_frag;
×
551

552
        case value_kind_t::VALUE_JSON:
1,095✔
553
        case value_kind_t::VALUE_XML:
554
        case value_kind_t::VALUE_STRUCT:
555
        case value_kind_t::VALUE_TEXT:
556
        case value_kind_t::VALUE_TIMESTAMP:
557
            if (this->lv_str) {
1,095✔
558
                return string_fragment::from_str(this->lv_str.value())
6✔
559
                    .to_owned(alloc);
6✔
560
            }
561
            if (this->lv_frag.empty()) {
1,089✔
562
                return this->lv_intern_string.to_string_fragment().to_owned(
×
563
                    alloc);
×
564
            }
565
            return this->lv_frag.to_owned(alloc);
1,089✔
566

567
        case value_kind_t::VALUE_QUOTED:
×
568
        case value_kind_t::VALUE_W3C_QUOTED:
569
            if (this->lv_frag.empty()) {
×
570
                return string_fragment{};
×
571
            } else {
572
                switch (this->lv_frag.data()[0]) {
×
573
                    case '\'':
×
574
                    case '"': {
575
                        auto unquote_func = this->lv_meta.lvm_kind
×
576
                                == value_kind_t::VALUE_W3C_QUOTED
577
                            ? unquote_w3c
×
578
                            : unquote;
579
                        stack_buf allocator;
×
580
                        auto* unquoted_str
581
                            = allocator.allocate(this->lv_frag.length());
×
582
                        size_t unquoted_len;
583

584
                        unquoted_len = unquote_func(unquoted_str,
×
585
                                                    this->lv_frag.data(),
586
                                                    this->lv_frag.length());
×
587
                        return string_fragment::from_bytes(unquoted_str,
×
588
                                                           unquoted_len)
589
                            .to_owned(alloc);
×
590
                    }
591
                    default:
×
592
                        return this->lv_frag.to_owned(alloc);
×
593
                }
594
            }
595
            break;
596

597
        case value_kind_t::VALUE_INTEGER:
×
598
            snprintf(buffer, sizeof(buffer), "%" PRId64, this->lv_value.i);
×
599
            break;
×
600

601
        case value_kind_t::VALUE_FLOAT:
×
602
            snprintf(buffer, sizeof(buffer), "%lf", this->lv_value.d);
×
603
            break;
×
604

605
        case value_kind_t::VALUE_BOOLEAN:
×
606
            if (this->lv_value.i) {
×
607
                return "true"_frag;
×
608
            }
609
            return "false"_frag;
×
610
            break;
611
        case value_kind_t::VALUE_UNKNOWN:
×
612
        case value_kind_t::VALUE__MAX:
613
            ensure(0);
×
614
            break;
615
    }
616

617
    return string_fragment::from_c_str(buffer).to_owned(alloc);
×
618
}
619

620
std::vector<std::shared_ptr<log_format>> log_format::lf_root_formats;
621

622
std::vector<std::shared_ptr<log_format>>&
623
log_format::get_root_formats()
15,653✔
624
{
625
    return lf_root_formats;
15,653✔
626
}
627

628
void
629
external_log_format::update_op_description(
5,415✔
630
    const std::vector<opid_descriptors*>& desc_defs_vec,
631
    log_op_description& lod,
632
    const pattern* fpat,
633
    const lnav::pcre2pp::match_data& md)
634
{
635
    std::optional<std::string> desc_elem_str;
5,415✔
636
    if (!lod.lod_index) {
5,415✔
637
        for (const auto& desc_defs : desc_defs_vec) {
3,870✔
638
            if (lod.lod_index) {
1,690✔
639
                break;
26✔
640
            }
641
            for (const auto& desc_def : *desc_defs->od_descriptors) {
2,475✔
642
                auto desc_field_index_iter = fpat->p_value_name_to_index.find(
1,762✔
643
                    desc_def.od_field.pp_value);
1,762✔
644

645
                if (desc_field_index_iter == fpat->p_value_name_to_index.end())
1,762✔
646
                {
647
                    continue;
43✔
648
                }
649

650
                auto desc_cap_opt = md[desc_field_index_iter->second];
1,758✔
651
                if (!desc_cap_opt) {
1,758✔
652
                    continue;
39✔
653
                }
654

655
                desc_elem_str = desc_def.matches(desc_cap_opt.value());
1,719✔
656
                if (desc_elem_str) {
1,719✔
657
                    lod.lod_index = desc_defs->od_index;
951✔
658
                    break;
951✔
659
                }
660
            }
661
        }
662
    }
663
    if (lod.lod_index) {
5,415✔
664
        const auto& desc_def_v
665
            = *desc_defs_vec[lod.lod_index.value()]->od_descriptors;
4,160✔
666
        auto& desc_v = lod.lod_elements;
4,160✔
667

668
        if (desc_def_v.size() == desc_v.size()
4,160✔
669
            || (this->elf_opid_field.empty() && !desc_v.empty()))
4,160✔
670
        {
671
            return;
3,209✔
672
        }
673
        for (size_t desc_def_index = 0; desc_def_index < desc_def_v.size();
2,745✔
674
             desc_def_index++)
675
        {
676
            const auto& desc_def = desc_def_v[desc_def_index];
1,794✔
677
            auto found_desc = desc_v.value_for(desc_def_index);
1,794✔
678
            auto desc_field_index_iter
679
                = fpat->p_value_name_to_index.find(desc_def.od_field.pp_value);
1,794✔
680

681
            if (desc_field_index_iter == fpat->p_value_name_to_index.end()) {
1,794✔
682
                continue;
35✔
683
            }
684
            auto desc_cap_opt = md[desc_field_index_iter->second];
1,794✔
685
            if (!desc_cap_opt) {
1,794✔
686
                continue;
35✔
687
            }
688

689
            if (!desc_elem_str) {
1,759✔
690
                desc_elem_str = desc_def.matches(desc_cap_opt.value());
808✔
691
            }
692
            if (desc_elem_str) {
1,759✔
693
                if (!found_desc) {
1,756✔
694
                    desc_v.insert(desc_def_index, desc_elem_str.value());
1,756✔
UNCOV
695
                } else if (!desc_elem_str->empty()) {
×
UNCOV
696
                    found_desc.value()->append(desc_def.od_joiner);
×
UNCOV
697
                    found_desc.value()->append(desc_elem_str.value());
×
698
                }
699
            }
700
            desc_elem_str = std::nullopt;
1,759✔
701
        }
702
    }
703
}
5,415✔
704

705
void
706
external_log_format::update_op_description(
2,381✔
707
    const std::vector<opid_descriptors*>& desc_defs_vec,
708
    log_op_description& lod)
709
{
710
    std::optional<std::string> desc_elem_str;
2,381✔
711
    if (!lod.lod_index) {
2,381✔
712
        for (const auto& desc_defs : desc_defs_vec) {
2,390✔
713
            if (lod.lod_index) {
15✔
UNCOV
714
                break;
×
715
            }
716
            for (const auto& desc_def : *desc_defs->od_descriptors) {
15✔
717
                auto desc_cap_iter
718
                    = this->lf_desc_captures.find(desc_def.od_field.pp_value);
15✔
719

720
                if (desc_cap_iter == this->lf_desc_captures.end()) {
15✔
UNCOV
721
                    continue;
×
722
                }
723
                desc_elem_str = desc_def.matches(desc_cap_iter->second);
15✔
724
                if (desc_elem_str) {
15✔
725
                    lod.lod_index = desc_defs->od_index;
15✔
726
                    break;
15✔
727
                }
728
            }
729
        }
730
    }
731
    if (lod.lod_index) {
2,381✔
732
        const auto& desc_def_v
733
            = *desc_defs_vec[lod.lod_index.value()]->od_descriptors;
21✔
734
        auto& desc_v = lod.lod_elements;
21✔
735

736
        if (desc_def_v.size() == desc_v.size()
21✔
737
            || (this->elf_opid_field.empty() && !desc_v.empty()))
21✔
738
        {
739
            return;
6✔
740
        }
741
        for (size_t desc_def_index = 0; desc_def_index < desc_def_v.size();
45✔
742
             desc_def_index++)
743
        {
744
            const auto& desc_def = desc_def_v[desc_def_index];
30✔
745
            auto found_desc = desc_v.value_for(desc_def_index);
30✔
746
            auto desc_cap_iter
747
                = this->lf_desc_captures.find(desc_def.od_field.pp_value);
30✔
748
            if (desc_cap_iter == this->lf_desc_captures.end()) {
30✔
UNCOV
749
                continue;
×
750
            }
751

752
            if (!desc_elem_str) {
30✔
753
                desc_elem_str = desc_def.matches(desc_cap_iter->second);
15✔
754
            }
755
            if (desc_elem_str) {
30✔
756
                if (!found_desc) {
30✔
757
                    desc_v.insert(desc_def_index, desc_elem_str.value());
30✔
UNCOV
758
                } else if (!desc_elem_str->empty()) {
×
UNCOV
759
                    found_desc.value()->append(desc_def.od_joiner);
×
UNCOV
760
                    found_desc.value()->append(desc_elem_str.value());
×
761
                }
762
            }
763
            desc_elem_str = std::nullopt;
30✔
764
        }
765
    }
766
}
2,381✔
767

768
static bool
769
next_format(
2,035,771✔
770
    const std::vector<std::shared_ptr<external_log_format::pattern>>& patterns,
771
    int& index,
772
    int& locked_index)
773
{
774
    bool retval = true;
2,035,771✔
775

776
    if (locked_index == -1) {
2,035,771✔
777
        index += 1;
2,028,552✔
778
        if (index >= (int) patterns.size()) {
2,028,552✔
779
            retval = false;
610,492✔
780
        }
781
    } else if (index == locked_index) {
7,219✔
782
        retval = false;
1✔
783
    } else {
784
        index = locked_index;
7,218✔
785
    }
786

787
    return retval;
2,035,771✔
788
}
789

790
bool
791
log_format::next_format(const pcre_format* fmt, int& index, int& locked_index)
160,778✔
792
{
793
    bool retval = true;
160,778✔
794

795
    if (locked_index == -1) {
160,778✔
796
        index += 1;
160,607✔
797
        if (fmt[index].name == nullptr) {
160,607✔
798
            retval = false;
9,254✔
799
        }
800
    } else if (index == locked_index) {
171✔
801
        retval = false;
31✔
802
    } else {
803
        index = locked_index;
140✔
804
    }
805

806
    return retval;
160,778✔
807
}
808

809
const char*
810
log_format::log_scanf(uint32_t line_number,
10,810✔
811
                      string_fragment line,
812
                      const pcre_format* fmt,
813
                      const char* time_fmt[],
814
                      struct exttm* tm_out,
815
                      struct timeval* tv_out,
816

817
                      string_fragment* ts_out,
818
                      std::optional<string_fragment>* level_out)
819
{
820
    int curr_fmt = -1;
10,810✔
821
    const char* retval = nullptr;
10,810✔
822
    bool done = false;
10,810✔
823
    int pat_index = this->last_pattern_index();
10,810✔
824

825
    while (!done && next_format(fmt, curr_fmt, pat_index)) {
162,303✔
826
        thread_local auto md = lnav::pcre2pp::match_data::unitialized();
151,493✔
827

828
        auto match_res = fmt[curr_fmt]
151,493✔
829
                             .pcre->capture_from(line)
151,493✔
830
                             .into(md)
151,493✔
831
                             .matches(PCRE2_NO_UTF_CHECK)
302,986✔
832
                             .ignore_error();
151,493✔
833
        if (!match_res) {
151,493✔
834
            retval = nullptr;
129,965✔
835
        } else {
836
            auto ts = md[fmt[curr_fmt].pf_timestamp_index];
21,528✔
837

838
            retval = this->lf_date_time.scan(
21,528✔
839
                ts->data(), ts->length(), nullptr, tm_out, *tv_out);
21,528✔
840

841
            if (retval == nullptr) {
21,528✔
842
                auto ls = this->lf_date_time.unlock();
20,003✔
843
                retval = this->lf_date_time.scan(
20,003✔
844
                    ts->data(), ts->length(), nullptr, tm_out, *tv_out);
20,003✔
845
                if (retval != nullptr) {
20,003✔
UNCOV
846
                    auto old_flags
×
UNCOV
847
                        = this->lf_timestamp_flags & DATE_TIME_SET_FLAGS;
×
UNCOV
848
                    auto new_flags = tm_out->et_flags & DATE_TIME_SET_FLAGS;
×
849

850
                    // It is unlikely a valid timestamp would lose much
851
                    // precision.
UNCOV
852
                    if (new_flags != old_flags) {
×
UNCOV
853
                        retval = nullptr;
×
854
                    }
855
                }
856
                if (retval == nullptr) {
20,003✔
857
                    this->lf_date_time.relock(ls);
20,003✔
858
                } else {
859
                    log_debug(
×
860
                        "%d: changed time format to '%s' due to %.*s",
861
                        line_number,
862
                        PTIMEC_FORMAT_STR[this->lf_date_time.dts_fmt_lock],
863
                        ts->length(),
864
                        ts->data());
865
                }
866
            }
867

868
            if (retval) {
21,528✔
869
                *ts_out = ts.value();
1,525✔
870
                if (md[2]) {
1,525✔
871
                    *level_out = md[2];
246✔
872
                } else {
873
                    *level_out = line.substr(md[0]->sf_end);
1,279✔
874
                }
875
                if (curr_fmt != pat_index) {
1,525✔
876
                    uint32_t lock_line;
877

878
                    if (this->lf_pattern_locks.empty()) {
1,416✔
879
                        lock_line = 0;
1,416✔
880
                    } else {
UNCOV
881
                        lock_line = line_number;
×
882
                    }
883

884
                    this->lf_pattern_locks.emplace_back(lock_line, curr_fmt);
1,416✔
885
                }
886
                this->lf_timestamp_flags = tm_out->et_flags;
1,525✔
887
                done = true;
1,525✔
888
            }
889
        }
890
    }
891

892
    return retval;
10,810✔
893
}
894

895
void
896
log_format::annotate(logfile* lf,
35,708✔
897
                     uint64_t line_number,
898
                     string_attrs_t& sa,
899
                     logline_value_vector& values,
900
                     bool annotate_module) const
901
{
902
    if (lf != nullptr && !values.lvv_opid_value) {
35,708✔
903
        const auto& bm = lf->get_bookmark_metadata();
1,181✔
904
        auto bm_iter = bm.find(line_number);
1,181✔
905
        if (bm_iter != bm.end() && !bm_iter->second.bm_opid.empty()) {
1,181✔
906
            values.lvv_opid_value = bm_iter->second.bm_opid;
18✔
907
            values.lvv_opid_provenance
908
                = logline_value_vector::opid_provenance::user;
18✔
909
        }
910
    }
911
}
35,708✔
912

913
void
914
log_format::check_for_new_year(std::vector<logline>& dst,
1,478✔
915
                               exttm etm,
916
                               struct timeval log_tv)
917
{
918
    if (dst.empty()) {
1,478✔
919
        return;
215✔
920
    }
921

922
    time_t diff
923
        = dst.back().get_time<std::chrono::seconds>().count() - log_tv.tv_sec;
1,263✔
924
    int off_year = 0, off_month = 0, off_day = 0, off_hour = 0;
1,263✔
925
    bool do_change = true;
1,263✔
926

927
    if (diff <= 0) {
1,263✔
928
        return;
1,204✔
929
    }
930
    if ((etm.et_flags & ETF_MONTH_SET) && diff >= (24 * 60 * 60)) {
59✔
931
        off_year = 1;
10✔
932
    } else if (diff >= (24 * 60 * 60)) {
49✔
933
        off_month = 1;
2✔
934
    } else if (!(etm.et_flags & ETF_DAY_SET) && (diff >= (60 * 60))) {
47✔
935
        off_day = 1;
1✔
936
    } else if (!(etm.et_flags & ETF_HOUR_SET) && (diff >= 60)) {
46✔
937
        off_hour = 1;
4✔
938
    } else {
939
        do_change = false;
42✔
940
    }
941

942
    if (!do_change) {
59✔
943
        return;
42✔
944
    }
945
    log_debug("%d:detected time rollover; offsets=%d %d %d %d",
17✔
946
              dst.size(),
947
              off_year,
948
              off_month,
949
              off_day,
950
              off_hour);
951
    for (auto& ll : dst) {
66✔
952
        time_t ot = ll.get_time<std::chrono::seconds>().count();
49✔
953
        struct tm otm;
954

955
        gmtime_r(&ot, &otm);
49✔
956
        otm.tm_yday = -1;
49✔
957
        if (otm.tm_year < off_year) {
49✔
UNCOV
958
            otm.tm_year = 0;
×
959
        } else {
960
            otm.tm_year -= off_year;
49✔
961
        }
962
        otm.tm_mon -= off_month;
49✔
963
        if (otm.tm_mon < 0) {
49✔
964
            otm.tm_mon += 12;
2✔
965
        }
966
        auto new_time = tm2sec(&otm);
49✔
967
        if (new_time == -1) {
49✔
UNCOV
968
            continue;
×
969
        }
970
        new_time -= (off_day * 24 * 60 * 60) + (off_hour * 60 * 60);
49✔
971
        auto old_sub = ll.get_subsecond_time<std::chrono::microseconds>();
49✔
972
        ll.set_time(std::chrono::seconds{new_time});
49✔
973
        ll.set_subsecond_time(old_sub);
49✔
974
    }
975
}
976

977
/*
978
 * XXX This needs some cleanup.
979
 */
980
struct json_log_userdata {
981
    json_log_userdata(shared_buffer_ref& sbr, scan_batch_context* sbc)
11,276✔
982
        : jlu_shared_buffer(sbr), jlu_batch_context(sbc)
22,552✔
983
    {
984
    }
11,276✔
985

986
    const external_log_format::value_def* get_field_def(
299,384✔
987
        yajlpp_parse_context* ypc)
988
    {
989
        const auto field_frag = ypc->get_path_as_string_fragment();
299,384✔
990
        auto* format = this->jlu_format;
299,384✔
991

992
        if (this->jlu_read_order_index < format->elf_value_def_read_order.size()
299,384✔
993
            && format->elf_value_def_read_order[this->jlu_read_order_index]
411,549✔
994
                    .first
995
                == field_frag)
112,165✔
996
        {
997
            return format
998
                ->elf_value_def_read_order[this->jlu_read_order_index++]
108,058✔
999
                .second;
108,058✔
1000
        }
1001

1002
        format->elf_value_def_read_order.resize(this->jlu_read_order_index);
191,326✔
1003
        auto vd_iter = format->elf_value_def_frag_map.find(field_frag);
191,326✔
1004
        if (vd_iter != format->elf_value_def_frag_map.end()) {
191,326✔
1005
            format->elf_value_def_read_order.emplace_back(vd_iter->first,
165,298✔
1006
                                                          vd_iter->second);
165,298✔
1007
            this->jlu_read_order_index += 1;
165,298✔
1008
            return vd_iter->second;
165,298✔
1009
        }
1010

1011
        auto owned_frag = field_frag.to_owned(format->elf_allocator);
26,028✔
1012
        format->elf_value_def_frag_map[owned_frag] = nullptr;
26,028✔
1013
        format->elf_value_def_read_order.emplace_back(owned_frag, nullptr);
26,028✔
1014
        this->jlu_read_order_index += 1;
26,028✔
1015
        return nullptr;
26,028✔
1016
    }
1017

1018
    void add_sub_lines_for(const external_log_format::value_def* vd,
208,358✔
1019
                           bool top_level,
1020
                           std::optional<double> val,
1021
                           const unsigned char* str,
1022
                           ssize_t len,
1023
                           yajl_string_props_t* props)
1024
    {
1025
        auto res = this->jlu_format->value_line_count(
208,358✔
1026
            vd, top_level, val, str, len, props);
1027
        this->jlu_has_ansi |= res.vlcr_has_ansi;
208,358✔
1028
        if (!res.vlcr_valid_utf) {
208,358✔
UNCOV
1029
            this->jlu_valid_utf = false;
×
1030
        }
1031
        this->jlu_sub_line_count += res.vlcr_count;
208,358✔
1032
        this->jlu_quality += res.vlcr_line_format_count;
208,358✔
1033
        if (res.vlcr_line_format_index) {
208,358✔
1034
            this->jlu_format_hits[res.vlcr_line_format_index.value()] = true;
3,086✔
1035
        }
1036
    }
208,358✔
1037

1038
    external_log_format* jlu_format{nullptr};
1039
    const logline* jlu_line{nullptr};
1040
    logline* jlu_base_line{nullptr};
1041
    int jlu_sub_line_count{1};
1042
    bool jlu_has_ansi{false};
1043
    bool jlu_valid_utf{true};
1044
    yajl_handle jlu_handle{nullptr};
1045
    const char* jlu_line_value{nullptr};
1046
    size_t jlu_line_size{0};
1047
    size_t jlu_sub_start{0};
1048
    uint32_t jlu_quality{0};
1049
    uint32_t jlu_strikes{0};
1050
    std::vector<bool> jlu_format_hits;
1051
    shared_buffer_ref& jlu_shared_buffer;
1052
    scan_batch_context* jlu_batch_context;
1053
    std::optional<string_fragment> jlu_opid_frag;
1054
    std::optional<string_fragment> jlu_opid_desc_frag;
1055
    std::optional<string_fragment> jlu_tid_frag;
1056
    std::optional<int64_t> jlu_tid_number;
1057
    std::optional<std::string> jlu_subid;
1058
    hasher jlu_opid_hasher;
1059
    std::chrono::microseconds jlu_duration{0};
11,276✔
1060
    exttm jlu_exttm;
1061
    size_t jlu_read_order_index{0};
1062
    subline_options jlu_subline_opts;
1063
};
1064

1065
static int read_json_field(yajlpp_parse_context* ypc,
1066
                           const unsigned char* str,
1067
                           size_t len,
1068
                           yajl_string_props_t*);
1069

1070
static int
1071
read_json_null(yajlpp_parse_context* ypc)
2,992✔
1072
{
1073
    json_log_userdata* jlu = (json_log_userdata*) ypc->ypc_userdata;
2,992✔
1074
    const auto* vd = jlu->get_field_def(ypc);
2,992✔
1075

1076
    jlu->add_sub_lines_for(
5,984✔
1077
        vd, ypc->is_level(1), std::nullopt, nullptr, -1, nullptr);
2,992✔
1078

1079
    return 1;
2,992✔
1080
}
1081

1082
static int
1083
read_json_bool(yajlpp_parse_context* ypc, int val)
14,244✔
1084
{
1085
    json_log_userdata* jlu = (json_log_userdata*) ypc->ypc_userdata;
14,244✔
1086
    const auto* vd = jlu->get_field_def(ypc);
14,244✔
1087

1088
    jlu->add_sub_lines_for(
28,488✔
1089
        vd, ypc->is_level(1), std::nullopt, nullptr, -1, nullptr);
14,244✔
1090

1091
    return 1;
14,244✔
1092
}
1093

1094
static int
1095
read_json_number(yajlpp_parse_context* ypc,
22,297✔
1096
                 const char* numberVal,
1097
                 size_t numberLen)
1098
{
1099
    auto* jlu = (json_log_userdata*) ypc->ypc_userdata;
22,297✔
1100
    auto number_frag = string_fragment::from_bytes(numberVal, numberLen);
22,297✔
1101
    std::optional<double> val;
22,297✔
1102

1103
    intern_string_t field_name;
22,297✔
1104
    const auto* vd = jlu->get_field_def(ypc);
22,297✔
1105
    if (vd != nullptr) {
22,297✔
1106
        field_name = vd->vd_meta.lvm_name;
691✔
1107
    }
1108

1109
    if (field_name.empty()) {
22,297✔
1110
    } else if (jlu->jlu_format->lf_timestamp_field == field_name) {
691✔
1111
        long long divisor = jlu->jlu_format->elf_timestamp_divisor;
113✔
1112
        auto scan_res = scn::scan_value<double>(number_frag.to_string_view());
113✔
1113
        if (!scan_res) {
113✔
UNCOV
1114
            log_error("invalid number %.*s", numberLen, numberVal);
×
UNCOV
1115
            return 0;
×
1116
        }
1117
        auto ts_val = scan_res.value().value();
113✔
1118
        timeval tv;
1119
        tv.tv_sec = ts_val / divisor;
113✔
1120
        tv.tv_usec = fmod(ts_val, divisor) * (1000000.0 / divisor);
113✔
1121
        jlu->jlu_format->lf_date_time.to_localtime(tv.tv_sec, jlu->jlu_exttm);
113✔
1122
        tv.tv_sec = tm2sec(&jlu->jlu_exttm.et_tm);
113✔
1123
        jlu->jlu_exttm.et_gmtoff
1124
            = jlu->jlu_format->lf_date_time.dts_local_offset_cache;
113✔
1125
        jlu->jlu_exttm.et_flags
113✔
1126
            |= ETF_MACHINE_ORIENTED | ETF_SUB_NOT_IN_FORMAT | ETF_ZONE_SET;
113✔
1127
        if (divisor == 1000) {
113✔
1128
            jlu->jlu_exttm.et_flags |= ETF_MILLIS_SET;
17✔
1129
        } else {
1130
            jlu->jlu_exttm.et_flags |= ETF_MICROS_SET;
96✔
1131
        }
1132
        jlu->jlu_exttm.et_nsec = tv.tv_usec * 1000;
113✔
1133
        jlu->jlu_base_line->set_time(tv);
113✔
1134
    } else if (jlu->jlu_format->lf_subsecond_field == field_name) {
578✔
1135
        auto scan_res = scn::scan_value<double>(number_frag.to_string_view());
3✔
1136
        if (!scan_res) {
3✔
UNCOV
1137
            log_error("invalid number %.*s", numberLen, numberVal);
×
UNCOV
1138
            return 0;
×
1139
        }
1140
        auto ts_val = scan_res.value().value();
3✔
1141

1142
        uint64_t millis = 0;
3✔
1143
        jlu->jlu_exttm.et_flags &= ~(ETF_MICROS_SET | ETF_MILLIS_SET);
3✔
1144
        switch (jlu->jlu_format->lf_subsecond_unit.value()) {
3✔
UNCOV
1145
            case log_format::subsecond_unit::milli:
×
UNCOV
1146
                millis = ts_val;
×
UNCOV
1147
                jlu->jlu_exttm.et_nsec = ts_val * 1000000;
×
UNCOV
1148
                jlu->jlu_exttm.et_flags |= ETF_MILLIS_SET;
×
1149
                break;
×
1150
            case log_format::subsecond_unit::micro:
×
UNCOV
1151
                millis = std::chrono::duration_cast<std::chrono::milliseconds>(
×
UNCOV
1152
                             std::chrono::microseconds((int64_t) ts_val))
×
UNCOV
1153
                             .count();
×
UNCOV
1154
                jlu->jlu_exttm.et_nsec = ts_val * 1000;
×
UNCOV
1155
                jlu->jlu_exttm.et_flags |= ETF_MICROS_SET;
×
UNCOV
1156
                break;
×
1157
            case log_format::subsecond_unit::nano:
3✔
1158
                millis = std::chrono::duration_cast<std::chrono::milliseconds>(
3✔
1159
                             std::chrono::nanoseconds((int64_t) ts_val))
3✔
1160
                             .count();
3✔
1161
                jlu->jlu_exttm.et_nsec = ts_val;
3✔
1162
                jlu->jlu_exttm.et_flags |= ETF_NANOS_SET;
3✔
1163
                break;
3✔
1164
        }
1165
        jlu->jlu_exttm.et_flags |= ETF_SUB_NOT_IN_FORMAT;
3✔
1166
        jlu->jlu_base_line->set_subsecond_time(
3✔
1167
            std::chrono::milliseconds(millis));
1168
    } else if (jlu->jlu_format->elf_level_field == field_name) {
575✔
1169
        if (jlu->jlu_format->elf_level_pairs.empty()) {
300✔
1170
            jlu->jlu_base_line->set_level(jlu->jlu_format->convert_level(
186✔
1171
                number_frag, jlu->jlu_batch_context));
1172
        } else {
1173
            auto scan_res
1174
                = scn::scan_int<int64_t>(number_frag.to_string_view());
114✔
1175
            if (!scan_res) {
114✔
UNCOV
1176
                log_error("invalid number %.*s", numberLen, numberVal);
×
UNCOV
1177
                return 0;
×
1178
            }
1179
            auto level_int = scan_res.value().value();
114✔
1180

1181
            for (const auto& pair : jlu->jlu_format->elf_level_pairs) {
431✔
1182
                if (pair.first == level_int) {
385✔
1183
                    jlu->jlu_base_line->set_level(pair.second);
68✔
1184
                    break;
68✔
1185
                }
1186
            }
1187
        }
1188
    } else if (vd != nullptr) {
275✔
1189
        if (jlu->jlu_format->elf_thread_id_field == field_name) {
275✔
1190
            auto& sbc = *jlu->jlu_batch_context;
70✔
1191
            auto tid_iter = sbc.sbc_tids.ltis_tid_ranges.find(number_frag);
70✔
1192
            if (tid_iter == sbc.sbc_tids.ltis_tid_ranges.end()) {
70✔
1193
                jlu->jlu_tid_frag = number_frag.to_owned(sbc.sbc_allocator);
39✔
1194
            } else {
1195
                jlu->jlu_tid_frag = tid_iter->first;
31✔
1196
            }
1197
        }
1198
        if ((vd->vd_meta.lvm_kind == value_kind_t::VALUE_INTEGER
275✔
1199
             || vd->vd_meta.lvm_kind == value_kind_t::VALUE_FLOAT)
94✔
1200
            && !vd->vd_meta.lvm_foreign_key && !vd->vd_meta.lvm_identifier)
199✔
1201
        {
1202
            auto scan_res
1203
                = scn::scan_value<double>(number_frag.to_string_view());
151✔
1204
            if (!scan_res) {
151✔
UNCOV
1205
                log_error("invalid number %.*s", numberLen, numberVal);
×
UNCOV
1206
                return 0;
×
1207
            }
1208
            val = scan_res.value().value();
151✔
1209
            if (jlu->jlu_format->elf_duration_field == field_name) {
151✔
UNCOV
1210
                jlu->jlu_duration = std::chrono::microseconds(
×
1211
                    static_cast<int64_t>(val.value() * 1000000));
18✔
1212
            }
1213
        }
1214
    }
1215

1216
    jlu->add_sub_lines_for(vd,
22,297✔
1217
                           ypc->is_level(1),
22,297✔
1218
                           val,
1219
                           (const unsigned char*) numberVal,
1220
                           numberLen,
1221
                           nullptr);
1222

1223
    return 1;
22,297✔
1224
}
1225

1226
static int
1227
json_array_start(void* ctx)
59,610✔
1228
{
1229
    auto* ypc = (yajlpp_parse_context*) ctx;
59,610✔
1230
    auto* jlu = (json_log_userdata*) ypc->ypc_userdata;
59,610✔
1231

1232
    if (ypc->ypc_path_index_stack.size() == 2) {
59,610✔
1233
        intern_string_t field_name;
18,428✔
1234
        const auto* vd = jlu->get_field_def(ypc);
18,428✔
1235

1236
        jlu->add_sub_lines_for(vd, true, std::nullopt, nullptr, -1, nullptr);
18,428✔
1237
        jlu->jlu_sub_start = yajl_get_bytes_consumed(jlu->jlu_handle) - 1;
18,428✔
1238
    }
1239

1240
    return 1;
59,610✔
1241
}
1242

1243
static int
1244
json_array_end(void* ctx)
13,460✔
1245
{
1246
    auto* ypc = (yajlpp_parse_context*) ctx;
13,460✔
1247
    auto* jlu = (json_log_userdata*) ypc->ypc_userdata;
13,460✔
1248

1249
    if (ypc->ypc_path_index_stack.size() == 1) {
13,460✔
1250
        const intern_string_t field_name = ypc->get_path_fragment_i(0);
3,629✔
1251
        size_t sub_end = yajl_get_bytes_consumed(jlu->jlu_handle);
3,629✔
1252
        auto json_frag = string_fragment::from_byte_range(
3,629✔
1253
            jlu->jlu_shared_buffer.get_data(), jlu->jlu_sub_start, sub_end);
3,629✔
1254
        jlu->jlu_format->jlf_line_values.lvv_values.emplace_back(
3,629✔
1255
            jlu->jlu_format->get_value_meta(field_name,
7,258✔
1256
                                            value_kind_t::VALUE_JSON),
1257
            json_frag);
1258
        if (field_name == jlu->jlu_format->elf_opid_field) {
3,629✔
1259
            jlu->jlu_opid_desc_frag = json_frag;
28✔
1260
        }
1261
    }
1262

1263
    return 1;
13,460✔
1264
}
1265

1266
static int
1267
read_array_end(void* ctx)
45,948✔
1268
{
1269
    auto* ypc = (yajlpp_parse_context*) ctx;
45,948✔
1270
    auto* jlu = (json_log_userdata*) ypc->ypc_userdata;
45,948✔
1271

1272
    if (ypc->ypc_path_index_stack.size() == 1) {
45,948✔
1273
        const intern_string_t field_name = ypc->get_path_fragment_i(0);
14,768✔
1274
        size_t sub_end = yajl_get_bytes_consumed(jlu->jlu_handle);
14,768✔
1275
        auto json_frag = string_fragment::from_byte_range(
14,768✔
1276
            jlu->jlu_shared_buffer.get_data(), jlu->jlu_sub_start, sub_end);
14,768✔
1277
        if (field_name == jlu->jlu_format->elf_opid_field) {
14,768✔
1278
            jlu->jlu_opid_desc_frag = json_frag;
2,196✔
1279
        }
1280
    }
1281

1282
    return 1;
45,948✔
1283
}
1284

1285
static const json_path_container json_log_handlers = {
1286
    yajlpp::pattern_property_handler("\\w+")
1287
        .add_cb(read_json_null)
1288
        .add_cb(read_json_bool)
1289
        .add_cb(read_json_number)
1290
        .add_cb(read_json_field),
1291
};
1292

1293
static int rewrite_json_field(yajlpp_parse_context* ypc,
1294
                              const unsigned char* str,
1295
                              size_t len,
1296
                              yajl_string_props_t*);
1297

1298
static int
1299
rewrite_json_null(yajlpp_parse_context* ypc)
1,957✔
1300
{
1301
    auto jlu = (json_log_userdata*) ypc->ypc_userdata;
1,957✔
1302
    const auto* vd = jlu->get_field_def(ypc);
1,957✔
1303

1304
    if (!ypc->is_level(1) && vd == nullptr) {
1,957✔
1305
        return 1;
1,957✔
1306
    }
UNCOV
1307
    jlu->jlu_format->jlf_line_values.lvv_values.emplace_back(
×
UNCOV
1308
        jlu->jlu_format->get_value_meta(ypc, vd, value_kind_t::VALUE_NULL));
×
1309

UNCOV
1310
    return 1;
×
1311
}
1312

1313
static int
1314
rewrite_json_bool(yajlpp_parse_context* ypc, int val)
8,809✔
1315
{
1316
    auto* jlu = (json_log_userdata*) ypc->ypc_userdata;
8,809✔
1317
    const auto* vd = jlu->get_field_def(ypc);
8,809✔
1318

1319
    if (!ypc->is_level(1) && vd == nullptr) {
8,809✔
1320
        return 1;
7,861✔
1321
    }
1322
    jlu->jlu_format->jlf_line_values.lvv_values.emplace_back(
948✔
1323
        jlu->jlu_format->get_value_meta(ypc, vd, value_kind_t::VALUE_BOOLEAN),
948✔
1324
        (bool) val);
948✔
1325
    return 1;
948✔
1326
}
1327

1328
static int
1329
rewrite_json_int(yajlpp_parse_context* ypc, long long val)
10,755✔
1330
{
1331
    auto* jlu = (json_log_userdata*) ypc->ypc_userdata;
10,755✔
1332
    const auto* vd = jlu->get_field_def(ypc);
10,755✔
1333

1334
    if (vd != nullptr) {
10,755✔
1335
        const intern_string_t field_name = vd->vd_meta.lvm_name;
748✔
1336
        if (jlu->jlu_format->lf_timestamp_field == field_name) {
748✔
1337
            long long divisor = jlu->jlu_format->elf_timestamp_divisor;
25✔
1338
            timeval tv;
1339

1340
            tv.tv_sec = val / divisor;
25✔
1341
            tv.tv_usec = fmod(val, divisor) * (1000000.0 / divisor);
25✔
1342
            jlu->jlu_format->lf_date_time.to_localtime(tv.tv_sec,
25✔
1343
                                                       jlu->jlu_exttm);
25✔
1344
            jlu->jlu_exttm.et_gmtoff
1345
                = jlu->jlu_format->lf_date_time.dts_local_offset_cache;
25✔
1346
            jlu->jlu_exttm.et_flags |= ETF_MACHINE_ORIENTED
25✔
1347
                | ETF_SUB_NOT_IN_FORMAT | ETF_ZONE_SET | ETF_Z_FOR_UTC;
1348
            if (divisor == 1) {
25✔
1349
                jlu->jlu_exttm.et_flags |= ETF_MICROS_SET;
4✔
1350
            } else {
1351
                jlu->jlu_exttm.et_flags |= ETF_MILLIS_SET;
21✔
1352
            }
1353
            jlu->jlu_exttm.et_nsec = tv.tv_usec * 1000;
25✔
1354
        } else if (jlu->jlu_format->lf_subsecond_field == field_name) {
723✔
1355
            jlu->jlu_exttm.et_flags &= ~(ETF_MICROS_SET | ETF_MILLIS_SET);
4✔
1356
            switch (jlu->jlu_format->lf_subsecond_unit.value()) {
4✔
UNCOV
1357
                case log_format::subsecond_unit::milli:
×
UNCOV
1358
                    jlu->jlu_exttm.et_nsec = val * 1000000;
×
UNCOV
1359
                    jlu->jlu_exttm.et_flags |= ETF_MILLIS_SET;
×
UNCOV
1360
                    break;
×
UNCOV
1361
                case log_format::subsecond_unit::micro:
×
UNCOV
1362
                    jlu->jlu_exttm.et_nsec = val * 1000;
×
UNCOV
1363
                    jlu->jlu_exttm.et_flags |= ETF_MICROS_SET;
×
UNCOV
1364
                    break;
×
1365
                case log_format::subsecond_unit::nano:
4✔
1366
                    jlu->jlu_exttm.et_nsec = val;
4✔
1367
                    jlu->jlu_exttm.et_flags |= ETF_NANOS_SET;
4✔
1368
                    break;
4✔
1369
            }
1370
            jlu->jlu_exttm.et_flags |= ETF_SUB_NOT_IN_FORMAT;
4✔
1371
        }
1372
    }
1373

1374
    if (!ypc->is_level(1) && vd == nullptr) {
10,755✔
1375
        return 1;
9,960✔
1376
    }
1377
    if (vd != nullptr
795✔
1378
        && vd->vd_meta.lvm_name == jlu->jlu_format->elf_thread_id_field)
795✔
1379
    {
1380
        jlu->jlu_tid_number = val;
114✔
1381
    }
1382
    jlu->jlu_format->jlf_line_values.lvv_values.emplace_back(
795✔
1383
        jlu->jlu_format->get_value_meta(ypc, vd, value_kind_t::VALUE_INTEGER),
795✔
1384
        (int64_t) val);
795✔
1385
    return 1;
795✔
1386
}
1387

1388
static int
1389
rewrite_json_double(yajlpp_parse_context* ypc, double val)
162✔
1390
{
1391
    auto* jlu = (json_log_userdata*) ypc->ypc_userdata;
162✔
1392
    const auto* vd = jlu->get_field_def(ypc);
162✔
1393

1394
    if (vd != nullptr) {
162✔
1395
        const intern_string_t field_name = vd->vd_meta.lvm_name;
158✔
1396
        if (jlu->jlu_format->lf_timestamp_field == field_name) {
158✔
1397
            long long divisor = jlu->jlu_format->elf_timestamp_divisor;
130✔
1398
            timeval tv;
1399

1400
            tv.tv_sec = val / divisor;
130✔
1401
            tv.tv_usec = fmod(val, divisor) * (1000000.0 / divisor);
130✔
1402
            jlu->jlu_format->lf_date_time.to_localtime(tv.tv_sec,
130✔
1403
                                                       jlu->jlu_exttm);
130✔
1404
            jlu->jlu_exttm.et_gmtoff
1405
                = jlu->jlu_format->lf_date_time.dts_local_offset_cache;
130✔
1406
            jlu->jlu_exttm.et_flags |= ETF_MACHINE_ORIENTED
130✔
1407
                | ETF_SUB_NOT_IN_FORMAT | ETF_ZONE_SET | ETF_Z_FOR_UTC;
1408
            if (divisor == 1) {
130✔
1409
                jlu->jlu_exttm.et_flags |= ETF_MICROS_SET;
118✔
1410
            } else {
1411
                jlu->jlu_exttm.et_flags |= ETF_MILLIS_SET;
12✔
1412
            }
1413
            jlu->jlu_exttm.et_nsec = tv.tv_usec * 1000;
130✔
1414
        } else if (jlu->jlu_format->lf_subsecond_field == field_name) {
28✔
UNCOV
1415
            jlu->jlu_exttm.et_flags &= ~(ETF_MICROS_SET | ETF_MILLIS_SET);
×
UNCOV
1416
            switch (jlu->jlu_format->lf_subsecond_unit.value()) {
×
UNCOV
1417
                case log_format::subsecond_unit::milli:
×
UNCOV
1418
                    jlu->jlu_exttm.et_nsec = val * 1000000;
×
UNCOV
1419
                    jlu->jlu_exttm.et_flags |= ETF_MILLIS_SET;
×
UNCOV
1420
                    break;
×
UNCOV
1421
                case log_format::subsecond_unit::micro:
×
UNCOV
1422
                    jlu->jlu_exttm.et_nsec = val * 1000;
×
UNCOV
1423
                    jlu->jlu_exttm.et_flags |= ETF_MICROS_SET;
×
UNCOV
1424
                    break;
×
UNCOV
1425
                case log_format::subsecond_unit::nano:
×
UNCOV
1426
                    jlu->jlu_exttm.et_nsec = val;
×
1427
                    jlu->jlu_exttm.et_flags |= ETF_NANOS_SET;
×
1428
                    break;
×
1429
            }
1430
            jlu->jlu_exttm.et_flags |= ETF_SUB_NOT_IN_FORMAT;
×
1431
        } else if (jlu->jlu_format->elf_duration_field == field_name) {
28✔
1432
            jlu->jlu_duration = std::chrono::microseconds(
56✔
1433
                static_cast<int64_t>(val * 1000000.0));
28✔
1434
        }
1435
    }
1436

1437
    if (!ypc->is_level(1) && vd == nullptr) {
162✔
1438
        return 1;
×
1439
    }
1440
    jlu->jlu_format->jlf_line_values.lvv_values.emplace_back(
162✔
1441
        jlu->jlu_format->get_value_meta(ypc, vd, value_kind_t::VALUE_FLOAT),
324✔
1442
        val);
1443

1444
    return 1;
162✔
1445
}
1446

1447
static const json_path_container json_log_rewrite_handlers = {
1448
    yajlpp::pattern_property_handler("\\w+")
1449
        .add_cb(rewrite_json_null)
1450
        .add_cb(rewrite_json_bool)
1451
        .add_cb(rewrite_json_int)
1452
        .add_cb(rewrite_json_double)
1453
        .add_cb(rewrite_json_field),
1454
};
1455

1456
bool
1457
external_log_format::scan_for_partial(shared_buffer_ref& sbr,
1✔
1458
                                      size_t& len_out) const
1459
{
1460
    if (this->elf_type != elf_type_t::ELF_TYPE_TEXT) {
1✔
UNCOV
1461
        return false;
×
1462
    }
1463

1464
    const auto& pat = this->elf_pattern_order[this->last_pattern_index()];
1✔
1465
    if (!this->lf_multiline) {
1✔
1466
        len_out = pat->p_pcre.pp_value->match_partial(sbr.to_string_fragment());
1✔
1467
        return true;
1✔
1468
    }
1469

UNCOV
1470
    if (pat->p_timestamp_end == -1 || pat->p_timestamp_end > (int) sbr.length())
×
1471
    {
UNCOV
1472
        len_out = 0;
×
1473
        return false;
×
1474
    }
1475

UNCOV
1476
    len_out = pat->p_pcre.pp_value->match_partial(sbr.to_string_fragment());
×
UNCOV
1477
    return (int) len_out > pat->p_timestamp_end;
×
1478
}
1479

1480
std::vector<lnav::console::snippet>
1481
external_log_format::get_snippets() const
23✔
1482
{
1483
    std::vector<lnav::console::snippet> retval;
23✔
1484

1485
    for (const auto& src_pair : this->elf_format_sources) {
46✔
1486
        retval.emplace_back(lnav::console::snippet::from(src_pair.first, "")
46✔
1487
                                .with_line(src_pair.second));
23✔
1488
    }
1489

1490
    return retval;
23✔
UNCOV
1491
}
×
1492

1493
log_format::scan_result_t
1494
external_log_format::scan_json(std::vector<logline>& dst,
121,740✔
1495
                               const line_info& li,
1496
                               shared_buffer_ref& sbr,
1497
                               scan_batch_context& sbc)
1498
{
1499
    logline ll(
1500
        li.li_file_range.fr_offset, std::chrono::microseconds{0}, LEVEL_INFO);
121,740✔
1501
    auto line_frag = sbr.to_string_fragment();
121,740✔
1502

1503
    if (!line_frag.startswith("{")) {
121,740✔
1504
        if (!this->lf_specialized) {
112,887✔
1505
            return scan_no_match{"line is not a JSON object"};
112,882✔
1506
        }
1507

1508
        ll.set_time(dst.back().get_time<std::chrono::microseconds>());
5✔
1509
        ll.set_level(LEVEL_INVALID);
5✔
1510
        dst.emplace_back(ll);
5✔
1511
        return scan_match{0};
5✔
1512
    }
1513

1514
    auto& ypc = *(this->jlf_parse_context);
8,853✔
1515
    yajl_handle handle = this->jlf_yajl_handle.get();
8,853✔
1516
    json_log_userdata jlu(sbr, &sbc);
8,853✔
1517

1518
    if (li.li_partial) {
8,853✔
UNCOV
1519
        log_debug("skipping partial line at offset %d",
×
1520
                  li.li_file_range.fr_offset);
UNCOV
1521
        if (this->lf_specialized) {
×
UNCOV
1522
            if (!dst.empty()) {
×
UNCOV
1523
                ll.set_time(dst.back().get_time<std::chrono::microseconds>());
×
1524
            }
UNCOV
1525
            ll.set_level(LEVEL_INVALID);
×
UNCOV
1526
            dst.emplace_back(ll);
×
1527
        }
UNCOV
1528
        return scan_incomplete{};
×
1529
    }
1530

1531
    const auto* line_data = (const unsigned char*) sbr.get_data();
8,853✔
1532

1533
    this->lf_desc_captures.clear();
8,853✔
1534
    this->lf_desc_allocator.reset();
8,853✔
1535

1536
    yajl_reset(handle);
8,853✔
1537
    ypc.set_static_handler(json_log_handlers.jpc_children[0]);
8,853✔
1538
    ypc.ypc_userdata = &jlu;
8,853✔
1539
    ypc.ypc_ignore_unused = true;
8,853✔
1540
    ypc.ypc_alt_callbacks.yajl_start_array = json_array_start;
8,853✔
1541
    ypc.ypc_alt_callbacks.yajl_start_map = json_array_start;
8,853✔
1542
    ypc.ypc_alt_callbacks.yajl_end_array = read_array_end;
8,853✔
1543
    ypc.ypc_alt_callbacks.yajl_end_map = read_array_end;
8,853✔
1544
    jlu.jlu_format = this;
8,853✔
1545
    jlu.jlu_base_line = &ll;
8,853✔
1546
    jlu.jlu_line_value = sbr.get_data();
8,853✔
1547
    jlu.jlu_line_size = sbr.length();
8,853✔
1548
    jlu.jlu_handle = handle;
8,853✔
1549
    jlu.jlu_format_hits.resize(this->jlf_line_format.size());
8,853✔
1550
    if (yajl_parse(handle, line_data, sbr.length()) == yajl_status_ok
8,853✔
1551
        && yajl_complete_parse(handle) == yajl_status_ok)
8,853✔
1552
    {
1553
        if (ll.get_time<std::chrono::microseconds>().count() == 0) {
8,696✔
1554
            if (this->lf_specialized) {
5,613✔
UNCOV
1555
                if (!dst.empty()) {
×
UNCOV
1556
                    ll.set_time(
×
UNCOV
1557
                        dst.back().get_time<std::chrono::microseconds>());
×
1558
                }
UNCOV
1559
                ll.set_ignore(true);
×
UNCOV
1560
                dst.emplace_back(ll);
×
UNCOV
1561
                return scan_match{0};
×
1562
            }
1563

1564
            return scan_no_match{
5,613✔
1565
                "JSON message does not have expected timestamp property"};
5,613✔
1566
        }
1567

1568
        if (jlu.jlu_tid_frag) {
3,083✔
1569
            this->jlf_line_values.lvv_thread_id_value
1570
                = jlu.jlu_tid_frag->to_string();
70✔
1571
            auto tid_iter = sbc.sbc_tids.insert_tid(
140✔
1572
                sbc.sbc_allocator,
1573
                jlu.jlu_tid_frag.value(),
70✔
1574
                ll.get_time<std::chrono::microseconds>());
70✔
1575
            tid_iter->second.titr_level_stats.update_msg_count(
70✔
1576
                ll.get_msg_level());
1577
        } else {
1578
            auto tid_iter = sbc.sbc_tids.insert_tid(
3,013✔
1579
                sbc.sbc_allocator,
UNCOV
1580
                string_fragment{},
×
1581
                ll.get_time<std::chrono::microseconds>());
3,013✔
1582
            tid_iter->second.titr_level_stats.update_msg_count(
3,013✔
1583
                ll.get_msg_level());
1584
        }
1585

1586
        auto found_opid_desc = false;
3,083✔
1587
        if (this->elf_opid_field.empty()
3,083✔
1588
            && this->lf_opid_description_def->size() == 1)
3,083✔
1589
        {
1590
            const auto& od = this->lf_opid_description_def->begin()->second;
322✔
1591
            for (const auto& desc : *od.od_descriptors) {
966✔
1592
                auto desc_iter
1593
                    = this->lf_desc_captures.find(desc.od_field.pp_value);
644✔
1594
                if (desc_iter == this->lf_desc_captures.end()) {
644✔
1595
                    continue;
602✔
1596
                }
1597
                jlu.jlu_opid_hasher.update(desc_iter->second);
42✔
1598
                found_opid_desc = true;
42✔
1599
            }
1600

1601
        } else if (!jlu.jlu_opid_desc_frag && !jlu.jlu_opid_frag
3,326✔
1602
                   && jlu.jlu_duration > 0us)
3,326✔
1603
        {
UNCOV
1604
            jlu.jlu_opid_hasher.update(sbr.to_string_fragment());
×
1605
        }
1606

1607
        if (jlu.jlu_opid_desc_frag || jlu.jlu_duration > 0us
3,970✔
1608
            || (found_opid_desc && this->lf_opid_description_def->size() == 1))
3,970✔
1609
        {
1610
            char buf[hasher::STRING_SIZE];
1611
            jlu.jlu_opid_hasher.to_string(buf);
2,217✔
1612
            auto opid_frag = string_fragment::from_bytes(buf, sizeof(buf) - 1);
2,217✔
1613
            jlu.jlu_base_line->set_opid(opid_frag.hash());
2,217✔
1614
            auto opid_iter = sbc.sbc_opids.los_opid_ranges.find(opid_frag);
2,217✔
1615
            if (opid_iter == sbc.sbc_opids.los_opid_ranges.end()) {
2,217✔
1616
                jlu.jlu_opid_frag = opid_frag.to_owned(sbc.sbc_allocator);
2,205✔
1617
            } else {
1618
                jlu.jlu_opid_frag = opid_iter->first;
12✔
1619
            }
1620
        }
1621

1622
        if (jlu.jlu_opid_frag) {
3,083✔
1623
            this->jlf_line_values.lvv_opid_value
1624
                = jlu.jlu_opid_frag->to_string();
2,381✔
1625
            this->jlf_line_values.lvv_opid_provenance
1626
                = logline_value_vector::opid_provenance::file;
2,381✔
1627
            auto opid_iter = sbc.sbc_opids.insert_op(
4,762✔
1628
                sbc.sbc_allocator,
1629
                jlu.jlu_opid_frag.value(),
2,381✔
1630
                ll.get_time<std::chrono::microseconds>(),
2,381✔
1631
                this->lf_timestamp_point_of_reference,
1632
                jlu.jlu_duration);
1633
            opid_iter->second.otr_level_stats.update_msg_count(
2,381✔
1634
                ll.get_msg_level());
1635
            auto& elems = opid_iter->second.otr_description.lod_elements;
2,381✔
1636
            if (jlu.jlu_opid_desc_frag && elems.empty()) {
2,381✔
UNCOV
1637
                elems.insert(0,
×
1638
                             fmt::format(FMT_STRING(" {}"),
8,760✔
1639
                                         jlu.jlu_opid_desc_frag.value()));
1640
            }
1641

1642
            if (jlu.jlu_subid) {
2,381✔
1643
                auto subid_frag
UNCOV
1644
                    = string_fragment::from_str(jlu.jlu_subid.value());
×
1645

UNCOV
1646
                auto* ostr = sbc.sbc_opids.sub_op_in_use(
×
1647
                    sbc.sbc_allocator,
1648
                    opid_iter,
1649
                    subid_frag,
UNCOV
1650
                    ll.get_time<std::chrono::microseconds>(),
×
1651
                    ll.get_msg_level());
UNCOV
1652
                if (ostr != nullptr && ostr->ostr_description.empty()) {
×
UNCOV
1653
                    log_op_description sub_desc;
×
UNCOV
1654
                    this->update_op_description(
×
UNCOV
1655
                        *this->lf_subid_description_def_vec, sub_desc);
×
UNCOV
1656
                    if (!sub_desc.lod_elements.empty()) {
×
1657
                        auto& sub_desc_def
1658
                            = this->lf_subid_description_def_vec->at(
×
UNCOV
1659
                                sub_desc.lod_index.value());
×
1660
                        ostr->ostr_description
UNCOV
1661
                            = sub_desc_def->to_string(sub_desc.lod_elements);
×
1662
                    }
1663
                }
1664
            }
1665

1666
            auto& otr = opid_iter->second;
2,381✔
1667
            this->update_op_description(*this->lf_opid_description_def_vec,
2,381✔
1668
                                        otr.otr_description);
2,381✔
1669
        } else {
1670
            this->jlf_line_values.lvv_opid_value = std::nullopt;
702✔
1671
        }
1672

1673
        jlu.jlu_sub_line_count += this->jlf_line_format_init_count;
3,083✔
1674
        for (int lpc = 0; lpc < jlu.jlu_sub_line_count; lpc++) {
16,302✔
1675
            ll.set_sub_offset(lpc);
13,219✔
1676
            if (lpc > 0) {
13,219✔
1677
                ll.set_level(
10,136✔
1678
                    (log_level_t) (ll.get_level_and_flags() | LEVEL_CONTINUED));
10,136✔
1679
            }
1680
            ll.set_has_ansi(jlu.jlu_has_ansi);
13,219✔
1681
            ll.set_valid_utf(jlu.jlu_valid_utf);
13,219✔
1682
            dst.emplace_back(ll);
13,219✔
1683
        }
1684

1685
        if (!this->lf_specialized) {
3,083✔
1686
            static const intern_string_t ts_field
1687
                = intern_string::lookup("__timestamp__", -1);
2,565✔
1688
            static const intern_string_t level_field
1689
                = intern_string::lookup("__level__");
4,021✔
1690
            for (const auto& [index, jfe] :
24,030✔
1691
                 lnav::itertools::enumerate(this->jlf_line_format))
26,595✔
1692
            {
1693
                if (jfe.jfe_type != json_log_field::VARIABLE
56,887✔
1694
                    || jfe.jfe_value.pp_value == ts_field
14,109✔
1695
                    || jfe.jfe_value.pp_value == level_field
11,598✔
1696
                    || jfe.jfe_default_value != "-")
35,574✔
1697
                {
1698
                    continue;
13,957✔
1699
                }
1700
                if (!jlu.jlu_format_hits[index]) {
7,508✔
1701
                    jlu.jlu_strikes += 1;
7,092✔
1702
                }
1703
            }
1704
        }
1705
    } else {
1706
        unsigned char* msg;
1707
        int line_count = 2;
157✔
1708

1709
        msg = yajl_get_error(
314✔
1710
            handle, 1, (const unsigned char*) sbr.get_data(), sbr.length());
157✔
1711
        if (msg != nullptr) {
157✔
1712
            auto msg_frag = string_fragment::from_c_str(msg);
157✔
1713
            log_debug("Unable to parse line at offset %d: %s",
157✔
1714
                      li.li_file_range.fr_offset,
1715
                      msg);
1716
            line_count = msg_frag.count('\n') + 1;
157✔
1717
            yajl_free_error(handle, msg);
157✔
1718
        }
1719
        if (!this->lf_specialized) {
157✔
1720
            return scan_no_match{"JSON parsing failed"};
154✔
1721
        }
1722
        for (int lpc = 0; lpc < line_count; lpc++) {
15✔
1723
            log_level_t level = LEVEL_INVALID;
12✔
1724

1725
            ll.set_time(dst.back().get_timeval());
12✔
1726
            if (lpc > 0) {
12✔
1727
                level = (log_level_t) (level | LEVEL_CONTINUED);
9✔
1728
            }
1729
            ll.set_level(level);
12✔
1730
            ll.set_sub_offset(lpc);
12✔
1731
            dst.emplace_back(ll);
12✔
1732
        }
1733
    }
1734

1735
    if (jlu.jlu_quality > 0) {
3,086✔
1736
        jlu.jlu_quality += 3000;
786✔
1737
    }
1738
    return scan_match{jlu.jlu_quality, jlu.jlu_strikes};
3,086✔
1739
}
8,853✔
1740

1741
log_format::scan_result_t
1742
external_log_format::scan(logfile& lf,
725,753✔
1743
                          std::vector<logline>& dst,
1744
                          const line_info& li,
1745
                          shared_buffer_ref& sbr,
1746
                          scan_batch_context& sbc)
1747
{
1748
    if (dst.empty()) {
725,753✔
1749
        auto file_options = lf.get_file_options();
32,113✔
1750

1751
        if (file_options) {
32,113✔
1752
            this->lf_date_time.dts_default_zone
1753
                = file_options->second.fo_default_zone.pp_value;
2,246✔
1754
        } else {
1755
            this->lf_date_time.dts_default_zone = nullptr;
29,867✔
1756
        }
1757
    }
32,113✔
1758

1759
    if (this->elf_type == elf_type_t::ELF_TYPE_JSON) {
725,753✔
1760
        return this->scan_json(dst, li, sbr, sbc);
119,554✔
1761
    }
1762

1763
    int curr_fmt = -1, orig_lock = this->last_pattern_index();
606,199✔
1764
    int pat_index = orig_lock;
606,199✔
1765
    auto line_sf = sbr.to_string_fragment();
606,199✔
1766
    thread_local auto md = lnav::pcre2pp::match_data::unitialized();
606,199✔
1767
    char tmp_opid_buf[hasher::STRING_SIZE];
1768

1769
    while (::next_format(this->elf_pattern_order, curr_fmt, pat_index)) {
2,007,223✔
1770
        auto* fpat = this->elf_pattern_order[curr_fmt].get();
1,406,833✔
1771
        auto* pat = fpat->p_pcre.pp_value.get();
1,406,833✔
1772

1773
        if (fpat->p_module_format) {
1,406,833✔
1774
            continue;
1,401,024✔
1775
        }
1776

1777
        auto found_match
1778
            = pat->capture_from(line_sf).into(md).found_p(PCRE2_NO_UTF_CHECK);
1,375,753✔
1779
        if (!found_match) {
1,375,753✔
1780
            if (!this->lf_pattern_locks.empty() && pat_index != -1) {
1,369,941✔
1781
                curr_fmt = -1;
1,994✔
1782
                pat_index = -1;
1,994✔
1783
            }
1784
            continue;
1,369,941✔
1785
        }
1786

1787
        auto ts = md[fpat->p_timestamp_field_index];
5,812✔
1788
        auto level_cap = md[fpat->p_level_field_index];
5,812✔
1789
        auto opid_cap = md[fpat->p_opid_field_index];
5,812✔
1790
        auto body_cap = md[fpat->p_body_field_index];
5,812✔
1791
        const char* last;
1792
        exttm log_time_tm;
5,812✔
1793
        timeval log_tv;
1794
        uint8_t mod_index = 0;
5,812✔
1795
        uint16_t opid = 0;
5,812✔
1796
        char combined_datetime_buf[512];
1797

1798
        if (fpat->p_time_field_index != -1) {
5,812✔
UNCOV
1799
            auto time_cap = md[fpat->p_time_field_index];
×
UNCOV
1800
            if (ts && time_cap) {
×
UNCOV
1801
                auto ts_str_len = snprintf(combined_datetime_buf,
×
1802
                                           sizeof(combined_datetime_buf),
1803
                                           "%.*sT%.*s",
1804
                                           ts->length(),
1805
                                           ts->data(),
1806
                                           time_cap->length(),
1807
                                           time_cap->data());
UNCOV
1808
                ts = string_fragment::from_bytes(combined_datetime_buf,
×
UNCOV
1809
                                                 ts_str_len);
×
1810
            }
1811
        }
1812

1813
        auto level = this->convert_level(
5,812✔
1814
            level_cap.value_or(string_fragment::invalid()), &sbc);
5,812✔
1815

1816
        if (!ts) {
5,812✔
UNCOV
1817
            level = log_level_t::LEVEL_INVALID;
×
1818
        } else if ((last
5,812✔
1819
                    = this->lf_date_time.scan(ts->data(),
11,624✔
1820
                                              ts->length(),
5,812✔
1821
                                              this->get_timestamp_formats(),
1822
                                              &log_time_tm,
1823
                                              log_tv))
1824
                   == nullptr)
5,812✔
1825
        {
1826
            auto ls = this->lf_date_time.unlock();
13✔
1827
            if ((last = this->lf_date_time.scan(ts->data(),
26✔
1828
                                                ts->length(),
13✔
1829
                                                this->get_timestamp_formats(),
1830
                                                &log_time_tm,
1831
                                                log_tv))
1832
                == nullptr)
13✔
1833
            {
1834
                this->lf_date_time.relock(ls);
2✔
1835
                continue;
3✔
1836
            }
1837
            if (last != nullptr) {
11✔
1838
                auto old_flags = this->lf_timestamp_flags & DATE_TIME_SET_FLAGS;
11✔
1839
                auto new_flags = log_time_tm.et_flags & DATE_TIME_SET_FLAGS;
11✔
1840

1841
                // It is unlikely a valid timestamp would lose much
1842
                // precision.
1843
                if (new_flags != old_flags) {
11✔
1844
                    continue;
1✔
1845
                }
1846
            }
1847

1848
            log_debug("%s:%d: date-time re-locked to %d",
10✔
1849
                      lf.get_unique_path().c_str(),
1850
                      dst.size(),
1851
                      this->lf_date_time.dts_fmt_lock);
1852
        }
1853

1854
        this->lf_timestamp_flags = log_time_tm.et_flags;
5,809✔
1855

1856
        if (!(this->lf_timestamp_flags
11,618✔
1857
              & (ETF_MILLIS_SET | ETF_MICROS_SET | ETF_NANOS_SET))
5,809✔
1858
            && !dst.empty()
5,265✔
1859
            && dst.back().get_time<std::chrono::seconds>().count()
4,538✔
1860
                == log_tv.tv_sec
4,538✔
1861
            && dst.back()
14,502✔
1862
                    .get_subsecond_time<std::chrono::milliseconds>()
9,237✔
1863
                    .count()
3,428✔
1864
                != 0)
1865
        {
1866
            auto log_ms
UNCOV
1867
                = dst.back().get_subsecond_time<std::chrono::microseconds>();
×
1868

1869
            log_time_tm.et_nsec
UNCOV
1870
                = std::chrono::duration_cast<std::chrono::nanoseconds>(log_ms)
×
UNCOV
1871
                      .count();
×
1872
            log_tv.tv_usec
UNCOV
1873
                = std::chrono::duration_cast<std::chrono::microseconds>(log_ms)
×
UNCOV
1874
                      .count();
×
1875
        }
1876

1877
        if (!((log_time_tm.et_flags & ETF_DAY_SET)
5,809✔
1878
              && (log_time_tm.et_flags & ETF_MONTH_SET)
5,768✔
1879
              && (log_time_tm.et_flags & ETF_YEAR_SET)))
5,768✔
1880
        {
1881
            this->check_for_new_year(dst, log_time_tm, log_tv);
792✔
1882
        }
1883

1884
        auto log_us = to_us(log_tv);
5,809✔
1885
        if (!fpat->p_opid_description_field_indexes.empty()) {
5,809✔
1886
            hasher h;
4,576✔
1887
            for (auto& fidx : fpat->p_opid_description_field_indexes) {
13,133✔
1888
                auto desc_cap = md[fidx];
8,557✔
1889
                if (desc_cap) {
8,557✔
1890
                    h.update(desc_cap.value());
8,510✔
1891
                }
1892
            }
1893
            h.to_string(tmp_opid_buf);
4,576✔
1894
            opid_cap = string_fragment::from_bytes(tmp_opid_buf,
9,152✔
1895
                                                   sizeof(tmp_opid_buf) - 1);
4,576✔
1896
        }
1897

1898
        auto duration_cap = md[fpat->p_duration_field_index];
5,809✔
1899
        if (duration_cap && !opid_cap) {
5,809✔
UNCOV
1900
            hasher h;
×
UNCOV
1901
            h.update(line_sf);
×
UNCOV
1902
            h.to_string(tmp_opid_buf);
×
UNCOV
1903
            opid_cap = string_fragment::from_bytes(tmp_opid_buf,
×
UNCOV
1904
                                                   sizeof(tmp_opid_buf) - 1);
×
1905
        }
1906
        if (opid_cap && !opid_cap->empty()) {
5,809✔
1907
            auto duration = std::chrono::microseconds{0};
5,372✔
1908
            if (duration_cap) {
5,372✔
1909
                auto from_res
1910
                    = humanize::try_from<double>(duration_cap.value());
86✔
1911
                if (from_res) {
86✔
UNCOV
1912
                    duration = std::chrono::microseconds(
×
1913
                        static_cast<int64_t>(from_res.value() * 1000000));
86✔
1914
                }
1915
            }
1916
            auto opid_iter
1917
                = sbc.sbc_opids.insert_op(sbc.sbc_allocator,
10,744✔
1918
                                          opid_cap.value(),
5,372✔
1919
                                          log_us,
1920
                                          this->lf_timestamp_point_of_reference,
1921
                                          duration);
1922
            auto& otr = opid_iter->second;
5,372✔
1923

1924
            otr.otr_level_stats.update_msg_count(level);
5,372✔
1925
            if (fpat->p_subid_field_index != -1) {
5,372✔
1926
                auto subid_cap = md[fpat->p_subid_field_index];
64✔
1927
                if (subid_cap && !subid_cap->empty()) {
64✔
1928
                    auto* ostr = sbc.sbc_opids.sub_op_in_use(sbc.sbc_allocator,
192✔
1929
                                                             opid_iter,
1930
                                                             subid_cap.value(),
64✔
1931
                                                             log_us,
1932
                                                             level);
1933
                    if (ostr != nullptr && ostr->ostr_description.empty()) {
64✔
1934
                        log_op_description sub_desc;
43✔
1935
                        this->update_op_description(
43✔
1936
                            *this->lf_subid_description_def_vec,
43✔
1937
                            sub_desc,
1938
                            fpat,
1939
                            md);
1940
                        if (!sub_desc.lod_elements.empty()) {
43✔
1941
                            auto& sub_desc_def
1942
                                = this->lf_subid_description_def_vec->at(
39✔
1943
                                    sub_desc.lod_index.value());
39✔
1944
                            ostr->ostr_description = sub_desc_def->to_string(
78✔
1945
                                sub_desc.lod_elements);
39✔
1946
                        }
1947
                    }
43✔
1948
                }
1949
            }
1950
            this->update_op_description(*this->lf_opid_description_def_vec,
5,372✔
1951
                                        otr.otr_description,
5,372✔
1952
                                        fpat,
1953
                                        md);
1954
            opid = opid_cap->hash();
5,372✔
1955
        }
1956
        if (fpat->p_module_field_index != -1) {
5,809✔
1957
            auto mod_cap = md[fpat->p_module_field_index];
637✔
1958
            if (mod_cap && body_cap) {
637✔
1959
                intern_string_t mod_name
1960
                    = intern_string::lookup(mod_cap.value());
634✔
1961
                auto mod_iter = MODULE_FORMATS.find(mod_name);
634✔
1962

1963
                if (mod_iter == MODULE_FORMATS.end()) {
634✔
1964
                    mod_index = this->module_scan(body_cap.value(), mod_name);
163✔
1965
                    mod_iter = MODULE_FORMATS.find(mod_name);
163✔
1966
                } else if (mod_iter->second.mf_mod_format) {
471✔
1967
                    mod_index = mod_iter->second.mf_mod_format->lf_mod_index;
4✔
1968
                }
1969

1970
                if (mod_index && level_cap && body_cap) {
634✔
1971
                    auto mod_elf
1972
                        = std::dynamic_pointer_cast<external_log_format>(
1973
                            mod_iter->second.mf_mod_format);
47✔
1974

1975
                    if (mod_elf) {
47✔
1976
                        thread_local auto mod_md
1977
                            = lnav::pcre2pp::match_data::unitialized();
47✔
1978

1979
                        shared_buffer_ref body_ref;
47✔
1980

1981
                        body_cap->trim();
47✔
1982

1983
                        int mod_pat_index = mod_elf->last_pattern_index();
47✔
1984
                        auto& mod_pat
1985
                            = *mod_elf->elf_pattern_order[mod_pat_index];
47✔
1986
                        auto match_res = mod_pat.p_pcre.pp_value
47✔
1987
                                             ->capture_from(body_cap.value())
47✔
1988
                                             .into(mod_md)
47✔
1989
                                             .matches(PCRE2_NO_UTF_CHECK)
94✔
1990
                                             .ignore_error();
47✔
1991
                        if (match_res) {
47✔
1992
                            auto mod_level_cap
1993
                                = mod_md[mod_pat.p_level_field_index];
47✔
1994

1995
                            level = mod_elf->convert_level(
94✔
1996
                                mod_level_cap.value_or(
1997
                                    string_fragment::invalid()),
94✔
1998
                                &sbc);
1999
                        }
2000
                    }
47✔
2001
                }
47✔
2002
            }
2003
        }
2004

2005
        for (const auto& ivd : fpat->p_value_by_index) {
65,727✔
2006
            if (!ivd.ivd_value_def->vd_meta.lvm_values_index) {
59,918✔
2007
                continue;
10,960✔
2008
            }
2009

2010
            ssize_t cap_size = md.capture_size(ivd.ivd_index);
48,958✔
2011
            auto& lvs = this->lf_value_stats[ivd.ivd_value_def->vd_meta
48,958✔
2012
                                                 .lvm_values_index.value()];
48,958✔
2013

2014
            if (cap_size > lvs.lvs_width) {
48,958✔
2015
                lvs.lvs_width = cap_size;
3,641✔
2016
            }
2017
        }
2018

2019
        for (auto value_index : fpat->p_numeric_value_indexes) {
10,608✔
2020
            const indexed_value_def& ivd = fpat->p_value_by_index[value_index];
4,799✔
2021
            const value_def& vd = *ivd.ivd_value_def;
4,799✔
2022
            auto num_cap = md[ivd.ivd_index];
4,799✔
2023

2024
            if (num_cap && num_cap->is_valid()) {
4,799✔
2025
                const struct scaling_factor* scaling = nullptr;
4,755✔
2026

2027
                if (ivd.ivd_unit_field_index >= 0) {
4,755✔
2028
                    auto unit_cap = md[ivd.ivd_unit_field_index];
80✔
2029

2030
                    if (unit_cap && unit_cap->is_valid()) {
80✔
2031
                        intern_string_t unit_val
2032
                            = intern_string::lookup(unit_cap.value());
80✔
2033

2034
                        auto unit_iter = vd.vd_unit_scaling.find(unit_val);
80✔
2035
                        if (unit_iter != vd.vd_unit_scaling.end()) {
80✔
2036
                            const auto& sf = unit_iter->second;
80✔
2037

2038
                            scaling = &sf;
80✔
2039
                        }
2040
                    }
2041
                }
2042

2043
                std::optional<double> dvalue_opt;
4,755✔
2044
                switch (vd.vd_meta.lvm_kind) {
4,755✔
2045
                    case value_kind_t::VALUE_INTEGER: {
4,589✔
2046
                        auto scan_res
2047
                            = scn::scan_int<int64_t>(num_cap->to_string_view());
4,589✔
2048
                        if (scan_res) {
4,589✔
2049
                            dvalue_opt = scan_res->value();
4,589✔
2050
                        }
2051
                        break;
4,589✔
2052
                    }
2053
                    case value_kind_t::VALUE_FLOAT: {
166✔
2054
                        auto scan_res = scn::scan_value<double>(
2055
                            num_cap->to_string_view());
166✔
2056
                        if (scan_res) {
166✔
2057
                            dvalue_opt = scan_res->value();
166✔
2058
                        }
2059
                        break;
166✔
2060
                    }
UNCOV
2061
                    default:
×
UNCOV
2062
                        break;
×
2063
                }
2064
                if (dvalue_opt) {
4,755✔
2065
                    auto dvalue = dvalue_opt.value();
4,755✔
2066
                    if (scaling != nullptr) {
4,755✔
2067
                        scaling->scale(dvalue);
80✔
2068
                    }
2069
                    this->lf_value_stats[vd.vd_meta.lvm_values_index.value()]
4,755✔
2070
                        .add_value(dvalue);
4,755✔
2071
                }
2072
            }
2073
        }
2074

2075
        dst.emplace_back(
5,809✔
2076
            li.li_file_range.fr_offset, log_us, level, mod_index, opid);
5,809✔
2077

2078
        auto src_file_cap = md[fpat->p_src_file_field_index];
5,809✔
2079
        auto src_line_cap = md[fpat->p_src_line_field_index];
5,809✔
2080
        if (src_file_cap && src_line_cap) {
5,809✔
2081
            auto h = hasher();
94✔
2082
            h.update(this->get_name().c_str());
94✔
2083
            h.update(src_file_cap.value());
94✔
2084
            h.update(src_line_cap.value());
94✔
2085
            dst.back().set_schema(h.to_array());
94✔
2086
        }
2087
        auto thread_id_cap = md[fpat->p_thread_id_field_index];
5,809✔
2088
        if (thread_id_cap) {
5,809✔
2089
            auto tid_iter = sbc.sbc_tids.insert_tid(
1,988✔
2090
                sbc.sbc_allocator, thread_id_cap.value(), log_us);
994✔
2091
            tid_iter->second.titr_level_stats.update_msg_count(level);
994✔
2092
        } else {
2093
            auto tid_iter = sbc.sbc_tids.insert_tid(
4,815✔
UNCOV
2094
                sbc.sbc_allocator, string_fragment{}, log_us);
×
2095
            tid_iter->second.titr_level_stats.update_msg_count(level);
4,815✔
2096
        }
2097

2098
        if (orig_lock != curr_fmt) {
5,809✔
2099
            uint32_t lock_line;
2100

2101
            log_debug("%s:%zu: changing pattern lock %d -> (%d)%s",
586✔
2102
                      lf.get_unique_path().c_str(),
2103
                      dst.size() - 1,
2104
                      orig_lock,
2105
                      curr_fmt,
2106
                      this->elf_pattern_order[curr_fmt]->p_name.c_str());
2107
            if (this->lf_pattern_locks.empty()) {
586✔
2108
                lock_line = 0;
560✔
2109
            } else {
2110
                lock_line = dst.size() - 1;
26✔
2111
            }
2112
            this->lf_pattern_locks.emplace_back(lock_line, curr_fmt);
586✔
2113
        }
2114
        return scan_match{1000};
5,809✔
2115
    }
2116

2117
    if (this->lf_specialized && !this->lf_multiline) {
600,390✔
2118
        const auto& last_line = dst.back();
1✔
2119

2120
        log_debug("%s: invalid line %d file_offset=%" PRIu64,
1✔
2121
                  lf.get_filename().c_str(),
2122
                  dst.size(),
2123
                  li.li_file_range.fr_offset);
2124
        dst.emplace_back(li.li_file_range.fr_offset,
1✔
UNCOV
2125
                         last_line.get_timeval(),
×
2126
                         log_level_t::LEVEL_INVALID);
1✔
2127

2128
        return scan_match{0};
1✔
2129
    }
2130

2131
    return scan_no_match{"no patterns matched"};
600,389✔
2132
}
2133

2134
uint8_t
2135
external_log_format::module_scan(string_fragment body_cap,
163✔
2136
                                 const intern_string_t& mod_name)
2137
{
2138
    uint8_t mod_index;
2139
    body_cap = body_cap.trim();
163✔
2140
    auto& ext_fmts = GRAPH_ORDERED_FORMATS;
163✔
2141
    module_format mf;
163✔
2142

2143
    for (auto& elf : ext_fmts) {
10,266✔
2144
        int curr_fmt = -1, fmt_lock = -1;
10,146✔
2145

2146
        while (::next_format(elf->elf_pattern_order, curr_fmt, fmt_lock)) {
28,548✔
2147
            thread_local auto md = lnav::pcre2pp::match_data::unitialized();
18,445✔
2148

2149
            auto& fpat = elf->elf_pattern_order[curr_fmt];
18,445✔
2150
            auto& pat = fpat->p_pcre;
18,445✔
2151

2152
            if (!fpat->p_module_format) {
18,445✔
2153
                continue;
18,402✔
2154
            }
2155

2156
            auto match_res = pat.pp_value->capture_from(body_cap)
445✔
2157
                                 .into(md)
445✔
2158
                                 .matches(PCRE2_NO_UTF_CHECK)
890✔
2159
                                 .ignore_error();
445✔
2160
            if (!match_res) {
445✔
2161
                continue;
402✔
2162
            }
2163

2164
            log_debug("%s:module format found -- %s (%d)",
43✔
2165
                      mod_name.get(),
2166
                      elf->get_name().get(),
2167
                      elf->lf_mod_index);
2168

2169
            mod_index = elf->lf_mod_index;
43✔
2170
            mf.mf_mod_format = elf->specialized(curr_fmt);
43✔
2171
            MODULE_FORMATS[mod_name] = mf;
43✔
2172

2173
            return mod_index;
43✔
2174
        }
2175
    }
2176

2177
    MODULE_FORMATS[mod_name] = mf;
120✔
2178

2179
    return 0;
120✔
2180
}
163✔
2181

2182
void
2183
external_log_format::annotate(logfile* lf,
7,346✔
2184
                              uint64_t line_number,
2185
                              string_attrs_t& sa,
2186
                              logline_value_vector& values,
2187
                              bool annotate_module) const
2188
{
2189
    thread_local auto md = lnav::pcre2pp::match_data::unitialized();
7,346✔
2190

2191
    auto& line = values.lvv_sbr;
7,346✔
2192
    line_range lr;
7,346✔
2193

2194
    line.erase_ansi();
7,346✔
2195
    if (this->elf_type != elf_type_t::ELF_TYPE_TEXT) {
7,346✔
2196
        if (this->jlf_cached_opts.full_message) {
738✔
2197
            values = this->jlf_line_values;
319✔
2198
            sa = this->jlf_line_attrs;
319✔
2199
        } else {
2200
            values.lvv_sbr = this->jlf_line_values.lvv_sbr.clone();
419✔
2201
            for (const auto& llv : this->jlf_line_values.lvv_values) {
4,208✔
2202
                if (this->jlf_cached_sub_range.contains(llv.lv_origin)) {
3,789✔
2203
                    values.lvv_values.emplace_back(llv);
912✔
2204
                    values.lvv_values.back().lv_origin.shift(
912✔
2205
                        this->jlf_cached_sub_range.lr_start,
912✔
2206
                        -this->jlf_cached_sub_range.lr_start);
912✔
2207
                }
2208
            }
2209
            for (const auto& attr : this->jlf_line_attrs) {
2,279✔
2210
                if (this->jlf_cached_sub_range.contains(attr.sa_range)) {
1,860✔
2211
                    sa.emplace_back(attr);
669✔
2212
                    sa.back().sa_range.shift(
669✔
2213
                        this->jlf_cached_sub_range.lr_start,
669✔
2214
                        -this->jlf_cached_sub_range.lr_start);
669✔
2215
                }
2216
            }
2217
            values.lvv_opid_value = this->jlf_line_values.lvv_opid_value;
419✔
2218
            values.lvv_opid_provenance
2219
                = this->jlf_line_values.lvv_opid_provenance;
419✔
2220
            values.lvv_thread_id_value
2221
                = this->jlf_line_values.lvv_thread_id_value;
419✔
2222
        }
2223
        log_format::annotate(lf, line_number, sa, values, annotate_module);
738✔
2224
        return;
2,345✔
2225
    }
2226

2227
    if (line.empty()) {
6,608✔
2228
        return;
5✔
2229
    }
2230

2231
    values.lvv_values.reserve(this->elf_value_defs.size());
6,603✔
2232

2233
    int pat_index = this->pattern_index_for_line(line_number);
6,603✔
2234
    const auto& pat = *this->elf_pattern_order[pat_index];
6,603✔
2235
    char tmp_opid_buf[hasher::STRING_SIZE];
2236

2237
    sa.reserve(pat.p_pcre.pp_value->get_capture_count());
6,603✔
2238
    auto match_res
2239
        = pat.p_pcre.pp_value->capture_from(line.to_string_fragment())
6,603✔
2240
              .into(md)
6,603✔
2241
              .matches(PCRE2_NO_UTF_CHECK)
13,206✔
2242
              .ignore_error();
6,603✔
2243
    if (!match_res) {
6,603✔
2244
        // A continued line still needs a body.
2245
        lr.lr_start = 0;
1,602✔
2246
        lr.lr_end = line.length();
1,602✔
2247
        sa.emplace_back(lr, SA_BODY.value());
1,602✔
2248
        if (!this->lf_multiline) {
1,602✔
2249
            auto len
UNCOV
2250
                = pat.p_pcre.pp_value->match_partial(line.to_string_fragment());
×
UNCOV
2251
            sa.emplace_back(
×
UNCOV
2252
                line_range{(int) len, -1},
×
UNCOV
2253
                SA_INVALID.value("Log line does not match any pattern"));
×
2254
        }
2255
        return;
1,602✔
2256
    }
2257

2258
    auto duration_cap = md[pat.p_duration_field_index];
5,001✔
2259

2260
    std::optional<string_fragment> module_cap;
5,001✔
2261
    if (!pat.p_module_format) {
5,001✔
2262
        auto ts_cap = md[pat.p_timestamp_field_index];
4,992✔
2263
        if (ts_cap) {
4,992✔
2264
            sa.emplace_back(to_line_range(ts_cap.value()), L_TIMESTAMP.value());
4,992✔
2265
        }
2266

2267
        if (pat.p_module_field_index != -1) {
4,992✔
2268
            module_cap = md[pat.p_module_field_index];
392✔
2269
            if (module_cap) {
392✔
2270
                sa.emplace_back(to_line_range(module_cap.value()),
390✔
2271
                                L_MODULE.value());
780✔
2272
            }
2273
        }
2274

2275
        auto opid_cap = md[pat.p_opid_field_index];
4,992✔
2276

2277
        if (!pat.p_opid_description_field_indexes.empty()) {
4,992✔
2278
            hasher h;
4,196✔
2279
            for (auto& fidx : pat.p_opid_description_field_indexes) {
12,145✔
2280
                auto desc_cap = md[fidx];
7,949✔
2281
                if (desc_cap) {
7,949✔
2282
                    h.update(desc_cap.value());
7,921✔
2283
                }
2284
            }
2285
            h.to_string(tmp_opid_buf);
4,196✔
2286
            opid_cap = string_fragment::from_bytes(tmp_opid_buf,
8,392✔
2287
                                                   sizeof(tmp_opid_buf) - 1);
4,196✔
2288
        } else if (duration_cap && !opid_cap) {
796✔
UNCOV
2289
            hasher h;
×
UNCOV
2290
            h.update(line.to_string_fragment());
×
UNCOV
2291
            h.to_string(tmp_opid_buf);
×
UNCOV
2292
            opid_cap = string_fragment::from_bytes(tmp_opid_buf,
×
UNCOV
2293
                                                   sizeof(tmp_opid_buf) - 1);
×
2294
        }
2295
        if (opid_cap && !opid_cap->empty()) {
4,992✔
2296
            sa.emplace_back(to_line_range(opid_cap.value()), L_OPID.value());
4,640✔
2297
            values.lvv_opid_value = opid_cap->to_string();
4,640✔
2298
            values.lvv_opid_provenance
2299
                = logline_value_vector::opid_provenance::file;
4,640✔
2300
        }
2301
    }
2302

2303
    auto body_cap = md[pat.p_body_field_index];
5,001✔
2304
    auto level_cap = md[pat.p_level_field_index];
5,001✔
2305
    auto src_file_cap = md[pat.p_src_file_field_index];
5,001✔
2306
    auto src_line_cap = md[pat.p_src_line_field_index];
5,001✔
2307
    auto thread_id_cap = md[pat.p_thread_id_field_index];
5,001✔
2308

2309
    if (level_cap
5,001✔
2310
        && (!body_cap
9,949✔
2311
            || (body_cap && level_cap->sf_begin != body_cap->sf_begin)))
9,949✔
2312
    {
2313
        sa.emplace_back(to_line_range(level_cap.value()), L_LEVEL.value());
4,556✔
2314
    }
2315

2316
    if (src_file_cap) {
5,001✔
2317
        sa.emplace_back(to_line_range(src_file_cap.value()),
105✔
2318
                        SA_SRC_FILE.value());
210✔
2319
    }
2320
    if (src_line_cap) {
5,001✔
2321
        sa.emplace_back(to_line_range(src_line_cap.value()),
105✔
2322
                        SA_SRC_LINE.value());
210✔
2323
    }
2324
    if (thread_id_cap) {
5,001✔
2325
        sa.emplace_back(to_line_range(thread_id_cap.value()),
590✔
2326
                        SA_THREAD_ID.value());
1,180✔
2327
        values.lvv_thread_id_value = thread_id_cap->to_string();
590✔
2328
    }
2329
    if (duration_cap) {
5,001✔
2330
        sa.emplace_back(to_line_range(duration_cap.value()),
3✔
2331
                        SA_DURATION.value());
6✔
2332
    }
2333

2334
    for (size_t lpc = 0; lpc < pat.p_value_by_index.size(); lpc++) {
57,265✔
2335
        const auto& ivd = pat.p_value_by_index[lpc];
52,264✔
2336
        const scaling_factor* scaling = nullptr;
52,264✔
2337
        auto cap = md[ivd.ivd_index];
52,264✔
2338
        const auto& vd = *ivd.ivd_value_def;
52,264✔
2339

2340
        if (ivd.ivd_unit_field_index >= 0) {
52,264✔
2341
            auto unit_cap = md[ivd.ivd_unit_field_index];
10✔
2342

2343
            if (unit_cap) {
10✔
2344
                intern_string_t unit_val
2345
                    = intern_string::lookup(unit_cap.value());
10✔
2346
                auto unit_iter = vd.vd_unit_scaling.find(unit_val);
10✔
2347
                if (unit_iter != vd.vd_unit_scaling.end()) {
10✔
2348
                    const auto& sf = unit_iter->second;
10✔
2349

2350
                    scaling = &sf;
10✔
2351
                }
2352
            }
2353
        }
2354

2355
        if (cap) {
52,264✔
2356
            values.lvv_values.emplace_back(
85,454✔
2357
                vd.vd_meta, line, to_line_range(cap.value()));
42,727✔
2358
            values.lvv_values.back().apply_scaling(scaling);
42,727✔
2359
        } else {
2360
            values.lvv_values.emplace_back(vd.vd_meta);
9,537✔
2361
        }
2362
        if (pat.p_module_format) {
52,264✔
2363
            values.lvv_values.back().lv_meta.lvm_from_module = true;
64✔
2364
        }
2365
    }
2366

2367
    bool did_mod_annotate_body = false;
5,001✔
2368
    if (annotate_module && module_cap && body_cap && body_cap->is_valid()) {
5,001✔
2369
        intern_string_t mod_name = intern_string::lookup(module_cap.value());
102✔
2370
        auto mod_iter = MODULE_FORMATS.find(mod_name);
102✔
2371

2372
        if (mod_iter != MODULE_FORMATS.end()
102✔
2373
            && mod_iter->second.mf_mod_format != nullptr)
102✔
2374
        {
2375
            auto& mf = mod_iter->second;
7✔
2376
            auto narrow_res
2377
                = line.narrow(body_cap->sf_begin, body_cap->length());
7✔
2378
            auto pre_mod_values_size = values.lvv_values.size();
7✔
2379
            auto pre_mod_sa_size = sa.size();
7✔
2380
            mf.mf_mod_format->annotate(lf, line_number, sa, values, false);
7✔
2381
            for (size_t lpc = pre_mod_values_size;
49✔
2382
                 lpc < values.lvv_values.size();
49✔
2383
                 lpc++)
2384
            {
2385
                values.lvv_values[lpc].lv_origin.shift(0, body_cap->sf_begin);
42✔
2386
            }
2387
            for (size_t lpc = pre_mod_sa_size; lpc < sa.size(); lpc++) {
14✔
2388
                sa[lpc].sa_range.shift(0, body_cap->sf_begin);
7✔
2389
            }
2390
            line.widen(narrow_res);
7✔
2391
            did_mod_annotate_body = true;
7✔
2392
        }
2393
    }
2394
    if (!did_mod_annotate_body) {
5,001✔
2395
        if (body_cap && body_cap->is_valid()) {
4,994✔
2396
            lr = to_line_range(body_cap.value());
4,975✔
2397
        } else {
2398
            lr.lr_start = line.length();
19✔
2399
            lr.lr_end = line.length();
19✔
2400
        }
2401
        sa.emplace_back(lr, SA_BODY.value());
4,994✔
2402
    }
2403

2404
    log_format::annotate(lf, line_number, sa, values, annotate_module);
5,001✔
2405
}
2406

2407
void
2408
external_log_format::rewrite(exec_context& ec,
43✔
2409
                             shared_buffer_ref& line,
2410
                             string_attrs_t& sa,
2411
                             std::string& value_out)
2412
{
2413
    auto& values = *ec.ec_line_values;
43✔
2414

2415
    value_out.assign(line.get_data(), line.length());
43✔
2416

2417
    for (auto iter = values.lvv_values.begin(); iter != values.lvv_values.end();
259✔
2418
         ++iter)
216✔
2419
    {
2420
        if (!iter->lv_origin.is_valid()) {
216✔
2421
            log_debug("%d: not rewriting value with invalid origin -- %s",
24✔
2422
                      ec.ec_top_line,
2423
                      iter->lv_meta.lvm_name.get());
2424
            continue;
184✔
2425
        }
2426

2427
        auto vd_iter = this->elf_value_defs.find(iter->lv_meta.lvm_name);
192✔
2428
        if (vd_iter == this->elf_value_defs.end()) {
192✔
UNCOV
2429
            log_debug("not rewriting undefined value -- %s",
×
2430
                      iter->lv_meta.lvm_name.get());
UNCOV
2431
            continue;
×
2432
        }
2433

2434
        const auto& vd = *vd_iter->second;
192✔
2435

2436
        if (vd.vd_rewriter.empty()) {
192✔
2437
            continue;
160✔
2438
        }
2439

2440
        auto _sg = ec.enter_source(
2441
            vd_iter->second->vd_rewrite_src_name, 1, vd.vd_rewriter);
32✔
2442
        std::string field_value;
32✔
2443

2444
        auto_mem<FILE> tmpout(fclose);
32✔
2445

2446
        tmpout = std::tmpfile();
32✔
2447
        if (!tmpout) {
32✔
UNCOV
2448
            log_error("unable to create temporary file");
×
UNCOV
2449
            return;
×
2450
        }
2451
        fcntl(fileno(tmpout), F_SETFD, FD_CLOEXEC);
32✔
2452
        auto fd_copy = auto_fd::dup_of(fileno(tmpout));
32✔
2453
        fd_copy.close_on_exec();
32✔
2454
        auto ec_out = std::make_pair(tmpout.release(), fclose);
32✔
2455
        {
2456
            exec_context::output_guard og(ec, "tmp", ec_out);
64✔
2457

2458
            auto exec_res = execute_any(ec, vd.vd_rewriter);
32✔
2459
            if (exec_res.isOk()) {
32✔
2460
                field_value = exec_res.unwrap();
32✔
2461
            } else {
2462
                field_value = exec_res.unwrapErr().to_attr_line().get_string();
×
2463
            }
2464
        }
32✔
2465
        struct stat st;
2466
        fstat(fd_copy.get(), &st);
32✔
2467
        if (st.st_size > 0) {
32✔
2468
            auto buf = auto_buffer::alloc(st.st_size);
2✔
2469

2470
            buf.resize(st.st_size);
2✔
2471
            pread(fd_copy.get(), buf.in(), st.st_size, 0);
2✔
2472
            field_value = buf.to_string();
2✔
2473
        }
2✔
2474
        value_out.erase(iter->lv_origin.lr_start, iter->lv_origin.length());
32✔
2475

2476
        int32_t shift_amount
2477
            = ((int32_t) field_value.length()) - iter->lv_origin.length();
32✔
2478
        auto orig_lr = iter->lv_origin;
32✔
2479
        value_out.insert(iter->lv_origin.lr_start, field_value);
32✔
2480
        for (auto shift_iter = values.lvv_values.begin();
32✔
2481
             shift_iter != values.lvv_values.end();
180✔
2482
             ++shift_iter)
148✔
2483
        {
2484
            shift_iter->lv_origin.shift_range(orig_lr, shift_amount);
148✔
2485
        }
2486
        shift_string_attrs(sa, orig_lr, shift_amount);
32✔
2487
    }
32✔
2488
}
2489

2490
static int
2491
read_json_field(yajlpp_parse_context* ypc,
150,397✔
2492
                const unsigned char* str,
2493
                size_t len,
2494
                yajl_string_props_t* props)
2495
{
2496
    auto* jlu = (json_log_userdata*) ypc->ypc_userdata;
150,397✔
2497
    timeval tv_out;
2498
    const auto frag = string_fragment::from_bytes(str, len);
150,397✔
2499
    intern_string_t field_name;
150,397✔
2500
    const auto* vd = jlu->get_field_def(ypc);
150,397✔
2501

2502
    if (vd != nullptr) {
150,397✔
2503
        field_name = vd->vd_meta.lvm_name;
13,437✔
2504
    }
2505

2506
    if (field_name.empty()) {
150,397✔
2507
        if (!jlu->jlu_format->elf_opid_field.empty()) {
136,960✔
2508
            auto path_sf = ypc->get_path_as_string_fragment();
45,802✔
2509
            if (path_sf.startswith(jlu->jlu_format->elf_opid_field.c_str())) {
45,802✔
2510
                jlu->jlu_opid_hasher.update(path_sf);
8,757✔
2511
                jlu->jlu_opid_hasher.update(frag);
8,757✔
2512
            }
2513
        }
2514
    } else if (jlu->jlu_format->lf_timestamp_field == field_name) {
13,437✔
2515
        const auto* last = jlu->jlu_format->lf_date_time.scan(
2,976✔
2516
            (const char*) str,
2517
            len,
2518
            jlu->jlu_format->get_timestamp_formats(),
2,976✔
2519
            &jlu->jlu_exttm,
2520
            tv_out);
2521
        if (last == nullptr) {
2,976✔
2522
            auto ls = jlu->jlu_format->lf_date_time.unlock();
22✔
2523
            if ((last = jlu->jlu_format->lf_date_time.scan(
22✔
2524
                     (const char*) str,
2525
                     len,
2526
                     jlu->jlu_format->get_timestamp_formats(),
22✔
2527
                     &jlu->jlu_exttm,
2528
                     tv_out))
2529
                == nullptr)
22✔
2530
            {
UNCOV
2531
                jlu->jlu_format->lf_date_time.relock(ls);
×
2532
            }
2533
            if (last != nullptr) {
22✔
2534
                auto old_flags
22✔
2535
                    = jlu->jlu_format->lf_timestamp_flags & DATE_TIME_SET_FLAGS;
22✔
2536
                auto new_flags = jlu->jlu_exttm.et_flags & DATE_TIME_SET_FLAGS;
22✔
2537

2538
                // It is unlikely a valid timestamp would lose much
2539
                // precision.
2540
                if (new_flags != old_flags) {
22✔
UNCOV
2541
                    last = nullptr;
×
2542
                }
2543
            }
2544
        }
2545
        if (last != nullptr) {
2,976✔
2546
            jlu->jlu_format->lf_timestamp_flags = jlu->jlu_exttm.et_flags;
2,976✔
2547
            jlu->jlu_base_line->set_time(tv_out);
2,976✔
2548
        }
2549
    } else if (jlu->jlu_format->elf_level_pointer.pp_value != nullptr) {
10,461✔
2550
        if (jlu->jlu_format->elf_level_pointer.pp_value
230✔
2551
                ->find_in(field_name.to_string_fragment(), PCRE2_NO_UTF_CHECK)
230✔
2552
                .ignore_error()
230✔
2553
                .has_value())
115✔
2554
        {
2555
            jlu->jlu_base_line->set_level(
×
UNCOV
2556
                jlu->jlu_format->convert_level(frag, jlu->jlu_batch_context));
×
2557
        }
2558
    }
2559
    if (!field_name.empty() && jlu->jlu_format->elf_level_field == field_name) {
150,397✔
2560
        jlu->jlu_base_line->set_level(
3,318✔
2561
            jlu->jlu_format->convert_level(frag, jlu->jlu_batch_context));
3,318✔
2562
    }
2563
    if (!field_name.empty() && jlu->jlu_format->elf_opid_field == field_name) {
150,397✔
2564
        jlu->jlu_base_line->set_opid(frag.hash());
164✔
2565

2566
        auto& sbc = *jlu->jlu_batch_context;
164✔
2567
        auto opid_iter = sbc.sbc_opids.los_opid_ranges.find(frag);
164✔
2568
        if (opid_iter == sbc.sbc_opids.los_opid_ranges.end()) {
164✔
2569
            jlu->jlu_opid_frag = frag.to_owned(sbc.sbc_allocator);
141✔
2570
        } else {
2571
            jlu->jlu_opid_frag = opid_iter->first;
23✔
2572
        }
2573
    }
2574
    if (!field_name.empty()
150,397✔
2575
        && jlu->jlu_format->elf_thread_id_field == field_name)
150,397✔
2576
    {
UNCOV
2577
        auto& sbc = *jlu->jlu_batch_context;
×
UNCOV
2578
        auto tid_iter = sbc.sbc_tids.ltis_tid_ranges.find(frag);
×
UNCOV
2579
        if (tid_iter == sbc.sbc_tids.ltis_tid_ranges.end()) {
×
UNCOV
2580
            jlu->jlu_tid_frag = frag.to_owned(sbc.sbc_allocator);
×
2581
        } else {
UNCOV
2582
            jlu->jlu_tid_frag = tid_iter->first;
×
2583
        }
2584
    }
2585
    if (!jlu->jlu_format->elf_subid_field.empty()
150,397✔
2586
        && jlu->jlu_format->elf_subid_field == field_name)
150,397✔
2587
    {
UNCOV
2588
        jlu->jlu_subid = frag.to_string();
×
2589
    }
2590
    if (!field_name.empty()
150,397✔
2591
        && jlu->jlu_format->elf_duration_field == field_name)
150,397✔
2592
    {
2593
        auto from_res = humanize::try_from<double>(frag);
×
2594
        if (from_res) {
×
UNCOV
2595
            jlu->jlu_duration = std::chrono::microseconds(
×
2596
                static_cast<int64_t>(from_res.value() * 1000000));
2597
        }
2598
    }
2599

2600
    if (vd != nullptr && vd->vd_is_desc_field) {
150,397✔
2601
        auto frag_copy = frag.to_owned(jlu->jlu_format->lf_desc_allocator);
42✔
2602

2603
        jlu->jlu_format->lf_desc_captures.emplace(field_name, frag_copy);
42✔
2604
    }
2605

2606
    jlu->add_sub_lines_for(vd, ypc->is_level(1), std::nullopt, str, len, props);
150,397✔
2607

2608
    return 1;
150,397✔
2609
}
2610

2611
static int
2612
rewrite_json_field(yajlpp_parse_context* ypc,
69,343✔
2613
                   const unsigned char* str,
2614
                   size_t len,
2615
                   yajl_string_props_t* props)
2616
{
2617
    static const intern_string_t body_name = intern_string::lookup("body", -1);
69,343✔
2618
    auto* jlu = (json_log_userdata*) ypc->ypc_userdata;
69,343✔
2619
    intern_string_t field_name;
69,343✔
2620
    const auto* vd = jlu->get_field_def(ypc);
69,343✔
2621
    auto frag = string_fragment::from_bytes(str, len);
69,343✔
2622

2623
    if (!ypc->is_level(1) && vd == nullptr) {
69,343✔
2624
        if (!jlu->jlu_format->elf_opid_field.empty()) {
57,531✔
2625
            auto path_sf = ypc->get_path_as_string_fragment();
56,725✔
2626
            if (path_sf.startswith(jlu->jlu_format->elf_opid_field.c_str())) {
56,725✔
2627
                jlu->jlu_opid_hasher.update(path_sf);
43✔
2628
                jlu->jlu_opid_hasher.update(frag);
43✔
2629
            }
2630
        }
2631
        return 1;
57,531✔
2632
    }
2633
    if (vd != nullptr) {
11,812✔
2634
        field_name = vd->vd_meta.lvm_name;
11,338✔
2635
    } else {
2636
        field_name = ypc->get_path();
474✔
2637
    }
2638

2639
    if (jlu->jlu_format->elf_opid_field == field_name) {
11,812✔
2640
        jlu->jlu_format->jlf_line_values.lvv_opid_value = frag.to_string();
967✔
2641
        jlu->jlu_format->jlf_line_values.lvv_opid_provenance
967✔
2642
            = logline_value_vector::opid_provenance::file;
967✔
2643
    }
2644
    if (jlu->jlu_format->lf_timestamp_field == field_name) {
11,812✔
2645
        char time_buf[64];
2646

2647
        // TODO add a timeval kind to logline_value
2648
        if (jlu->jlu_line->is_time_skewed()
2,196✔
2649
            || (jlu->jlu_format->lf_timestamp_flags
4,392✔
2650
                & (ETF_MICROS_SET | ETF_NANOS_SET | ETF_ZONE_SET)))
2,196✔
2651
        {
2652
            timeval tv;
2653

2654
            const auto* last = jlu->jlu_format->lf_date_time.scan(
2,196✔
2655
                (const char*) str,
2656
                len,
2657
                jlu->jlu_format->get_timestamp_formats(),
2,196✔
2658
                &jlu->jlu_exttm,
2659
                tv);
2660
            if (last == nullptr) {
2,196✔
2661
                auto ls = jlu->jlu_format->lf_date_time.unlock();
58✔
2662
                if ((last = jlu->jlu_format->lf_date_time.scan(
58✔
2663
                         (const char*) str,
2664
                         len,
2665
                         jlu->jlu_format->get_timestamp_formats(),
58✔
2666
                         &jlu->jlu_exttm,
2667
                         tv))
2668
                    == nullptr)
58✔
2669
                {
UNCOV
2670
                    jlu->jlu_format->lf_date_time.relock(ls);
×
2671
                }
2672
            }
2673
            if (!jlu->jlu_subline_opts.hash_hack) {
2,196✔
2674
                if (jlu->jlu_exttm.et_flags & ETF_ZONE_SET
2,196✔
2675
                    && jlu->jlu_format->lf_date_time.dts_zoned_to_local)
2,196✔
2676
                {
2677
                    jlu->jlu_exttm.et_flags &= ~ETF_Z_IS_UTC;
2,196✔
2678
                }
2679
                jlu->jlu_exttm.et_gmtoff
2680
                    = jlu->jlu_format->lf_date_time.dts_local_offset_cache;
2,196✔
2681
            }
2682
            jlu->jlu_format->lf_date_time.ftime(
2,196✔
2683
                time_buf,
2684
                sizeof(time_buf),
2685
                jlu->jlu_format->get_timestamp_formats(),
2,196✔
2686
                jlu->jlu_exttm);
2,196✔
2687
        } else {
UNCOV
2688
            sql_strftime(
×
UNCOV
2689
                time_buf, sizeof(time_buf), jlu->jlu_line->get_timeval(), 'T');
×
2690
        }
2691
        jlu->jlu_format->jlf_line_values.lvv_values.emplace_back(
4,392✔
2692
            jlu->jlu_format->get_value_meta(field_name,
4,392✔
2693
                                            value_kind_t::VALUE_TEXT),
2694
            std::string{time_buf});
6,588✔
2695
    } else if (jlu->jlu_shared_buffer.contains((const char*) str)) {
9,616✔
2696
        auto str_offset = (int) ((const char*) str - jlu->jlu_line_value);
9,210✔
2697
        if (field_name == jlu->jlu_format->elf_body_field) {
9,210✔
2698
            jlu->jlu_format->jlf_line_values.lvv_values.emplace_back(
820✔
2699
                logline_value_meta(body_name,
1,640✔
2700
                                   value_kind_t::VALUE_TEXT,
UNCOV
2701
                                   logline_value_meta::internal_column{},
×
2702
                                   jlu->jlu_format),
820✔
2703
                string_fragment::from_byte_range(
1,640✔
2704
                    jlu->jlu_shared_buffer.get_data(),
820✔
2705
                    str_offset,
2706
                    str_offset + len));
820✔
2707
        }
2708

2709
        jlu->jlu_format->jlf_line_values.lvv_values.emplace_back(
9,210✔
2710
            jlu->jlu_format->get_value_meta(field_name,
18,420✔
2711
                                            value_kind_t::VALUE_TEXT),
2712
            string_fragment::from_byte_range(jlu->jlu_shared_buffer.get_data(),
18,420✔
2713
                                             str_offset,
2714
                                             str_offset + len));
9,210✔
2715
    } else {
2716
        if (field_name == jlu->jlu_format->elf_body_field) {
406✔
2717
            jlu->jlu_format->jlf_line_values.lvv_values.emplace_back(
315✔
2718
                logline_value_meta(body_name,
630✔
2719
                                   value_kind_t::VALUE_TEXT,
UNCOV
2720
                                   logline_value_meta::internal_column{},
×
2721
                                   jlu->jlu_format),
315✔
2722
                std::string{(const char*) str, len});
1,260✔
2723
        }
2724

2725
        jlu->jlu_format->jlf_line_values.lvv_values.emplace_back(
406✔
2726
            jlu->jlu_format->get_value_meta(ypc, vd, value_kind_t::VALUE_TEXT),
812✔
2727
            std::string{(const char*) str, len});
1,624✔
2728
    }
2729
    if (vd != nullptr && vd->vd_is_desc_field
11,338✔
2730
        && jlu->jlu_format->elf_opid_field.empty())
23,150✔
2731
    {
2732
        auto frag_copy = frag.to_owned(jlu->jlu_format->lf_desc_allocator);
62✔
2733

2734
        jlu->jlu_format->lf_desc_captures.emplace(field_name, frag_copy);
62✔
2735
    }
2736

2737
    return 1;
11,812✔
2738
}
2739

2740
void
2741
external_log_format::get_subline(const logline& ll,
23,456✔
2742
                                 shared_buffer_ref& sbr,
2743
                                 subline_options opts)
2744
{
2745
    if (this->elf_type == elf_type_t::ELF_TYPE_TEXT) {
23,456✔
2746
        return;
18,927✔
2747
    }
2748

2749
    if (this->jlf_cached_offset != ll.get_offset()
4,529✔
2750
        || this->jlf_cached_opts != opts)
4,529✔
2751
    {
2752
        auto& ypc = *(this->jlf_parse_context);
2,423✔
2753
        yajl_handle handle = this->jlf_yajl_handle.get();
2,423✔
2754
        json_log_userdata jlu(sbr, nullptr);
2,423✔
2755

2756
        jlu.jlu_subline_opts = opts;
2,423✔
2757

2758
        this->lf_desc_captures.clear();
2,423✔
2759
        this->lf_desc_allocator.reset();
2,423✔
2760
        this->jlf_share_manager.invalidate_refs();
2,423✔
2761
        this->jlf_cached_line.clear();
2,423✔
2762
        this->jlf_line_values.clear();
2,423✔
2763
        this->jlf_line_offsets.clear();
2,423✔
2764
        this->jlf_line_attrs.clear();
2,423✔
2765

2766
        auto line_frag = sbr.to_string_fragment();
2,423✔
2767

2768
        if (!line_frag.startswith("{")) {
2,423✔
2769
            this->jlf_cached_line.resize(line_frag.length());
72✔
2770
            memcpy(this->jlf_cached_line.data(),
72✔
2771
                   line_frag.data(),
72✔
2772
                   line_frag.length());
72✔
2773
            this->jlf_line_values.clear();
72✔
2774
            sbr.share(this->jlf_share_manager,
144✔
2775
                      &this->jlf_cached_line[0],
72✔
2776
                      this->jlf_cached_line.size());
2777
            this->jlf_line_values.lvv_sbr = sbr.clone();
72✔
2778
            this->jlf_line_attrs.emplace_back(
72✔
UNCOV
2779
                line_range{0, -1},
×
2780
                SA_INVALID.value(fmt::format(
144✔
2781
                    FMT_STRING("line at offset {} is not a JSON-line"),
144✔
2782
                    ll.get_offset())));
72✔
2783
            return;
72✔
2784
        }
2785

2786
        yajl_reset(handle);
2,351✔
2787
        ypc.set_static_handler(json_log_rewrite_handlers.jpc_children[0]);
2,351✔
2788
        ypc.ypc_userdata = &jlu;
2,351✔
2789
        ypc.ypc_ignore_unused = true;
2,351✔
2790
        ypc.ypc_alt_callbacks.yajl_start_array = json_array_start;
2,351✔
2791
        ypc.ypc_alt_callbacks.yajl_end_array = json_array_end;
2,351✔
2792
        ypc.ypc_alt_callbacks.yajl_start_map = json_array_start;
2,351✔
2793
        ypc.ypc_alt_callbacks.yajl_end_map = json_array_end;
2,351✔
2794
        jlu.jlu_format = this;
2,351✔
2795
        jlu.jlu_line = &ll;
2,351✔
2796
        jlu.jlu_handle = handle;
2,351✔
2797
        jlu.jlu_line_value = sbr.get_data();
2,351✔
2798
        jlu.jlu_format_hits.resize(this->jlf_line_format.size());
2,351✔
2799

2800
        yajl_status parse_status = yajl_parse(
4,702✔
2801
            handle, (const unsigned char*) sbr.get_data(), sbr.length());
2,351✔
2802
        if (parse_status != yajl_status_ok
2,351✔
2803
            || yajl_complete_parse(handle) != yajl_status_ok)
2,351✔
2804
        {
2805
            unsigned char* msg;
2806
            std::string full_msg;
14✔
2807

2808
            msg = yajl_get_error(
28✔
2809
                handle, 1, (const unsigned char*) sbr.get_data(), sbr.length());
14✔
2810
            if (msg != nullptr) {
14✔
2811
                full_msg = fmt::format(
14✔
2812
                    FMT_STRING("[offset: {}] {}\n{}"),
28✔
2813
                    ll.get_offset(),
14✔
2814
                    fmt::string_view{sbr.get_data(), sbr.length()},
14✔
2815
                    reinterpret_cast<char*>(msg));
28✔
2816
                yajl_free_error(handle, msg);
14✔
2817
            }
2818

2819
            this->jlf_cached_line.resize(full_msg.size());
14✔
2820
            memcpy(
14✔
2821
                this->jlf_cached_line.data(), full_msg.data(), full_msg.size());
14✔
2822
            this->jlf_line_values.clear();
14✔
2823
            this->jlf_line_attrs.emplace_back(
14✔
UNCOV
2824
                line_range{0, -1},
×
2825
                SA_INVALID.value("JSON line failed to parse"));
28✔
2826
        } else {
14✔
2827
            std::vector<logline_value>::iterator lv_iter;
2,337✔
2828
            bool used_values[this->jlf_line_values.lvv_values.size()];
4,674✔
2829
            struct line_range lr;
2,337✔
2830

2831
            memset(used_values, 0, sizeof(used_values));
2,337✔
2832
            for (lv_iter = this->jlf_line_values.lvv_values.begin();
2,337✔
2833
                 lv_iter != this->jlf_line_values.lvv_values.end();
20,790✔
2834
                 ++lv_iter)
18,453✔
2835
            {
2836
                lv_iter->lv_meta.lvm_format = this;
18,453✔
2837
            }
2838

2839
            if (jlu.jlu_tid_number) {
2,337✔
2840
                this->jlf_line_values.lvv_thread_id_value
2841
                    = fmt::to_string(jlu.jlu_tid_number.value());
114✔
2842
            } else if (jlu.jlu_tid_frag) {
2,223✔
2843
                this->jlf_line_values.lvv_thread_id_value
UNCOV
2844
                    = jlu.jlu_tid_frag->to_string();
×
2845
            }
2846

2847
            if (this->elf_opid_field.empty()
2,337✔
2848
                && this->lf_opid_description_def->size() == 1)
2,337✔
2849
            {
2850
                auto found_opid_desc = false;
217✔
2851
                const auto& od = this->lf_opid_description_def->begin()->second;
217✔
2852
                for (const auto& desc : *od.od_descriptors) {
651✔
2853
                    auto desc_iter
2854
                        = this->lf_desc_captures.find(desc.od_field.pp_value);
434✔
2855
                    if (desc_iter == this->lf_desc_captures.end()) {
434✔
2856
                        continue;
372✔
2857
                    }
2858
                    found_opid_desc = true;
62✔
2859
                    jlu.jlu_opid_hasher.update(desc_iter->second);
62✔
2860
                }
2861
                if (found_opid_desc) {
217✔
2862
                    this->jlf_line_values.lvv_opid_value
2863
                        = jlu.jlu_opid_hasher.to_string();
31✔
2864
                    this->jlf_line_values.lvv_opid_provenance
2865
                        = logline_value_vector::opid_provenance::file;
31✔
2866
                }
2867
            } else if (!jlu.jlu_opid_desc_frag && !jlu.jlu_opid_frag
4,212✔
2868
                       && jlu.jlu_duration > 0us)
4,212✔
2869
            {
UNCOV
2870
                jlu.jlu_opid_hasher.update(line_frag);
×
2871
                this->jlf_line_values.lvv_opid_value
UNCOV
2872
                    = jlu.jlu_opid_hasher.to_string();
×
2873
                this->jlf_line_values.lvv_opid_provenance
UNCOV
2874
                    = logline_value_vector::opid_provenance::file;
×
2875
            }
2876
            if (jlu.jlu_opid_desc_frag) {
2,337✔
2877
                this->jlf_line_values.lvv_opid_value
2878
                    = jlu.jlu_opid_hasher.to_string();
28✔
2879
                this->jlf_line_values.lvv_opid_provenance
2880
                    = logline_value_vector::opid_provenance::file;
28✔
2881
            }
2882
            if (jlu.jlu_opid_frag) {
2,337✔
2883
                this->jlf_line_values.lvv_opid_value
2884
                    = jlu.jlu_opid_frag->to_string();
×
2885
                this->jlf_line_values.lvv_opid_provenance
2886
                    = logline_value_vector::opid_provenance::file;
×
2887
            }
2888

2889
            int sub_offset = this->jlf_line_format_init_count;
2,337✔
2890
            for (const auto& jfe : this->jlf_line_format) {
30,258✔
2891
                static const intern_string_t ts_field
2892
                    = intern_string::lookup("__timestamp__", -1);
27,921✔
2893
                static const intern_string_t level_field
2894
                    = intern_string::lookup("__level__");
28,013✔
2895
                size_t begin_size = this->jlf_cached_line.size();
27,921✔
2896

2897
                switch (jfe.jfe_type) {
27,921✔
2898
                    case json_log_field::CONSTANT:
3,711✔
2899
                        this->json_append_to_cache(
3,711✔
2900
                            jfe.jfe_default_value.c_str(),
2901
                            jfe.jfe_default_value.size());
3,711✔
2902
                        break;
3,711✔
2903
                    case json_log_field::VARIABLE:
24,210✔
2904
                        lv_iter = find_if(
24,210✔
2905
                            this->jlf_line_values.lvv_values.begin(),
2906
                            this->jlf_line_values.lvv_values.end(),
2907
                            logline_value_name_cmp(&jfe.jfe_value.pp_value));
2908
                        if (lv_iter != this->jlf_line_values.lvv_values.end()) {
24,210✔
2909
                            auto str = lv_iter->to_string();
8,640✔
2910
                            value_def* vd = nullptr;
8,640✔
2911

2912
                            if (lv_iter->lv_meta.lvm_values_index) {
8,640✔
2913
                                vd = this->elf_value_def_order
2914
                                         [lv_iter->lv_meta.lvm_values_index
8,640✔
2915
                                              .value()]
8,640✔
2916
                                             .get();
8,640✔
2917
                            }
2918
                            while (endswith(str, "\n")) {
8,902✔
2919
                                str.pop_back();
262✔
2920
                            }
2921
                            size_t nl_pos = str.find('\n');
8,640✔
2922

2923
                            if (!jfe.jfe_prefix.empty()) {
8,640✔
2924
                                this->json_append_to_cache(jfe.jfe_prefix);
4,985✔
2925
                            }
2926
                            lr.lr_start = this->jlf_cached_line.size();
8,640✔
2927

2928
                            if ((int) str.size() > jfe.jfe_max_width) {
8,640✔
2929
                                switch (jfe.jfe_overflow) {
233✔
2930
                                    case json_format_element::overflow_t::
165✔
2931
                                        ABBREV: {
2932
                                        size_t new_size
2933
                                            = abbreviate_str(&str[0],
165✔
2934
                                                             str.size(),
2935
                                                             jfe.jfe_max_width);
165✔
2936
                                        str.resize(new_size);
165✔
2937
                                        this->json_append(jfe, vd, str);
165✔
2938
                                        break;
165✔
2939
                                    }
2940
                                    case json_format_element::overflow_t::
62✔
2941
                                        TRUNCATE: {
2942
                                        this->json_append_to_cache(
62✔
2943
                                            str.c_str(), jfe.jfe_max_width);
62✔
2944
                                        break;
62✔
2945
                                    }
2946
                                    case json_format_element::overflow_t::
6✔
2947
                                        DOTDOT: {
2948
                                        size_t middle
6✔
2949
                                            = (jfe.jfe_max_width / 2) - 1;
6✔
2950
                                        this->json_append_to_cache(str.c_str(),
6✔
2951
                                                                   middle);
2952
                                        this->json_append_to_cache("..", 2);
6✔
2953
                                        size_t rest
6✔
2954
                                            = (jfe.jfe_max_width - middle - 2);
6✔
2955
                                        this->json_append_to_cache(
12✔
2956
                                            str.c_str() + str.size() - rest,
6✔
2957
                                            rest);
2958
                                        break;
6✔
2959
                                    }
UNCOV
2960
                                    case json_format_element::overflow_t::
×
2961
                                        LASTWORD: {
2962
                                        size_t new_size
UNCOV
2963
                                            = last_word_str(&str[0],
×
2964
                                                            str.size(),
UNCOV
2965
                                                            jfe.jfe_max_width);
×
UNCOV
2966
                                        str.resize(new_size);
×
UNCOV
2967
                                        this->json_append(jfe, vd, str);
×
UNCOV
2968
                                        break;
×
2969
                                    }
2970
                                }
2971
                            } else {
2972
                                sub_offset
2973
                                    += std::count(str.begin(), str.end(), '\n');
8,407✔
2974
                                this->json_append(jfe, vd, str);
8,407✔
2975
                            }
2976

2977
                            if (nl_pos == std::string::npos
8,640✔
2978
                                || opts.full_message)
3✔
2979
                            {
2980
                                lr.lr_end = this->jlf_cached_line.size();
8,637✔
2981
                            } else {
2982
                                lr.lr_end = lr.lr_start + nl_pos;
3✔
2983
                            }
2984

2985
                            if (lv_iter->lv_meta.lvm_name
8,640✔
2986
                                == this->lf_timestamp_field)
8,640✔
2987
                            {
2988
                                this->jlf_line_attrs.emplace_back(
1,003✔
2989
                                    lr, L_TIMESTAMP.value());
2,006✔
2990
                            } else if (lv_iter->lv_meta.lvm_name
7,637✔
2991
                                       == this->elf_body_field)
7,637✔
2992
                            {
2993
                                this->jlf_line_attrs.emplace_back(
1,135✔
2994
                                    lr, SA_BODY.value());
2,270✔
2995
                            } else if (lv_iter->lv_meta.lvm_name
6,502✔
2996
                                       == this->elf_src_file_field)
6,502✔
2997
                            {
2998
                                this->jlf_line_attrs.emplace_back(
8✔
2999
                                    lr, SA_SRC_FILE.value());
16✔
3000
                            } else if (lv_iter->lv_meta.lvm_name
6,494✔
3001
                                       == this->elf_src_line_field)
6,494✔
3002
                            {
3003
                                this->jlf_line_attrs.emplace_back(
8✔
3004
                                    lr, SA_SRC_LINE.value());
16✔
3005
                            } else if (lv_iter->lv_meta.lvm_name
6,486✔
3006
                                       == this->elf_thread_id_field)
6,486✔
3007
                            {
3008
                                this->jlf_line_attrs.emplace_back(
60✔
3009
                                    lr, SA_THREAD_ID.value());
120✔
3010
                            } else if (lv_iter->lv_meta.lvm_name
6,426✔
3011
                                       == this->elf_duration_field)
6,426✔
3012
                            {
UNCOV
3013
                                this->jlf_line_attrs.emplace_back(
×
UNCOV
3014
                                    lr, SA_DURATION.value());
×
3015
                            } else if (lv_iter->lv_meta.lvm_name
6,426✔
3016
                                       == this->elf_level_field)
6,426✔
3017
                            {
3018
                                this->jlf_line_attrs.emplace_back(
1,077✔
3019
                                    lr, L_LEVEL.value());
2,154✔
3020
                            } else if (lv_iter->lv_meta.lvm_name
10,698✔
3021
                                           == this->elf_opid_field
5,349✔
3022
                                       && !lr.empty())
5,349✔
3023
                            {
3024
                                this->jlf_line_attrs.emplace_back(
967✔
3025
                                    lr, L_OPID.value());
1,934✔
3026
                            }
3027
                            lv_iter->lv_origin = lr;
8,640✔
3028
                            lv_iter->lv_sub_offset = sub_offset;
8,640✔
3029
                            used_values[std::distance(
8,640✔
3030
                                this->jlf_line_values.lvv_values.begin(),
3031
                                lv_iter)]
3032
                                = true;
8,640✔
3033

3034
                            if (!jfe.jfe_suffix.empty()) {
8,640✔
3035
                                this->json_append_to_cache(jfe.jfe_suffix);
1,322✔
3036
                            }
3037
                        } else if (jfe.jfe_value.pp_value == ts_field) {
24,210✔
3038
                            struct line_range lr;
1,479✔
3039
                            ssize_t ts_len;
3040
                            char ts[64];
3041
                            struct exttm et;
1,479✔
3042

3043
                            ll.to_exttm(et);
1,479✔
3044
                            et.et_nsec += jlu.jlu_exttm.et_nsec % 1000;
1,479✔
3045
                            et.et_gmtoff = jlu.jlu_exttm.et_gmtoff;
1,479✔
3046
                            et.et_flags |= jlu.jlu_exttm.et_flags;
1,479✔
3047
                            if (!jfe.jfe_prefix.empty()) {
1,479✔
3048
                                this->json_append_to_cache(jfe.jfe_prefix);
3✔
3049
                            }
3050
                            if (jfe.jfe_ts_format.empty()) {
1,479✔
3051
                                ts_len = this->lf_date_time.ftime(
1,320✔
3052
                                    ts,
3053
                                    sizeof(ts),
3054
                                    this->get_timestamp_formats(),
3055
                                    et);
3056
                            } else {
3057
                                ts_len = ftime_fmt(ts,
159✔
3058
                                                   sizeof(ts),
3059
                                                   jfe.jfe_ts_format.c_str(),
3060
                                                   et);
3061
                            }
3062
                            lr.lr_start = this->jlf_cached_line.size();
1,479✔
3063
                            this->json_append_to_cache(ts, ts_len);
1,479✔
3064
                            lr.lr_end = this->jlf_cached_line.size();
1,479✔
3065
                            this->jlf_line_attrs.emplace_back(
1,479✔
3066
                                lr, L_TIMESTAMP.value());
2,958✔
3067

3068
                            lv_iter = find_if(
1,479✔
3069
                                this->jlf_line_values.lvv_values.begin(),
3070
                                this->jlf_line_values.lvv_values.end(),
3071
                                logline_value_name_cmp(
3072
                                    &this->lf_timestamp_field));
1,479✔
3073
                            if (lv_iter
1,479✔
3074
                                != this->jlf_line_values.lvv_values.end())
1,479✔
3075
                            {
3076
                                used_values[distance(
1,479✔
3077
                                    this->jlf_line_values.lvv_values.begin(),
3078
                                    lv_iter)]
3079
                                    = true;
1,479✔
3080
                            }
3081
                            if (!jfe.jfe_suffix.empty()) {
1,479✔
3082
                                this->json_append_to_cache(jfe.jfe_suffix);
3✔
3083
                            }
3084
                        } else if (jfe.jfe_value.pp_value == level_field
14,091✔
3085
                                   || jfe.jfe_value.pp_value
28,088✔
3086
                                       == this->elf_level_field)
13,997✔
3087
                        {
3088
                            auto level_name = ll.get_level_name();
94✔
3089
                            lr.lr_start = this->jlf_cached_line.size();
94✔
3090
                            this->json_append(jfe, nullptr, level_name);
94✔
3091
                            if (jfe.jfe_auto_width) {
94✔
3092
                                this->json_append_to_cache(
60✔
3093
                                    MAX_LEVEL_NAME_LEN - level_name.length());
60✔
3094
                            }
3095
                            lr.lr_end = this->jlf_cached_line.size();
94✔
3096
                            this->jlf_line_attrs.emplace_back(lr,
94✔
3097
                                                              L_LEVEL.value());
188✔
3098
                        } else if (!jfe.jfe_default_value.empty()) {
13,997✔
3099
                            if (!jfe.jfe_prefix.empty()) {
124✔
UNCOV
3100
                                this->json_append_to_cache(jfe.jfe_prefix);
×
3101
                            }
3102
                            this->json_append(
124✔
3103
                                jfe, nullptr, jfe.jfe_default_value);
124✔
3104
                            if (!jfe.jfe_suffix.empty()) {
124✔
UNCOV
3105
                                this->json_append_to_cache(jfe.jfe_suffix);
×
3106
                            }
3107
                        }
3108

3109
                        switch (jfe.jfe_text_transform) {
24,210✔
3110
                            case external_log_format::json_format_element::
24,116✔
3111
                                transform_t::NONE:
3112
                                break;
24,116✔
3113
                            case external_log_format::json_format_element::
94✔
3114
                                transform_t::UPPERCASE:
3115
                                for (size_t cindex = begin_size;
94✔
3116
                                     cindex < this->jlf_cached_line.size();
713✔
3117
                                     cindex++)
3118
                                {
3119
                                    this->jlf_cached_line[cindex] = toupper(
619✔
3120
                                        this->jlf_cached_line[cindex]);
619✔
3121
                                }
3122
                                break;
94✔
UNCOV
3123
                            case external_log_format::json_format_element::
×
3124
                                transform_t::LOWERCASE:
UNCOV
3125
                                for (size_t cindex = begin_size;
×
UNCOV
3126
                                     cindex < this->jlf_cached_line.size();
×
3127
                                     cindex++)
3128
                                {
UNCOV
3129
                                    this->jlf_cached_line[cindex] = tolower(
×
UNCOV
3130
                                        this->jlf_cached_line[cindex]);
×
3131
                                }
UNCOV
3132
                                break;
×
UNCOV
3133
                            case external_log_format::json_format_element::
×
3134
                                transform_t::CAPITALIZE:
UNCOV
3135
                                for (size_t cindex = begin_size;
×
UNCOV
3136
                                     cindex < begin_size + 1;
×
3137
                                     cindex++)
3138
                                {
3139
                                    this->jlf_cached_line[cindex] = toupper(
×
3140
                                        this->jlf_cached_line[cindex]);
×
3141
                                }
UNCOV
3142
                                for (size_t cindex = begin_size + 1;
×
3143
                                     cindex < this->jlf_cached_line.size();
×
3144
                                     cindex++)
3145
                                {
3146
                                    this->jlf_cached_line[cindex] = tolower(
×
3147
                                        this->jlf_cached_line[cindex]);
×
3148
                                }
3149
                                break;
×
3150
                        }
3151
                        break;
24,210✔
3152
                }
3153
            }
3154
            this->json_append_to_cache("\n", 1);
2,337✔
3155
            sub_offset += 1;
2,337✔
3156

3157
            for (size_t lpc = 0; lpc < this->jlf_line_values.lvv_values.size();
20,790✔
3158
                 lpc++)
3159
            {
3160
                static const intern_string_t body_name
3161
                    = intern_string::lookup("body", -1);
18,453✔
3162
                auto& lv = this->jlf_line_values.lvv_values[lpc];
18,453✔
3163

3164
                if (lv.lv_meta.is_hidden() || used_values[lpc]
28,289✔
3165
                    || body_name == lv.lv_meta.lvm_name)
28,289✔
3166
                {
3167
                    continue;
17,373✔
3168
                }
3169

3170
                auto str = lv.to_string();
1,080✔
3171
                while (endswith(str, "\n")) {
1,080✔
UNCOV
3172
                    str.pop_back();
×
3173
                }
3174

3175
                lv.lv_sub_offset = sub_offset;
1,080✔
3176
                lv.lv_origin.lr_start = this->jlf_cached_line.size() + 2
1,080✔
3177
                    + lv.lv_meta.lvm_name.size() + 2;
1,080✔
3178
                auto frag = string_fragment::from_str(str);
1,080✔
3179
                while (true) {
3180
                    auto utf_scan_res = is_utf8(frag, '\n');
1,086✔
3181

3182
                    this->json_append_to_cache("  ", 2);
1,086✔
3183
                    this->json_append_to_cache(
1,086✔
3184
                        lv.lv_meta.lvm_name.to_string_fragment());
1,086✔
3185
                    this->json_append_to_cache(": ", 2);
1,086✔
3186
                    lr.lr_start = this->jlf_cached_line.size();
1,086✔
3187
                    this->json_append_to_cache(utf_scan_res.usr_valid_frag);
1,086✔
3188
                    lr.lr_end = this->jlf_cached_line.size();
1,086✔
3189
                    if (lv.lv_meta.lvm_name == this->elf_body_field) {
1,086✔
UNCOV
3190
                        this->jlf_line_attrs.emplace_back(lr, SA_BODY.value());
×
3191
                    } else {
3192
                        this->jlf_line_attrs.emplace_back(
1,086✔
3193
                            lr, SA_EXTRA_CONTENT.value());
2,172✔
3194
                    }
3195
                    this->json_append_to_cache("\n", 1);
1,086✔
3196
                    sub_offset += 1;
1,086✔
3197
                    if (utf_scan_res.usr_remaining) {
1,086✔
3198
                        frag = utf_scan_res.usr_remaining.value();
6✔
3199
                    } else {
3200
                        break;
1,080✔
3201
                    }
3202
                }
6✔
3203
                lv.lv_origin.lr_end = this->jlf_cached_line.size() - 1;
1,080✔
3204
                if (lv.lv_meta.lvm_name == this->elf_opid_field
1,080✔
3205
                    && !lv.lv_origin.empty())
1,080✔
3206
                {
UNCOV
3207
                    this->jlf_line_attrs.emplace_back(lv.lv_origin,
×
UNCOV
3208
                                                      L_OPID.value());
×
3209
                }
3210
            }
1,080✔
3211
        }
2,337✔
3212

3213
        this->jlf_line_offsets.push_back(0);
2,351✔
3214
        for (size_t lpc = 0; lpc < this->jlf_cached_line.size(); lpc++) {
264,389✔
3215
            if (this->jlf_cached_line[lpc] == '\n') {
262,038✔
3216
                this->jlf_line_offsets.push_back(lpc + 1);
4,217✔
3217
            }
3218
        }
3219
        this->jlf_line_offsets.push_back(this->jlf_cached_line.size());
2,351✔
3220
        this->jlf_cached_offset = ll.get_offset();
2,351✔
3221
        this->jlf_cached_opts = opts;
2,351✔
3222
    }
2,423✔
3223

3224
    off_t this_off = 0, next_off = 0;
4,457✔
3225

3226
    if (!this->jlf_line_offsets.empty()
4,457✔
3227
        && ll.get_sub_offset() < this->jlf_line_offsets.size())
4,457✔
3228
    {
3229
        require(ll.get_sub_offset() < this->jlf_line_offsets.size());
4,457✔
3230

3231
        this_off = this->jlf_line_offsets[ll.get_sub_offset()];
4,457✔
3232
        if ((ll.get_sub_offset() + 1) < (int) this->jlf_line_offsets.size()) {
4,457✔
3233
            next_off = this->jlf_line_offsets[ll.get_sub_offset() + 1];
4,457✔
3234
        } else {
UNCOV
3235
            next_off = this->jlf_cached_line.size();
×
3236
        }
3237
        if (next_off > 0 && this->jlf_cached_line[next_off - 1] == '\n'
4,457✔
3238
            && this_off != next_off)
8,914✔
3239
        {
3240
            next_off -= 1;
4,457✔
3241
        }
3242
    }
3243

3244
    if (opts.full_message) {
4,457✔
3245
        sbr.share(this->jlf_share_manager,
644✔
3246
                  &this->jlf_cached_line[0],
322✔
3247
                  this->jlf_cached_line.size());
3248
    } else {
3249
        sbr.share(this->jlf_share_manager,
8,270✔
3250
                  this->jlf_cached_line.data() + this_off,
4,135✔
3251
                  next_off - this_off);
4,135✔
3252
    }
3253
    sbr.get_metadata().m_valid_utf = ll.is_valid_utf();
4,457✔
3254
    sbr.get_metadata().m_has_ansi = ll.has_ansi();
4,457✔
3255
    this->jlf_cached_sub_range.lr_start = this_off;
4,457✔
3256
    this->jlf_cached_sub_range.lr_end = next_off;
4,457✔
3257
    this->jlf_line_values.lvv_sbr = sbr.clone();
4,457✔
3258
}
3259

3260
struct compiled_header_expr {
3261
    auto_mem<sqlite3_stmt> che_stmt{sqlite3_finalize};
3262
    bool che_enabled{true};
3263
};
3264

3265
struct format_header_expressions : public lnav_config_listener {
3266
    format_header_expressions() : lnav_config_listener(__FILE__) {}
1,148✔
3267

3268
    auto_sqlite3 e_db;
3269
    std::map<intern_string_t, std::map<std::string, compiled_header_expr>>
3270
        e_header_exprs;
3271
};
3272

3273
using safe_format_header_expressions = safe::Safe<format_header_expressions>;
3274

3275
static safe_format_header_expressions format_header_exprs;
3276

3277
std::optional<external_file_format>
3278
detect_mime_type(const std::filesystem::path& filename)
596✔
3279
{
3280
    uint8_t buffer[1024];
3281
    size_t buffer_size = 0;
596✔
3282

3283
    {
3284
        auto_fd fd;
596✔
3285

3286
        if ((fd = lnav::filesystem::openp(filename, O_RDONLY)) == -1) {
596✔
UNCOV
3287
            return std::nullopt;
×
3288
        }
3289

3290
        ssize_t rc;
3291

3292
        if ((rc = read(fd, buffer, sizeof(buffer))) == -1) {
596✔
UNCOV
3293
            return std::nullopt;
×
3294
        }
3295
        buffer_size = rc;
596✔
3296
    }
596✔
3297

3298
    auto hexbuf = auto_buffer::alloc(buffer_size * 2);
596✔
3299

3300
    for (size_t lpc = 0; lpc < buffer_size; lpc++) {
314,239✔
3301
        fmt::format_to(
313,643✔
3302
            std::back_inserter(hexbuf), FMT_STRING("{:02x}"), buffer[lpc]);
1,254,572✔
3303
    }
3304

3305
    safe::WriteAccess<safe_format_header_expressions> in(format_header_exprs);
596✔
3306

3307
    for (const auto& format : log_format::get_root_formats()) {
44,014✔
3308
        auto elf = std::dynamic_pointer_cast<external_log_format>(format);
43,418✔
3309
        if (elf == nullptr) {
43,418✔
3310
            continue;
2,980✔
3311
        }
3312

3313
        if (elf->elf_converter.c_header.h_exprs.he_exprs.empty()) {
40,438✔
3314
            continue;
39,842✔
3315
        }
3316

3317
        if (buffer_size < elf->elf_converter.c_header.h_size) {
596✔
3318
            log_debug(
21✔
3319
                "%s: file content too small (%d) for header detection: %s",
3320
                filename.c_str(),
3321
                buffer_size,
3322
                elf->get_name().get());
3323
            continue;
21✔
3324
        }
3325
        for (const auto& hpair : elf->elf_converter.c_header.h_exprs.he_exprs) {
2,875✔
3326
            auto& he = in->e_header_exprs[elf->get_name()][hpair.first];
2,300✔
3327

3328
            if (!he.che_enabled) {
2,300✔
UNCOV
3329
                continue;
×
3330
            }
3331

3332
            auto* stmt = he.che_stmt.in();
2,300✔
3333

3334
            if (stmt == nullptr) {
2,300✔
UNCOV
3335
                continue;
×
3336
            }
3337
            sqlite3_reset(stmt);
2,300✔
3338
            auto count = sqlite3_bind_parameter_count(stmt);
2,300✔
3339
            for (int lpc = 0; lpc < count; lpc++) {
4,600✔
3340
                const auto* name = sqlite3_bind_parameter_name(stmt, lpc + 1);
2,300✔
3341

3342
                if (name[0] == '$') {
2,300✔
3343
                    const char* env_value;
3344

UNCOV
3345
                    if ((env_value = getenv(&name[1])) != nullptr) {
×
UNCOV
3346
                        sqlite3_bind_text(
×
3347
                            stmt, lpc + 1, env_value, -1, SQLITE_STATIC);
3348
                    }
3349
                    continue;
×
3350
                }
3351
                if (strcmp(name, ":header") == 0) {
2,300✔
3352
                    sqlite3_bind_text(stmt,
2,300✔
3353
                                      lpc + 1,
3354
                                      hexbuf.in(),
2,300✔
3355
                                      hexbuf.size(),
2,300✔
3356
                                      SQLITE_STATIC);
3357
                    continue;
2,300✔
3358
                }
3359
                if (strcmp(name, ":filepath") == 0) {
×
3360
                    sqlite3_bind_text(
×
3361
                        stmt, lpc + 1, filename.c_str(), -1, SQLITE_STATIC);
UNCOV
3362
                    continue;
×
3363
                }
3364
            }
3365

3366
            auto step_res = sqlite3_step(stmt);
2,300✔
3367

3368
            switch (step_res) {
2,300✔
3369
                case SQLITE_OK:
2,300✔
3370
                case SQLITE_DONE:
3371
                    continue;
2,300✔
UNCOV
3372
                case SQLITE_ROW:
×
3373
                    break;
×
3374
                default: {
×
UNCOV
3375
                    log_error(
×
3376
                        "failed to execute file-format header expression: "
3377
                        "%s:%s -- %s",
3378
                        elf->get_name().get(),
3379
                        hpair.first.c_str(),
3380
                        sqlite3_errmsg(in->e_db));
UNCOV
3381
                    he.che_enabled = false;
×
UNCOV
3382
                    continue;
×
3383
                }
3384
            }
3385

3386
            log_info("detected format for: %s -- %s (header-expr: %s)",
×
3387
                     filename.c_str(),
3388
                     elf->get_name().get(),
3389
                     hpair.first.c_str());
UNCOV
3390
            return external_file_format{
×
UNCOV
3391
                elf->get_name().to_string(),
×
UNCOV
3392
                elf->elf_converter.c_command.pp_value,
×
UNCOV
3393
                elf->elf_converter.c_command.pp_location.sl_source.to_string(),
×
3394
            };
3395
        }
3396
    }
43,418✔
3397

3398
    return std::nullopt;
596✔
3399
}
596✔
3400

3401
log_format::scan_result_t
UNCOV
3402
log_format::test_line(sample_t& sample,
×
3403
                      std::vector<lnav::console::user_message>& msgs)
3404
{
3405
    return scan_no_match{};
×
3406
}
3407

3408
log_format::scan_result_t
3409
external_log_format::test_line(sample_t& sample,
179,142✔
3410
                               std::vector<lnav::console::user_message>& msgs)
3411
{
3412
    auto lines
3413
        = string_fragment::from_str(sample.s_line.pp_value).split_lines();
179,142✔
3414

3415
    if (this->elf_type == elf_type_t::ELF_TYPE_JSON) {
179,142✔
3416
        auto alloc = ArenaAlloc::Alloc<char>{};
2,186✔
3417
        auto sbc = scan_batch_context{
2,186✔
3418
            alloc,
3419
        };
2,186✔
3420
        std::vector<logline> dst;
2,186✔
3421
        auto li = line_info{
2,186✔
3422
            {0, lines[0].length()},
2,186✔
3423
        };
2,186✔
3424
        shared_buffer sb;
2,186✔
3425
        shared_buffer_ref sbr;
2,186✔
3426
        sbr.share(sb, lines[0].data(), (size_t) lines[0].length());
2,186✔
3427

3428
        return this->scan_json(dst, li, sbr, sbc);
2,186✔
3429
    }
2,186✔
3430

3431
    scan_result_t retval = scan_no_match{"no patterns matched"};
176,956✔
3432
    auto found = false;
176,956✔
3433

3434
    for (auto pat_iter = this->elf_pattern_order.begin();
176,956✔
3435
         pat_iter != this->elf_pattern_order.end();
1,344,204✔
3436
         ++pat_iter)
1,167,248✔
3437
    {
3438
        auto& pat = *(*pat_iter);
1,167,248✔
3439

3440
        if (!pat.p_pcre.pp_value) {
1,167,248✔
3441
            continue;
991,750✔
3442
        }
3443

3444
        auto md = pat.p_pcre.pp_value->create_match_data();
1,167,248✔
3445
        auto match_res = pat.p_pcre.pp_value->capture_from(lines[0])
1,167,248✔
3446
                             .into(md)
1,167,248✔
3447
                             .matches(PCRE2_NO_UTF_CHECK)
2,334,496✔
3448
                             .ignore_error();
1,167,248✔
3449
        if (!match_res) {
1,167,248✔
3450
            continue;
990,294✔
3451
        }
3452
        retval = scan_match{1000};
176,954✔
3453
        found = true;
176,954✔
3454

3455
        if (pat.p_module_format) {
176,954✔
3456
            continue;
1,456✔
3457
        }
3458

3459
        sample.s_matched_regexes.insert(pat.p_name.to_string());
175,498✔
3460

3461
        const auto ts_cap = md[pat.p_timestamp_field_index];
175,498✔
3462
        const auto level_cap = md[pat.p_level_field_index];
175,498✔
3463
        const char* const* custom_formats = this->get_timestamp_formats();
175,498✔
3464
        date_time_scanner dts;
175,498✔
3465
        timeval tv;
3466
        exttm tm;
175,498✔
3467

3468
        if (ts_cap && ts_cap->sf_begin == 0) {
175,498✔
3469
            pat.p_timestamp_end = ts_cap->sf_end;
106,677✔
3470
        }
3471
        const char* dts_scan_res = nullptr;
175,498✔
3472

3473
        if (ts_cap) {
175,498✔
3474
            dts_scan_res = dts.scan(
175,496✔
3475
                ts_cap->data(), ts_cap->length(), custom_formats, &tm, tv);
175,496✔
3476
        }
3477
        if (dts_scan_res != nullptr) {
175,498✔
3478
            if (dts_scan_res != ts_cap->data() + ts_cap->length()) {
175,495✔
UNCOV
3479
                auto match_len = dts_scan_res - ts_cap->data();
×
UNCOV
3480
                auto notes = attr_line_t("the used timestamp format: ");
×
UNCOV
3481
                if (custom_formats == nullptr) {
×
UNCOV
3482
                    notes.append(PTIMEC_FORMATS[dts.dts_fmt_lock].pf_fmt);
×
3483
                } else {
UNCOV
3484
                    notes.append(custom_formats[dts.dts_fmt_lock]);
×
3485
                }
UNCOV
3486
                notes.append("\n  ")
×
UNCOV
3487
                    .append(ts_cap.value())
×
UNCOV
3488
                    .append("\n")
×
UNCOV
3489
                    .append(2 + match_len, ' ')
×
UNCOV
3490
                    .append("^ matched up to here"_snippet_border);
×
UNCOV
3491
                auto um = lnav::console::user_message::warning(
×
UNCOV
3492
                              attr_line_t("timestamp was not fully matched: ")
×
3493
                                  .append_quoted(ts_cap.value()))
×
3494
                              .with_snippet(sample.s_line.to_snippet())
×
3495
                              .with_note(notes)
×
3496
                              .move();
×
3497

3498
                msgs.emplace_back(um);
×
3499
            }
3500
        } else if (!ts_cap) {
3✔
3501
            msgs.emplace_back(
2✔
3502
                lnav::console::user_message::error(
×
3503
                    attr_line_t("invalid sample log message: ")
4✔
3504
                        .append(lnav::to_json(sample.s_line.pp_value)))
4✔
3505
                    .with_reason(attr_line_t("timestamp was not captured"))
4✔
3506
                    .with_snippet(sample.s_line.to_snippet())
4✔
3507
                    .with_help(attr_line_t(
4✔
3508
                        "A timestamp needs to be captured in order for a "
3509
                        "line to be recognized as a log message")));
3510
        } else {
3511
            attr_line_t notes;
1✔
3512

3513
            if (custom_formats == nullptr) {
1✔
UNCOV
3514
                notes.append("the following built-in formats were tried:");
×
UNCOV
3515
                for (int lpc = 0; PTIMEC_FORMATS[lpc].pf_fmt != nullptr; lpc++)
×
3516
                {
UNCOV
3517
                    off_t off = 0;
×
3518

UNCOV
3519
                    PTIMEC_FORMATS[lpc].pf_func(
×
UNCOV
3520
                        &tm, ts_cap->data(), off, ts_cap->length());
×
UNCOV
3521
                    notes.append("\n  ")
×
UNCOV
3522
                        .append(ts_cap.value())
×
UNCOV
3523
                        .append("\n")
×
UNCOV
3524
                        .append(2 + off, ' ')
×
UNCOV
3525
                        .append("^ "_snippet_border)
×
UNCOV
3526
                        .append_quoted(
×
UNCOV
3527
                            lnav::roles::symbol(PTIMEC_FORMATS[lpc].pf_fmt))
×
3528
                        .append(" matched up to here"_snippet_border);
×
3529
                }
3530
            } else {
3531
                notes.append("the following custom formats were tried:");
1✔
3532
                for (int lpc = 0; custom_formats[lpc] != nullptr; lpc++) {
2✔
3533
                    off_t off = 0;
1✔
3534

3535
                    ptime_fmt(custom_formats[lpc],
1✔
3536
                              &tm,
3537
                              ts_cap->data(),
3538
                              off,
3539
                              ts_cap->length());
1✔
3540
                    notes.append("\n  ")
1✔
3541
                        .append(ts_cap.value())
1✔
3542
                        .append("\n")
1✔
3543
                        .append(2 + off, ' ')
1✔
3544
                        .append("^ "_snippet_border)
1✔
3545
                        .append_quoted(lnav::roles::symbol(custom_formats[lpc]))
2✔
3546
                        .append(" matched up to here"_snippet_border);
1✔
3547
                }
3548
            }
3549

3550
            msgs.emplace_back(
1✔
UNCOV
3551
                lnav::console::user_message::error(
×
3552
                    attr_line_t("invalid sample log message: ")
1✔
3553
                        .append(lnav::to_json(sample.s_line.pp_value)))
2✔
3554
                    .with_reason(attr_line_t("unrecognized timestamp -- ")
2✔
3555
                                     .append(ts_cap.value()))
1✔
3556
                    .with_snippet(sample.s_line.to_snippet())
2✔
3557
                    .with_note(notes)
1✔
3558
                    .with_help(attr_line_t("If the timestamp format is not "
2✔
3559
                                           "supported by default, you can "
3560
                                           "add a custom format with the ")
3561
                                   .append_quoted("timestamp-format"_symbol)
1✔
3562
                                   .append(" property")));
1✔
3563
        }
1✔
3564

3565
        auto level = this->convert_level(
175,498✔
3566
            level_cap.value_or(string_fragment::invalid()), nullptr);
175,498✔
3567

3568
        if (sample.s_level != LEVEL_UNKNOWN && sample.s_level != level) {
175,498✔
3569
            attr_line_t note_al;
1✔
3570

3571
            note_al.append("matched regex = ")
1✔
3572
                .append(lnav::roles::symbol(pat.p_name.to_string()))
2✔
3573
                .append("\n")
1✔
3574
                .append("captured level = ")
1✔
3575
                .append_quoted(level_cap->to_string());
1✔
3576
            if (level_cap && !this->elf_level_patterns.empty()) {
1✔
3577
                thread_local auto md = lnav::pcre2pp::match_data::unitialized();
1✔
3578

3579
                note_al.append("\nlevel regular expression match results:");
1✔
3580
                for (const auto& level_pattern : this->elf_level_patterns) {
3✔
3581
                    attr_line_t regex_al
3582
                        = level_pattern.second.lp_pcre.pp_value->get_pattern();
2✔
3583
                    lnav::snippets::regex_highlighter(
2✔
3584
                        regex_al, -1, line_range{0, (int) regex_al.length()});
2✔
3585
                    note_al.append("\n  ")
2✔
3586
                        .append(lnav::roles::symbol(
4✔
3587
                            level_pattern.second.lp_pcre.pp_path.to_string()))
4✔
3588
                        .append(" = ")
2✔
3589
                        .append(regex_al)
2✔
3590
                        .append("\n    ");
2✔
3591
                    auto match_res = level_pattern.second.lp_pcre.pp_value
2✔
3592
                                         ->capture_from(level_cap.value())
2✔
3593
                                         .into(md)
2✔
3594
                                         .matches(PCRE2_NO_UTF_CHECK)
4✔
3595
                                         .ignore_error();
2✔
3596
                    if (!match_res) {
2✔
3597
                        note_al.append(lnav::roles::warning("no match"));
1✔
3598
                        continue;
1✔
3599
                    }
3600

3601
                    note_al.append(level_cap.value())
1✔
3602
                        .append("\n    ")
1✔
3603
                        .append(md.leading().length(), ' ')
1✔
3604
                        .append("^"_snippet_border);
1✔
3605
                    if (match_res->f_all.length() > 2) {
1✔
3606
                        note_al.append(lnav::roles::snippet_border(
1✔
3607
                            std::string(match_res->f_all.length() - 2, '-')));
3✔
3608
                    }
3609
                    if (match_res->f_all.length() > 1) {
1✔
3610
                        note_al.append("^"_snippet_border);
1✔
3611
                    }
3612
                }
2✔
3613
            }
3614
            auto um
UNCOV
3615
                = lnav::console::user_message::error(
×
3616
                      attr_line_t("invalid sample log message: ")
1✔
3617
                          .append(lnav::to_json(sample.s_line.pp_value)))
2✔
3618
                      .with_reason(attr_line_t()
2✔
3619
                                       .append_quoted(lnav::roles::symbol(
2✔
3620
                                           level_names[level]))
1✔
3621
                                       .append(" does not match the expected "
1✔
3622
                                               "level of ")
3623
                                       .append_quoted(lnav::roles::symbol(
2✔
3624
                                           level_names[sample.s_level])))
1✔
3625
                      .with_snippet(sample.s_line.to_snippet())
2✔
3626
                      .with_note(note_al)
1✔
3627
                      .move();
1✔
3628
            if (!this->elf_level_patterns.empty()) {
1✔
3629
                um.with_help(
1✔
3630
                    attr_line_t("Level regexes are not anchored to the "
2✔
3631
                                "start/end of the string.  Prepend ")
3632
                        .append_quoted("^"_symbol)
1✔
3633
                        .append(" to the expression to match from the "
1✔
3634
                                "start of the string and append ")
3635
                        .append_quoted("$"_symbol)
1✔
3636
                        .append(" to match up to the end of the string."));
1✔
3637
            }
3638
            msgs.emplace_back(um);
1✔
3639
        }
1✔
3640

3641
        {
3642
            auto full_match_res
3643
                = pat.p_pcre.pp_value->capture_from(sample.s_line.pp_value)
175,498✔
3644
                      .into(md)
175,498✔
3645
                      .matches()
350,996✔
3646
                      .ignore_error();
175,498✔
3647
            if (!full_match_res) {
175,498✔
3648
                attr_line_t regex_al = pat.p_pcre.pp_value->get_pattern();
1✔
3649
                lnav::snippets::regex_highlighter(
1✔
3650
                    regex_al, -1, line_range{0, (int) regex_al.length()});
1✔
3651
                msgs.emplace_back(
1✔
UNCOV
3652
                    lnav::console::user_message::error(
×
3653
                        attr_line_t("invalid pattern: ")
1✔
3654
                            .append_quoted(
1✔
3655
                                lnav::roles::symbol(pat.p_name.to_string())))
2✔
3656
                        .with_reason("pattern does not match entire "
2✔
3657
                                     "multiline sample message")
3658
                        .with_snippet(sample.s_line.to_snippet())
2✔
3659
                        .with_note(attr_line_t()
2✔
3660
                                       .append(lnav::roles::symbol(
1✔
3661
                                           pat.p_name.to_string()))
2✔
3662
                                       .append(" = ")
1✔
3663
                                       .append(regex_al))
1✔
3664
                        .with_help(
3665
                            attr_line_t("use ").append_quoted(".*").append(
2✔
3666
                                " to match new-lines")));
3667
            } else if (static_cast<size_t>(full_match_res->f_all.length())
175,498✔
3668
                       != sample.s_line.pp_value.length())
175,497✔
3669
            {
3670
                attr_line_t regex_al = pat.p_pcre.pp_value->get_pattern();
1✔
3671
                lnav::snippets::regex_highlighter(
1✔
3672
                    regex_al, -1, line_range{0, (int) regex_al.length()});
1✔
3673
                auto match_length
3674
                    = static_cast<size_t>(full_match_res->f_all.length());
1✔
3675
                attr_line_t sample_al = sample.s_line.pp_value;
1✔
3676
                sample_al.append("\n")
1✔
3677
                    .append(match_length, ' ')
1✔
3678
                    .append("^ matched up to here"_error)
1✔
3679
                    .with_attr_for_all(VC_ROLE.value(role_t::VCR_QUOTED_CODE));
1✔
3680
                auto sample_snippet = lnav::console::snippet::from(
3681
                    sample.s_line.pp_location, sample_al);
1✔
3682
                msgs.emplace_back(
1✔
UNCOV
3683
                    lnav::console::user_message::error(
×
3684
                        attr_line_t("invalid pattern: ")
1✔
3685
                            .append_quoted(
1✔
3686
                                lnav::roles::symbol(pat.p_name.to_string())))
2✔
3687
                        .with_reason("pattern does not match entire "
2✔
3688
                                     "message")
3689
                        .with_snippet(sample_snippet)
1✔
3690
                        .with_note(attr_line_t()
3✔
3691
                                       .append(lnav::roles::symbol(
2✔
3692
                                           pat.p_name.to_string()))
2✔
3693
                                       .append(" = ")
1✔
3694
                                       .append(regex_al))
1✔
3695
                        .with_help("update the regular expression to fully "
3696
                                   "capture the sample message"));
3697
            }
1✔
3698
        }
3699
    }
1,167,248✔
3700

3701
    if (!found && !this->elf_pattern_order.empty()) {
176,956✔
3702
        std::vector<std::pair<ssize_t, intern_string_t>> partial_indexes;
2✔
3703
        attr_line_t notes;
2✔
3704
        size_t max_name_width = 0;
2✔
3705

3706
        for (const auto& pat_iter : this->elf_pattern_order) {
11✔
3707
            auto& pat = *pat_iter;
9✔
3708

3709
            if (!pat.p_pcre.pp_value) {
9✔
UNCOV
3710
                continue;
×
3711
            }
3712

3713
            partial_indexes.emplace_back(
9✔
3714
                pat.p_pcre.pp_value->match_partial(lines[0]), pat.p_name);
9✔
3715
            max_name_width = std::max(max_name_width, pat.p_name.size());
9✔
3716
        }
3717
        for (const auto& line_frag : lines) {
4✔
3718
            auto src_line = attr_line_t(line_frag.to_string());
2✔
3719
            if (!line_frag.endswith("\n")) {
2✔
3720
                src_line.append("\n");
2✔
3721
            }
3722
            src_line.with_attr_for_all(VC_ROLE.value(role_t::VCR_QUOTED_CODE));
2✔
3723
            notes.append("   ").append(src_line);
2✔
3724
            for (auto& part_pair : partial_indexes) {
11✔
3725
                if (part_pair.first >= 0
18✔
3726
                    && part_pair.first < line_frag.length())
9✔
3727
                {
3728
                    notes.append("   ")
9✔
3729
                        .append(part_pair.first, ' ')
9✔
3730
                        .append("^ "_snippet_border)
9✔
3731
                        .append(
9✔
3732
                            lnav::roles::symbol(part_pair.second.to_string()))
18✔
3733
                        .append(" matched up to here"_snippet_border)
9✔
3734
                        .append("\n");
9✔
3735
                }
3736
                part_pair.first -= line_frag.length();
9✔
3737
            }
3738
        }
2✔
3739
        notes.add_header(
2✔
3740
            "the following shows how each pattern matched this sample:\n");
3741

3742
        attr_line_t regex_note;
2✔
3743
        for (const auto& pat_iter : this->elf_pattern_order) {
11✔
3744
            if (!pat_iter->p_pcre.pp_value) {
9✔
3745
                regex_note
UNCOV
3746
                    .append(lnav::roles::symbol(fmt::format(
×
UNCOV
3747
                        FMT_STRING("{:{}}"), pat_iter->p_name, max_name_width)))
×
UNCOV
3748
                    .append(" is invalid");
×
UNCOV
3749
                continue;
×
3750
            }
3751

3752
            attr_line_t regex_al = pat_iter->p_pcre.pp_value->get_pattern();
9✔
3753
            lnav::snippets::regex_highlighter(
9✔
3754
                regex_al, -1, line_range{0, (int) regex_al.length()});
9✔
3755

3756
            regex_note
3757
                .append(lnav::roles::symbol(fmt::format(
18✔
3758
                    FMT_STRING("{:{}}"), pat_iter->p_name, max_name_width)))
27✔
3759
                .append(" = ")
9✔
3760
                .append_quoted(regex_al)
18✔
3761
                .append("\n");
9✔
3762
        }
9✔
3763

3764
        msgs.emplace_back(
2✔
UNCOV
3765
            lnav::console::user_message::error(
×
3766
                attr_line_t("invalid sample log message: ")
2✔
3767
                    .append(lnav::to_json(sample.s_line.pp_value)))
4✔
3768
                .with_reason("sample does not match any patterns")
4✔
3769
                .with_snippet(sample.s_line.to_snippet())
4✔
3770
                .with_note(notes.rtrim())
4✔
3771
                .with_note(regex_note));
3772
    }
2✔
3773

3774
    return retval;
176,956✔
3775
}
179,142✔
3776

3777
void
3778
external_log_format::build(std::vector<lnav::console::user_message>& errors)
49,842✔
3779
{
3780
    auto& vc = view_colors::singleton();
49,842✔
3781

3782
    if (!this->lf_timestamp_field.empty()) {
49,842✔
3783
        auto& vd = this->elf_value_defs[this->lf_timestamp_field];
49,842✔
3784
        if (vd.get() == nullptr) {
49,842✔
3785
            vd = std::make_shared<value_def>(
35,914✔
3786
                this->lf_timestamp_field,
35,914✔
UNCOV
3787
                value_kind_t::VALUE_TEXT,
×
UNCOV
3788
                logline_value_meta::internal_column{},
×
3789
                this);
35,914✔
3790
            if (this->elf_type == elf_type_t::ELF_TYPE_JSON) {
35,914✔
3791
                this->elf_value_def_order.emplace_back(vd);
4,850✔
3792
            }
3793
        }
3794
        vd->vd_meta.lvm_name = this->lf_timestamp_field;
49,842✔
3795
        vd->vd_meta.lvm_kind = value_kind_t::VALUE_TEXT;
49,842✔
3796
        vd->vd_meta.lvm_column = logline_value_meta::internal_column{};
49,842✔
3797
        vd->vd_internal = true;
49,842✔
3798

3799
        this->elf_value_defs[LOG_TIME_STR] = vd;
49,842✔
3800
    }
3801

3802
    if (!this->lf_subsecond_field.empty()) {
49,842✔
3803
        if (!this->lf_subsecond_unit.has_value()) {
97✔
3804
            errors.emplace_back(
1✔
UNCOV
3805
                lnav::console::user_message::error(
×
3806
                    attr_line_t()
2✔
3807
                        .append_quoted(
1✔
3808
                            lnav::roles::symbol(this->elf_name.to_string()))
2✔
3809
                        .append(" is not a valid log format"))
1✔
3810
                    .with_reason(attr_line_t()
2✔
3811
                                     .append_quoted("subsecond-units"_symbol)
1✔
3812
                                     .append(" must be set when ")
1✔
3813
                                     .append_quoted("subsecond-field"_symbol)
1✔
3814
                                     .append(" is used"))
1✔
3815
                    .with_snippets(this->get_snippets()));
2✔
3816
        } else {
3817
            auto& vd = this->elf_value_defs[this->lf_subsecond_field];
96✔
3818
            if (vd.get() == nullptr) {
96✔
3819
                vd = std::make_shared<value_def>(
96✔
3820
                    this->lf_subsecond_field,
96✔
UNCOV
3821
                    value_kind_t::VALUE_INTEGER,
×
UNCOV
3822
                    logline_value_meta::internal_column{},
×
3823
                    this);
96✔
3824
                if (this->elf_type == elf_type_t::ELF_TYPE_JSON) {
96✔
3825
                    this->elf_value_def_order.emplace_back(vd);
96✔
3826
                }
3827
            }
3828
            vd->vd_meta.lvm_name = this->lf_subsecond_field;
96✔
3829
            vd->vd_meta.lvm_column = logline_value_meta::internal_column{};
96✔
3830
            vd->vd_meta.lvm_hidden = true;
96✔
3831
            vd->vd_internal = true;
96✔
3832
        }
3833
    }
3834

3835
    if (startswith(this->elf_level_field.get(), "/")) {
49,842✔
3836
        this->elf_level_field
3837
            = intern_string::lookup(this->elf_level_field.get() + 1);
192✔
3838
    }
3839
    if (!this->elf_level_field.empty()) {
49,842✔
3840
        auto level_iter = this->elf_value_defs.find(this->elf_level_field);
49,842✔
3841
        if (level_iter == this->elf_value_defs.end()) {
49,842✔
3842
            auto& vd = this->elf_value_defs[this->elf_level_field];
24,897✔
3843
            vd = std::make_shared<value_def>(
24,897✔
3844
                this->elf_level_field,
24,897✔
UNCOV
3845
                value_kind_t::VALUE_TEXT,
×
UNCOV
3846
                logline_value_meta::internal_column{},
×
3847
                this);
24,897✔
3848
            if (this->elf_type == elf_type_t::ELF_TYPE_JSON) {
24,897✔
3849
                this->elf_value_def_order.emplace_back(vd);
2,570✔
3850
            }
3851
            vd->vd_meta.lvm_name = this->elf_level_field;
24,897✔
3852
            vd->vd_meta.lvm_column = logline_value_meta::internal_column{};
24,897✔
3853
            vd->vd_internal = true;
24,897✔
3854

3855
            if (this->elf_level_field != this->elf_body_field) {
24,897✔
3856
                this->elf_value_defs[LOG_LEVEL_STR] = vd;
24,073✔
3857
            }
3858
        } else {
3859
            if (level_iter->second->vd_meta.lvm_kind
24,945✔
3860
                != value_kind_t::VALUE_TEXT)
24,945✔
3861
            {
3862
                this->lf_level_hideable = false;
5,192✔
3863
            }
3864
            this->elf_value_defs[LOG_LEVEL_STR] = level_iter->second;
24,945✔
3865
        }
3866
    }
3867

3868
    auto opid_field_iter = this->elf_value_defs.find(LOG_OPID_STR);
49,842✔
3869
    if (opid_field_iter == this->elf_value_defs.end()) {
49,842✔
3870
        auto vd
3871
            = std::make_shared<value_def>(this->elf_opid_field,
49,842✔
UNCOV
3872
                                          value_kind_t::VALUE_TEXT,
×
UNCOV
3873
                                          logline_value_meta::internal_column{},
×
3874
                                          this);
49,842✔
3875
        if (this->elf_type == elf_type_t::ELF_TYPE_JSON) {
49,842✔
3876
            this->elf_value_def_order.emplace_back(vd);
8,586✔
3877
        }
3878
        vd->vd_meta.lvm_name = LOG_OPID_STR;
49,842✔
3879
        vd->vd_meta.lvm_kind = value_kind_t::VALUE_TEXT;
49,842✔
3880
        vd->vd_meta.lvm_column = logline_value_meta::internal_column{};
49,842✔
3881
        vd->vd_internal = true;
49,842✔
3882

3883
        this->elf_value_defs[LOG_OPID_STR] = vd;
49,842✔
3884
    }
49,842✔
3885

3886
    if (!this->elf_body_field.empty()) {
49,842✔
3887
        auto& vd = this->elf_value_defs[this->elf_body_field];
49,842✔
3888
        if (vd.get() == nullptr) {
49,842✔
3889
            vd = std::make_shared<value_def>(
40,913✔
3890
                this->elf_body_field,
40,913✔
UNCOV
3891
                value_kind_t::VALUE_TEXT,
×
UNCOV
3892
                logline_value_meta::internal_column{},
×
3893
                this);
40,913✔
3894
            if (this->elf_type == elf_type_t::ELF_TYPE_JSON) {
40,913✔
3895
                this->elf_value_def_order.emplace_back(vd);
5,577✔
3896
            }
3897
        }
3898
        vd->vd_meta.lvm_name = this->elf_body_field;
49,842✔
3899
        vd->vd_meta.lvm_kind = value_kind_t::VALUE_TEXT;
49,842✔
3900
        vd->vd_meta.lvm_column = logline_value_meta::internal_column{};
49,842✔
3901
        vd->vd_internal = true;
49,842✔
3902
    }
3903

3904
    if (!this->elf_src_file_field.empty()) {
49,842✔
3905
        auto& vd = this->elf_value_defs[this->elf_src_file_field];
7,281✔
3906
        if (vd.get() == nullptr) {
7,281✔
3907
            vd = std::make_shared<value_def>(
1✔
3908
                this->elf_src_file_field,
1✔
UNCOV
3909
                value_kind_t::VALUE_TEXT,
×
UNCOV
3910
                logline_value_meta::internal_column{},
×
3911
                this);
2✔
3912
        }
3913
        vd->vd_meta.lvm_name = this->elf_src_file_field;
7,281✔
3914
        vd->vd_meta.lvm_kind = value_kind_t::VALUE_TEXT;
7,281✔
3915
        vd->vd_meta.lvm_column = logline_value_meta::internal_column{};
7,281✔
3916
    }
3917

3918
    if (!this->elf_src_line_field.empty()) {
49,842✔
3919
        auto& vd = this->elf_value_defs[this->elf_src_line_field];
7,281✔
3920
        if (vd.get() == nullptr) {
7,281✔
3921
            vd = std::make_shared<value_def>(
1✔
3922
                this->elf_src_line_field,
1✔
3923
                value_kind_t::VALUE_INTEGER,
×
3924
                logline_value_meta::internal_column{},
×
3925
                this);
2✔
3926
        }
3927
        vd->vd_meta.lvm_name = this->elf_src_line_field;
7,281✔
3928
        vd->vd_meta.lvm_kind = value_kind_t::VALUE_INTEGER;
7,281✔
3929
        vd->vd_meta.lvm_column = logline_value_meta::internal_column{};
7,281✔
3930
    }
3931

3932
    if (!this->elf_thread_id_field.empty()) {
49,842✔
3933
        auto& vd = this->elf_value_defs[this->elf_thread_id_field];
16,017✔
3934
        if (vd.get() == nullptr) {
16,017✔
3935
            vd = std::make_shared<value_def>(
1✔
3936
                this->elf_thread_id_field,
1✔
3937
                value_kind_t::VALUE_TEXT,
×
3938
                logline_value_meta::internal_column{},
×
3939
                this);
2✔
3940
        }
3941
        vd->vd_meta.lvm_name = this->elf_thread_id_field;
16,017✔
3942
        vd->vd_meta.lvm_kind = value_kind_t::VALUE_TEXT;
16,017✔
3943
        vd->vd_meta.lvm_column = logline_value_meta::internal_column{};
16,017✔
3944
    }
3945

3946
    if (!this->elf_duration_field.empty()) {
49,842✔
3947
        auto& vd = this->elf_value_defs[this->elf_duration_field];
2,184✔
3948
        if (vd.get() == nullptr) {
2,184✔
UNCOV
3949
            vd = std::make_shared<value_def>(
×
UNCOV
3950
                this->elf_duration_field,
×
3951
                value_kind_t::VALUE_FLOAT,
×
3952
                logline_value_meta::internal_column{},
×
UNCOV
3953
                this);
×
3954
        }
3955
        vd->vd_meta.lvm_name = this->elf_duration_field;
2,184✔
3956
        vd->vd_meta.lvm_kind = value_kind_t::VALUE_FLOAT;
2,184✔
3957
        vd->vd_meta.lvm_column = logline_value_meta::internal_column{};
2,184✔
3958
    }
3959

3960
    for (auto& od_pair : *this->lf_opid_description_def) {
70,956✔
3961
        od_pair.second.od_index = this->lf_opid_description_def_vec->size();
21,114✔
3962
        this->lf_opid_description_def_vec->emplace_back(&od_pair.second);
21,114✔
3963
    }
3964

3965
    for (auto& od_pair : *this->lf_subid_description_def) {
50,570✔
3966
        od_pair.second.od_index = this->lf_subid_description_def_vec->size();
728✔
3967
        this->lf_subid_description_def_vec->emplace_back(&od_pair.second);
728✔
3968
    }
3969

3970
    if (!this->lf_timestamp_format.empty()) {
49,842✔
3971
        this->lf_timestamp_format.push_back(nullptr);
5,196✔
3972
    }
3973
    auto src_file_found = 0;
49,842✔
3974
    auto src_line_found = 0;
49,842✔
3975
    auto thread_id_found = 0;
49,842✔
3976
    auto duration_found = 0;
49,842✔
3977
    for (auto& elf_pattern : this->elf_patterns) {
145,798✔
3978
        auto& pat = *elf_pattern.second;
95,956✔
3979

3980
        if (pat.p_pcre.pp_value == nullptr) {
95,956✔
3981
            continue;
1✔
3982
        }
3983

3984
        if (pat.p_module_format) {
95,955✔
3985
            this->elf_has_module_format = true;
2,184✔
3986
        }
3987

3988
        if (pat.p_opid_field_index == -1
191,910✔
3989
            && this->lf_opid_description_def->size() == 1)
95,955✔
3990
        {
3991
            const auto& opid_def
3992
                = this->lf_opid_description_def->begin()->second;
28,392✔
3993
            for (const auto& desc : *opid_def.od_descriptors) {
63,336✔
3994
                for (auto named_cap : pat.p_pcre.pp_value->get_named_captures())
380,744✔
3995
                {
3996
                    const intern_string_t name
3997
                        = intern_string::lookup(named_cap.get_name());
345,800✔
3998

3999
                    if (name == desc.od_field.pp_value) {
345,800✔
4000
                        pat.p_opid_description_field_indexes.emplace_back(
62,608✔
4001
                            named_cap.get_index());
31,304✔
4002
                    }
4003
                }
4004
            }
4005
        }
4006

4007
        for (auto named_cap : pat.p_pcre.pp_value->get_named_captures()) {
825,898✔
4008
            const intern_string_t name
4009
                = intern_string::lookup(named_cap.get_name());
729,943✔
4010

4011
            if (name == this->lf_timestamp_field) {
729,943✔
4012
                pat.p_timestamp_field_index = named_cap.get_index();
93,769✔
4013
            }
4014
            if (name == this->lf_time_field) {
729,943✔
4015
                pat.p_time_field_index = named_cap.get_index();
728✔
4016
            }
4017
            if (name == this->elf_level_field) {
729,943✔
4018
                pat.p_level_field_index = named_cap.get_index();
73,186✔
4019
            }
4020
            if (name == this->elf_module_id_field) {
729,943✔
4021
                pat.p_module_field_index = named_cap.get_index();
5,825✔
4022
            }
4023
            if (name == this->elf_opid_field) {
729,943✔
4024
                pat.p_opid_field_index = named_cap.get_index();
19,656✔
4025
            }
4026
            if (name == this->elf_subid_field) {
729,943✔
4027
                pat.p_subid_field_index = named_cap.get_index();
10,920✔
4028
            }
4029
            if (name == this->elf_body_field) {
729,943✔
4030
                pat.p_body_field_index = named_cap.get_index();
82,121✔
4031
            }
4032
            if (name == this->elf_src_file_field) {
729,943✔
4033
                pat.p_src_file_field_index = named_cap.get_index();
11,648✔
4034
                src_file_found += 1;
11,648✔
4035
            }
4036
            if (name == this->elf_src_line_field) {
729,943✔
4037
                pat.p_src_line_field_index = named_cap.get_index();
13,104✔
4038
                src_line_found += 1;
13,104✔
4039
            }
4040
            if (name == this->elf_thread_id_field) {
729,943✔
4041
                pat.p_thread_id_field_index = named_cap.get_index();
41,496✔
4042
                thread_id_found += 1;
41,496✔
4043
            }
4044
            if (name == this->elf_duration_field) {
729,943✔
4045
                pat.p_duration_field_index = named_cap.get_index();
4,368✔
4046
                duration_found += 1;
4,368✔
4047
            }
4048

4049
            auto value_iter = this->elf_value_defs.find(name);
729,943✔
4050
            if (value_iter != this->elf_value_defs.end()) {
729,943✔
4051
                auto vd = value_iter->second;
718,925✔
4052
                indexed_value_def ivd;
718,925✔
4053

4054
                ivd.ivd_index = named_cap.get_index();
718,925✔
4055
                if (!vd->vd_unit_field.empty()) {
718,925✔
4056
                    ivd.ivd_unit_field_index = pat.p_pcre.pp_value->name_index(
1,456✔
4057
                        vd->vd_unit_field.get());
728✔
4058
                } else {
4059
                    ivd.ivd_unit_field_index = -1;
718,197✔
4060
                }
4061
                if (!vd->vd_internal
718,925✔
4062
                    && !vd->vd_meta.lvm_column
1,237,647✔
4063
                            .is<logline_value_meta::table_column>())
518,722✔
4064
                {
4065
                    vd->vd_meta.lvm_column = logline_value_meta::table_column{
285,034✔
4066
                        this->elf_column_count++};
285,034✔
4067
                }
4068
                ivd.ivd_value_def = vd;
718,925✔
4069
                pat.p_value_by_index.push_back(ivd);
718,925✔
4070
            }
718,925✔
4071
            pat.p_value_name_to_index[name] = named_cap.get_index();
729,943✔
4072
        }
4073

4074
        stable_sort(pat.p_value_by_index.begin(), pat.p_value_by_index.end());
95,955✔
4075

4076
        for (int lpc = 0; lpc < (int) pat.p_value_by_index.size(); lpc++) {
814,880✔
4077
            auto& ivd = pat.p_value_by_index[lpc];
718,925✔
4078
            auto vd = ivd.ivd_value_def;
718,925✔
4079

4080
            if (!vd->vd_meta.lvm_foreign_key && !vd->vd_meta.lvm_identifier) {
718,925✔
4081
                switch (vd->vd_meta.lvm_kind) {
364,196✔
4082
                    case value_kind_t::VALUE_INTEGER:
50,328✔
4083
                    case value_kind_t::VALUE_FLOAT:
4084
                        pat.p_numeric_value_indexes.push_back(lpc);
50,328✔
4085
                        break;
50,328✔
4086
                    default:
313,868✔
4087
                        break;
313,868✔
4088
                }
4089
            }
4090
        }
718,925✔
4091

4092
        if (!pat.p_module_format && pat.p_timestamp_field_index == -1) {
95,955✔
4093
            errors.emplace_back(
2✔
UNCOV
4094
                lnav::console::user_message::error(
×
4095
                    attr_line_t("invalid pattern: ")
4✔
4096
                        .append_quoted(lnav::roles::symbol(pat.p_config_path)))
4✔
4097
                    .with_reason("no timestamp capture found in the pattern")
4✔
4098
                    .with_snippets(this->get_snippets())
4✔
4099
                    .with_help("all log messages need a timestamp"));
4100
        }
4101

4102
        if (!this->elf_level_field.empty() && pat.p_level_field_index == -1) {
95,955✔
4103
            log_warning("%s:level field '%s' not found in pattern",
22,769✔
4104
                        pat.p_config_path.c_str(),
4105
                        this->elf_level_field.get());
4106
        }
4107
        if (!this->elf_module_id_field.empty()
95,955✔
4108
            && pat.p_module_field_index == -1)
95,955✔
4109
        {
UNCOV
4110
            log_warning("%s:module field '%s' not found in pattern",
×
4111
                        pat.p_config_path.c_str(),
4112
                        this->elf_module_id_field.get());
4113
        }
4114
        if (!this->elf_body_field.empty() && pat.p_body_field_index == -1) {
95,955✔
4115
            log_warning("%s:body field '%s' not found in pattern",
13,834✔
4116
                        pat.p_config_path.c_str(),
4117
                        this->elf_body_field.get());
4118
        }
4119

4120
        this->elf_pattern_order.push_back(elf_pattern.second);
95,955✔
4121
    }
4122
    if (this->elf_type == elf_type_t::ELF_TYPE_TEXT
99,684✔
4123
        && !this->elf_src_file_field.empty() && src_file_found == 0)
49,842✔
4124
    {
4125
        errors.emplace_back(
1✔
UNCOV
4126
            lnav::console::user_message::error(
×
4127
                attr_line_t("invalid pattern: ")
2✔
4128
                    .append_quoted(
1✔
4129
                        lnav::roles::symbol(this->elf_name.to_string())))
2✔
4130
                .with_reason("no source file capture found in the pattern")
2✔
4131
                .with_snippets(this->get_snippets())
2✔
4132
                .with_help(attr_line_t("at least one pattern needs a source "
2✔
4133
                                       "file capture named ")
4134
                               .append_quoted(this->elf_src_file_field.get())));
1✔
4135
    }
4136
    if (this->elf_type == elf_type_t::ELF_TYPE_TEXT
99,684✔
4137
        && !this->elf_src_line_field.empty() && src_line_found == 0)
49,842✔
4138
    {
4139
        errors.emplace_back(
1✔
4140
            lnav::console::user_message::error(
×
4141
                attr_line_t("invalid pattern: ")
2✔
4142
                    .append_quoted(
1✔
4143
                        lnav::roles::symbol(this->elf_name.to_string())))
2✔
4144
                .with_reason("no source line capture found in the pattern")
2✔
4145
                .with_snippets(this->get_snippets())
2✔
4146
                .with_help(attr_line_t("at least one pattern needs a source "
2✔
4147
                                       "line capture named ")
4148
                               .append_quoted(this->elf_src_line_field.get())));
1✔
4149
    }
4150
    if (this->elf_type == elf_type_t::ELF_TYPE_TEXT
99,684✔
4151
        && !this->elf_thread_id_field.empty() && thread_id_found == 0)
49,842✔
4152
    {
4153
        errors.emplace_back(
1✔
4154
            lnav::console::user_message::error(
×
4155
                attr_line_t("invalid pattern: ")
2✔
4156
                    .append_quoted(
1✔
4157
                        lnav::roles::symbol(this->elf_name.to_string())))
2✔
4158
                .with_reason("no thread ID capture found in the pattern")
2✔
4159
                .with_snippets(this->get_snippets())
2✔
4160
                .with_help(
4161
                    attr_line_t(
2✔
4162
                        "at least one pattern needs a thread ID capture named ")
4163
                        .append_quoted(this->elf_thread_id_field.get())));
1✔
4164
    }
4165
    if (this->elf_type == elf_type_t::ELF_TYPE_TEXT
99,684✔
4166
        && !this->elf_duration_field.empty() && duration_found == 0)
49,842✔
4167
    {
4168
        errors.emplace_back(
×
UNCOV
4169
            lnav::console::user_message::error(
×
UNCOV
4170
                attr_line_t("invalid pattern: ")
×
UNCOV
4171
                    .append_quoted(
×
UNCOV
4172
                        lnav::roles::symbol(this->elf_name.to_string())))
×
UNCOV
4173
                .with_reason("no duration capture found in the pattern")
×
UNCOV
4174
                .with_snippets(this->get_snippets())
×
4175
                .with_help(
UNCOV
4176
                    attr_line_t(
×
4177
                        "at least one pattern needs a duration capture named ")
UNCOV
4178
                        .append_quoted(this->elf_duration_field.get())));
×
4179
    }
4180

4181
    if (this->elf_type != elf_type_t::ELF_TYPE_TEXT) {
49,842✔
4182
        if (!this->elf_patterns.empty()) {
8,586✔
4183
            errors.emplace_back(
1✔
4184
                lnav::console::user_message::error(
×
4185
                    attr_line_t()
2✔
4186
                        .append_quoted(
1✔
4187
                            lnav::roles::symbol(this->elf_name.to_string()))
2✔
4188
                        .append(" is not a valid log format"))
1✔
4189
                    .with_reason("structured logs cannot have regexes")
2✔
4190
                    .with_snippets(this->get_snippets()));
2✔
4191
        }
4192
        if (this->elf_type == elf_type_t::ELF_TYPE_JSON) {
8,586✔
4193
            this->lf_multiline = true;
8,586✔
4194
            this->lf_structured = true;
8,586✔
4195
            this->lf_formatted_lines = true;
8,586✔
4196
            this->jlf_parse_context
4197
                = std::make_shared<yajlpp_parse_context>(this->elf_name);
8,586✔
4198
            this->jlf_yajl_handle.reset(
8,586✔
4199
                yajl_alloc(&this->jlf_parse_context->ypc_callbacks,
8,586✔
4200
                           nullptr,
4201
                           this->jlf_parse_context.get()),
8,586✔
4202
                yajl_handle_deleter());
4203
            yajl_config(
8,586✔
4204
                this->jlf_yajl_handle.get(), yajl_dont_validate_strings, 1);
4205
        }
4206
    } else {
4207
        if (this->elf_patterns.empty()) {
41,256✔
4208
            errors.emplace_back(lnav::console::user_message::error(
2✔
4209
                                    attr_line_t()
4✔
4210
                                        .append_quoted(lnav::roles::symbol(
4✔
4211
                                            this->elf_name.to_string()))
4✔
4212
                                        .append(" is not a valid log format"))
2✔
4213
                                    .with_reason("no regexes specified")
4✔
4214
                                    .with_snippets(this->get_snippets()));
4✔
4215
        }
4216
    }
4217

4218
    stable_sort(this->elf_level_pairs.begin(), this->elf_level_pairs.end());
49,842✔
4219

4220
    {
4221
        safe::WriteAccess<safe_format_header_expressions> hexprs(
4222
            format_header_exprs);
49,842✔
4223

4224
        if (hexprs->e_db.in() == nullptr) {
49,842✔
4225
            if (sqlite3_open(":memory:", hexprs->e_db.out()) != SQLITE_OK) {
728✔
UNCOV
4226
                log_error("unable to open memory DB");
×
UNCOV
4227
                return;
×
4228
            }
4229
            register_sqlite_funcs(hexprs->e_db.in(), sqlite_registration_funcs);
728✔
4230
        }
4231

4232
        for (const auto& hpair : this->elf_converter.c_header.h_exprs.he_exprs)
52,755✔
4233
        {
4234
            auto stmt_str
4235
                = fmt::format(FMT_STRING("SELECT 1 WHERE {}"), hpair.second);
8,739✔
4236
            compiled_header_expr che;
2,913✔
4237

4238
            log_info("preparing file-format header expression: %s",
2,913✔
4239
                     stmt_str.c_str());
4240
            auto retcode = sqlite3_prepare_v2(hexprs->e_db.in(),
5,826✔
4241
                                              stmt_str.c_str(),
4242
                                              stmt_str.size(),
2,913✔
4243
                                              che.che_stmt.out(),
4244
                                              nullptr);
4245
            if (retcode != SQLITE_OK) {
2,913✔
4246
                auto sql_al = attr_line_t(hpair.second)
2✔
4247
                                  .with_attr_for_all(SA_PREFORMATTED.value())
2✔
4248
                                  .with_attr_for_all(
1✔
4249
                                      VC_ROLE.value(role_t::VCR_QUOTED_CODE))
2✔
4250
                                  .move();
1✔
4251
                readline_sql_highlighter(
1✔
4252
                    sql_al, lnav::sql::dialect::sqlite, std::nullopt);
4253
                intern_string_t watch_expr_path = intern_string::lookup(
4254
                    fmt::format(FMT_STRING("/{}/converter/header/expr/{}"),
3✔
4255
                                this->elf_name,
1✔
4256
                                hpair.first));
2✔
4257
                auto snippet = lnav::console::snippet::from(
4258
                    source_location(watch_expr_path), sql_al);
1✔
4259

4260
                auto um = lnav::console::user_message::error(
2✔
4261
                              "SQL expression is invalid")
4262
                              .with_reason(sqlite3_errmsg(hexprs->e_db.in()))
2✔
4263
                              .with_snippet(snippet)
1✔
4264
                              .move();
1✔
4265

4266
                errors.emplace_back(um);
1✔
4267
                continue;
1✔
4268
            }
1✔
4269

4270
            hexprs->e_header_exprs[this->elf_name][hpair.first]
2,912✔
4271
                = std::move(che);
5,824✔
4272
        }
2,914✔
4273

4274
        if (!this->elf_converter.c_header.h_exprs.he_exprs.empty()
49,842✔
4275
            && this->elf_converter.c_command.pp_value.empty())
49,842✔
4276
        {
4277
            auto um = lnav::console::user_message::error(
2✔
4278
                          "A command is required when a converter is defined")
4279
                          .with_help(
2✔
4280
                              "The converter command transforms the file "
4281
                              "into a format that can be consumed by lnav")
4282
                          .with_snippets(this->get_snippets())
2✔
4283
                          .move();
1✔
4284
            errors.emplace_back(um);
1✔
4285
        }
1✔
4286
    }
49,842✔
4287

4288
    for (auto& vd : this->elf_value_def_order) {
487,815✔
4289
        std::vector<std::string>::iterator act_iter;
437,973✔
4290

4291
        if (!vd->vd_internal
437,973✔
4292
            && log_vtab_impl::RESERVED_COLUMNS.count(
832,234✔
4293
                vd->vd_meta.lvm_name.to_string_fragment()))
832,234✔
4294
        {
UNCOV
4295
            auto um = lnav::console::user_message::error(
×
4296
                          attr_line_t("value name ")
1✔
4297
                              .append_quoted(lnav::roles::symbol(
2✔
4298
                                  fmt::format(FMT_STRING("/{}/value/{}"),
4✔
4299
                                              this->elf_name,
1✔
4300
                                              vd->vd_meta.lvm_name)))
1✔
4301
                              .append(" is reserved and cannot be used"))
1✔
4302
                          .with_reason(
2✔
4303
                              "lnav automatically defines several columns in "
4304
                              "the log virtual table")
4305
                          .with_snippets(this->get_snippets())
2✔
4306
                          .with_help("Choose another name")
2✔
4307
                          .move();
1✔
4308
            errors.emplace_back(um);
1✔
4309
        }
1✔
4310

4311
        vd->vd_meta.lvm_format = this;
437,973✔
4312
        if (!vd->vd_internal
437,973✔
4313
            && !vd->vd_meta.lvm_column.is<logline_value_meta::table_column>())
437,973✔
4314
        {
4315
            vd->vd_meta.lvm_column
109,227✔
4316
                = logline_value_meta::table_column{this->elf_column_count++};
109,227✔
4317
        }
4318

4319
        if (vd->vd_meta.lvm_kind == value_kind_t::VALUE_UNKNOWN) {
437,973✔
UNCOV
4320
            vd->vd_meta.lvm_kind = value_kind_t::VALUE_TEXT;
×
4321
        }
4322

4323
        if (this->elf_type == elf_type_t::ELF_TYPE_TEXT) {
437,973✔
4324
            std::set<std::string> available_captures;
300,324✔
4325

4326
            bool found_in_pattern = false;
300,324✔
4327
            for (const auto& pat : this->elf_patterns) {
509,264✔
4328
                if (pat.second->p_pcre.pp_value == nullptr) {
509,262✔
UNCOV
4329
                    continue;
×
4330
                }
4331

4332
                auto cap_index = pat.second->p_pcre.pp_value->name_index(
1,018,524✔
4333
                    vd->vd_meta.lvm_name.get());
509,262✔
4334
                if (cap_index >= 0) {
509,262✔
4335
                    found_in_pattern = true;
300,322✔
4336
                    break;
300,322✔
4337
                }
4338

4339
                for (auto named_cap :
208,940✔
4340
                     pat.second->p_pcre.pp_value->get_named_captures())
1,907,378✔
4341
                {
4342
                    available_captures.insert(named_cap.get_name().to_string());
1,489,498✔
4343
                }
4344
            }
4345
            if (!found_in_pattern) {
300,324✔
4346
                auto notes
4347
                    = attr_line_t("the following captures are available:\n  ")
2✔
4348
                          .join(available_captures,
2✔
4349
                                VC_ROLE.value(role_t::VCR_SYMBOL),
4✔
4350
                                ", ")
4351
                          .move();
2✔
4352
                errors.emplace_back(
2✔
UNCOV
4353
                    lnav::console::user_message::warning(
×
4354
                        attr_line_t("invalid value ")
2✔
4355
                            .append_quoted(lnav::roles::symbol(
4✔
4356
                                fmt::format(FMT_STRING("/{}/value/{}"),
8✔
4357
                                            this->elf_name,
2✔
4358
                                            vd->vd_meta.lvm_name.get()))))
4✔
4359
                        .with_reason(
4✔
4360
                            attr_line_t("no patterns have a capture named ")
4✔
4361
                                .append_quoted(vd->vd_meta.lvm_name.get()))
2✔
4362
                        .with_note(notes)
2✔
4363
                        .with_snippets(this->get_snippets())
4✔
4364
                        .with_help("values are populated from captures in "
4365
                                   "patterns, so at least one pattern must "
4366
                                   "have a capture with this value name"));
4367
            }
2✔
4368
        }
300,324✔
4369

4370
        for (act_iter = vd->vd_action_list.begin();
437,973✔
4371
             act_iter != vd->vd_action_list.end();
438,701✔
4372
             ++act_iter)
728✔
4373
        {
4374
            if (this->lf_action_defs.find(*act_iter)
728✔
4375
                == this->lf_action_defs.end())
1,456✔
4376
            {
4377
#if 0
4378
                errors.push_back("error:" + this->elf_name.to_string() + ":"
4379
                                 + vd->vd_meta.lvm_name.get()
4380
                                 + ": cannot find action -- " + (*act_iter));
4381
#endif
4382
            }
4383
        }
4384

4385
        vd->set_rewrite_src_name();
437,973✔
4386
    }
4387

4388
    if (this->elf_type == elf_type_t::ELF_TYPE_JSON) {
49,842✔
4389
        for (const auto& vd : this->elf_value_def_order) {
146,235✔
4390
            this->elf_value_def_frag_map[vd->vd_meta.lvm_name
137,649✔
4391
                                             .to_string_fragment()]
137,649✔
4392
                = vd.get();
275,298✔
4393
        }
4394
    }
4395

4396
    for (const auto& td_pair : this->lf_tag_defs) {
52,125✔
4397
        const auto& td = td_pair.second;
2,283✔
4398

4399
        if (td->ftd_pattern.pp_value == nullptr
2,283✔
4400
            || td->ftd_pattern.pp_value->get_pattern().empty())
2,283✔
4401
        {
4402
            errors.emplace_back(
3✔
UNCOV
4403
                lnav::console::user_message::error(
×
4404
                    attr_line_t("invalid tag definition ")
6✔
4405
                        .append_quoted(lnav::roles::symbol(
6✔
4406
                            fmt::format(FMT_STRING("/{}/tags/{}"),
12✔
4407
                                        this->elf_name,
3✔
4408
                                        td_pair.first))))
3✔
4409
                    .with_reason(
6✔
4410
                        "tag definitions must have a non-empty pattern")
4411
                    .with_snippets(this->get_snippets()));
6✔
4412
        }
4413
    }
4414

4415
    if (this->elf_opid_field.empty()
49,842✔
4416
        && this->lf_opid_description_def->size() > 1)
49,842✔
4417
    {
4418
        errors.emplace_back(
1✔
UNCOV
4419
            lnav::console::user_message::error(
×
4420
                attr_line_t("too many opid descriptions")
2✔
4421
                    .append_quoted(lnav::roles::symbol(fmt::format(
2✔
4422
                        FMT_STRING("/{}/opid/description"), this->elf_name))))
4✔
4423
                .with_reason(attr_line_t("when no ")
2✔
4424
                                 .append("opid-field"_symbol)
1✔
4425
                                 .append(" is specified, only a single "
1✔
4426
                                         "description is supported"))
4427
                .with_snippets(this->get_snippets()));
2✔
4428
    }
4429

4430
    for (const auto& opid_desc_pair : *this->lf_opid_description_def) {
70,956✔
4431
        for (const auto& opid_desc : *opid_desc_pair.second.od_descriptors) {
50,236✔
4432
            auto iter = this->elf_value_defs.find(opid_desc.od_field.pp_value);
29,122✔
4433
            if (iter == this->elf_value_defs.end()) {
29,122✔
4434
                errors.emplace_back(
2✔
UNCOV
4435
                    lnav::console::user_message::error(
×
4436
                        attr_line_t("invalid opid description field ")
4✔
4437
                            .append_quoted(lnav::roles::symbol(
4✔
4438
                                opid_desc.od_field.pp_path.to_string())))
4✔
4439
                        .with_reason(
4✔
4440
                            attr_line_t("unknown value name ")
4✔
4441
                                .append_quoted(opid_desc.od_field.pp_value))
2✔
4442
                        .with_snippets(this->get_snippets()));
4✔
4443
            } else {
4444
                this->lf_desc_fields.insert(iter->first);
29,120✔
4445
                iter->second->vd_is_desc_field = true;
29,120✔
4446
            }
4447
        }
4448
    }
4449

4450
    for (const auto& subid_desc_pair : *this->lf_subid_description_def) {
50,570✔
4451
        for (const auto& subid_desc : *subid_desc_pair.second.od_descriptors) {
1,456✔
4452
            auto iter = this->elf_value_defs.find(subid_desc.od_field.pp_value);
728✔
4453
            if (iter == this->elf_value_defs.end()) {
728✔
UNCOV
4454
                errors.emplace_back(
×
UNCOV
4455
                    lnav::console::user_message::error(
×
UNCOV
4456
                        attr_line_t("invalid subid description field ")
×
UNCOV
4457
                            .append_quoted(lnav::roles::symbol(
×
UNCOV
4458
                                subid_desc.od_field.pp_path.to_string())))
×
UNCOV
4459
                        .with_reason(
×
UNCOV
4460
                            attr_line_t("unknown value name ")
×
UNCOV
4461
                                .append_quoted(subid_desc.od_field.pp_value))
×
UNCOV
4462
                        .with_snippets(this->get_snippets()));
×
4463
            } else {
4464
                this->lf_desc_fields.insert(iter->first);
728✔
4465
                iter->second->vd_is_desc_field = true;
728✔
4466
            }
4467
        }
4468
    }
4469

4470
    if (this->elf_type == elf_type_t::ELF_TYPE_TEXT
99,684✔
4471
        && this->elf_samples.empty())
49,842✔
4472
    {
4473
        errors.emplace_back(
3✔
4474
            lnav::console::user_message::error(
×
4475
                attr_line_t()
6✔
4476
                    .append_quoted(
3✔
4477
                        lnav::roles::symbol(this->elf_name.to_string()))
6✔
4478
                    .append(" is not a valid log format"))
3✔
4479
                .with_reason("log message samples must be included in a format "
6✔
4480
                             "definition")
4481
                .with_snippets(this->get_snippets()));
6✔
4482
    }
4483

4484
    for (const auto& pat : this->elf_pattern_order) {
145,797✔
4485
        if (pat->p_module_format || this->elf_type != elf_type_t::ELF_TYPE_TEXT)
95,955✔
4486
        {
4487
            continue;
2,185✔
4488
        }
4489
        if (pat->p_pcre.pp_value->name_index(this->lf_timestamp_field.get())
93,770✔
4490
            < 0)
93,770✔
4491
        {
4492
            attr_line_t notes;
1✔
4493
            bool first_note = true;
1✔
4494

4495
            if (pat->p_pcre.pp_value->get_capture_count() > 0) {
1✔
4496
                notes.append("the following captures are available:\n  ");
1✔
4497
            }
4498
            for (auto named_cap : pat->p_pcre.pp_value->get_named_captures()) {
4✔
4499
                if (!first_note) {
3✔
4500
                    notes.append(", ");
2✔
4501
                }
4502
                notes.append(
3✔
4503
                    lnav::roles::symbol(named_cap.get_name().to_string()));
6✔
4504
                first_note = false;
3✔
4505
            }
4506
            errors.emplace_back(
1✔
UNCOV
4507
                lnav::console::user_message::error(
×
4508
                    attr_line_t("invalid value for property ")
1✔
4509
                        .append_quoted(lnav::roles::symbol(
2✔
4510
                            fmt::format(FMT_STRING("/{}/timestamp-field"),
4✔
4511
                                        this->elf_name))))
1✔
4512
                    .with_reason(
2✔
4513
                        attr_line_t()
2✔
4514
                            .append_quoted(this->lf_timestamp_field)
1✔
4515
                            .append(" was not found in the pattern at ")
1✔
4516
                            .append(lnav::roles::symbol(pat->p_config_path)))
2✔
4517
                    .with_note(notes)
1✔
4518
                    .with_snippets(this->get_snippets()));
2✔
4519
        }
1✔
4520
    }
4521

4522
    for (size_t sample_index = 0; sample_index < this->elf_samples.size();
228,980✔
4523
         sample_index += 1)
179,138✔
4524
    {
4525
        auto& elf_sample = this->elf_samples[sample_index];
179,138✔
4526
        auto sample_lines
4527
            = string_fragment(elf_sample.s_line.pp_value).split_lines();
179,138✔
4528

4529
        if (this->test_line(elf_sample, errors).is<scan_match>()) {
179,138✔
4530
            for (const auto& pat_name : elf_sample.s_matched_regexes) {
354,634✔
4531
                this->elf_patterns[pat_name]->p_matched_samples.emplace(
175,497✔
4532
                    sample_index);
4533
            }
4534
        }
4535
    }
179,138✔
4536

4537
    if (!this->elf_samples.empty()) {
49,842✔
4538
        for (const auto& elf_sample : this->elf_samples) {
221,119✔
4539
            if (elf_sample.s_matched_regexes.size() <= 1) {
179,138✔
4540
                continue;
179,138✔
4541
            }
4542

UNCOV
4543
            errors.emplace_back(
×
UNCOV
4544
                lnav::console::user_message::warning(
×
UNCOV
4545
                    attr_line_t("invalid log format: ")
×
UNCOV
4546
                        .append_quoted(
×
UNCOV
4547
                            lnav::roles::symbol(this->elf_name.to_string())))
×
UNCOV
4548
                    .with_reason(
×
UNCOV
4549
                        attr_line_t(
×
4550
                            "sample is matched by more than one regex: ")
UNCOV
4551
                            .join(elf_sample.s_matched_regexes,
×
UNCOV
4552
                                  VC_ROLE.value(role_t::VCR_SYMBOL),
×
4553
                                  ", "))
UNCOV
4554
                    .with_snippet(lnav::console::snippet::from(
×
4555
                        elf_sample.s_line.pp_location,
UNCOV
4556
                        attr_line_t().append(lnav::roles::quoted_code(
×
4557
                            elf_sample.s_line.pp_value))))
×
4558
                    .with_help("log format regexes must match a single type "
4559
                               "of log message"));
4560
        }
4561

4562
        for (const auto& pat : this->elf_pattern_order) {
137,933✔
4563
            if (pat->p_module_format) {
95,952✔
4564
                continue;
2,184✔
4565
            }
4566

4567
            if (pat->p_matched_samples.empty()) {
93,768✔
4568
                errors.emplace_back(
2✔
UNCOV
4569
                    lnav::console::user_message::warning(
×
4570
                        attr_line_t("invalid pattern: ")
4✔
4571
                            .append_quoted(
2✔
4572
                                lnav::roles::symbol(pat->p_config_path)))
4✔
4573
                        .with_reason("pattern does not match any samples")
4✔
4574
                        .with_snippet(lnav::console::snippet::from(
6✔
4575
                            pat->p_pcre.pp_location, ""))
2✔
4576
                        .with_help(
4577
                            "every pattern should have at least one sample "
4578
                            "that it matches"));
4579
            }
4580
        }
4581
    }
4582

4583
    size_t value_def_index = 0;
49,842✔
4584
    for (auto& elf_value_def : this->elf_value_def_order) {
487,815✔
4585
        elf_value_def->vd_meta.lvm_values_index
437,973✔
4586
            = std::make_optional(value_def_index++);
437,973✔
4587

4588
        if (elf_value_def->vd_meta.lvm_foreign_key
437,973✔
4589
            || elf_value_def->vd_meta.lvm_identifier)
437,973✔
4590
        {
4591
            continue;
231,297✔
4592
        }
4593

4594
        switch (elf_value_def->vd_meta.lvm_kind) {
206,676✔
4595
            case value_kind_t::VALUE_INTEGER:
51,344✔
4596
            case value_kind_t::VALUE_FLOAT:
4597
                this->elf_numeric_value_defs.push_back(elf_value_def);
51,344✔
4598
                break;
51,344✔
4599
            default:
155,332✔
4600
                break;
155,332✔
4601
        }
4602
    }
4603

4604
    int format_index = 0;
49,842✔
4605
    for (auto iter = this->jlf_line_format.begin();
49,842✔
4606
         iter != this->jlf_line_format.end();
152,157✔
4607
         ++iter, format_index++)
102,315✔
4608
    {
4609
        static const intern_string_t ts
4610
            = intern_string::lookup("__timestamp__");
103,771✔
4611
        static const intern_string_t level_field
4612
            = intern_string::lookup("__level__");
103,771✔
4613
        auto& jfe = *iter;
102,315✔
4614

4615
        if (startswith(jfe.jfe_value.pp_value.get(), "/")) {
102,315✔
4616
            jfe.jfe_value.pp_value
4617
                = intern_string::lookup(jfe.jfe_value.pp_value.get() + 1);
192✔
4618
        }
4619
        if (!jfe.jfe_ts_format.empty()) {
102,315✔
4620
            if (!jfe.jfe_value.pp_value.empty() && jfe.jfe_value.pp_value != ts)
824✔
4621
            {
4622
                log_warning(
96✔
4623
                    "%s:line-format[%d]:ignoring field '%s' since "
4624
                    "timestamp-format was used",
4625
                    this->elf_name.get(),
4626
                    format_index,
4627
                    jfe.jfe_value.pp_value.get());
4628
            }
4629
            jfe.jfe_value.pp_value = ts;
824✔
4630
        }
4631

4632
        switch (jfe.jfe_type) {
102,315✔
4633
            case json_log_field::VARIABLE: {
67,099✔
4634
                auto vd_iter
4635
                    = this->elf_value_defs.find(jfe.jfe_value.pp_value);
67,099✔
4636
                if (jfe.jfe_value.pp_value == ts) {
67,099✔
4637
                    this->elf_value_defs[this->lf_timestamp_field]
6,841✔
4638
                        ->vd_meta.lvm_hidden
6,841✔
4639
                        = true;
6,841✔
4640
                } else if (jfe.jfe_value.pp_value == level_field) {
60,258✔
4641
                    this->elf_value_defs[this->elf_level_field]
2,912✔
4642
                        ->vd_meta.lvm_hidden
2,912✔
4643
                        = true;
2,912✔
4644
                } else if (vd_iter == this->elf_value_defs.end()) {
57,346✔
4645
                    errors.emplace_back(
2✔
UNCOV
4646
                        lnav::console::user_message::error(
×
4647
                            attr_line_t("invalid line format element ")
4✔
4648
                                .append_quoted(lnav::roles::symbol(fmt::format(
4✔
4649
                                    FMT_STRING("/{}/line-format/{}/field"),
6✔
4650
                                    this->elf_name,
2✔
4651
                                    format_index))))
4652
                            .with_reason(
4✔
4653
                                attr_line_t()
4✔
4654
                                    .append_quoted(jfe.jfe_value.pp_value)
2✔
4655
                                    .append(" is not a defined value"))
2✔
4656
                            .with_snippet(jfe.jfe_value.to_snippet()));
4✔
4657
                } else {
4658
                    vd_iter->second->vd_line_format_index = format_index;
57,344✔
4659
                    switch (vd_iter->second->vd_meta.lvm_kind) {
57,344✔
4660
                        case value_kind_t::VALUE_INTEGER:
9,656✔
4661
                        case value_kind_t::VALUE_FLOAT:
4662
                            if (jfe.jfe_align
9,656✔
4663
                                == json_format_element::align_t::NONE)
4664
                            {
4665
                                jfe.jfe_align
4666
                                    = json_format_element::align_t::RIGHT;
8,928✔
4667
                            }
4668
                            break;
9,656✔
4669
                        default:
47,688✔
4670
                            break;
47,688✔
4671
                    }
4672
                }
4673
                break;
67,099✔
4674
            }
4675
            case json_log_field::CONSTANT:
35,216✔
4676
                this->jlf_line_format_init_count
35,216✔
4677
                    += std::count(jfe.jfe_default_value.begin(),
35,216✔
4678
                                  jfe.jfe_default_value.end(),
4679
                                  '\n');
35,216✔
4680
                break;
35,216✔
UNCOV
4681
            default:
×
UNCOV
4682
                break;
×
4683
        }
4684
    }
4685

4686
    for (auto& hd_pair : this->elf_highlighter_patterns) {
50,228✔
4687
        auto& hd = hd_pair.second;
386✔
4688
        text_attrs attrs;
386✔
4689

4690
        if (!hd.hd_color.pp_value.empty()) {
386✔
4691
            attrs.ta_fg_color = vc.match_color(
385✔
4692
                styling::color_unit::from_str(hd.hd_color.pp_value)
770✔
4693
                    .unwrapOrElse([&](const auto& msg) {
1✔
4694
                        errors.emplace_back(
1✔
4695
                            lnav::console::user_message::error(
4696
                                attr_line_t()
1✔
4697
                                    .append_quoted(hd.hd_color.pp_value)
2✔
4698
                                    .append(" is not a valid color value for "
1✔
4699
                                            "property ")
4700
                                    .append_quoted(lnav::roles::symbol(
2✔
4701
                                        hd.hd_color.pp_path.to_string())))
1✔
4702
                                .with_reason(msg)
2✔
4703
                                .with_snippet(hd.hd_color.to_snippet()));
1✔
4704
                        return styling::color_unit::EMPTY;
1✔
4705
                    }));
4706
        }
4707

4708
        if (!hd.hd_background_color.pp_value.empty()) {
386✔
4709
            attrs.ta_bg_color = vc.match_color(
1✔
4710
                styling::color_unit::from_str(hd.hd_background_color.pp_value)
2✔
4711
                    .unwrapOrElse([&](const auto& msg) {
1✔
4712
                        errors.emplace_back(
1✔
4713
                            lnav::console::user_message::error(
4714
                                attr_line_t()
1✔
4715
                                    .append_quoted(
2✔
4716
                                        hd.hd_background_color.pp_value)
1✔
4717
                                    .append(" is not a valid color value for "
1✔
4718
                                            "property ")
4719
                                    .append_quoted(lnav::roles::symbol(
2✔
4720
                                        hd.hd_background_color.pp_path
1✔
4721
                                            .to_string())))
4722
                                .with_reason(msg)
2✔
4723
                                .with_snippet(
4724
                                    hd.hd_background_color.to_snippet()));
1✔
4725
                        return styling::color_unit::EMPTY;
1✔
4726
                    }));
4727
        }
4728

4729
        if (hd.hd_underline) {
386✔
4730
            attrs |= text_attrs::style::underline;
96✔
4731
        }
4732
        if (hd.hd_blink) {
386✔
UNCOV
4733
            attrs |= text_attrs::style::blink;
×
4734
        }
4735

4736
        if (hd.hd_pattern.pp_value != nullptr) {
386✔
4737
            this->lf_highlighters.emplace_back(hd.hd_pattern.pp_value);
384✔
4738
            this->lf_highlighters.back()
384✔
4739
                .with_name(hd_pair.first.to_string())
768✔
4740
                .with_format_name(this->elf_name)
384✔
4741
                .with_attrs(attrs)
384✔
4742
                .with_nestable(hd.hd_nestable);
384✔
4743
        }
4744
    }
4745

4746
    this->lf_value_stats.resize(this->elf_value_defs.size());
49,842✔
4747
}
4748

4749
void
4750
external_log_format::register_vtabs(
41,698✔
4751
    log_vtab_manager* vtab_manager,
4752
    std::vector<lnav::console::user_message>& errors)
4753
{
4754
    for (auto& elf_search_table : this->elf_search_tables) {
49,067✔
4755
        if (elf_search_table.second.std_pattern.pp_value == nullptr) {
7,369✔
4756
            continue;
1✔
4757
        }
4758

4759
        auto lst = std::make_shared<log_search_table>(
4760
            elf_search_table.second.std_pattern.pp_value,
7,368✔
4761
            elf_search_table.first);
7,368✔
4762
        lst->lst_format = this;
7,368✔
4763
        lst->lst_log_path_glob = elf_search_table.second.std_glob;
7,368✔
4764
        if (elf_search_table.second.std_level != LEVEL_UNKNOWN) {
7,368✔
4765
            lst->lst_log_level = elf_search_table.second.std_level;
3,070✔
4766
        }
4767
        auto errmsg = vtab_manager->register_vtab(lst);
7,368✔
4768
        if (!errmsg.empty()) {
7,368✔
4769
#if 0
4770
            errors.push_back("error:" + this->elf_name.to_string() + ":"
4771
                             + search_iter->first.to_string()
4772
                             + ":unable to register table -- " + errmsg);
4773
#endif
4774
        }
4775
    }
7,368✔
4776
}
41,698✔
4777

4778
bool
4779
external_log_format::match_samples(const std::vector<sample_t>& samples) const
3,372,678✔
4780
{
4781
    for (const auto& sample_iter : samples) {
15,338,331✔
4782
        for (const auto& pat_iter : this->elf_pattern_order) {
33,625,300✔
4783
            auto& pat = *pat_iter;
21,659,647✔
4784

4785
            if (!pat.p_pcre.pp_value) {
21,659,647✔
UNCOV
4786
                continue;
×
4787
            }
4788

4789
            if (pat.p_pcre.pp_value
43,319,294✔
4790
                    ->find_in(sample_iter.s_line.pp_value, PCRE2_NO_UTF_CHECK)
43,319,294✔
4791
                    .ignore_error())
43,319,294✔
4792
            {
4793
                return true;
15,924✔
4794
            }
4795
        }
4796
    }
4797

4798
    return false;
3,356,754✔
4799
}
4800

4801
class external_log_table : public log_format_vtab_impl {
4802
public:
4803
    explicit external_log_table(const external_log_format& elf)
41,698✔
4804
        : log_format_vtab_impl(elf), elt_format(elf)
41,698✔
4805
    {
4806
    }
41,698✔
4807

4808
    void get_columns(std::vector<vtab_column>& cols) const override
42,090✔
4809
    {
4810
        const auto& elf = this->elt_format;
42,090✔
4811

4812
        cols.resize(elf.elf_column_count);
42,090✔
4813
        for (const auto& vd : elf.elf_value_def_order) {
413,871✔
4814
            auto type_pair = log_vtab_impl::logline_value_to_sqlite_type(
371,781✔
4815
                vd->vd_meta.lvm_kind);
371,781✔
4816

4817
            if (!vd->vd_meta.lvm_column.is<logline_value_meta::table_column>())
371,781✔
4818
            {
4819
                continue;
36,283✔
4820
            }
4821

4822
            auto col
4823
                = vd->vd_meta.lvm_column.get<logline_value_meta::table_column>()
335,498✔
4824
                      .value;
335,498✔
4825
            require(0 <= col && col < elf.elf_column_count);
335,498✔
4826

4827
            cols[col].vc_name = vd->vd_meta.lvm_name.get();
335,498✔
4828
            cols[col].vc_type = type_pair.first;
335,498✔
4829
            cols[col].vc_subtype = type_pair.second;
335,498✔
4830
            cols[col].vc_collator = vd->vd_collate;
335,498✔
4831
            cols[col].vc_comment = vd->vd_description;
335,498✔
4832
        }
4833
    }
42,090✔
4834

4835
    void get_foreign_keys(
34,898✔
4836
        std::unordered_set<std::string>& keys_inout) const override
4837
    {
4838
        log_vtab_impl::get_foreign_keys(keys_inout);
34,898✔
4839

4840
        for (const auto& elf_value_def : this->elt_format.elf_value_defs) {
502,606✔
4841
            if (elf_value_def.second->vd_meta.lvm_foreign_key
467,708✔
4842
                || elf_value_def.second->vd_meta.lvm_identifier)
467,708✔
4843
            {
4844
                keys_inout.emplace(elf_value_def.first.to_string());
168,234✔
4845
            }
4846
        }
4847
    }
34,898✔
4848

4849
    bool next(log_cursor& lc, logfile_sub_source& lss) override
3,481✔
4850
    {
4851
        if (lc.is_eof()) {
3,481✔
UNCOV
4852
            return true;
×
4853
        }
4854

4855
        content_line_t cl(lss.at(lc.lc_curr_line));
3,481✔
4856
        auto* lf = lss.find_file_ptr(cl);
3,481✔
4857
        auto lf_iter = lf->begin() + cl;
3,481✔
4858
        uint8_t mod_id = lf_iter->get_module_id();
3,481✔
4859

4860
        if (lf_iter->is_continued()) {
3,481✔
UNCOV
4861
            return false;
×
4862
        }
4863

4864
        this->elt_module_format.mf_mod_format = nullptr;
3,481✔
4865
        if (lf->get_format_name() == this->lfvi_format.get_name()) {
3,481✔
4866
            return true;
3,470✔
4867
        }
4868
        if (mod_id && mod_id == this->lfvi_format.lf_mod_index) {
11✔
4869
            auto format = lf->get_format();
2✔
4870

4871
            return lf->read_line(lf_iter)
2✔
4872
                .map([this, format, cl, lf](auto line) {
4✔
4873
                    string_attrs_t sa;
2✔
4874
                    logline_value_vector values;
2✔
4875
                    line_range mod_name_range;
2✔
4876
                    intern_string_t mod_name;
2✔
4877

4878
                    values.lvv_sbr = line.clone();
2✔
4879
                    format->annotate(lf, cl, sa, values, false);
2✔
4880
                    this->elt_container_body
2✔
4881
                        = find_string_attr_range(sa, &SA_BODY);
2✔
4882
                    if (!this->elt_container_body.is_valid()) {
2✔
UNCOV
4883
                        return false;
×
4884
                    }
4885
                    this->elt_container_body.ltrim(line.get_data());
2✔
4886
                    mod_name_range = find_string_attr_range(sa, &L_MODULE);
2✔
4887
                    if (!mod_name_range.is_valid()) {
2✔
UNCOV
4888
                        return false;
×
4889
                    }
4890
                    mod_name = intern_string::lookup(
2✔
4891
                        &line.get_data()[mod_name_range.lr_start],
2✔
4892
                        mod_name_range.length());
2✔
4893
                    this->elt_module_format
2✔
4894
                        = external_log_format::MODULE_FORMATS[mod_name];
2✔
4895
                    if (!this->elt_module_format.mf_mod_format) {
2✔
UNCOV
4896
                        return false;
×
4897
                    }
4898
                    return this->elt_module_format.mf_mod_format->get_name()
2✔
4899
                        == this->lfvi_format.get_name();
4✔
4900
                })
2✔
4901
                .unwrapOr(false);
2✔
4902
        }
2✔
4903

4904
        return false;
9✔
4905
    }
4906

4907
    void extract(logfile* lf,
3,387✔
4908
                 uint64_t line_number,
4909
                 string_attrs_t& sa,
4910
                 logline_value_vector& values) override
4911
    {
4912
        auto& line = values.lvv_sbr;
3,387✔
4913
        auto format = lf->get_format();
3,387✔
4914

4915
        if (this->elt_module_format.mf_mod_format != nullptr) {
3,387✔
4916
            shared_buffer_ref body_ref;
2✔
4917

4918
            body_ref.subset(line,
2✔
4919
                            this->elt_container_body.lr_start,
2✔
4920
                            this->elt_container_body.length());
2✔
4921
            sa.clear();
2✔
4922
            auto narrow_res
4923
                = values.lvv_sbr.narrow(this->elt_container_body.lr_start,
2✔
4924
                                        this->elt_container_body.length());
2✔
4925
            values.lvv_values.clear();
2✔
4926
            this->elt_module_format.mf_mod_format->annotate(
2✔
4927
                lf, line_number, sa, values, false);
4928
            values.lvv_sbr.widen(narrow_res);
2✔
4929
        } else {
2✔
4930
            sa.clear();
3,385✔
4931
            format->annotate(lf, line_number, sa, values, false);
3,385✔
4932
        }
4933
    }
3,387✔
4934

4935
    const external_log_format& elt_format;
4936
    module_format elt_module_format;
4937
    struct line_range elt_container_body;
4938
};
4939

4940
std::shared_ptr<log_vtab_impl>
4941
external_log_format::get_vtab_impl() const
41,698✔
4942
{
4943
    return std::make_shared<external_log_table>(*this);
41,698✔
4944
}
4945

4946
std::shared_ptr<log_format>
4947
external_log_format::specialized(int fmt_lock)
541✔
4948
{
4949
    auto retval = std::make_shared<external_log_format>(*this);
541✔
4950

4951
    retval->lf_specialized = true;
541✔
4952
    this->lf_pattern_locks.clear();
541✔
4953
    if (fmt_lock != -1) {
541✔
4954
        retval->lf_pattern_locks.emplace_back(0, fmt_lock);
43✔
4955
    }
4956

4957
    if (this->elf_type == elf_type_t::ELF_TYPE_JSON) {
541✔
4958
        this->jlf_parse_context
4959
            = std::make_shared<yajlpp_parse_context>(this->elf_name);
46✔
4960
        this->jlf_yajl_handle.reset(
46✔
4961
            yajl_alloc(&this->jlf_parse_context->ypc_callbacks,
46✔
4962
                       nullptr,
4963
                       this->jlf_parse_context.get()),
46✔
4964
            yajl_handle_deleter());
4965
        yajl_config(this->jlf_yajl_handle.get(), yajl_dont_validate_strings, 1);
46✔
4966
        this->jlf_cached_line.reserve(16 * 1024);
46✔
4967
    }
4968

4969
    this->lf_value_stats.clear();
541✔
4970
    this->lf_value_stats.resize(this->elf_value_defs.size());
541✔
4971
    this->elf_specialized_value_defs_state = *this->elf_value_defs_state;
541✔
4972

4973
    return retval;
1,082✔
4974
}
541✔
4975

4976
log_format::match_name_result
4977
external_log_format::match_name(const std::string& filename)
720,754✔
4978
{
4979
    if (this->elf_filename_pcre.pp_value == nullptr) {
720,754✔
4980
        return name_matched{};
719,388✔
4981
    }
4982

4983
    if (this->elf_filename_pcre.pp_value->find_in(filename)
2,732✔
4984
            .ignore_error()
2,732✔
4985
            .has_value())
1,366✔
4986
    {
4987
        return name_matched{};
238✔
4988
    }
4989

4990
    return name_mismatched{
2,256✔
4991
        this->elf_filename_pcre.pp_value->match_partial(filename),
2,256✔
4992
        this->elf_filename_pcre.pp_value->get_pattern(),
1,128✔
4993
    };
1,128✔
4994
}
4995

4996
auto
4997
external_log_format::value_line_count(const value_def* vd,
208,358✔
4998
                                      bool top_level,
4999
                                      std::optional<double> val,
5000
                                      const unsigned char* str,
5001
                                      ssize_t len,
5002
                                      yajl_string_props_t* props)
5003
    -> value_line_count_result
5004
{
5005
    value_line_count_result retval;
208,358✔
5006

5007
    if (vd == nullptr) {
208,358✔
5008
        if (this->jlf_hide_extra || !top_level) {
190,241✔
5009
            retval.vlcr_count = 0;
176,725✔
5010
        }
5011

5012
        return retval;
190,241✔
5013
    }
5014

5015
    if (str != nullptr && props != nullptr && !val) {
18,117✔
5016
        auto frag = string_fragment::from_bytes(str, len);
13,437✔
5017
        while (frag.endswith("\n")) {
13,606✔
5018
            frag.pop_back();
169✔
5019
            props->line_feeds -= 1;
169✔
5020
        }
5021
        retval.vlcr_has_ansi |= props->has_ansi;
13,437✔
5022
        retval.vlcr_count += props->line_feeds;
13,437✔
5023
    }
5024

5025
    if (vd->vd_meta.lvm_values_index) {
18,117✔
5026
        auto& lvs = this->lf_value_stats[vd->vd_meta.lvm_values_index.value()];
9,381✔
5027
        if (len > lvs.lvs_width) {
9,381✔
5028
            lvs.lvs_width = len;
889✔
5029
        }
5030
        if (val) {
9,381✔
5031
            lvs.add_value(val.value());
151✔
5032
        }
5033
    }
5034

5035
    if (vd->vd_line_format_index) {
18,117✔
5036
        retval.vlcr_line_format_count += 1;
3,086✔
5037
        retval.vlcr_count -= 1;
3,086✔
5038
        retval.vlcr_line_format_index = vd->vd_line_format_index;
3,086✔
5039
    }
5040
    if (vd->vd_meta.is_hidden()) {
18,117✔
5041
        retval.vlcr_count = 0;
6,098✔
5042
        return retval;
6,098✔
5043
    }
5044

5045
    return retval;
12,019✔
5046
}
5047

5048
log_level_t
5049
external_log_format::convert_level(string_fragment sf,
184,861✔
5050
                                   scan_batch_context* sbc) const
5051
{
5052
    auto retval = LEVEL_INFO;
184,861✔
5053

5054
    if (sf.is_valid()) {
184,861✔
5055
        if (sbc != nullptr) {
144,364✔
5056
            auto ssm_res = sbc->sbc_level_cache.lookup(sf);
9,103✔
5057
            if (ssm_res.has_value()) {
9,103✔
5058
                return static_cast<log_level_t>(ssm_res.value());
4,083✔
5059
            }
5060
        }
5061

5062
        if (this->elf_level_patterns.empty()) {
140,281✔
5063
            retval = string2level(sf.data(), sf.length());
29,465✔
5064
        } else {
5065
            for (const auto& elf_level_pattern : this->elf_level_patterns) {
298,639✔
5066
                if (elf_level_pattern.second.lp_pcre.pp_value
546,628✔
5067
                        ->find_in(sf, PCRE2_NO_UTF_CHECK)
546,628✔
5068
                        .ignore_error()
546,628✔
5069
                        .has_value())
273,314✔
5070
                {
5071
                    retval = elf_level_pattern.first;
85,491✔
5072
                    break;
85,491✔
5073
                }
5074
            }
5075
        }
5076

5077
        if (sbc != nullptr
140,281✔
5078
            && sf.length() <= lnav::small_string_map::MAX_KEY_SIZE)
140,281✔
5079
        {
5080
            sbc->sbc_level_cache.insert(sf, retval);
4,372✔
5081
        }
5082
    }
5083

5084
    return retval;
180,778✔
5085
}
5086

5087
logline_value_meta
5088
external_log_format::get_value_meta(intern_string_t field_name,
15,035✔
5089
                                    value_kind_t kind)
5090
{
5091
    const auto iter = this->elf_value_defs.find(field_name);
15,035✔
5092
    if (iter == this->elf_value_defs.end()) {
15,035✔
5093
        auto retval = logline_value_meta(
5094
            field_name, kind, logline_value_meta::external_column{}, this);
879✔
5095

5096
        retval.lvm_hidden = this->jlf_hide_extra;
879✔
5097
        return retval;
879✔
5098
    }
879✔
5099

5100
    auto lvm = iter->second->vd_meta;
14,156✔
5101

5102
    lvm.lvm_kind = kind;
14,156✔
5103
    return lvm;
14,156✔
5104
}
14,156✔
5105

5106
logline_value_meta
5107
external_log_format::get_value_meta(yajlpp_parse_context* ypc,
2,311✔
5108
                                    const value_def* vd,
5109
                                    value_kind_t kind)
5110
{
5111
    if (vd == nullptr) {
2,311✔
5112
        auto retval = logline_value_meta(
5113
            ypc->get_path(), kind, logline_value_meta::external_column{}, this);
57✔
5114

5115
        retval.lvm_hidden = this->jlf_hide_extra;
57✔
5116
        return retval;
57✔
5117
    }
57✔
5118

5119
    auto lvm = vd->vd_meta;
2,254✔
5120

5121
    lvm.lvm_kind = kind;
2,254✔
5122
    return lvm;
2,254✔
5123
}
2,254✔
5124

5125
void
5126
external_log_format::json_append(const json_format_element& jfe,
8,790✔
5127
                                 const value_def* vd,
5128
                                 const string_fragment& sf)
5129
{
5130
    if (jfe.jfe_align == json_format_element::align_t::RIGHT) {
8,790✔
5131
        auto sf_width = sf.column_width();
343✔
5132
        if (sf_width < jfe.jfe_min_width) {
343✔
UNCOV
5133
            this->json_append_to_cache(jfe.jfe_min_width - sf_width);
×
5134
        } else if (jfe.jfe_auto_width && vd != nullptr
32✔
5135
                   && sf_width
375✔
5136
                       < this->lf_value_stats[vd->vd_meta.lvm_values_index
5137
                                                  .value()]
32✔
5138
                             .lvs_width)
32✔
5139
        {
5140
            this->json_append_to_cache(
12✔
5141
                this->lf_value_stats[vd->vd_meta.lvm_values_index.value()]
12✔
5142
                    .lvs_width
12✔
5143
                - sf_width);
12✔
5144
        }
5145
    }
5146
    this->json_append_to_cache(sf.data(), sf.length());
8,790✔
5147
    if ((jfe.jfe_align == json_format_element::align_t::LEFT
8,790✔
5148
         || jfe.jfe_align == json_format_element::align_t::NONE)
8,732✔
5149
        && (jfe.jfe_min_width > 0 || jfe.jfe_auto_width))
8,447✔
5150
    {
5151
        auto sf_width = sf.column_width();
1,198✔
5152
        if (sf_width < jfe.jfe_min_width) {
1,198✔
5153
            this->json_append_to_cache(jfe.jfe_min_width - sf_width);
369✔
5154
        } else if (jfe.jfe_auto_width && vd != nullptr
704✔
5155
                   && sf_width
1,533✔
5156
                       < this->lf_value_stats[vd->vd_meta.lvm_values_index
5157
                                                  .value()]
644✔
5158
                             .lvs_width)
644✔
5159
        {
5160
            this->json_append_to_cache(
377✔
5161
                this->lf_value_stats[vd->vd_meta.lvm_values_index.value()]
377✔
5162
                    .lvs_width
377✔
5163
                - sf_width);
377✔
5164
        }
5165
    }
5166
}
8,790✔
5167

5168
intern_string_t
5169
external_log_format::get_pattern_name(uint64_t line_number) const
50✔
5170
{
5171
    if (this->elf_type != elf_type_t::ELF_TYPE_TEXT) {
50✔
5172
        static auto structured = intern_string::lookup("structured");
5173

UNCOV
5174
        return structured;
×
5175
    }
5176
    int pat_index = this->pattern_index_for_line(line_number);
50✔
5177
    return this->elf_pattern_order[pat_index]->p_name;
50✔
5178
}
5179

5180
int
5181
log_format::pattern_index_for_line(uint64_t line_number) const
6,758✔
5182
{
5183
    if (this->lf_pattern_locks.empty()) {
6,758✔
UNCOV
5184
        return -1;
×
5185
    }
5186

5187
    auto iter = lower_bound(this->lf_pattern_locks.cbegin(),
6,758✔
5188
                            this->lf_pattern_locks.cend(),
5189
                            line_number,
5190
                            [](const pattern_for_lines& pfl, uint32_t line) {
6,817✔
5191
                                return pfl.pfl_line < line;
6,817✔
5192
                            });
5193

5194
    if (iter == this->lf_pattern_locks.end() || iter->pfl_line != line_number) {
6,758✔
5195
        --iter;
6,134✔
5196
    }
5197

5198
    return iter->pfl_pat_index;
6,758✔
5199
}
5200

5201
std::string
UNCOV
5202
log_format::get_pattern_path(uint64_t line_number) const
×
5203
{
UNCOV
5204
    int pat_index = this->pattern_index_for_line(line_number);
×
UNCOV
5205
    return fmt::format(FMT_STRING("builtin ({})"), pat_index);
×
5206
}
5207

5208
intern_string_t
5209
log_format::get_pattern_name(uint64_t line_number) const
6✔
5210
{
5211
    char pat_str[128];
5212

5213
    int pat_index = this->pattern_index_for_line(line_number);
6✔
UNCOV
5214
    auto to_n_res = fmt::format_to_n(
×
5215
        pat_str, sizeof(pat_str) - 1, FMT_STRING("builtin ({})"), pat_index);
18✔
5216
    pat_str[to_n_res.size] = '\0';
6✔
5217
    return intern_string::lookup(pat_str);
12✔
5218
}
5219

5220
std::shared_ptr<log_format>
5221
log_format::find_root_format(const char* name)
1,238✔
5222
{
5223
    auto& fmts = get_root_formats();
1,238✔
5224
    for (auto& lf : fmts) {
47,377✔
5225
        if (lf->get_name() == name) {
47,377✔
5226
            return lf;
1,238✔
5227
        }
5228
    }
UNCOV
5229
    return nullptr;
×
5230
}
5231

5232
exttm
5233
log_format::tm_for_display(logfile::iterator ll, string_fragment sf)
380✔
5234
{
5235
    auto adjusted_time = ll->get_timeval();
380✔
5236
    exttm retval;
380✔
5237

5238
    retval.et_nsec = std::chrono::duration_cast<std::chrono::nanoseconds>(
380✔
UNCOV
5239
                         std::chrono::microseconds{adjusted_time.tv_usec})
×
5240
                         .count();
380✔
5241
    if (this->lf_timestamp_flags & ETF_NANOS_SET) {
380✔
5242
        timeval actual_tv;
5243
        exttm tm;
×
UNCOV
5244
        if (this->lf_date_time.scan(sf.data(),
×
UNCOV
5245
                                    sf.length(),
×
5246
                                    this->get_timestamp_formats(),
5247
                                    &tm,
5248
                                    actual_tv,
5249
                                    false))
5250
        {
UNCOV
5251
            adjusted_time.tv_usec = actual_tv.tv_usec;
×
UNCOV
5252
            retval.et_nsec = tm.et_nsec;
×
5253
        }
5254
    }
5255
    gmtime_r(&adjusted_time.tv_sec, &retval.et_tm);
380✔
5256
    retval.et_flags = this->lf_timestamp_flags;
380✔
5257
    if (this->lf_timestamp_flags & ETF_ZONE_SET
380✔
5258
        && this->lf_date_time.dts_zoned_to_local)
290✔
5259
    {
5260
        retval.et_flags &= ~ETF_Z_IS_UTC;
290✔
5261
    }
5262
    retval.et_gmtoff = this->lf_date_time.dts_local_offset_cache;
380✔
5263

5264
    return retval;
760✔
5265
}
5266

5267
log_format::pattern_for_lines::pattern_for_lines(uint32_t pfl_line,
2,045✔
5268
                                                 uint32_t pfl_pat_index)
2,045✔
5269
    : pfl_line(pfl_line), pfl_pat_index(pfl_pat_index)
2,045✔
5270
{
5271
}
2,045✔
5272

5273
void
5274
logline_value_stats::merge(const logline_value_stats& other)
106✔
5275
{
5276
    if (other.lvs_count == 0) {
106✔
UNCOV
5277
        return;
×
5278
    }
5279

5280
    require(other.lvs_min_value <= other.lvs_max_value);
106✔
5281

5282
    if (other.lvs_width > this->lvs_width) {
106✔
5283
        this->lvs_width = other.lvs_width;
71✔
5284
    }
5285
    if (other.lvs_min_value < this->lvs_min_value) {
106✔
5286
        this->lvs_min_value = other.lvs_min_value;
106✔
5287
    }
5288
    if (other.lvs_max_value > this->lvs_max_value) {
106✔
5289
        this->lvs_max_value = other.lvs_max_value;
106✔
5290
    }
5291
    this->lvs_count += other.lvs_count;
106✔
5292
    this->lvs_total += other.lvs_total;
106✔
5293

5294
    ensure(this->lvs_count >= 0);
106✔
5295
    ensure(this->lvs_min_value <= this->lvs_max_value);
106✔
5296
}
5297

5298
void
5299
logline_value_stats::add_value(double value)
23,568✔
5300
{
5301
    if (value < this->lvs_min_value) {
23,568✔
5302
        this->lvs_min_value = value;
720✔
5303
    }
5304
    if (value > this->lvs_max_value) {
23,568✔
5305
        this->lvs_max_value = value;
1,374✔
5306
    }
5307
    this->lvs_count += 1;
23,568✔
5308
    this->lvs_total += value;
23,568✔
5309
}
23,568✔
5310

5311
std::vector<logline_value_meta>
5312
external_log_format::get_value_metadata() const
7,368✔
5313
{
5314
    std::vector<logline_value_meta> retval;
7,368✔
5315

5316
    for (const auto& vd : this->elf_value_def_order) {
75,522✔
5317
        retval.emplace_back(vd->vd_meta);
68,154✔
5318
    }
5319

5320
    return retval;
7,368✔
UNCOV
5321
}
×
5322

5323
const logline_value_stats*
5324
external_log_format::stats_for_value(const intern_string_t& name) const
71✔
5325
{
5326
    const auto iter = this->elf_value_defs.find(name);
71✔
5327
    if (iter != this->elf_value_defs.end()
71✔
5328
        && iter->second->vd_meta.lvm_values_index)
71✔
5329
    {
5330
        return &this->lf_value_stats[iter->second->vd_meta.lvm_values_index
71✔
5331
                                         .value()];
142✔
5332
    }
5333

UNCOV
5334
    return nullptr;
×
5335
}
5336

5337
std::string
5338
external_log_format::get_pattern_regex(uint64_t line_number) const
9✔
5339
{
5340
    if (this->elf_type != elf_type_t::ELF_TYPE_TEXT) {
9✔
5341
        return "";
2✔
5342
    }
5343
    int pat_index = this->pattern_index_for_line(line_number);
8✔
5344
    return this->elf_pattern_order[pat_index]->p_pcre.pp_value->get_pattern();
8✔
5345
}
5346

5347
bool
5348
external_log_format::hide_field(const intern_string_t field_name, bool val)
12✔
5349
{
5350
    const auto vd_iter = this->elf_value_defs.find(field_name);
12✔
5351
    if (vd_iter == this->elf_value_defs.end()) {
12✔
5352
        log_warning("field to hide not found: %s.%s",
1✔
5353
                    this->elf_name.c_str(),
5354
                    field_name.c_str());
5355
        return false;
1✔
5356
    }
5357

5358
    vd_iter->second->vd_meta.lvm_user_hidden = val;
11✔
5359
    if (this->elf_type == elf_type_t::ELF_TYPE_JSON) {
11✔
5360
        bool found = false;
2✔
5361

5362
        if (!field_name.to_string_fragment().find('#')) {
2✔
5363
            for (const auto& jfe : this->jlf_line_format) {
18✔
5364
                if (jfe.jfe_value.pp_value == field_name) {
17✔
UNCOV
5365
                    log_debug(
×
5366
                        "hide-field not triggering rebuild since it is in "
5367
                        "line-format: %s.%s",
5368
                        this->elf_name.c_str(),
5369
                        field_name.c_str());
UNCOV
5370
                    found = true;
×
UNCOV
5371
                    break;
×
5372
                }
5373
            }
5374
        }
5375
        if (!found) {
2✔
5376
            log_info("format field %s.%s changed, rebuilding",
2✔
5377
                     this->elf_name.get(),
5378
                     field_name.get());
5379
            this->elf_value_defs_state->vds_generation += 1;
2✔
5380
        }
5381
    }
5382
    return true;
11✔
5383
}
5384

5385
bool
5386
external_log_format::format_changed()
2,544✔
5387
{
5388
    if (this->elf_specialized_value_defs_state.vds_generation
5,088✔
5389
        != this->elf_value_defs_state->vds_generation)
2,544✔
5390
    {
5391
        this->elf_specialized_value_defs_state = *this->elf_value_defs_state;
2✔
5392
        this->jlf_cached_offset = -1;
2✔
5393
        return true;
2✔
5394
    }
5395

5396
    return false;
2,542✔
5397
}
5398

5399
bool
5400
format_tag_def::path_restriction::matches(const char* fn) const
5✔
5401
{
5402
    return fnmatch(this->p_glob.c_str(), fn, 0) == 0;
5✔
5403
}
5404

5405
bool
5406
format_partition_def::path_restriction::matches(const char* fn) const
5✔
5407
{
5408
    return fnmatch(this->p_glob.c_str(), fn, 0) == 0;
5✔
5409
}
5410

5411
/* XXX */
5412
#include "log_format_impls.cc"
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