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

tstack / lnav / 19674547642-2710

25 Nov 2025 03:17PM UTC coverage: 68.841% (+0.001%) from 68.84%
19674547642-2710

push

github

tstack
[spectro] per-row thresholds

32 of 37 new or added lines in 3 files covered. (86.49%)

378 existing lines in 10 files now uncovered.

51184 of 74351 relevant lines covered (68.84%)

431944.62 hits per line

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

85.59
/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_OPID("opid");
78
constexpr string_attr_type<bookmark_metadata*> L_META("meta");
79

80
std::vector<std::shared_ptr<external_log_format>>
81
    external_log_format::GRAPH_ORDERED_FORMATS;
82

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

92
static constexpr uint32_t DATE_TIME_SET_FLAGS = ETF_YEAR_SET | ETF_MONTH_SET
93
    | ETF_DAY_SET | ETF_HOUR_SET | ETF_MINUTE_SET | ETF_SECOND_SET;
94

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

102
    return *this;
858✔
103
}
104

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

115
    return *this;
385✔
116
}
117

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

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

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

147
    return *this;
385✔
148
}
149

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

157
thread_id_time_range&
158
thread_id_time_range::operator|=(const thread_id_time_range& rhs)
473✔
159
{
160
    this->titr_range |= rhs.titr_range;
473✔
161
    this->titr_level_stats |= rhs.titr_level_stats;
473✔
162

163
    return *this;
473✔
164
}
165

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

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

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

209
    return retval;
11,395✔
210
}
211

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

241
    return retval;
12,667✔
242
}
243

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

257
        sub_iter = emp_res.first;
43✔
258
    }
259

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

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

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

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

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

306
        return std::nullopt;
771✔
307
    }
308
    return sf.to_string();
1,781✔
309
}
310

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

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

326
    return retval;
1,023✔
327
}
×
328

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

355
    return retval;
38✔
356
}
357

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

365
    if (len == -1) {
×
366
        len = strlen(msg);
×
367
    }
368

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

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

376
        next += 1;
×
377
        int amount = (next - last);
×
378

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

384
        last = next + 1;
×
385
    }
386

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

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

397
    return retval;
×
398
}
399

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

409
    switch (this->lv_meta.lvm_kind) {
633,807✔
410
        case value_kind_t::VALUE_JSON:
322,146✔
411
        case value_kind_t::VALUE_XML:
412
        case value_kind_t::VALUE_STRUCT:
413
        case value_kind_t::VALUE_TEXT:
414
        case value_kind_t::VALUE_QUOTED:
415
        case value_kind_t::VALUE_W3C_QUOTED:
416
        case value_kind_t::VALUE_TIMESTAMP:
417
            require(origin.lr_end != -1);
322,146✔
418
            this->lv_frag = string_fragment::from_byte_range(
322,146✔
419
                sbr.get_data(), origin.lr_start, origin.lr_end);
322,146✔
420
            break;
322,146✔
421

422
        case value_kind_t::VALUE_NULL:
×
423
            break;
×
424

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

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

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

460
        case value_kind_t::VALUE_UNKNOWN:
×
461
        case value_kind_t::VALUE__MAX:
462
            ensure(0);
×
463
            break;
464
    }
465
}
633,807✔
466

467
std::string
468
logline_value::to_string() const
10,061✔
469
{
470
    char buffer[128];
471

472
    switch (this->lv_meta.lvm_kind) {
10,061✔
473
        case value_kind_t::VALUE_NULL:
15✔
474
            return "null";
30✔
475

476
        case value_kind_t::VALUE_JSON:
9,463✔
477
        case value_kind_t::VALUE_XML:
478
        case value_kind_t::VALUE_STRUCT:
479
        case value_kind_t::VALUE_TEXT:
480
        case value_kind_t::VALUE_TIMESTAMP:
481
            if (this->lv_str) {
9,463✔
482
                return this->lv_str.value();
1,418✔
483
            }
484
            if (this->lv_frag.empty()) {
8,045✔
485
                return this->lv_intern_string.to_string();
55✔
486
            }
487
            return this->lv_frag.to_string();
7,990✔
488

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

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

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

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

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

538
    return {buffer};
1,140✔
539
}
540

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

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

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

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

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

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

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

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

615
    return string_fragment::from_c_str(buffer).to_owned(alloc);
×
616
}
617

618
std::vector<std::shared_ptr<log_format>> log_format::lf_root_formats;
619

620
std::vector<std::shared_ptr<log_format>>&
621
log_format::get_root_formats()
17,391✔
622
{
623
    return lf_root_formats;
17,391✔
624
}
625

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

643
                if (desc_field_index_iter == fpat->p_value_name_to_index.end())
1,813✔
644
                {
645
                    continue;
43✔
646
                }
647

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

653
                desc_elem_str = desc_def.matches(desc_cap_opt.value());
1,770✔
654
                if (desc_elem_str) {
1,770✔
655
                    lod.lod_index = desc_defs->od_index;
1,002✔
656
                    break;
1,002✔
657
                }
658
            }
659
        }
660
    }
661
    if (lod.lod_index) {
5,428✔
662
        const auto& desc_def_v
663
            = *desc_defs_vec[lod.lod_index.value()]->od_descriptors;
4,174✔
664
        auto& desc_v = lod.lod_elements;
4,174✔
665

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

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

687
            if (!desc_elem_str) {
1,820✔
688
                desc_elem_str = desc_def.matches(desc_cap_opt.value());
818✔
689
            }
690
            if (desc_elem_str) {
1,820✔
691
                if (!found_desc) {
1,817✔
692
                    desc_v.insert(desc_def_index, desc_elem_str.value());
1,817✔
693
                } else if (!desc_elem_str->empty()) {
×
694
                    found_desc.value()->append(desc_def.od_joiner);
×
695
                    found_desc.value()->append(desc_elem_str.value());
×
696
                }
697
            }
698
            desc_elem_str = std::nullopt;
1,820✔
699
        }
700
    }
701
}
5,428✔
702

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

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

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

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

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

774
    if (locked_index == -1) {
2,270,563✔
775
        index += 1;
2,263,337✔
776
        if (index >= (int) patterns.size()) {
2,263,337✔
777
            retval = false;
680,620✔
778
        }
779
    } else if (index == locked_index) {
7,226✔
780
        retval = false;
1✔
781
    } else {
782
        index = locked_index;
7,225✔
783
    }
784

785
    return retval;
2,270,563✔
786
}
787

788
bool
789
log_format::next_format(const pcre_format* fmt, int& index, int& locked_index)
179,386✔
790
{
791
    bool retval = true;
179,386✔
792

793
    if (locked_index == -1) {
179,386✔
794
        index += 1;
179,215✔
795
        if (fmt[index].name == nullptr) {
179,215✔
796
            retval = false;
10,103✔
797
        }
798
    } else if (index == locked_index) {
171✔
799
        retval = false;
31✔
800
    } else {
801
        index = locked_index;
140✔
802
    }
803

804
    return retval;
179,386✔
805
}
806

807
const char*
808
log_format::log_scanf(scan_batch_context& sbc,
12,497✔
809
                      uint32_t line_number,
810
                      string_fragment line,
811
                      const pcre_format* fmt,
812
                      const char* time_fmt[],
813
                      exttm* tm_out,
814
                      timeval* tv_out,
815

816
                      string_fragment* ts_out,
817
                      std::optional<string_fragment>* level_out)
818
{
819
    int curr_fmt = -1;
12,497✔
820
    const char* retval = nullptr;
12,497✔
821
    bool done = false;
12,497✔
822
    int pat_index = sbc.sbc_pattern_locks.last_pattern_index();
12,497✔
823

824
    while (!done && next_format(fmt, curr_fmt, pat_index)) {
181,749✔
825
        thread_local auto md = lnav::pcre2pp::match_data::unitialized();
169,252✔
826

827
        auto match_res = fmt[curr_fmt]
169,252✔
828
                             .pcre->capture_from(line)
169,252✔
829
                             .into(md)
169,252✔
830
                             .matches(PCRE2_NO_UTF_CHECK)
338,504✔
831
                             .ignore_error();
169,252✔
832
        if (!match_res) {
169,252✔
833
            retval = nullptr;
145,544✔
834
        } else {
835
            auto ts = md[fmt[curr_fmt].pf_timestamp_index];
23,708✔
836

837
            retval = this->lf_date_time.scan(
23,708✔
838
                ts->data(), ts->length(), nullptr, tm_out, *tv_out);
23,708✔
839

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

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

867
            if (retval) {
23,708✔
868
                *ts_out = ts.value();
2,363✔
869
                if (md[2]) {
2,363✔
870
                    *level_out = md[2];
246✔
871
                } else {
872
                    *level_out = line.substr(md[0]->sf_end);
2,117✔
873
                }
874
                if (curr_fmt != pat_index) {
2,363✔
875
                    uint32_t lock_line;
876

877
                    if (sbc.sbc_pattern_locks.empty()) {
2,254✔
878
                        lock_line = 0;
2,254✔
879
                    } else {
880
                        lock_line = line_number;
×
881
                    }
882

883
                    sbc.sbc_pattern_locks.pl_lines.emplace_back(lock_line,
2,254✔
884
                                                                curr_fmt);
885
                }
886
                this->lf_timestamp_flags = tm_out->et_flags;
2,363✔
887
                done = true;
2,363✔
888
            }
889
        }
890
    }
891

892
    return retval;
12,497✔
893
}
894

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

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

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

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

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

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

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

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

991
        if (this->jlu_read_order_index < format->elf_value_def_read_order.size()
298,698✔
992
            && format->elf_value_def_read_order[this->jlu_read_order_index]
402,140✔
993
                    .first
994
                == field_frag)
103,442✔
995
        {
996
            return format
997
                ->elf_value_def_read_order[this->jlu_read_order_index++]
98,935✔
998
                .second;
98,935✔
999
        }
1000

1001
        format->elf_value_def_read_order.resize(this->jlu_read_order_index);
199,763✔
1002
        auto vd_iter = format->elf_value_def_frag_map.find(field_frag);
199,763✔
1003
        if (vd_iter != format->elf_value_def_frag_map.end()) {
199,763✔
1004
            format->elf_value_def_read_order.emplace_back(vd_iter->first,
173,330✔
1005
                                                          vd_iter->second);
173,330✔
1006
            this->jlu_read_order_index += 1;
173,330✔
1007
            return vd_iter->second;
173,330✔
1008
        }
1009

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

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

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

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

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

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

1078
    return 1;
2,992✔
1079
}
1080

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

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

1090
    return 1;
14,244✔
1091
}
1092

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

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

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

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

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

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

1222
    return 1;
22,924✔
1223
}
1224

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

1231
    if (ypc->ypc_path_index_stack.size() == 2) {
46,754✔
1232
        const auto* vd = jlu->get_field_def(ypc);
14,977✔
1233

1234
        jlu->add_sub_lines_for(vd, true, std::nullopt, nullptr, -1, nullptr);
14,977✔
1235
        jlu->jlu_sub_start = yajl_get_bytes_consumed(jlu->jlu_handle) - 1;
14,977✔
1236
    }
1237

1238
    return 1;
46,754✔
1239
}
1240

1241
static int
1242
json_array_start_const(void* ctx)
13,523✔
1243
{
1244
    auto* ypc = (yajlpp_parse_context*) ctx;
13,523✔
1245
    auto* jlu = (json_log_userdata*) ypc->ypc_userdata;
13,523✔
1246

1247
    if (ypc->ypc_path_index_stack.size() == 2) {
13,523✔
1248
        jlu->jlu_sub_start = yajl_get_bytes_consumed(jlu->jlu_handle) - 1;
3,640✔
1249
    }
1250

1251
    return 1;
13,523✔
1252
}
1253

1254
static int
1255
json_array_end(void* ctx)
13,503✔
1256
{
1257
    auto* ypc = (yajlpp_parse_context*) ctx;
13,503✔
1258
    auto* jlu = (json_log_userdata*) ypc->ypc_userdata;
13,503✔
1259

1260
    if (ypc->ypc_path_index_stack.size() == 1) {
13,503✔
1261
        const intern_string_t field_name = ypc->get_path_fragment_i(0);
3,634✔
1262
        size_t sub_end = yajl_get_bytes_consumed(jlu->jlu_handle);
3,634✔
1263
        auto json_frag = string_fragment::from_byte_range(
3,634✔
1264
            jlu->jlu_shared_buffer.get_data(), jlu->jlu_sub_start, sub_end);
3,634✔
1265
        jlu->jlu_format->jlf_line_values.lvv_values.emplace_back(
3,634✔
1266
            jlu->jlu_format->get_value_meta(field_name,
7,268✔
1267
                                            value_kind_t::VALUE_JSON),
1268
            json_frag);
1269
        if (field_name == jlu->jlu_format->elf_opid_field) {
3,634✔
1270
            jlu->jlu_opid_desc_frag = json_frag;
28✔
1271
        }
1272
    }
1273

1274
    return 1;
13,503✔
1275
}
1276

1277
static int
1278
read_array_end(void* ctx)
46,561✔
1279
{
1280
    auto* ypc = (yajlpp_parse_context*) ctx;
46,561✔
1281
    auto* jlu = (json_log_userdata*) ypc->ypc_userdata;
46,561✔
1282

1283
    if (ypc->ypc_path_index_stack.size() == 1) {
46,561✔
1284
        const intern_string_t field_name = ypc->get_path_fragment_i(0);
14,952✔
1285
        size_t sub_end = yajl_get_bytes_consumed(jlu->jlu_handle);
14,952✔
1286
        auto json_frag = string_fragment::from_byte_range(
14,952✔
1287
            jlu->jlu_shared_buffer.get_data(), jlu->jlu_sub_start, sub_end);
14,952✔
1288
        if (field_name == jlu->jlu_format->elf_opid_field) {
14,952✔
1289
            jlu->jlu_opid_desc_frag = json_frag;
2,250✔
1290
        }
1291
    }
1292

1293
    return 1;
46,561✔
1294
}
1295

1296
static const json_path_container json_log_handlers = {
1297
    yajlpp::pattern_property_handler("\\w+")
1298
        .add_cb(read_json_null)
1299
        .add_cb(read_json_bool)
1300
        .add_cb(read_json_number)
1301
        .add_cb(read_json_field),
1302
};
1303

1304
static int rewrite_json_field(yajlpp_parse_context* ypc,
1305
                              const unsigned char* str,
1306
                              size_t len,
1307
                              yajl_string_props_t*);
1308

1309
static int
1310
rewrite_json_null(yajlpp_parse_context* ypc)
1,957✔
1311
{
1312
    auto jlu = (json_log_userdata*) ypc->ypc_userdata;
1,957✔
1313
    const auto* vd = jlu->get_field_def(ypc);
1,957✔
1314

1315
    if (!ypc->is_level(1) && vd == nullptr) {
1,957✔
1316
        return 1;
1,957✔
1317
    }
1318
    jlu->jlu_format->jlf_line_values.lvv_values.emplace_back(
×
1319
        jlu->jlu_format->get_value_meta(ypc, vd, value_kind_t::VALUE_NULL));
×
1320

1321
    return 1;
×
1322
}
1323

1324
static int
1325
rewrite_json_bool(yajlpp_parse_context* ypc, int val)
8,809✔
1326
{
1327
    auto* jlu = (json_log_userdata*) ypc->ypc_userdata;
8,809✔
1328
    const auto* vd = jlu->get_field_def(ypc);
8,809✔
1329

1330
    if (!ypc->is_level(1) && vd == nullptr) {
8,809✔
1331
        return 1;
7,861✔
1332
    }
1333
    jlu->jlu_format->jlf_line_values.lvv_values.emplace_back(
948✔
1334
        jlu->jlu_format->get_value_meta(ypc, vd, value_kind_t::VALUE_BOOLEAN),
948✔
1335
        (bool) val);
948✔
1336
    return 1;
948✔
1337
}
1338

1339
static int
1340
rewrite_json_int(yajlpp_parse_context* ypc, long long val)
10,858✔
1341
{
1342
    auto* jlu = (json_log_userdata*) ypc->ypc_userdata;
10,858✔
1343
    const auto* vd = jlu->get_field_def(ypc);
10,858✔
1344

1345
    if (vd != nullptr) {
10,858✔
1346
        const intern_string_t field_name = vd->vd_meta.lvm_name;
835✔
1347
        if (jlu->jlu_format->lf_timestamp_field == field_name) {
835✔
1348
            long long divisor = jlu->jlu_format->elf_timestamp_divisor;
25✔
1349
            timeval tv;
1350

1351
            tv.tv_sec = val / divisor;
25✔
1352
            tv.tv_usec = fmod(val, divisor) * (1000000.0 / divisor);
25✔
1353
            jlu->jlu_format->lf_date_time.to_localtime(tv.tv_sec,
25✔
1354
                                                       jlu->jlu_exttm);
25✔
1355
            jlu->jlu_exttm.et_gmtoff
1356
                = jlu->jlu_format->lf_date_time.dts_local_offset_cache;
25✔
1357
            jlu->jlu_exttm.et_flags |= ETF_MACHINE_ORIENTED
25✔
1358
                | ETF_SUB_NOT_IN_FORMAT | ETF_ZONE_SET | ETF_Z_FOR_UTC;
1359
            if (divisor == 1) {
25✔
1360
                jlu->jlu_exttm.et_flags |= ETF_MICROS_SET;
4✔
1361
            } else {
1362
                jlu->jlu_exttm.et_flags |= ETF_MILLIS_SET;
21✔
1363
            }
1364
            jlu->jlu_exttm.et_nsec = tv.tv_usec * 1000;
25✔
1365
        } else if (jlu->jlu_format->lf_subsecond_field == field_name) {
810✔
1366
            jlu->jlu_exttm.et_flags &= ~(ETF_MICROS_SET | ETF_MILLIS_SET);
4✔
1367
            switch (jlu->jlu_format->lf_subsecond_unit.value()) {
4✔
1368
                case log_format::subsecond_unit::milli:
×
1369
                    jlu->jlu_exttm.et_nsec = val * 1000000;
×
1370
                    jlu->jlu_exttm.et_flags |= ETF_MILLIS_SET;
×
1371
                    break;
×
1372
                case log_format::subsecond_unit::micro:
×
1373
                    jlu->jlu_exttm.et_nsec = val * 1000;
×
1374
                    jlu->jlu_exttm.et_flags |= ETF_MICROS_SET;
×
1375
                    break;
×
1376
                case log_format::subsecond_unit::nano:
4✔
1377
                    jlu->jlu_exttm.et_nsec = val;
4✔
1378
                    jlu->jlu_exttm.et_flags |= ETF_NANOS_SET;
4✔
1379
                    break;
4✔
1380
            }
1381
            jlu->jlu_exttm.et_flags |= ETF_SUB_NOT_IN_FORMAT;
4✔
1382
        }
1383
    }
1384

1385
    if (!ypc->is_level(1) && vd == nullptr) {
10,858✔
1386
        return 1;
9,976✔
1387
    }
1388
    if (vd != nullptr
882✔
1389
        && vd->vd_meta.lvm_name == jlu->jlu_format->elf_thread_id_field)
882✔
1390
    {
1391
        jlu->jlu_tid_number = val;
142✔
1392
    }
1393
    jlu->jlu_format->jlf_line_values.lvv_values.emplace_back(
882✔
1394
        jlu->jlu_format->get_value_meta(ypc, vd, value_kind_t::VALUE_INTEGER),
882✔
1395
        (int64_t) val);
882✔
1396
    return 1;
882✔
1397
}
1398

1399
static int
1400
rewrite_json_double(yajlpp_parse_context* ypc, double val)
162✔
1401
{
1402
    auto* jlu = (json_log_userdata*) ypc->ypc_userdata;
162✔
1403
    const auto* vd = jlu->get_field_def(ypc);
162✔
1404

1405
    if (vd != nullptr) {
162✔
1406
        const intern_string_t field_name = vd->vd_meta.lvm_name;
158✔
1407
        if (jlu->jlu_format->lf_timestamp_field == field_name) {
158✔
1408
            long long divisor = jlu->jlu_format->elf_timestamp_divisor;
130✔
1409
            timeval tv;
1410

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

1448
    if (!ypc->is_level(1) && vd == nullptr) {
162✔
1449
        return 1;
×
1450
    }
1451
    jlu->jlu_format->jlf_line_values.lvv_values.emplace_back(
162✔
1452
        jlu->jlu_format->get_value_meta(ypc, vd, value_kind_t::VALUE_FLOAT),
324✔
1453
        val);
1454

1455
    return 1;
162✔
1456
}
1457

1458
static const json_path_container json_log_rewrite_handlers = {
1459
    yajlpp::pattern_property_handler("\\w+")
1460
        .add_cb(rewrite_json_null)
1461
        .add_cb(rewrite_json_bool)
1462
        .add_cb(rewrite_json_int)
1463
        .add_cb(rewrite_json_double)
1464
        .add_cb(rewrite_json_field),
1465
};
1466

1467
bool
1468
external_log_format::scan_for_partial(const log_format_file_state& lffs,
1✔
1469
                                      shared_buffer_ref& sbr,
1470
                                      size_t& len_out) const
1471
{
1472
    if (this->elf_type != elf_type_t::ELF_TYPE_TEXT) {
1✔
1473
        return false;
×
1474
    }
1475

1476
    const auto& pat
1477
        = this->elf_pattern_order[lffs.lffs_pattern_locks.last_pattern_index()];
1✔
1478
    if (!this->lf_multiline) {
1✔
1479
        len_out = pat->p_pcre.pp_value->match_partial(sbr.to_string_fragment());
1✔
1480
        return true;
1✔
1481
    }
1482

1483
    if (pat->p_timestamp_end == -1 || pat->p_timestamp_end > (int) sbr.length())
×
1484
    {
1485
        len_out = 0;
×
1486
        return false;
×
1487
    }
1488

1489
    len_out = pat->p_pcre.pp_value->match_partial(sbr.to_string_fragment());
×
1490
    return (int) len_out > pat->p_timestamp_end;
×
1491
}
1492

1493
std::vector<lnav::console::snippet>
1494
external_log_format::get_snippets() const
23✔
1495
{
1496
    std::vector<lnav::console::snippet> retval;
23✔
1497

1498
    for (const auto& src_pair : this->elf_format_sources) {
46✔
1499
        retval.emplace_back(lnav::console::snippet::from(src_pair.first, "")
46✔
1500
                                .with_line(src_pair.second));
23✔
1501
    }
1502

1503
    return retval;
23✔
1504
}
×
1505

1506
log_format::scan_result_t
1507
external_log_format::scan_json(std::vector<logline>& dst,
140,351✔
1508
                               const line_info& li,
1509
                               shared_buffer_ref& sbr,
1510
                               scan_batch_context& sbc)
1511
{
1512
    logline ll(
1513
        li.li_file_range.fr_offset, std::chrono::microseconds{0}, LEVEL_INFO);
140,351✔
1514
    auto line_frag = sbr.to_string_fragment();
140,351✔
1515

1516
    if (!line_frag.startswith("{")) {
140,351✔
1517
        if (!this->lf_specialized) {
131,257✔
1518
            return scan_no_match{"line is not a JSON object"};
131,252✔
1519
        }
1520

1521
        ll.set_time(dst.back().get_time<std::chrono::microseconds>());
5✔
1522
        ll.set_level(LEVEL_INVALID);
5✔
1523
        dst.emplace_back(ll);
5✔
1524
        return scan_match{0};
5✔
1525
    }
1526

1527
    auto& ypc = *(this->jlf_parse_context);
9,094✔
1528
    yajl_handle handle = this->jlf_yajl_handle.get();
9,094✔
1529
    json_log_userdata jlu(sbr, &sbc);
9,094✔
1530

1531
    if (li.li_partial) {
9,094✔
1532
        log_debug("skipping partial line at offset %lld",
×
1533
                  li.li_file_range.fr_offset);
1534
        if (this->lf_specialized) {
×
1535
            if (!dst.empty()) {
×
1536
                ll.set_time(dst.back().get_time<std::chrono::microseconds>());
×
1537
            }
1538
            ll.set_level(LEVEL_INVALID);
×
1539
            dst.emplace_back(ll);
×
1540
        }
1541
        return scan_incomplete{};
×
1542
    }
1543

1544
    const auto* line_data = (const unsigned char*) sbr.get_data();
9,094✔
1545

1546
    this->lf_desc_captures.clear();
9,094✔
1547
    this->lf_desc_allocator.reset();
9,094✔
1548

1549
    yajl_reset(handle);
9,094✔
1550
    ypc.set_static_handler(json_log_handlers.jpc_children[0]);
9,094✔
1551
    ypc.ypc_userdata = &jlu;
9,094✔
1552
    ypc.ypc_ignore_unused = true;
9,094✔
1553
    ypc.ypc_alt_callbacks.yajl_start_array = json_array_start;
9,094✔
1554
    ypc.ypc_alt_callbacks.yajl_start_map = json_array_start;
9,094✔
1555
    ypc.ypc_alt_callbacks.yajl_end_array = read_array_end;
9,094✔
1556
    ypc.ypc_alt_callbacks.yajl_end_map = read_array_end;
9,094✔
1557
    jlu.jlu_format = this;
9,094✔
1558
    jlu.jlu_base_line = &ll;
9,094✔
1559
    jlu.jlu_line_value = sbr.get_data();
9,094✔
1560
    jlu.jlu_line_size = sbr.length();
9,094✔
1561
    jlu.jlu_handle = handle;
9,094✔
1562
    jlu.jlu_format_hits.resize(this->jlf_line_format.size());
9,094✔
1563
    if (yajl_parse(handle, line_data, sbr.length()) == yajl_status_ok
9,094✔
1564
        && yajl_complete_parse(handle) == yajl_status_ok)
9,094✔
1565
    {
1566
        if (ll.get_time<std::chrono::microseconds>().count() == 0) {
8,926✔
1567
            if (this->lf_specialized) {
5,725✔
1568
                if (!dst.empty()) {
×
1569
                    ll.set_time(
×
1570
                        dst.back().get_time<std::chrono::microseconds>());
×
1571
                }
1572
                ll.set_ignore(true);
×
1573
                dst.emplace_back(ll);
×
1574
                return scan_match{0};
×
1575
            }
1576

1577
            return scan_no_match{
5,725✔
1578
                "JSON message does not have expected timestamp property"};
5,725✔
1579
        }
1580

1581
        if (jlu.jlu_tid_frag) {
3,201✔
1582
            this->jlf_line_values.lvv_thread_id_value
1583
                = jlu.jlu_tid_frag->to_string();
102✔
1584
            auto tid_iter = sbc.sbc_tids.insert_tid(
204✔
1585
                sbc.sbc_allocator,
1586
                jlu.jlu_tid_frag.value(),
102✔
1587
                ll.get_time<std::chrono::microseconds>());
102✔
1588
            tid_iter->second.titr_level_stats.update_msg_count(
102✔
1589
                ll.get_msg_level());
1590
        } else {
1591
            auto tid_iter = sbc.sbc_tids.insert_tid(
3,099✔
1592
                sbc.sbc_allocator,
1593
                string_fragment{},
×
1594
                ll.get_time<std::chrono::microseconds>());
3,099✔
1595
            tid_iter->second.titr_level_stats.update_msg_count(
3,099✔
1596
                ll.get_msg_level());
1597
        }
1598

1599
        auto found_opid_desc = false;
3,201✔
1600
        if (this->elf_opid_field.empty()
3,201✔
1601
            && this->lf_opid_source.value_or(opid_source_t::from_description)
742✔
1602
                == opid_source_t::from_description
1603
            && this->lf_opid_description_def->size() == 1)
3,943✔
1604
        {
1605
            const auto& od = this->lf_opid_description_def->begin()->second;
322✔
1606
            for (const auto& desc : *od.od_descriptors) {
966✔
1607
                auto desc_iter
1608
                    = this->lf_desc_captures.find(desc.od_field.pp_value);
644✔
1609
                if (desc_iter == this->lf_desc_captures.end()) {
644✔
1610
                    continue;
602✔
1611
                }
1612
                jlu.jlu_opid_hasher.update(desc_iter->second);
42✔
1613
                found_opid_desc = true;
42✔
1614
            }
1615

1616
        } else if (!jlu.jlu_opid_desc_frag && !jlu.jlu_opid_frag
3,508✔
1617
                   && jlu.jlu_duration > 0us)
3,508✔
1618
        {
1619
            jlu.jlu_opid_hasher.update(sbr.to_string_fragment());
×
1620
        }
1621

1622
        if (jlu.jlu_opid_desc_frag || jlu.jlu_duration > 0us
4,152✔
1623
            || (found_opid_desc && this->lf_opid_description_def->size() == 1))
4,152✔
1624
        {
1625
            char buf[hasher::STRING_SIZE];
1626
            jlu.jlu_opid_hasher.to_string(buf);
2,271✔
1627
            auto opid_frag = string_fragment::from_bytes(buf, sizeof(buf) - 1);
2,271✔
1628
            jlu.jlu_base_line->set_opid(opid_frag.hash());
2,271✔
1629
            auto opid_iter = sbc.sbc_opids.los_opid_ranges.find(opid_frag);
2,271✔
1630
            if (opid_iter == sbc.sbc_opids.los_opid_ranges.end()) {
2,271✔
1631
                jlu.jlu_opid_frag = opid_frag.to_owned(sbc.sbc_allocator);
2,259✔
1632
            } else {
1633
                jlu.jlu_opid_frag = opid_iter->first;
12✔
1634
            }
1635
        }
1636

1637
        if (jlu.jlu_opid_frag) {
3,201✔
1638
            this->jlf_line_values.lvv_opid_value
1639
                = jlu.jlu_opid_frag->to_string();
2,435✔
1640
            this->jlf_line_values.lvv_opid_provenance
1641
                = logline_value_vector::opid_provenance::file;
2,435✔
1642
            auto opid_iter = sbc.sbc_opids.insert_op(
4,870✔
1643
                sbc.sbc_allocator,
1644
                jlu.jlu_opid_frag.value(),
2,435✔
1645
                ll.get_time<std::chrono::microseconds>(),
2,435✔
1646
                this->lf_timestamp_point_of_reference,
1647
                jlu.jlu_duration);
1648
            opid_iter->second.otr_level_stats.update_msg_count(
2,435✔
1649
                ll.get_msg_level());
1650
            auto& elems = opid_iter->second.otr_description.lod_elements;
2,435✔
1651
            if (jlu.jlu_opid_desc_frag && elems.empty()) {
2,435✔
1652
                elems.insert(0,
×
1653
                             fmt::format(FMT_STRING(" {}"),
8,976✔
1654
                                         jlu.jlu_opid_desc_frag.value()));
1655
            }
1656

1657
            if (jlu.jlu_subid) {
2,435✔
1658
                auto subid_frag
1659
                    = string_fragment::from_str(jlu.jlu_subid.value());
×
1660

1661
                auto* ostr = sbc.sbc_opids.sub_op_in_use(
×
1662
                    sbc.sbc_allocator,
1663
                    opid_iter,
1664
                    subid_frag,
1665
                    ll.get_time<std::chrono::microseconds>(),
×
1666
                    ll.get_msg_level());
1667
                if (ostr != nullptr && ostr->ostr_description.empty()) {
×
1668
                    log_op_description sub_desc;
×
1669
                    this->update_op_description(
×
1670
                        *this->lf_subid_description_def_vec, sub_desc);
×
1671
                    if (!sub_desc.lod_elements.empty()) {
×
1672
                        auto& sub_desc_def
1673
                            = this->lf_subid_description_def_vec->at(
×
1674
                                sub_desc.lod_index.value());
×
1675
                        ostr->ostr_description
1676
                            = sub_desc_def->to_string(sub_desc.lod_elements);
×
1677
                    }
1678
                }
1679
            }
1680

1681
            auto& otr = opid_iter->second;
2,435✔
1682
            this->update_op_description(*this->lf_opid_description_def_vec,
2,435✔
1683
                                        otr.otr_description);
2,435✔
1684
        } else {
1685
            this->jlf_line_values.lvv_opid_value = std::nullopt;
766✔
1686
        }
1687

1688
        jlu.jlu_sub_line_count += this->jlf_line_format_init_count;
3,201✔
1689
        for (int lpc = 0; lpc < jlu.jlu_sub_line_count; lpc++) {
16,911✔
1690
            ll.set_sub_offset(lpc);
13,710✔
1691
            if (lpc > 0) {
13,710✔
1692
                ll.set_level(
10,509✔
1693
                    (log_level_t) (ll.get_level_and_flags() | LEVEL_CONTINUED));
10,509✔
1694
            }
1695
            ll.set_has_ansi(jlu.jlu_has_ansi);
13,710✔
1696
            ll.set_valid_utf(jlu.jlu_valid_utf);
13,710✔
1697
            dst.emplace_back(ll);
13,710✔
1698
        }
1699

1700
        if (!this->lf_specialized) {
3,201✔
1701
            static const intern_string_t ts_field
1702
                = intern_string::lookup("__timestamp__", -1);
2,668✔
1703
            static const intern_string_t level_field
1704
                = intern_string::lookup("__level__");
4,160✔
1705
            for (const auto& [index, jfe] :
25,137✔
1706
                 lnav::itertools::enumerate(this->jlf_line_format))
27,805✔
1707
            {
1708
                if (jfe.jfe_type != json_log_field::VARIABLE
59,526✔
1709
                    || jfe.jfe_value.pp_value == ts_field
14,707✔
1710
                    || jfe.jfe_value.pp_value == level_field
12,109✔
1711
                    || jfe.jfe_default_value != "-")
37,176✔
1712
                {
1713
                    continue;
14,588✔
1714
                }
1715
                if (!jlu.jlu_format_hits[index]) {
7,881✔
1716
                    jlu.jlu_strikes += 1;
7,398✔
1717
                }
1718
            }
1719
        }
1720
    } else {
1721
        unsigned char* msg;
1722
        int line_count = 2;
168✔
1723

1724
        msg = yajl_get_error(
336✔
1725
            handle, 1, (const unsigned char*) sbr.get_data(), sbr.length());
168✔
1726
        if (msg != nullptr) {
168✔
1727
            auto msg_frag = string_fragment::from_c_str(msg);
168✔
1728
            log_debug("Unable to parse line at offset %lld: %.*s",
168✔
1729
                      li.li_file_range.fr_offset,
1730
                      msg_frag.length(),
1731
                      msg_frag.data());
1732
            line_count = msg_frag.count('\n') + 1;
168✔
1733
            yajl_free_error(handle, msg);
168✔
1734
        }
1735
        if (!this->lf_specialized) {
168✔
1736
            return scan_no_match{"JSON parsing failed"};
165✔
1737
        }
1738
        for (int lpc = 0; lpc < line_count; lpc++) {
15✔
1739
            log_level_t level = LEVEL_INVALID;
12✔
1740

1741
            ll.set_time(dst.back().get_timeval());
12✔
1742
            if (lpc > 0) {
12✔
1743
                level = (log_level_t) (level | LEVEL_CONTINUED);
9✔
1744
            }
1745
            ll.set_level(level);
12✔
1746
            ll.set_sub_offset(lpc);
12✔
1747
            dst.emplace_back(ll);
12✔
1748
        }
1749
    }
1750

1751
    if (jlu.jlu_quality > 0) {
3,204✔
1752
        jlu.jlu_quality += 3000;
834✔
1753
    }
1754
    return scan_match{jlu.jlu_quality, jlu.jlu_strikes};
3,204✔
1755
}
9,094✔
1756

1757
log_format::scan_result_t
1758
external_log_format::scan(logfile& lf,
824,563✔
1759
                          std::vector<logline>& dst,
1760
                          const line_info& li,
1761
                          shared_buffer_ref& sbr,
1762
                          scan_batch_context& sbc)
1763
{
1764
    if (dst.empty()) {
824,563✔
1765
        auto file_options = lf.get_file_options();
32,164✔
1766

1767
        if (file_options) {
32,164✔
1768
            this->lf_date_time.dts_default_zone
1769
                = file_options->second.fo_default_zone.pp_value;
2,220✔
1770
        } else {
1771
            this->lf_date_time.dts_default_zone = nullptr;
29,944✔
1772
        }
1773
    }
32,164✔
1774

1775
    sbc.sbc_value_stats.resize(this->elf_value_defs.size());
824,563✔
1776
    if (this->elf_type == elf_type_t::ELF_TYPE_JSON) {
824,563✔
1777
        return this->scan_json(dst, li, sbr, sbc);
138,111✔
1778
    }
1779

1780
    int curr_fmt = -1, orig_lock = sbc.sbc_pattern_locks.last_pattern_index();
686,452✔
1781
    int pat_index = orig_lock;
686,452✔
1782
    auto line_sf = sbr.to_string_fragment();
686,452✔
1783
    thread_local auto md = lnav::pcre2pp::match_data::unitialized();
686,452✔
1784
    char tmp_opid_buf[hasher::STRING_SIZE];
1785

1786
    while (::next_format(this->elf_pattern_order, curr_fmt, pat_index)) {
2,270,563✔
1787
        auto* fpat = this->elf_pattern_order[curr_fmt].get();
1,589,942✔
1788
        auto* pat = fpat->p_pcre.pp_value.get();
1,589,942✔
1789

1790
        auto found_match
1791
            = pat->capture_from(line_sf).into(md).found_p(PCRE2_NO_UTF_CHECK);
1,589,942✔
1792
        if (!found_match) {
1,589,942✔
1793
            if (!sbc.sbc_pattern_locks.empty() && pat_index != -1) {
1,584,108✔
1794
                curr_fmt = -1;
1,994✔
1795
                pat_index = -1;
1,994✔
1796
            }
1797
            continue;
1,584,111✔
1798
        }
1799

1800
        auto ts = md[fpat->p_timestamp_field_index];
5,834✔
1801
        auto level_cap = md[fpat->p_level_field_index];
5,834✔
1802
        auto opid_cap = md[fpat->p_opid_field_index];
5,834✔
1803
        const char* last;
1804
        exttm log_time_tm;
5,834✔
1805
        timeval log_tv;
1806
        uint16_t opid = 0;
5,834✔
1807
        char combined_datetime_buf[512];
1808

1809
        if (fpat->p_time_field_index != -1) {
5,834✔
1810
            auto time_cap = md[fpat->p_time_field_index];
×
1811
            if (ts && time_cap) {
×
1812
                auto ts_str_len = snprintf(combined_datetime_buf,
×
1813
                                           sizeof(combined_datetime_buf),
1814
                                           "%.*sT%.*s",
1815
                                           ts->length(),
1816
                                           ts->data(),
1817
                                           time_cap->length(),
1818
                                           time_cap->data());
1819
                ts = string_fragment::from_bytes(combined_datetime_buf,
×
1820
                                                 ts_str_len);
×
1821
            }
1822
        }
1823

1824
        auto level = this->convert_level(
5,834✔
1825
            level_cap.value_or(string_fragment::invalid()), &sbc);
5,834✔
1826

1827
        if (!ts) {
5,834✔
1828
            level = log_level_t::LEVEL_INVALID;
×
1829
        } else if ((last
5,834✔
1830
                    = this->lf_date_time.scan(ts->data(),
11,668✔
1831
                                              ts->length(),
5,834✔
1832
                                              this->get_timestamp_formats(),
1833
                                              &log_time_tm,
1834
                                              log_tv))
1835
                   == nullptr)
5,834✔
1836
        {
1837
            auto ls = this->lf_date_time.unlock();
13✔
1838
            if ((last = this->lf_date_time.scan(ts->data(),
26✔
1839
                                                ts->length(),
13✔
1840
                                                this->get_timestamp_formats(),
1841
                                                &log_time_tm,
1842
                                                log_tv))
1843
                == nullptr)
13✔
1844
            {
1845
                this->lf_date_time.relock(ls);
2✔
1846
                continue;
3✔
1847
            }
1848
            if (last != nullptr) {
11✔
1849
                auto old_flags = this->lf_timestamp_flags & DATE_TIME_SET_FLAGS;
11✔
1850
                auto new_flags = log_time_tm.et_flags & DATE_TIME_SET_FLAGS;
11✔
1851

1852
                // It is unlikely a valid timestamp would lose much
1853
                // precision.
1854
                if (new_flags != old_flags) {
11✔
1855
                    continue;
1✔
1856
                }
1857
            }
1858

1859
            log_debug("%s:%zu: date-time re-locked to %d",
10✔
1860
                      lf.get_unique_path().c_str(),
1861
                      dst.size(),
1862
                      this->lf_date_time.dts_fmt_lock);
1863
        }
1864

1865
        this->lf_timestamp_flags = log_time_tm.et_flags;
5,831✔
1866

1867
        if (!(this->lf_timestamp_flags
11,662✔
1868
              & (ETF_MILLIS_SET | ETF_MICROS_SET | ETF_NANOS_SET))
5,831✔
1869
            && !dst.empty()
5,287✔
1870
            && dst.back().get_time<std::chrono::seconds>().count()
4,548✔
1871
                == log_tv.tv_sec
4,548✔
1872
            && dst.back()
14,554✔
1873
                    .get_subsecond_time<std::chrono::milliseconds>()
9,267✔
1874
                    .count()
3,436✔
1875
                != 0)
1876
        {
1877
            auto log_ms
1878
                = dst.back().get_subsecond_time<std::chrono::microseconds>();
×
1879

1880
            log_time_tm.et_nsec
1881
                = std::chrono::duration_cast<std::chrono::nanoseconds>(log_ms)
×
1882
                      .count();
×
1883
            log_tv.tv_usec
1884
                = std::chrono::duration_cast<std::chrono::microseconds>(log_ms)
×
1885
                      .count();
×
1886
        }
1887

1888
        if (!((log_time_tm.et_flags & ETF_DAY_SET)
5,831✔
1889
              && (log_time_tm.et_flags & ETF_MONTH_SET)
5,790✔
1890
              && (log_time_tm.et_flags & ETF_YEAR_SET)))
5,790✔
1891
        {
1892
            this->check_for_new_year(dst, log_time_tm, log_tv);
794✔
1893
        }
1894

1895
        auto log_us = to_us(log_tv);
5,831✔
1896
        if (!fpat->p_opid_description_field_indexes.empty()) {
5,831✔
1897
            hasher h;
4,504✔
1898
            for (auto& fidx : fpat->p_opid_description_field_indexes) {
13,009✔
1899
                auto desc_cap = md[fidx];
8,505✔
1900
                if (desc_cap) {
8,505✔
1901
                    h.update(desc_cap.value());
8,458✔
1902
                }
1903
            }
1904
            h.to_string(tmp_opid_buf);
4,504✔
1905
            opid_cap = string_fragment::from_bytes(tmp_opid_buf,
9,008✔
1906
                                                   sizeof(tmp_opid_buf) - 1);
4,504✔
1907
        }
1908

1909
        auto duration_cap = md[fpat->p_duration_field_index];
5,831✔
1910
        if (duration_cap && !opid_cap) {
5,831✔
1911
            hasher h;
86✔
1912
            h.update(line_sf);
86✔
1913
            h.to_string(tmp_opid_buf);
86✔
1914
            opid_cap = string_fragment::from_bytes(tmp_opid_buf,
172✔
1915
                                                   sizeof(tmp_opid_buf) - 1);
86✔
1916
        }
1917
        if (opid_cap && !opid_cap->empty()) {
5,831✔
1918
            auto duration = std::chrono::microseconds{0};
5,385✔
1919
            if (duration_cap) {
5,385✔
1920
                auto from_res
1921
                    = humanize::try_from<double>(duration_cap.value());
86✔
1922
                if (from_res) {
86✔
1923
                    duration = std::chrono::microseconds(
×
1924
                        static_cast<int64_t>(from_res.value() * 1000000));
86✔
1925
                }
1926
            }
1927
            auto opid_iter
1928
                = sbc.sbc_opids.insert_op(sbc.sbc_allocator,
10,770✔
1929
                                          opid_cap.value(),
5,385✔
1930
                                          log_us,
1931
                                          this->lf_timestamp_point_of_reference,
1932
                                          duration);
1933
            auto& otr = opid_iter->second;
5,385✔
1934

1935
            otr.otr_level_stats.update_msg_count(level);
5,385✔
1936
            if (fpat->p_subid_field_index != -1) {
5,385✔
1937
                auto subid_cap = md[fpat->p_subid_field_index];
64✔
1938
                if (subid_cap && !subid_cap->empty()) {
64✔
1939
                    auto* ostr = sbc.sbc_opids.sub_op_in_use(sbc.sbc_allocator,
192✔
1940
                                                             opid_iter,
1941
                                                             subid_cap.value(),
64✔
1942
                                                             log_us,
1943
                                                             level);
1944
                    if (ostr != nullptr && ostr->ostr_description.empty()) {
64✔
1945
                        log_op_description sub_desc;
43✔
1946
                        this->update_op_description(
43✔
1947
                            *this->lf_subid_description_def_vec,
43✔
1948
                            sub_desc,
1949
                            fpat,
1950
                            md);
1951
                        if (!sub_desc.lod_elements.empty()) {
43✔
1952
                            auto& sub_desc_def
1953
                                = this->lf_subid_description_def_vec->at(
39✔
1954
                                    sub_desc.lod_index.value());
39✔
1955
                            ostr->ostr_description = sub_desc_def->to_string(
78✔
1956
                                sub_desc.lod_elements);
39✔
1957
                        }
1958
                    }
43✔
1959
                }
1960
            }
1961
            this->update_op_description(*this->lf_opid_description_def_vec,
5,385✔
1962
                                        otr.otr_description,
5,385✔
1963
                                        fpat,
1964
                                        md);
1965
            opid = opid_cap->hash();
5,385✔
1966
        }
1967

1968
        for (const auto& ivd : fpat->p_value_by_index) {
66,081✔
1969
            if (!ivd.ivd_value_def->vd_meta.lvm_values_index) {
60,250✔
1970
                continue;
11,000✔
1971
            }
1972

1973
            ssize_t cap_size = md.capture_size(ivd.ivd_index);
49,250✔
1974
            auto& lvs = sbc.sbc_value_stats[ivd.ivd_value_def->vd_meta
49,250✔
1975
                                                .lvm_values_index.value()];
49,250✔
1976

1977
            if (cap_size > lvs.lvs_width) {
49,250✔
1978
                lvs.lvs_width = cap_size;
6,311✔
1979
            }
1980
        }
1981

1982
        for (auto value_index : fpat->p_numeric_value_indexes) {
10,671✔
1983
            const indexed_value_def& ivd = fpat->p_value_by_index[value_index];
4,840✔
1984
            const value_def& vd = *ivd.ivd_value_def;
4,840✔
1985
            auto num_cap = md[ivd.ivd_index];
4,840✔
1986

1987
            if (num_cap && num_cap->is_valid()) {
4,840✔
1988
                const struct scaling_factor* scaling = nullptr;
4,796✔
1989

1990
                if (ivd.ivd_unit_field_index >= 0) {
4,796✔
1991
                    auto unit_cap = md[ivd.ivd_unit_field_index];
80✔
1992

1993
                    if (unit_cap && unit_cap->is_valid()) {
80✔
1994
                        intern_string_t unit_val
1995
                            = intern_string::lookup(unit_cap.value());
80✔
1996

1997
                        auto unit_iter = vd.vd_unit_scaling.find(unit_val);
80✔
1998
                        if (unit_iter != vd.vd_unit_scaling.end()) {
80✔
1999
                            const auto& sf = unit_iter->second;
80✔
2000

2001
                            scaling = &sf;
80✔
2002
                        }
2003
                    }
2004
                }
2005

2006
                std::optional<double> dvalue_opt;
4,796✔
2007
                switch (vd.vd_meta.lvm_kind) {
4,796✔
2008
                    case value_kind_t::VALUE_INTEGER: {
4,630✔
2009
                        auto scan_res
2010
                            = scn::scan_int<int64_t>(num_cap->to_string_view());
4,630✔
2011
                        if (scan_res) {
4,630✔
2012
                            dvalue_opt = scan_res->value();
4,630✔
2013
                        }
2014
                        break;
4,630✔
2015
                    }
2016
                    case value_kind_t::VALUE_FLOAT: {
166✔
2017
                        auto scan_res = scn::scan_value<double>(
2018
                            num_cap->to_string_view());
166✔
2019
                        if (scan_res) {
166✔
2020
                            dvalue_opt = scan_res->value();
166✔
2021
                        }
2022
                        break;
166✔
2023
                    }
2024
                    default:
×
2025
                        break;
×
2026
                }
2027
                if (dvalue_opt) {
4,796✔
2028
                    auto dvalue = dvalue_opt.value();
4,796✔
2029
                    if (scaling != nullptr) {
4,796✔
2030
                        scaling->scale(dvalue);
80✔
2031
                    }
2032
                    sbc.sbc_value_stats[vd.vd_meta.lvm_values_index.value()]
4,796✔
2033
                        .add_value(dvalue);
4,796✔
2034
                }
2035
            }
2036
        }
2037

2038
        dst.emplace_back(li.li_file_range.fr_offset, log_us, level, opid);
5,831✔
2039

2040
        auto src_file_cap = md[fpat->p_src_file_field_index];
5,831✔
2041
        auto src_line_cap = md[fpat->p_src_line_field_index];
5,831✔
2042
        if (src_file_cap && src_line_cap) {
5,831✔
2043
            auto h = hasher();
94✔
2044
            h.update(this->get_name().c_str());
94✔
2045
            h.update(src_file_cap.value());
94✔
2046
            h.update(src_line_cap.value());
94✔
2047
            dst.back().set_schema(h.to_array());
94✔
2048
        }
2049
        auto thread_id_cap = md[fpat->p_thread_id_field_index];
5,831✔
2050
        if (thread_id_cap) {
5,831✔
2051
            auto tid_iter = sbc.sbc_tids.insert_tid(
1,994✔
2052
                sbc.sbc_allocator, thread_id_cap.value(), log_us);
997✔
2053
            tid_iter->second.titr_level_stats.update_msg_count(level);
997✔
2054
        } else {
2055
            auto tid_iter = sbc.sbc_tids.insert_tid(
4,834✔
2056
                sbc.sbc_allocator, string_fragment{}, log_us);
×
2057
            tid_iter->second.titr_level_stats.update_msg_count(level);
4,834✔
2058
        }
2059

2060
        if (orig_lock != curr_fmt) {
5,831✔
2061
            uint32_t lock_line;
2062

2063
            if (!this->lf_specialized && orig_lock != -1) {
601✔
2064
                log_debug("%s:%zu: changing pattern lock %d -> (%d)%s",
×
2065
                          lf.get_unique_path().c_str(),
2066
                          dst.size() - 1,
2067
                          orig_lock,
2068
                          curr_fmt,
2069
                          this->elf_pattern_order[curr_fmt]->p_name.c_str());
2070
            }
2071
            if (sbc.sbc_pattern_locks.empty()) {
601✔
2072
                lock_line = 0;
575✔
2073
            } else {
2074
                lock_line = dst.size() - 1;
26✔
2075
            }
2076
            sbc.sbc_pattern_locks.pl_lines.emplace_back(lock_line, curr_fmt);
601✔
2077
        }
2078
        return scan_match{1000};
5,831✔
2079
    }
2080

2081
    if (this->lf_specialized && !this->lf_multiline && !dst.empty()) {
680,621✔
2082
        const auto& last_line = dst.back();
1✔
2083

2084
        log_debug("%s: invalid line %zu file_offset=%" PRIu64,
1✔
2085
                  lf.get_filename().c_str(),
2086
                  dst.size(),
2087
                  li.li_file_range.fr_offset);
2088
        dst.emplace_back(li.li_file_range.fr_offset,
1✔
2089
                         last_line.get_timeval(),
×
2090
                         log_level_t::LEVEL_INVALID);
1✔
2091

2092
        return scan_match{0};
1✔
2093
    }
2094

2095
    return scan_no_match{"no patterns matched"};
680,620✔
2096
}
2097

2098
void
2099
external_log_format::annotate(logfile* lf,
7,386✔
2100
                              uint64_t line_number,
2101
                              string_attrs_t& sa,
2102
                              logline_value_vector& values) const
2103
{
2104
    thread_local auto md = lnav::pcre2pp::match_data::unitialized();
7,386✔
2105

2106
    auto& line = values.lvv_sbr;
7,386✔
2107
    line_range lr;
7,386✔
2108

2109
    line.erase_ansi();
7,386✔
2110
    if (this->elf_type != elf_type_t::ELF_TYPE_TEXT) {
7,386✔
2111
        if (this->jlf_cached_opts.full_message) {
763✔
2112
            values = this->jlf_line_values;
319✔
2113
            sa = this->jlf_line_attrs;
319✔
2114
        } else {
2115
            values.lvv_sbr = this->jlf_line_values.lvv_sbr.clone();
444✔
2116
            for (const auto& llv : this->jlf_line_values.lvv_values) {
4,533✔
2117
                if (this->jlf_cached_sub_range.contains(llv.lv_origin)) {
4,089✔
2118
                    values.lvv_values.emplace_back(llv);
947✔
2119
                    values.lvv_values.back().lv_origin.shift(
947✔
2120
                        this->jlf_cached_sub_range.lr_start,
947✔
2121
                        -this->jlf_cached_sub_range.lr_start);
947✔
2122
                }
2123
            }
2124
            for (const auto& attr : this->jlf_line_attrs) {
2,504✔
2125
                if (this->jlf_cached_sub_range.contains(attr.sa_range)) {
2,060✔
2126
                    sa.emplace_back(attr);
709✔
2127
                    sa.back().sa_range.shift(
709✔
2128
                        this->jlf_cached_sub_range.lr_start,
709✔
2129
                        -this->jlf_cached_sub_range.lr_start);
709✔
2130
                }
2131
            }
2132
            values.lvv_opid_value = this->jlf_line_values.lvv_opid_value;
444✔
2133
            values.lvv_opid_provenance
2134
                = this->jlf_line_values.lvv_opid_provenance;
444✔
2135
            values.lvv_thread_id_value
2136
                = this->jlf_line_values.lvv_thread_id_value;
444✔
2137
        }
2138
        log_format::annotate(lf, line_number, sa, values);
763✔
2139
        return;
2,386✔
2140
    }
2141

2142
    if (line.empty()) {
6,623✔
2143
        return;
5✔
2144
    }
2145

2146
    values.lvv_values.reserve(this->elf_value_defs.size());
6,618✔
2147

2148
    auto lffs = lf->get_format_file_state();
6,618✔
2149
    int pat_index = lffs.lffs_pattern_locks.pattern_index_for_line(line_number);
6,618✔
2150
    const auto& pat = *this->elf_pattern_order[pat_index];
6,618✔
2151
    char tmp_opid_buf[hasher::STRING_SIZE];
2152

2153
    sa.reserve(pat.p_pcre.pp_value->get_capture_count());
6,618✔
2154
    auto match_res
2155
        = pat.p_pcre.pp_value->capture_from(line.to_string_fragment())
6,618✔
2156
              .into(md)
6,618✔
2157
              .matches(PCRE2_NO_UTF_CHECK)
13,236✔
2158
              .ignore_error();
6,618✔
2159
    if (!match_res) {
6,618✔
2160
        // A continued line still needs a body.
2161
        lr.lr_start = 0;
1,618✔
2162
        lr.lr_end = line.length();
1,618✔
2163
        sa.emplace_back(lr, SA_BODY.value());
1,618✔
2164
        if (!this->lf_multiline) {
1,618✔
2165
            auto len
2166
                = pat.p_pcre.pp_value->match_partial(line.to_string_fragment());
×
2167
            sa.emplace_back(
×
2168
                line_range{(int) len, -1},
×
2169
                SA_INVALID.value("Log line does not match any pattern"));
×
2170
        }
2171
        return;
1,618✔
2172
    }
2173

2174
    auto duration_cap = md[pat.p_duration_field_index];
5,000✔
2175

2176
    auto ts_cap = md[pat.p_timestamp_field_index];
5,000✔
2177
    if (ts_cap) {
5,000✔
2178
        sa.emplace_back(to_line_range(ts_cap.value()), L_TIMESTAMP.value());
5,000✔
2179
    }
2180

2181
    auto opid_cap = md[pat.p_opid_field_index];
5,000✔
2182

2183
    if (!pat.p_opid_description_field_indexes.empty()) {
5,000✔
2184
        hasher h;
4,203✔
2185
        for (auto& fidx : pat.p_opid_description_field_indexes) {
12,169✔
2186
            auto desc_cap = md[fidx];
7,966✔
2187
            if (desc_cap) {
7,966✔
2188
                h.update(desc_cap.value());
7,938✔
2189
            }
2190
        }
2191
        h.to_string(tmp_opid_buf);
4,203✔
2192
        opid_cap = string_fragment::from_bytes(tmp_opid_buf,
8,406✔
2193
                                               sizeof(tmp_opid_buf) - 1);
4,203✔
2194
    } else if (duration_cap && !opid_cap) {
797✔
2195
        hasher h;
3✔
2196
        h.update(line.to_string_fragment());
3✔
2197
        h.to_string(tmp_opid_buf);
3✔
2198
        opid_cap = string_fragment::from_bytes(tmp_opid_buf,
6✔
2199
                                               sizeof(tmp_opid_buf) - 1);
3✔
2200
    }
2201
    if (opid_cap && !opid_cap->empty()) {
5,000✔
2202
        sa.emplace_back(to_line_range(opid_cap.value()), L_OPID.value());
4,647✔
2203
        values.lvv_opid_value = opid_cap->to_string();
4,647✔
2204
        values.lvv_opid_provenance
2205
            = logline_value_vector::opid_provenance::file;
4,647✔
2206
    }
2207

2208
    auto body_cap = md[pat.p_body_field_index];
5,000✔
2209
    auto level_cap = md[pat.p_level_field_index];
5,000✔
2210
    auto src_file_cap = md[pat.p_src_file_field_index];
5,000✔
2211
    auto src_line_cap = md[pat.p_src_line_field_index];
5,000✔
2212
    auto thread_id_cap = md[pat.p_thread_id_field_index];
5,000✔
2213

2214
    if (level_cap
5,000✔
2215
        && (!body_cap
9,954✔
2216
            || (body_cap && level_cap->sf_begin != body_cap->sf_begin)))
9,954✔
2217
    {
2218
        sa.emplace_back(to_line_range(level_cap.value()), L_LEVEL.value());
4,565✔
2219
    }
2220

2221
    if (src_file_cap) {
5,000✔
2222
        sa.emplace_back(to_line_range(src_file_cap.value()),
105✔
2223
                        SA_SRC_FILE.value());
210✔
2224
    }
2225
    if (src_line_cap) {
5,000✔
2226
        sa.emplace_back(to_line_range(src_line_cap.value()),
105✔
2227
                        SA_SRC_LINE.value());
210✔
2228
    }
2229
    if (thread_id_cap) {
5,000✔
2230
        sa.emplace_back(to_line_range(thread_id_cap.value()),
590✔
2231
                        SA_THREAD_ID.value());
1,180✔
2232
        values.lvv_thread_id_value = thread_id_cap->to_string();
590✔
2233
    }
2234
    if (duration_cap) {
5,000✔
2235
        sa.emplace_back(to_line_range(duration_cap.value()),
3✔
2236
                        SA_DURATION.value());
6✔
2237
    }
2238

2239
    for (size_t lpc = 0; lpc < pat.p_value_by_index.size(); lpc++) {
57,308✔
2240
        const auto& ivd = pat.p_value_by_index[lpc];
52,308✔
2241
        const scaling_factor* scaling = nullptr;
52,308✔
2242
        auto cap = md[ivd.ivd_index];
52,308✔
2243
        const auto& vd = *ivd.ivd_value_def;
52,308✔
2244

2245
        if (ivd.ivd_unit_field_index >= 0) {
52,308✔
2246
            auto unit_cap = md[ivd.ivd_unit_field_index];
10✔
2247

2248
            if (unit_cap) {
10✔
2249
                intern_string_t unit_val
2250
                    = intern_string::lookup(unit_cap.value());
10✔
2251
                auto unit_iter = vd.vd_unit_scaling.find(unit_val);
10✔
2252
                if (unit_iter != vd.vd_unit_scaling.end()) {
10✔
2253
                    const auto& sf = unit_iter->second;
10✔
2254

2255
                    scaling = &sf;
10✔
2256
                }
2257
            }
2258
        }
2259

2260
        if (cap) {
52,308✔
2261
            values.lvv_values.emplace_back(
85,514✔
2262
                vd.vd_meta, line, to_line_range(cap.value()));
42,757✔
2263
            values.lvv_values.back().apply_scaling(scaling);
42,757✔
2264
        } else {
2265
            values.lvv_values.emplace_back(vd.vd_meta);
9,551✔
2266
        }
2267
    }
2268

2269
    if (body_cap && body_cap->is_valid()) {
5,000✔
2270
        lr = to_line_range(body_cap.value());
4,988✔
2271
    } else {
2272
        lr.lr_start = line.length();
12✔
2273
        lr.lr_end = line.length();
12✔
2274
    }
2275
    sa.emplace_back(lr, SA_BODY.value());
5,000✔
2276

2277
    log_format::annotate(lf, line_number, sa, values);
5,000✔
2278
}
2279

2280
void
2281
external_log_format::rewrite(exec_context& ec,
43✔
2282
                             shared_buffer_ref& line,
2283
                             string_attrs_t& sa,
2284
                             std::string& value_out)
2285
{
2286
    auto& values = *ec.ec_line_values;
43✔
2287

2288
    value_out.assign(line.get_data(), line.length());
43✔
2289

2290
    for (auto iter = values.lvv_values.begin(); iter != values.lvv_values.end();
259✔
2291
         ++iter)
216✔
2292
    {
2293
        if (!iter->lv_origin.is_valid()) {
216✔
2294
            log_debug("%d: not rewriting value with invalid origin -- %s",
24✔
2295
                      (int) ec.ec_top_line,
2296
                      iter->lv_meta.lvm_name.get());
2297
            continue;
184✔
2298
        }
2299

2300
        auto vd_iter = this->elf_value_defs.find(iter->lv_meta.lvm_name);
192✔
2301
        if (vd_iter == this->elf_value_defs.end()) {
192✔
2302
            log_debug("%d: not rewriting undefined value -- %s",
×
2303
                      (int) ec.ec_top_line,
2304
                      iter->lv_meta.lvm_name.get());
2305
            continue;
×
2306
        }
2307

2308
        const auto& vd = *vd_iter->second;
192✔
2309

2310
        if (vd.vd_rewriter.empty()) {
192✔
2311
            continue;
160✔
2312
        }
2313

2314
        auto _sg = ec.enter_source(
2315
            vd_iter->second->vd_rewrite_src_name, 1, vd.vd_rewriter);
32✔
2316
        std::string field_value;
32✔
2317

2318
        auto_mem<FILE> tmpout(fclose);
32✔
2319

2320
        tmpout = std::tmpfile();
32✔
2321
        if (!tmpout) {
32✔
2322
            log_error("unable to create temporary file");
×
2323
            return;
×
2324
        }
2325
        fcntl(fileno(tmpout), F_SETFD, FD_CLOEXEC);
32✔
2326
        auto fd_copy = auto_fd::dup_of(fileno(tmpout));
32✔
2327
        fd_copy.close_on_exec();
32✔
2328
        auto ec_out = std::make_pair(tmpout.release(), fclose);
32✔
2329
        {
2330
            exec_context::output_guard og(ec, "tmp", ec_out);
64✔
2331

2332
            auto exec_res = execute_any(ec, vd.vd_rewriter);
32✔
2333
            if (exec_res.isOk()) {
32✔
2334
                field_value = exec_res.unwrap();
32✔
2335
            } else {
2336
                field_value = exec_res.unwrapErr().to_attr_line().get_string();
×
2337
            }
2338
        }
32✔
2339
        struct stat st;
2340
        fstat(fd_copy.get(), &st);
32✔
2341
        if (st.st_size > 0) {
32✔
2342
            auto buf = auto_buffer::alloc(st.st_size);
2✔
2343

2344
            buf.resize(st.st_size);
2✔
2345
            pread(fd_copy.get(), buf.in(), st.st_size, 0);
2✔
2346
            field_value = buf.to_string();
2✔
2347
        }
2✔
2348
        value_out.erase(iter->lv_origin.lr_start, iter->lv_origin.length());
32✔
2349

2350
        int32_t shift_amount
2351
            = ((int32_t) field_value.length()) - iter->lv_origin.length();
32✔
2352
        auto orig_lr = iter->lv_origin;
32✔
2353
        value_out.insert(iter->lv_origin.lr_start, field_value);
32✔
2354
        for (auto shift_iter = values.lvv_values.begin();
32✔
2355
             shift_iter != values.lvv_values.end();
180✔
2356
             ++shift_iter)
148✔
2357
        {
2358
            shift_iter->lv_origin.shift_range(orig_lr, shift_amount);
148✔
2359
        }
2360
        shift_string_attrs(sa, orig_lr, shift_amount);
32✔
2361
    }
32✔
2362
}
2363

2364
static int
2365
read_json_field(yajlpp_parse_context* ypc,
152,228✔
2366
                const unsigned char* str,
2367
                size_t len,
2368
                yajl_string_props_t* props)
2369
{
2370
    auto* jlu = (json_log_userdata*) ypc->ypc_userdata;
152,228✔
2371
    timeval tv_out;
2372
    const auto frag = string_fragment::from_bytes(str, len);
152,228✔
2373
    intern_string_t field_name;
152,228✔
2374
    const auto* vd = jlu->get_field_def(ypc);
152,228✔
2375

2376
    if (vd != nullptr) {
152,228✔
2377
        field_name = vd->vd_meta.lvm_name;
13,815✔
2378
    }
2379

2380
    if (field_name.empty()) {
152,228✔
2381
        if (!jlu->jlu_format->elf_opid_field.empty()) {
138,413✔
2382
            auto path_sf = ypc->get_path_as_string_fragment();
46,449✔
2383
            if (path_sf.startswith(jlu->jlu_format->elf_opid_field.c_str())) {
46,449✔
2384
                jlu->jlu_opid_hasher.update(path_sf);
8,973✔
2385
                jlu->jlu_opid_hasher.update(frag);
8,973✔
2386
            }
2387
        }
2388
    } else if (jlu->jlu_format->lf_timestamp_field == field_name) {
13,815✔
2389
        const auto* last = jlu->jlu_format->lf_date_time.scan(
3,094✔
2390
            (const char*) str,
2391
            len,
2392
            jlu->jlu_format->get_timestamp_formats(),
3,094✔
2393
            &jlu->jlu_exttm,
2394
            tv_out);
2395
        if (last == nullptr) {
3,094✔
2396
            auto ls = jlu->jlu_format->lf_date_time.unlock();
22✔
2397
            if ((last = jlu->jlu_format->lf_date_time.scan(
22✔
2398
                     (const char*) str,
2399
                     len,
2400
                     jlu->jlu_format->get_timestamp_formats(),
22✔
2401
                     &jlu->jlu_exttm,
2402
                     tv_out))
2403
                == nullptr)
22✔
2404
            {
2405
                jlu->jlu_format->lf_date_time.relock(ls);
×
2406
            }
2407
            if (last != nullptr) {
22✔
2408
                auto old_flags
22✔
2409
                    = jlu->jlu_format->lf_timestamp_flags & DATE_TIME_SET_FLAGS;
22✔
2410
                auto new_flags = jlu->jlu_exttm.et_flags & DATE_TIME_SET_FLAGS;
22✔
2411

2412
                // It is unlikely a valid timestamp would lose much
2413
                // precision.
2414
                if (new_flags != old_flags) {
22✔
2415
                    last = nullptr;
×
2416
                }
2417
            }
2418
        }
2419
        if (last != nullptr) {
3,094✔
2420
            jlu->jlu_format->lf_timestamp_flags = jlu->jlu_exttm.et_flags;
3,094✔
2421
            jlu->jlu_base_line->set_time(tv_out);
3,094✔
2422
        }
2423
    } else if (jlu->jlu_format->elf_level_pointer.pp_value != nullptr) {
10,721✔
2424
        if (jlu->jlu_format->elf_level_pointer.pp_value
230✔
2425
                ->find_in(field_name.to_string_fragment(), PCRE2_NO_UTF_CHECK)
230✔
2426
                .ignore_error()
230✔
2427
                .has_value())
115✔
2428
        {
2429
            jlu->jlu_base_line->set_level(
×
2430
                jlu->jlu_format->convert_level(frag, jlu->jlu_batch_context));
×
2431
        }
2432
    }
2433
    if (!field_name.empty() && jlu->jlu_format->elf_level_field == field_name) {
152,228✔
2434
        jlu->jlu_base_line->set_level(
3,372✔
2435
            jlu->jlu_format->convert_level(frag, jlu->jlu_batch_context));
3,372✔
2436
    }
2437
    if (!field_name.empty() && jlu->jlu_format->elf_opid_field == field_name) {
152,228✔
2438
        jlu->jlu_base_line->set_opid(frag.hash());
164✔
2439

2440
        auto& sbc = *jlu->jlu_batch_context;
164✔
2441
        auto opid_iter = sbc.sbc_opids.los_opid_ranges.find(frag);
164✔
2442
        if (opid_iter == sbc.sbc_opids.los_opid_ranges.end()) {
164✔
2443
            jlu->jlu_opid_frag = frag.to_owned(sbc.sbc_allocator);
141✔
2444
        } else {
2445
            jlu->jlu_opid_frag = opid_iter->first;
23✔
2446
        }
2447
    }
2448
    if (!field_name.empty()
152,228✔
2449
        && jlu->jlu_format->elf_thread_id_field == field_name)
152,228✔
2450
    {
2451
        auto& sbc = *jlu->jlu_batch_context;
×
2452
        auto tid_iter = sbc.sbc_tids.ltis_tid_ranges.find(frag);
×
2453
        if (tid_iter == sbc.sbc_tids.ltis_tid_ranges.end()) {
×
2454
            jlu->jlu_tid_frag = frag.to_owned(sbc.sbc_allocator);
×
2455
        } else {
2456
            jlu->jlu_tid_frag = tid_iter->first;
×
2457
        }
2458
    }
2459
    if (!jlu->jlu_format->elf_subid_field.empty()
152,228✔
2460
        && jlu->jlu_format->elf_subid_field == field_name)
152,228✔
2461
    {
2462
        jlu->jlu_subid = frag.to_string();
×
2463
    }
2464
    if (!field_name.empty()
152,228✔
2465
        && jlu->jlu_format->elf_duration_field == field_name)
152,228✔
2466
    {
2467
        auto from_res = humanize::try_from<double>(frag);
×
2468
        if (from_res) {
×
2469
            jlu->jlu_duration = std::chrono::microseconds(
×
2470
                static_cast<int64_t>(from_res.value() * 1000000));
2471
        }
2472
    }
2473

2474
    if (vd != nullptr && vd->vd_is_desc_field) {
152,228✔
2475
        auto frag_copy = frag.to_owned(jlu->jlu_format->lf_desc_allocator);
42✔
2476

2477
        jlu->jlu_format->lf_desc_captures.emplace(field_name, frag_copy);
42✔
2478
    }
2479

2480
    jlu->add_sub_lines_for(vd, ypc->is_level(1), std::nullopt, str, len, props);
152,228✔
2481

2482
    return 1;
152,228✔
2483
}
2484

2485
static int
2486
rewrite_json_field(yajlpp_parse_context* ypc,
69,547✔
2487
                   const unsigned char* str,
2488
                   size_t len,
2489
                   yajl_string_props_t* props)
2490
{
2491
    static const intern_string_t body_name = intern_string::lookup("body", -1);
69,547✔
2492
    auto* jlu = (json_log_userdata*) ypc->ypc_userdata;
69,547✔
2493
    intern_string_t field_name;
69,547✔
2494
    const auto* vd = jlu->get_field_def(ypc);
69,547✔
2495
    auto frag = string_fragment::from_bytes(str, len);
69,547✔
2496

2497
    if (!ypc->is_level(1) && vd == nullptr) {
69,547✔
2498
        if (!jlu->jlu_format->elf_opid_field.empty()) {
57,531✔
2499
            auto path_sf = ypc->get_path_as_string_fragment();
56,725✔
2500
            if (path_sf.startswith(jlu->jlu_format->elf_opid_field.c_str())) {
56,725✔
2501
                jlu->jlu_opid_hasher.update(path_sf);
43✔
2502
                jlu->jlu_opid_hasher.update(frag);
43✔
2503
            }
2504
        }
2505
        return 1;
57,531✔
2506
    }
2507
    if (vd != nullptr) {
12,016✔
2508
        field_name = vd->vd_meta.lvm_name;
11,456✔
2509
    } else {
2510
        field_name = ypc->get_path();
560✔
2511
    }
2512

2513
    if (jlu->jlu_format->elf_opid_field == field_name) {
12,016✔
2514
        jlu->jlu_format->jlf_line_values.lvv_opid_value = frag.to_string();
967✔
2515
        jlu->jlu_format->jlf_line_values.lvv_opid_provenance
967✔
2516
            = logline_value_vector::opid_provenance::file;
967✔
2517
    }
2518
    if (jlu->jlu_format->lf_timestamp_field == field_name) {
12,016✔
2519
        char time_buf[64];
2520

2521
        // TODO add a timeval kind to logline_value
2522
        if (jlu->jlu_line->is_time_skewed()
2,224✔
2523
            || (jlu->jlu_format->lf_timestamp_flags
4,448✔
2524
                & (ETF_MICROS_SET | ETF_NANOS_SET | ETF_ZONE_SET)))
2,224✔
2525
        {
2526
            timeval tv;
2527

2528
            const auto* last = jlu->jlu_format->lf_date_time.scan(
2,224✔
2529
                (const char*) str,
2530
                len,
2531
                jlu->jlu_format->get_timestamp_formats(),
2,224✔
2532
                &jlu->jlu_exttm,
2533
                tv);
2534
            if (last == nullptr) {
2,224✔
2535
                auto ls = jlu->jlu_format->lf_date_time.unlock();
58✔
2536
                if ((last = jlu->jlu_format->lf_date_time.scan(
58✔
2537
                         (const char*) str,
2538
                         len,
2539
                         jlu->jlu_format->get_timestamp_formats(),
58✔
2540
                         &jlu->jlu_exttm,
2541
                         tv))
2542
                    == nullptr)
58✔
2543
                {
2544
                    jlu->jlu_format->lf_date_time.relock(ls);
×
2545
                }
2546
            }
2547
            if (!jlu->jlu_subline_opts.hash_hack) {
2,224✔
2548
                if (jlu->jlu_exttm.et_flags & ETF_ZONE_SET
2,224✔
2549
                    && jlu->jlu_format->lf_date_time.dts_zoned_to_local)
2,224✔
2550
                {
2551
                    jlu->jlu_exttm.et_flags &= ~ETF_Z_IS_UTC;
2,224✔
2552
                }
2553
                jlu->jlu_exttm.et_gmtoff
2554
                    = jlu->jlu_format->lf_date_time.dts_local_offset_cache;
2,224✔
2555
            }
2556
            jlu->jlu_format->lf_date_time.ftime(
2,224✔
2557
                time_buf,
2558
                sizeof(time_buf),
2559
                jlu->jlu_format->get_timestamp_formats(),
2,224✔
2560
                jlu->jlu_exttm);
2,224✔
2561
        } else {
2562
            sql_strftime(
×
2563
                time_buf, sizeof(time_buf), jlu->jlu_line->get_timeval(), 'T');
×
2564
        }
2565
        jlu->jlu_format->jlf_line_values.lvv_values.emplace_back(
4,448✔
2566
            jlu->jlu_format->get_value_meta(field_name,
4,448✔
2567
                                            value_kind_t::VALUE_TEXT),
2568
            std::string{time_buf});
6,672✔
2569
    } else if (jlu->jlu_shared_buffer.contains((const char*) str)) {
9,792✔
2570
        auto str_offset = (int) ((const char*) str - jlu->jlu_line_value);
9,386✔
2571
        if (field_name == jlu->jlu_format->elf_body_field) {
9,386✔
2572
            jlu->jlu_format->jlf_line_values.lvv_values.emplace_back(
848✔
2573
                logline_value_meta(body_name,
1,696✔
2574
                                   value_kind_t::VALUE_TEXT,
2575
                                   logline_value_meta::internal_column{},
×
2576
                                   jlu->jlu_format),
848✔
2577
                string_fragment::from_byte_range(
1,696✔
2578
                    jlu->jlu_shared_buffer.get_data(),
848✔
2579
                    str_offset,
2580
                    str_offset + len));
848✔
2581
        }
2582

2583
        jlu->jlu_format->jlf_line_values.lvv_values.emplace_back(
9,386✔
2584
            jlu->jlu_format->get_value_meta(field_name,
18,772✔
2585
                                            value_kind_t::VALUE_TEXT),
2586
            string_fragment::from_byte_range(jlu->jlu_shared_buffer.get_data(),
18,772✔
2587
                                             str_offset,
2588
                                             str_offset + len));
9,386✔
2589
    } else {
2590
        if (field_name == jlu->jlu_format->elf_body_field) {
406✔
2591
            jlu->jlu_format->jlf_line_values.lvv_values.emplace_back(
315✔
2592
                logline_value_meta(body_name,
630✔
2593
                                   value_kind_t::VALUE_TEXT,
2594
                                   logline_value_meta::internal_column{},
×
2595
                                   jlu->jlu_format),
315✔
2596
                std::string{(const char*) str, len});
1,260✔
2597
        }
2598

2599
        jlu->jlu_format->jlf_line_values.lvv_values.emplace_back(
406✔
2600
            jlu->jlu_format->get_value_meta(ypc, vd, value_kind_t::VALUE_TEXT),
812✔
2601
            std::string{(const char*) str, len});
1,624✔
2602
    }
2603
    if (vd != nullptr && vd->vd_is_desc_field
11,456✔
2604
        && jlu->jlu_format->elf_opid_field.empty())
23,472✔
2605
    {
2606
        auto frag_copy = frag.to_owned(jlu->jlu_format->lf_desc_allocator);
62✔
2607

2608
        jlu->jlu_format->lf_desc_captures.emplace(field_name, frag_copy);
62✔
2609
    }
2610

2611
    return 1;
12,016✔
2612
}
2613

2614
void
2615
external_log_format::get_subline(const log_format_file_state& lffs,
23,643✔
2616
                                 const logline& ll,
2617
                                 shared_buffer_ref& sbr,
2618
                                 subline_options opts)
2619
{
2620
    if (this->elf_type == elf_type_t::ELF_TYPE_TEXT) {
23,643✔
2621
        return;
18,980✔
2622
    }
2623

2624
    if (this->jlf_cached_offset != ll.get_offset()
4,663✔
2625
        || this->jlf_cached_opts != opts)
4,663✔
2626
    {
2627
        auto& ypc = *(this->jlf_parse_context);
2,451✔
2628
        yajl_handle handle = this->jlf_yajl_handle.get();
2,451✔
2629
        json_log_userdata jlu(sbr, nullptr);
2,451✔
2630

2631
        jlu.jlu_subline_opts = opts;
2,451✔
2632

2633
        this->lf_desc_captures.clear();
2,451✔
2634
        this->lf_desc_allocator.reset();
2,451✔
2635
        this->jlf_share_manager.invalidate_refs();
2,451✔
2636
        this->jlf_cached_line.clear();
2,451✔
2637
        this->jlf_line_values.clear();
2,451✔
2638
        this->jlf_line_offsets.clear();
2,451✔
2639
        this->jlf_line_attrs.clear();
2,451✔
2640

2641
        auto line_frag = sbr.to_string_fragment();
2,451✔
2642

2643
        if (!line_frag.startswith("{")) {
2,451✔
2644
            this->jlf_cached_line.resize(line_frag.length());
72✔
2645
            memcpy(this->jlf_cached_line.data(),
72✔
2646
                   line_frag.data(),
72✔
2647
                   line_frag.length());
72✔
2648
            this->jlf_line_values.clear();
72✔
2649
            sbr.share(this->jlf_share_manager,
144✔
2650
                      &this->jlf_cached_line[0],
72✔
2651
                      this->jlf_cached_line.size());
2652
            this->jlf_line_values.lvv_sbr = sbr.clone();
72✔
2653
            this->jlf_line_attrs.emplace_back(
72✔
2654
                line_range{0, -1},
×
2655
                SA_INVALID.value(fmt::format(
144✔
2656
                    FMT_STRING("line at offset {} is not a JSON-line"),
144✔
2657
                    ll.get_offset())));
72✔
2658
            return;
72✔
2659
        }
2660

2661
        yajl_reset(handle);
2,379✔
2662
        ypc.set_static_handler(json_log_rewrite_handlers.jpc_children[0]);
2,379✔
2663
        ypc.ypc_userdata = &jlu;
2,379✔
2664
        ypc.ypc_ignore_unused = true;
2,379✔
2665
        ypc.ypc_alt_callbacks.yajl_start_array = json_array_start_const;
2,379✔
2666
        ypc.ypc_alt_callbacks.yajl_end_array = json_array_end;
2,379✔
2667
        ypc.ypc_alt_callbacks.yajl_start_map = json_array_start_const;
2,379✔
2668
        ypc.ypc_alt_callbacks.yajl_end_map = json_array_end;
2,379✔
2669
        jlu.jlu_format = this;
2,379✔
2670
        jlu.jlu_line = &ll;
2,379✔
2671
        jlu.jlu_handle = handle;
2,379✔
2672
        jlu.jlu_line_value = sbr.get_data();
2,379✔
2673
        jlu.jlu_format_hits.resize(this->jlf_line_format.size());
2,379✔
2674

2675
        yajl_status parse_status = yajl_parse(
4,758✔
2676
            handle, (const unsigned char*) sbr.get_data(), sbr.length());
2,379✔
2677
        if (parse_status != yajl_status_ok
2,379✔
2678
            || yajl_complete_parse(handle) != yajl_status_ok)
2,379✔
2679
        {
2680
            unsigned char* msg;
2681
            std::string full_msg;
14✔
2682

2683
            msg = yajl_get_error(
28✔
2684
                handle, 1, (const unsigned char*) sbr.get_data(), sbr.length());
14✔
2685
            if (msg != nullptr) {
14✔
2686
                full_msg = fmt::format(
14✔
2687
                    FMT_STRING("[offset: {}] {}\n{}"),
28✔
2688
                    ll.get_offset(),
14✔
2689
                    fmt::string_view{sbr.get_data(), sbr.length()},
14✔
2690
                    reinterpret_cast<char*>(msg));
28✔
2691
                yajl_free_error(handle, msg);
14✔
2692
            }
2693

2694
            this->jlf_cached_line.resize(full_msg.size());
14✔
2695
            memcpy(
14✔
2696
                this->jlf_cached_line.data(), full_msg.data(), full_msg.size());
14✔
2697
            this->jlf_line_values.clear();
14✔
2698
            this->jlf_line_attrs.emplace_back(
14✔
2699
                line_range{0, -1},
×
2700
                SA_INVALID.value("JSON line failed to parse"));
28✔
2701
        } else {
14✔
2702
            std::vector<logline_value>::iterator lv_iter;
2,365✔
2703
            bool used_values[this->jlf_line_values.lvv_values.size()];
4,730✔
2704
            struct line_range lr;
2,365✔
2705

2706
            memset(used_values, 0, sizeof(used_values));
2,365✔
2707
            for (lv_iter = this->jlf_line_values.lvv_values.begin();
2,365✔
2708
                 lv_iter != this->jlf_line_values.lvv_values.end();
21,142✔
2709
                 ++lv_iter)
18,777✔
2710
            {
2711
                lv_iter->lv_meta.lvm_format = this;
18,777✔
2712
            }
2713

2714
            if (jlu.jlu_tid_number) {
2,365✔
2715
                this->jlf_line_values.lvv_thread_id_value
2716
                    = fmt::to_string(jlu.jlu_tid_number.value());
142✔
2717
            } else if (jlu.jlu_tid_frag) {
2,223✔
2718
                this->jlf_line_values.lvv_thread_id_value
2719
                    = jlu.jlu_tid_frag->to_string();
×
2720
            }
2721

2722
            if (this->elf_opid_field.empty()
2,365✔
2723
                && this->lf_opid_source.value_or(
1,358✔
2724
                       opid_source_t::from_description)
1,358✔
2725
                    == opid_source_t::from_description
2726
                && this->lf_opid_description_def->size() == 1)
3,723✔
2727
            {
2728
                auto found_opid_desc = false;
217✔
2729
                const auto& od = this->lf_opid_description_def->begin()->second;
217✔
2730
                for (const auto& desc : *od.od_descriptors) {
651✔
2731
                    auto desc_iter
2732
                        = this->lf_desc_captures.find(desc.od_field.pp_value);
434✔
2733
                    if (desc_iter == this->lf_desc_captures.end()) {
434✔
2734
                        continue;
372✔
2735
                    }
2736
                    found_opid_desc = true;
62✔
2737
                    jlu.jlu_opid_hasher.update(desc_iter->second);
62✔
2738
                }
2739
                if (found_opid_desc) {
217✔
2740
                    this->jlf_line_values.lvv_opid_value
2741
                        = jlu.jlu_opid_hasher.to_string();
31✔
2742
                    this->jlf_line_values.lvv_opid_provenance
2743
                        = logline_value_vector::opid_provenance::file;
31✔
2744
                }
2745
            } else if (!jlu.jlu_opid_desc_frag && !jlu.jlu_opid_frag
4,268✔
2746
                       && jlu.jlu_duration > 0us)
4,268✔
2747
            {
2748
                jlu.jlu_opid_hasher.update(line_frag);
×
2749
                this->jlf_line_values.lvv_opid_value
2750
                    = jlu.jlu_opid_hasher.to_string();
×
2751
                this->jlf_line_values.lvv_opid_provenance
2752
                    = logline_value_vector::opid_provenance::file;
×
2753
            }
2754
            if (jlu.jlu_opid_desc_frag) {
2,365✔
2755
                this->jlf_line_values.lvv_opid_value
2756
                    = jlu.jlu_opid_hasher.to_string();
28✔
2757
                this->jlf_line_values.lvv_opid_provenance
2758
                    = logline_value_vector::opid_provenance::file;
28✔
2759
            }
2760
            if (jlu.jlu_opid_frag) {
2,365✔
2761
                this->jlf_line_values.lvv_opid_value
2762
                    = jlu.jlu_opid_frag->to_string();
×
2763
                this->jlf_line_values.lvv_opid_provenance
2764
                    = logline_value_vector::opid_provenance::file;
×
2765
            }
2766

2767
            int sub_offset = this->jlf_line_format_init_count;
2,365✔
2768
            for (const auto& jfe : this->jlf_line_format) {
30,622✔
2769
                static const intern_string_t ts_field
2770
                    = intern_string::lookup("__timestamp__", -1);
28,257✔
2771
                static const intern_string_t level_field
2772
                    = intern_string::lookup("__level__");
28,351✔
2773
                size_t begin_size = this->jlf_cached_line.size();
28,257✔
2774

2775
                switch (jfe.jfe_type) {
28,257✔
2776
                    case json_log_field::CONSTANT:
3,823✔
2777
                        this->json_append_to_cache(
3,823✔
2778
                            jfe.jfe_default_value.c_str(),
2779
                            jfe.jfe_default_value.size());
3,823✔
2780
                        break;
3,823✔
2781
                    case json_log_field::VARIABLE:
24,434✔
2782
                        lv_iter = find_if(
24,434✔
2783
                            this->jlf_line_values.lvv_values.begin(),
2784
                            this->jlf_line_values.lvv_values.end(),
2785
                            logline_value_name_cmp(&jfe.jfe_value.pp_value));
2786
                        if (lv_iter != this->jlf_line_values.lvv_values.end()) {
24,434✔
2787
                            auto str = lv_iter->to_string();
8,733✔
2788
                            value_def* vd = nullptr;
8,733✔
2789

2790
                            if (lv_iter->lv_meta.lvm_values_index) {
8,733✔
2791
                                vd = this->elf_value_def_order
2792
                                         [lv_iter->lv_meta.lvm_values_index
8,733✔
2793
                                              .value()]
8,733✔
2794
                                             .get();
8,733✔
2795
                            }
2796
                            while (endswith(str, "\n")) {
8,995✔
2797
                                str.pop_back();
262✔
2798
                            }
2799
                            size_t nl_pos = str.find('\n');
8,733✔
2800

2801
                            if (!jfe.jfe_prefix.empty()) {
8,733✔
2802
                                this->json_append_to_cache(jfe.jfe_prefix);
4,994✔
2803
                            }
2804
                            lr.lr_start = this->jlf_cached_line.size();
8,733✔
2805

2806
                            if ((int) str.size() > jfe.jfe_max_width) {
8,733✔
2807
                                switch (jfe.jfe_overflow) {
233✔
2808
                                    case json_format_element::overflow_t::
165✔
2809
                                        ABBREV: {
2810
                                        size_t new_size
2811
                                            = abbreviate_str(&str[0],
165✔
2812
                                                             str.size(),
2813
                                                             jfe.jfe_max_width);
165✔
2814
                                        str.resize(new_size);
165✔
2815
                                        this->json_append(lffs, jfe, vd, str);
165✔
2816
                                        break;
165✔
2817
                                    }
2818
                                    case json_format_element::overflow_t::
62✔
2819
                                        TRUNCATE: {
2820
                                        this->json_append_to_cache(
62✔
2821
                                            str.c_str(), jfe.jfe_max_width);
62✔
2822
                                        break;
62✔
2823
                                    }
2824
                                    case json_format_element::overflow_t::
6✔
2825
                                        DOTDOT: {
2826
                                        size_t middle
6✔
2827
                                            = (jfe.jfe_max_width / 2) - 1;
6✔
2828
                                        this->json_append_to_cache(str.c_str(),
6✔
2829
                                                                   middle);
2830
                                        this->json_append_to_cache("..", 2);
6✔
2831
                                        size_t rest
6✔
2832
                                            = (jfe.jfe_max_width - middle - 2);
6✔
2833
                                        this->json_append_to_cache(
12✔
2834
                                            str.c_str() + str.size() - rest,
6✔
2835
                                            rest);
2836
                                        break;
6✔
2837
                                    }
2838
                                    case json_format_element::overflow_t::
×
2839
                                        LASTWORD: {
2840
                                        size_t new_size
2841
                                            = last_word_str(&str[0],
×
2842
                                                            str.size(),
2843
                                                            jfe.jfe_max_width);
×
2844
                                        str.resize(new_size);
×
2845
                                        this->json_append(lffs, jfe, vd, str);
×
2846
                                        break;
×
2847
                                    }
2848
                                }
2849
                            } else {
2850
                                sub_offset
2851
                                    += std::count(str.begin(), str.end(), '\n');
8,500✔
2852
                                this->json_append(lffs, jfe, vd, str);
8,500✔
2853
                            }
2854

2855
                            if (nl_pos == std::string::npos
8,733✔
2856
                                || opts.full_message)
3✔
2857
                            {
2858
                                lr.lr_end = this->jlf_cached_line.size();
8,730✔
2859
                            } else {
2860
                                lr.lr_end = lr.lr_start + nl_pos;
3✔
2861
                            }
2862

2863
                            if (lv_iter->lv_meta.lvm_name
8,733✔
2864
                                == this->lf_timestamp_field)
8,733✔
2865
                            {
2866
                                this->jlf_line_attrs.emplace_back(
1,003✔
2867
                                    lr, L_TIMESTAMP.value());
2,006✔
2868
                            } else if (lv_iter->lv_meta.lvm_name
7,730✔
2869
                                       == this->elf_body_field)
7,730✔
2870
                            {
2871
                                this->jlf_line_attrs.emplace_back(
1,163✔
2872
                                    lr, SA_BODY.value());
2,326✔
2873
                            } else if (lv_iter->lv_meta.lvm_name
6,567✔
2874
                                       == this->elf_src_file_field)
6,567✔
2875
                            {
2876
                                this->jlf_line_attrs.emplace_back(
11✔
2877
                                    lr, SA_SRC_FILE.value());
22✔
2878
                            } else if (lv_iter->lv_meta.lvm_name
6,556✔
2879
                                       == this->elf_src_line_field)
6,556✔
2880
                            {
2881
                                this->jlf_line_attrs.emplace_back(
11✔
2882
                                    lr, SA_SRC_LINE.value());
22✔
2883
                            } else if (lv_iter->lv_meta.lvm_name
6,545✔
2884
                                       == this->elf_thread_id_field)
6,545✔
2885
                            {
2886
                                this->jlf_line_attrs.emplace_back(
88✔
2887
                                    lr, SA_THREAD_ID.value());
176✔
2888
                            } else if (lv_iter->lv_meta.lvm_name
6,457✔
2889
                                       == this->elf_duration_field)
6,457✔
2890
                            {
2891
                                this->jlf_line_attrs.emplace_back(
×
2892
                                    lr, SA_DURATION.value());
×
2893
                            } else if (lv_iter->lv_meta.lvm_name
6,457✔
2894
                                       == this->elf_level_field)
6,457✔
2895
                            {
2896
                                this->jlf_line_attrs.emplace_back(
1,077✔
2897
                                    lr, L_LEVEL.value());
2,154✔
2898
                            } else if (lv_iter->lv_meta.lvm_name
10,760✔
2899
                                           == this->elf_opid_field
5,380✔
2900
                                       && !lr.empty())
5,380✔
2901
                            {
2902
                                this->jlf_line_attrs.emplace_back(
967✔
2903
                                    lr, L_OPID.value());
1,934✔
2904
                            }
2905
                            lv_iter->lv_origin = lr;
8,733✔
2906
                            lv_iter->lv_sub_offset = sub_offset;
8,733✔
2907
                            used_values[std::distance(
8,733✔
2908
                                this->jlf_line_values.lvv_values.begin(),
2909
                                lv_iter)]
2910
                                = true;
8,733✔
2911

2912
                            if (!jfe.jfe_suffix.empty()) {
8,733✔
2913
                                this->json_append_to_cache(jfe.jfe_suffix);
1,325✔
2914
                            }
2915
                        } else if (jfe.jfe_value.pp_value == ts_field) {
24,434✔
2916
                            line_range lr;
1,507✔
2917
                            ssize_t ts_len;
2918
                            char ts[64];
2919
                            exttm et;
1,507✔
2920

2921
                            ll.to_exttm(et);
1,507✔
2922
                            et.et_nsec += jlu.jlu_exttm.et_nsec % 1000;
1,507✔
2923
                            et.et_gmtoff = jlu.jlu_exttm.et_gmtoff;
1,507✔
2924
                            et.et_flags |= jlu.jlu_exttm.et_flags;
1,507✔
2925
                            if (!jfe.jfe_prefix.empty()) {
1,507✔
2926
                                this->json_append_to_cache(jfe.jfe_prefix);
3✔
2927
                            }
2928
                            if (jfe.jfe_ts_format.empty()) {
1,507✔
2929
                                ts_len = this->lf_date_time.ftime(
1,348✔
2930
                                    ts,
2931
                                    sizeof(ts),
2932
                                    this->get_timestamp_formats(),
2933
                                    et);
2934
                            } else {
2935
                                ts_len = ftime_fmt(ts,
159✔
2936
                                                   sizeof(ts),
2937
                                                   jfe.jfe_ts_format.c_str(),
2938
                                                   et);
2939
                            }
2940
                            lr.lr_start = this->jlf_cached_line.size();
1,507✔
2941
                            this->json_append_to_cache(ts, ts_len);
1,507✔
2942
                            lr.lr_end = this->jlf_cached_line.size();
1,507✔
2943
                            this->jlf_line_attrs.emplace_back(
1,507✔
2944
                                lr, L_TIMESTAMP.value());
3,014✔
2945

2946
                            lv_iter = find_if(
1,507✔
2947
                                this->jlf_line_values.lvv_values.begin(),
2948
                                this->jlf_line_values.lvv_values.end(),
2949
                                logline_value_name_cmp(
2950
                                    &this->lf_timestamp_field));
1,507✔
2951
                            if (lv_iter
1,507✔
2952
                                != this->jlf_line_values.lvv_values.end())
1,507✔
2953
                            {
2954
                                used_values[distance(
1,507✔
2955
                                    this->jlf_line_values.lvv_values.begin(),
2956
                                    lv_iter)]
2957
                                    = true;
1,507✔
2958
                            }
2959
                            if (!jfe.jfe_suffix.empty()) {
1,507✔
2960
                                this->json_append_to_cache(jfe.jfe_suffix);
3✔
2961
                            }
2962
                        } else if (jfe.jfe_value.pp_value == level_field
14,194✔
2963
                                   || jfe.jfe_value.pp_value
28,266✔
2964
                                       == this->elf_level_field)
14,072✔
2965
                        {
2966
                            auto level_name = ll.get_level_name();
122✔
2967
                            lr.lr_start = this->jlf_cached_line.size();
122✔
2968
                            this->json_append(lffs, jfe, nullptr, level_name);
122✔
2969
                            if (jfe.jfe_auto_width) {
122✔
2970
                                this->json_append_to_cache(
88✔
2971
                                    MAX_LEVEL_NAME_LEN - level_name.length());
88✔
2972
                            }
2973
                            lr.lr_end = this->jlf_cached_line.size();
122✔
2974
                            this->jlf_line_attrs.emplace_back(lr,
122✔
2975
                                                              L_LEVEL.value());
244✔
2976
                        } else if (!jfe.jfe_default_value.empty()) {
14,072✔
2977
                            if (!jfe.jfe_prefix.empty()) {
124✔
2978
                                this->json_append_to_cache(jfe.jfe_prefix);
×
2979
                            }
2980
                            this->json_append(
124✔
2981
                                lffs, jfe, nullptr, jfe.jfe_default_value);
124✔
2982
                            if (!jfe.jfe_suffix.empty()) {
124✔
2983
                                this->json_append_to_cache(jfe.jfe_suffix);
×
2984
                            }
2985
                        }
2986

2987
                        switch (jfe.jfe_text_transform) {
24,434✔
2988
                            case json_format_element::transform_t::NONE:
24,312✔
2989
                                break;
24,312✔
2990
                            case json_format_element::transform_t::UPPERCASE:
122✔
2991
                                for (size_t cindex = begin_size;
122✔
2992
                                     cindex < this->jlf_cached_line.size();
965✔
2993
                                     cindex++)
2994
                                {
2995
                                    this->jlf_cached_line[cindex] = toupper(
843✔
2996
                                        this->jlf_cached_line[cindex]);
843✔
2997
                                }
2998
                                break;
122✔
2999
                            case json_format_element::transform_t::LOWERCASE:
×
3000
                                for (size_t cindex = begin_size;
×
3001
                                     cindex < this->jlf_cached_line.size();
×
3002
                                     cindex++)
3003
                                {
3004
                                    this->jlf_cached_line[cindex] = tolower(
×
3005
                                        this->jlf_cached_line[cindex]);
×
3006
                                }
3007
                                break;
×
3008
                            case json_format_element::transform_t::CAPITALIZE:
×
3009
                                for (size_t cindex = begin_size;
×
3010
                                     cindex < begin_size + 1;
×
3011
                                     cindex++)
3012
                                {
3013
                                    this->jlf_cached_line[cindex] = toupper(
×
3014
                                        this->jlf_cached_line[cindex]);
×
3015
                                }
3016
                                for (size_t cindex = begin_size + 1;
×
3017
                                     cindex < this->jlf_cached_line.size();
×
3018
                                     cindex++)
3019
                                {
3020
                                    this->jlf_cached_line[cindex] = tolower(
×
3021
                                        this->jlf_cached_line[cindex]);
×
3022
                                }
3023
                                break;
×
3024
                        }
3025
                        break;
24,434✔
3026
                }
3027
            }
3028
            this->json_append_to_cache("\n", 1);
2,365✔
3029
            sub_offset += 1;
2,365✔
3030

3031
            for (size_t lpc = 0; lpc < this->jlf_line_values.lvv_values.size();
21,142✔
3032
                 lpc++)
3033
            {
3034
                static const intern_string_t body_name
3035
                    = intern_string::lookup("body", -1);
18,777✔
3036
                auto& lv = this->jlf_line_values.lvv_values[lpc];
18,777✔
3037

3038
                if (lv.lv_meta.is_hidden() || used_values[lpc]
28,822✔
3039
                    || body_name == lv.lv_meta.lvm_name)
28,822✔
3040
                {
3041
                    continue;
17,609✔
3042
                }
3043

3044
                auto str = lv.to_string();
1,168✔
3045
                while (endswith(str, "\n")) {
1,168✔
3046
                    str.pop_back();
×
3047
                }
3048

3049
                lv.lv_sub_offset = sub_offset;
1,168✔
3050
                lv.lv_origin.lr_start = this->jlf_cached_line.size() + 2
1,168✔
3051
                    + lv.lv_meta.lvm_name.size() + 2;
1,168✔
3052
                auto frag = string_fragment::from_str(str);
1,168✔
3053
                while (true) {
3054
                    auto utf_scan_res = is_utf8(frag, '\n');
1,174✔
3055

3056
                    this->json_append_to_cache("  ", 2);
1,174✔
3057
                    this->json_append_to_cache(
1,174✔
3058
                        lv.lv_meta.lvm_name.to_string_fragment());
1,174✔
3059
                    this->json_append_to_cache(": ", 2);
1,174✔
3060
                    lr.lr_start = this->jlf_cached_line.size();
1,174✔
3061
                    this->json_append_to_cache(utf_scan_res.usr_valid_frag);
1,174✔
3062
                    lr.lr_end = this->jlf_cached_line.size();
1,174✔
3063
                    if (lv.lv_meta.lvm_name == this->elf_body_field) {
1,174✔
3064
                        this->jlf_line_attrs.emplace_back(lr, SA_BODY.value());
×
3065
                    } else {
3066
                        this->jlf_line_attrs.emplace_back(
1,174✔
3067
                            lr, SA_EXTRA_CONTENT.value());
2,348✔
3068
                    }
3069
                    this->json_append_to_cache("\n", 1);
1,174✔
3070
                    sub_offset += 1;
1,174✔
3071
                    if (utf_scan_res.usr_remaining) {
1,174✔
3072
                        frag = utf_scan_res.usr_remaining.value();
6✔
3073
                    } else {
3074
                        break;
1,168✔
3075
                    }
3076
                }
6✔
3077
                lv.lv_origin.lr_end = this->jlf_cached_line.size() - 1;
1,168✔
3078
                if (lv.lv_meta.lvm_name == this->elf_opid_field
1,168✔
3079
                    && !lv.lv_origin.empty())
1,168✔
3080
                {
3081
                    this->jlf_line_attrs.emplace_back(lv.lv_origin,
×
3082
                                                      L_OPID.value());
×
3083
                }
3084
            }
1,168✔
3085
        }
2,365✔
3086

3087
        this->jlf_line_offsets.push_back(0);
2,379✔
3088
        for (size_t lpc = 0; lpc < this->jlf_cached_line.size(); lpc++) {
269,189✔
3089
            if (this->jlf_cached_line[lpc] == '\n') {
266,810✔
3090
                this->jlf_line_offsets.push_back(lpc + 1);
4,333✔
3091
            }
3092
        }
3093
        this->jlf_line_offsets.push_back(this->jlf_cached_line.size());
2,379✔
3094
        this->jlf_cached_offset = ll.get_offset();
2,379✔
3095
        this->jlf_cached_opts = opts;
2,379✔
3096
    }
2,451✔
3097

3098
    off_t this_off = 0, next_off = 0;
4,591✔
3099

3100
    if (!this->jlf_line_offsets.empty()
4,591✔
3101
        && ll.get_sub_offset() < this->jlf_line_offsets.size())
4,591✔
3102
    {
3103
        require(ll.get_sub_offset() < this->jlf_line_offsets.size());
4,591✔
3104

3105
        this_off = this->jlf_line_offsets[ll.get_sub_offset()];
4,591✔
3106
        if ((ll.get_sub_offset() + 1) < (int) this->jlf_line_offsets.size()) {
4,591✔
3107
            next_off = this->jlf_line_offsets[ll.get_sub_offset() + 1];
4,591✔
3108
        } else {
3109
            next_off = this->jlf_cached_line.size();
×
3110
        }
3111
        if (next_off > 0 && this->jlf_cached_line[next_off - 1] == '\n'
4,591✔
3112
            && this_off != next_off)
9,182✔
3113
        {
3114
            next_off -= 1;
4,591✔
3115
        }
3116
    }
3117

3118
    if (opts.full_message) {
4,591✔
3119
        sbr.share(this->jlf_share_manager,
644✔
3120
                  &this->jlf_cached_line[0],
322✔
3121
                  this->jlf_cached_line.size());
3122
    } else {
3123
        sbr.share(this->jlf_share_manager,
8,538✔
3124
                  this->jlf_cached_line.data() + this_off,
4,269✔
3125
                  next_off - this_off);
4,269✔
3126
    }
3127
    sbr.get_metadata().m_valid_utf = ll.is_valid_utf();
4,591✔
3128
    sbr.get_metadata().m_has_ansi = ll.has_ansi();
4,591✔
3129
    this->jlf_cached_sub_range.lr_start = this_off;
4,591✔
3130
    this->jlf_cached_sub_range.lr_end = next_off;
4,591✔
3131
    this->jlf_line_values.lvv_sbr = sbr.clone();
4,591✔
3132
}
3133

3134
struct compiled_header_expr {
3135
    auto_mem<sqlite3_stmt> che_stmt{sqlite3_finalize};
3136
    bool che_enabled{true};
3137
};
3138

3139
struct format_header_expressions : public lnav_config_listener {
3140
    format_header_expressions() : lnav_config_listener(__FILE__) {}
1,166✔
3141

3142
    auto_sqlite3 e_db;
3143
    std::map<intern_string_t, std::map<std::string, compiled_header_expr>>
3144
        e_header_exprs;
3145
};
3146

3147
using safe_format_header_expressions = safe::Safe<format_header_expressions>;
3148

3149
static safe_format_header_expressions format_header_exprs;
3150

3151
std::optional<external_file_format>
3152
detect_mime_type(const std::filesystem::path& filename)
612✔
3153
{
3154
    uint8_t buffer[1024];
3155
    size_t buffer_size = 0;
612✔
3156

3157
    {
3158
        auto_fd fd;
612✔
3159

3160
        if ((fd = lnav::filesystem::openp(filename, O_RDONLY)) == -1) {
612✔
3161
            return std::nullopt;
×
3162
        }
3163

3164
        ssize_t rc;
3165

3166
        if ((rc = read(fd, buffer, sizeof(buffer))) == -1) {
612✔
3167
            return std::nullopt;
×
3168
        }
3169
        buffer_size = rc;
612✔
3170
    }
612✔
3171

3172
    auto hexbuf = auto_buffer::alloc(buffer_size * 2);
612✔
3173

3174
    for (size_t lpc = 0; lpc < buffer_size; lpc++) {
327,790✔
3175
        fmt::format_to(
327,178✔
3176
            std::back_inserter(hexbuf), FMT_STRING("{:02x}"), buffer[lpc]);
1,308,712✔
3177
    }
3178

3179
    safe::WriteAccess<safe_format_header_expressions> in(format_header_exprs);
612✔
3180

3181
    for (const auto& format : log_format::get_root_formats()) {
44,570✔
3182
        auto elf = std::dynamic_pointer_cast<external_log_format>(format);
43,958✔
3183
        if (elf == nullptr) {
43,958✔
3184
            continue;
3,060✔
3185
        }
3186

3187
        if (elf->elf_converter.c_header.h_exprs.he_exprs.empty()) {
40,898✔
3188
            continue;
40,286✔
3189
        }
3190

3191
        if (buffer_size < elf->elf_converter.c_header.h_size) {
612✔
3192
            log_debug(
21✔
3193
                "%s: file content too small (%zu) for header detection: %s",
3194
                filename.c_str(),
3195
                buffer_size,
3196
                elf->get_name().get());
3197
            continue;
21✔
3198
        }
3199
        for (const auto& hpair : elf->elf_converter.c_header.h_exprs.he_exprs) {
2,955✔
3200
            auto& he = in->e_header_exprs[elf->get_name()][hpair.first];
2,364✔
3201

3202
            if (!he.che_enabled) {
2,364✔
3203
                continue;
×
3204
            }
3205

3206
            auto* stmt = he.che_stmt.in();
2,364✔
3207

3208
            if (stmt == nullptr) {
2,364✔
3209
                continue;
×
3210
            }
3211
            sqlite3_reset(stmt);
2,364✔
3212
            auto count = sqlite3_bind_parameter_count(stmt);
2,364✔
3213
            for (int lpc = 0; lpc < count; lpc++) {
4,728✔
3214
                const auto* name = sqlite3_bind_parameter_name(stmt, lpc + 1);
2,364✔
3215

3216
                if (name[0] == '$') {
2,364✔
3217
                    const char* env_value;
3218

3219
                    if ((env_value = getenv(&name[1])) != nullptr) {
×
3220
                        sqlite3_bind_text(
×
3221
                            stmt, lpc + 1, env_value, -1, SQLITE_STATIC);
3222
                    }
3223
                    continue;
×
3224
                }
3225
                if (strcmp(name, ":header") == 0) {
2,364✔
3226
                    sqlite3_bind_text(stmt,
2,364✔
3227
                                      lpc + 1,
3228
                                      hexbuf.in(),
2,364✔
3229
                                      hexbuf.size(),
2,364✔
3230
                                      SQLITE_STATIC);
3231
                    continue;
2,364✔
3232
                }
3233
                if (strcmp(name, ":filepath") == 0) {
×
3234
                    sqlite3_bind_text(
×
3235
                        stmt, lpc + 1, filename.c_str(), -1, SQLITE_STATIC);
3236
                    continue;
×
3237
                }
3238
            }
3239

3240
            auto step_res = sqlite3_step(stmt);
2,364✔
3241

3242
            switch (step_res) {
2,364✔
3243
                case SQLITE_OK:
2,364✔
3244
                case SQLITE_DONE:
3245
                    continue;
2,364✔
3246
                case SQLITE_ROW:
×
3247
                    break;
×
3248
                default: {
×
3249
                    log_error(
×
3250
                        "failed to execute file-format header expression: "
3251
                        "%s:%s -- %s",
3252
                        elf->get_name().get(),
3253
                        hpair.first.c_str(),
3254
                        sqlite3_errmsg(in->e_db));
3255
                    he.che_enabled = false;
×
3256
                    continue;
×
3257
                }
3258
            }
3259

3260
            log_info("detected format for: %s -- %s (header-expr: %s)",
×
3261
                     filename.c_str(),
3262
                     elf->get_name().get(),
3263
                     hpair.first.c_str());
3264
            return external_file_format{
×
3265
                elf->get_name().to_string(),
×
3266
                elf->elf_converter.c_command.pp_value,
×
3267
                elf->elf_converter.c_command.pp_location.sl_source.to_string(),
×
3268
            };
3269
        }
3270
    }
43,958✔
3271

3272
    return std::nullopt;
612✔
3273
}
612✔
3274

3275
log_format::scan_result_t
3276
log_format::test_line(sample_t& sample,
×
3277
                      std::vector<lnav::console::user_message>& msgs)
3278
{
3279
    return scan_no_match{};
×
3280
}
3281

3282
log_format::scan_result_t
3283
external_log_format::test_line(sample_t& sample,
182,060✔
3284
                               std::vector<lnav::console::user_message>& msgs)
3285
{
3286
    auto lines
3287
        = string_fragment::from_str(sample.s_line.pp_value).split_lines();
182,060✔
3288

3289
    if (this->elf_type == elf_type_t::ELF_TYPE_JSON) {
182,060✔
3290
        auto alloc = ArenaAlloc::Alloc<char>{};
2,240✔
3291
        pattern_locks pats;
2,240✔
3292
        auto sbc = scan_batch_context{
2,240✔
3293
            alloc,
3294
            pats,
3295
        };
2,240✔
3296
        sbc.sbc_value_stats.resize(this->elf_value_defs.size());
2,240✔
3297
        std::vector<logline> dst;
2,240✔
3298
        auto li = line_info{
2,240✔
3299
            {0, lines[0].length()},
2,240✔
3300
        };
2,240✔
3301
        shared_buffer sb;
2,240✔
3302
        shared_buffer_ref sbr;
2,240✔
3303
        sbr.share(sb, lines[0].data(), (size_t) lines[0].length());
2,240✔
3304

3305
        return this->scan_json(dst, li, sbr, sbc);
2,240✔
3306
    }
2,240✔
3307

3308
    scan_result_t retval = scan_no_match{"no patterns matched"};
179,820✔
3309
    auto found = false;
179,820✔
3310

3311
    for (auto pat_iter = this->elf_pattern_order.begin();
179,820✔
3312
         pat_iter != this->elf_pattern_order.end();
1,365,458✔
3313
         ++pat_iter)
1,185,638✔
3314
    {
3315
        auto& pat = *(*pat_iter);
1,185,638✔
3316

3317
        if (!pat.p_pcre.pp_value) {
1,185,638✔
3318
            continue;
1,005,820✔
3319
        }
3320

3321
        auto md = pat.p_pcre.pp_value->create_match_data();
1,185,638✔
3322
        auto match_res = pat.p_pcre.pp_value->capture_from(lines[0])
1,185,638✔
3323
                             .into(md)
1,185,638✔
3324
                             .matches(PCRE2_NO_UTF_CHECK)
2,371,276✔
3325
                             .ignore_error();
1,185,638✔
3326
        if (!match_res) {
1,185,638✔
3327
            continue;
1,005,820✔
3328
        }
3329
        retval = scan_match{1000};
179,818✔
3330
        found = true;
179,818✔
3331

3332
        sample.s_matched_regexes.insert(pat.p_name.to_string());
179,818✔
3333

3334
        const auto ts_cap = md[pat.p_timestamp_field_index];
179,818✔
3335
        const auto level_cap = md[pat.p_level_field_index];
179,818✔
3336
        const char* const* custom_formats = this->get_timestamp_formats();
179,818✔
3337
        date_time_scanner dts;
179,818✔
3338
        timeval tv;
3339
        exttm tm;
179,818✔
3340

3341
        if (ts_cap && ts_cap->sf_begin == 0) {
179,818✔
3342
            pat.p_timestamp_end = ts_cap->sf_end;
109,305✔
3343
        }
3344
        const char* dts_scan_res = nullptr;
179,818✔
3345

3346
        if (ts_cap) {
179,818✔
3347
            dts_scan_res = dts.scan(
179,816✔
3348
                ts_cap->data(), ts_cap->length(), custom_formats, &tm, tv);
179,816✔
3349
        }
3350
        if (dts_scan_res != nullptr) {
179,818✔
3351
            if (dts_scan_res != ts_cap->data() + ts_cap->length()) {
179,815✔
3352
                auto match_len = dts_scan_res - ts_cap->data();
×
3353
                auto notes = attr_line_t("the used timestamp format: ");
×
3354
                if (custom_formats == nullptr) {
×
3355
                    notes.append(PTIMEC_FORMATS[dts.dts_fmt_lock].pf_fmt);
×
3356
                } else {
3357
                    notes.append(custom_formats[dts.dts_fmt_lock]);
×
3358
                }
3359
                notes.append("\n  ")
×
3360
                    .append(ts_cap.value())
×
3361
                    .append("\n")
×
3362
                    .append(2 + match_len, ' ')
×
3363
                    .append("^ matched up to here"_snippet_border);
×
3364
                auto um = lnav::console::user_message::warning(
×
3365
                              attr_line_t("timestamp was not fully matched: ")
×
3366
                                  .append_quoted(ts_cap.value()))
×
3367
                              .with_snippet(sample.s_line.to_snippet())
×
3368
                              .with_note(notes)
×
3369
                              .move();
×
3370

3371
                msgs.emplace_back(um);
×
3372
            }
3373
        } else if (!ts_cap) {
3✔
3374
            msgs.emplace_back(
2✔
3375
                lnav::console::user_message::error(
×
3376
                    attr_line_t("invalid sample log message: ")
4✔
3377
                        .append(lnav::to_json(sample.s_line.pp_value)))
4✔
3378
                    .with_reason(attr_line_t("timestamp was not captured"))
4✔
3379
                    .with_snippet(sample.s_line.to_snippet())
4✔
3380
                    .with_help(attr_line_t(
4✔
3381
                        "A timestamp needs to be captured in order for a "
3382
                        "line to be recognized as a log message")));
3383
        } else {
3384
            attr_line_t notes;
1✔
3385

3386
            if (custom_formats == nullptr) {
1✔
3387
                notes.append("the following built-in formats were tried:");
×
3388
                for (int lpc = 0; PTIMEC_FORMATS[lpc].pf_fmt != nullptr; lpc++)
×
3389
                {
3390
                    off_t off = 0;
×
3391

3392
                    PTIMEC_FORMATS[lpc].pf_func(
×
3393
                        &tm, ts_cap->data(), off, ts_cap->length());
×
3394
                    notes.append("\n  ")
×
3395
                        .append(ts_cap.value())
×
3396
                        .append("\n")
×
3397
                        .append(2 + off, ' ')
×
3398
                        .append("^ "_snippet_border)
×
3399
                        .append_quoted(
×
3400
                            lnav::roles::symbol(PTIMEC_FORMATS[lpc].pf_fmt))
×
3401
                        .append(" matched up to here"_snippet_border);
×
3402
                }
3403
            } else {
3404
                notes.append("the following custom formats were tried:");
1✔
3405
                for (int lpc = 0; custom_formats[lpc] != nullptr; lpc++) {
2✔
3406
                    off_t off = 0;
1✔
3407

3408
                    ptime_fmt(custom_formats[lpc],
1✔
3409
                              &tm,
3410
                              ts_cap->data(),
3411
                              off,
3412
                              ts_cap->length());
1✔
3413
                    notes.append("\n  ")
1✔
3414
                        .append(ts_cap.value())
1✔
3415
                        .append("\n")
1✔
3416
                        .append(2 + off, ' ')
1✔
3417
                        .append("^ "_snippet_border)
1✔
3418
                        .append_quoted(lnav::roles::symbol(custom_formats[lpc]))
2✔
3419
                        .append(" matched up to here"_snippet_border);
1✔
3420
                }
3421
            }
3422

3423
            msgs.emplace_back(
1✔
3424
                lnav::console::user_message::error(
×
3425
                    attr_line_t("invalid sample log message: ")
1✔
3426
                        .append(lnav::to_json(sample.s_line.pp_value)))
2✔
3427
                    .with_reason(attr_line_t("unrecognized timestamp -- ")
2✔
3428
                                     .append(ts_cap.value()))
1✔
3429
                    .with_snippet(sample.s_line.to_snippet())
2✔
3430
                    .with_note(notes)
1✔
3431
                    .with_help(attr_line_t("If the timestamp format is not "
2✔
3432
                                           "supported by default, you can "
3433
                                           "add a custom format with the ")
3434
                                   .append_quoted("timestamp-format"_symbol)
1✔
3435
                                   .append(" property")));
1✔
3436
        }
1✔
3437

3438
        auto level = this->convert_level(
179,818✔
3439
            level_cap.value_or(string_fragment::invalid()), nullptr);
179,818✔
3440

3441
        if (sample.s_level != LEVEL_UNKNOWN && sample.s_level != level) {
179,818✔
3442
            attr_line_t note_al;
1✔
3443

3444
            note_al.append("matched regex = ")
1✔
3445
                .append(lnav::roles::symbol(pat.p_name.to_string()))
2✔
3446
                .append("\n")
1✔
3447
                .append("captured level = ")
1✔
3448
                .append_quoted(level_cap->to_string());
1✔
3449
            if (level_cap && !this->elf_level_patterns.empty()) {
1✔
3450
                thread_local auto md = lnav::pcre2pp::match_data::unitialized();
1✔
3451

3452
                note_al.append("\nlevel regular expression match results:");
1✔
3453
                for (const auto& level_pattern : this->elf_level_patterns) {
3✔
3454
                    attr_line_t regex_al
3455
                        = level_pattern.second.lp_pcre.pp_value->get_pattern();
2✔
3456
                    lnav::snippets::regex_highlighter(
2✔
3457
                        regex_al, -1, line_range{0, (int) regex_al.length()});
2✔
3458
                    note_al.append("\n  ")
2✔
3459
                        .append(lnav::roles::symbol(
4✔
3460
                            level_pattern.second.lp_pcre.pp_path.to_string()))
4✔
3461
                        .append(" = ")
2✔
3462
                        .append(regex_al)
2✔
3463
                        .append("\n    ");
2✔
3464
                    auto match_res = level_pattern.second.lp_pcre.pp_value
2✔
3465
                                         ->capture_from(level_cap.value())
2✔
3466
                                         .into(md)
2✔
3467
                                         .matches(PCRE2_NO_UTF_CHECK)
4✔
3468
                                         .ignore_error();
2✔
3469
                    if (!match_res) {
2✔
3470
                        note_al.append(lnav::roles::warning("no match"));
1✔
3471
                        continue;
1✔
3472
                    }
3473

3474
                    note_al.append(level_cap.value())
1✔
3475
                        .append("\n    ")
1✔
3476
                        .append(md.leading().length(), ' ')
1✔
3477
                        .append("^"_snippet_border);
1✔
3478
                    if (match_res->f_all.length() > 2) {
1✔
3479
                        note_al.append(lnav::roles::snippet_border(
1✔
3480
                            std::string(match_res->f_all.length() - 2, '-')));
3✔
3481
                    }
3482
                    if (match_res->f_all.length() > 1) {
1✔
3483
                        note_al.append("^"_snippet_border);
1✔
3484
                    }
3485
                }
2✔
3486
            }
3487
            auto um
3488
                = lnav::console::user_message::error(
×
3489
                      attr_line_t("invalid sample log message: ")
1✔
3490
                          .append(lnav::to_json(sample.s_line.pp_value)))
2✔
3491
                      .with_reason(attr_line_t()
2✔
3492
                                       .append_quoted(lnav::roles::symbol(
2✔
3493
                                           level_names[level]))
1✔
3494
                                       .append(" does not match the expected "
1✔
3495
                                               "level of ")
3496
                                       .append_quoted(lnav::roles::symbol(
2✔
3497
                                           level_names[sample.s_level])))
1✔
3498
                      .with_snippet(sample.s_line.to_snippet())
2✔
3499
                      .with_note(note_al)
1✔
3500
                      .move();
1✔
3501
            if (!this->elf_level_patterns.empty()) {
1✔
3502
                um.with_help(
1✔
3503
                    attr_line_t("Level regexes are not anchored to the "
2✔
3504
                                "start/end of the string.  Prepend ")
3505
                        .append_quoted("^"_symbol)
1✔
3506
                        .append(" to the expression to match from the "
1✔
3507
                                "start of the string and append ")
3508
                        .append_quoted("$"_symbol)
1✔
3509
                        .append(" to match up to the end of the string."));
1✔
3510
            }
3511
            msgs.emplace_back(um);
1✔
3512
        }
1✔
3513

3514
        {
3515
            auto full_match_res
3516
                = pat.p_pcre.pp_value->capture_from(sample.s_line.pp_value)
179,818✔
3517
                      .into(md)
179,818✔
3518
                      .matches()
359,636✔
3519
                      .ignore_error();
179,818✔
3520
            if (!full_match_res) {
179,818✔
3521
                attr_line_t regex_al = pat.p_pcre.pp_value->get_pattern();
1✔
3522
                lnav::snippets::regex_highlighter(
1✔
3523
                    regex_al, -1, line_range{0, (int) regex_al.length()});
1✔
3524
                msgs.emplace_back(
1✔
3525
                    lnav::console::user_message::error(
×
3526
                        attr_line_t("invalid pattern: ")
1✔
3527
                            .append_quoted(
1✔
3528
                                lnav::roles::symbol(pat.p_name.to_string())))
2✔
3529
                        .with_reason("pattern does not match entire "
2✔
3530
                                     "multiline sample message")
3531
                        .with_snippet(sample.s_line.to_snippet())
2✔
3532
                        .with_note(attr_line_t()
2✔
3533
                                       .append(lnav::roles::symbol(
1✔
3534
                                           pat.p_name.to_string()))
2✔
3535
                                       .append(" = ")
1✔
3536
                                       .append(regex_al))
1✔
3537
                        .with_help(
3538
                            attr_line_t("use ").append_quoted(".*").append(
2✔
3539
                                " to match new-lines")));
3540
            } else if (static_cast<size_t>(full_match_res->f_all.length())
179,818✔
3541
                       != sample.s_line.pp_value.length())
179,817✔
3542
            {
3543
                attr_line_t regex_al = pat.p_pcre.pp_value->get_pattern();
1✔
3544
                lnav::snippets::regex_highlighter(
1✔
3545
                    regex_al, -1, line_range{0, (int) regex_al.length()});
1✔
3546
                auto match_length
3547
                    = static_cast<size_t>(full_match_res->f_all.length());
1✔
3548
                attr_line_t sample_al = sample.s_line.pp_value;
1✔
3549
                sample_al.append("\n")
1✔
3550
                    .append(match_length, ' ')
1✔
3551
                    .append("^ matched up to here"_error)
1✔
3552
                    .with_attr_for_all(VC_ROLE.value(role_t::VCR_QUOTED_CODE));
1✔
3553
                auto sample_snippet = lnav::console::snippet::from(
3554
                    sample.s_line.pp_location, sample_al);
1✔
3555
                msgs.emplace_back(
1✔
3556
                    lnav::console::user_message::error(
×
3557
                        attr_line_t("invalid pattern: ")
1✔
3558
                            .append_quoted(
1✔
3559
                                lnav::roles::symbol(pat.p_name.to_string())))
2✔
3560
                        .with_reason("pattern does not match entire "
2✔
3561
                                     "message")
3562
                        .with_snippet(sample_snippet)
1✔
3563
                        .with_note(attr_line_t()
3✔
3564
                                       .append(lnav::roles::symbol(
2✔
3565
                                           pat.p_name.to_string()))
2✔
3566
                                       .append(" = ")
1✔
3567
                                       .append(regex_al))
1✔
3568
                        .with_help("update the regular expression to fully "
3569
                                   "capture the sample message"));
3570
            }
1✔
3571
        }
3572
    }
1,185,638✔
3573

3574
    if (!found && !this->elf_pattern_order.empty()) {
179,820✔
3575
        std::vector<std::pair<ssize_t, intern_string_t>> partial_indexes;
2✔
3576
        attr_line_t notes;
2✔
3577
        size_t max_name_width = 0;
2✔
3578

3579
        for (const auto& pat_iter : this->elf_pattern_order) {
10✔
3580
            auto& pat = *pat_iter;
8✔
3581

3582
            if (!pat.p_pcre.pp_value) {
8✔
3583
                continue;
×
3584
            }
3585

3586
            partial_indexes.emplace_back(
8✔
3587
                pat.p_pcre.pp_value->match_partial(lines[0]), pat.p_name);
8✔
3588
            max_name_width = std::max(max_name_width, pat.p_name.size());
8✔
3589
        }
3590
        for (const auto& line_frag : lines) {
4✔
3591
            auto src_line = attr_line_t(line_frag.to_string());
2✔
3592
            if (!line_frag.endswith("\n")) {
2✔
3593
                src_line.append("\n");
2✔
3594
            }
3595
            src_line.with_attr_for_all(VC_ROLE.value(role_t::VCR_QUOTED_CODE));
2✔
3596
            notes.append("   ").append(src_line);
2✔
3597
            for (auto& part_pair : partial_indexes) {
10✔
3598
                if (part_pair.first >= 0
16✔
3599
                    && part_pair.first < line_frag.length())
8✔
3600
                {
3601
                    notes.append("   ")
8✔
3602
                        .append(part_pair.first, ' ')
8✔
3603
                        .append("^ "_snippet_border)
8✔
3604
                        .append(
8✔
3605
                            lnav::roles::symbol(part_pair.second.to_string()))
16✔
3606
                        .append(" matched up to here"_snippet_border)
8✔
3607
                        .append("\n");
8✔
3608
                }
3609
                part_pair.first -= line_frag.length();
8✔
3610
            }
3611
        }
2✔
3612
        notes.add_header(
2✔
3613
            "the following shows how each pattern matched this sample:\n");
3614

3615
        attr_line_t regex_note;
2✔
3616
        for (const auto& pat_iter : this->elf_pattern_order) {
10✔
3617
            if (!pat_iter->p_pcre.pp_value) {
8✔
3618
                regex_note
3619
                    .append(lnav::roles::symbol(fmt::format(
×
3620
                        FMT_STRING("{:{}}"), pat_iter->p_name, max_name_width)))
×
3621
                    .append(" is invalid");
×
3622
                continue;
×
3623
            }
3624

3625
            attr_line_t regex_al = pat_iter->p_pcre.pp_value->get_pattern();
8✔
3626
            lnav::snippets::regex_highlighter(
8✔
3627
                regex_al, -1, line_range{0, (int) regex_al.length()});
8✔
3628

3629
            regex_note
3630
                .append(lnav::roles::symbol(fmt::format(
16✔
3631
                    FMT_STRING("{:{}}"), pat_iter->p_name, max_name_width)))
24✔
3632
                .append(" = ")
8✔
3633
                .append_quoted(regex_al)
16✔
3634
                .append("\n");
8✔
3635
        }
8✔
3636

3637
        msgs.emplace_back(
2✔
3638
            lnav::console::user_message::error(
×
3639
                attr_line_t("invalid sample log message: ")
2✔
3640
                    .append(lnav::to_json(sample.s_line.pp_value)))
4✔
3641
                .with_reason("sample does not match any patterns")
4✔
3642
                .with_snippet(sample.s_line.to_snippet())
4✔
3643
                .with_note(notes.rtrim())
4✔
3644
                .with_note(regex_note));
3645
    }
2✔
3646

3647
    return retval;
179,820✔
3648
}
182,060✔
3649

3650
void
3651
external_log_format::build(std::vector<lnav::console::user_message>& errors)
50,302✔
3652
{
3653
    auto& vc = view_colors::singleton();
50,302✔
3654

3655
    if (!this->lf_timestamp_field.empty()) {
50,302✔
3656
        auto& vd = this->elf_value_defs[this->lf_timestamp_field];
50,302✔
3657
        if (vd.get() == nullptr) {
50,302✔
3658
            vd = std::make_shared<value_def>(
36,032✔
3659
                this->lf_timestamp_field,
36,032✔
3660
                value_kind_t::VALUE_TEXT,
×
3661
                logline_value_meta::internal_column{},
×
3662
                this);
36,032✔
3663
            if (this->elf_type == elf_type_t::ELF_TYPE_JSON) {
36,032✔
3664
                this->elf_value_def_order.emplace_back(vd);
4,958✔
3665
            }
3666
        }
3667
        vd->vd_meta.lvm_name = this->lf_timestamp_field;
50,302✔
3668
        vd->vd_meta.lvm_kind = value_kind_t::VALUE_TEXT;
50,302✔
3669
        vd->vd_meta.lvm_column = logline_value_meta::internal_column{};
50,302✔
3670
        vd->vd_internal = true;
50,302✔
3671

3672
        this->elf_value_defs[LOG_TIME_STR] = vd;
50,302✔
3673
    }
3674

3675
    if (!this->lf_subsecond_field.empty()) {
50,302✔
3676
        if (!this->lf_subsecond_unit.has_value()) {
97✔
3677
            errors.emplace_back(
1✔
3678
                lnav::console::user_message::error(
×
3679
                    attr_line_t()
2✔
3680
                        .append_quoted(
1✔
3681
                            lnav::roles::symbol(this->elf_name.to_string()))
2✔
3682
                        .append(" is not a valid log format"))
1✔
3683
                    .with_reason(attr_line_t()
2✔
3684
                                     .append_quoted("subsecond-units"_symbol)
1✔
3685
                                     .append(" must be set when ")
1✔
3686
                                     .append_quoted("subsecond-field"_symbol)
1✔
3687
                                     .append(" is used"))
1✔
3688
                    .with_snippets(this->get_snippets()));
2✔
3689
        } else {
3690
            auto& vd = this->elf_value_defs[this->lf_subsecond_field];
96✔
3691
            if (vd.get() == nullptr) {
96✔
3692
                vd = std::make_shared<value_def>(
96✔
3693
                    this->lf_subsecond_field,
96✔
3694
                    value_kind_t::VALUE_INTEGER,
×
3695
                    logline_value_meta::internal_column{},
×
3696
                    this);
96✔
3697
                if (this->elf_type == elf_type_t::ELF_TYPE_JSON) {
96✔
3698
                    this->elf_value_def_order.emplace_back(vd);
96✔
3699
                }
3700
            }
3701
            vd->vd_meta.lvm_name = this->lf_subsecond_field;
96✔
3702
            vd->vd_meta.lvm_column = logline_value_meta::internal_column{};
96✔
3703
            vd->vd_meta.lvm_hidden = true;
96✔
3704
            vd->vd_internal = true;
96✔
3705
        }
3706
    }
3707

3708
    if (startswith(this->elf_level_field.get(), "/")) {
50,302✔
3709
        this->elf_level_field
3710
            = intern_string::lookup(this->elf_level_field.get() + 1);
192✔
3711
    }
3712
    if (!this->elf_level_field.empty()) {
50,302✔
3713
        auto level_iter = this->elf_value_defs.find(this->elf_level_field);
50,302✔
3714
        if (level_iter == this->elf_value_defs.end()) {
50,302✔
3715
            auto& vd = this->elf_value_defs[this->elf_level_field];
25,491✔
3716
            vd = std::make_shared<value_def>(
25,491✔
3717
                this->elf_level_field,
25,491✔
3718
                value_kind_t::VALUE_TEXT,
×
3719
                logline_value_meta::internal_column{},
×
3720
                this);
25,491✔
3721
            if (this->elf_type == elf_type_t::ELF_TYPE_JSON) {
25,491✔
3722
                this->elf_value_def_order.emplace_back(vd);
2,624✔
3723
            }
3724
            vd->vd_meta.lvm_name = this->elf_level_field;
25,491✔
3725
            vd->vd_meta.lvm_column = logline_value_meta::internal_column{};
25,491✔
3726
            vd->vd_internal = true;
25,491✔
3727

3728
            if (this->elf_level_field != this->elf_body_field) {
25,491✔
3729
                this->elf_value_defs[LOG_LEVEL_STR] = vd;
24,649✔
3730
            }
3731
        } else {
3732
            if (level_iter->second->vd_meta.lvm_kind
24,811✔
3733
                != value_kind_t::VALUE_TEXT)
24,811✔
3734
            {
3735
                this->lf_level_hideable = false;
5,318✔
3736
            }
3737
            this->elf_value_defs[LOG_LEVEL_STR] = level_iter->second;
24,811✔
3738
        }
3739
    }
3740

3741
    auto opid_field_iter = this->elf_value_defs.find(LOG_OPID_STR);
50,302✔
3742
    if (opid_field_iter == this->elf_value_defs.end()) {
50,302✔
3743
        auto vd
3744
            = std::make_shared<value_def>(this->elf_opid_field,
50,302✔
3745
                                          value_kind_t::VALUE_TEXT,
×
3746
                                          logline_value_meta::internal_column{},
×
3747
                                          this);
50,302✔
3748
        if (this->elf_type == elf_type_t::ELF_TYPE_JSON) {
50,302✔
3749
            this->elf_value_def_order.emplace_back(vd);
8,784✔
3750
        }
3751
        vd->vd_meta.lvm_name = LOG_OPID_STR;
50,302✔
3752
        vd->vd_meta.lvm_kind = value_kind_t::VALUE_TEXT;
50,302✔
3753
        vd->vd_meta.lvm_column = logline_value_meta::internal_column{};
50,302✔
3754
        vd->vd_internal = true;
50,302✔
3755

3756
        this->elf_value_defs[LOG_OPID_STR] = vd;
50,302✔
3757
    }
50,302✔
3758

3759
    if (!this->elf_body_field.empty()) {
50,302✔
3760
        auto& vd = this->elf_value_defs[this->elf_body_field];
50,302✔
3761
        if (vd.get() == nullptr) {
50,302✔
3762
            vd = std::make_shared<value_def>(
41,157✔
3763
                this->elf_body_field,
41,157✔
3764
                value_kind_t::VALUE_TEXT,
×
3765
                logline_value_meta::internal_column{},
×
3766
                this);
41,157✔
3767
            if (this->elf_type == elf_type_t::ELF_TYPE_JSON) {
41,157✔
3768
                this->elf_value_def_order.emplace_back(vd);
5,703✔
3769
            }
3770
        }
3771
        vd->vd_meta.lvm_name = this->elf_body_field;
50,302✔
3772
        vd->vd_meta.lvm_kind = value_kind_t::VALUE_TEXT;
50,302✔
3773
        vd->vd_meta.lvm_column = logline_value_meta::internal_column{};
50,302✔
3774
        vd->vd_internal = true;
50,302✔
3775
    }
3776

3777
    if (!this->elf_src_file_field.empty()) {
50,302✔
3778
        auto& vd = this->elf_value_defs[this->elf_src_file_field];
7,461✔
3779
        if (vd.get() == nullptr) {
7,461✔
3780
            vd = std::make_shared<value_def>(
1✔
3781
                this->elf_src_file_field,
1✔
3782
                value_kind_t::VALUE_TEXT,
×
3783
                logline_value_meta::internal_column{},
×
3784
                this);
2✔
3785
        }
3786
        vd->vd_meta.lvm_name = this->elf_src_file_field;
7,461✔
3787
        vd->vd_meta.lvm_kind = value_kind_t::VALUE_TEXT;
7,461✔
3788
        vd->vd_meta.lvm_column = logline_value_meta::internal_column{};
7,461✔
3789
    }
3790

3791
    if (!this->elf_src_line_field.empty()) {
50,302✔
3792
        auto& vd = this->elf_value_defs[this->elf_src_line_field];
7,461✔
3793
        if (vd.get() == nullptr) {
7,461✔
3794
            vd = std::make_shared<value_def>(
1✔
3795
                this->elf_src_line_field,
1✔
3796
                value_kind_t::VALUE_INTEGER,
×
3797
                logline_value_meta::internal_column{},
×
3798
                this);
2✔
3799
        }
3800
        vd->vd_meta.lvm_name = this->elf_src_line_field;
7,461✔
3801
        vd->vd_meta.lvm_kind = value_kind_t::VALUE_INTEGER;
7,461✔
3802
        vd->vd_meta.lvm_column = logline_value_meta::internal_column{};
7,461✔
3803
    }
3804

3805
    if (!this->elf_thread_id_field.empty()) {
50,302✔
3806
        auto& vd = this->elf_value_defs[this->elf_thread_id_field];
16,413✔
3807
        if (vd.get() == nullptr) {
16,413✔
3808
            vd = std::make_shared<value_def>(
1✔
3809
                this->elf_thread_id_field,
1✔
3810
                value_kind_t::VALUE_TEXT,
×
3811
                logline_value_meta::internal_column{},
×
3812
                this);
2✔
3813
        }
3814
        vd->vd_meta.lvm_name = this->elf_thread_id_field;
16,413✔
3815
        vd->vd_meta.lvm_kind = value_kind_t::VALUE_TEXT;
16,413✔
3816
        vd->vd_meta.lvm_column = logline_value_meta::internal_column{};
16,413✔
3817
    }
3818

3819
    if (!this->elf_duration_field.empty()) {
50,302✔
3820
        auto& vd = this->elf_value_defs[this->elf_duration_field];
2,238✔
3821
        if (vd.get() == nullptr) {
2,238✔
3822
            vd = std::make_shared<value_def>(
×
3823
                this->elf_duration_field,
×
3824
                value_kind_t::VALUE_FLOAT,
×
3825
                logline_value_meta::internal_column{},
×
3826
                this);
×
3827
        }
3828
        vd->vd_meta.lvm_name = this->elf_duration_field;
2,238✔
3829
        vd->vd_meta.lvm_kind = value_kind_t::VALUE_FLOAT;
2,238✔
3830
        vd->vd_meta.lvm_column = logline_value_meta::internal_column{};
2,238✔
3831
    }
3832

3833
    for (auto& od_pair : *this->lf_opid_description_def) {
72,684✔
3834
        od_pair.second.od_index = this->lf_opid_description_def_vec->size();
22,382✔
3835
        this->lf_opid_description_def_vec->emplace_back(&od_pair.second);
22,382✔
3836
    }
3837

3838
    for (auto& od_pair : *this->lf_subid_description_def) {
51,048✔
3839
        od_pair.second.od_index = this->lf_subid_description_def_vec->size();
746✔
3840
        this->lf_subid_description_def_vec->emplace_back(&od_pair.second);
746✔
3841
    }
3842

3843
    if (!this->lf_timestamp_format.empty()) {
50,302✔
3844
        this->lf_timestamp_format.push_back(nullptr);
5,322✔
3845
    }
3846
    auto src_file_found = 0;
50,302✔
3847
    auto src_line_found = 0;
50,302✔
3848
    auto thread_id_found = 0;
50,302✔
3849
    auto duration_found = 0;
50,302✔
3850
    for (auto& elf_pattern : this->elf_patterns) {
146,378✔
3851
        auto& pat = *elf_pattern.second;
96,076✔
3852

3853
        if (pat.p_pcre.pp_value == nullptr) {
96,076✔
3854
            continue;
1✔
3855
        }
3856

3857
        if (pat.p_opid_field_index == -1
192,150✔
3858
            && this->lf_opid_source.value_or(opid_source_t::from_description)
96,075✔
3859
                == opid_source_t::from_description
3860
            && this->lf_opid_description_def->size() == 1)
192,150✔
3861
        {
3862
            const auto& opid_def
3863
                = this->lf_opid_description_def->begin()->second;
24,618✔
3864
            for (const auto& desc : *opid_def.od_descriptors) {
55,204✔
3865
                for (auto named_cap : pat.p_pcre.pp_value->get_named_captures())
349,128✔
3866
                {
3867
                    const intern_string_t name
3868
                        = intern_string::lookup(named_cap.get_name());
318,542✔
3869

3870
                    if (name == desc.od_field.pp_value) {
318,542✔
3871
                        pat.p_opid_description_field_indexes.emplace_back(
56,696✔
3872
                            named_cap.get_index());
28,348✔
3873
                    }
3874
                }
3875
            }
3876
        }
3877

3878
        for (auto named_cap : pat.p_pcre.pp_value->get_named_captures()) {
828,352✔
3879
            const intern_string_t name
3880
                = intern_string::lookup(named_cap.get_name());
732,277✔
3881

3882
            if (name == this->lf_timestamp_field) {
732,277✔
3883
                pat.p_timestamp_field_index = named_cap.get_index();
96,073✔
3884
            }
3885
            if (name == this->lf_time_field) {
732,277✔
3886
                pat.p_time_field_index = named_cap.get_index();
746✔
3887
            }
3888
            if (name == this->elf_level_field) {
732,277✔
3889
                pat.p_level_field_index = named_cap.get_index();
72,748✔
3890
            }
3891
            if (name == this->elf_opid_field) {
732,277✔
3892
                pat.p_opid_field_index = named_cap.get_index();
20,142✔
3893
            }
3894
            if (name == this->elf_subid_field) {
732,277✔
3895
                pat.p_subid_field_index = named_cap.get_index();
11,190✔
3896
            }
3897
            if (name == this->elf_body_field) {
732,277✔
3898
                pat.p_body_field_index = named_cap.get_index();
82,645✔
3899
            }
3900
            if (name == this->elf_src_file_field) {
732,277✔
3901
                pat.p_src_file_field_index = named_cap.get_index();
11,936✔
3902
                src_file_found += 1;
11,936✔
3903
            }
3904
            if (name == this->elf_src_line_field) {
732,277✔
3905
                pat.p_src_line_field_index = named_cap.get_index();
13,428✔
3906
                src_line_found += 1;
13,428✔
3907
            }
3908
            if (name == this->elf_thread_id_field) {
732,277✔
3909
                pat.p_thread_id_field_index = named_cap.get_index();
41,776✔
3910
                thread_id_found += 1;
41,776✔
3911
            }
3912
            if (name == this->elf_duration_field) {
732,277✔
3913
                pat.p_duration_field_index = named_cap.get_index();
4,476✔
3914
                duration_found += 1;
4,476✔
3915
            }
3916

3917
            auto value_iter = this->elf_value_defs.find(name);
732,277✔
3918
            if (value_iter != this->elf_value_defs.end()) {
732,277✔
3919
                auto vd = value_iter->second;
720,989✔
3920
                indexed_value_def ivd;
720,989✔
3921

3922
                ivd.ivd_index = named_cap.get_index();
720,989✔
3923
                if (!vd->vd_unit_field.empty()) {
720,989✔
3924
                    ivd.ivd_unit_field_index = pat.p_pcre.pp_value->name_index(
1,492✔
3925
                        vd->vd_unit_field.get());
746✔
3926
                } else {
3927
                    ivd.ivd_unit_field_index = -1;
720,243✔
3928
                }
3929
                if (!vd->vd_internal
720,989✔
3930
                    && !vd->vd_meta.lvm_column
1,239,099✔
3931
                            .is<logline_value_meta::table_column>())
518,110✔
3932
                {
3933
                    vd->vd_meta.lvm_column = logline_value_meta::table_column{
287,596✔
3934
                        this->elf_column_count++};
287,596✔
3935
                }
3936
                ivd.ivd_value_def = vd;
720,989✔
3937
                pat.p_value_by_index.push_back(ivd);
720,989✔
3938
            }
720,989✔
3939
            pat.p_value_name_to_index[name] = named_cap.get_index();
732,277✔
3940
        }
3941

3942
        stable_sort(pat.p_value_by_index.begin(), pat.p_value_by_index.end());
96,075✔
3943

3944
        for (int lpc = 0; lpc < (int) pat.p_value_by_index.size(); lpc++) {
817,064✔
3945
            auto& ivd = pat.p_value_by_index[lpc];
720,989✔
3946
            auto vd = ivd.ivd_value_def;
720,989✔
3947

3948
            if (!vd->vd_meta.lvm_foreign_key && !vd->vd_meta.lvm_identifier) {
720,989✔
3949
                switch (vd->vd_meta.lvm_kind) {
366,446✔
3950
                    case value_kind_t::VALUE_INTEGER:
50,824✔
3951
                    case value_kind_t::VALUE_FLOAT:
3952
                        pat.p_numeric_value_indexes.push_back(lpc);
50,824✔
3953
                        break;
50,824✔
3954
                    default:
315,622✔
3955
                        break;
315,622✔
3956
                }
3957
            }
3958
        }
720,989✔
3959

3960
        if (pat.p_timestamp_field_index == -1) {
96,075✔
3961
            errors.emplace_back(
2✔
3962
                lnav::console::user_message::error(
×
3963
                    attr_line_t("invalid pattern: ")
4✔
3964
                        .append_quoted(lnav::roles::symbol(pat.p_config_path)))
4✔
3965
                    .with_reason("no timestamp capture found in the pattern")
4✔
3966
                    .with_snippets(this->get_snippets())
4✔
3967
                    .with_help("all log messages need a timestamp"));
3968
        }
3969

3970
        if (!this->elf_level_field.empty() && pat.p_level_field_index == -1) {
96,075✔
3971
            log_warning("%s:level field '%s' not found in pattern",
23,327✔
3972
                        pat.p_config_path.c_str(),
3973
                        this->elf_level_field.get());
3974
        }
3975
        if (!this->elf_body_field.empty() && pat.p_body_field_index == -1) {
96,075✔
3976
            log_warning("%s:body field '%s' not found in pattern",
13,430✔
3977
                        pat.p_config_path.c_str(),
3978
                        this->elf_body_field.get());
3979
        }
3980

3981
        this->elf_pattern_order.push_back(elf_pattern.second);
96,075✔
3982
    }
3983
    if (this->elf_type == elf_type_t::ELF_TYPE_TEXT
100,604✔
3984
        && !this->elf_src_file_field.empty() && src_file_found == 0)
50,302✔
3985
    {
3986
        errors.emplace_back(
1✔
3987
            lnav::console::user_message::error(
×
3988
                attr_line_t("invalid pattern: ")
2✔
3989
                    .append_quoted(
1✔
3990
                        lnav::roles::symbol(this->elf_name.to_string())))
2✔
3991
                .with_reason("no source file capture found in the pattern")
2✔
3992
                .with_snippets(this->get_snippets())
2✔
3993
                .with_help(attr_line_t("at least one pattern needs a source "
2✔
3994
                                       "file capture named ")
3995
                               .append_quoted(this->elf_src_file_field.get())));
1✔
3996
    }
3997
    if (this->elf_type == elf_type_t::ELF_TYPE_TEXT
100,604✔
3998
        && !this->elf_src_line_field.empty() && src_line_found == 0)
50,302✔
3999
    {
4000
        errors.emplace_back(
1✔
4001
            lnav::console::user_message::error(
×
4002
                attr_line_t("invalid pattern: ")
2✔
4003
                    .append_quoted(
1✔
4004
                        lnav::roles::symbol(this->elf_name.to_string())))
2✔
4005
                .with_reason("no source line capture found in the pattern")
2✔
4006
                .with_snippets(this->get_snippets())
2✔
4007
                .with_help(attr_line_t("at least one pattern needs a source "
2✔
4008
                                       "line capture named ")
4009
                               .append_quoted(this->elf_src_line_field.get())));
1✔
4010
    }
4011
    if (this->elf_type == elf_type_t::ELF_TYPE_TEXT
100,604✔
4012
        && !this->elf_thread_id_field.empty() && thread_id_found == 0)
50,302✔
4013
    {
4014
        errors.emplace_back(
1✔
4015
            lnav::console::user_message::error(
×
4016
                attr_line_t("invalid pattern: ")
2✔
4017
                    .append_quoted(
1✔
4018
                        lnav::roles::symbol(this->elf_name.to_string())))
2✔
4019
                .with_reason("no thread ID capture found in the pattern")
2✔
4020
                .with_snippets(this->get_snippets())
2✔
4021
                .with_help(
4022
                    attr_line_t(
2✔
4023
                        "at least one pattern needs a thread ID capture named ")
4024
                        .append_quoted(this->elf_thread_id_field.get())));
1✔
4025
    }
4026
    if (this->elf_type == elf_type_t::ELF_TYPE_TEXT
100,604✔
4027
        && !this->elf_duration_field.empty() && duration_found == 0)
50,302✔
4028
    {
4029
        errors.emplace_back(
×
4030
            lnav::console::user_message::error(
×
4031
                attr_line_t("invalid pattern: ")
×
4032
                    .append_quoted(
×
4033
                        lnav::roles::symbol(this->elf_name.to_string())))
×
4034
                .with_reason("no duration capture found in the pattern")
×
4035
                .with_snippets(this->get_snippets())
×
4036
                .with_help(
4037
                    attr_line_t(
×
4038
                        "at least one pattern needs a duration capture named ")
4039
                        .append_quoted(this->elf_duration_field.get())));
×
4040
    }
4041

4042
    if (this->elf_type != elf_type_t::ELF_TYPE_TEXT) {
50,302✔
4043
        if (!this->elf_patterns.empty()) {
8,784✔
4044
            errors.emplace_back(
1✔
4045
                lnav::console::user_message::error(
×
4046
                    attr_line_t()
2✔
4047
                        .append_quoted(
1✔
4048
                            lnav::roles::symbol(this->elf_name.to_string()))
2✔
4049
                        .append(" is not a valid log format"))
1✔
4050
                    .with_reason("structured logs cannot have regexes")
2✔
4051
                    .with_snippets(this->get_snippets()));
2✔
4052
        }
4053
        if (this->elf_type == elf_type_t::ELF_TYPE_JSON) {
8,784✔
4054
            this->lf_multiline = true;
8,784✔
4055
            this->lf_structured = true;
8,784✔
4056
            this->lf_formatted_lines = true;
8,784✔
4057
            this->jlf_parse_context
4058
                = std::make_shared<yajlpp_parse_context>(this->elf_name);
8,784✔
4059
            this->jlf_yajl_handle.reset(
8,784✔
4060
                yajl_alloc(&this->jlf_parse_context->ypc_callbacks,
8,784✔
4061
                           nullptr,
4062
                           this->jlf_parse_context.get()),
8,784✔
4063
                yajl_handle_deleter());
4064
            yajl_config(
8,784✔
4065
                this->jlf_yajl_handle.get(), yajl_dont_validate_strings, 1);
4066
        }
4067
    } else {
4068
        if (this->elf_patterns.empty()) {
41,518✔
4069
            errors.emplace_back(lnav::console::user_message::error(
2✔
4070
                                    attr_line_t()
4✔
4071
                                        .append_quoted(lnav::roles::symbol(
4✔
4072
                                            this->elf_name.to_string()))
4✔
4073
                                        .append(" is not a valid log format"))
2✔
4074
                                    .with_reason("no regexes specified")
4✔
4075
                                    .with_snippets(this->get_snippets()));
4✔
4076
        }
4077
    }
4078

4079
    stable_sort(this->elf_level_pairs.begin(), this->elf_level_pairs.end());
50,302✔
4080

4081
    {
4082
        safe::WriteAccess<safe_format_header_expressions> hexprs(
4083
            format_header_exprs);
50,302✔
4084

4085
        if (hexprs->e_db.in() == nullptr) {
50,302✔
4086
            if (sqlite3_open(":memory:", hexprs->e_db.out()) != SQLITE_OK) {
746✔
4087
                log_error("unable to open memory DB");
×
4088
                return;
×
4089
            }
4090
            register_sqlite_funcs(hexprs->e_db.in(), sqlite_registration_funcs);
746✔
4091
        }
4092

4093
        for (const auto& hpair : this->elf_converter.c_header.h_exprs.he_exprs)
53,287✔
4094
        {
4095
            auto stmt_str
4096
                = fmt::format(FMT_STRING("SELECT 1 WHERE {}"), hpair.second);
8,955✔
4097
            compiled_header_expr che;
2,985✔
4098

4099
            log_info("preparing file-format header expression: %s",
2,985✔
4100
                     stmt_str.c_str());
4101
            auto retcode = sqlite3_prepare_v2(hexprs->e_db.in(),
5,970✔
4102
                                              stmt_str.c_str(),
4103
                                              stmt_str.size(),
2,985✔
4104
                                              che.che_stmt.out(),
4105
                                              nullptr);
4106
            if (retcode != SQLITE_OK) {
2,985✔
4107
                auto sql_al = attr_line_t(hpair.second)
2✔
4108
                                  .with_attr_for_all(SA_PREFORMATTED.value())
2✔
4109
                                  .with_attr_for_all(
1✔
4110
                                      VC_ROLE.value(role_t::VCR_QUOTED_CODE))
2✔
4111
                                  .move();
1✔
4112
                readline_sql_highlighter(
1✔
4113
                    sql_al, lnav::sql::dialect::sqlite, std::nullopt);
4114
                intern_string_t watch_expr_path = intern_string::lookup(
4115
                    fmt::format(FMT_STRING("/{}/converter/header/expr/{}"),
3✔
4116
                                this->elf_name,
1✔
4117
                                hpair.first));
2✔
4118
                auto snippet = lnav::console::snippet::from(
4119
                    source_location(watch_expr_path), sql_al);
1✔
4120

4121
                auto um = lnav::console::user_message::error(
2✔
4122
                              "SQL expression is invalid")
4123
                              .with_reason(sqlite3_errmsg(hexprs->e_db.in()))
2✔
4124
                              .with_snippet(snippet)
1✔
4125
                              .move();
1✔
4126

4127
                errors.emplace_back(um);
1✔
4128
                continue;
1✔
4129
            }
1✔
4130

4131
            hexprs->e_header_exprs[this->elf_name][hpair.first]
2,984✔
4132
                = std::move(che);
5,968✔
4133
        }
2,986✔
4134

4135
        if (!this->elf_converter.c_header.h_exprs.he_exprs.empty()
50,302✔
4136
            && this->elf_converter.c_command.pp_value.empty())
50,302✔
4137
        {
4138
            auto um = lnav::console::user_message::error(
2✔
4139
                          "A command is required when a converter is defined")
4140
                          .with_help(
2✔
4141
                              "The converter command transforms the file "
4142
                              "into a format that can be consumed by lnav")
4143
                          .with_snippets(this->get_snippets())
2✔
4144
                          .move();
1✔
4145
            errors.emplace_back(um);
1✔
4146
        }
1✔
4147
    }
50,302✔
4148

4149
    for (auto& vd : this->elf_value_def_order) {
494,509✔
4150
        std::vector<std::string>::iterator act_iter;
444,207✔
4151

4152
        if (!vd->vd_internal
444,207✔
4153
            && log_vtab_impl::RESERVED_COLUMNS.count(
843,676✔
4154
                vd->vd_meta.lvm_name.to_string_fragment()))
843,676✔
4155
        {
4156
            auto um = lnav::console::user_message::error(
×
4157
                          attr_line_t("value name ")
1✔
4158
                              .append_quoted(lnav::roles::symbol(
2✔
4159
                                  fmt::format(FMT_STRING("/{}/value/{}"),
4✔
4160
                                              this->elf_name,
1✔
4161
                                              vd->vd_meta.lvm_name)))
1✔
4162
                              .append(" is reserved and cannot be used"))
1✔
4163
                          .with_reason(
2✔
4164
                              "lnav automatically defines several columns in "
4165
                              "the log virtual table")
4166
                          .with_snippets(this->get_snippets())
2✔
4167
                          .with_help("Choose another name")
2✔
4168
                          .move();
1✔
4169
            errors.emplace_back(um);
1✔
4170
        }
1✔
4171

4172
        vd->vd_meta.lvm_format = this;
444,207✔
4173
        if (!vd->vd_internal
444,207✔
4174
            && !vd->vd_meta.lvm_column.is<logline_value_meta::table_column>())
444,207✔
4175
        {
4176
            vd->vd_meta.lvm_column
111,873✔
4177
                = logline_value_meta::table_column{this->elf_column_count++};
111,873✔
4178
        }
4179

4180
        if (vd->vd_meta.lvm_kind == value_kind_t::VALUE_UNKNOWN) {
444,207✔
4181
            vd->vd_meta.lvm_kind = value_kind_t::VALUE_TEXT;
×
4182
        }
4183

4184
        if (this->elf_type == elf_type_t::ELF_TYPE_TEXT) {
444,207✔
4185
            std::set<std::string> available_captures;
303,264✔
4186

4187
            bool found_in_pattern = false;
303,264✔
4188
            for (const auto& pat : this->elf_patterns) {
515,878✔
4189
                if (pat.second->p_pcre.pp_value == nullptr) {
515,876✔
4190
                    continue;
×
4191
                }
4192

4193
                auto cap_index = pat.second->p_pcre.pp_value->name_index(
1,031,752✔
4194
                    vd->vd_meta.lvm_name.get());
515,876✔
4195
                if (cap_index >= 0) {
515,876✔
4196
                    found_in_pattern = true;
303,262✔
4197
                    break;
303,262✔
4198
                }
4199

4200
                for (auto named_cap :
212,614✔
4201
                     pat.second->p_pcre.pp_value->get_named_captures())
1,940,364✔
4202
                {
4203
                    available_captures.insert(named_cap.get_name().to_string());
1,515,136✔
4204
                }
4205
            }
4206
            if (!found_in_pattern) {
303,264✔
4207
                auto notes
4208
                    = attr_line_t("the following captures are available:\n  ")
2✔
4209
                          .join(available_captures,
2✔
4210
                                VC_ROLE.value(role_t::VCR_SYMBOL),
4✔
4211
                                ", ")
4212
                          .move();
2✔
4213
                errors.emplace_back(
2✔
4214
                    lnav::console::user_message::warning(
×
4215
                        attr_line_t("invalid value ")
2✔
4216
                            .append_quoted(lnav::roles::symbol(
4✔
4217
                                fmt::format(FMT_STRING("/{}/value/{}"),
8✔
4218
                                            this->elf_name,
2✔
4219
                                            vd->vd_meta.lvm_name.get()))))
4✔
4220
                        .with_reason(
4✔
4221
                            attr_line_t("no patterns have a capture named ")
4✔
4222
                                .append_quoted(vd->vd_meta.lvm_name.get()))
2✔
4223
                        .with_note(notes)
2✔
4224
                        .with_snippets(this->get_snippets())
4✔
4225
                        .with_help("values are populated from captures in "
4226
                                   "patterns, so at least one pattern must "
4227
                                   "have a capture with this value name"));
4228
            }
2✔
4229
        }
303,264✔
4230

4231
        for (act_iter = vd->vd_action_list.begin();
444,207✔
4232
             act_iter != vd->vd_action_list.end();
444,953✔
4233
             ++act_iter)
746✔
4234
        {
4235
            if (this->lf_action_defs.find(*act_iter)
746✔
4236
                == this->lf_action_defs.end())
1,492✔
4237
            {
4238
#if 0
4239
                errors.push_back("error:" + this->elf_name.to_string() + ":"
4240
                                 + vd->vd_meta.lvm_name.get()
4241
                                 + ": cannot find action -- " + (*act_iter));
4242
#endif
4243
            }
4244
        }
4245

4246
        vd->set_rewrite_src_name();
444,207✔
4247
    }
4248

4249
    if (this->elf_type == elf_type_t::ELF_TYPE_JSON) {
50,302✔
4250
        for (const auto& vd : this->elf_value_def_order) {
149,727✔
4251
            this->elf_value_def_frag_map[vd->vd_meta.lvm_name
140,943✔
4252
                                             .to_string_fragment()]
140,943✔
4253
                = vd.get();
281,886✔
4254
        }
4255
    }
4256

4257
    for (const auto& td_pair : this->lf_tag_defs) {
52,639✔
4258
        const auto& td = td_pair.second;
2,337✔
4259

4260
        if (td->ftd_pattern.pp_value == nullptr
2,337✔
4261
            || td->ftd_pattern.pp_value->get_pattern().empty())
2,337✔
4262
        {
4263
            errors.emplace_back(
3✔
4264
                lnav::console::user_message::error(
×
4265
                    attr_line_t("invalid tag definition ")
6✔
4266
                        .append_quoted(lnav::roles::symbol(
6✔
4267
                            fmt::format(FMT_STRING("/{}/tags/{}"),
12✔
4268
                                        this->elf_name,
3✔
4269
                                        td_pair.first))))
3✔
4270
                    .with_reason(
6✔
4271
                        "tag definitions must have a non-empty pattern")
4272
                    .with_snippets(this->get_snippets()));
6✔
4273
        }
4274
    }
4275

4276
    if (this->elf_opid_field.empty()
50,302✔
4277
        && this->lf_opid_description_def->size() > 1)
50,302✔
4278
    {
4279
        errors.emplace_back(
1✔
4280
            lnav::console::user_message::error(
×
4281
                attr_line_t("too many opid descriptions")
2✔
4282
                    .append_quoted(lnav::roles::symbol(fmt::format(
2✔
4283
                        FMT_STRING("/{}/opid/description"), this->elf_name))))
4✔
4284
                .with_reason(attr_line_t("when no ")
2✔
4285
                                 .append("opid-field"_symbol)
1✔
4286
                                 .append(" is specified, only a single "
1✔
4287
                                         "description is supported"))
4288
                .with_snippets(this->get_snippets()));
2✔
4289
    }
4290

4291
    for (const auto& opid_desc_pair : *this->lf_opid_description_def) {
72,684✔
4292
        for (const auto& opid_desc : *opid_desc_pair.second.od_descriptors) {
52,970✔
4293
            auto iter = this->elf_value_defs.find(opid_desc.od_field.pp_value);
30,588✔
4294
            if (iter == this->elf_value_defs.end()) {
30,588✔
4295
                errors.emplace_back(
2✔
4296
                    lnav::console::user_message::error(
×
4297
                        attr_line_t("invalid opid description field ")
4✔
4298
                            .append_quoted(lnav::roles::symbol(
4✔
4299
                                opid_desc.od_field.pp_path.to_string())))
4✔
4300
                        .with_reason(
4✔
4301
                            attr_line_t("unknown value name ")
4✔
4302
                                .append_quoted(opid_desc.od_field.pp_value))
2✔
4303
                        .with_snippets(this->get_snippets()));
4✔
4304
            } else {
4305
                this->lf_desc_fields.insert(iter->first);
30,586✔
4306
                iter->second->vd_is_desc_field = true;
30,586✔
4307
            }
4308
        }
4309
    }
4310

4311
    for (const auto& subid_desc_pair : *this->lf_subid_description_def) {
51,048✔
4312
        for (const auto& subid_desc : *subid_desc_pair.second.od_descriptors) {
1,492✔
4313
            auto iter = this->elf_value_defs.find(subid_desc.od_field.pp_value);
746✔
4314
            if (iter == this->elf_value_defs.end()) {
746✔
4315
                errors.emplace_back(
×
4316
                    lnav::console::user_message::error(
×
4317
                        attr_line_t("invalid subid description field ")
×
4318
                            .append_quoted(lnav::roles::symbol(
×
4319
                                subid_desc.od_field.pp_path.to_string())))
×
4320
                        .with_reason(
×
4321
                            attr_line_t("unknown value name ")
×
4322
                                .append_quoted(subid_desc.od_field.pp_value))
×
4323
                        .with_snippets(this->get_snippets()));
×
4324
            } else {
4325
                this->lf_desc_fields.insert(iter->first);
746✔
4326
                iter->second->vd_is_desc_field = true;
746✔
4327
            }
4328
        }
4329
    }
4330

4331
    if (this->elf_type == elf_type_t::ELF_TYPE_TEXT
100,604✔
4332
        && this->elf_samples.empty())
50,302✔
4333
    {
4334
        errors.emplace_back(
3✔
4335
            lnav::console::user_message::error(
×
4336
                attr_line_t()
6✔
4337
                    .append_quoted(
3✔
4338
                        lnav::roles::symbol(this->elf_name.to_string()))
6✔
4339
                    .append(" is not a valid log format"))
3✔
4340
                .with_reason("log message samples must be included in a format "
6✔
4341
                             "definition")
4342
                .with_snippets(this->get_snippets()));
6✔
4343
    }
4344

4345
    for (const auto& pat : this->elf_pattern_order) {
146,377✔
4346
        if (this->elf_type != elf_type_t::ELF_TYPE_TEXT) {
96,075✔
4347
            continue;
1✔
4348
        }
4349
        if (pat->p_pcre.pp_value->name_index(this->lf_timestamp_field.get())
96,074✔
4350
            < 0)
96,074✔
4351
        {
4352
            attr_line_t notes;
1✔
4353
            bool first_note = true;
1✔
4354

4355
            if (pat->p_pcre.pp_value->get_capture_count() > 0) {
1✔
4356
                notes.append("the following captures are available:\n  ");
1✔
4357
            }
4358
            for (auto named_cap : pat->p_pcre.pp_value->get_named_captures()) {
4✔
4359
                if (!first_note) {
3✔
4360
                    notes.append(", ");
2✔
4361
                }
4362
                notes.append(
3✔
4363
                    lnav::roles::symbol(named_cap.get_name().to_string()));
6✔
4364
                first_note = false;
3✔
4365
            }
4366
            errors.emplace_back(
1✔
4367
                lnav::console::user_message::error(
×
4368
                    attr_line_t("invalid value for property ")
1✔
4369
                        .append_quoted(lnav::roles::symbol(
2✔
4370
                            fmt::format(FMT_STRING("/{}/timestamp-field"),
4✔
4371
                                        this->elf_name))))
1✔
4372
                    .with_reason(
2✔
4373
                        attr_line_t()
2✔
4374
                            .append_quoted(this->lf_timestamp_field)
1✔
4375
                            .append(" was not found in the pattern at ")
1✔
4376
                            .append(lnav::roles::symbol(pat->p_config_path)))
2✔
4377
                    .with_note(notes)
1✔
4378
                    .with_snippets(this->get_snippets()));
2✔
4379
        }
1✔
4380
    }
4381

4382
    for (size_t sample_index = 0; sample_index < this->elf_samples.size();
232,358✔
4383
         sample_index += 1)
182,056✔
4384
    {
4385
        auto& elf_sample = this->elf_samples[sample_index];
182,056✔
4386
        auto sample_lines
4387
            = string_fragment(elf_sample.s_line.pp_value).split_lines();
182,056✔
4388

4389
        if (this->test_line(elf_sample, errors).is<scan_match>()) {
182,056✔
4390
            for (const auto& pat_name : elf_sample.s_matched_regexes) {
361,872✔
4391
                this->elf_patterns[pat_name]->p_matched_samples.emplace(
179,817✔
4392
                    sample_index);
4393
            }
4394
        }
4395
    }
182,056✔
4396

4397
    if (!this->elf_samples.empty()) {
50,302✔
4398
        for (const auto& elf_sample : this->elf_samples) {
224,317✔
4399
            if (elf_sample.s_matched_regexes.size() <= 1) {
182,056✔
4400
                continue;
182,056✔
4401
            }
4402

4403
            errors.emplace_back(
×
4404
                lnav::console::user_message::warning(
×
4405
                    attr_line_t("invalid log format: ")
×
4406
                        .append_quoted(
×
4407
                            lnav::roles::symbol(this->elf_name.to_string())))
×
4408
                    .with_reason(
×
4409
                        attr_line_t(
×
4410
                            "sample is matched by more than one regex: ")
4411
                            .join(elf_sample.s_matched_regexes,
×
4412
                                  VC_ROLE.value(role_t::VCR_SYMBOL),
×
4413
                                  ", "))
4414
                    .with_snippet(lnav::console::snippet::from(
×
4415
                        elf_sample.s_line.pp_location,
4416
                        attr_line_t().append(lnav::roles::quoted_code(
×
4417
                            elf_sample.s_line.pp_value))))
×
4418
                    .with_help("log format regexes must match a single type "
4419
                               "of log message"));
4420
        }
4421

4422
        for (const auto& pat : this->elf_pattern_order) {
138,333✔
4423
            if (pat->p_matched_samples.empty()) {
96,072✔
4424
                errors.emplace_back(
2✔
4425
                    lnav::console::user_message::warning(
×
4426
                        attr_line_t("invalid pattern: ")
4✔
4427
                            .append_quoted(
2✔
4428
                                lnav::roles::symbol(pat->p_config_path)))
4✔
4429
                        .with_reason("pattern does not match any samples")
4✔
4430
                        .with_snippet(lnav::console::snippet::from(
6✔
4431
                            pat->p_pcre.pp_location, ""))
2✔
4432
                        .with_help(
4433
                            "every pattern should have at least one sample "
4434
                            "that it matches"));
4435
            }
4436
        }
4437
    }
4438

4439
    size_t value_def_index = 0;
50,302✔
4440
    for (auto& elf_value_def : this->elf_value_def_order) {
494,509✔
4441
        elf_value_def->vd_meta.lvm_values_index
444,207✔
4442
            = std::make_optional(value_def_index++);
444,207✔
4443

4444
        if (elf_value_def->vd_meta.lvm_foreign_key
444,207✔
4445
            || elf_value_def->vd_meta.lvm_identifier)
444,207✔
4446
        {
4447
            continue;
235,493✔
4448
        }
4449

4450
        switch (elf_value_def->vd_meta.lvm_kind) {
208,714✔
4451
            case value_kind_t::VALUE_INTEGER:
52,604✔
4452
            case value_kind_t::VALUE_FLOAT:
4453
                this->elf_numeric_value_defs.push_back(elf_value_def);
52,604✔
4454
                break;
52,604✔
4455
            default:
156,110✔
4456
                break;
156,110✔
4457
        }
4458
    }
4459

4460
    int format_index = 0;
50,302✔
4461
    for (auto iter = this->jlf_line_format.begin();
50,302✔
4462
         iter != this->jlf_line_format.end();
155,047✔
4463
         ++iter, format_index++)
104,745✔
4464
    {
4465
        static const intern_string_t ts
4466
            = intern_string::lookup("__timestamp__");
106,237✔
4467
        static const intern_string_t level_field
4468
            = intern_string::lookup("__level__");
106,237✔
4469
        auto& jfe = *iter;
104,745✔
4470

4471
        if (startswith(jfe.jfe_value.pp_value.get(), "/")) {
104,745✔
4472
            jfe.jfe_value.pp_value
4473
                = intern_string::lookup(jfe.jfe_value.pp_value.get() + 1);
192✔
4474
        }
4475
        if (!jfe.jfe_ts_format.empty()) {
104,745✔
4476
            if (!jfe.jfe_value.pp_value.empty() && jfe.jfe_value.pp_value != ts)
842✔
4477
            {
4478
                log_warning(
96✔
4479
                    "%s:line-format[%d]:ignoring field '%s' since "
4480
                    "timestamp-format was used",
4481
                    this->elf_name.get(),
4482
                    format_index,
4483
                    jfe.jfe_value.pp_value.get());
4484
            }
4485
            jfe.jfe_value.pp_value = ts;
842✔
4486
        }
4487

4488
        switch (jfe.jfe_type) {
104,745✔
4489
            case json_log_field::VARIABLE: {
68,701✔
4490
                auto vd_iter
4491
                    = this->elf_value_defs.find(jfe.jfe_value.pp_value);
68,701✔
4492
                if (jfe.jfe_value.pp_value == ts) {
68,701✔
4493
                    this->elf_value_defs[this->lf_timestamp_field]
7,003✔
4494
                        ->vd_meta.lvm_hidden
7,003✔
4495
                        = true;
7,003✔
4496
                } else if (jfe.jfe_value.pp_value == level_field) {
61,698✔
4497
                    this->elf_value_defs[this->elf_level_field]
2,984✔
4498
                        ->vd_meta.lvm_hidden
2,984✔
4499
                        = true;
2,984✔
4500
                } else if (vd_iter == this->elf_value_defs.end()) {
58,714✔
4501
                    errors.emplace_back(
2✔
4502
                        lnav::console::user_message::error(
×
4503
                            attr_line_t("invalid line format element ")
4✔
4504
                                .append_quoted(lnav::roles::symbol(fmt::format(
4✔
4505
                                    FMT_STRING("/{}/line-format/{}/field"),
6✔
4506
                                    this->elf_name,
2✔
4507
                                    format_index))))
4508
                            .with_reason(
4✔
4509
                                attr_line_t()
4✔
4510
                                    .append_quoted(jfe.jfe_value.pp_value)
2✔
4511
                                    .append(" is not a defined value"))
2✔
4512
                            .with_snippet(jfe.jfe_value.to_snippet()));
4✔
4513
                } else {
4514
                    vd_iter->second->vd_line_format_index = format_index;
58,712✔
4515
                    switch (vd_iter->second->vd_meta.lvm_kind) {
58,712✔
4516
                        case value_kind_t::VALUE_INTEGER:
9,890✔
4517
                        case value_kind_t::VALUE_FLOAT:
4518
                            if (jfe.jfe_align
9,890✔
4519
                                == json_format_element::align_t::NONE)
4520
                            {
4521
                                jfe.jfe_align
4522
                                    = json_format_element::align_t::RIGHT;
9,144✔
4523
                            }
4524
                            break;
9,890✔
4525
                        default:
48,822✔
4526
                            break;
48,822✔
4527
                    }
4528
                }
4529
                break;
68,701✔
4530
            }
4531
            case json_log_field::CONSTANT:
36,044✔
4532
                this->jlf_line_format_init_count
36,044✔
4533
                    += std::count(jfe.jfe_default_value.begin(),
36,044✔
4534
                                  jfe.jfe_default_value.end(),
4535
                                  '\n');
36,044✔
4536
                break;
36,044✔
4537
            default:
×
4538
                break;
×
4539
        }
4540
    }
4541

4542
    for (auto& hd_pair : this->elf_highlighter_patterns) {
50,688✔
4543
        auto& hd = hd_pair.second;
386✔
4544
        text_attrs attrs;
386✔
4545

4546
        if (!hd.hd_color.pp_value.empty()) {
386✔
4547
            attrs.ta_fg_color = vc.match_color(
385✔
4548
                styling::color_unit::from_str(hd.hd_color.pp_value)
770✔
4549
                    .unwrapOrElse([&](const auto& msg) {
1✔
4550
                        errors.emplace_back(
1✔
4551
                            lnav::console::user_message::error(
4552
                                attr_line_t()
1✔
4553
                                    .append_quoted(hd.hd_color.pp_value)
2✔
4554
                                    .append(" is not a valid color value for "
1✔
4555
                                            "property ")
4556
                                    .append_quoted(lnav::roles::symbol(
2✔
4557
                                        hd.hd_color.pp_path.to_string())))
1✔
4558
                                .with_reason(msg)
2✔
4559
                                .with_snippet(hd.hd_color.to_snippet()));
1✔
4560
                        return styling::color_unit::EMPTY;
1✔
4561
                    }));
4562
        }
4563

4564
        if (!hd.hd_background_color.pp_value.empty()) {
386✔
4565
            attrs.ta_bg_color = vc.match_color(
1✔
4566
                styling::color_unit::from_str(hd.hd_background_color.pp_value)
2✔
4567
                    .unwrapOrElse([&](const auto& msg) {
1✔
4568
                        errors.emplace_back(
1✔
4569
                            lnav::console::user_message::error(
4570
                                attr_line_t()
1✔
4571
                                    .append_quoted(
2✔
4572
                                        hd.hd_background_color.pp_value)
1✔
4573
                                    .append(" is not a valid color value for "
1✔
4574
                                            "property ")
4575
                                    .append_quoted(lnav::roles::symbol(
2✔
4576
                                        hd.hd_background_color.pp_path
1✔
4577
                                            .to_string())))
4578
                                .with_reason(msg)
2✔
4579
                                .with_snippet(
4580
                                    hd.hd_background_color.to_snippet()));
1✔
4581
                        return styling::color_unit::EMPTY;
1✔
4582
                    }));
4583
        }
4584

4585
        if (hd.hd_underline) {
386✔
4586
            attrs |= text_attrs::style::underline;
96✔
4587
        }
4588
        if (hd.hd_blink) {
386✔
4589
            attrs |= text_attrs::style::blink;
×
4590
        }
4591

4592
        if (hd.hd_pattern.pp_value != nullptr) {
386✔
4593
            this->lf_highlighters.emplace_back(hd.hd_pattern.pp_value);
384✔
4594
            this->lf_highlighters.back()
384✔
4595
                .with_name(hd_pair.first.to_string())
768✔
4596
                .with_format_name(this->elf_name)
384✔
4597
                .with_attrs(attrs)
384✔
4598
                .with_nestable(hd.hd_nestable);
384✔
4599
        }
4600
    }
4601
}
4602

4603
void
4604
external_log_format::register_vtabs(
42,272✔
4605
    log_vtab_manager* vtab_manager,
4606
    std::vector<lnav::console::user_message>& errors)
4607
{
4608
    for (auto& elf_search_table : this->elf_search_tables) {
51,121✔
4609
        if (elf_search_table.second.std_pattern.pp_value == nullptr) {
8,849✔
4610
            continue;
1✔
4611
        }
4612

4613
        auto lst = std::make_shared<log_search_table>(
4614
            elf_search_table.second.std_pattern.pp_value,
8,848✔
4615
            elf_search_table.first);
8,848✔
4616
        lst->lst_format = this;
8,848✔
4617
        lst->lst_log_path_glob = elf_search_table.second.std_glob;
8,848✔
4618
        if (elf_search_table.second.std_level != LEVEL_UNKNOWN) {
8,848✔
4619
            lst->lst_log_level = elf_search_table.second.std_level;
3,160✔
4620
        }
4621
        auto errmsg = vtab_manager->register_vtab(lst);
8,848✔
4622
        if (!errmsg.empty()) {
8,848✔
4623
#if 0
4624
            errors.push_back("error:" + this->elf_name.to_string() + ":"
4625
                             + search_iter->first.to_string()
4626
                             + ":unable to register table -- " + errmsg);
4627
#endif
4628
        }
4629
    }
8,848✔
4630
}
42,272✔
4631

4632
bool
4633
external_log_format::match_samples(const std::vector<sample_t>& samples) const
3,351,670✔
4634
{
4635
    for (const auto& sample_iter : samples) {
15,324,209✔
4636
        for (const auto& pat_iter : this->elf_pattern_order) {
33,433,252✔
4637
            auto& pat = *pat_iter;
21,460,713✔
4638

4639
            if (!pat.p_pcre.pp_value) {
21,460,713✔
4640
                continue;
×
4641
            }
4642

4643
            if (pat.p_pcre.pp_value
42,921,426✔
4644
                    ->find_in(sample_iter.s_line.pp_value, PCRE2_NO_UTF_CHECK)
42,921,426✔
4645
                    .ignore_error())
42,921,426✔
4646
            {
4647
                return true;
15,538✔
4648
            }
4649
        }
4650
    }
4651

4652
    return false;
3,336,132✔
4653
}
4654

4655
class external_log_table : public log_format_vtab_impl {
4656
public:
4657
    explicit external_log_table(std::shared_ptr<const log_format> elf)
42,272✔
4658
        : log_format_vtab_impl(elf),
42,272✔
4659
          elt_format(dynamic_cast<const external_log_format*>(elf.get()))
42,272✔
4660
    {
4661
    }
42,272✔
4662

4663
    void get_columns(std::vector<vtab_column>& cols) const override
42,664✔
4664
    {
4665
        const auto& elf = this->elt_format;
42,664✔
4666

4667
        cols.resize(elf->elf_column_count);
42,664✔
4668
        for (const auto& vd : elf->elf_value_def_order) {
421,366✔
4669
            auto type_pair = logline_value_to_sqlite_type(vd->vd_meta.lvm_kind);
378,702✔
4670

4671
            if (!vd->vd_meta.lvm_column.is<logline_value_meta::table_column>())
378,702✔
4672
            {
4673
                continue;
37,309✔
4674
            }
4675

4676
            auto col
4677
                = vd->vd_meta.lvm_column.get<logline_value_meta::table_column>()
341,393✔
4678
                      .value;
341,393✔
4679
            require(0 <= col && col < elf->elf_column_count);
341,393✔
4680

4681
            cols[col].vc_name = vd->vd_meta.lvm_name.get();
341,393✔
4682
            cols[col].vc_type = type_pair.first;
341,393✔
4683
            cols[col].vc_subtype = type_pair.second;
341,393✔
4684
            cols[col].vc_collator = vd->vd_collate;
341,393✔
4685
            cols[col].vc_comment = vd->vd_description;
341,393✔
4686
        }
4687
    }
42,664✔
4688

4689
    void get_foreign_keys(
34,452✔
4690
        std::unordered_set<std::string>& keys_inout) const override
4691
    {
4692
        log_vtab_impl::get_foreign_keys(keys_inout);
34,452✔
4693

4694
        for (const auto& elf_value_def : this->elt_format->elf_value_defs) {
497,421✔
4695
            if (elf_value_def.second->vd_meta.lvm_foreign_key
462,969✔
4696
                || elf_value_def.second->vd_meta.lvm_identifier)
462,969✔
4697
            {
4698
                keys_inout.emplace(elf_value_def.first.to_string());
167,535✔
4699
            }
4700
        }
4701
    }
34,452✔
4702

4703
    bool next(log_cursor& lc, logfile_sub_source& lss) override
3,476✔
4704
    {
4705
        if (lc.is_eof()) {
3,476✔
4706
            return true;
×
4707
        }
4708

4709
        content_line_t cl(lss.at(lc.lc_curr_line));
3,476✔
4710
        auto* lf = lss.find_file_ptr(cl);
3,476✔
4711
        auto lf_iter = lf->begin() + cl;
3,476✔
4712
        if (lf_iter->is_continued()) {
3,476✔
4713
            return false;
×
4714
        }
4715

4716
        if (lf->get_format_name() == this->lfvi_format->get_name()) {
3,476✔
4717
            return true;
3,470✔
4718
        }
4719

4720
        return false;
6✔
4721
    }
4722

4723
    void extract(logfile* lf,
3,385✔
4724
                 uint64_t line_number,
4725
                 string_attrs_t& sa,
4726
                 logline_value_vector& values) override
4727
    {
4728
        auto format = lf->get_format();
3,385✔
4729

4730
        sa.clear();
3,385✔
4731
        format->annotate(lf, line_number, sa, values);
3,385✔
4732
    }
3,385✔
4733

4734
    const external_log_format* elt_format;
4735
    line_range elt_container_body;
4736
};
4737

4738
std::shared_ptr<log_vtab_impl>
4739
external_log_format::get_vtab_impl() const
42,272✔
4740
{
4741
    return std::make_shared<external_log_table>(this->shared_from_this());
42,272✔
4742
}
4743

4744
std::shared_ptr<log_format>
4745
external_log_format::specialized(int fmt_lock)
507✔
4746
{
4747
    auto retval = std::make_shared<external_log_format>(*this);
507✔
4748

4749
    retval->lf_specialized = true;
507✔
4750
    if (this->elf_type == elf_type_t::ELF_TYPE_JSON) {
507✔
4751
        this->jlf_parse_context
4752
            = std::make_shared<yajlpp_parse_context>(this->elf_name);
47✔
4753
        this->jlf_yajl_handle.reset(
47✔
4754
            yajl_alloc(&this->jlf_parse_context->ypc_callbacks,
47✔
4755
                       nullptr,
4756
                       this->jlf_parse_context.get()),
47✔
4757
            yajl_handle_deleter());
4758
        yajl_config(this->jlf_yajl_handle.get(), yajl_dont_validate_strings, 1);
47✔
4759
        this->jlf_cached_line.reserve(16 * 1024);
47✔
4760
    }
4761

4762
    this->elf_specialized_value_defs_state = *this->elf_value_defs_state;
507✔
4763

4764
    return retval;
1,014✔
4765
}
507✔
4766

4767
log_format::match_name_result
4768
external_log_format::match_name(const std::string& filename)
819,617✔
4769
{
4770
    if (this->elf_filename_pcre.pp_value == nullptr) {
819,617✔
4771
        return name_matched{};
818,235✔
4772
    }
4773

4774
    if (this->elf_filename_pcre.pp_value->find_in(filename)
2,764✔
4775
            .ignore_error()
2,764✔
4776
            .has_value())
1,382✔
4777
    {
4778
        return name_matched{};
238✔
4779
    }
4780

4781
    return name_mismatched{
2,288✔
4782
        this->elf_filename_pcre.pp_value->match_partial(filename),
2,288✔
4783
        this->elf_filename_pcre.pp_value->get_pattern(),
1,144✔
4784
    };
1,144✔
4785
}
4786

4787
auto
4788
external_log_format::value_line_count(scan_batch_context& sbc,
207,365✔
4789
                                      const value_def* vd,
4790
                                      bool top_level,
4791
                                      std::optional<double> val,
4792
                                      const unsigned char* str,
4793
                                      ssize_t len,
4794
                                      yajl_string_props_t* props)
4795
    -> value_line_count_result
4796
{
4797
    value_line_count_result retval;
207,365✔
4798

4799
    if (vd == nullptr) {
207,365✔
4800
        if (this->jlf_hide_extra || !top_level) {
191,893✔
4801
            retval.vlcr_count = 0;
178,064✔
4802
        }
4803

4804
        return retval;
191,893✔
4805
    }
4806

4807
    if (str != nullptr && props != nullptr && !val) {
15,472✔
4808
        auto frag = string_fragment::from_bytes(str, len);
13,815✔
4809
        while (frag.endswith("\n")) {
13,984✔
4810
            frag.pop_back();
169✔
4811
            props->line_feeds -= 1;
169✔
4812
        }
4813
        retval.vlcr_has_ansi |= props->has_ansi;
13,815✔
4814
        retval.vlcr_count += props->line_feeds;
13,815✔
4815
    }
4816

4817
    if (vd->vd_meta.lvm_values_index) {
15,472✔
4818
        auto& lvs = sbc.sbc_value_stats[vd->vd_meta.lvm_values_index.value()];
6,520✔
4819
        if (len > lvs.lvs_width) {
6,520✔
4820
            lvs.lvs_width = len;
3,410✔
4821
        }
4822
        if (val) {
6,520✔
4823
            lvs.add_value(val.value());
168✔
4824
        }
4825
    }
4826

4827
    if (vd->vd_line_format_index) {
15,472✔
4828
        retval.vlcr_line_format_count += 1;
3,205✔
4829
        retval.vlcr_count -= 1;
3,205✔
4830
        retval.vlcr_line_format_index = vd->vd_line_format_index;
3,205✔
4831
    }
4832
    if (vd->vd_meta.is_hidden()) {
15,472✔
4833
        retval.vlcr_count = 0;
3,072✔
4834
        return retval;
3,072✔
4835
    }
4836

4837
    return retval;
12,400✔
4838
}
4839

4840
log_level_t
4841
external_log_format::convert_level(string_fragment sf,
189,290✔
4842
                                   scan_batch_context* sbc) const
4843
{
4844
    auto retval = LEVEL_INFO;
189,290✔
4845

4846
    if (sf.is_valid()) {
189,290✔
4847
        if (sbc != nullptr) {
147,845✔
4848
            auto ssm_res = sbc->sbc_level_cache.lookup(sf);
9,254✔
4849
            if (ssm_res.has_value()) {
9,254✔
4850
                return static_cast<log_level_t>(ssm_res.value());
4,088✔
4851
            }
4852
        }
4853

4854
        if (this->elf_level_patterns.empty()) {
143,757✔
4855
            retval = string2level(sf.data(), sf.length());
30,231✔
4856
        } else {
4857
            for (const auto& elf_level_pattern : this->elf_level_patterns) {
305,961✔
4858
                if (elf_level_pattern.second.lp_pcre.pp_value
560,034✔
4859
                        ->find_in(sf, PCRE2_NO_UTF_CHECK)
560,034✔
4860
                        .ignore_error()
560,034✔
4861
                        .has_value())
280,017✔
4862
                {
4863
                    retval = elf_level_pattern.first;
87,582✔
4864
                    break;
87,582✔
4865
                }
4866
            }
4867
        }
4868

4869
        if (sbc != nullptr
143,757✔
4870
            && sf.length() <= lnav::small_string_map::MAX_KEY_SIZE)
143,757✔
4871
        {
4872
            sbc->sbc_level_cache.insert(sf, retval);
4,519✔
4873
        }
4874
    }
4875

4876
    return retval;
185,202✔
4877
}
4878

4879
logline_value_meta
4880
external_log_format::get_value_meta(intern_string_t field_name,
15,244✔
4881
                                    value_kind_t kind)
4882
{
4883
    const auto iter = this->elf_value_defs.find(field_name);
15,244✔
4884
    if (iter == this->elf_value_defs.end()) {
15,244✔
4885
        auto retval = logline_value_meta(
4886
            field_name, kind, logline_value_meta::external_column{}, this);
967✔
4887

4888
        retval.lvm_hidden = this->jlf_hide_extra;
967✔
4889
        return retval;
967✔
4890
    }
967✔
4891

4892
    auto lvm = iter->second->vd_meta;
14,277✔
4893

4894
    lvm.lvm_kind = kind;
14,277✔
4895
    return lvm;
14,277✔
4896
}
14,277✔
4897

4898
logline_value_meta
4899
external_log_format::get_value_meta(yajlpp_parse_context* ypc,
2,398✔
4900
                                    const value_def* vd,
4901
                                    value_kind_t kind)
4902
{
4903
    if (vd == nullptr) {
2,398✔
4904
        auto retval = logline_value_meta(
4905
            ypc->get_path(), kind, logline_value_meta::external_column{}, this);
57✔
4906

4907
        retval.lvm_hidden = this->jlf_hide_extra;
57✔
4908
        return retval;
57✔
4909
    }
57✔
4910

4911
    auto lvm = vd->vd_meta;
2,341✔
4912

4913
    lvm.lvm_kind = kind;
2,341✔
4914
    return lvm;
2,341✔
4915
}
2,341✔
4916

4917
void
4918
external_log_format::json_append(const log_format_file_state& lffs,
8,911✔
4919
                                 const json_format_element& jfe,
4920
                                 const value_def* vd,
4921
                                 const string_fragment& sf)
4922
{
4923
    if (jfe.jfe_align == json_format_element::align_t::RIGHT) {
8,911✔
4924
        auto sf_width = sf.column_width();
346✔
4925
        if (sf_width < jfe.jfe_min_width) {
346✔
4926
            this->json_append_to_cache(jfe.jfe_min_width - sf_width);
×
4927
        } else if (jfe.jfe_auto_width && vd != nullptr
32✔
4928
                   && sf_width
378✔
4929
                       < lffs.lffs_value_stats[vd->vd_meta.lvm_values_index
32✔
4930
                                                   .value()]
32✔
4931
                             .lvs_width)
32✔
4932
        {
4933
            this->json_append_to_cache(
8✔
4934
                lffs.lffs_value_stats[vd->vd_meta.lvm_values_index.value()]
8✔
4935
                    .lvs_width
8✔
4936
                - sf_width);
8✔
4937
        }
4938
    }
4939
    this->json_append_to_cache(sf.data(), sf.length());
8,911✔
4940
    if ((jfe.jfe_align == json_format_element::align_t::LEFT
8,911✔
4941
         || jfe.jfe_align == json_format_element::align_t::NONE)
8,853✔
4942
        && (jfe.jfe_min_width > 0 || jfe.jfe_auto_width))
8,565✔
4943
    {
4944
        auto sf_width = sf.column_width();
1,226✔
4945
        if (sf_width < jfe.jfe_min_width) {
1,226✔
4946
            this->json_append_to_cache(jfe.jfe_min_width - sf_width);
369✔
4947
        } else if (jfe.jfe_auto_width && vd != nullptr
732✔
4948
                   && sf_width
1,589✔
4949
                       < lffs.lffs_value_stats[vd->vd_meta.lvm_values_index
644✔
4950
                                                   .value()]
644✔
4951
                             .lvs_width)
644✔
4952
        {
4953
            this->json_append_to_cache(
×
4954
                lffs.lffs_value_stats[vd->vd_meta.lvm_values_index.value()]
×
4955
                    .lvs_width
×
4956
                - sf_width);
×
4957
        }
4958
    }
4959
}
8,911✔
4960

4961
intern_string_t
4962
external_log_format::get_pattern_name(const pattern_locks& pl,
50✔
4963
                                      uint64_t line_number) const
4964
{
4965
    if (this->elf_type != elf_type_t::ELF_TYPE_TEXT) {
50✔
4966
        static auto structured = intern_string::lookup("structured");
4967

4968
        return structured;
×
4969
    }
4970
    auto pat_index = pl.pattern_index_for_line(line_number);
50✔
4971
    return this->elf_pattern_order[pat_index]->p_name;
50✔
4972
}
4973

4974
std::string
4975
log_format::get_pattern_path(const pattern_locks& pl,
×
4976
                             uint64_t line_number) const
4977
{
4978
    auto pat_index = pl.pattern_index_for_line(line_number);
×
4979
    return fmt::format(FMT_STRING("builtin ({})"), pat_index);
×
4980
}
4981

4982
intern_string_t
4983
log_format::get_pattern_name(const pattern_locks& pl,
6✔
4984
                             uint64_t line_number) const
4985
{
4986
    char pat_str[128];
4987

4988
    auto pat_index = pl.pattern_index_for_line(line_number);
6✔
4989
    auto to_n_res = fmt::format_to_n(
×
4990
        pat_str, sizeof(pat_str) - 1, FMT_STRING("builtin ({})"), pat_index);
18✔
4991
    pat_str[to_n_res.size] = '\0';
6✔
4992
    return intern_string::lookup(pat_str);
12✔
4993
}
4994

4995
std::shared_ptr<log_format>
4996
log_format::find_root_format(const char* name)
1,274✔
4997
{
4998
    auto& fmts = get_root_formats();
1,274✔
4999
    for (auto& lf : fmts) {
48,113✔
5000
        if (lf->get_name() == name) {
48,113✔
5001
            return lf;
1,274✔
5002
        }
5003
    }
5004
    return nullptr;
×
5005
}
5006

5007
exttm
5008
log_format::tm_for_display(logfile::iterator ll, string_fragment sf)
579✔
5009
{
5010
    auto adjusted_time = ll->get_timeval();
579✔
5011
    exttm retval;
579✔
5012

5013
    retval.et_nsec = std::chrono::duration_cast<std::chrono::nanoseconds>(
579✔
5014
                         std::chrono::microseconds{adjusted_time.tv_usec})
×
5015
                         .count();
579✔
5016
    if (this->lf_timestamp_flags & ETF_NANOS_SET) {
579✔
5017
        timeval actual_tv;
5018
        exttm tm;
×
5019
        if (this->lf_date_time.scan(sf.data(),
×
5020
                                    sf.length(),
×
5021
                                    this->get_timestamp_formats(),
5022
                                    &tm,
5023
                                    actual_tv,
5024
                                    false))
5025
        {
5026
            adjusted_time.tv_usec = actual_tv.tv_usec;
×
5027
            retval.et_nsec = tm.et_nsec;
×
5028
        }
5029
    }
5030
    gmtime_r(&adjusted_time.tv_sec, &retval.et_tm);
579✔
5031
    retval.et_flags = this->lf_timestamp_flags;
579✔
5032
    if (this->lf_timestamp_flags & ETF_ZONE_SET
579✔
5033
        && this->lf_date_time.dts_zoned_to_local)
489✔
5034
    {
5035
        retval.et_flags &= ~ETF_Z_IS_UTC;
489✔
5036
    }
5037
    retval.et_gmtoff = this->lf_date_time.dts_local_offset_cache;
579✔
5038

5039
    return retval;
1,158✔
5040
}
5041

5042
pattern_for_lines::pattern_for_lines(uint32_t pfl_line, uint32_t pfl_pat_index)
2,855✔
5043
    : pfl_line(pfl_line), pfl_pat_index(pfl_pat_index)
2,855✔
5044
{
5045
}
2,855✔
5046

5047
void
5048
logline_value_stats::merge(const logline_value_stats& other)
15,203✔
5049
{
5050
    if (other.lvs_count == 0) {
15,203✔
5051
        return;
14,065✔
5052
    }
5053

5054
    require(other.lvs_min_value <= other.lvs_max_value);
1,138✔
5055

5056
    if (other.lvs_width > this->lvs_width) {
1,138✔
5057
        this->lvs_width = other.lvs_width;
757✔
5058
    }
5059
    if (other.lvs_min_value < this->lvs_min_value) {
1,138✔
5060
        this->lvs_min_value = other.lvs_min_value;
784✔
5061
    }
5062
    if (other.lvs_max_value > this->lvs_max_value) {
1,138✔
5063
        this->lvs_max_value = other.lvs_max_value;
1,067✔
5064
    }
5065
    this->lvs_count += other.lvs_count;
1,138✔
5066
    this->lvs_total += other.lvs_total;
1,138✔
5067
    this->lvs_tdigest.insert(other.lvs_tdigest);
1,138✔
5068
    ensure(this->lvs_count >= 0);
1,138✔
5069
    ensure(this->lvs_min_value <= this->lvs_max_value);
1,138✔
5070
}
5071

5072
void
5073
logline_value_stats::add_value(double value)
31,426✔
5074
{
5075
    if (value < this->lvs_min_value) {
31,426✔
5076
        this->lvs_min_value = value;
1,661✔
5077
    }
5078
    if (value > this->lvs_max_value) {
31,426✔
5079
        this->lvs_max_value = value;
2,353✔
5080
    }
5081
    this->lvs_count += 1;
31,426✔
5082
    this->lvs_total += value;
31,426✔
5083
    this->lvs_tdigest.insert(value);
31,426✔
5084
}
31,426✔
5085

5086
std::vector<logline_value_meta>
5087
external_log_format::get_value_metadata() const
8,848✔
5088
{
5089
    std::vector<logline_value_meta> retval;
8,848✔
5090

5091
    for (const auto& vd : this->elf_value_def_order) {
92,904✔
5092
        retval.emplace_back(vd->vd_meta);
84,056✔
5093
    }
5094

5095
    return retval;
8,848✔
UNCOV
5096
}
×
5097

5098
std::optional<size_t>
5099
external_log_format::stats_index_for_value(const intern_string_t& name) const
72✔
5100
{
5101
    const auto iter = this->elf_value_defs.find(name);
72✔
5102
    if (iter != this->elf_value_defs.end()
72✔
5103
        && iter->second->vd_meta.lvm_values_index)
72✔
5104
    {
5105
        return iter->second->vd_meta.lvm_values_index.value();
72✔
5106
    }
5107

UNCOV
5108
    return std::nullopt;
×
5109
}
5110

5111
std::string
5112
external_log_format::get_pattern_regex(const pattern_locks& pl,
9✔
5113
                                       uint64_t line_number) const
5114
{
5115
    if (this->elf_type != elf_type_t::ELF_TYPE_TEXT) {
9✔
5116
        return "";
2✔
5117
    }
5118
    auto pat_index = pl.pattern_index_for_line(line_number);
8✔
5119
    return this->elf_pattern_order[pat_index]->p_pcre.pp_value->get_pattern();
8✔
5120
}
5121

5122
bool
5123
external_log_format::hide_field(const intern_string_t field_name, bool val)
12✔
5124
{
5125
    const auto vd_iter = this->elf_value_defs.find(field_name);
12✔
5126
    if (vd_iter == this->elf_value_defs.end()) {
12✔
5127
        log_warning("field to hide not found: %s.%s",
1✔
5128
                    this->elf_name.c_str(),
5129
                    field_name.c_str());
5130
        return false;
1✔
5131
    }
5132

5133
    vd_iter->second->vd_meta.lvm_user_hidden = val;
11✔
5134
    if (this->elf_type == elf_type_t::ELF_TYPE_JSON) {
11✔
5135
        bool found = false;
2✔
5136

5137
        if (!field_name.to_string_fragment().find('#')) {
2✔
5138
            for (const auto& jfe : this->jlf_line_format) {
18✔
5139
                if (jfe.jfe_value.pp_value == field_name) {
17✔
UNCOV
5140
                    log_debug(
×
5141
                        "hide-field not triggering rebuild since it is in "
5142
                        "line-format: %s.%s",
5143
                        this->elf_name.c_str(),
5144
                        field_name.c_str());
5145
                    found = true;
×
UNCOV
5146
                    break;
×
5147
                }
5148
            }
5149
        }
5150
        if (!found) {
2✔
5151
            log_info("format field %s.%s changed, rebuilding",
2✔
5152
                     this->elf_name.get(),
5153
                     field_name.get());
5154
            this->elf_value_defs_state->vds_generation += 1;
2✔
5155
        }
5156
    }
5157
    return true;
11✔
5158
}
5159

5160
bool
5161
external_log_format::format_changed()
2,570✔
5162
{
5163
    if (this->elf_specialized_value_defs_state.vds_generation
5,140✔
5164
        != this->elf_value_defs_state->vds_generation)
2,570✔
5165
    {
5166
        this->elf_specialized_value_defs_state = *this->elf_value_defs_state;
2✔
5167
        this->jlf_cached_offset = -1;
2✔
5168
        return true;
2✔
5169
    }
5170

5171
    return false;
2,568✔
5172
}
5173

5174
bool
5175
format_tag_def::path_restriction::matches(const char* fn) const
5✔
5176
{
5177
    return fnmatch(this->p_glob.c_str(), fn, 0) == 0;
5✔
5178
}
5179

5180
bool
5181
format_partition_def::path_restriction::matches(const char* fn) const
5✔
5182
{
5183
    return fnmatch(this->p_glob.c_str(), fn, 0) == 0;
5✔
5184
}
5185

5186
int
5187
pattern_locks::pattern_index_for_line(uint64_t line_number) const
6,773✔
5188
{
5189
    if (this->pl_lines.empty()) {
6,773✔
UNCOV
5190
        return -1;
×
5191
    }
5192

5193
    auto iter
5194
        = std::lower_bound(this->pl_lines.cbegin(),
6,773✔
5195
                           this->pl_lines.cend(),
5196
                           line_number,
5197
                           [](const pattern_for_lines& pfl, uint32_t line) {
6,832✔
5198
                               return pfl.pfl_line < line;
6,832✔
5199
                           });
5200

5201
    if (iter == this->pl_lines.end() || iter->pfl_line != line_number) {
6,773✔
5202
        --iter;
6,146✔
5203
    }
5204

5205
    return iter->pfl_pat_index;
6,773✔
5206
}
5207

5208
/* XXX */
5209
#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