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

tstack / lnav / 24005722728-2927

05 Apr 2026 04:31PM UTC coverage: 69.142% (+0.002%) from 69.14%
24005722728-2927

push

github

tstack
[docs] add some missing stuff

53377 of 77199 relevant lines covered (69.14%)

538923.9 hits per line

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

84.75
/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 "fast_float/single_include/fast_float/fast_float.h"
54
#include "fmt/format.h"
55
#include "lnav_util.hh"
56
#include "log_format_ext.hh"
57
#include "log_search_table.hh"
58
#include "log_vtab_impl.hh"
59
#include "logfile_sub_source.hh"
60
#include "ptimec.hh"
61
#include "readline_highlighters.hh"
62
#include "scn/scan.h"
63
#include "sql_util.hh"
64
#include "sqlite-extension-func.hh"
65
#include "sqlitepp.hh"
66
#include "yajlpp/yajlpp.hh"
67
#include "yajlpp/yajlpp_def.hh"
68

69
using namespace lnav::roles::literals;
70
using namespace std::chrono_literals;
71
using std::string_literals::operator""s;
72

73
static auto intern_lifetime = intern_string::get_table_lifetime();
74

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

83
std::vector<std::shared_ptr<external_log_format>>
84
    external_log_format::GRAPH_ORDERED_FORMATS;
85

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

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

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

105
    return *this;
922✔
106
}
107

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

118
    return *this;
383✔
119
}
120

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

129
opid_time_range&
130
opid_time_range::operator|=(const opid_time_range& rhs)
387✔
131
{
132
    if (rhs.otr_range.tr_begin < this->otr_range.tr_begin) {
387✔
133
        this->otr_description = rhs.otr_description;
4✔
134
    } else {
135
        this->otr_description |= rhs.otr_description;
383✔
136
    }
137
    this->otr_range |= rhs.otr_range;
387✔
138
    this->otr_level_stats |= rhs.otr_level_stats;
387✔
139
    for (const auto& rhs_sub : rhs.otr_sub_ops) {
396✔
140
        bool found = false;
9✔
141

142
        for (auto& sub : this->otr_sub_ops) {
18✔
143
            if (sub.ostr_subid == rhs_sub.ostr_subid) {
9✔
144
                sub.ostr_range |= rhs_sub.ostr_range;
9✔
145
                found = true;
9✔
146
            }
147
        }
148
        if (!found) {
9✔
149
            this->otr_sub_ops.emplace_back(rhs_sub);
×
150
        }
151
    }
152
    std::stable_sort(this->otr_sub_ops.begin(), this->otr_sub_ops.end());
387✔
153

154
    return *this;
387✔
155
}
156

157
void
158
thread_id_time_range::clear()
×
159
{
160
    this->titr_range.invalidate();
×
161
    this->titr_level_stats = {};
×
162
}
163

164
thread_id_time_range&
165
thread_id_time_range::operator|=(const thread_id_time_range& rhs)
535✔
166
{
167
    this->titr_range |= rhs.titr_range;
535✔
168
    this->titr_level_stats |= rhs.titr_level_stats;
535✔
169

170
    return *this;
535✔
171
}
172

173
void
174
log_level_stats::update_msg_count(log_level_t lvl, int32_t amount)
41,090✔
175
{
176
    switch (lvl) {
41,090✔
177
        case LEVEL_FATAL:
3,442✔
178
        case LEVEL_CRITICAL:
179
        case LEVEL_ERROR:
180
            this->lls_error_count += amount;
3,442✔
181
            break;
3,442✔
182
        case LEVEL_WARNING:
285✔
183
            this->lls_warning_count += amount;
285✔
184
            break;
285✔
185
        default:
37,363✔
186
            break;
37,363✔
187
    }
188
    this->lls_total_count += amount;
41,090✔
189
}
41,090✔
190

191
void
192
opid_time_range::close_sub_ops(const string_fragment& subid)
×
193
{
194
    for (auto& other_sub : this->otr_sub_ops) {
×
195
        if (other_sub.ostr_subid == subid) {
×
196
            other_sub.ostr_open = false;
×
197
        }
198
    }
199
}
200

201
log_thread_id_map::iterator
202
log_thread_id_state::insert_tid(ArenaAlloc::Alloc<char>& alloc,
13,367✔
203
                                const string_fragment& tid,
204
                                const std::chrono::microseconds& us)
205
{
206
    auto retval = this->ltis_tid_ranges.find(tid);
13,367✔
207
    if (retval == this->ltis_tid_ranges.end()) {
13,367✔
208
        auto tid_copy = tid.to_owned(alloc);
7,917✔
209
        auto titr = thread_id_time_range{time_range{us, us}};
7,917✔
210
        auto emplace_res = this->ltis_tid_ranges.emplace(tid_copy, titr);
7,917✔
211
        retval = emplace_res.first;
7,917✔
212
    } else {
213
        retval->second.titr_range.extend_to(us);
5,450✔
214
    }
215

216
    return retval;
13,367✔
217
}
218

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

248
    return retval;
13,559✔
249
}
250

251
opid_sub_time_range*
252
log_opid_state::sub_op_in_use(ArenaAlloc::Alloc<char>& alloc,
78✔
253
                              log_opid_map::iterator& op_iter,
254
                              const string_fragment& subid,
255
                              const std::chrono::microseconds& us,
256
                              log_level_t level)
257
{
258
    const auto& opid = op_iter->first;
78✔
259
    auto sub_iter = this->los_sub_in_use.find(subid);
78✔
260
    if (sub_iter == this->los_sub_in_use.end()) {
78✔
261
        auto emp_res
262
            = this->los_sub_in_use.emplace(subid.to_owned(alloc), opid);
53✔
263

264
        sub_iter = emp_res.first;
53✔
265
    }
266

267
    auto retval = sub_iter->first;
78✔
268
    if (sub_iter->second != opid) {
78✔
269
        auto other_otr
270
            = lnav::map::find(this->los_opid_ranges, sub_iter->second);
×
271
        if (other_otr) {
×
272
            other_otr->get().close_sub_ops(retval);
×
273
        }
274
    }
275
    sub_iter->second = opid;
78✔
276

277
    auto& otr = op_iter->second;
78✔
278
    auto sub_op_iter = otr.otr_sub_ops.rbegin();
78✔
279
    for (; sub_op_iter != otr.otr_sub_ops.rend(); ++sub_op_iter) {
78✔
280
        if (sub_op_iter->ostr_open && sub_op_iter->ostr_subid == retval) {
25✔
281
            break;
25✔
282
        }
283
    }
284
    if (sub_op_iter == otr.otr_sub_ops.rend()) {
78✔
285
        otr.otr_sub_ops.emplace_back(opid_sub_time_range{
53✔
286
            retval,
287
            time_range{us, us},
288
        });
289
        otr.otr_sub_ops.back().ostr_level_stats.update_msg_count(level);
53✔
290

291
        return &otr.otr_sub_ops.back();
53✔
292
    } else {
293
        sub_op_iter->ostr_range.extend_to(us);
25✔
294
        sub_op_iter->ostr_level_stats.update_msg_count(level);
25✔
295
        return &(*sub_op_iter);
25✔
296
    }
297
}
298

299
std::optional<std::string>
300
log_format::opid_descriptor::matches(const string_fragment& sf) const
2,272✔
301
{
302
    if (this->od_extractor.pp_value) {
2,272✔
303
        thread_local auto desc_md = lnav::pcre2pp::match_data::unitialized();
430✔
304

305
        auto desc_match_res = this->od_extractor.pp_value->capture_from(sf)
430✔
306
                                  .into(desc_md)
430✔
307
                                  .matches(PCRE2_NO_UTF_CHECK | PCRE2_ANCHORED)
860✔
308
                                  .ignore_error();
430✔
309
        if (desc_match_res) {
430✔
310
            return desc_md.to_string();
86✔
311
        }
312

313
        return std::nullopt;
344✔
314
    }
315
    return sf.to_string();
1,842✔
316
}
317

318
std::string
319
log_format::opid_descriptors::to_string(
1,041✔
320
    const lnav::map::small<size_t, std::string>& lod) const
321
{
322
    std::string retval;
1,041✔
323

324
    for (size_t lpc = 0; lpc < this->od_descriptors->size(); lpc++) {
2,095✔
325
        if (this->od_descriptors->at(lpc).od_prefix) {
1,054✔
326
            retval.append(this->od_descriptors->at(lpc).od_prefix.value());
×
327
        } else if (lpc > 0) {
1,054✔
328
            retval.append(" ");
13✔
329
        }
330
        auto val = lod.value_for(lpc);
1,054✔
331
        if (val) {
1,054✔
332
            retval.append(*val.value());
1,052✔
333
        }
334
        retval.append(this->od_descriptors->at(lpc).od_suffix);
1,054✔
335
    }
336

337
    return retval;
1,041✔
338
}
×
339

340
bool
341
logline_value_meta::is_numeric() const
×
342
{
343
    if (this->lvm_identifier || this->lvm_foreign_key) {
×
344
        return false;
×
345
    }
346
    switch (this->lvm_kind) {
×
347
        case value_kind_t::VALUE_FLOAT:
×
348
        case value_kind_t::VALUE_INTEGER:
349
            return true;
×
350
        default:
×
351
            return false;
×
352
    }
353
}
354

355
chart_type_t
356
logline_value_meta::to_chart_type() const
54✔
357
{
358
    auto retval = chart_type_t::hist;
54✔
359
    switch (this->lvm_kind) {
54✔
360
        case value_kind_t::VALUE_NULL:
9✔
361
            retval = chart_type_t::none;
9✔
362
            break;
9✔
363
        case value_kind_t::VALUE_INTEGER:
10✔
364
            if (!this->lvm_identifier && !this->lvm_foreign_key) {
10✔
365
                retval = chart_type_t::spectro;
5✔
366
            }
367
            break;
10✔
368
        case value_kind_t::VALUE_FLOAT:
×
369
            retval = chart_type_t::spectro;
×
370
            break;
×
371
        case value_kind_t::VALUE_XML:
1✔
372
        case value_kind_t::VALUE_JSON:
373
        case value_kind_t::VALUE_BOOLEAN:
374
        case value_kind_t::VALUE_TIMESTAMP:
375
            retval = chart_type_t::none;
1✔
376
            break;
1✔
377
        default:
34✔
378
            break;
34✔
379
    }
380

381
    return retval;
54✔
382
}
383

384
struct line_range
385
logline_value::origin_in_full_msg(const char* msg, ssize_t len) const
×
386
{
387
    if (this->lv_sub_offset == 0) {
×
388
        return this->lv_origin;
×
389
    }
390

391
    if (len == -1) {
×
392
        len = strlen(msg);
×
393
    }
394

395
    struct line_range retval = this->lv_origin;
×
396
    const char *last = msg, *msg_end = msg + len;
×
397

398
    for (int lpc = 0; lpc < this->lv_sub_offset; lpc++) {
×
399
        const auto* next = (const char*) memchr(last, '\n', msg_end - last);
×
400
        require(next != nullptr);
×
401

402
        next += 1;
×
403
        int amount = (next - last);
×
404

405
        retval.lr_start += amount;
×
406
        if (retval.lr_end != -1) {
×
407
            retval.lr_end += amount;
×
408
        }
409

410
        last = next + 1;
×
411
    }
412

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

416
        if (eol == nullptr) {
×
417
            retval.lr_end = len;
×
418
        } else {
419
            retval.lr_end = eol - msg;
×
420
        }
421
    }
422

423
    return retval;
×
424
}
425

426
logline_value::logline_value(logline_value_meta lvm,
642,670✔
427
                             shared_buffer_ref& sbr,
428
                             struct line_range origin)
642,670✔
429
    : lv_meta(std::move(lvm)), lv_origin(origin)
642,670✔
430
{
431
    if (sbr.get_data() == nullptr) {
642,670✔
432
        this->lv_meta.lvm_kind = value_kind_t::VALUE_NULL;
×
433
    }
434

435
    switch (this->lv_meta.lvm_kind) {
642,670✔
436
        case value_kind_t::VALUE_JSON:
328,633✔
437
        case value_kind_t::VALUE_XML:
438
        case value_kind_t::VALUE_STRUCT:
439
        case value_kind_t::VALUE_TEXT:
440
        case value_kind_t::VALUE_QUOTED:
441
        case value_kind_t::VALUE_W3C_QUOTED:
442
        case value_kind_t::VALUE_TIMESTAMP:
443
            require(origin.lr_end != -1);
328,633✔
444
            this->lv_frag = string_fragment::from_byte_range(
328,633✔
445
                sbr.get_data(), origin.lr_start, origin.lr_end);
328,633✔
446
            break;
328,633✔
447

448
        case value_kind_t::VALUE_NULL:
×
449
            break;
×
450

451
        case value_kind_t::VALUE_INTEGER: {
283,740✔
452
            auto scan_res
453
                = scn::scan_value<int64_t>(sbr.to_string_view(origin));
283,740✔
454
            if (scan_res) {
283,740✔
455
                this->lv_value.i = scan_res->value();
283,738✔
456
            } else {
457
                this->lv_value.i = 0;
2✔
458
            }
459
            break;
283,740✔
460
        }
461

462
        case value_kind_t::VALUE_FLOAT: {
30,297✔
463
            auto scan_res = scn::scan_value<double>(sbr.to_string_view(origin));
30,297✔
464
            if (scan_res) {
30,297✔
465
                this->lv_value.d = scan_res->value();
30,297✔
466
            } else {
467
                this->lv_value.d = 0;
×
468
            }
469
            break;
30,297✔
470
        }
471

472
        case value_kind_t::VALUE_BOOLEAN:
×
473
            if (strncmp(
×
474
                    sbr.get_data_at(origin.lr_start), "true", origin.length())
×
475
                    == 0
476
                || strncmp(
×
477
                       sbr.get_data_at(origin.lr_start), "yes", origin.length())
×
478
                    == 0)
479
            {
480
                this->lv_value.i = 1;
×
481
            } else {
482
                this->lv_value.i = 0;
×
483
            }
484
            break;
×
485

486
        case value_kind_t::VALUE_UNKNOWN:
×
487
        case value_kind_t::VALUE__MAX:
488
            ensure(0);
×
489
            break;
490
    }
491
}
642,670✔
492

493
void
494
logline_value::apply_scaling(const scaling_factor* sf)
44,532✔
495
{
496
    if (sf != nullptr) {
44,532✔
497
        switch (this->lv_meta.lvm_kind) {
10✔
498
            case value_kind_t::VALUE_INTEGER:
×
499
                sf->scale(this->lv_value.i);
×
500
                break;
×
501
            case value_kind_t::VALUE_FLOAT:
10✔
502
                sf->scale(this->lv_value.d);
10✔
503
                break;
10✔
504
            default:
×
505
                break;
×
506
        }
507
    }
508
}
44,532✔
509

510
std::string
511
logline_value::to_string() const
9,796✔
512
{
513
    char buffer[128];
514

515
    switch (this->lv_meta.lvm_kind) {
9,796✔
516
        case value_kind_t::VALUE_NULL:
77✔
517
            return "null";
154✔
518

519
        case value_kind_t::VALUE_JSON:
9,197✔
520
        case value_kind_t::VALUE_XML:
521
        case value_kind_t::VALUE_STRUCT:
522
        case value_kind_t::VALUE_TEXT:
523
        case value_kind_t::VALUE_TIMESTAMP:
524
            if (this->lv_str) {
9,197✔
525
                return this->lv_str.value();
1,737✔
526
            }
527
            if (this->lv_frag.empty()) {
7,460✔
528
                return this->lv_intern_string.to_string();
51✔
529
            }
530
            return this->lv_frag.to_string();
7,409✔
531

532
        case value_kind_t::VALUE_QUOTED:
7✔
533
        case value_kind_t::VALUE_W3C_QUOTED:
534
            if (this->lv_frag.empty()) {
7✔
535
                return "";
×
536
            } else {
537
                switch (this->lv_frag.data()[0]) {
7✔
538
                    case '\'':
7✔
539
                    case '"': {
540
                        auto unquote_func = this->lv_meta.lvm_kind
14✔
541
                                == value_kind_t::VALUE_W3C_QUOTED
542
                            ? unquote_w3c
7✔
543
                            : unquote;
544
                        stack_buf allocator;
7✔
545
                        auto* unquoted_str
546
                            = allocator.allocate(this->lv_frag.length());
7✔
547
                        size_t unquoted_len;
548

549
                        unquoted_len = unquote_func(unquoted_str,
7✔
550
                                                    this->lv_frag.data(),
551
                                                    this->lv_frag.length());
7✔
552
                        return {unquoted_str, unquoted_len};
14✔
553
                    }
7✔
554
                    default:
×
555
                        return this->lv_frag.to_string();
×
556
                }
557
            }
558
            break;
559

560
        case value_kind_t::VALUE_INTEGER:
475✔
561
            snprintf(buffer, sizeof(buffer), "%" PRId64, this->lv_value.i);
475✔
562
            break;
475✔
563

564
        case value_kind_t::VALUE_FLOAT:
32✔
565
            snprintf(buffer, sizeof(buffer), "%lf", this->lv_value.d);
32✔
566
            break;
32✔
567

568
        case value_kind_t::VALUE_BOOLEAN:
8✔
569
            if (this->lv_value.i) {
8✔
570
                return "true";
×
571
            } else {
572
                return "false";
16✔
573
            }
574
            break;
575
        case value_kind_t::VALUE_UNKNOWN:
×
576
        case value_kind_t::VALUE__MAX:
577
            ensure(0);
×
578
            break;
579
    }
580

581
    return {buffer};
1,014✔
582
}
583

584
string_fragment
585
logline_value::to_string_fragment(ArenaAlloc::Alloc<char>& alloc) const
1,093✔
586
{
587
    char buffer[128];
588

589
    switch (this->lv_meta.lvm_kind) {
1,093✔
590
        case value_kind_t::VALUE_NULL:
×
591
            return "null"_frag;
×
592

593
        case value_kind_t::VALUE_JSON:
1,093✔
594
        case value_kind_t::VALUE_XML:
595
        case value_kind_t::VALUE_STRUCT:
596
        case value_kind_t::VALUE_TEXT:
597
        case value_kind_t::VALUE_TIMESTAMP:
598
            if (this->lv_str) {
1,093✔
599
                return string_fragment::from_str(this->lv_str.value())
×
600
                    .to_owned(alloc);
×
601
            }
602
            if (this->lv_frag.empty()) {
1,093✔
603
                return this->lv_intern_string.to_string_fragment().to_owned(
×
604
                    alloc);
×
605
            }
606
            return this->lv_frag.to_owned(alloc);
1,093✔
607

608
        case value_kind_t::VALUE_QUOTED:
×
609
        case value_kind_t::VALUE_W3C_QUOTED:
610
            if (this->lv_frag.empty()) {
×
611
                return string_fragment{};
×
612
            } else {
613
                switch (this->lv_frag.data()[0]) {
×
614
                    case '\'':
×
615
                    case '"': {
616
                        auto unquote_func = this->lv_meta.lvm_kind
×
617
                                == value_kind_t::VALUE_W3C_QUOTED
618
                            ? unquote_w3c
×
619
                            : unquote;
620
                        stack_buf allocator;
×
621
                        auto* unquoted_str
622
                            = allocator.allocate(this->lv_frag.length());
×
623
                        size_t unquoted_len;
624

625
                        unquoted_len = unquote_func(unquoted_str,
×
626
                                                    this->lv_frag.data(),
627
                                                    this->lv_frag.length());
×
628
                        return string_fragment::from_bytes(unquoted_str,
×
629
                                                           unquoted_len)
630
                            .to_owned(alloc);
×
631
                    }
632
                    default:
×
633
                        return this->lv_frag.to_owned(alloc);
×
634
                }
635
            }
636
            break;
637

638
        case value_kind_t::VALUE_INTEGER:
×
639
            snprintf(buffer, sizeof(buffer), "%" PRId64, this->lv_value.i);
×
640
            break;
×
641

642
        case value_kind_t::VALUE_FLOAT:
×
643
            snprintf(buffer, sizeof(buffer), "%lf", this->lv_value.d);
×
644
            break;
×
645

646
        case value_kind_t::VALUE_BOOLEAN:
×
647
            if (this->lv_value.i) {
×
648
                return "true"_frag;
×
649
            }
650
            return "false"_frag;
×
651
            break;
652
        case value_kind_t::VALUE_UNKNOWN:
×
653
        case value_kind_t::VALUE__MAX:
654
            ensure(0);
×
655
            break;
656
    }
657

658
    return string_fragment::from_c_str(buffer).to_owned(alloc);
×
659
}
660

661
const char*
662
logline_value::text_value() const
68,691✔
663
{
664
    if (this->lv_str) {
68,691✔
665
        return this->lv_str->c_str();
242✔
666
    }
667
    if (this->lv_frag.empty()) {
68,449✔
668
        if (this->lv_intern_string.empty()) {
21✔
669
            return "";
21✔
670
        }
671
        return this->lv_intern_string.get();
×
672
    }
673
    return this->lv_frag.data();
68,428✔
674
}
675

676
size_t
677
logline_value::text_length() const
68,724✔
678
{
679
    if (this->lv_str) {
68,724✔
680
        return this->lv_str->size();
242✔
681
    }
682
    if (this->lv_frag.empty()) {
68,482✔
683
        return this->lv_intern_string.size();
21✔
684
    }
685
    return this->lv_frag.length();
68,461✔
686
}
687

688
string_fragment
689
logline_value::text_value_fragment() const
×
690
{
691
    return string_fragment::from_bytes(this->text_value(), this->text_length());
×
692
}
693

694
void
695
logline_value_vector::shift_origins_by(const line_range& cover, int32_t amount)
2✔
696
{
697
    for (auto& lv : this->lvv_values) {
14✔
698
        if (!lv.lv_origin.is_valid()) {
12✔
699
            continue;
×
700
        }
701
        lv.lv_origin.shift_range(cover, amount);
12✔
702
    }
703
}
2✔
704

705
void
706
logline_value_vector::clear()
42,526✔
707
{
708
    this->lvv_values.clear();
42,526✔
709
    this->lvv_sbr.disown();
42,526✔
710
    this->lvv_opid_value = std::nullopt;
42,526✔
711
    this->lvv_opid_provenance = opid_provenance::none;
42,526✔
712
    this->lvv_thread_id_value = std::nullopt;
42,526✔
713
    this->lvv_src_file_value = std::nullopt;
42,526✔
714
    this->lvv_src_line_value = std::nullopt;
42,526✔
715
}
42,526✔
716

717
logline_value_vector::logline_value_vector(const logline_value_vector& other)
1,018✔
718
    : lvv_sbr(other.lvv_sbr.clone()), lvv_values(other.lvv_values),
1,018✔
719
      lvv_opid_value(other.lvv_opid_value),
1,018✔
720
      lvv_opid_provenance(other.lvv_opid_provenance),
1,018✔
721
      lvv_thread_id_value(
722
          to_owned(other.lvv_thread_id_value, this->lvv_allocator)),
1,018✔
723
      lvv_src_file_value(
724
          to_owned(other.lvv_src_file_value, this->lvv_allocator)),
1,018✔
725
      lvv_src_line_value(
726
          to_owned(other.lvv_src_line_value, this->lvv_allocator))
2,036✔
727
{
728
}
1,018✔
729

730
logline_value_vector&
731
logline_value_vector::operator=(const logline_value_vector& other)
464✔
732
{
733
    this->lvv_sbr = other.lvv_sbr.clone();
464✔
734
    this->lvv_values = other.lvv_values;
464✔
735
    this->lvv_opid_value = other.lvv_opid_value;
464✔
736
    this->lvv_opid_provenance = other.lvv_opid_provenance;
464✔
737
    this->lvv_thread_id_value
738
        = to_owned(other.lvv_thread_id_value, this->lvv_allocator);
464✔
739
    this->lvv_src_file_value
740
        = to_owned(other.lvv_src_file_value, this->lvv_allocator);
464✔
741
    this->lvv_src_line_value
742
        = to_owned(other.lvv_src_line_value, this->lvv_allocator);
464✔
743

744
    return *this;
464✔
745
}
746

747
std::vector<std::shared_ptr<log_format>> log_format::lf_root_formats;
748

749
date_time_scanner
750
log_format::build_time_scanner() const
241✔
751
{
752
    date_time_scanner retval;
241✔
753

754
    retval.set_base_time(this->lf_date_time.dts_base_time,
241✔
755
                         this->lf_date_time.dts_base_tm.et_tm);
241✔
756
    if (this->lf_date_time.dts_default_zone != nullptr) {
241✔
757
        retval.dts_default_zone = this->lf_date_time.dts_default_zone;
×
758
    }
759
    retval.dts_zoned_to_local = this->lf_date_time.dts_zoned_to_local;
241✔
760

761
    return retval;
241✔
762
}
763

764
std::vector<std::shared_ptr<log_format>>&
765
log_format::get_root_formats()
18,382✔
766
{
767
    return lf_root_formats;
18,382✔
768
}
769

770
void
771
external_log_format::update_op_description(
5,231✔
772
    const std::vector<opid_descriptors*>& desc_defs_vec,
773
    log_op_description& lod,
774
    const pattern* fpat,
775
    const lnav::pcre2pp::match_data& md)
776
{
777
    std::optional<std::string> desc_elem_str;
5,231✔
778
    if (!lod.lod_index) {
5,231✔
779
        for (const auto& desc_defs : desc_defs_vec) {
3,348✔
780
            if (lod.lod_index) {
1,357✔
781
                break;
34✔
782
            }
783
            for (const auto& desc_def : *desc_defs->od_descriptors) {
1,719✔
784
                auto desc_field_index_iter = fpat->p_value_name_to_index.find(
1,449✔
785
                    desc_def.od_field.pp_value);
1,449✔
786

787
                if (desc_field_index_iter == fpat->p_value_name_to_index.end())
1,449✔
788
                {
789
                    continue;
55✔
790
                }
791

792
                auto desc_cap_opt = md[desc_field_index_iter->second];
1,445✔
793
                if (!desc_cap_opt) {
1,445✔
794
                    continue;
51✔
795
                }
796

797
                desc_elem_str = desc_def.matches(desc_cap_opt.value());
1,394✔
798
                if (desc_elem_str) {
1,394✔
799
                    lod.lod_index = desc_defs->od_index;
1,053✔
800
                    break;
1,053✔
801
                }
802
            }
803
        }
804
    }
805
    if (lod.lod_index) {
5,231✔
806
        const auto& desc_def_v
807
            = *desc_defs_vec[lod.lod_index.value()]->od_descriptors;
4,259✔
808
        auto& desc_v = lod.lod_elements;
4,259✔
809

810
        if (desc_def_v.size() == desc_v.size()
4,259✔
811
            || (this->elf_opid_field.empty() && !desc_v.empty()))
4,259✔
812
        {
813
            return;
3,206✔
814
        }
815
        for (size_t desc_def_index = 0; desc_def_index < desc_def_v.size();
2,990✔
816
             desc_def_index++)
817
        {
818
            const auto& desc_def = desc_def_v[desc_def_index];
1,937✔
819
            auto found_desc = desc_v.value_for(desc_def_index);
1,937✔
820
            auto desc_field_index_iter
821
                = fpat->p_value_name_to_index.find(desc_def.od_field.pp_value);
1,937✔
822

823
            if (desc_field_index_iter == fpat->p_value_name_to_index.end()) {
1,937✔
824
                continue;
36✔
825
            }
826
            auto desc_cap_opt = md[desc_field_index_iter->second];
1,937✔
827
            if (!desc_cap_opt) {
1,937✔
828
                continue;
36✔
829
            }
830

831
            if (!desc_elem_str) {
1,901✔
832
                desc_elem_str = desc_def.matches(desc_cap_opt.value());
848✔
833
            }
834
            if (desc_elem_str) {
1,901✔
835
                if (!found_desc) {
1,898✔
836
                    desc_v.insert(desc_def_index, desc_elem_str.value());
1,898✔
837
                } else if (!desc_elem_str->empty()) {
×
838
                    found_desc.value()->append(desc_def.od_joiner);
×
839
                    found_desc.value()->append(desc_elem_str.value());
×
840
                }
841
            }
842
            desc_elem_str = std::nullopt;
1,901✔
843
        }
844
    }
845
}
5,231✔
846

847
void
848
external_log_format::update_op_description(
3,516✔
849
    const std::vector<opid_descriptors*>& desc_defs_vec,
850
    log_op_description& lod)
851
{
852
    std::optional<std::string> desc_elem_str;
3,516✔
853
    if (!lod.lod_index) {
3,516✔
854
        for (const auto& desc_defs : desc_defs_vec) {
3,525✔
855
            if (lod.lod_index) {
15✔
856
                break;
×
857
            }
858
            for (const auto& desc_def : *desc_defs->od_descriptors) {
15✔
859
                auto desc_cap_iter
860
                    = this->lf_desc_captures.find(desc_def.od_field.pp_value);
15✔
861

862
                if (desc_cap_iter == this->lf_desc_captures.end()) {
15✔
863
                    continue;
×
864
                }
865
                desc_elem_str = desc_def.matches(desc_cap_iter->second);
15✔
866
                if (desc_elem_str) {
15✔
867
                    lod.lod_index = desc_defs->od_index;
15✔
868
                    break;
15✔
869
                }
870
            }
871
        }
872
    }
873
    if (lod.lod_index) {
3,516✔
874
        const auto& desc_def_v
875
            = *desc_defs_vec[lod.lod_index.value()]->od_descriptors;
21✔
876
        auto& desc_v = lod.lod_elements;
21✔
877

878
        if (desc_def_v.size() == desc_v.size()
21✔
879
            || (this->elf_opid_field.empty() && !desc_v.empty()))
21✔
880
        {
881
            return;
6✔
882
        }
883
        for (size_t desc_def_index = 0; desc_def_index < desc_def_v.size();
45✔
884
             desc_def_index++)
885
        {
886
            const auto& desc_def = desc_def_v[desc_def_index];
30✔
887
            auto found_desc = desc_v.value_for(desc_def_index);
30✔
888
            auto desc_cap_iter
889
                = this->lf_desc_captures.find(desc_def.od_field.pp_value);
30✔
890
            if (desc_cap_iter == this->lf_desc_captures.end()) {
30✔
891
                continue;
×
892
            }
893

894
            if (!desc_elem_str) {
30✔
895
                desc_elem_str = desc_def.matches(desc_cap_iter->second);
15✔
896
            }
897
            if (desc_elem_str) {
30✔
898
                if (!found_desc) {
30✔
899
                    desc_v.insert(desc_def_index, desc_elem_str.value());
30✔
900
                } else if (!desc_elem_str->empty()) {
×
901
                    found_desc.value()->append(desc_def.od_joiner);
×
902
                    found_desc.value()->append(desc_elem_str.value());
×
903
                }
904
            }
905
            desc_elem_str = std::nullopt;
30✔
906
        }
907
    }
908
}
3,516✔
909

910
static bool
911
next_format(
2,466,426✔
912
    const std::vector<std::shared_ptr<external_log_format::pattern>>& patterns,
913
    int& index,
914
    int& locked_index)
915
{
916
    bool retval = true;
2,466,426✔
917

918
    if (locked_index == -1) {
2,466,426✔
919
        index += 1;
2,458,701✔
920
        if (index >= (int) patterns.size()) {
2,458,701✔
921
            retval = false;
747,872✔
922
        }
923
    } else if (index == locked_index) {
7,725✔
924
        retval = false;
1✔
925
    } else {
926
        index = locked_index;
7,724✔
927
    }
928

929
    return retval;
2,466,426✔
930
}
931

932
bool
933
log_format::next_format(const pcre_format* fmt, int& index, int& locked_index)
185,427✔
934
{
935
    bool retval = true;
185,427✔
936

937
    if (locked_index == -1) {
185,427✔
938
        index += 1;
185,243✔
939
        if (fmt[index].name == nullptr) {
185,243✔
940
            retval = false;
10,321✔
941
        }
942
    } else if (index == locked_index) {
184✔
943
        retval = false;
36✔
944
    } else {
945
        index = locked_index;
148✔
946
    }
947

948
    return retval;
185,427✔
949
}
950

951
const char*
952
log_format::log_scanf(scan_batch_context& sbc,
12,986✔
953
                      uint32_t line_number,
954
                      string_fragment line,
955
                      const pcre_format* fmt,
956
                      const char* time_fmt[],
957
                      exttm* tm_out,
958
                      timeval* tv_out,
959

960
                      string_fragment* ts_out,
961
                      std::optional<string_fragment>* level_out)
962
{
963
    int curr_fmt = -1;
12,986✔
964
    const char* retval = nullptr;
12,986✔
965
    bool done = false;
12,986✔
966
    int pat_index = sbc.sbc_pattern_locks.last_pattern_index();
12,986✔
967

968
    while (!done && next_format(fmt, curr_fmt, pat_index)) {
188,056✔
969
        thread_local auto md = lnav::pcre2pp::match_data::unitialized();
175,070✔
970

971
        auto match_res = fmt[curr_fmt]
175,070✔
972
                             .pcre->capture_from(line)
175,070✔
973
                             .into(md)
175,070✔
974
                             .matches(PCRE2_NO_UTF_CHECK)
350,140✔
975
                             .ignore_error();
175,070✔
976
        if (!match_res) {
175,070✔
977
            retval = nullptr;
150,423✔
978
        } else {
979
            auto ts = md[fmt[curr_fmt].pf_timestamp_index];
24,647✔
980

981
            retval = this->lf_date_time.scan(
24,647✔
982
                ts->data(), ts->length(), nullptr, tm_out, *tv_out);
24,647✔
983

984
            if (retval == nullptr) {
24,647✔
985
                auto ls = this->lf_date_time.unlock();
22,018✔
986
                retval = this->lf_date_time.scan(
22,018✔
987
                    ts->data(), ts->length(), nullptr, tm_out, *tv_out);
22,018✔
988
                if (retval != nullptr) {
22,018✔
989
                    auto old_flags
×
990
                        = this->lf_timestamp_flags & DATE_TIME_SET_FLAGS;
×
991
                    auto new_flags = tm_out->et_flags & DATE_TIME_SET_FLAGS;
×
992

993
                    // It is unlikely a valid timestamp would lose much
994
                    // precision.
995
                    if (new_flags != old_flags) {
×
996
                        retval = nullptr;
×
997
                    }
998
                }
999
                if (retval == nullptr) {
22,018✔
1000
                    this->lf_date_time.relock(ls);
22,018✔
1001
                } else {
1002
                    log_debug(
×
1003
                        "%d: changed time format to '%s' due to %.*s",
1004
                        line_number,
1005
                        PTIMEC_FORMAT_STR[this->lf_date_time.dts_fmt_lock],
1006
                        ts->length(),
1007
                        ts->data());
1008
                }
1009
            }
1010

1011
            if (retval) {
24,647✔
1012
                *ts_out = ts.value();
2,629✔
1013
                if (md[2]) {
2,629✔
1014
                    *level_out = md[2];
276✔
1015
                } else {
1016
                    *level_out = line.substr(md[0]->sf_end);
2,353✔
1017
                }
1018
                if (curr_fmt != pat_index) {
2,629✔
1019
                    uint32_t lock_line;
1020

1021
                    if (sbc.sbc_pattern_locks.empty()) {
2,517✔
1022
                        lock_line = 0;
2,517✔
1023
                    } else {
1024
                        lock_line = line_number;
×
1025
                    }
1026

1027
                    sbc.sbc_pattern_locks.pl_lines.emplace_back(lock_line,
2,517✔
1028
                                                                curr_fmt);
1029
                }
1030
                this->lf_timestamp_flags = tm_out->et_flags;
2,629✔
1031
                done = true;
2,629✔
1032
            }
1033
        }
1034
    }
1035

1036
    return retval;
12,986✔
1037
}
1038

1039
void
1040
log_format::annotate(logfile* lf,
37,865✔
1041
                     uint64_t line_number,
1042
                     string_attrs_t& sa,
1043
                     logline_value_vector& values) const
1044
{
1045
    if (lf != nullptr && !values.lvv_opid_value) {
37,865✔
1046
        const auto& bm = lf->get_bookmark_metadata();
3,099✔
1047
        auto bm_iter = bm.find(line_number);
3,099✔
1048
        if (bm_iter != bm.end() && !bm_iter->second.bm_opid.empty()) {
3,099✔
1049
            values.lvv_opid_value = bm_iter->second.bm_opid;
15✔
1050
            values.lvv_opid_provenance
1051
                = logline_value_vector::opid_provenance::user;
15✔
1052
        }
1053
    }
1054
}
37,865✔
1055

1056
void
1057
log_format::check_for_new_year(std::vector<logline>& dst,
2,110✔
1058
                               exttm etm,
1059
                               timeval log_tv) const
1060
{
1061
    if (dst.empty()) {
2,110✔
1062
        return;
277✔
1063
    }
1064

1065
    time_t diff
1066
        = dst.back().get_time<std::chrono::seconds>().count() - log_tv.tv_sec;
1,833✔
1067
    int off_year = 0, off_month = 0, off_day = 0, off_hour = 0;
1,833✔
1068
    bool do_change = true;
1,833✔
1069

1070
    if (diff <= 0) {
1,833✔
1071
        return;
1,773✔
1072
    }
1073
    if ((etm.et_flags & ETF_MONTH_SET) && diff >= (24 * 60 * 60)) {
60✔
1074
        off_year = 1;
11✔
1075
    } else if (diff >= (24 * 60 * 60)) {
49✔
1076
        off_month = 1;
2✔
1077
    } else if (!(etm.et_flags & ETF_DAY_SET) && (diff >= (60 * 60))) {
47✔
1078
        off_day = 1;
1✔
1079
    } else if (!(etm.et_flags & ETF_HOUR_SET) && (diff >= 60)) {
46✔
1080
        off_hour = 1;
4✔
1081
    } else {
1082
        do_change = false;
42✔
1083
    }
1084

1085
    if (!do_change) {
60✔
1086
        return;
42✔
1087
    }
1088
    log_debug("%zu:detected time rollover; offsets=%d %d %d %d",
18✔
1089
              dst.size(),
1090
              off_year,
1091
              off_month,
1092
              off_day,
1093
              off_hour);
1094
    for (auto& ll : dst) {
68✔
1095
        time_t ot = ll.get_time<std::chrono::seconds>().count();
50✔
1096
        struct tm otm;
1097

1098
        gmtime_r(&ot, &otm);
50✔
1099
        otm.tm_yday = -1;
50✔
1100
        if (otm.tm_year < off_year) {
50✔
1101
            otm.tm_year = 0;
×
1102
        } else {
1103
            otm.tm_year -= off_year;
50✔
1104
        }
1105
        otm.tm_mon -= off_month;
50✔
1106
        if (otm.tm_mon < 0) {
50✔
1107
            otm.tm_mon += 12;
2✔
1108
        }
1109
        auto new_time = tm2sec(&otm);
50✔
1110
        if (new_time == -1) {
50✔
1111
            continue;
×
1112
        }
1113
        new_time -= (off_day * 24 * 60 * 60) + (off_hour * 60 * 60);
50✔
1114
        auto old_sub = ll.get_subsecond_time<std::chrono::microseconds>();
50✔
1115
        ll.set_time(std::chrono::seconds{new_time});
50✔
1116
        ll.set_subsecond_time(old_sub);
50✔
1117
    }
1118
}
1119

1120
/*
1121
 * XXX This needs some cleanup.
1122
 */
1123
struct json_log_userdata {
1124
    json_log_userdata(shared_buffer_ref& sbr, scan_batch_context* sbc)
15,366✔
1125
        : jlu_shared_buffer(sbr), jlu_batch_context(sbc)
30,732✔
1126
    {
1127
    }
15,366✔
1128

1129
    const external_log_format::value_def* get_field_def(
427,905✔
1130
        yajlpp_parse_context* ypc)
1131
    {
1132
        const auto field_frag = ypc->get_path_as_string_fragment();
427,905✔
1133
        auto* format = this->jlu_format;
427,905✔
1134

1135
        if (this->jlu_read_order_index < format->elf_value_def_read_order.size()
427,905✔
1136
            && format->elf_value_def_read_order[this->jlu_read_order_index]
575,690✔
1137
                    .first
1138
                == field_frag)
147,785✔
1139
        {
1140
            auto retval
1141
                = format->elf_value_def_read_order[this->jlu_read_order_index]
141,998✔
1142
                      .second;
141,998✔
1143
            if (retval != nullptr) {
141,998✔
1144
                this->jlu_precision += 1;
20,128✔
1145
            }
1146
            this->jlu_read_order_index += 1;
141,998✔
1147
            return retval;
141,998✔
1148
        }
1149

1150
        format->elf_value_def_read_order.resize(this->jlu_read_order_index);
285,907✔
1151
        auto vd_iter = format->elf_value_def_frag_map.find(field_frag);
285,907✔
1152
        if (vd_iter != format->elf_value_def_frag_map.end()) {
285,907✔
1153
            format->elf_value_def_read_order.emplace_back(vd_iter->first,
241,203✔
1154
                                                          vd_iter->second);
241,203✔
1155
            this->jlu_read_order_index += 1;
241,203✔
1156
            if (vd_iter->second != nullptr) {
241,203✔
1157
                this->jlu_precision += 1;
15,988✔
1158
            }
1159
            return vd_iter->second;
241,203✔
1160
        }
1161

1162
        auto owned_frag = field_frag.to_owned(format->elf_allocator);
44,704✔
1163
        format->elf_value_def_frag_map[owned_frag] = nullptr;
44,704✔
1164
        format->elf_value_def_read_order.emplace_back(owned_frag, nullptr);
44,704✔
1165
        this->jlu_read_order_index += 1;
44,704✔
1166
        return nullptr;
44,704✔
1167
    }
1168

1169
    void add_sub_lines_for(const external_log_format::value_def* vd,
327,720✔
1170
                           bool top_level,
1171
                           std::optional<double> val,
1172
                           const unsigned char* str,
1173
                           ssize_t len,
1174
                           yajl_string_props_t* props)
1175
    {
1176
        auto res = this->jlu_format->value_line_count(
327,720✔
1177
            *this->jlu_batch_context, vd, top_level, val, str, len, props);
327,720✔
1178
        this->jlu_has_ansi |= res.vlcr_has_ansi;
327,720✔
1179
        if (!res.vlcr_valid_utf) {
327,720✔
1180
            this->jlu_valid_utf = false;
×
1181
        }
1182
        this->jlu_sub_line_count += res.vlcr_count;
327,720✔
1183
        this->jlu_quality += res.vlcr_line_format_count;
327,720✔
1184
        if (res.vlcr_line_format_index) {
327,720✔
1185
            this->jlu_format_hits[res.vlcr_line_format_index.value()] = true;
3,989✔
1186
        }
1187
    }
327,720✔
1188

1189
    external_log_format* jlu_format{nullptr};
1190
    const logline* jlu_line{nullptr};
1191
    logline* jlu_base_line{nullptr};
1192
    int jlu_sub_line_count{1};
1193
    bool jlu_has_ansi{false};
1194
    bool jlu_valid_utf{true};
1195
    yajl_handle jlu_handle{nullptr};
1196
    const char* jlu_line_value{nullptr};
1197
    size_t jlu_line_size{0};
1198
    std::stack<size_t> jlu_sub_start;
1199
    uint32_t jlu_quality{0};
1200
    uint32_t jlu_strikes{0};
1201
    uint32_t jlu_precision{0};
1202
    std::vector<bool> jlu_format_hits;
1203
    shared_buffer_ref& jlu_shared_buffer;
1204
    scan_batch_context* jlu_batch_context;
1205
    std::optional<string_fragment> jlu_opid_frag;
1206
    std::optional<string_fragment> jlu_opid_desc_frag;
1207
    std::optional<string_fragment> jlu_tid_frag;
1208
    std::optional<int64_t> jlu_tid_number;
1209
    std::optional<std::string> jlu_subid;
1210
    std::optional<log_format::scan_error> jlu_scan_error;
1211
    hasher jlu_opid_hasher;
1212
    std::chrono::microseconds jlu_duration{0};
15,366✔
1213
    exttm jlu_exttm;
1214
    size_t jlu_read_order_index{0};
1215
    subline_options jlu_subline_opts;
1216
};
1217

1218
static int read_json_field(yajlpp_parse_context* ypc,
1219
                           const unsigned char* str,
1220
                           size_t len,
1221
                           yajl_string_props_t*);
1222

1223
static int
1224
read_json_null(yajlpp_parse_context* ypc)
5,054✔
1225
{
1226
    json_log_userdata* jlu = (json_log_userdata*) ypc->ypc_userdata;
5,054✔
1227
    const auto* vd = jlu->get_field_def(ypc);
5,054✔
1228

1229
    jlu->add_sub_lines_for(
10,108✔
1230
        vd, ypc->is_level(1), std::nullopt, nullptr, -1, nullptr);
5,054✔
1231

1232
    return 1;
5,054✔
1233
}
1234

1235
static int
1236
read_json_bool(yajlpp_parse_context* ypc, int val)
24,006✔
1237
{
1238
    json_log_userdata* jlu = (json_log_userdata*) ypc->ypc_userdata;
24,006✔
1239
    const auto* vd = jlu->get_field_def(ypc);
24,006✔
1240

1241
    jlu->add_sub_lines_for(
48,012✔
1242
        vd, ypc->is_level(1), std::nullopt, nullptr, -1, nullptr);
24,006✔
1243

1244
    return 1;
24,006✔
1245
}
1246

1247
static int
1248
read_json_number(yajlpp_parse_context* ypc,
37,823✔
1249
                 const char* numberVal,
1250
                 size_t numberLen)
1251
{
1252
    auto* jlu = (json_log_userdata*) ypc->ypc_userdata;
37,823✔
1253
    auto number_frag = string_fragment::from_bytes(numberVal, numberLen);
37,823✔
1254
    std::optional<double> val;
37,823✔
1255

1256
    intern_string_t field_name;
37,823✔
1257
    const auto* vd = jlu->get_field_def(ypc);
37,823✔
1258
    if (vd != nullptr) {
37,823✔
1259
        field_name = vd->vd_meta.lvm_name;
1,831✔
1260
    }
1261

1262
    if (field_name.empty()) {
37,823✔
1263
    } else if (jlu->jlu_format->lf_timestamp_field == field_name) {
1,831✔
1264
        long long divisor = jlu->jlu_format->elf_timestamp_divisor;
113✔
1265
        auto scan_res = scn::scan_value<double>(number_frag.to_string_view());
113✔
1266
        if (!scan_res) {
113✔
1267
            log_error("invalid number %.*s", (int) numberLen, numberVal);
×
1268
            return 0;
×
1269
        }
1270
        auto ts_val = scan_res.value().value();
113✔
1271
        timeval tv;
1272
        tv.tv_sec = ts_val / divisor;
113✔
1273
        tv.tv_usec = fmod(ts_val, divisor) * (1000000.0 / divisor);
113✔
1274
        jlu->jlu_format->lf_date_time.to_localtime(tv.tv_sec, jlu->jlu_exttm);
113✔
1275
        tv.tv_sec = tm2sec(&jlu->jlu_exttm.et_tm);
113✔
1276
        jlu->jlu_exttm.et_gmtoff
1277
            = jlu->jlu_format->lf_date_time.dts_local_offset_cache;
113✔
1278
        jlu->jlu_exttm.et_flags
113✔
1279
            |= ETF_MACHINE_ORIENTED | ETF_SUB_NOT_IN_FORMAT | ETF_ZONE_SET;
113✔
1280
        if (divisor == 1000) {
113✔
1281
            jlu->jlu_exttm.et_flags |= ETF_MILLIS_SET;
17✔
1282
        } else {
1283
            jlu->jlu_exttm.et_flags |= ETF_MICROS_SET;
96✔
1284
        }
1285
        jlu->jlu_exttm.et_nsec = tv.tv_usec * 1000;
113✔
1286
        jlu->jlu_base_line->set_time(tv);
113✔
1287
    } else if (jlu->jlu_format->lf_subsecond_field == field_name) {
1,718✔
1288
        auto scan_res = scn::scan_value<double>(number_frag.to_string_view());
3✔
1289
        if (!scan_res) {
3✔
1290
            log_error("invalid number %.*s", (int) numberLen, numberVal);
×
1291
            return 0;
×
1292
        }
1293
        auto ts_val = scan_res.value().value();
3✔
1294

1295
        uint64_t millis = 0;
3✔
1296
        jlu->jlu_exttm.et_flags &= ~(ETF_MICROS_SET | ETF_MILLIS_SET);
3✔
1297
        switch (jlu->jlu_format->lf_subsecond_unit.value()) {
3✔
1298
            case log_format::subsecond_unit::milli:
×
1299
                millis = ts_val;
×
1300
                jlu->jlu_exttm.et_nsec = ts_val * 1000000;
×
1301
                jlu->jlu_exttm.et_flags |= ETF_MILLIS_SET;
×
1302
                break;
×
1303
            case log_format::subsecond_unit::micro:
×
1304
                millis = std::chrono::duration_cast<std::chrono::milliseconds>(
×
1305
                             std::chrono::microseconds((int64_t) ts_val))
×
1306
                             .count();
×
1307
                jlu->jlu_exttm.et_nsec = ts_val * 1000;
×
1308
                jlu->jlu_exttm.et_flags |= ETF_MICROS_SET;
×
1309
                break;
×
1310
            case log_format::subsecond_unit::nano:
3✔
1311
                millis = std::chrono::duration_cast<std::chrono::milliseconds>(
3✔
1312
                             std::chrono::nanoseconds((int64_t) ts_val))
3✔
1313
                             .count();
3✔
1314
                jlu->jlu_exttm.et_nsec = ts_val;
3✔
1315
                jlu->jlu_exttm.et_flags |= ETF_NANOS_SET;
3✔
1316
                break;
3✔
1317
        }
1318
        jlu->jlu_exttm.et_flags |= ETF_SUB_NOT_IN_FORMAT;
3✔
1319
        jlu->jlu_base_line->set_subsecond_time(
3✔
1320
            std::chrono::milliseconds(millis));
1321
    } else if (jlu->jlu_format->elf_level_field == field_name) {
1,715✔
1322
        if (jlu->jlu_format->elf_level_pairs.empty()) {
516✔
1323
            jlu->jlu_base_line->set_level(jlu->jlu_format->convert_level(
321✔
1324
                number_frag, jlu->jlu_batch_context));
1325
        } else {
1326
            auto scan_res
1327
                = scn::scan_int<int64_t>(number_frag.to_string_view());
195✔
1328
            if (!scan_res) {
195✔
1329
                log_error("invalid number %.*s", (int) numberLen, numberVal);
×
1330
                return 0;
×
1331
            }
1332
            auto level_int = scan_res.value().value();
195✔
1333

1334
            for (const auto& pair : jlu->jlu_format->elf_level_pairs) {
705✔
1335
                if (pair.first == level_int) {
632✔
1336
                    jlu->jlu_base_line->set_level(pair.second);
122✔
1337
                    break;
122✔
1338
                }
1339
            }
1340
        }
1341
    } else if (vd != nullptr) {
1,199✔
1342
        if (jlu->jlu_format->elf_thread_id_field == field_name) {
1,199✔
1343
            auto& sbc = *jlu->jlu_batch_context;
124✔
1344
            auto tid_iter = sbc.sbc_tids.ltis_tid_ranges.find(number_frag);
124✔
1345
            if (tid_iter == sbc.sbc_tids.ltis_tid_ranges.end()) {
124✔
1346
                jlu->jlu_tid_frag = number_frag.to_owned(sbc.sbc_allocator);
71✔
1347
            } else {
1348
                jlu->jlu_tid_frag = tid_iter->first;
53✔
1349
            }
1350
        }
1351
        if ((vd->vd_meta.lvm_kind == value_kind_t::VALUE_INTEGER
1,199✔
1352
             || vd->vd_meta.lvm_kind == value_kind_t::VALUE_FLOAT)
154✔
1353
            && !vd->vd_meta.lvm_foreign_key && !vd->vd_meta.lvm_identifier)
1,063✔
1354
        {
1355
            auto scan_res
1356
                = scn::scan_value<double>(number_frag.to_string_view());
182✔
1357
            if (!scan_res) {
182✔
1358
                log_error("invalid number %.*s", (int) numberLen, numberVal);
×
1359
                return 0;
×
1360
            }
1361
            val = scan_res.value().value();
182✔
1362
            if (jlu->jlu_format->elf_duration_field == field_name) {
182✔
1363
                jlu->jlu_duration = std::chrono::microseconds(
×
1364
                    static_cast<int64_t>(val.value() * 1000000));
18✔
1365
            }
1366
        }
1367
    }
1368

1369
    jlu->add_sub_lines_for(vd,
37,823✔
1370
                           ypc->is_level(1),
37,823✔
1371
                           val,
1372
                           (const unsigned char*) numberVal,
1373
                           numberLen,
1374
                           nullptr);
1375

1376
    return 1;
37,823✔
1377
}
1378

1379
static int
1380
json_array_start(void* ctx)
65,926✔
1381
{
1382
    auto* ypc = (yajlpp_parse_context*) ctx;
65,926✔
1383
    auto* jlu = (json_log_userdata*) ypc->ypc_userdata;
65,926✔
1384

1385
    jlu->jlu_sub_start.push(yajl_get_bytes_consumed(jlu->jlu_handle) - 1);
65,926✔
1386
    if (ypc->ypc_path_index_stack.size() == 2) {
65,926✔
1387
        const auto* vd = jlu->get_field_def(ypc);
20,342✔
1388

1389
        jlu->add_sub_lines_for(vd, true, std::nullopt, nullptr, -1, nullptr);
20,342✔
1390
    }
1391

1392
    return 1;
65,926✔
1393
}
1394

1395
static int
1396
json_array_start_const(void* ctx)
9,823✔
1397
{
1398
    auto* ypc = (yajlpp_parse_context*) ctx;
9,823✔
1399
    auto* jlu = (json_log_userdata*) ypc->ypc_userdata;
9,823✔
1400

1401
    jlu->jlu_sub_start.push(yajl_get_bytes_consumed(jlu->jlu_handle) - 1);
9,823✔
1402

1403
    return 1;
9,823✔
1404
}
1405

1406
static int
1407
json_array_end(void* ctx)
9,803✔
1408
{
1409
    auto* ypc = (yajlpp_parse_context*) ctx;
9,803✔
1410
    auto* jlu = (json_log_userdata*) ypc->ypc_userdata;
9,803✔
1411
    const auto* vd = ypc->ypc_path_index_stack.size() > 1
9,803✔
1412
        ? jlu->get_field_def(ypc)
9,803✔
1413
        : nullptr;
9,803✔
1414

1415
    if (ypc->ypc_path_index_stack.size() == 1 || vd != nullptr) {
9,803✔
1416
        intern_string_t field_name;
2,606✔
1417
        if (vd != nullptr) {
2,606✔
1418
            field_name = vd->vd_meta.lvm_name;
×
1419
        } else {
1420
            field_name = ypc->get_path();
2,606✔
1421
        }
1422
        auto sub_start = jlu->jlu_sub_start.top();
2,606✔
1423
        size_t sub_end = yajl_get_bytes_consumed(jlu->jlu_handle);
2,606✔
1424
        auto json_frag = string_fragment::from_byte_range(
2,606✔
1425
            jlu->jlu_shared_buffer.get_data(), sub_start, sub_end);
2,606✔
1426
        jlu->jlu_format->jlf_line_values.lvv_values.emplace_back(
2,606✔
1427
            jlu->jlu_format->get_value_meta(field_name,
5,212✔
1428
                                            value_kind_t::VALUE_JSON),
1429
            json_frag);
1430
        if (field_name == jlu->jlu_format->elf_opid_field) {
2,606✔
1431
            jlu->jlu_opid_desc_frag = json_frag;
29✔
1432
        }
1433
    }
1434
    jlu->jlu_sub_start.pop();
9,803✔
1435

1436
    return 1;
9,803✔
1437
}
1438

1439
static int
1440
read_array_end(void* ctx)
65,682✔
1441
{
1442
    auto* ypc = (yajlpp_parse_context*) ctx;
65,682✔
1443
    auto* jlu = (json_log_userdata*) ypc->ypc_userdata;
65,682✔
1444
    const auto* vd = ypc->ypc_path_index_stack.size() > 1
65,682✔
1445
        ? jlu->get_field_def(ypc)
65,682✔
1446
        : nullptr;
65,682✔
1447

1448
    if (ypc->ypc_path_index_stack.size() == 1 || vd != nullptr) {
65,682✔
1449
        const intern_string_t field_name = ypc->get_path_fragment_i(0);
20,311✔
1450
        auto sub_start = jlu->jlu_sub_start.top();
20,311✔
1451
        jlu->jlu_sub_start.pop();
20,311✔
1452
        size_t sub_end = yajl_get_bytes_consumed(jlu->jlu_handle);
20,311✔
1453
        auto json_frag = string_fragment::from_byte_range(
20,311✔
1454
            jlu->jlu_shared_buffer.get_data(), sub_start, sub_end);
20,311✔
1455
        if (field_name == jlu->jlu_format->elf_opid_field) {
20,311✔
1456
            jlu->jlu_opid_desc_frag = json_frag;
2,463✔
1457
        }
1458
    }
1459

1460
    return 1;
65,682✔
1461
}
1462

1463
static const json_path_container json_log_handlers = {
1464
    yajlpp::pattern_property_handler("\\w+")
1465
        .add_cb(read_json_null)
1466
        .add_cb(read_json_bool)
1467
        .add_cb(read_json_number)
1468
        .add_cb(read_json_field),
1469
};
1470

1471
static int rewrite_json_field(yajlpp_parse_context* ypc,
1472
                              const unsigned char* str,
1473
                              size_t len,
1474
                              yajl_string_props_t*);
1475

1476
static int
1477
rewrite_json_null(yajlpp_parse_context* ypc)
1,290✔
1478
{
1479
    auto jlu = (json_log_userdata*) ypc->ypc_userdata;
1,290✔
1480
    const auto* vd = jlu->get_field_def(ypc);
1,290✔
1481

1482
    if (!ypc->is_level(1) && vd == nullptr) {
1,290✔
1483
        return 1;
1,228✔
1484
    }
1485
    jlu->jlu_format->jlf_line_values.lvv_values.emplace_back(
62✔
1486
        jlu->jlu_format->get_value_meta(ypc, vd, value_kind_t::VALUE_NULL));
124✔
1487

1488
    return 1;
62✔
1489
}
1490

1491
static int
1492
rewrite_json_bool(yajlpp_parse_context* ypc, int val)
5,641✔
1493
{
1494
    auto* jlu = (json_log_userdata*) ypc->ypc_userdata;
5,641✔
1495
    const auto* vd = jlu->get_field_def(ypc);
5,641✔
1496

1497
    if (!ypc->is_level(1) && vd == nullptr) {
5,641✔
1498
        return 1;
5,007✔
1499
    }
1500
    jlu->jlu_format->jlf_line_values.lvv_values.emplace_back(
634✔
1501
        jlu->jlu_format->get_value_meta(ypc, vd, value_kind_t::VALUE_BOOLEAN),
634✔
1502
        (bool) val);
634✔
1503
    return 1;
634✔
1504
}
1505

1506
static int
1507
rewrite_json_int(yajlpp_parse_context* ypc, long long val)
7,483✔
1508
{
1509
    auto* jlu = (json_log_userdata*) ypc->ypc_userdata;
7,483✔
1510
    const auto* vd = jlu->get_field_def(ypc);
7,483✔
1511

1512
    if (vd != nullptr) {
7,483✔
1513
        const intern_string_t field_name = vd->vd_meta.lvm_name;
802✔
1514
        if (jlu->jlu_format->lf_timestamp_field == field_name) {
802✔
1515
            long long divisor = jlu->jlu_format->elf_timestamp_divisor;
22✔
1516
            timeval tv;
1517

1518
            tv.tv_sec = val / divisor;
22✔
1519
            tv.tv_usec = fmod(val, divisor) * (1000000.0 / divisor);
22✔
1520
            jlu->jlu_format->lf_date_time.to_localtime(tv.tv_sec,
22✔
1521
                                                       jlu->jlu_exttm);
22✔
1522
            jlu->jlu_exttm.et_gmtoff
1523
                = jlu->jlu_format->lf_date_time.dts_local_offset_cache;
22✔
1524
            jlu->jlu_exttm.et_flags |= ETF_MACHINE_ORIENTED
22✔
1525
                | ETF_SUB_NOT_IN_FORMAT | ETF_ZONE_SET | ETF_Z_FOR_UTC;
1526
            if (divisor == 1) {
22✔
1527
                jlu->jlu_exttm.et_flags |= ETF_MICROS_SET;
4✔
1528
            } else {
1529
                jlu->jlu_exttm.et_flags |= ETF_MILLIS_SET;
18✔
1530
            }
1531
            jlu->jlu_exttm.et_nsec = tv.tv_usec * 1000;
22✔
1532
        } else if (jlu->jlu_format->lf_subsecond_field == field_name) {
780✔
1533
            jlu->jlu_exttm.et_flags &= ~(ETF_MICROS_SET | ETF_MILLIS_SET);
4✔
1534
            switch (jlu->jlu_format->lf_subsecond_unit.value()) {
4✔
1535
                case log_format::subsecond_unit::milli:
×
1536
                    jlu->jlu_exttm.et_nsec = val * 1000000;
×
1537
                    jlu->jlu_exttm.et_flags |= ETF_MILLIS_SET;
×
1538
                    break;
×
1539
                case log_format::subsecond_unit::micro:
×
1540
                    jlu->jlu_exttm.et_nsec = val * 1000;
×
1541
                    jlu->jlu_exttm.et_flags |= ETF_MICROS_SET;
×
1542
                    break;
×
1543
                case log_format::subsecond_unit::nano:
4✔
1544
                    jlu->jlu_exttm.et_nsec = val;
4✔
1545
                    jlu->jlu_exttm.et_flags |= ETF_NANOS_SET;
4✔
1546
                    break;
4✔
1547
            }
1548
            jlu->jlu_exttm.et_flags |= ETF_SUB_NOT_IN_FORMAT;
4✔
1549
        }
1550
    }
1551

1552
    if (!ypc->is_level(1) && vd == nullptr) {
7,483✔
1553
        return 1;
6,625✔
1554
    }
1555
    if (vd != nullptr
858✔
1556
        && vd->vd_meta.lvm_name == jlu->jlu_format->elf_thread_id_field)
858✔
1557
    {
1558
        jlu->jlu_tid_number = val;
156✔
1559
    }
1560
    jlu->jlu_format->jlf_line_values.lvv_values.emplace_back(
858✔
1561
        jlu->jlu_format->get_value_meta(ypc, vd, value_kind_t::VALUE_INTEGER),
858✔
1562
        (int64_t) val);
858✔
1563
    return 1;
858✔
1564
}
1565

1566
static int
1567
rewrite_json_double(yajlpp_parse_context* ypc, double val)
188✔
1568
{
1569
    auto* jlu = (json_log_userdata*) ypc->ypc_userdata;
188✔
1570
    const auto* vd = jlu->get_field_def(ypc);
188✔
1571

1572
    if (vd != nullptr) {
188✔
1573
        const intern_string_t field_name = vd->vd_meta.lvm_name;
180✔
1574
        if (jlu->jlu_format->lf_timestamp_field == field_name) {
180✔
1575
            long long divisor = jlu->jlu_format->elf_timestamp_divisor;
156✔
1576
            timeval tv;
1577

1578
            tv.tv_sec = val / divisor;
156✔
1579
            tv.tv_usec = fmod(val, divisor) * (1000000.0 / divisor);
156✔
1580
            jlu->jlu_format->lf_date_time.to_localtime(tv.tv_sec,
156✔
1581
                                                       jlu->jlu_exttm);
156✔
1582
            jlu->jlu_exttm.et_gmtoff
1583
                = jlu->jlu_format->lf_date_time.dts_local_offset_cache;
156✔
1584
            jlu->jlu_exttm.et_flags |= ETF_MACHINE_ORIENTED
156✔
1585
                | ETF_SUB_NOT_IN_FORMAT | ETF_ZONE_SET | ETF_Z_FOR_UTC;
1586
            if (divisor == 1) {
156✔
1587
                jlu->jlu_exttm.et_flags |= ETF_MICROS_SET;
148✔
1588
            } else {
1589
                jlu->jlu_exttm.et_flags |= ETF_MILLIS_SET;
8✔
1590
            }
1591
            jlu->jlu_exttm.et_nsec = tv.tv_usec * 1000;
156✔
1592
        } else if (jlu->jlu_format->lf_subsecond_field == field_name) {
24✔
1593
            jlu->jlu_exttm.et_flags &= ~(ETF_MICROS_SET | ETF_MILLIS_SET);
×
1594
            switch (jlu->jlu_format->lf_subsecond_unit.value()) {
×
1595
                case log_format::subsecond_unit::milli:
×
1596
                    jlu->jlu_exttm.et_nsec = val * 1000000;
×
1597
                    jlu->jlu_exttm.et_flags |= ETF_MILLIS_SET;
×
1598
                    break;
×
1599
                case log_format::subsecond_unit::micro:
×
1600
                    jlu->jlu_exttm.et_nsec = val * 1000;
×
1601
                    jlu->jlu_exttm.et_flags |= ETF_MICROS_SET;
×
1602
                    break;
×
1603
                case log_format::subsecond_unit::nano:
×
1604
                    jlu->jlu_exttm.et_nsec = val;
×
1605
                    jlu->jlu_exttm.et_flags |= ETF_NANOS_SET;
×
1606
                    break;
×
1607
            }
1608
            jlu->jlu_exttm.et_flags |= ETF_SUB_NOT_IN_FORMAT;
×
1609
        } else if (jlu->jlu_format->elf_duration_field == field_name) {
24✔
1610
            jlu->jlu_duration = std::chrono::microseconds(
48✔
1611
                static_cast<int64_t>(val * 1000000.0));
24✔
1612
        }
1613
    }
1614

1615
    if (!ypc->is_level(1) && vd == nullptr) {
188✔
1616
        return 1;
×
1617
    }
1618
    jlu->jlu_format->jlf_line_values.lvv_values.emplace_back(
188✔
1619
        jlu->jlu_format->get_value_meta(ypc, vd, value_kind_t::VALUE_FLOAT),
376✔
1620
        val);
1621

1622
    return 1;
188✔
1623
}
1624

1625
static const json_path_container json_log_rewrite_handlers = {
1626
    yajlpp::pattern_property_handler("\\w+")
1627
        .add_cb(rewrite_json_null)
1628
        .add_cb(rewrite_json_bool)
1629
        .add_cb(rewrite_json_int)
1630
        .add_cb(rewrite_json_double)
1631
        .add_cb(rewrite_json_field),
1632
};
1633

1634
bool
1635
external_log_format::scan_for_partial(const log_format_file_state& lffs,
1✔
1636
                                      shared_buffer_ref& sbr,
1637
                                      size_t& len_out) const
1638
{
1639
    if (this->elf_type != elf_type_t::ELF_TYPE_TEXT) {
1✔
1640
        return false;
×
1641
    }
1642

1643
    const auto& pat
1644
        = this->elf_pattern_order[lffs.lffs_pattern_locks.last_pattern_index()];
1✔
1645
    if (!this->lf_multiline) {
1✔
1646
        len_out = pat->p_pcre.pp_value->match_partial(sbr.to_string_fragment());
1✔
1647
        return true;
1✔
1648
    }
1649

1650
    if (pat->p_timestamp_end == -1 || pat->p_timestamp_end > (int) sbr.length())
×
1651
    {
1652
        len_out = 0;
×
1653
        return false;
×
1654
    }
1655

1656
    len_out = pat->p_pcre.pp_value->match_partial(sbr.to_string_fragment());
×
1657
    return (int) len_out > pat->p_timestamp_end;
×
1658
}
1659

1660
std::vector<lnav::console::snippet>
1661
external_log_format::get_snippets() const
23✔
1662
{
1663
    std::vector<lnav::console::snippet> retval;
23✔
1664

1665
    for (const auto& src_pair : this->elf_format_sources) {
46✔
1666
        retval.emplace_back(lnav::console::snippet::from(src_pair.first, "")
46✔
1667
                                .with_line(src_pair.second));
23✔
1668
    }
1669

1670
    return retval;
23✔
1671
}
×
1672

1673
log_format::scan_result_t
1674
external_log_format::scan_json(std::vector<logline>& dst,
185,838✔
1675
                               const line_info& li,
1676
                               shared_buffer_ref& sbr,
1677
                               scan_batch_context& sbc)
1678
{
1679
    logline ll(
1680
        li.li_file_range.fr_offset, std::chrono::microseconds{0}, LEVEL_INFO);
185,838✔
1681
    auto line_frag = sbr.to_string_fragment();
185,838✔
1682

1683
    if (!line_frag.startswith("{")) {
185,838✔
1684
        if (!this->lf_specialized) {
172,779✔
1685
            return scan_no_match{"line is not a JSON object"};
172,774✔
1686
        }
1687

1688
        if (!dst.empty()) {
5✔
1689
            ll.set_time(dst.back().get_time<std::chrono::microseconds>());
5✔
1690
        }
1691
        ll.set_level(LEVEL_INVALID);
5✔
1692
        dst.emplace_back(ll);
5✔
1693
        return scan_match{0};
5✔
1694
    }
1695

1696
    auto& ypc = *(this->jlf_parse_context);
13,059✔
1697
    yajl_handle handle = this->jlf_yajl_handle.get();
13,059✔
1698
    json_log_userdata jlu(sbr, &sbc);
13,059✔
1699

1700
    if (li.li_partial) {
13,059✔
1701
        log_debug("skipping partial line at offset %lld",
28✔
1702
                  li.li_file_range.fr_offset);
1703
        if (this->lf_specialized) {
28✔
1704
            if (!dst.empty()) {
2✔
1705
                ll.set_time(dst.back().get_time<std::chrono::microseconds>());
2✔
1706
            }
1707
            ll.set_level(LEVEL_INVALID);
2✔
1708
            dst.emplace_back(ll);
2✔
1709
        }
1710
        return scan_incomplete{};
28✔
1711
    }
1712

1713
    const auto* line_data = (const unsigned char*) sbr.get_data();
13,031✔
1714

1715
    this->lf_desc_captures.clear();
13,031✔
1716
    this->lf_desc_allocator.reset();
13,031✔
1717

1718
    yajl_reset(handle);
13,031✔
1719
    ypc.set_static_handler(json_log_handlers.jpc_children[0]);
13,031✔
1720
    ypc.ypc_userdata = &jlu;
13,031✔
1721
    ypc.ypc_ignore_unused = true;
13,031✔
1722
    ypc.ypc_alt_callbacks.yajl_start_array = json_array_start;
13,031✔
1723
    ypc.ypc_alt_callbacks.yajl_start_map = json_array_start;
13,031✔
1724
    ypc.ypc_alt_callbacks.yajl_end_array = read_array_end;
13,031✔
1725
    ypc.ypc_alt_callbacks.yajl_end_map = read_array_end;
13,031✔
1726
    jlu.jlu_format = this;
13,031✔
1727
    jlu.jlu_base_line = &ll;
13,031✔
1728
    jlu.jlu_line_value = sbr.get_data();
13,031✔
1729
    jlu.jlu_line_size = sbr.length();
13,031✔
1730
    jlu.jlu_handle = handle;
13,031✔
1731
    jlu.jlu_format_hits.resize(this->jlf_line_format.size());
13,031✔
1732
    if (yajl_parse(handle, line_data, sbr.length()) == yajl_status_ok
13,031✔
1733
        && yajl_complete_parse(handle) == yajl_status_ok)
13,031✔
1734
    {
1735
        if (jlu.jlu_scan_error) {
12,818✔
1736
            if (this->lf_specialized) {
19✔
1737
                if (!dst.empty()) {
10✔
1738
                    ll.set_time(
10✔
1739
                        dst.back().get_time<std::chrono::microseconds>());
10✔
1740
                }
1741
                ll.set_level(LEVEL_INVALID);
10✔
1742
                dst.emplace_back(ll);
10✔
1743
                return scan_match{0};
10✔
1744
            }
1745
            return jlu.jlu_scan_error.value();
9✔
1746
        }
1747
        if (ll.get_time<std::chrono::microseconds>().count() == 0) {
12,799✔
1748
            if (this->lf_specialized) {
8,449✔
1749
                if (!dst.empty()) {
×
1750
                    ll.set_time(
×
1751
                        dst.back().get_time<std::chrono::microseconds>());
×
1752
                }
1753
                ll.set_ignore(true);
×
1754
                dst.emplace_back(ll);
×
1755
                return scan_match{0};
×
1756
            }
1757

1758
            return scan_no_match{
8,449✔
1759
                "JSON message does not have expected timestamp property"};
8,449✔
1760
        }
1761

1762
        if (jlu.jlu_tid_frag) {
4,350✔
1763
            this->jlf_line_values.lvv_thread_id_value
1764
                = jlu.jlu_tid_frag->to_owned(
372✔
1765
                    this->jlf_line_values.lvv_allocator);
248✔
1766
            auto tid_iter = sbc.sbc_tids.insert_tid(
248✔
1767
                sbc.sbc_allocator,
1768
                jlu.jlu_tid_frag.value(),
124✔
1769
                ll.get_time<std::chrono::microseconds>());
124✔
1770
            tid_iter->second.titr_level_stats.update_msg_count(
124✔
1771
                ll.get_msg_level());
1772
            ll.merge_bloom_bits(jlu.jlu_tid_frag->bloom_bits());
124✔
1773
        } else {
1774
            auto tid_iter = sbc.sbc_tids.insert_tid(
4,226✔
1775
                sbc.sbc_allocator,
1776
                string_fragment{},
×
1777
                ll.get_time<std::chrono::microseconds>());
4,226✔
1778
            tid_iter->second.titr_level_stats.update_msg_count(
4,226✔
1779
                ll.get_msg_level());
1780
        }
1781

1782
        auto found_opid_desc = false;
4,350✔
1783
        if (this->elf_opid_field.empty()
4,350✔
1784
            && this->lf_opid_source.value_or(opid_source_t::from_description)
799✔
1785
                == opid_source_t::from_description
1786
            && this->lf_opid_description_def->size() == 1)
5,149✔
1787
        {
1788
            const auto& od = this->lf_opid_description_def->begin()->second;
322✔
1789
            for (const auto& desc : *od.od_descriptors) {
966✔
1790
                auto desc_iter
1791
                    = this->lf_desc_captures.find(desc.od_field.pp_value);
644✔
1792
                if (desc_iter == this->lf_desc_captures.end()) {
644✔
1793
                    continue;
602✔
1794
                }
1795
                jlu.jlu_opid_hasher.update(desc_iter->second);
42✔
1796
                found_opid_desc = true;
42✔
1797
            }
1798

1799
        } else if (!jlu.jlu_opid_desc_frag && !jlu.jlu_opid_frag
5,593✔
1800
                   && jlu.jlu_duration > 0us)
5,593✔
1801
        {
1802
            jlu.jlu_opid_hasher.update(sbr.to_string_fragment());
×
1803
        }
1804

1805
        if (jlu.jlu_opid_desc_frag || jlu.jlu_duration > 0us
6,237✔
1806
            || (found_opid_desc && this->lf_opid_description_def->size() == 1))
6,237✔
1807
        {
1808
            char buf[hasher::STRING_SIZE];
1809
            jlu.jlu_opid_hasher.to_string(buf);
2,484✔
1810
            auto opid_frag = string_fragment::from_bytes(buf, sizeof(buf) - 1);
2,484✔
1811
            auto opid_iter = sbc.sbc_opids.los_opid_ranges.find(opid_frag);
2,484✔
1812
            if (opid_iter == sbc.sbc_opids.los_opid_ranges.end()) {
2,484✔
1813
                jlu.jlu_opid_frag = opid_frag.to_owned(sbc.sbc_allocator);
2,472✔
1814
            } else {
1815
                jlu.jlu_opid_frag = opid_iter->first;
12✔
1816
            }
1817
        }
1818

1819
        if (jlu.jlu_opid_frag) {
4,350✔
1820
            ll.merge_bloom_bits(jlu.jlu_opid_frag->bloom_bits());
3,516✔
1821
            this->jlf_line_values.lvv_opid_value
1822
                = jlu.jlu_opid_frag->to_string();
3,516✔
1823
            this->jlf_line_values.lvv_opid_provenance
1824
                = logline_value_vector::opid_provenance::file;
3,516✔
1825
            auto opid_iter = sbc.sbc_opids.insert_op(
7,032✔
1826
                sbc.sbc_allocator,
1827
                jlu.jlu_opid_frag.value(),
3,516✔
1828
                ll.get_time<std::chrono::microseconds>(),
3,516✔
1829
                this->lf_timestamp_point_of_reference,
1830
                jlu.jlu_duration);
1831
            opid_iter->second.otr_level_stats.update_msg_count(
3,516✔
1832
                ll.get_msg_level());
1833
            auto& elems = opid_iter->second.otr_description.lod_elements;
3,516✔
1834
            if (jlu.jlu_opid_desc_frag && elems.empty()) {
3,516✔
1835
                elems.insert(0,
×
1836
                             fmt::format(FMT_STRING(" {}"),
9,828✔
1837
                                         jlu.jlu_opid_desc_frag.value()));
1838
            }
1839

1840
            if (jlu.jlu_subid) {
3,516✔
1841
                auto subid_frag
1842
                    = string_fragment::from_str(jlu.jlu_subid.value());
×
1843

1844
                auto* ostr = sbc.sbc_opids.sub_op_in_use(
×
1845
                    sbc.sbc_allocator,
1846
                    opid_iter,
1847
                    subid_frag,
1848
                    ll.get_time<std::chrono::microseconds>(),
×
1849
                    ll.get_msg_level());
1850
                if (ostr != nullptr && ostr->ostr_description.empty()) {
×
1851
                    log_op_description sub_desc;
×
1852
                    this->update_op_description(
×
1853
                        *this->lf_subid_description_def_vec, sub_desc);
×
1854
                    if (!sub_desc.lod_elements.empty()) {
×
1855
                        auto& sub_desc_def
1856
                            = this->lf_subid_description_def_vec->at(
×
1857
                                sub_desc.lod_index.value());
×
1858
                        ostr->ostr_description
1859
                            = sub_desc_def->to_string(sub_desc.lod_elements);
×
1860
                    }
1861
                }
1862
            }
1863

1864
            auto& otr = opid_iter->second;
3,516✔
1865
            this->update_op_description(*this->lf_opid_description_def_vec,
3,516✔
1866
                                        otr.otr_description);
3,516✔
1867
        } else {
1868
            this->jlf_line_values.lvv_opid_value = std::nullopt;
834✔
1869
        }
1870

1871
        jlu.jlu_sub_line_count += this->jlf_line_format_init_count;
4,350✔
1872
        ll.set_has_ansi(jlu.jlu_has_ansi);
4,350✔
1873
        ll.set_valid_utf(jlu.jlu_valid_utf);
4,350✔
1874
        for (int lpc = 0; lpc < jlu.jlu_sub_line_count; lpc++) {
26,861✔
1875
            ll.set_sub_offset(lpc);
22,511✔
1876
            ll.set_continued(lpc > 0);
22,511✔
1877
            dst.emplace_back(ll);
22,511✔
1878
        }
1879
        this->lf_timestamp_flags = jlu.jlu_exttm.et_flags;
4,350✔
1880

1881
        if (!this->lf_specialized) {
4,350✔
1882
            static const intern_string_t ts_field
1883
                = intern_string::lookup("__timestamp__", -1);
3,753✔
1884
            static const intern_string_t level_field
1885
                = intern_string::lookup("__level__");
5,387✔
1886
            for (const auto& [index, jfe] :
34,999✔
1887
                 lnav::itertools::enumerate(this->jlf_line_format))
38,752✔
1888
            {
1889
                if (jfe.jfe_type != json_log_field::VARIABLE
83,374✔
1890
                    || jfe.jfe_value.pp_value == ts_field
20,217✔
1891
                    || jfe.jfe_value.pp_value == level_field
16,545✔
1892
                    || jfe.jfe_default_value != "-")
51,463✔
1893
                {
1894
                    continue;
20,882✔
1895
                }
1896
                if (!jlu.jlu_format_hits[index]) {
10,364✔
1897
                    jlu.jlu_strikes += 1;
9,824✔
1898
                }
1899
            }
1900
        }
1901
    } else {
1902
        unsigned char* msg;
1903
        int line_count = 2;
213✔
1904

1905
        msg = yajl_get_error(
426✔
1906
            handle, 1, (const unsigned char*) sbr.get_data(), sbr.length());
213✔
1907
        if (msg != nullptr) {
213✔
1908
            auto msg_frag = string_fragment::from_c_str(msg);
213✔
1909
            log_debug("Unable to parse line at offset %lld: %.*s",
213✔
1910
                      li.li_file_range.fr_offset,
1911
                      msg_frag.length(),
1912
                      msg_frag.data());
1913
            line_count = msg_frag.count('\n') + 1;
213✔
1914
            yajl_free_error(handle, msg);
213✔
1915
        }
1916
        if (!this->lf_specialized || dst.empty()) {
213✔
1917
            return scan_no_match{"JSON parsing failed"};
210✔
1918
        }
1919
        for (int lpc = 0; lpc < line_count; lpc++) {
15✔
1920
            log_level_t level = LEVEL_INVALID;
12✔
1921

1922
            ll.set_time(dst.back().get_timeval());
12✔
1923
            ll.set_continued(lpc > 0);
12✔
1924
            ll.set_level(level);
12✔
1925
            ll.set_sub_offset(lpc);
12✔
1926
            dst.emplace_back(ll);
12✔
1927
        }
1928
    }
1929

1930
    if (jlu.jlu_quality > 0) {
4,353✔
1931
        jlu.jlu_quality += 3000;
924✔
1932
    }
1933
    return scan_match{jlu.jlu_quality, jlu.jlu_strikes, jlu.jlu_precision};
4,353✔
1934
}
13,059✔
1935

1936
log_format::scan_result_t
1937
external_log_format::scan(logfile& lf,
936,829✔
1938
                          std::vector<logline>& dst,
1939
                          const line_info& li,
1940
                          shared_buffer_ref& sbr,
1941
                          scan_batch_context& sbc)
1942
{
1943
    if (dst.empty()) {
936,829✔
1944
        auto file_options = lf.get_file_options();
38,790✔
1945

1946
        if (file_options) {
38,790✔
1947
            this->lf_date_time.dts_default_zone
1948
                = file_options->second.fo_default_zone.pp_value;
2,431✔
1949
        } else {
1950
            this->lf_date_time.dts_default_zone = nullptr;
36,359✔
1951
        }
1952
    }
38,790✔
1953

1954
    sbc.sbc_value_stats.resize(this->elf_value_defs.size());
936,829✔
1955
    if (this->elf_type == elf_type_t::ELF_TYPE_JSON) {
936,829✔
1956
        return this->scan_json(dst, li, sbr, sbc);
182,568✔
1957
    }
1958

1959
    int curr_fmt = -1, orig_lock = sbc.sbc_pattern_locks.last_pattern_index();
754,261✔
1960
    int pat_index = orig_lock;
754,261✔
1961
    auto line_sf = sbr.to_string_fragment();
754,261✔
1962
    thread_local auto md = lnav::pcre2pp::match_data::unitialized();
754,261✔
1963
    char tmp_opid_buf[hasher::STRING_SIZE];
1964

1965
    while (::next_format(this->elf_pattern_order, curr_fmt, pat_index)) {
2,466,426✔
1966
        auto* fpat = this->elf_pattern_order[curr_fmt].get();
1,718,553✔
1967
        auto* pat = fpat->p_pcre.pp_value.get();
1,718,553✔
1968

1969
        auto found_match
1970
            = pat->capture_from(line_sf).into(md).found_p(PCRE2_NO_UTF_CHECK);
1,718,553✔
1971
        if (!found_match) {
1,718,553✔
1972
            if (!sbc.sbc_pattern_locks.empty() && pat_index != -1) {
1,712,162✔
1973
                curr_fmt = -1;
2,016✔
1974
                pat_index = -1;
2,016✔
1975
            }
1976
            continue;
1,712,165✔
1977
        }
1978

1979
        auto ts = md[fpat->p_timestamp_field_index];
6,391✔
1980
        auto level_cap = md[fpat->p_level_field_index];
6,391✔
1981
        auto opid_cap = md[fpat->p_opid_field_index];
6,391✔
1982
        const char* last;
1983
        exttm log_time_tm;
6,391✔
1984
        timeval log_tv;
1985
        uint64_t opid_bloom = 0;
6,391✔
1986
        char combined_datetime_buf[512];
1987

1988
        if (fpat->p_time_field_index != -1) {
6,391✔
1989
            auto time_cap = md[fpat->p_time_field_index];
×
1990
            if (ts && time_cap) {
×
1991
                auto ts_str_len = snprintf(combined_datetime_buf,
×
1992
                                           sizeof(combined_datetime_buf),
1993
                                           "%.*sT%.*s",
1994
                                           ts->length(),
1995
                                           ts->data(),
1996
                                           time_cap->length(),
1997
                                           time_cap->data());
1998
                ts = string_fragment::from_bytes(combined_datetime_buf,
×
1999
                                                 ts_str_len);
×
2000
            }
2001
        }
2002

2003
        auto level = this->convert_level(
6,391✔
2004
            level_cap.value_or(string_fragment::invalid()), &sbc);
6,391✔
2005

2006
        if (!ts) {
6,391✔
2007
            level = log_level_t::LEVEL_INVALID;
×
2008
        } else if ((last
6,391✔
2009
                    = this->lf_date_time.scan(ts->data(),
12,782✔
2010
                                              ts->length(),
6,391✔
2011
                                              this->get_timestamp_formats(),
2012
                                              &log_time_tm,
2013
                                              log_tv))
2014
                   == nullptr)
6,391✔
2015
        {
2016
            auto ls = this->lf_date_time.unlock();
13✔
2017
            if ((last = this->lf_date_time.scan(ts->data(),
26✔
2018
                                                ts->length(),
13✔
2019
                                                this->get_timestamp_formats(),
2020
                                                &log_time_tm,
2021
                                                log_tv))
2022
                == nullptr)
13✔
2023
            {
2024
                this->lf_date_time.relock(ls);
2✔
2025
                continue;
3✔
2026
            }
2027
            if (last != nullptr) {
11✔
2028
                auto old_flags = this->lf_timestamp_flags & DATE_TIME_SET_FLAGS;
11✔
2029
                auto new_flags = log_time_tm.et_flags & DATE_TIME_SET_FLAGS;
11✔
2030

2031
                // It is unlikely a valid timestamp would lose much
2032
                // precision.
2033
                if (new_flags != old_flags) {
11✔
2034
                    continue;
1✔
2035
                }
2036
            }
2037

2038
            log_debug("%s:%zu: date-time re-locked to %d",
10✔
2039
                      lf.get_unique_path().c_str(),
2040
                      dst.size(),
2041
                      this->lf_date_time.dts_fmt_lock);
2042
        }
2043

2044
        this->lf_timestamp_flags = log_time_tm.et_flags;
6,388✔
2045

2046
        if (!(this->lf_timestamp_flags
12,776✔
2047
              & (ETF_MILLIS_SET | ETF_MICROS_SET | ETF_NANOS_SET))
6,388✔
2048
            && !dst.empty()
5,576✔
2049
            && dst.back().get_time<std::chrono::seconds>().count()
4,792✔
2050
                == log_tv.tv_sec
4,792✔
2051
            && dst.back()
15,560✔
2052
                    .get_subsecond_time<std::chrono::milliseconds>()
9,984✔
2053
                    .count()
3,596✔
2054
                != 0)
2055
        {
2056
            auto log_ms
2057
                = dst.back().get_subsecond_time<std::chrono::microseconds>();
×
2058

2059
            log_time_tm.et_nsec
2060
                = std::chrono::duration_cast<std::chrono::nanoseconds>(log_ms)
×
2061
                      .count();
×
2062
            log_tv.tv_usec
2063
                = std::chrono::duration_cast<std::chrono::microseconds>(log_ms)
×
2064
                      .count();
×
2065
        }
2066

2067
        if (!((log_time_tm.et_flags & ETF_DAY_SET)
6,388✔
2068
              && (log_time_tm.et_flags & ETF_MONTH_SET)
6,347✔
2069
              && (log_time_tm.et_flags & ETF_YEAR_SET)))
6,347✔
2070
        {
2071
            this->check_for_new_year(dst, log_time_tm, log_tv);
1,212✔
2072
        }
2073

2074
        auto log_us = to_us(log_tv);
6,388✔
2075
        if (this->elf_opid_field.empty()
6,388✔
2076
            && !fpat->p_opid_description_field_indexes.empty())
6,388✔
2077
        {
2078
            auto empty_desc = true;
4,062✔
2079
            hasher h;
4,062✔
2080
            for (const auto& fidx : fpat->p_opid_description_field_indexes) {
12,186✔
2081
                auto desc_cap = md[fidx];
8,124✔
2082
                if (desc_cap) {
8,124✔
2083
                    h.update(desc_cap.value());
8,076✔
2084
                    empty_desc = false;
8,076✔
2085
                }
2086
            }
2087
            if (!empty_desc) {
4,062✔
2088
                h.to_string(tmp_opid_buf);
4,062✔
2089
                opid_cap = string_fragment::from_bytes(
8,124✔
2090
                    tmp_opid_buf, sizeof(tmp_opid_buf) - 1);
4,062✔
2091
            }
2092
        }
2093

2094
        auto duration_cap = md[fpat->p_duration_field_index];
6,388✔
2095
        if (duration_cap && !opid_cap) {
6,388✔
2096
            hasher h;
86✔
2097
            h.update(line_sf);
86✔
2098
            h.to_string(tmp_opid_buf);
86✔
2099
            opid_cap = string_fragment::from_bytes(tmp_opid_buf,
172✔
2100
                                                   sizeof(tmp_opid_buf) - 1);
86✔
2101
        }
2102
        if (opid_cap && !opid_cap->empty()) {
6,388✔
2103
            auto duration = std::chrono::microseconds{0};
5,178✔
2104
            if (duration_cap) {
5,178✔
2105
                auto from_res
2106
                    = humanize::try_from<double>(duration_cap.value());
86✔
2107
                if (from_res) {
86✔
2108
                    duration = std::chrono::microseconds(
×
2109
                        static_cast<int64_t>(from_res.value() * 1000000));
86✔
2110
                }
2111
            }
2112
            auto opid_iter
2113
                = sbc.sbc_opids.insert_op(sbc.sbc_allocator,
10,356✔
2114
                                          opid_cap.value(),
5,178✔
2115
                                          log_us,
2116
                                          this->lf_timestamp_point_of_reference,
2117
                                          duration);
2118
            auto& otr = opid_iter->second;
5,178✔
2119

2120
            otr.otr_level_stats.update_msg_count(level);
5,178✔
2121
            if (fpat->p_subid_field_index != -1) {
5,178✔
2122
                auto subid_cap = md[fpat->p_subid_field_index];
78✔
2123
                if (subid_cap && !subid_cap->empty()) {
78✔
2124
                    auto* ostr = sbc.sbc_opids.sub_op_in_use(sbc.sbc_allocator,
234✔
2125
                                                             opid_iter,
2126
                                                             subid_cap.value(),
78✔
2127
                                                             log_us,
2128
                                                             level);
2129
                    if (ostr != nullptr && ostr->ostr_description.empty()) {
78✔
2130
                        log_op_description sub_desc;
53✔
2131
                        this->update_op_description(
53✔
2132
                            *this->lf_subid_description_def_vec,
53✔
2133
                            sub_desc,
2134
                            fpat,
2135
                            md);
2136
                        if (!sub_desc.lod_elements.empty()) {
53✔
2137
                            auto& sub_desc_def
2138
                                = this->lf_subid_description_def_vec->at(
51✔
2139
                                    sub_desc.lod_index.value());
51✔
2140
                            ostr->ostr_description = sub_desc_def->to_string(
102✔
2141
                                sub_desc.lod_elements);
51✔
2142
                        }
2143
                    }
53✔
2144
                }
2145
            }
2146
            this->update_op_description(*this->lf_opid_description_def_vec,
5,178✔
2147
                                        otr.otr_description,
5,178✔
2148
                                        fpat,
2149
                                        md);
2150
            opid_bloom = opid_cap->bloom_bits();
5,178✔
2151
        }
2152

2153
        for (const auto& ivd : fpat->p_value_by_index) {
70,461✔
2154
            if (!ivd.ivd_value_def->vd_meta.lvm_values_index) {
64,073✔
2155
                continue;
12,099✔
2156
            }
2157

2158
            ssize_t cap_size = md.capture_size(ivd.ivd_index);
51,974✔
2159
            auto& lvs = sbc.sbc_value_stats[ivd.ivd_value_def->vd_meta
51,974✔
2160
                                                .lvm_values_index.value()];
51,974✔
2161

2162
            if (cap_size > lvs.lvs_width) {
51,974✔
2163
                lvs.lvs_width = cap_size;
6,908✔
2164
            }
2165
        }
2166

2167
        for (auto value_index : fpat->p_numeric_value_indexes) {
11,290✔
2168
            const indexed_value_def& ivd = fpat->p_value_by_index[value_index];
4,902✔
2169
            const value_def& vd = *ivd.ivd_value_def;
4,902✔
2170
            auto num_cap = md[ivd.ivd_index];
4,902✔
2171

2172
            if (vd.vd_meta.lvm_identifier || vd.vd_meta.lvm_foreign_key) {
4,902✔
2173
                continue;
×
2174
            }
2175

2176
            if (num_cap && num_cap->is_valid()) {
4,902✔
2177
                const scaling_factor* scaling = nullptr;
4,836✔
2178

2179
                if (ivd.ivd_unit_field_index >= 0) {
4,836✔
2180
                    auto unit_cap = md[ivd.ivd_unit_field_index];
80✔
2181

2182
                    if (unit_cap && unit_cap->is_valid()) {
80✔
2183
                        intern_string_t unit_val
2184
                            = intern_string::lookup(unit_cap.value());
80✔
2185

2186
                        auto unit_iter = vd.vd_unit_scaling.find(unit_val);
80✔
2187
                        if (unit_iter != vd.vd_unit_scaling.end()) {
80✔
2188
                            const auto& sf = unit_iter->second;
80✔
2189

2190
                            scaling = &sf;
80✔
2191
                        }
2192
                    }
2193
                }
2194

2195
                std::optional<double> dvalue_opt;
4,836✔
2196
                switch (vd.vd_meta.lvm_kind) {
4,836✔
2197
                    case value_kind_t::VALUE_INTEGER: {
4,670✔
2198
                        int64_t ivalue;
2199
                        auto from_res = fast_float::from_chars(
4,670✔
2200
                            num_cap->begin(), num_cap->end(), ivalue);
2201
                        if (from_res.ec == std::errc()) {
4,670✔
2202
                            dvalue_opt = ivalue;
4,670✔
2203
                        }
2204
                        break;
4,670✔
2205
                    }
2206
                    case value_kind_t::VALUE_FLOAT: {
166✔
2207
                        auto scan_res = scn::scan_value<double>(
2208
                            num_cap->to_string_view());
166✔
2209
                        if (scan_res) {
166✔
2210
                            dvalue_opt = scan_res->value();
166✔
2211
                        }
2212
                        break;
166✔
2213
                    }
2214
                    default:
×
2215
                        break;
×
2216
                }
2217
                if (dvalue_opt) {
4,836✔
2218
                    auto dvalue = dvalue_opt.value();
4,836✔
2219
                    if (scaling != nullptr) {
4,836✔
2220
                        scaling->scale(dvalue);
80✔
2221
                    }
2222
                    sbc.sbc_value_stats[vd.vd_meta.lvm_values_index.value()]
4,836✔
2223
                        .add_value(dvalue);
4,836✔
2224
                }
2225
            }
2226
        }
2227

2228
        dst.emplace_back(li.li_file_range.fr_offset, log_us, level);
6,388✔
2229
        auto& new_line = dst.back();
6,388✔
2230
        new_line.merge_bloom_bits(opid_bloom);
6,388✔
2231

2232
        auto src_file_cap = md[fpat->p_src_file_field_index];
6,388✔
2233
        auto src_line_cap = md[fpat->p_src_line_field_index];
6,388✔
2234
        if (src_file_cap && src_line_cap) {
6,388✔
2235
            auto h = hasher();
302✔
2236
            h.update(this->get_name().c_str());
302✔
2237
            h.update(src_file_cap.value());
302✔
2238
            h.update(src_line_cap.value());
302✔
2239
            new_line.merge_bloom_bits(h.to_bloom_bits());
302✔
2240
            new_line.set_schema_computed(true);
302✔
2241
        }
2242
        auto thread_id_cap = md[fpat->p_thread_id_field_index];
6,388✔
2243
        if (thread_id_cap) {
6,388✔
2244
            auto tid_iter = sbc.sbc_tids.insert_tid(
2,822✔
2245
                sbc.sbc_allocator, thread_id_cap.value(), log_us);
1,411✔
2246
            tid_iter->second.titr_level_stats.update_msg_count(level);
1,411✔
2247
            new_line.merge_bloom_bits(thread_id_cap->bloom_bits());
1,411✔
2248
        } else {
2249
            auto tid_iter = sbc.sbc_tids.insert_tid(
4,977✔
2250
                sbc.sbc_allocator, string_fragment{}, log_us);
×
2251
            tid_iter->second.titr_level_stats.update_msg_count(level);
4,977✔
2252
        }
2253

2254
        if (orig_lock != curr_fmt) {
6,388✔
2255
            uint32_t lock_line;
2256

2257
            if (!this->lf_specialized && orig_lock != -1) {
681✔
2258
                log_debug("%s:%zu: changing pattern lock %d -> (%d)%s",
×
2259
                          lf.get_unique_path().c_str(),
2260
                          dst.size() - 1,
2261
                          orig_lock,
2262
                          curr_fmt,
2263
                          this->elf_pattern_order[curr_fmt]->p_name.c_str());
2264
            }
2265
            if (sbc.sbc_pattern_locks.empty()) {
681✔
2266
                lock_line = 0;
655✔
2267
            } else {
2268
                lock_line = dst.size() - 1;
26✔
2269
            }
2270
            sbc.sbc_pattern_locks.pl_lines.emplace_back(lock_line, curr_fmt);
681✔
2271
        }
2272
        return scan_match{1000};
6,388✔
2273
    }
2274

2275
    if (this->lf_specialized && !this->lf_multiline && !dst.empty()) {
747,873✔
2276
        const auto& last_line = dst.back();
1✔
2277

2278
        log_debug("%s: invalid line %zu file_offset=%" PRIu64,
1✔
2279
                  lf.get_filename().c_str(),
2280
                  dst.size(),
2281
                  li.li_file_range.fr_offset);
2282
        dst.emplace_back(li.li_file_range.fr_offset,
1✔
2283
                         last_line.get_time<std::chrono::microseconds>(),
×
2284
                         log_level_t::LEVEL_INVALID);
1✔
2285

2286
        return scan_match{0};
1✔
2287
    }
2288

2289
    return scan_no_match{"no patterns matched"};
747,872✔
2290
}
2291

2292
void
2293
external_log_format::annotate(logfile* lf,
7,747✔
2294
                              uint64_t line_number,
2295
                              string_attrs_t& sa,
2296
                              logline_value_vector& values) const
2297
{
2298
    thread_local auto md = lnav::pcre2pp::match_data::unitialized();
7,747✔
2299

2300
    auto& line = values.lvv_sbr;
7,747✔
2301
    line_range lr;
7,747✔
2302

2303
    line.erase_ansi();
7,747✔
2304
    if (this->elf_type != elf_type_t::ELF_TYPE_TEXT) {
7,747✔
2305
        if (this->jlf_cached_opts.full_message) {
842✔
2306
            values = this->jlf_line_values;
376✔
2307
            sa = this->jlf_attr_line.al_attrs;
376✔
2308
        } else {
2309
            values.lvv_sbr = this->jlf_line_values.lvv_sbr.clone();
466✔
2310
            for (const auto& llv : this->jlf_line_values.lvv_values) {
4,743✔
2311
                if (this->jlf_cached_sub_range.contains(llv.lv_origin)) {
4,277✔
2312
                    values.lvv_values.emplace_back(llv);
953✔
2313
                    values.lvv_values.back().lv_origin.shift(
953✔
2314
                        this->jlf_cached_sub_range.lr_start,
953✔
2315
                        -this->jlf_cached_sub_range.lr_start);
953✔
2316
                }
2317
            }
2318
            for (const auto& attr : this->jlf_attr_line.al_attrs) {
2,695✔
2319
                if (this->jlf_cached_sub_range.contains(attr.sa_range)) {
2,229✔
2320
                    sa.emplace_back(attr);
757✔
2321
                    sa.back().sa_range.shift(
757✔
2322
                        this->jlf_cached_sub_range.lr_start,
757✔
2323
                        -this->jlf_cached_sub_range.lr_start);
757✔
2324
                }
2325
            }
2326
            values.lvv_opid_value = this->jlf_line_values.lvv_opid_value;
466✔
2327
            values.lvv_opid_provenance
2328
                = this->jlf_line_values.lvv_opid_provenance;
466✔
2329
            values.lvv_thread_id_value
2330
                = to_owned(this->jlf_line_values.lvv_thread_id_value,
466✔
2331
                           values.lvv_allocator);
466✔
2332
            values.lvv_src_file_value = to_owned(
466✔
2333
                this->jlf_line_values.lvv_src_file_value, values.lvv_allocator);
466✔
2334
            values.lvv_src_line_value = to_owned(
466✔
2335
                this->jlf_line_values.lvv_src_line_value, values.lvv_allocator);
466✔
2336
        }
2337
        log_format::annotate(lf, line_number, sa, values);
842✔
2338
        return;
2,476✔
2339
    }
2340

2341
    if (line.empty()) {
6,905✔
2342
        return;
5✔
2343
    }
2344

2345
    values.lvv_values.reserve(this->elf_value_defs.size());
6,900✔
2346

2347
    auto lffs = lf->get_format_file_state();
6,900✔
2348
    int pat_index = lffs.lffs_pattern_locks.pattern_index_for_line(line_number);
6,900✔
2349
    const auto& pat = *this->elf_pattern_order[pat_index];
6,900✔
2350
    char tmp_opid_buf[hasher::STRING_SIZE];
2351

2352
    sa.reserve(pat.p_pcre.pp_value->get_capture_count());
6,900✔
2353
    auto match_res
2354
        = pat.p_pcre.pp_value->capture_from(line.to_string_fragment())
6,900✔
2355
              .into(md)
6,900✔
2356
              .matches(PCRE2_NO_UTF_CHECK)
13,800✔
2357
              .ignore_error();
6,900✔
2358
    if (!match_res) {
6,900✔
2359
        // A continued line still needs a body.
2360
        lr.lr_start = 0;
1,629✔
2361
        lr.lr_end = line.length();
1,629✔
2362
        sa.emplace_back(lr, SA_BODY.value());
1,629✔
2363
        if (!this->lf_multiline) {
1,629✔
2364
            auto len
2365
                = pat.p_pcre.pp_value->match_partial(line.to_string_fragment());
×
2366
            sa.emplace_back(
×
2367
                line_range{(int) len, -1},
×
2368
                SA_INVALID.value("Log line does not match any pattern"));
×
2369
        }
2370
        return;
1,629✔
2371
    }
2372

2373
    auto duration_cap = md[pat.p_duration_field_index];
5,271✔
2374

2375
    auto ts_cap = md[pat.p_timestamp_field_index];
5,271✔
2376
    if (ts_cap) {
5,271✔
2377
        sa.emplace_back(to_line_range(ts_cap.value()), L_TIMESTAMP.value());
5,271✔
2378
    }
2379

2380
    auto opid_cap = md[pat.p_opid_field_index];
5,271✔
2381

2382
    if (this->elf_opid_field.empty()
5,271✔
2383
        && !pat.p_opid_description_field_indexes.empty())
5,271✔
2384
    {
2385
        auto empty_desc = true;
3,805✔
2386
        hasher h;
3,805✔
2387
        for (const auto& fidx : pat.p_opid_description_field_indexes) {
11,415✔
2388
            auto desc_cap = md[fidx];
7,610✔
2389
            if (desc_cap) {
7,610✔
2390
                h.update(desc_cap.value());
7,582✔
2391
                empty_desc = false;
7,582✔
2392
            }
2393
        }
2394
        if (!empty_desc) {
3,805✔
2395
            h.to_string(tmp_opid_buf);
3,805✔
2396
            opid_cap = string_fragment::from_bytes(tmp_opid_buf,
7,610✔
2397
                                                   sizeof(tmp_opid_buf) - 1);
3,805✔
2398
        }
2399
    } else if (duration_cap && !opid_cap) {
1,466✔
2400
        hasher h;
3✔
2401
        h.update(line.to_string_fragment());
3✔
2402
        h.to_string(tmp_opid_buf);
3✔
2403
        opid_cap = string_fragment::from_bytes(tmp_opid_buf,
6✔
2404
                                               sizeof(tmp_opid_buf) - 1);
3✔
2405
    }
2406
    if (opid_cap && !opid_cap->empty()) {
5,271✔
2407
        sa.emplace_back(to_line_range(opid_cap.value()), L_OPID.value());
4,329✔
2408
        values.lvv_opid_value = opid_cap->to_string();
4,329✔
2409
        values.lvv_opid_provenance
2410
            = logline_value_vector::opid_provenance::file;
4,329✔
2411
    }
2412

2413
    auto body_cap = md[pat.p_body_field_index];
5,271✔
2414
    auto level_cap = md[pat.p_level_field_index];
5,271✔
2415
    auto src_file_cap = md[pat.p_src_file_field_index];
5,271✔
2416
    auto src_line_cap = md[pat.p_src_line_field_index];
5,271✔
2417
    auto thread_id_cap = md[pat.p_thread_id_field_index];
5,271✔
2418

2419
    if (level_cap
5,271✔
2420
        && (!body_cap
10,496✔
2421
            || (body_cap && level_cap->sf_begin != body_cap->sf_begin)))
10,496✔
2422
    {
2423
        sa.emplace_back(to_line_range(level_cap.value()), L_LEVEL.value());
4,796✔
2424
    }
2425

2426
    if (src_file_cap) {
5,271✔
2427
        sa.emplace_back(to_line_range(src_file_cap.value()),
224✔
2428
                        SA_SRC_FILE.value());
448✔
2429
        values.lvv_src_file_value
2430
            = src_file_cap->to_owned(values.lvv_allocator);
224✔
2431
    }
2432
    if (src_line_cap) {
5,271✔
2433
        sa.emplace_back(to_line_range(src_line_cap.value()),
224✔
2434
                        SA_SRC_LINE.value());
448✔
2435
        values.lvv_src_line_value
2436
            = src_line_cap->to_owned(values.lvv_allocator);
224✔
2437
    }
2438
    if (thread_id_cap) {
5,271✔
2439
        sa.emplace_back(to_line_range(thread_id_cap.value()),
810✔
2440
                        SA_THREAD_ID.value());
1,620✔
2441
        values.lvv_thread_id_value
2442
            = thread_id_cap->to_owned(values.lvv_allocator);
810✔
2443
    }
2444
    if (duration_cap) {
5,271✔
2445
        sa.emplace_back(to_line_range(duration_cap.value()),
3✔
2446
                        SA_DURATION.value());
6✔
2447
    }
2448

2449
    for (size_t lpc = 0; lpc < pat.p_value_by_index.size(); lpc++) {
59,923✔
2450
        const auto& ivd = pat.p_value_by_index[lpc];
54,652✔
2451
        const scaling_factor* scaling = nullptr;
54,652✔
2452
        auto cap = md[ivd.ivd_index];
54,652✔
2453
        const auto& vd = *ivd.ivd_value_def;
54,652✔
2454

2455
        if (ivd.ivd_unit_field_index >= 0) {
54,652✔
2456
            auto unit_cap = md[ivd.ivd_unit_field_index];
10✔
2457

2458
            if (unit_cap) {
10✔
2459
                intern_string_t unit_val
2460
                    = intern_string::lookup(unit_cap.value());
10✔
2461
                auto unit_iter = vd.vd_unit_scaling.find(unit_val);
10✔
2462
                if (unit_iter != vd.vd_unit_scaling.end()) {
10✔
2463
                    const auto& sf = unit_iter->second;
10✔
2464

2465
                    scaling = &sf;
10✔
2466
                }
2467
            }
2468
        }
2469

2470
        if (cap) {
54,652✔
2471
            if (vd.vd_meta.lvm_kind == value_kind_t::VALUE_TIMESTAMP) {
44,539✔
2472
                auto dts = this->build_time_scanner();
7✔
2473
                exttm tm;
7✔
2474
                timeval tv;
2475
                auto val_sf = cap.value();
7✔
2476

2477
                if (dts.scan(val_sf.data(),
14✔
2478
                             val_sf.length(),
7✔
2479
                             this->get_timestamp_formats(),
2480
                             &tm,
2481
                             tv,
2482
                             true))
2483
                {
2484
                    char ts[64];
2485
                    tm.et_gmtoff = tm.et_orig_gmtoff;
7✔
2486
                    auto len = dts.ftime(
7✔
2487
                        ts, sizeof(ts), this->get_timestamp_formats(), tm);
2488
                    ts[len] = '\0';
7✔
2489
                    values.lvv_values.emplace_back(vd.vd_meta,
7✔
2490
                                                   std::string{ts, len});
21✔
2491
                    values.lvv_values.back().lv_origin
7✔
2492
                        = to_line_range(cap.value());
14✔
2493
                } else {
2494
                    values.lvv_values.emplace_back(
×
2495
                        vd.vd_meta, line, to_line_range(cap.value()));
×
2496
                }
2497
            } else {
2498
                values.lvv_values.emplace_back(
89,064✔
2499
                    vd.vd_meta, line, to_line_range(cap.value()));
44,532✔
2500
                values.lvv_values.back().apply_scaling(scaling);
44,532✔
2501
            }
2502
        } else {
2503
            values.lvv_values.emplace_back(vd.vd_meta);
10,113✔
2504
        }
2505
    }
2506

2507
    if (body_cap && body_cap->is_valid()) {
5,271✔
2508
        lr = to_line_range(body_cap.value());
5,259✔
2509
    } else {
2510
        lr.lr_start = line.length();
12✔
2511
        lr.lr_end = line.length();
12✔
2512
    }
2513
    sa.emplace_back(lr, SA_BODY.value());
5,271✔
2514

2515
    log_format::annotate(lf, line_number, sa, values);
5,271✔
2516
}
2517

2518
void
2519
external_log_format::rewrite(exec_context& ec,
43✔
2520
                             shared_buffer_ref& line,
2521
                             string_attrs_t& sa,
2522
                             std::string& value_out)
2523
{
2524
    auto pg = ec.with_provenance(exec_context::format_rewrite{});
43✔
2525
    auto& values = *ec.ec_line_values;
43✔
2526

2527
    value_out.assign(line.get_data(), line.length());
43✔
2528

2529
    for (auto iter = values.lvv_values.begin(); iter != values.lvv_values.end();
251✔
2530
         ++iter)
208✔
2531
    {
2532
        if (!iter->lv_origin.is_valid()) {
208✔
2533
            log_debug("%d: not rewriting value with invalid origin -- %s",
22✔
2534
                      (int) ec.ec_top_line,
2535
                      iter->lv_meta.lvm_name.get());
2536
            continue;
178✔
2537
        }
2538

2539
        auto vd_iter = this->elf_value_defs.find(iter->lv_meta.lvm_name);
186✔
2540
        if (vd_iter == this->elf_value_defs.end()) {
186✔
2541
            log_debug("%d: not rewriting undefined value -- %s",
×
2542
                      (int) ec.ec_top_line,
2543
                      iter->lv_meta.lvm_name.get());
2544
            continue;
×
2545
        }
2546

2547
        const auto& vd = *vd_iter->second;
186✔
2548

2549
        if (vd.vd_rewriter.empty()) {
186✔
2550
            continue;
156✔
2551
        }
2552

2553
        auto _sg = ec.enter_source(
2554
            vd_iter->second->vd_rewrite_src_name, 1, vd.vd_rewriter);
30✔
2555
        std::string field_value;
30✔
2556

2557
        auto_mem<FILE> tmpout(fclose);
30✔
2558

2559
        tmpout = std::tmpfile();
30✔
2560
        if (!tmpout) {
30✔
2561
            log_error("unable to create temporary file");
×
2562
            return;
×
2563
        }
2564
        fcntl(fileno(tmpout), F_SETFD, FD_CLOEXEC);
30✔
2565
        auto fd_copy = auto_fd::dup_of(fileno(tmpout));
30✔
2566
        fd_copy.close_on_exec();
30✔
2567
        auto ec_out = std::make_pair(tmpout.release(), fclose);
30✔
2568
        {
2569
            exec_context::output_guard og(ec, "tmp", ec_out);
60✔
2570

2571
            auto exec_res = execute_any(ec, vd.vd_rewriter);
30✔
2572
            if (exec_res.isOk()) {
30✔
2573
                field_value = exec_res.unwrap();
30✔
2574
            } else {
2575
                field_value = exec_res.unwrapErr().to_attr_line().get_string();
×
2576
            }
2577
        }
30✔
2578
        struct stat st;
2579
        fstat(fd_copy.get(), &st);
30✔
2580
        if (st.st_size > 0) {
30✔
2581
            auto buf = auto_buffer::alloc(st.st_size);
2✔
2582

2583
            buf.resize(st.st_size);
2✔
2584
            pread(fd_copy.get(), buf.in(), st.st_size, 0);
2✔
2585
            field_value = buf.to_string();
2✔
2586
        }
2✔
2587
        value_out.erase(iter->lv_origin.lr_start, iter->lv_origin.length());
30✔
2588

2589
        int32_t shift_amount
2590
            = ((int32_t) field_value.length()) - iter->lv_origin.length();
30✔
2591
        auto orig_lr = iter->lv_origin;
30✔
2592
        value_out.insert(iter->lv_origin.lr_start, field_value);
30✔
2593
        for (auto shift_iter = values.lvv_values.begin();
30✔
2594
             shift_iter != values.lvv_values.end();
170✔
2595
             ++shift_iter)
140✔
2596
        {
2597
            shift_iter->lv_origin.shift_range(orig_lr, shift_amount);
140✔
2598
        }
2599
        shift_string_attrs(sa, orig_lr, shift_amount);
30✔
2600
    }
30✔
2601
}
43✔
2602

2603
static int
2604
read_json_field(yajlpp_parse_context* ypc,
240,495✔
2605
                const unsigned char* str,
2606
                size_t len,
2607
                yajl_string_props_t* props)
2608
{
2609
    auto* jlu = (json_log_userdata*) ypc->ypc_userdata;
240,495✔
2610
    timeval tv_out;
2611
    const auto frag = string_fragment::from_bytes(str, len);
240,495✔
2612
    intern_string_t field_name;
240,495✔
2613
    const auto* vd = jlu->get_field_def(ypc);
240,495✔
2614

2615
    if (vd != nullptr) {
240,495✔
2616
        field_name = vd->vd_meta.lvm_name;
21,572✔
2617
    }
2618

2619
    if (field_name.empty()) {
240,495✔
2620
        if (!jlu->jlu_format->elf_opid_field.empty()) {
218,923✔
2621
            auto path_sf = ypc->get_path_as_string_fragment();
71,920✔
2622
            if (path_sf.startswith(jlu->jlu_format->elf_opid_field.c_str())) {
71,920✔
2623
                jlu->jlu_opid_hasher.update(path_sf);
9,825✔
2624
                jlu->jlu_opid_hasher.update(frag);
9,825✔
2625
            }
2626
        }
2627
    } else if (jlu->jlu_format->lf_timestamp_field == field_name) {
21,572✔
2628
        const auto* last = jlu->jlu_format->lf_date_time.scan(
4,262✔
2629
            (const char*) str,
2630
            len,
2631
            jlu->jlu_format->get_timestamp_formats(),
4,262✔
2632
            &jlu->jlu_exttm,
2633
            tv_out);
2634
        if (last == nullptr) {
4,262✔
2635
            auto ls = jlu->jlu_format->lf_date_time.unlock();
41✔
2636
            if ((last = jlu->jlu_format->lf_date_time.scan(
41✔
2637
                     (const char*) str,
2638
                     len,
2639
                     jlu->jlu_format->get_timestamp_formats(),
41✔
2640
                     &jlu->jlu_exttm,
2641
                     tv_out))
2642
                == nullptr)
41✔
2643
            {
2644
                jlu->jlu_format->lf_date_time.relock(ls);
19✔
2645
            }
2646
            if (last != nullptr) {
41✔
2647
                auto old_flags
22✔
2648
                    = jlu->jlu_format->lf_timestamp_flags & DATE_TIME_SET_FLAGS;
22✔
2649
                auto new_flags = jlu->jlu_exttm.et_flags & DATE_TIME_SET_FLAGS;
22✔
2650

2651
                // It is unlikely a valid timestamp would lose much
2652
                // precision.
2653
                if (new_flags != old_flags) {
22✔
2654
                    last = nullptr;
×
2655
                }
2656
            }
2657
        }
2658
        if (last != nullptr) {
4,262✔
2659
            jlu->jlu_format->lf_timestamp_flags = jlu->jlu_exttm.et_flags;
4,243✔
2660
            jlu->jlu_base_line->set_time(tv_out);
4,243✔
2661
        } else {
2662
            jlu->jlu_scan_error = log_format::scan_error{fmt::format(
19✔
2663
                "failed to parse timestamp '{}' in string property '{}'",
2664
                frag,
2665
                field_name)};
19✔
2666
        }
2667
    } else if (jlu->jlu_format->elf_level_pointer.pp_value != nullptr) {
17,310✔
2668
        if (jlu->jlu_format->elf_level_pointer.pp_value
230✔
2669
                ->find_in(field_name.to_string_fragment(), PCRE2_NO_UTF_CHECK)
230✔
2670
                .ignore_error()
230✔
2671
                .has_value())
115✔
2672
        {
2673
            jlu->jlu_base_line->set_level(
×
2674
                jlu->jlu_format->convert_level(frag, jlu->jlu_batch_context));
×
2675
        }
2676
    }
2677
    if (!field_name.empty() && jlu->jlu_format->elf_level_field == field_name) {
240,495✔
2678
        jlu->jlu_base_line->set_level(
4,417✔
2679
            jlu->jlu_format->convert_level(frag, jlu->jlu_batch_context));
4,417✔
2680
    }
2681
    if (!field_name.empty() && jlu->jlu_format->elf_opid_field == field_name) {
240,495✔
2682
        jlu->jlu_base_line->merge_bloom_bits(frag.bloom_bits());
1,033✔
2683

2684
        auto& sbc = *jlu->jlu_batch_context;
1,033✔
2685
        auto opid_iter = sbc.sbc_opids.los_opid_ranges.find(frag);
1,033✔
2686
        if (opid_iter == sbc.sbc_opids.los_opid_ranges.end()) {
1,033✔
2687
            jlu->jlu_opid_frag = frag.to_owned(sbc.sbc_allocator);
1,003✔
2688
        } else {
2689
            jlu->jlu_opid_frag = opid_iter->first;
30✔
2690
        }
2691
    }
2692
    if (!field_name.empty()
240,495✔
2693
        && jlu->jlu_format->elf_thread_id_field == field_name)
240,495✔
2694
    {
2695
        auto& sbc = *jlu->jlu_batch_context;
×
2696
        auto tid_iter = sbc.sbc_tids.ltis_tid_ranges.find(frag);
×
2697
        if (tid_iter == sbc.sbc_tids.ltis_tid_ranges.end()) {
×
2698
            jlu->jlu_tid_frag = frag.to_owned(sbc.sbc_allocator);
×
2699
        } else {
2700
            jlu->jlu_tid_frag = tid_iter->first;
×
2701
        }
2702
    }
2703
    if (!jlu->jlu_format->elf_subid_field.empty()
240,495✔
2704
        && jlu->jlu_format->elf_subid_field == field_name)
240,495✔
2705
    {
2706
        jlu->jlu_subid = frag.to_string();
×
2707
    }
2708
    if (!field_name.empty()
240,495✔
2709
        && jlu->jlu_format->elf_duration_field == field_name)
240,495✔
2710
    {
2711
        auto from_res = humanize::try_from<double>(frag);
×
2712
        if (from_res) {
×
2713
            jlu->jlu_duration = std::chrono::microseconds(
×
2714
                static_cast<int64_t>(from_res.value() * 1000000));
2715
        }
2716
    }
2717

2718
    if (vd != nullptr && vd->vd_is_desc_field) {
240,495✔
2719
        auto frag_copy = frag.to_owned(jlu->jlu_format->lf_desc_allocator);
42✔
2720

2721
        jlu->jlu_format->lf_desc_captures.emplace(field_name, frag_copy);
42✔
2722
    }
2723

2724
    jlu->add_sub_lines_for(vd, ypc->is_level(1), std::nullopt, str, len, props);
240,495✔
2725

2726
    return 1;
240,495✔
2727
}
2728

2729
static int
2730
rewrite_json_field(yajlpp_parse_context* ypc,
48,033✔
2731
                   const unsigned char* str,
2732
                   size_t len,
2733
                   yajl_string_props_t* props)
2734
{
2735
    static const intern_string_t body_name = intern_string::lookup("body", -1);
48,033✔
2736
    auto* jlu = (json_log_userdata*) ypc->ypc_userdata;
48,033✔
2737
    intern_string_t field_name;
48,033✔
2738
    const auto* vd = jlu->get_field_def(ypc);
48,033✔
2739
    auto frag = string_fragment::from_bytes(str, len);
48,033✔
2740

2741
    if (!ypc->is_level(1) && vd == nullptr) {
48,033✔
2742
        if (!jlu->jlu_format->elf_opid_field.empty()) {
37,352✔
2743
            auto path_sf = ypc->get_path_as_string_fragment();
36,642✔
2744
            if (path_sf.startswith(jlu->jlu_format->elf_opid_field.c_str())) {
36,642✔
2745
                jlu->jlu_opid_hasher.update(path_sf);
48✔
2746
                jlu->jlu_opid_hasher.update(frag);
48✔
2747
            }
2748
        }
2749
        return 1;
37,352✔
2750
    }
2751
    if (vd != nullptr) {
10,681✔
2752
        field_name = vd->vd_meta.lvm_name;
10,005✔
2753
    } else {
2754
        field_name = ypc->get_path();
676✔
2755
    }
2756

2757
    if (jlu->jlu_format->elf_opid_field == field_name) {
10,681✔
2758
        jlu->jlu_format->jlf_line_values.lvv_opid_value = frag.to_string();
646✔
2759
        jlu->jlu_format->jlf_line_values.lvv_opid_provenance
646✔
2760
            = logline_value_vector::opid_provenance::file;
646✔
2761
    }
2762
    if (jlu->jlu_format->elf_thread_id_field == field_name) {
10,681✔
2763
        jlu->jlu_format->jlf_line_values.lvv_thread_id_value
×
2764
            = frag.to_owned(jlu->jlu_format->jlf_line_values.lvv_allocator);
×
2765
    }
2766
    if (jlu->jlu_format->lf_timestamp_field == field_name) {
10,681✔
2767
        char time_buf[64];
2768

2769
        // TODO add a timeval kind to logline_value
2770
        if (jlu->jlu_line->is_time_skewed()
2,036✔
2771
            || jlu->jlu_line->get_msg_level() == LEVEL_INVALID
2,036✔
2772
            || (jlu->jlu_format->lf_timestamp_flags
4,072✔
2773
                & (ETF_MICROS_SET | ETF_NANOS_SET | ETF_ZONE_SET)))
1,954✔
2774
        {
2775
            timeval tv;
2776

2777
            const auto* last = jlu->jlu_format->lf_date_time.scan(
2,036✔
2778
                (const char*) str,
2779
                len,
2780
                jlu->jlu_format->get_timestamp_formats(),
2,036✔
2781
                &jlu->jlu_exttm,
2782
                tv);
2783
            if (last == nullptr) {
2,036✔
2784
                auto ls = jlu->jlu_format->lf_date_time.unlock();
124✔
2785
                if ((last = jlu->jlu_format->lf_date_time.scan(
124✔
2786
                         (const char*) str,
2787
                         len,
2788
                         jlu->jlu_format->get_timestamp_formats(),
124✔
2789
                         &jlu->jlu_exttm,
2790
                         tv))
2791
                    == nullptr)
124✔
2792
                {
2793
                    jlu->jlu_format->lf_date_time.relock(ls);
66✔
2794
                    jlu->jlu_scan_error = log_format::scan_error{
66✔
2795
                        fmt::format("failed to parse timestamp '{}' in string "
2796
                                    "property '{}'",
2797
                                    frag,
2798
                                    field_name)};
66✔
2799
                }
2800
            }
2801
            if (!jlu->jlu_subline_opts.hash_hack) {
2,036✔
2802
                if (jlu->jlu_exttm.et_flags & ETF_ZONE_SET
2,036✔
2803
                    && jlu->jlu_format->lf_date_time.dts_zoned_to_local)
1,970✔
2804
                {
2805
                    jlu->jlu_exttm.et_flags &= ~ETF_Z_IS_UTC;
1,970✔
2806
                }
2807
                jlu->jlu_exttm.et_gmtoff
2808
                    = jlu->jlu_format->lf_date_time.dts_local_offset_cache;
2,036✔
2809
            }
2810
            jlu->jlu_format->lf_date_time.ftime(
2,036✔
2811
                time_buf,
2812
                sizeof(time_buf),
2813
                jlu->jlu_format->get_timestamp_formats(),
2,036✔
2814
                jlu->jlu_exttm);
2,036✔
2815
        } else {
2816
            sql_strftime(
×
2817
                time_buf, sizeof(time_buf), jlu->jlu_line->get_timeval(), 'T');
×
2818
        }
2819
        jlu->jlu_format->jlf_line_values.lvv_values.emplace_back(
4,072✔
2820
            jlu->jlu_format->get_value_meta(field_name,
4,072✔
2821
                                            value_kind_t::VALUE_TEXT),
2822
            std::string{time_buf});
6,108✔
2823
    } else if (vd != nullptr
8,645✔
2824
               && vd->vd_meta.lvm_kind == value_kind_t::VALUE_TIMESTAMP)
7,969✔
2825
    {
2826
        auto dts = jlu->jlu_format->build_time_scanner();
223✔
2827
        exttm tm;
223✔
2828
        timeval tv;
2829

2830
        if (dts.scan((const char*) str,
223✔
2831
                     len,
2832
                     jlu->jlu_format->get_timestamp_formats(),
223✔
2833
                     &tm,
2834
                     tv,
2835
                     true))
2836
        {
2837
            char ts[64];
2838
            tm.et_gmtoff = tm.et_orig_gmtoff;
223✔
2839
            auto tslen = dts.ftime(
223✔
2840
                ts, sizeof(ts), jlu->jlu_format->get_timestamp_formats(), tm);
223✔
2841
            ts[tslen] = '\0';
223✔
2842
            jlu->jlu_format->jlf_line_values.lvv_values.emplace_back(
223✔
2843
                jlu->jlu_format->get_value_meta(
446✔
2844
                    ypc, vd, value_kind_t::VALUE_TIMESTAMP),
2845
                std::string{(const char*) ts, tslen});
892✔
2846
        } else {
2847
            jlu->jlu_format->jlf_line_values.lvv_values.emplace_back(
×
2848
                jlu->jlu_format->get_value_meta(
×
2849
                    ypc, vd, value_kind_t::VALUE_TEXT),
2850
                std::string{(const char*) str, len});
×
2851
        }
2852
    } else if (jlu->jlu_shared_buffer.contains((const char*) str)) {
8,645✔
2853
        auto str_offset = (int) ((const char*) str - jlu->jlu_line_value);
7,914✔
2854
        if (field_name == jlu->jlu_format->elf_body_field) {
7,914✔
2855
            jlu->jlu_format->jlf_line_values.lvv_values.emplace_back(
881✔
2856
                logline_value_meta(body_name,
1,762✔
2857
                                   value_kind_t::VALUE_TEXT,
2858
                                   logline_value_meta::internal_column{},
×
2859
                                   jlu->jlu_format),
881✔
2860
                string_fragment::from_byte_range(
1,762✔
2861
                    jlu->jlu_shared_buffer.get_data(),
881✔
2862
                    str_offset,
2863
                    str_offset + len));
881✔
2864
        }
2865

2866
        jlu->jlu_format->jlf_line_values.lvv_values.emplace_back(
7,914✔
2867
            jlu->jlu_format->get_value_meta(ypc, vd, value_kind_t::VALUE_TEXT),
15,828✔
2868
            string_fragment::from_byte_range(jlu->jlu_shared_buffer.get_data(),
15,828✔
2869
                                             str_offset,
2870
                                             str_offset + len));
7,914✔
2871
    } else {
2872
        if (field_name == jlu->jlu_format->elf_body_field) {
508✔
2873
            jlu->jlu_format->jlf_line_values.lvv_values.emplace_back(
403✔
2874
                logline_value_meta(body_name,
806✔
2875
                                   value_kind_t::VALUE_TEXT,
2876
                                   logline_value_meta::internal_column{},
×
2877
                                   jlu->jlu_format),
403✔
2878
                std::string{(const char*) str, len});
1,612✔
2879
        }
2880

2881
        jlu->jlu_format->jlf_line_values.lvv_values.emplace_back(
508✔
2882
            jlu->jlu_format->get_value_meta(ypc, vd, value_kind_t::VALUE_TEXT),
1,016✔
2883
            std::string{(const char*) str, len});
2,032✔
2884
    }
2885
    if (vd != nullptr && vd->vd_is_desc_field
10,005✔
2886
        && jlu->jlu_format->elf_opid_field.empty())
20,686✔
2887
    {
2888
        auto frag_copy = frag.to_owned(jlu->jlu_format->lf_desc_allocator);
56✔
2889

2890
        jlu->jlu_format->lf_desc_captures.emplace(field_name, frag_copy);
56✔
2891
    }
2892

2893
    return 1;
10,681✔
2894
}
2895

2896
void
2897
external_log_format::get_subline(const log_format_file_state& lffs,
24,029✔
2898
                                 const logline& ll,
2899
                                 shared_buffer_ref& sbr,
2900
                                 subline_options opts)
2901
{
2902
    if (this->elf_type == elf_type_t::ELF_TYPE_TEXT) {
24,029✔
2903
        return;
19,287✔
2904
    }
2905

2906
    if (this->jlf_cached_offset != ll.get_offset()
4,742✔
2907
        || this->jlf_cached_opts != opts)
4,742✔
2908
    {
2909
        auto& ypc = *(this->jlf_parse_context);
2,307✔
2910
        yajl_handle handle = this->jlf_yajl_handle.get();
2,307✔
2911
        json_log_userdata jlu(sbr, nullptr);
2,307✔
2912

2913
        jlu.jlu_subline_opts = opts;
2,307✔
2914

2915
        this->lf_desc_captures.clear();
2,307✔
2916
        this->lf_desc_allocator.reset();
2,307✔
2917
        this->jlf_share_manager.invalidate_refs();
2,307✔
2918
        this->jlf_attr_line.clear();
2,307✔
2919
        this->jlf_line_values.clear();
2,307✔
2920
        this->jlf_line_offsets.clear();
2,307✔
2921

2922
        auto line_frag = sbr.to_string_fragment();
2,307✔
2923

2924
        if (!line_frag.startswith("{")) {
2,307✔
2925
            this->jlf_attr_line.al_string.assign(line_frag.data(),
93✔
2926
                                                 line_frag.length());
93✔
2927
            this->jlf_line_values.clear();
93✔
2928
            sbr.share(this->jlf_share_manager,
186✔
2929
                      this->jlf_attr_line.al_string.data(),
93✔
2930
                      this->jlf_attr_line.al_string.size());
2931
            this->jlf_line_values.lvv_sbr = sbr.clone();
93✔
2932
            this->jlf_attr_line.al_attrs.emplace_back(
93✔
2933
                line_range{0, -1},
×
2934
                SA_INVALID.value(fmt::format(
186✔
2935
                    FMT_STRING("line at offset {} is not a JSON-line"),
186✔
2936
                    ll.get_offset())));
93✔
2937
            return;
93✔
2938
        }
2939

2940
        yajl_reset(handle);
2,214✔
2941
        ypc.set_static_handler(json_log_rewrite_handlers.jpc_children[0]);
2,214✔
2942
        ypc.ypc_userdata = &jlu;
2,214✔
2943
        ypc.ypc_ignore_unused = true;
2,214✔
2944
        ypc.ypc_alt_callbacks.yajl_start_array = json_array_start_const;
2,214✔
2945
        ypc.ypc_alt_callbacks.yajl_end_array = json_array_end;
2,214✔
2946
        ypc.ypc_alt_callbacks.yajl_start_map = json_array_start_const;
2,214✔
2947
        ypc.ypc_alt_callbacks.yajl_end_map = json_array_end;
2,214✔
2948
        jlu.jlu_format = this;
2,214✔
2949
        jlu.jlu_line = &ll;
2,214✔
2950
        jlu.jlu_handle = handle;
2,214✔
2951
        jlu.jlu_line_value = sbr.get_data();
2,214✔
2952
        jlu.jlu_format_hits.resize(this->jlf_line_format.size());
2,214✔
2953

2954
        yajl_status parse_status = yajl_parse(
4,428✔
2955
            handle, (const unsigned char*) sbr.get_data(), sbr.length());
2,214✔
2956
        if (parse_status != yajl_status_ok
2,214✔
2957
            || yajl_complete_parse(handle) != yajl_status_ok
2,214✔
2958
            || jlu.jlu_scan_error)
4,428✔
2959
        {
2960
            unsigned char* msg;
2961
            std::string full_msg;
80✔
2962

2963
            msg = yajl_get_error(
160✔
2964
                handle, 1, (const unsigned char*) sbr.get_data(), sbr.length());
80✔
2965
            if (msg != nullptr) {
80✔
2966
                full_msg = fmt::format(
80✔
2967
                    FMT_STRING("[offset: {}] {}\n{}"),
160✔
2968
                    ll.get_offset(),
80✔
2969
                    fmt::string_view{sbr.get_data(), sbr.length()},
80✔
2970
                    reinterpret_cast<char*>(msg));
160✔
2971
                yajl_free_error(handle, msg);
80✔
2972
            }
2973

2974
            this->jlf_attr_line.al_string.assign(full_msg.data(),
80✔
2975
                                                 full_msg.size());
2976
            this->jlf_line_values.clear();
80✔
2977
            this->jlf_attr_line.al_attrs.emplace_back(
80✔
2978
                line_range{0, -1},
×
2979
                SA_INVALID.value(jlu.jlu_scan_error
240✔
2980
                                     ? jlu.jlu_scan_error->se_message
174✔
2981
                                     : "JSON line failed to parse"));
2982
        } else {
80✔
2983
            std::vector<logline_value>::iterator lv_iter;
2,134✔
2984
            bool used_values[this->jlf_line_values.lvv_values.size()];
4,268✔
2985
            struct line_range lr;
2,134✔
2986

2987
            memset(used_values, 0, sizeof(used_values));
2,134✔
2988
            for (lv_iter = this->jlf_line_values.lvv_values.begin();
2,134✔
2989
                 lv_iter != this->jlf_line_values.lvv_values.end();
18,160✔
2990
                 ++lv_iter)
16,026✔
2991
            {
2992
                lv_iter->lv_meta.lvm_format = this;
16,026✔
2993
            }
2994

2995
            if (jlu.jlu_tid_number) {
2,134✔
2996
                char tid_buf[128];
2997
                auto to_n_res = fmt::format_to_n(tid_buf,
156✔
2998
                                                 sizeof(tid_buf) - 1,
2999
                                                 FMT_STRING("{}"),
468✔
3000
                                                 jlu.jlu_tid_number.value());
3001
                *to_n_res.out = '\0';
156✔
3002
                this->jlf_line_values.lvv_thread_id_value
3003
                    = string_fragment::from_c_str(tid_buf).to_owned(
468✔
3004
                        this->jlf_line_values.lvv_allocator);
312✔
3005
            } else if (jlu.jlu_tid_frag) {
1,978✔
3006
                this->jlf_line_values.lvv_thread_id_value
3007
                    = jlu.jlu_tid_frag->to_owned(
×
3008
                        this->jlf_line_values.lvv_allocator);
×
3009
            }
3010

3011
            if (this->elf_opid_field.empty()
2,134✔
3012
                && this->lf_opid_source.value_or(
1,449✔
3013
                       opid_source_t::from_description)
1,449✔
3014
                    == opid_source_t::from_description
3015
                && this->lf_opid_description_def->size() == 1)
3,583✔
3016
            {
3017
                auto found_opid_desc = false;
253✔
3018
                const auto& od = this->lf_opid_description_def->begin()->second;
253✔
3019
                for (const auto& desc : *od.od_descriptors) {
759✔
3020
                    auto desc_iter
3021
                        = this->lf_desc_captures.find(desc.od_field.pp_value);
506✔
3022
                    if (desc_iter == this->lf_desc_captures.end()) {
506✔
3023
                        continue;
450✔
3024
                    }
3025
                    found_opid_desc = true;
56✔
3026
                    jlu.jlu_opid_hasher.update(desc_iter->second);
56✔
3027
                }
3028
                if (found_opid_desc) {
253✔
3029
                    this->jlf_line_values.lvv_opid_value
3030
                        = jlu.jlu_opid_hasher.to_string();
28✔
3031
                    this->jlf_line_values.lvv_opid_provenance
3032
                        = logline_value_vector::opid_provenance::file;
28✔
3033
                }
3034
            } else if (!jlu.jlu_opid_desc_frag && !jlu.jlu_opid_frag
3,733✔
3035
                       && jlu.jlu_duration > 0us)
3,733✔
3036
            {
3037
                jlu.jlu_opid_hasher.update(line_frag);
×
3038
                this->jlf_line_values.lvv_opid_value
3039
                    = jlu.jlu_opid_hasher.to_string();
×
3040
                this->jlf_line_values.lvv_opid_provenance
3041
                    = logline_value_vector::opid_provenance::file;
×
3042
            }
3043
            if (jlu.jlu_opid_desc_frag) {
2,134✔
3044
                this->jlf_line_values.lvv_opid_value
3045
                    = jlu.jlu_opid_hasher.to_string();
29✔
3046
                this->jlf_line_values.lvv_opid_provenance
3047
                    = logline_value_vector::opid_provenance::file;
29✔
3048
            }
3049
            if (jlu.jlu_opid_frag) {
2,134✔
3050
                this->jlf_line_values.lvv_opid_value
3051
                    = jlu.jlu_opid_frag->to_string();
×
3052
                this->jlf_line_values.lvv_opid_provenance
3053
                    = logline_value_vector::opid_provenance::file;
×
3054
            }
3055

3056
            int sub_offset = this->jlf_line_format_init_count;
2,134✔
3057
            for (const auto& jfe : this->jlf_line_format) {
25,565✔
3058
                static const intern_string_t ts_field
3059
                    = intern_string::lookup("__timestamp__", -1);
23,431✔
3060
                static const intern_string_t level_field
3061
                    = intern_string::lookup("__level__");
23,533✔
3062
                size_t begin_size = this->jlf_attr_line.al_string.size();
23,431✔
3063

3064
                switch (jfe.jfe_type) {
23,431✔
3065
                    case json_log_field::CONSTANT:
3,979✔
3066
                        this->json_append_to_cache(
3,979✔
3067
                            jfe.jfe_default_value.c_str(),
3068
                            jfe.jfe_default_value.size());
3,979✔
3069
                        break;
3,979✔
3070
                    case json_log_field::VARIABLE:
19,452✔
3071
                        lv_iter = find_if(
19,452✔
3072
                            this->jlf_line_values.lvv_values.begin(),
3073
                            this->jlf_line_values.lvv_values.end(),
3074
                            logline_value_name_cmp(&jfe.jfe_value.pp_value));
3075
                        if (lv_iter != this->jlf_line_values.lvv_values.end()) {
19,452✔
3076
                            auto str = lv_iter->to_string();
7,495✔
3077
                            value_def* vd = nullptr;
7,495✔
3078

3079
                            if (lv_iter->lv_meta.lvm_values_index) {
7,495✔
3080
                                vd = this->elf_value_def_order
3081
                                         [lv_iter->lv_meta.lvm_values_index
7,495✔
3082
                                              .value()]
7,495✔
3083
                                             .get();
7,495✔
3084
                            }
3085
                            while (endswith(str, "\n")) {
7,777✔
3086
                                str.pop_back();
282✔
3087
                            }
3088
                            size_t nl_pos = str.find('\n');
7,495✔
3089

3090
                            if (!jfe.jfe_prefix.empty()) {
7,495✔
3091
                                this->json_append_to_cache(jfe.jfe_prefix);
3,611✔
3092
                            }
3093
                            lr.lr_start = this->jlf_attr_line.al_string.size();
7,495✔
3094

3095
                            if ((int) str.size() > jfe.jfe_max_width) {
7,495✔
3096
                                switch (jfe.jfe_overflow) {
232✔
3097
                                    case json_format_element::overflow_t::
173✔
3098
                                        ABBREV: {
3099
                                        size_t new_size
3100
                                            = abbreviate_str(&str[0],
173✔
3101
                                                             str.size(),
3102
                                                             jfe.jfe_max_width);
173✔
3103
                                        str.resize(new_size);
173✔
3104
                                        this->json_append(lffs, jfe, vd, str);
173✔
3105
                                        break;
173✔
3106
                                    }
3107
                                    case json_format_element::overflow_t::
53✔
3108
                                        TRUNCATE: {
3109
                                        this->json_append_to_cache(
53✔
3110
                                            str.c_str(), jfe.jfe_max_width);
53✔
3111
                                        break;
53✔
3112
                                    }
3113
                                    case json_format_element::overflow_t::
6✔
3114
                                        DOTDOT: {
3115
                                        size_t middle
6✔
3116
                                            = (jfe.jfe_max_width / 2) - 1;
6✔
3117
                                        this->json_append_to_cache(str.c_str(),
6✔
3118
                                                                   middle);
3119
                                        this->json_append_to_cache("..", 2);
6✔
3120
                                        size_t rest
6✔
3121
                                            = (jfe.jfe_max_width - middle - 2);
6✔
3122
                                        this->json_append_to_cache(
12✔
3123
                                            str.c_str() + str.size() - rest,
6✔
3124
                                            rest);
3125
                                        break;
6✔
3126
                                    }
3127
                                    case json_format_element::overflow_t::
×
3128
                                        LASTWORD: {
3129
                                        size_t new_size
3130
                                            = last_word_str(&str[0],
×
3131
                                                            str.size(),
3132
                                                            jfe.jfe_max_width);
×
3133
                                        str.resize(new_size);
×
3134
                                        this->json_append(lffs, jfe, vd, str);
×
3135
                                        break;
×
3136
                                    }
3137
                                }
3138
                            } else {
3139
                                sub_offset
3140
                                    += std::count(str.begin(), str.end(), '\n');
7,263✔
3141
                                if (vd != nullptr
7,263✔
3142
                                    && vd->vd_meta.lvm_kind
7,263✔
3143
                                        == value_kind_t::VALUE_JSON)
3144
                                {
3145
                                    auto json_al = attr_line_t();
24✔
3146
                                    json_al.append(str);
24✔
3147
                                    highlight_syntax(text_format_t::TF_JSON,
24✔
3148
                                                     json_al,
3149
                                                     std::nullopt);
3150
                                    this->jlf_attr_line.append(json_al);
24✔
3151
                                } else {
24✔
3152
                                    this->json_append(lffs, jfe, vd, str);
7,239✔
3153
                                }
3154
                            }
3155

3156
                            if (nl_pos == std::string::npos
7,495✔
3157
                                || opts.full_message)
3✔
3158
                            {
3159
                                lr.lr_end
3160
                                    = this->jlf_attr_line.al_string.size();
7,492✔
3161
                            } else {
3162
                                lr.lr_end = lr.lr_start + nl_pos;
3✔
3163
                            }
3164

3165
                            if (lv_iter->lv_meta.lvm_name
7,495✔
3166
                                == this->lf_timestamp_field)
7,495✔
3167
                            {
3168
                                this->jlf_attr_line.al_attrs.emplace_back(
1,043✔
3169
                                    lr, L_TIMESTAMP.value());
2,086✔
3170
                            } else if (lv_iter->lv_meta.lvm_name
6,452✔
3171
                                       == this->elf_body_field)
6,452✔
3172
                            {
3173
                                this->jlf_attr_line.al_attrs.emplace_back(
1,223✔
3174
                                    lr, SA_BODY.value());
2,446✔
3175
                            } else if (lv_iter->lv_meta.lvm_name
5,229✔
3176
                                       == this->elf_src_loc_field)
5,229✔
3177
                            {
3178
                                size_t digits = 0;
×
3179
                                for (auto str_iter = str.rbegin();
×
3180
                                     str_iter != str.rend();
×
3181
                                     ++str_iter)
×
3182
                                {
3183
                                    if (isdigit(*str_iter)) {
×
3184
                                        digits += 1;
×
3185
                                    } else {
3186
                                        break;
×
3187
                                    }
3188
                                }
3189
                                auto diff = str.size() - digits;
×
3190
                                auto file_lr = lr;
×
3191
                                file_lr.lr_end -= digits;
×
3192
                                auto line_lr = lr;
×
3193
                                line_lr.lr_start += diff;
×
3194
                                if (digits > 0) {
×
3195
                                    file_lr.lr_end -= 1;
×
3196
                                }
3197
                                this->jlf_attr_line.al_attrs.emplace_back(
×
3198
                                    lr, SA_SRC_LOC.value());
×
3199
                                this->jlf_attr_line.al_attrs.emplace_back(
×
3200
                                    file_lr, SA_SRC_FILE.value());
×
3201
                                this->jlf_line_values.lvv_src_file_value
3202
                                    = this->jlf_attr_line
3203
                                          .to_string_fragment(
×
3204
                                              this->jlf_attr_line.al_attrs
3205
                                                  .back())
×
3206
                                          .to_owned(this->jlf_line_values
×
3207
                                                        .lvv_allocator);
×
3208
                                this->jlf_attr_line.al_attrs.emplace_back(
×
3209
                                    line_lr, SA_SRC_LINE.value());
×
3210
                                this->jlf_line_values.lvv_src_line_value
3211
                                    = this->jlf_attr_line
3212
                                          .to_string_fragment(
×
3213
                                              this->jlf_attr_line.al_attrs
3214
                                                  .back())
×
3215
                                          .to_owned(this->jlf_line_values
×
3216
                                                        .lvv_allocator);
×
3217
                            } else if (lv_iter->lv_meta.lvm_name
5,229✔
3218
                                       == this->elf_src_file_field)
5,229✔
3219
                            {
3220
                                this->jlf_attr_line.al_attrs.emplace_back(
14✔
3221
                                    lr, SA_SRC_FILE.value());
28✔
3222
                                this->jlf_line_values.lvv_src_file_value
3223
                                    = this->jlf_attr_line
3224
                                          .to_string_fragment(
14✔
3225
                                              this->jlf_attr_line.al_attrs
3226
                                                  .back())
14✔
3227
                                          .to_owned(this->jlf_line_values
28✔
3228
                                                        .lvv_allocator);
28✔
3229
                            } else if (lv_iter->lv_meta.lvm_name
5,215✔
3230
                                       == this->elf_src_line_field)
5,215✔
3231
                            {
3232
                                this->jlf_attr_line.al_attrs.emplace_back(
14✔
3233
                                    lr, SA_SRC_LINE.value());
28✔
3234
                                this->jlf_line_values.lvv_src_line_value
3235
                                    = this->jlf_attr_line
3236
                                          .to_string_fragment(
14✔
3237
                                              this->jlf_attr_line.al_attrs
3238
                                                  .back())
14✔
3239
                                          .to_owned(this->jlf_line_values
28✔
3240
                                                        .lvv_allocator);
28✔
3241
                            } else if (lv_iter->lv_meta.lvm_name
5,201✔
3242
                                       == this->elf_thread_id_field)
5,201✔
3243
                            {
3244
                                this->jlf_attr_line.al_attrs.emplace_back(
105✔
3245
                                    lr, SA_THREAD_ID.value());
210✔
3246
                            } else if (lv_iter->lv_meta.lvm_name
5,096✔
3247
                                       == this->elf_duration_field)
5,096✔
3248
                            {
3249
                                this->jlf_attr_line.al_attrs.emplace_back(
×
3250
                                    lr, SA_DURATION.value());
×
3251
                            } else if (lv_iter->lv_meta.lvm_name
5,096✔
3252
                                       == this->elf_level_field)
5,096✔
3253
                            {
3254
                                this->jlf_attr_line.al_attrs.emplace_back(
1,113✔
3255
                                    lr, L_LEVEL.value());
2,226✔
3256
                            } else if (lv_iter->lv_meta.lvm_name
7,966✔
3257
                                           == this->elf_opid_field
3,983✔
3258
                                       && !lr.empty())
3,983✔
3259
                            {
3260
                                this->jlf_attr_line.al_attrs.emplace_back(
646✔
3261
                                    lr, L_OPID.value());
1,292✔
3262
                            }
3263
                            lv_iter->lv_origin = lr;
7,495✔
3264
                            lv_iter->lv_sub_offset = sub_offset;
7,495✔
3265
                            used_values[std::distance(
7,495✔
3266
                                this->jlf_line_values.lvv_values.begin(),
3267
                                lv_iter)] = true;
7,495✔
3268

3269
                            if (!jfe.jfe_suffix.empty()) {
7,495✔
3270
                                this->json_append_to_cache(jfe.jfe_suffix);
1,018✔
3271
                            }
3272
                        } else if (jfe.jfe_value.pp_value == ts_field) {
19,452✔
3273
                            line_range lr;
1,234✔
3274
                            ssize_t ts_len;
3275
                            char ts[64];
3276
                            exttm et;
1,234✔
3277

3278
                            ll.to_exttm(et);
1,234✔
3279
                            et.et_nsec += jlu.jlu_exttm.et_nsec % 1000;
1,234✔
3280
                            et.et_gmtoff = jlu.jlu_exttm.et_gmtoff;
1,234✔
3281
                            et.et_flags |= jlu.jlu_exttm.et_flags;
1,234✔
3282
                            if (!jfe.jfe_prefix.empty()) {
1,234✔
3283
                                this->json_append_to_cache(jfe.jfe_prefix);
4✔
3284
                            }
3285
                            if (jfe.jfe_ts_format.empty()) {
1,234✔
3286
                                ts_len = this->lf_date_time.ftime(
1,079✔
3287
                                    ts,
3288
                                    sizeof(ts),
3289
                                    this->get_timestamp_formats(),
3290
                                    et);
3291
                            } else {
3292
                                ts_len = ftime_fmt(ts,
155✔
3293
                                                   sizeof(ts),
3294
                                                   jfe.jfe_ts_format.c_str(),
3295
                                                   et);
3296
                            }
3297
                            lr.lr_start = this->jlf_attr_line.al_string.size();
1,234✔
3298
                            this->json_append_to_cache(ts, ts_len);
1,234✔
3299
                            lr.lr_end = this->jlf_attr_line.al_string.size();
1,234✔
3300
                            this->jlf_attr_line.al_attrs.emplace_back(
1,234✔
3301
                                lr, L_TIMESTAMP.value());
2,468✔
3302

3303
                            lv_iter = find_if(
1,234✔
3304
                                this->jlf_line_values.lvv_values.begin(),
3305
                                this->jlf_line_values.lvv_values.end(),
3306
                                logline_value_name_cmp(
3307
                                    &this->lf_timestamp_field));
1,234✔
3308
                            if (lv_iter
1,234✔
3309
                                != this->jlf_line_values.lvv_values.end())
1,234✔
3310
                            {
3311
                                used_values[distance(
1,234✔
3312
                                    this->jlf_line_values.lvv_values.begin(),
3313
                                    lv_iter)] = true;
1,234✔
3314
                            }
3315
                            if (!jfe.jfe_suffix.empty()) {
1,234✔
3316
                                this->json_append_to_cache(jfe.jfe_suffix);
4✔
3317
                            }
3318
                        } else if (jfe.jfe_value.pp_value == level_field
10,723✔
3319
                                   || jfe.jfe_value.pp_value
21,310✔
3320
                                       == this->elf_level_field)
10,587✔
3321
                        {
3322
                            auto level_name = ll.get_level_name();
136✔
3323
                            lr.lr_start = this->jlf_attr_line.al_string.size();
136✔
3324
                            this->json_append(lffs, jfe, nullptr, level_name);
136✔
3325
                            if (jfe.jfe_auto_width) {
136✔
3326
                                this->json_append_to_cache(
105✔
3327
                                    MAX_LEVEL_NAME_LEN - level_name.length());
105✔
3328
                            }
3329
                            lr.lr_end = this->jlf_attr_line.al_string.size();
136✔
3330
                            this->jlf_attr_line.al_attrs.emplace_back(
136✔
3331
                                lr, L_LEVEL.value());
272✔
3332
                        } else if (!jfe.jfe_default_value.empty()) {
10,587✔
3333
                            if (!jfe.jfe_prefix.empty()) {
122✔
3334
                                this->json_append_to_cache(jfe.jfe_prefix);
×
3335
                            }
3336
                            this->json_append(
122✔
3337
                                lffs, jfe, nullptr, jfe.jfe_default_value);
122✔
3338
                            if (!jfe.jfe_suffix.empty()) {
122✔
3339
                                this->json_append_to_cache(jfe.jfe_suffix);
×
3340
                            }
3341
                        }
3342

3343
                        switch (jfe.jfe_text_transform) {
19,452✔
3344
                            case json_format_element::transform_t::NONE:
19,316✔
3345
                                break;
19,316✔
3346
                            case json_format_element::transform_t::UPPERCASE:
136✔
3347
                                for (size_t cindex = begin_size; cindex
136✔
3348
                                     < this->jlf_attr_line.al_string.size();
1,104✔
3349
                                     cindex++)
3350
                                {
3351
                                    this->jlf_attr_line.al_string[cindex]
1,936✔
3352
                                        = toupper(this->jlf_attr_line
968✔
3353
                                                      .al_string[cindex]);
968✔
3354
                                }
3355
                                break;
136✔
3356
                            case json_format_element::transform_t::LOWERCASE:
×
3357
                                for (size_t cindex = begin_size; cindex
×
3358
                                     < this->jlf_attr_line.al_string.size();
×
3359
                                     cindex++)
3360
                                {
3361
                                    this->jlf_attr_line.al_string[cindex]
×
3362
                                        = tolower(this->jlf_attr_line
×
3363
                                                      .al_string[cindex]);
×
3364
                                }
3365
                                break;
×
3366
                            case json_format_element::transform_t::CAPITALIZE:
×
3367
                                for (size_t cindex = begin_size;
×
3368
                                     cindex < begin_size + 1;
×
3369
                                     cindex++)
3370
                                {
3371
                                    this->jlf_attr_line.al_string[cindex]
×
3372
                                        = toupper(this->jlf_attr_line
×
3373
                                                      .al_string[cindex]);
×
3374
                                }
3375
                                for (size_t cindex = begin_size + 1; cindex
×
3376
                                     < this->jlf_attr_line.al_string.size();
×
3377
                                     cindex++)
3378
                                {
3379
                                    this->jlf_attr_line.al_string[cindex]
×
3380
                                        = tolower(this->jlf_attr_line
×
3381
                                                      .al_string[cindex]);
×
3382
                                }
3383
                                break;
×
3384
                        }
3385
                        break;
19,452✔
3386
                }
3387
            }
3388
            this->json_append_to_cache("\n", 1);
2,134✔
3389
            sub_offset += 1;
2,134✔
3390

3391
            for (size_t lpc = 0; lpc < this->jlf_line_values.lvv_values.size();
18,160✔
3392
                 lpc++)
3393
            {
3394
                static const intern_string_t body_name
3395
                    = intern_string::lookup("body", -1);
16,026✔
3396
                auto& lv = this->jlf_line_values.lvv_values[lpc];
16,026✔
3397

3398
                if (lv.lv_meta.is_hidden() || used_values[lpc]
25,235✔
3399
                    || body_name == lv.lv_meta.lvm_name)
25,235✔
3400
                {
3401
                    continue;
14,550✔
3402
                }
3403

3404
                auto str = lv.to_string();
1,476✔
3405
                while (endswith(str, "\n")) {
1,490✔
3406
                    str.pop_back();
14✔
3407
                }
3408

3409
                lv.lv_sub_offset = sub_offset;
1,476✔
3410
                lv.lv_origin.lr_start = this->jlf_attr_line.al_string.size() + 2
1,476✔
3411
                    + lv.lv_meta.lvm_name.size() + 2;
1,476✔
3412
                auto frag = string_fragment::from_str(str);
1,476✔
3413
                while (true) {
3414
                    auto utf_scan_res = is_utf8(frag, '\n');
1,524✔
3415

3416
                    this->json_append_to_cache("  ", 2);
1,524✔
3417
                    this->json_append_to_cache(
1,524✔
3418
                        lv.lv_meta.lvm_name.to_string_fragment());
1,524✔
3419
                    this->json_append_to_cache(": ", 2);
1,524✔
3420
                    lr.lr_start = this->jlf_attr_line.al_string.size();
1,524✔
3421
                    this->json_append_to_cache(utf_scan_res.usr_valid_frag);
1,524✔
3422
                    lr.lr_end = this->jlf_attr_line.al_string.size();
1,524✔
3423
                    if (lv.lv_meta.lvm_name == this->elf_body_field) {
1,524✔
3424
                        this->jlf_attr_line.al_attrs.emplace_back(
×
3425
                            lr, SA_BODY.value());
×
3426
                    } else {
3427
                        this->jlf_attr_line.al_attrs.emplace_back(
1,524✔
3428
                            lr, SA_EXTRA_CONTENT.value());
3,048✔
3429
                    }
3430
                    this->json_append_to_cache("\n", 1);
1,524✔
3431
                    sub_offset += 1;
1,524✔
3432
                    if (utf_scan_res.usr_remaining) {
1,524✔
3433
                        frag = utf_scan_res.usr_remaining.value();
48✔
3434
                    } else {
3435
                        break;
1,476✔
3436
                    }
3437
                }
48✔
3438
                lv.lv_origin.lr_end = this->jlf_attr_line.al_string.size() - 1;
1,476✔
3439
                if (lv.lv_meta.lvm_name == this->elf_opid_field
1,476✔
3440
                    && !lv.lv_origin.empty())
1,476✔
3441
                {
3442
                    this->jlf_attr_line.al_attrs.emplace_back(lv.lv_origin,
×
3443
                                                              L_OPID.value());
×
3444
                }
3445
            }
1,476✔
3446
        }
2,134✔
3447

3448
        this->jlf_line_offsets.push_back(0);
2,214✔
3449
        for (size_t lpc = 0; lpc < this->jlf_attr_line.al_string.size(); lpc++)
266,248✔
3450
        {
3451
            if (this->jlf_attr_line.al_string[lpc] == '\n') {
264,034✔
3452
                this->jlf_line_offsets.push_back(lpc + 1);
4,757✔
3453
            }
3454
        }
3455
        this->jlf_line_offsets.push_back(this->jlf_attr_line.al_string.size());
2,214✔
3456
        this->jlf_cached_offset = ll.get_offset();
2,214✔
3457
        this->jlf_cached_opts = opts;
2,214✔
3458
    }
2,307✔
3459

3460
    off_t this_off = 0, next_off = 0;
4,649✔
3461

3462
    if (!this->jlf_line_offsets.empty()
4,649✔
3463
        && ll.get_sub_offset() < this->jlf_line_offsets.size())
4,649✔
3464
    {
3465
        require(ll.get_sub_offset() < this->jlf_line_offsets.size());
4,649✔
3466

3467
        this_off = this->jlf_line_offsets[ll.get_sub_offset()];
4,649✔
3468
        if ((ll.get_sub_offset() + 1) < (int) this->jlf_line_offsets.size()) {
4,649✔
3469
            next_off = this->jlf_line_offsets[ll.get_sub_offset() + 1];
4,649✔
3470
        } else {
3471
            next_off = this->jlf_attr_line.al_string.size();
×
3472
        }
3473
        if (next_off > 0 && this->jlf_attr_line.al_string[next_off - 1] == '\n'
4,649✔
3474
            && this_off != next_off)
9,298✔
3475
        {
3476
            next_off -= 1;
4,649✔
3477
        }
3478
    }
3479

3480
    if (opts.full_message) {
4,649✔
3481
        sbr.share(this->jlf_share_manager,
742✔
3482
                  this->jlf_attr_line.al_string.data(),
371✔
3483
                  this->jlf_attr_line.al_string.size());
3484
    } else {
3485
        sbr.share(this->jlf_share_manager,
8,556✔
3486
                  this->jlf_attr_line.al_string.data() + this_off,
4,278✔
3487
                  next_off - this_off);
4,278✔
3488
    }
3489
    sbr.get_metadata().m_valid_utf = ll.is_valid_utf();
4,649✔
3490
    sbr.get_metadata().m_has_ansi = ll.has_ansi();
4,649✔
3491
    this->jlf_cached_sub_range.lr_start = this_off;
4,649✔
3492
    this->jlf_cached_sub_range.lr_end = next_off;
4,649✔
3493
    this->jlf_line_values.lvv_sbr = sbr.clone();
4,649✔
3494
}
3495

3496
struct compiled_header_expr {
3497
    auto_mem<sqlite3_stmt> che_stmt{sqlite3_finalize};
3498
    bool che_enabled{true};
3499
};
3500

3501
struct format_header_expressions : public lnav_config_listener {
3502
    format_header_expressions() : lnav_config_listener(__FILE__) {}
1,238✔
3503

3504
    auto_sqlite3 e_db;
3505
    std::map<intern_string_t, std::map<std::string, compiled_header_expr>>
3506
        e_header_exprs;
3507
};
3508

3509
using safe_format_header_expressions = safe::Safe<format_header_expressions>;
3510

3511
static safe_format_header_expressions format_header_exprs;
3512

3513
std::optional<external_file_format>
3514
detect_mime_type(const std::filesystem::path& filename)
679✔
3515
{
3516
    uint8_t buffer[1024];
3517
    size_t buffer_size = 0;
679✔
3518

3519
    {
3520
        auto_fd fd;
679✔
3521

3522
        if ((fd = lnav::filesystem::openp(filename, O_RDONLY)) == -1) {
679✔
3523
            return std::nullopt;
×
3524
        }
3525

3526
        ssize_t rc;
3527

3528
        if ((rc = read(fd, buffer, sizeof(buffer))) == -1) {
679✔
3529
            return std::nullopt;
×
3530
        }
3531
        buffer_size = rc;
679✔
3532
    }
679✔
3533

3534
    auto hexbuf = auto_buffer::alloc(buffer_size * 2);
679✔
3535

3536
    for (size_t lpc = 0; lpc < buffer_size; lpc++) {
360,626✔
3537
        fmt::format_to(
359,947✔
3538
            std::back_inserter(hexbuf), FMT_STRING("{:02x}"), buffer[lpc]);
1,439,788✔
3539
    }
3540

3541
    safe::WriteAccess<safe_format_header_expressions> in(format_header_exprs);
679✔
3542

3543
    for (const auto& format : log_format::get_root_formats()) {
53,574✔
3544
        auto elf = std::dynamic_pointer_cast<external_log_format>(format);
52,895✔
3545
        if (elf == nullptr) {
52,895✔
3546
            continue;
3,395✔
3547
        }
3548

3549
        if (elf->elf_converter.c_header.h_exprs.he_exprs.empty()) {
49,500✔
3550
            continue;
48,142✔
3551
        }
3552

3553
        if (buffer_size < elf->elf_converter.c_header.h_size) {
1,358✔
3554
            log_debug(
110✔
3555
                "%s: file content too small (%zu) for header detection: %s",
3556
                filename.c_str(),
3557
                buffer_size,
3558
                elf->get_name().get());
3559
            continue;
110✔
3560
        }
3561
        for (const auto& hpair : elf->elf_converter.c_header.h_exprs.he_exprs) {
4,467✔
3562
            auto& he = in->e_header_exprs[elf->get_name()][hpair.first];
3,219✔
3563

3564
            if (!he.che_enabled) {
3,219✔
3565
                continue;
×
3566
            }
3567

3568
            auto* stmt = he.che_stmt.in();
3,219✔
3569

3570
            if (stmt == nullptr) {
3,219✔
3571
                continue;
×
3572
            }
3573
            sqlite3_reset(stmt);
3,219✔
3574
            auto count = sqlite3_bind_parameter_count(stmt);
3,219✔
3575
            for (int lpc = 0; lpc < count; lpc++) {
6,438✔
3576
                const auto* name = sqlite3_bind_parameter_name(stmt, lpc + 1);
3,219✔
3577

3578
                if (name[0] == '$') {
3,219✔
3579
                    const char* env_value;
3580

3581
                    if ((env_value = getenv(&name[1])) != nullptr) {
×
3582
                        sqlite3_bind_text(
×
3583
                            stmt, lpc + 1, env_value, -1, SQLITE_STATIC);
3584
                    }
3585
                    continue;
×
3586
                }
3587
                if (strcmp(name, ":header") == 0) {
3,219✔
3588
                    sqlite3_bind_text(stmt,
3,219✔
3589
                                      lpc + 1,
3590
                                      hexbuf.in(),
3,219✔
3591
                                      hexbuf.size(),
3,219✔
3592
                                      SQLITE_STATIC);
3593
                    continue;
3,219✔
3594
                }
3595
                if (strcmp(name, ":filepath") == 0) {
×
3596
                    sqlite3_bind_text(
×
3597
                        stmt, lpc + 1, filename.c_str(), -1, SQLITE_STATIC);
3598
                    continue;
×
3599
                }
3600
            }
3601

3602
            auto step_res = sqlite3_step(stmt);
3,219✔
3603

3604
            switch (step_res) {
3,219✔
3605
                case SQLITE_OK:
3,219✔
3606
                case SQLITE_DONE:
3607
                    continue;
3,219✔
3608
                case SQLITE_ROW:
×
3609
                    break;
×
3610
                default: {
×
3611
                    log_error(
×
3612
                        "failed to execute file-format header expression: "
3613
                        "%s:%s -- %s",
3614
                        elf->get_name().get(),
3615
                        hpair.first.c_str(),
3616
                        sqlite3_errmsg(in->e_db));
3617
                    he.che_enabled = false;
×
3618
                    continue;
×
3619
                }
3620
            }
3621

3622
            log_info("detected format for: %s -- %s (header-expr: %s)",
×
3623
                     filename.c_str(),
3624
                     elf->get_name().get(),
3625
                     hpair.first.c_str());
3626
            return external_file_format{
×
3627
                elf->get_name().to_string(),
×
3628
                elf->elf_converter.c_command.pp_value,
×
3629
                elf->elf_converter.c_command.pp_location.sl_source.to_string(),
×
3630
            };
3631
        }
3632
    }
52,895✔
3633

3634
    return std::nullopt;
679✔
3635
}
679✔
3636

3637
log_format::scan_result_t
3638
log_format::test_line(sample_t& sample,
×
3639
                      std::vector<lnav::console::user_message>& msgs)
3640
{
3641
    return scan_no_match{};
×
3642
}
3643

3644
log_format::scan_result_t
3645
external_log_format::test_line(sample_t& sample,
208,450✔
3646
                               std::vector<lnav::console::user_message>& msgs)
3647
{
3648
    auto lines
3649
        = string_fragment::from_str(sample.s_line.pp_value).split_lines();
208,450✔
3650

3651
    if (this->elf_type == elf_type_t::ELF_TYPE_JSON) {
208,450✔
3652
        auto alloc = ArenaAlloc::Alloc<char>{};
3,270✔
3653
        pattern_locks pats;
3,270✔
3654
        auto sbc = scan_batch_context{
3,270✔
3655
            alloc,
3656
            pats,
3657
        };
3,270✔
3658
        sbc.sbc_value_stats.resize(this->elf_value_defs.size());
3,270✔
3659
        std::vector<logline> dst;
3,270✔
3660
        auto li = line_info{
3,270✔
3661
            {0, lines[0].length()},
3,270✔
3662
        };
3,270✔
3663
        shared_buffer sb;
3,270✔
3664
        shared_buffer_ref sbr;
3,270✔
3665
        sbr.share(sb, lines[0].data(), (size_t) lines[0].length());
3,270✔
3666

3667
        return this->scan_json(dst, li, sbr, sbc);
3,270✔
3668
    }
3,270✔
3669

3670
    scan_result_t retval = scan_no_match{"no patterns matched"};
205,180✔
3671
    auto found = false;
205,180✔
3672

3673
    for (auto pat_iter = this->elf_pattern_order.begin();
205,180✔
3674
         pat_iter != this->elf_pattern_order.end();
1,517,617✔
3675
         ++pat_iter)
1,312,437✔
3676
    {
3677
        auto& pat = *(*pat_iter);
1,312,437✔
3678

3679
        if (!pat.p_pcre.pp_value) {
1,312,437✔
3680
            continue;
1,107,259✔
3681
        }
3682

3683
        auto md = pat.p_pcre.pp_value->create_match_data();
1,312,437✔
3684
        auto match_res = pat.p_pcre.pp_value->capture_from(lines[0])
1,312,437✔
3685
                             .into(md)
1,312,437✔
3686
                             .matches(PCRE2_NO_UTF_CHECK)
2,624,874✔
3687
                             .ignore_error();
1,312,437✔
3688
        if (!match_res) {
1,312,437✔
3689
            continue;
1,107,259✔
3690
        }
3691
        retval = scan_match{1000};
205,178✔
3692
        found = true;
205,178✔
3693

3694
        sample.s_matched_regexes.insert(pat.p_name.to_string());
205,178✔
3695

3696
        const auto ts_cap = md[pat.p_timestamp_field_index];
205,178✔
3697
        const auto level_cap = md[pat.p_level_field_index];
205,178✔
3698
        const char* const* custom_formats = this->get_timestamp_formats();
205,178✔
3699
        date_time_scanner dts;
205,178✔
3700
        timeval tv;
3701
        exttm tm;
205,178✔
3702

3703
        if (ts_cap && ts_cap->sf_begin == 0) {
205,178✔
3704
            pat.p_timestamp_end = ts_cap->sf_end;
127,967✔
3705
        }
3706
        const char* dts_scan_res = nullptr;
205,178✔
3707

3708
        if (ts_cap) {
205,178✔
3709
            dts_scan_res = dts.scan(
205,176✔
3710
                ts_cap->data(), ts_cap->length(), custom_formats, &tm, tv);
205,176✔
3711
        }
3712
        if (dts_scan_res != nullptr) {
205,178✔
3713
            if (dts_scan_res != ts_cap->data() + ts_cap->length()) {
205,175✔
3714
                auto match_len = dts_scan_res - ts_cap->data();
×
3715
                auto notes = attr_line_t("the used timestamp format: ");
×
3716
                if (custom_formats == nullptr) {
×
3717
                    notes.append(PTIMEC_FORMATS[dts.dts_fmt_lock].pf_fmt);
×
3718
                } else {
3719
                    notes.append(custom_formats[dts.dts_fmt_lock]);
×
3720
                }
3721
                notes.append("\n  ")
×
3722
                    .append(ts_cap.value())
×
3723
                    .append("\n")
×
3724
                    .append(2 + match_len, ' ')
×
3725
                    .append("^ matched up to here"_snippet_border);
×
3726
                auto um = lnav::console::user_message::warning(
×
3727
                              attr_line_t("timestamp was not fully matched: ")
×
3728
                                  .append_quoted(ts_cap.value()))
×
3729
                              .with_snippet(sample.s_line.to_snippet())
×
3730
                              .with_note(notes)
×
3731
                              .move();
×
3732

3733
                msgs.emplace_back(um);
×
3734
            }
3735
        } else if (!ts_cap) {
3✔
3736
            msgs.emplace_back(
2✔
3737
                lnav::console::user_message::error(
×
3738
                    attr_line_t("invalid sample log message: ")
4✔
3739
                        .append(lnav::to_json(sample.s_line.pp_value)))
4✔
3740
                    .with_reason(attr_line_t("timestamp was not captured"))
4✔
3741
                    .with_snippet(sample.s_line.to_snippet())
4✔
3742
                    .with_help(attr_line_t(
4✔
3743
                        "A timestamp needs to be captured in order for a "
3744
                        "line to be recognized as a log message")));
3745
        } else {
3746
            attr_line_t notes;
1✔
3747

3748
            if (custom_formats == nullptr) {
1✔
3749
                notes.append("the following built-in formats were tried:");
×
3750
                for (int lpc = 0; PTIMEC_FORMATS[lpc].pf_fmt != nullptr; lpc++)
×
3751
                {
3752
                    off_t off = 0;
×
3753

3754
                    PTIMEC_FORMATS[lpc].pf_func(
×
3755
                        &tm, ts_cap->data(), off, ts_cap->length());
×
3756
                    notes.append("\n  ")
×
3757
                        .append(ts_cap.value())
×
3758
                        .append("\n")
×
3759
                        .append(2 + off, ' ')
×
3760
                        .append("^ "_snippet_border)
×
3761
                        .append_quoted(
×
3762
                            lnav::roles::symbol(PTIMEC_FORMATS[lpc].pf_fmt))
×
3763
                        .append(" matched up to here"_snippet_border);
×
3764
                }
3765
            } else {
3766
                notes.append("the following custom formats were tried:");
1✔
3767
                for (int lpc = 0; custom_formats[lpc] != nullptr; lpc++) {
2✔
3768
                    off_t off = 0;
1✔
3769

3770
                    ptime_fmt(custom_formats[lpc],
1✔
3771
                              &tm,
3772
                              ts_cap->data(),
3773
                              off,
3774
                              ts_cap->length());
1✔
3775
                    notes.append("\n  ")
1✔
3776
                        .append(ts_cap.value())
1✔
3777
                        .append("\n")
1✔
3778
                        .append(2 + off, ' ')
1✔
3779
                        .append("^ "_snippet_border)
1✔
3780
                        .append_quoted(lnav::roles::symbol(custom_formats[lpc]))
2✔
3781
                        .append(" matched up to here"_snippet_border);
1✔
3782
                }
3783
            }
3784

3785
            msgs.emplace_back(
1✔
3786
                lnav::console::user_message::error(
×
3787
                    attr_line_t("invalid sample log message: ")
1✔
3788
                        .append(lnav::to_json(sample.s_line.pp_value)))
2✔
3789
                    .with_reason(attr_line_t("unrecognized timestamp -- ")
2✔
3790
                                     .append(ts_cap.value()))
1✔
3791
                    .with_snippet(sample.s_line.to_snippet())
2✔
3792
                    .with_note(notes)
1✔
3793
                    .with_help(attr_line_t("If the timestamp format is not "
2✔
3794
                                           "supported by default, you can "
3795
                                           "add a custom format with the ")
3796
                                   .append_quoted("timestamp-format"_symbol)
1✔
3797
                                   .append(" property")));
1✔
3798
        }
1✔
3799

3800
        auto level = this->convert_level(
205,178✔
3801
            level_cap.value_or(string_fragment::invalid()), nullptr);
205,178✔
3802

3803
        if (sample.s_level != LEVEL_UNKNOWN && sample.s_level != level) {
205,178✔
3804
            attr_line_t note_al;
1✔
3805

3806
            note_al.append("matched regex = ")
1✔
3807
                .append(lnav::roles::symbol(pat.p_name.to_string()))
2✔
3808
                .append("\n")
1✔
3809
                .append("captured level = ")
1✔
3810
                .append_quoted(level_cap->to_string());
1✔
3811
            if (level_cap && !this->elf_level_patterns.empty()) {
1✔
3812
                thread_local auto md = lnav::pcre2pp::match_data::unitialized();
1✔
3813

3814
                note_al.append("\nlevel regular expression match results:");
1✔
3815
                for (const auto& level_pattern : this->elf_level_patterns) {
3✔
3816
                    attr_line_t regex_al
3817
                        = level_pattern.second.lp_pcre.pp_value->get_pattern();
2✔
3818
                    lnav::snippets::regex_highlighter(
2✔
3819
                        regex_al, -1, line_range{0, (int) regex_al.length()});
2✔
3820
                    note_al.append("\n  ")
2✔
3821
                        .append(lnav::roles::symbol(
4✔
3822
                            level_pattern.second.lp_pcre.pp_path.to_string()))
4✔
3823
                        .append(" = ")
2✔
3824
                        .append(regex_al)
2✔
3825
                        .append("\n    ");
2✔
3826
                    auto match_res = level_pattern.second.lp_pcre.pp_value
2✔
3827
                                         ->capture_from(level_cap.value())
2✔
3828
                                         .into(md)
2✔
3829
                                         .matches(PCRE2_NO_UTF_CHECK)
4✔
3830
                                         .ignore_error();
2✔
3831
                    if (!match_res) {
2✔
3832
                        note_al.append(lnav::roles::warning("no match"));
1✔
3833
                        continue;
1✔
3834
                    }
3835

3836
                    note_al.append(level_cap.value())
1✔
3837
                        .append("\n    ")
1✔
3838
                        .append(md.leading().length(), ' ')
1✔
3839
                        .append("^"_snippet_border);
1✔
3840
                    if (match_res->f_all.length() > 2) {
1✔
3841
                        note_al.append(lnav::roles::snippet_border(
1✔
3842
                            std::string(match_res->f_all.length() - 2, '-')));
3✔
3843
                    }
3844
                    if (match_res->f_all.length() > 1) {
1✔
3845
                        note_al.append("^"_snippet_border);
1✔
3846
                    }
3847
                }
2✔
3848
            }
3849
            auto um
3850
                = lnav::console::user_message::error(
×
3851
                      attr_line_t("invalid sample log message: ")
1✔
3852
                          .append(lnav::to_json(sample.s_line.pp_value)))
2✔
3853
                      .with_reason(attr_line_t()
2✔
3854
                                       .append_quoted(lnav::roles::symbol(
2✔
3855
                                           level_names[level]))
1✔
3856
                                       .append(" does not match the expected "
1✔
3857
                                               "level of ")
3858
                                       .append_quoted(lnav::roles::symbol(
2✔
3859
                                           level_names[sample.s_level])))
1✔
3860
                      .with_snippet(sample.s_line.to_snippet())
2✔
3861
                      .with_note(note_al)
1✔
3862
                      .move();
1✔
3863
            if (!this->elf_level_patterns.empty()) {
1✔
3864
                um.with_help(
1✔
3865
                    attr_line_t("Level regexes are not anchored to the "
2✔
3866
                                "start/end of the string.  Prepend ")
3867
                        .append_quoted("^"_symbol)
1✔
3868
                        .append(" to the expression to match from the "
1✔
3869
                                "start of the string and append ")
3870
                        .append_quoted("$"_symbol)
1✔
3871
                        .append(" to match up to the end of the string."));
1✔
3872
            }
3873
            msgs.emplace_back(um);
1✔
3874
        }
1✔
3875

3876
        {
3877
            auto full_match_res
3878
                = pat.p_pcre.pp_value->capture_from(sample.s_line.pp_value)
205,178✔
3879
                      .into(md)
205,178✔
3880
                      .matches()
410,356✔
3881
                      .ignore_error();
205,178✔
3882
            if (!full_match_res) {
205,178✔
3883
                attr_line_t regex_al = pat.p_pcre.pp_value->get_pattern();
1✔
3884
                lnav::snippets::regex_highlighter(
1✔
3885
                    regex_al, -1, line_range{0, (int) regex_al.length()});
1✔
3886
                msgs.emplace_back(
1✔
3887
                    lnav::console::user_message::error(
×
3888
                        attr_line_t("invalid pattern: ")
1✔
3889
                            .append_quoted(
1✔
3890
                                lnav::roles::symbol(pat.p_name.to_string())))
2✔
3891
                        .with_reason("pattern does not match entire "
2✔
3892
                                     "multiline sample message")
3893
                        .with_snippet(sample.s_line.to_snippet())
2✔
3894
                        .with_note(attr_line_t()
2✔
3895
                                       .append(lnav::roles::symbol(
1✔
3896
                                           pat.p_name.to_string()))
2✔
3897
                                       .append(" = ")
1✔
3898
                                       .append(regex_al))
1✔
3899
                        .with_help(
3900
                            attr_line_t("use ").append_quoted(".*").append(
2✔
3901
                                " to match new-lines")));
3902
            } else if (static_cast<size_t>(full_match_res->f_all.length())
205,178✔
3903
                       != sample.s_line.pp_value.length())
205,177✔
3904
            {
3905
                attr_line_t regex_al = pat.p_pcre.pp_value->get_pattern();
1✔
3906
                lnav::snippets::regex_highlighter(
1✔
3907
                    regex_al, -1, line_range{0, (int) regex_al.length()});
1✔
3908
                auto match_length
3909
                    = static_cast<size_t>(full_match_res->f_all.length());
1✔
3910
                attr_line_t sample_al = sample.s_line.pp_value;
1✔
3911
                sample_al.append("\n")
1✔
3912
                    .append(match_length, ' ')
1✔
3913
                    .append("^ matched up to here"_error)
1✔
3914
                    .with_attr_for_all(VC_ROLE.value(role_t::VCR_QUOTED_CODE));
1✔
3915
                auto sample_snippet = lnav::console::snippet::from(
3916
                    sample.s_line.pp_location, sample_al);
1✔
3917
                msgs.emplace_back(
1✔
3918
                    lnav::console::user_message::error(
×
3919
                        attr_line_t("invalid pattern: ")
1✔
3920
                            .append_quoted(
1✔
3921
                                lnav::roles::symbol(pat.p_name.to_string())))
2✔
3922
                        .with_reason("pattern does not match entire "
2✔
3923
                                     "message")
3924
                        .with_snippet(sample_snippet)
1✔
3925
                        .with_note(attr_line_t()
3✔
3926
                                       .append(lnav::roles::symbol(
2✔
3927
                                           pat.p_name.to_string()))
2✔
3928
                                       .append(" = ")
1✔
3929
                                       .append(regex_al))
1✔
3930
                        .with_help("update the regular expression to fully "
3931
                                   "capture the sample message"));
3932
            }
1✔
3933
        }
3934
    }
1,312,437✔
3935

3936
    if (!found && !this->elf_pattern_order.empty()) {
205,180✔
3937
        std::vector<std::pair<ssize_t, intern_string_t>> partial_indexes;
2✔
3938
        attr_line_t notes;
2✔
3939
        size_t max_name_width = 0;
2✔
3940

3941
        for (const auto& pat_iter : this->elf_pattern_order) {
10✔
3942
            auto& pat = *pat_iter;
8✔
3943

3944
            if (!pat.p_pcre.pp_value) {
8✔
3945
                continue;
×
3946
            }
3947

3948
            partial_indexes.emplace_back(
8✔
3949
                pat.p_pcre.pp_value->match_partial(lines[0]), pat.p_name);
8✔
3950
            max_name_width = std::max(max_name_width, pat.p_name.size());
8✔
3951
        }
3952
        for (const auto& line_frag : lines) {
4✔
3953
            auto src_line = attr_line_t(line_frag.to_string());
2✔
3954
            if (!line_frag.endswith("\n")) {
2✔
3955
                src_line.append("\n");
2✔
3956
            }
3957
            src_line.with_attr_for_all(VC_ROLE.value(role_t::VCR_QUOTED_CODE));
2✔
3958
            notes.append("   ").append(src_line);
2✔
3959
            for (auto& part_pair : partial_indexes) {
10✔
3960
                if (part_pair.first >= 0
16✔
3961
                    && part_pair.first < line_frag.length())
8✔
3962
                {
3963
                    notes.append("   ")
8✔
3964
                        .append(part_pair.first, ' ')
8✔
3965
                        .append("^ "_snippet_border)
8✔
3966
                        .append(
8✔
3967
                            lnav::roles::symbol(part_pair.second.to_string()))
16✔
3968
                        .append(" matched up to here"_snippet_border)
8✔
3969
                        .append("\n");
8✔
3970
                }
3971
                part_pair.first -= line_frag.length();
8✔
3972
            }
3973
        }
2✔
3974
        notes.add_header(
2✔
3975
            "the following shows how each pattern matched this sample:\n");
3976

3977
        attr_line_t regex_note;
2✔
3978
        for (const auto& pat_iter : this->elf_pattern_order) {
10✔
3979
            if (!pat_iter->p_pcre.pp_value) {
8✔
3980
                regex_note
3981
                    .append(lnav::roles::symbol(fmt::format(
×
3982
                        FMT_STRING("{:{}}"), pat_iter->p_name, max_name_width)))
×
3983
                    .append(" is invalid");
×
3984
                continue;
×
3985
            }
3986

3987
            attr_line_t regex_al = pat_iter->p_pcre.pp_value->get_pattern();
8✔
3988
            lnav::snippets::regex_highlighter(
8✔
3989
                regex_al, -1, line_range{0, (int) regex_al.length()});
8✔
3990

3991
            regex_note
3992
                .append(lnav::roles::symbol(fmt::format(
16✔
3993
                    FMT_STRING("{:{}}"), pat_iter->p_name, max_name_width)))
24✔
3994
                .append(" = ")
8✔
3995
                .append_quoted(regex_al)
16✔
3996
                .append("\n");
8✔
3997
        }
8✔
3998

3999
        msgs.emplace_back(
2✔
4000
            lnav::console::user_message::error(
×
4001
                attr_line_t("invalid sample log message: ")
2✔
4002
                    .append(lnav::to_json(sample.s_line.pp_value)))
4✔
4003
                .with_reason("sample does not match any patterns")
4✔
4004
                .with_snippet(sample.s_line.to_snippet())
4✔
4005
                .with_note(notes.rtrim())
4✔
4006
                .with_note(regex_note));
4007
    }
2✔
4008

4009
    return retval;
205,180✔
4010
}
208,450✔
4011

4012
void
4013
external_log_format::build(std::vector<lnav::console::user_message>& errors)
60,058✔
4014
{
4015
    auto& vc = view_colors::singleton();
60,058✔
4016

4017
    if (!this->lf_timestamp_field.empty()) {
60,058✔
4018
        auto& vd = this->elf_value_defs[this->lf_timestamp_field];
60,058✔
4019
        if (vd.get() == nullptr) {
60,058✔
4020
            vd = std::make_shared<value_def>(
43,616✔
4021
                this->lf_timestamp_field,
43,616✔
4022
                value_kind_t::VALUE_TEXT,
×
4023
                logline_value_meta::internal_column{},
×
4024
                this);
43,616✔
4025
            if (this->elf_type == elf_type_t::ELF_TYPE_JSON) {
43,616✔
4026
                this->elf_value_def_order.emplace_back(vd);
7,865✔
4027
            }
4028
        }
4029
        vd->vd_meta.lvm_name = this->lf_timestamp_field;
60,058✔
4030
        vd->vd_meta.lvm_kind = value_kind_t::VALUE_TEXT;
60,058✔
4031
        vd->vd_meta.lvm_column = logline_value_meta::internal_column{};
60,058✔
4032
        vd->vd_internal = true;
60,058✔
4033

4034
        this->elf_value_defs[LOG_TIME_STR] = vd;
60,058✔
4035
    }
4036

4037
    if (!this->lf_subsecond_field.empty()) {
60,058✔
4038
        if (!this->lf_subsecond_unit.has_value()) {
103✔
4039
            errors.emplace_back(
1✔
4040
                lnav::console::user_message::error(
×
4041
                    attr_line_t()
2✔
4042
                        .append_quoted(
1✔
4043
                            lnav::roles::symbol(this->elf_name.to_string()))
2✔
4044
                        .append(" is not a valid log format"))
1✔
4045
                    .with_reason(attr_line_t()
2✔
4046
                                     .append_quoted("subsecond-units"_symbol)
1✔
4047
                                     .append(" must be set when ")
1✔
4048
                                     .append_quoted("subsecond-field"_symbol)
1✔
4049
                                     .append(" is used"))
1✔
4050
                    .with_snippets(this->get_snippets()));
2✔
4051
        } else {
4052
            auto& vd = this->elf_value_defs[this->lf_subsecond_field];
102✔
4053
            if (vd.get() == nullptr) {
102✔
4054
                vd = std::make_shared<value_def>(
102✔
4055
                    this->lf_subsecond_field,
102✔
4056
                    value_kind_t::VALUE_INTEGER,
×
4057
                    logline_value_meta::internal_column{},
×
4058
                    this);
102✔
4059
                if (this->elf_type == elf_type_t::ELF_TYPE_JSON) {
102✔
4060
                    this->elf_value_def_order.emplace_back(vd);
102✔
4061
                }
4062
            }
4063
            vd->vd_meta.lvm_name = this->lf_subsecond_field;
102✔
4064
            vd->vd_meta.lvm_column = logline_value_meta::internal_column{};
102✔
4065
            vd->vd_meta.lvm_hidden = true;
102✔
4066
            vd->vd_internal = true;
102✔
4067
        }
4068
    }
4069

4070
    if (startswith(this->elf_level_field.get(), "/")) {
60,058✔
4071
        this->elf_level_field
4072
            = intern_string::lookup(this->elf_level_field.get() + 1);
204✔
4073
    }
4074
    if (!this->elf_level_field.empty()) {
60,058✔
4075
        auto level_iter = this->elf_value_defs.find(this->elf_level_field);
60,058✔
4076
        if (level_iter == this->elf_value_defs.end()) {
60,058✔
4077
            auto& vd = this->elf_value_defs[this->elf_level_field];
27,990✔
4078
            vd = std::make_shared<value_def>(
27,990✔
4079
                this->elf_level_field,
27,990✔
4080
                value_kind_t::VALUE_TEXT,
×
4081
                logline_value_meta::internal_column{},
×
4082
                this);
27,990✔
4083
            if (this->elf_type == elf_type_t::ELF_TYPE_JSON) {
27,990✔
4084
                this->elf_value_def_order.emplace_back(vd);
2,861✔
4085
            }
4086
            vd->vd_meta.lvm_name = this->elf_level_field;
27,990✔
4087
            vd->vd_meta.lvm_column = logline_value_meta::internal_column{};
27,990✔
4088
            vd->vd_internal = true;
27,990✔
4089

4090
            if (this->elf_level_field != this->elf_body_field) {
27,990✔
4091
                this->elf_value_defs[LOG_LEVEL_STR] = vd;
27,071✔
4092
            }
4093
        } else {
4094
            if (level_iter->second->vd_meta.lvm_kind
32,068✔
4095
                != value_kind_t::VALUE_TEXT)
32,068✔
4096
            {
4097
                this->lf_level_hideable = false;
5,821✔
4098
            }
4099
            this->elf_value_defs[LOG_LEVEL_STR] = level_iter->second;
32,068✔
4100
        }
4101
    }
4102

4103
    auto opid_field_iter = this->elf_value_defs.find(LOG_OPID_STR);
60,058✔
4104
    if (opid_field_iter == this->elf_value_defs.end()) {
60,058✔
4105
        auto vd
4106
            = std::make_shared<value_def>(this->elf_opid_field,
60,058✔
4107
                                          value_kind_t::VALUE_TEXT,
×
4108
                                          logline_value_meta::internal_column{},
×
4109
                                          this);
60,058✔
4110
        if (this->elf_type == elf_type_t::ELF_TYPE_JSON) {
60,058✔
4111
            this->elf_value_def_order.emplace_back(vd);
12,052✔
4112
        }
4113
        vd->vd_meta.lvm_name = LOG_OPID_STR;
60,058✔
4114
        vd->vd_meta.lvm_kind = value_kind_t::VALUE_TEXT;
60,058✔
4115
        vd->vd_meta.lvm_column = logline_value_meta::internal_column{};
60,058✔
4116
        vd->vd_internal = true;
60,058✔
4117

4118
        this->elf_value_defs[LOG_OPID_STR] = vd;
60,058✔
4119
    }
60,058✔
4120

4121
    if (!this->elf_body_field.empty()) {
60,058✔
4122
        auto& vd = this->elf_value_defs[this->elf_body_field];
60,058✔
4123
        if (vd.get() == nullptr) {
60,058✔
4124
            vd = std::make_shared<value_def>(
47,598✔
4125
                this->elf_body_field,
47,598✔
4126
                value_kind_t::VALUE_TEXT,
×
4127
                logline_value_meta::internal_column{},
×
4128
                this);
47,598✔
4129
            if (this->elf_type == elf_type_t::ELF_TYPE_JSON) {
47,598✔
4130
                this->elf_value_def_order.emplace_back(vd);
7,047✔
4131
            }
4132
        }
4133
        vd->vd_meta.lvm_name = this->elf_body_field;
60,058✔
4134
        vd->vd_meta.lvm_kind = value_kind_t::VALUE_TEXT;
60,058✔
4135
        vd->vd_meta.lvm_column = logline_value_meta::internal_column{};
60,058✔
4136
        vd->vd_internal = true;
60,058✔
4137
    }
4138

4139
    if (!this->elf_src_file_field.empty()) {
60,058✔
4140
        auto& vd = this->elf_value_defs[this->elf_src_file_field];
8,171✔
4141
        if (vd.get() == nullptr) {
8,171✔
4142
            vd = std::make_shared<value_def>(
1✔
4143
                this->elf_src_file_field,
1✔
4144
                value_kind_t::VALUE_TEXT,
×
4145
                logline_value_meta::internal_column{},
×
4146
                this);
2✔
4147
        }
4148
        vd->vd_meta.lvm_name = this->elf_src_file_field;
8,171✔
4149
        vd->vd_meta.lvm_kind = value_kind_t::VALUE_TEXT;
8,171✔
4150
        vd->vd_meta.lvm_column = logline_value_meta::internal_column{};
8,171✔
4151
    }
4152

4153
    if (!this->elf_src_line_field.empty()) {
60,058✔
4154
        auto& vd = this->elf_value_defs[this->elf_src_line_field];
8,171✔
4155
        if (vd.get() == nullptr) {
8,171✔
4156
            vd = std::make_shared<value_def>(
1✔
4157
                this->elf_src_line_field,
1✔
4158
                value_kind_t::VALUE_INTEGER,
×
4159
                logline_value_meta::internal_column{},
×
4160
                this);
2✔
4161
        }
4162
        vd->vd_meta.lvm_name = this->elf_src_line_field;
8,171✔
4163
        vd->vd_meta.lvm_kind = value_kind_t::VALUE_INTEGER;
8,171✔
4164
        vd->vd_meta.lvm_column = logline_value_meta::internal_column{};
8,171✔
4165
    }
4166

4167
    if (!this->elf_thread_id_field.empty()) {
60,058✔
4168
        auto& vd = this->elf_value_defs[this->elf_thread_id_field];
18,792✔
4169
        if (vd.get() == nullptr) {
18,792✔
4170
            vd = std::make_shared<value_def>(
1✔
4171
                this->elf_thread_id_field,
1✔
4172
                value_kind_t::VALUE_TEXT,
×
4173
                logline_value_meta::internal_column{},
×
4174
                this);
2✔
4175
        }
4176
        vd->vd_meta.lvm_name = this->elf_thread_id_field;
18,792✔
4177
        vd->vd_meta.lvm_kind = value_kind_t::VALUE_TEXT;
18,792✔
4178
        vd->vd_meta.lvm_column = logline_value_meta::internal_column{};
18,792✔
4179
    }
4180

4181
    if (!this->elf_duration_field.empty()) {
60,058✔
4182
        auto& vd = this->elf_value_defs[this->elf_duration_field];
2,451✔
4183
        if (vd.get() == nullptr) {
2,451✔
4184
            vd = std::make_shared<value_def>(
×
4185
                this->elf_duration_field,
×
4186
                value_kind_t::VALUE_FLOAT,
×
4187
                logline_value_meta::internal_column{},
×
4188
                this);
×
4189
        }
4190
        vd->vd_meta.lvm_name = this->elf_duration_field;
2,451✔
4191
        vd->vd_meta.lvm_kind = value_kind_t::VALUE_FLOAT;
2,451✔
4192
        vd->vd_meta.lvm_column = logline_value_meta::internal_column{};
2,451✔
4193
    }
4194

4195
    for (auto& od_pair : *this->lf_opid_description_def) {
84,570✔
4196
        od_pair.second.od_name = od_pair.first;
24,512✔
4197
        od_pair.second.od_index = this->lf_opid_description_def_vec->size();
24,512✔
4198
        this->lf_opid_description_def_vec->emplace_back(&od_pair.second);
24,512✔
4199
    }
4200

4201
    for (auto& od_pair : *this->lf_subid_description_def) {
60,875✔
4202
        od_pair.second.od_name = od_pair.first;
817✔
4203
        od_pair.second.od_index = this->lf_subid_description_def_vec->size();
817✔
4204
        this->lf_subid_description_def_vec->emplace_back(&od_pair.second);
817✔
4205
    }
4206

4207
    if (!this->lf_timestamp_format.empty()) {
60,058✔
4208
        this->lf_timestamp_format.push_back(nullptr);
7,459✔
4209
    }
4210
    auto src_file_found = 0;
60,058✔
4211
    auto src_line_found = 0;
60,058✔
4212
    auto thread_id_found = 0;
60,058✔
4213
    auto duration_found = 0;
60,058✔
4214
    for (auto& elf_pattern : this->elf_patterns) {
169,445✔
4215
        auto& pat = *elf_pattern.second;
109,387✔
4216

4217
        if (pat.p_pcre.pp_value == nullptr) {
109,387✔
4218
            continue;
1✔
4219
        }
4220

4221
        if (pat.p_opid_field_index == -1
218,772✔
4222
            && this->lf_opid_source.value_or(opid_source_t::from_description)
109,386✔
4223
                == opid_source_t::from_description
4224
            && this->lf_opid_description_def->size() == 1)
218,772✔
4225
        {
4226
            const auto& opid_def
4227
                = this->lf_opid_description_def->begin()->second;
26,961✔
4228
            for (const auto& desc : *opid_def.od_descriptors) {
60,458✔
4229
                for (auto named_cap : pat.p_pcre.pp_value->get_named_captures())
390,526✔
4230
                {
4231
                    const intern_string_t name
4232
                        = intern_string::lookup(named_cap.get_name());
357,029✔
4233

4234
                    if (name == desc.od_field.pp_value) {
357,029✔
4235
                        pat.p_opid_description_field_indexes.emplace_back(
62,092✔
4236
                            named_cap.get_index());
31,046✔
4237
                    }
4238
                }
4239
            }
4240
        }
4241

4242
        for (auto named_cap : pat.p_pcre.pp_value->get_named_captures()) {
935,495✔
4243
            const intern_string_t name
4244
                = intern_string::lookup(named_cap.get_name());
826,109✔
4245

4246
            if (name == this->lf_timestamp_field) {
826,109✔
4247
                pat.p_timestamp_field_index = named_cap.get_index();
109,384✔
4248
            }
4249
            if (name == this->lf_time_field) {
826,109✔
4250
                pat.p_time_field_index = named_cap.get_index();
817✔
4251
            }
4252
            if (name == this->elf_level_field) {
826,109✔
4253
                pat.p_level_field_index = named_cap.get_index();
83,846✔
4254
            }
4255
            if (name == this->elf_opid_field) {
826,109✔
4256
                pat.p_opid_field_index = named_cap.get_index();
22,876✔
4257
            }
4258
            if (name == this->elf_subid_field) {
826,109✔
4259
                pat.p_subid_field_index = named_cap.get_index();
12,255✔
4260
            }
4261
            if (name == this->elf_body_field) {
826,109✔
4262
                pat.p_body_field_index = named_cap.get_index();
94,678✔
4263
            }
4264
            if (name == this->elf_src_file_field) {
826,109✔
4265
                pat.p_src_file_field_index = named_cap.get_index();
13,072✔
4266
                src_file_found += 1;
13,072✔
4267
            }
4268
            if (name == this->elf_src_line_field) {
826,109✔
4269
                pat.p_src_line_field_index = named_cap.get_index();
14,706✔
4270
                src_line_found += 1;
14,706✔
4271
            }
4272
            if (name == this->elf_thread_id_field) {
826,109✔
4273
                pat.p_thread_id_field_index = named_cap.get_index();
45,752✔
4274
                thread_id_found += 1;
45,752✔
4275
            }
4276
            if (name == this->elf_duration_field) {
826,109✔
4277
                pat.p_duration_field_index = named_cap.get_index();
4,902✔
4278
                duration_found += 1;
4,902✔
4279
            }
4280

4281
            auto value_iter = this->elf_value_defs.find(name);
826,109✔
4282
            if (value_iter != this->elf_value_defs.end()) {
826,109✔
4283
                auto vd = value_iter->second;
812,933✔
4284
                indexed_value_def ivd;
812,933✔
4285

4286
                ivd.ivd_index = named_cap.get_index();
812,933✔
4287
                if (!vd->vd_unit_field.empty()) {
812,933✔
4288
                    ivd.ivd_unit_field_index = pat.p_pcre.pp_value->name_index(
1,634✔
4289
                        vd->vd_unit_field.get());
817✔
4290
                } else {
4291
                    ivd.ivd_unit_field_index = -1;
812,116✔
4292
                }
4293
                if (!vd->vd_internal
812,933✔
4294
                    && !vd->vd_meta.lvm_column
1,395,251✔
4295
                            .is<logline_value_meta::table_column>())
582,318✔
4296
                {
4297
                    vd->vd_meta.lvm_column = logline_value_meta::table_column{
326,597✔
4298
                        this->elf_column_count++};
326,597✔
4299
                }
4300
                ivd.ivd_value_def = vd;
812,933✔
4301
                pat.p_value_by_index.push_back(ivd);
812,933✔
4302
            }
812,933✔
4303
            pat.p_value_name_to_index[name] = named_cap.get_index();
826,109✔
4304
        }
4305

4306
        stable_sort(pat.p_value_by_index.begin(), pat.p_value_by_index.end());
109,386✔
4307

4308
        for (int lpc = 0; lpc < (int) pat.p_value_by_index.size(); lpc++) {
922,319✔
4309
            auto& ivd = pat.p_value_by_index[lpc];
812,933✔
4310
            auto vd = ivd.ivd_value_def;
812,933✔
4311

4312
            if (!vd->vd_meta.lvm_foreign_key && !vd->vd_meta.lvm_identifier) {
812,933✔
4313
                switch (vd->vd_meta.lvm_kind) {
416,381✔
4314
                    case value_kind_t::VALUE_INTEGER:
56,475✔
4315
                    case value_kind_t::VALUE_FLOAT:
4316
                        pat.p_numeric_value_indexes.push_back(lpc);
56,475✔
4317
                        break;
56,475✔
4318
                    default:
359,906✔
4319
                        break;
359,906✔
4320
                }
4321
            }
4322
        }
812,933✔
4323

4324
        if (pat.p_timestamp_field_index == -1) {
109,386✔
4325
            errors.emplace_back(
2✔
4326
                lnav::console::user_message::error(
×
4327
                    attr_line_t("invalid pattern: ")
4✔
4328
                        .append_quoted(lnav::roles::symbol(pat.p_config_path)))
4✔
4329
                    .with_reason("no timestamp capture found in the pattern")
4✔
4330
                    .with_snippets(this->get_snippets())
4✔
4331
                    .with_help("all log messages need a timestamp"));
4332
        }
4333

4334
        if (!this->elf_level_field.empty() && pat.p_level_field_index == -1) {
109,386✔
4335
            log_warning("%s:level field '%s' not found in pattern",
25,540✔
4336
                        pat.p_config_path.c_str(),
4337
                        this->elf_level_field.get());
4338
        }
4339
        if (!this->elf_body_field.empty() && pat.p_body_field_index == -1) {
109,386✔
4340
            log_warning("%s:body field '%s' not found in pattern",
14,708✔
4341
                        pat.p_config_path.c_str(),
4342
                        this->elf_body_field.get());
4343
        }
4344

4345
        this->elf_pattern_order.push_back(elf_pattern.second);
109,386✔
4346
    }
4347
    if (this->elf_type == elf_type_t::ELF_TYPE_TEXT
120,116✔
4348
        && !this->elf_src_file_field.empty() && src_file_found == 0)
60,058✔
4349
    {
4350
        errors.emplace_back(
1✔
4351
            lnav::console::user_message::error(
×
4352
                attr_line_t("invalid pattern: ")
2✔
4353
                    .append_quoted(
1✔
4354
                        lnav::roles::symbol(this->elf_name.to_string())))
2✔
4355
                .with_reason("no source file capture found in the pattern")
2✔
4356
                .with_snippets(this->get_snippets())
2✔
4357
                .with_help(attr_line_t("at least one pattern needs a source "
2✔
4358
                                       "file capture named ")
4359
                               .append_quoted(this->elf_src_file_field.get())));
1✔
4360
    }
4361
    if (this->elf_type == elf_type_t::ELF_TYPE_TEXT
120,116✔
4362
        && !this->elf_src_line_field.empty() && src_line_found == 0)
60,058✔
4363
    {
4364
        errors.emplace_back(
1✔
4365
            lnav::console::user_message::error(
×
4366
                attr_line_t("invalid pattern: ")
2✔
4367
                    .append_quoted(
1✔
4368
                        lnav::roles::symbol(this->elf_name.to_string())))
2✔
4369
                .with_reason("no source line capture found in the pattern")
2✔
4370
                .with_snippets(this->get_snippets())
2✔
4371
                .with_help(attr_line_t("at least one pattern needs a source "
2✔
4372
                                       "line capture named ")
4373
                               .append_quoted(this->elf_src_line_field.get())));
1✔
4374
    }
4375
    if (this->elf_type == elf_type_t::ELF_TYPE_TEXT
120,116✔
4376
        && !this->elf_thread_id_field.empty() && thread_id_found == 0)
60,058✔
4377
    {
4378
        errors.emplace_back(
1✔
4379
            lnav::console::user_message::error(
×
4380
                attr_line_t("invalid pattern: ")
2✔
4381
                    .append_quoted(
1✔
4382
                        lnav::roles::symbol(this->elf_name.to_string())))
2✔
4383
                .with_reason("no thread ID capture found in the pattern")
2✔
4384
                .with_snippets(this->get_snippets())
2✔
4385
                .with_help(
4386
                    attr_line_t(
2✔
4387
                        "at least one pattern needs a thread ID capture named ")
4388
                        .append_quoted(this->elf_thread_id_field.get())));
1✔
4389
    }
4390
    if (this->elf_type == elf_type_t::ELF_TYPE_TEXT
120,116✔
4391
        && !this->elf_duration_field.empty() && duration_found == 0)
60,058✔
4392
    {
4393
        errors.emplace_back(
×
4394
            lnav::console::user_message::error(
×
4395
                attr_line_t("invalid pattern: ")
×
4396
                    .append_quoted(
×
4397
                        lnav::roles::symbol(this->elf_name.to_string())))
×
4398
                .with_reason("no duration capture found in the pattern")
×
4399
                .with_snippets(this->get_snippets())
×
4400
                .with_help(
4401
                    attr_line_t(
×
4402
                        "at least one pattern needs a duration capture named ")
4403
                        .append_quoted(this->elf_duration_field.get())));
×
4404
    }
4405

4406
    if (this->elf_type != elf_type_t::ELF_TYPE_TEXT) {
60,058✔
4407
        if (!this->elf_patterns.empty()) {
12,052✔
4408
            errors.emplace_back(
1✔
4409
                lnav::console::user_message::error(
×
4410
                    attr_line_t()
2✔
4411
                        .append_quoted(
1✔
4412
                            lnav::roles::symbol(this->elf_name.to_string()))
2✔
4413
                        .append(" is not a valid log format"))
1✔
4414
                    .with_reason("structured logs cannot have regexes")
2✔
4415
                    .with_snippets(this->get_snippets()));
2✔
4416
        }
4417
        if (this->elf_type == elf_type_t::ELF_TYPE_JSON) {
12,052✔
4418
            this->lf_multiline = true;
12,052✔
4419
            this->lf_structured = true;
12,052✔
4420
            this->lf_formatted_lines = true;
12,052✔
4421
            this->jlf_parse_context
4422
                = std::make_shared<yajlpp_parse_context>(this->elf_name);
12,052✔
4423
            this->jlf_yajl_handle.reset(
12,052✔
4424
                yajl_alloc(&this->jlf_parse_context->ypc_callbacks,
12,052✔
4425
                           nullptr,
4426
                           this->jlf_parse_context.get()),
12,052✔
4427
                yajl_handle_deleter());
4428
            yajl_config(
12,052✔
4429
                this->jlf_yajl_handle.get(), yajl_dont_validate_strings, 1);
4430
        }
4431
    } else {
4432
        if (this->elf_patterns.empty()) {
48,006✔
4433
            errors.emplace_back(lnav::console::user_message::error(
2✔
4434
                                    attr_line_t()
4✔
4435
                                        .append_quoted(lnav::roles::symbol(
4✔
4436
                                            this->elf_name.to_string()))
4✔
4437
                                        .append(" is not a valid log format"))
2✔
4438
                                    .with_reason("no regexes specified")
4✔
4439
                                    .with_snippets(this->get_snippets()));
4✔
4440
        }
4441
    }
4442

4443
    stable_sort(this->elf_level_pairs.begin(), this->elf_level_pairs.end());
60,058✔
4444

4445
    {
4446
        safe::WriteAccess<safe_format_header_expressions> hexprs(
4447
            format_header_exprs);
60,058✔
4448

4449
        if (hexprs->e_db.in() == nullptr) {
60,058✔
4450
            if (sqlite3_open(":memory:", hexprs->e_db.out()) != SQLITE_OK) {
817✔
4451
                log_error("unable to open memory DB");
×
4452
                return;
×
4453
            }
4454
            register_sqlite_funcs(hexprs->e_db.in(), sqlite_registration_funcs);
817✔
4455
        }
4456

4457
        for (const auto& hpair : this->elf_converter.c_header.h_exprs.he_exprs)
64,144✔
4458
        {
4459
            auto stmt_str
4460
                = fmt::format(FMT_STRING("SELECT 1 WHERE {}"), hpair.second);
12,258✔
4461
            compiled_header_expr che;
4,086✔
4462

4463
            log_info("preparing file-format header expression: %s",
4,086✔
4464
                     stmt_str.c_str());
4465
            auto retcode = sqlite3_prepare_v2(hexprs->e_db.in(),
8,172✔
4466
                                              stmt_str.c_str(),
4467
                                              stmt_str.size(),
4,086✔
4468
                                              che.che_stmt.out(),
4469
                                              nullptr);
4470
            if (retcode != SQLITE_OK) {
4,086✔
4471
                auto sql_al = attr_line_t(hpair.second)
2✔
4472
                                  .with_attr_for_all(SA_PREFORMATTED.value())
2✔
4473
                                  .with_attr_for_all(
1✔
4474
                                      VC_ROLE.value(role_t::VCR_QUOTED_CODE))
2✔
4475
                                  .move();
1✔
4476
                readline_sql_highlighter(
1✔
4477
                    sql_al, lnav::sql::dialect::sqlite, std::nullopt);
4478
                intern_string_t watch_expr_path = intern_string::lookup(
4479
                    fmt::format(FMT_STRING("/{}/converter/header/expr/{}"),
3✔
4480
                                this->elf_name,
1✔
4481
                                hpair.first));
2✔
4482
                auto snippet = lnav::console::snippet::from(
4483
                    source_location(watch_expr_path), sql_al);
1✔
4484

4485
                auto um = lnav::console::user_message::error(
2✔
4486
                              "SQL expression is invalid")
4487
                              .with_reason(sqlite3_errmsg(hexprs->e_db.in()))
2✔
4488
                              .with_snippet(snippet)
1✔
4489
                              .move();
1✔
4490

4491
                errors.emplace_back(um);
1✔
4492
                continue;
1✔
4493
            }
1✔
4494

4495
            hexprs->e_header_exprs[this->elf_name][hpair.first]
4,085✔
4496
                = std::move(che);
8,170✔
4497
        }
4,087✔
4498

4499
        if (!this->elf_converter.c_header.h_exprs.he_exprs.empty()
60,058✔
4500
            && this->elf_converter.c_command.pp_value.empty())
60,058✔
4501
        {
4502
            auto um = lnav::console::user_message::error(
2✔
4503
                          "A command is required when a converter is defined")
4504
                          .with_help(
2✔
4505
                              "The converter command transforms the file "
4506
                              "into a format that can be consumed by lnav")
4507
                          .with_snippets(this->get_snippets())
2✔
4508
                          .move();
1✔
4509
            errors.emplace_back(um);
1✔
4510
        }
1✔
4511
    }
60,058✔
4512

4513
    for (auto& vd : this->elf_value_def_order) {
601,327✔
4514
        std::vector<std::string>::iterator act_iter;
541,269✔
4515

4516
        if (!vd->vd_internal
541,269✔
4517
            && log_vtab_impl::RESERVED_COLUMNS.count(
1,024,628✔
4518
                vd->vd_meta.lvm_name.to_string_fragment()))
1,024,628✔
4519
        {
4520
            auto um = lnav::console::user_message::error(
×
4521
                          attr_line_t("value name ")
1✔
4522
                              .append_quoted(lnav::roles::symbol(
2✔
4523
                                  fmt::format(FMT_STRING("/{}/value/{}"),
4✔
4524
                                              this->elf_name,
1✔
4525
                                              vd->vd_meta.lvm_name)))
1✔
4526
                              .append(" is reserved and cannot be used"))
1✔
4527
                          .with_reason(
2✔
4528
                              "lnav automatically defines several columns in "
4529
                              "the log virtual table")
4530
                          .with_snippets(this->get_snippets())
2✔
4531
                          .with_help("Choose another name")
2✔
4532
                          .move();
1✔
4533
            errors.emplace_back(um);
1✔
4534
        }
1✔
4535

4536
        vd->vd_meta.lvm_format = this;
541,269✔
4537
        if (!vd->vd_internal
541,269✔
4538
            && !vd->vd_meta.lvm_column.is<logline_value_meta::table_column>())
541,269✔
4539
        {
4540
            vd->vd_meta.lvm_column
156,762✔
4541
                = logline_value_meta::table_column{this->elf_column_count++};
156,762✔
4542
        }
4543

4544
        if (vd->vd_meta.lvm_kind == value_kind_t::VALUE_UNKNOWN) {
541,269✔
4545
            log_warning("no kind set for %s, assuming text",
×
4546
                        vd->vd_meta.lvm_name.c_str());
4547
            vd->vd_meta.lvm_kind = value_kind_t::VALUE_TEXT;
×
4548
        }
4549

4550
        if (this->elf_type == elf_type_t::ELF_TYPE_TEXT) {
541,269✔
4551
            std::set<std::string> available_captures;
345,390✔
4552

4553
            bool found_in_pattern = false;
345,390✔
4554
            for (const auto& pat : this->elf_patterns) {
579,873✔
4555
                if (pat.second->p_pcre.pp_value == nullptr) {
579,871✔
4556
                    continue;
×
4557
                }
4558

4559
                auto cap_index = pat.second->p_pcre.pp_value->name_index(
1,159,742✔
4560
                    vd->vd_meta.lvm_name.get());
579,871✔
4561
                if (cap_index >= 0) {
579,871✔
4562
                    found_in_pattern = true;
345,388✔
4563
                    break;
345,388✔
4564
                }
4565

4566
                for (auto named_cap :
234,483✔
4567
                     pat.second->p_pcre.pp_value->get_named_captures())
2,136,473✔
4568
                {
4569
                    available_captures.insert(named_cap.get_name().to_string());
1,667,507✔
4570
                }
4571
            }
4572
            if (!found_in_pattern) {
345,390✔
4573
                auto notes
4574
                    = attr_line_t("the following captures are available:\n  ")
2✔
4575
                          .join(available_captures,
2✔
4576
                                VC_ROLE.value(role_t::VCR_SYMBOL),
4✔
4577
                                ", ")
4578
                          .move();
2✔
4579
                errors.emplace_back(
2✔
4580
                    lnav::console::user_message::warning(
×
4581
                        attr_line_t("invalid value ")
2✔
4582
                            .append_quoted(lnav::roles::symbol(
4✔
4583
                                fmt::format(FMT_STRING("/{}/value/{}"),
8✔
4584
                                            this->elf_name,
2✔
4585
                                            vd->vd_meta.lvm_name.get()))))
4✔
4586
                        .with_reason(
4✔
4587
                            attr_line_t("no patterns have a capture named ")
4✔
4588
                                .append_quoted(vd->vd_meta.lvm_name.get()))
2✔
4589
                        .with_note(notes)
2✔
4590
                        .with_snippets(this->get_snippets())
4✔
4591
                        .with_help("values are populated from captures in "
4592
                                   "patterns, so at least one pattern must "
4593
                                   "have a capture with this value name"));
4594
            }
2✔
4595
        }
345,390✔
4596

4597
        for (act_iter = vd->vd_action_list.begin();
541,269✔
4598
             act_iter != vd->vd_action_list.end();
542,086✔
4599
             ++act_iter)
817✔
4600
        {
4601
            if (this->lf_action_defs.find(*act_iter)
817✔
4602
                == this->lf_action_defs.end())
1,634✔
4603
            {
4604
#if 0
4605
                errors.push_back("error:" + this->elf_name.to_string() + ":"
4606
                                 + vd->vd_meta.lvm_name.get()
4607
                                 + ": cannot find action -- " + (*act_iter));
4608
#endif
4609
            }
4610
        }
4611

4612
        vd->set_rewrite_src_name();
541,269✔
4613

4614
        for (auto& hd_pair : vd->vd_highlighter_patterns) {
543,822✔
4615
            auto& hd = hd_pair.second;
2,553✔
4616
            text_attrs attrs;
2,553✔
4617

4618
            if (hd.hd_pattern.pp_value == nullptr) {
2,553✔
4619
                hd.hd_pattern.pp_value
4620
                    = lnav::pcre2pp::code::from_const(".*").to_shared();
2,451✔
4621
            }
4622

4623
            if (!hd.hd_color.pp_value.empty()) {
2,553✔
4624
                attrs.ta_fg_color = vc.match_color(
2,553✔
4625
                    styling::color_unit::from_str(hd.hd_color.pp_value)
5,106✔
4626
                        .unwrapOrElse([&](const auto& msg) {
×
4627
                            errors.emplace_back(
×
4628
                                lnav::console::user_message::error(
4629
                                    attr_line_t()
×
4630
                                        .append_quoted(hd.hd_color.pp_value)
×
4631
                                        .append(
×
4632
                                            " is not a valid color value for "
4633
                                            "property ")
4634
                                        .append_quoted(lnav::roles::symbol(
×
4635
                                            hd.hd_color.pp_path.to_string())))
×
4636
                                    .with_reason(msg)
×
4637
                                    .with_snippet(hd.hd_color.to_snippet()));
×
4638
                            return styling::color_unit::EMPTY;
×
4639
                        }));
4640
            }
4641

4642
            if (!hd.hd_background_color.pp_value.empty()) {
2,553✔
4643
                attrs.ta_bg_color = vc.match_color(
×
4644
                    styling::color_unit::from_str(
×
4645
                        hd.hd_background_color.pp_value)
×
4646
                        .unwrapOrElse([&](const auto& msg) {
×
4647
                            errors.emplace_back(
×
4648
                                lnav::console::user_message::error(
4649
                                    attr_line_t()
×
4650
                                        .append_quoted(
×
4651
                                            hd.hd_background_color.pp_value)
×
4652
                                        .append(
×
4653
                                            " is not a valid color value for "
4654
                                            "property ")
4655
                                        .append_quoted(lnav::roles::symbol(
×
4656
                                            hd.hd_background_color.pp_path
×
4657
                                                .to_string())))
4658
                                    .with_reason(msg)
×
4659
                                    .with_snippet(
4660
                                        hd.hd_background_color.to_snippet()));
×
4661
                            return styling::color_unit::EMPTY;
×
4662
                        }));
4663
            }
4664

4665
            if (hd.hd_underline) {
2,553✔
4666
                attrs |= text_attrs::style::underline;
×
4667
            }
4668
            if (hd.hd_blink) {
2,553✔
4669
                attrs |= text_attrs::style::blink;
×
4670
            }
4671

4672
            if (hd.hd_pattern.pp_value != nullptr) {
2,553✔
4673
                this->lf_highlighters.emplace_back(hd.hd_pattern.pp_value);
2,553✔
4674
                this->lf_highlighters.back()
2,553✔
4675
                    .with_field(vd->vd_meta.lvm_name)
2,553✔
4676
                    .with_name(hd_pair.first.to_string())
5,106✔
4677
                    .with_attrs(attrs)
2,553✔
4678
                    .with_nestable(hd.hd_nestable);
2,553✔
4679
            }
4680
        }
4681
    }
4682

4683
    if (this->elf_type == elf_type_t::ELF_TYPE_JSON) {
60,058✔
4684
        for (const auto& vd : this->elf_value_def_order) {
207,931✔
4685
            this->elf_value_def_frag_map[vd->vd_meta.lvm_name
195,879✔
4686
                                             .to_string_fragment()] = vd.get();
195,879✔
4687
        }
4688
    }
4689

4690
    for (const auto& td_pair : this->lf_tag_defs) {
62,614✔
4691
        const auto& td = td_pair.second;
2,556✔
4692

4693
        if (td->ftd_pattern.pp_value == nullptr
2,556✔
4694
            || td->ftd_pattern.pp_value->get_pattern().empty())
2,556✔
4695
        {
4696
            errors.emplace_back(
3✔
4697
                lnav::console::user_message::error(
×
4698
                    attr_line_t("invalid tag definition ")
6✔
4699
                        .append_quoted(lnav::roles::symbol(
6✔
4700
                            fmt::format(FMT_STRING("/{}/tags/{}"),
12✔
4701
                                        this->elf_name,
3✔
4702
                                        td_pair.first))))
3✔
4703
                    .with_reason(
6✔
4704
                        "tag definitions must have a non-empty pattern")
4705
                    .with_snippets(this->get_snippets()));
6✔
4706
        }
4707
    }
4708

4709
    if (this->elf_opid_field.empty()
60,058✔
4710
        && this->lf_opid_description_def->size() > 1)
60,058✔
4711
    {
4712
        errors.emplace_back(
1✔
4713
            lnav::console::user_message::error(
×
4714
                attr_line_t("too many opid descriptions")
2✔
4715
                    .append_quoted(lnav::roles::symbol(fmt::format(
2✔
4716
                        FMT_STRING("/{}/opid/description"), this->elf_name))))
4✔
4717
                .with_reason(attr_line_t("when no ")
2✔
4718
                                 .append("opid-field"_symbol)
1✔
4719
                                 .append(" is specified, only a single "
1✔
4720
                                         "description is supported"))
4721
                .with_snippets(this->get_snippets()));
2✔
4722
    }
4723

4724
    for (const auto& opid_desc_pair : *this->lf_opid_description_def) {
84,570✔
4725
        for (const auto& opid_desc : *opid_desc_pair.second.od_descriptors) {
58,011✔
4726
            auto iter = this->elf_value_defs.find(opid_desc.od_field.pp_value);
33,499✔
4727
            if (iter == this->elf_value_defs.end()) {
33,499✔
4728
                errors.emplace_back(
2✔
4729
                    lnav::console::user_message::error(
×
4730
                        attr_line_t("invalid opid description field ")
4✔
4731
                            .append_quoted(lnav::roles::symbol(
4✔
4732
                                opid_desc.od_field.pp_path.to_string())))
4✔
4733
                        .with_reason(
4✔
4734
                            attr_line_t("unknown value name ")
4✔
4735
                                .append_quoted(opid_desc.od_field.pp_value))
2✔
4736
                        .with_snippets(this->get_snippets()));
4✔
4737
            } else {
4738
                this->lf_desc_fields.insert(iter->first);
33,497✔
4739
                iter->second->vd_is_desc_field = true;
33,497✔
4740
            }
4741
        }
4742
    }
4743

4744
    for (const auto& subid_desc_pair : *this->lf_subid_description_def) {
60,875✔
4745
        for (const auto& subid_desc : *subid_desc_pair.second.od_descriptors) {
1,634✔
4746
            auto iter = this->elf_value_defs.find(subid_desc.od_field.pp_value);
817✔
4747
            if (iter == this->elf_value_defs.end()) {
817✔
4748
                errors.emplace_back(
×
4749
                    lnav::console::user_message::error(
×
4750
                        attr_line_t("invalid subid description field ")
×
4751
                            .append_quoted(lnav::roles::symbol(
×
4752
                                subid_desc.od_field.pp_path.to_string())))
×
4753
                        .with_reason(
×
4754
                            attr_line_t("unknown value name ")
×
4755
                                .append_quoted(subid_desc.od_field.pp_value))
×
4756
                        .with_snippets(this->get_snippets()));
×
4757
            } else {
4758
                this->lf_desc_fields.insert(iter->first);
817✔
4759
                iter->second->vd_is_desc_field = true;
817✔
4760
            }
4761
        }
4762
    }
4763

4764
    if (this->elf_type == elf_type_t::ELF_TYPE_TEXT
120,116✔
4765
        && this->elf_samples.empty())
60,058✔
4766
    {
4767
        errors.emplace_back(
3✔
4768
            lnav::console::user_message::error(
×
4769
                attr_line_t()
6✔
4770
                    .append_quoted(
3✔
4771
                        lnav::roles::symbol(this->elf_name.to_string()))
6✔
4772
                    .append(" is not a valid log format"))
3✔
4773
                .with_reason("log message samples must be included in a format "
6✔
4774
                             "definition")
4775
                .with_snippets(this->get_snippets()));
6✔
4776
    }
4777

4778
    for (const auto& pat : this->elf_pattern_order) {
169,444✔
4779
        if (this->elf_type != elf_type_t::ELF_TYPE_TEXT) {
109,386✔
4780
            continue;
1✔
4781
        }
4782
        if (pat->p_pcre.pp_value->name_index(this->lf_timestamp_field.get())
109,385✔
4783
            < 0)
109,385✔
4784
        {
4785
            attr_line_t notes;
1✔
4786
            bool first_note = true;
1✔
4787

4788
            if (pat->p_pcre.pp_value->get_capture_count() > 0) {
1✔
4789
                notes.append("the following captures are available:\n  ");
1✔
4790
            }
4791
            for (auto named_cap : pat->p_pcre.pp_value->get_named_captures()) {
4✔
4792
                if (!first_note) {
3✔
4793
                    notes.append(", ");
2✔
4794
                }
4795
                notes.append(
3✔
4796
                    lnav::roles::symbol(named_cap.get_name().to_string()));
6✔
4797
                first_note = false;
3✔
4798
            }
4799
            errors.emplace_back(
1✔
4800
                lnav::console::user_message::error(
×
4801
                    attr_line_t("invalid value for property ")
1✔
4802
                        .append_quoted(lnav::roles::symbol(
2✔
4803
                            fmt::format(FMT_STRING("/{}/timestamp-field"),
4✔
4804
                                        this->elf_name))))
1✔
4805
                    .with_reason(
2✔
4806
                        attr_line_t()
2✔
4807
                            .append_quoted(this->lf_timestamp_field)
1✔
4808
                            .append(" was not found in the pattern at ")
1✔
4809
                            .append(lnav::roles::symbol(pat->p_config_path)))
2✔
4810
                    .with_note(notes)
1✔
4811
                    .with_snippets(this->get_snippets()));
2✔
4812
        }
1✔
4813
    }
4814

4815
    for (size_t sample_index = 0; sample_index < this->elf_samples.size();
268,504✔
4816
         sample_index += 1)
208,446✔
4817
    {
4818
        auto& elf_sample = this->elf_samples[sample_index];
208,446✔
4819
        auto sample_lines
4820
            = string_fragment(elf_sample.s_line.pp_value).split_lines();
208,446✔
4821

4822
        if (this->test_line(elf_sample, errors).is<scan_match>()) {
208,446✔
4823
            for (const auto& pat_name : elf_sample.s_matched_regexes) {
413,622✔
4824
                this->elf_patterns[pat_name]->p_matched_samples.emplace(
205,177✔
4825
                    sample_index);
4826
            }
4827
        }
4828
    }
208,446✔
4829

4830
    if (!this->elf_samples.empty()) {
60,058✔
4831
        for (const auto& elf_sample : this->elf_samples) {
258,083✔
4832
            if (elf_sample.s_matched_regexes.size() <= 1) {
208,446✔
4833
                continue;
208,446✔
4834
            }
4835

4836
            errors.emplace_back(
×
4837
                lnav::console::user_message::warning(
×
4838
                    attr_line_t("invalid log format: ")
×
4839
                        .append_quoted(
×
4840
                            lnav::roles::symbol(this->elf_name.to_string())))
×
4841
                    .with_reason(
×
4842
                        attr_line_t(
×
4843
                            "sample is matched by more than one regex: ")
4844
                            .join(elf_sample.s_matched_regexes,
×
4845
                                  VC_ROLE.value(role_t::VCR_SYMBOL),
×
4846
                                  ", "))
4847
                    .with_snippet(lnav::console::snippet::from(
×
4848
                        elf_sample.s_line.pp_location,
4849
                        attr_line_t().append(lnav::roles::quoted_code(
×
4850
                            elf_sample.s_line.pp_value))))
×
4851
                    .with_help("log format regexes must match a single type "
4852
                               "of log message"));
4853
        }
4854

4855
        for (const auto& pat : this->elf_pattern_order) {
159,020✔
4856
            if (pat->p_matched_samples.empty()) {
109,383✔
4857
                errors.emplace_back(
2✔
4858
                    lnav::console::user_message::warning(
×
4859
                        attr_line_t("invalid pattern: ")
4✔
4860
                            .append_quoted(
2✔
4861
                                lnav::roles::symbol(pat->p_config_path)))
4✔
4862
                        .with_reason("pattern does not match any samples")
4✔
4863
                        .with_snippet(lnav::console::snippet::from(
6✔
4864
                            pat->p_pcre.pp_location, ""))
2✔
4865
                        .with_help(
4866
                            "every pattern should have at least one sample "
4867
                            "that it matches"));
4868
            }
4869
        }
4870
    }
4871

4872
    size_t value_def_index = 0;
60,058✔
4873
    for (auto& elf_value_def : this->elf_value_def_order) {
601,327✔
4874
        elf_value_def->vd_meta.lvm_values_index
541,269✔
4875
            = std::make_optional(value_def_index++);
541,269✔
4876

4877
        if (elf_value_def->vd_meta.lvm_foreign_key
541,269✔
4878
            || elf_value_def->vd_meta.lvm_identifier)
541,269✔
4879
        {
4880
            continue;
282,477✔
4881
        }
4882

4883
        switch (elf_value_def->vd_meta.lvm_kind) {
258,792✔
4884
            case value_kind_t::VALUE_INTEGER:
60,866✔
4885
            case value_kind_t::VALUE_FLOAT:
4886
                this->elf_numeric_value_defs.push_back(elf_value_def);
60,866✔
4887
                break;
60,866✔
4888
            default:
197,926✔
4889
                break;
197,926✔
4890
        }
4891
    }
4892

4893
    int format_index = 0;
60,058✔
4894
    for (auto iter = this->jlf_line_format.begin();
60,058✔
4895
         iter != this->jlf_line_format.end();
204,869✔
4896
         ++iter, format_index++)
144,811✔
4897
    {
4898
        static const intern_string_t ts
4899
            = intern_string::lookup("__timestamp__");
146,445✔
4900
        static const intern_string_t level_field
4901
            = intern_string::lookup("__level__");
146,445✔
4902
        auto& jfe = *iter;
144,811✔
4903

4904
        if (startswith(jfe.jfe_value.pp_value.get(), "/")) {
144,811✔
4905
            jfe.jfe_value.pp_value
4906
                = intern_string::lookup(jfe.jfe_value.pp_value.get() + 1);
204✔
4907
        }
4908
        if (!jfe.jfe_ts_format.empty()) {
144,811✔
4909
            if (!jfe.jfe_value.pp_value.empty() && jfe.jfe_value.pp_value != ts)
919✔
4910
            {
4911
                log_warning(
102✔
4912
                    "%s:line-format[%d]:ignoring field '%s' since "
4913
                    "timestamp-format was used",
4914
                    this->elf_name.get(),
4915
                    format_index,
4916
                    jfe.jfe_value.pp_value.get());
4917
            }
4918
            jfe.jfe_value.pp_value = ts;
919✔
4919
        }
4920

4921
        switch (jfe.jfe_type) {
144,811✔
4922
            case json_log_field::VARIABLE: {
99,674✔
4923
                auto vd_iter
4924
                    = this->elf_value_defs.find(jfe.jfe_value.pp_value);
99,674✔
4925
                if (jfe.jfe_value.pp_value == ts) {
99,674✔
4926
                    this->elf_value_defs[this->lf_timestamp_field]
10,111✔
4927
                        ->vd_meta.lvm_hidden = true;
10,111✔
4928
                } else if (jfe.jfe_value.pp_value == level_field) {
89,563✔
4929
                    this->elf_value_defs[this->elf_level_field]
4,085✔
4930
                        ->vd_meta.lvm_hidden = true;
4,085✔
4931
                } else if (vd_iter == this->elf_value_defs.end()) {
85,478✔
4932
                    errors.emplace_back(
2✔
4933
                        lnav::console::user_message::error(
×
4934
                            attr_line_t("invalid line format element ")
4✔
4935
                                .append_quoted(lnav::roles::symbol(fmt::format(
4✔
4936
                                    FMT_STRING("/{}/line-format/{}/field"),
6✔
4937
                                    this->elf_name,
2✔
4938
                                    format_index))))
4939
                            .with_reason(
4✔
4940
                                attr_line_t()
4✔
4941
                                    .append_quoted(jfe.jfe_value.pp_value)
2✔
4942
                                    .append(" is not a defined value"))
2✔
4943
                            .with_snippet(jfe.jfe_value.to_snippet()));
4✔
4944
                } else {
4945
                    vd_iter->second->vd_line_format_index = format_index;
85,476✔
4946
                    switch (vd_iter->second->vd_meta.lvm_kind) {
85,476✔
4947
                        case value_kind_t::VALUE_INTEGER:
13,276✔
4948
                        case value_kind_t::VALUE_FLOAT:
4949
                            if (jfe.jfe_align
13,276✔
4950
                                == json_format_element::align_t::NONE)
4951
                            {
4952
                                jfe.jfe_align
4953
                                    = json_format_element::align_t::RIGHT;
12,459✔
4954
                            }
4955
                            break;
13,276✔
4956
                        default:
72,200✔
4957
                            break;
72,200✔
4958
                    }
4959
                }
4960
                break;
99,674✔
4961
            }
4962
            case json_log_field::CONSTANT:
45,137✔
4963
                this->jlf_line_format_init_count
45,137✔
4964
                    += std::count(jfe.jfe_default_value.begin(),
45,137✔
4965
                                  jfe.jfe_default_value.end(),
4966
                                  '\n');
45,137✔
4967
                break;
45,137✔
4968
            default:
×
4969
                break;
×
4970
        }
4971
    }
4972

4973
    for (auto& hd_pair : this->elf_highlighter_patterns) {
61,285✔
4974
        auto& hd = hd_pair.second;
1,227✔
4975
        text_attrs attrs;
1,227✔
4976

4977
        if (!hd.hd_color.pp_value.empty()) {
1,227✔
4978
            attrs.ta_fg_color = vc.match_color(
1,226✔
4979
                styling::color_unit::from_str(hd.hd_color.pp_value)
2,452✔
4980
                    .unwrapOrElse([&](const auto& msg) {
1✔
4981
                        errors.emplace_back(
1✔
4982
                            lnav::console::user_message::error(
4983
                                attr_line_t()
1✔
4984
                                    .append_quoted(hd.hd_color.pp_value)
2✔
4985
                                    .append(" is not a valid color value for "
1✔
4986
                                            "property ")
4987
                                    .append_quoted(lnav::roles::symbol(
2✔
4988
                                        hd.hd_color.pp_path.to_string())))
1✔
4989
                                .with_reason(msg)
2✔
4990
                                .with_snippet(hd.hd_color.to_snippet()));
1✔
4991
                        return styling::color_unit::EMPTY;
1✔
4992
                    }));
4993
        }
4994

4995
        if (!hd.hd_background_color.pp_value.empty()) {
1,227✔
4996
            attrs.ta_bg_color = vc.match_color(
1✔
4997
                styling::color_unit::from_str(hd.hd_background_color.pp_value)
2✔
4998
                    .unwrapOrElse([&](const auto& msg) {
1✔
4999
                        errors.emplace_back(
1✔
5000
                            lnav::console::user_message::error(
5001
                                attr_line_t()
1✔
5002
                                    .append_quoted(
2✔
5003
                                        hd.hd_background_color.pp_value)
1✔
5004
                                    .append(" is not a valid color value for "
1✔
5005
                                            "property ")
5006
                                    .append_quoted(lnav::roles::symbol(
2✔
5007
                                        hd.hd_background_color.pp_path
1✔
5008
                                            .to_string())))
5009
                                .with_reason(msg)
2✔
5010
                                .with_snippet(
5011
                                    hd.hd_background_color.to_snippet()));
1✔
5012
                        return styling::color_unit::EMPTY;
1✔
5013
                    }));
5014
        }
5015

5016
        if (hd.hd_underline) {
1,227✔
5017
            attrs |= text_attrs::style::underline;
102✔
5018
        }
5019
        if (hd.hd_blink) {
1,227✔
5020
            attrs |= text_attrs::style::blink;
×
5021
        }
5022

5023
        if (hd.hd_pattern.pp_value != nullptr) {
1,227✔
5024
            this->lf_highlighters.emplace_back(hd.hd_pattern.pp_value);
1,225✔
5025
            this->lf_highlighters.back()
1,225✔
5026
                .with_name(hd_pair.first.to_string())
2,450✔
5027
                .with_attrs(attrs)
1,225✔
5028
                .with_nestable(hd.hd_nestable);
1,225✔
5029
        }
5030
    }
5031
}
5032

5033
void
5034
external_log_format::register_vtabs(
51,214✔
5035
    log_vtab_manager* vtab_manager,
5036
    std::vector<lnav::console::user_message>& errors)
5037
{
5038
    for (auto& elf_search_table : this->elf_search_tables) {
61,043✔
5039
        if (elf_search_table.second.std_pattern.pp_value == nullptr) {
9,829✔
5040
            continue;
1✔
5041
        }
5042

5043
        auto lst = std::make_shared<log_search_table>(
5044
            elf_search_table.second.std_pattern.pp_value,
9,828✔
5045
            elf_search_table.first);
9,828✔
5046
        lst->lst_format = this;
9,828✔
5047
        lst->lst_log_path_glob = elf_search_table.second.std_glob;
9,828✔
5048
        if (elf_search_table.second.std_level != LEVEL_UNKNOWN) {
9,828✔
5049
            lst->lst_log_level = elf_search_table.second.std_level;
3,510✔
5050
        }
5051
        auto errmsg = vtab_manager->register_vtab(lst);
9,828✔
5052
        if (!errmsg.empty()) {
9,828✔
5053
#if 0
5054
            errors.push_back("error:" + this->elf_name.to_string() + ":"
5055
                             + search_iter->first.to_string()
5056
                             + ":unable to register table -- " + errmsg);
5057
#endif
5058
        }
5059
    }
9,828✔
5060
}
51,214✔
5061

5062
bool
5063
external_log_format::match_samples(const std::vector<sample_t>& samples) const
4,367,722✔
5064
{
5065
    for (const auto& sample_iter : samples) {
19,350,890✔
5066
        for (const auto& pat_iter : this->elf_pattern_order) {
40,704,238✔
5067
            auto& pat = *pat_iter;
25,721,070✔
5068

5069
            if (!pat.p_pcre.pp_value) {
25,721,070✔
5070
                continue;
×
5071
            }
5072

5073
            if (pat.p_pcre.pp_value
51,442,140✔
5074
                    ->find_in(sample_iter.s_line.pp_value, PCRE2_NO_UTF_CHECK)
51,442,140✔
5075
                    .ignore_error())
51,442,140✔
5076
            {
5077
                return true;
17,175✔
5078
            }
5079
        }
5080
    }
5081

5082
    return false;
4,350,547✔
5083
}
5084

5085
class external_log_table : public log_format_vtab_impl {
5086
public:
5087
    explicit external_log_table(std::shared_ptr<const log_format> elf)
51,214✔
5088
        : log_format_vtab_impl(elf),
51,214✔
5089
          elt_format(dynamic_cast<const external_log_format*>(elf.get()))
51,214✔
5090
    {
5091
    }
51,214✔
5092

5093
    void get_columns(std::vector<vtab_column>& cols) const override
51,639✔
5094
    {
5095
        const auto& elf = this->elt_format;
51,639✔
5096

5097
        cols.resize(elf->elf_column_count);
51,639✔
5098
        for (const auto& vd : elf->elf_value_def_order) {
519,153✔
5099
            auto type_pair = logline_value_to_sqlite_type(vd->vd_meta.lvm_kind);
467,514✔
5100

5101
            if (!vd->vd_meta.lvm_column.is<logline_value_meta::table_column>())
467,514✔
5102
            {
5103
                continue;
49,137✔
5104
            }
5105

5106
            auto col
5107
                = vd->vd_meta.lvm_column.get<logline_value_meta::table_column>()
418,377✔
5108
                      .value;
418,377✔
5109
            require(0 <= col && col < elf->elf_column_count);
418,377✔
5110

5111
            cols[col].vc_name = vd->vd_meta.lvm_name.get();
418,377✔
5112
            cols[col].vc_type = type_pair.first;
418,377✔
5113
            cols[col].vc_subtype = type_pair.second;
418,377✔
5114
            cols[col].vc_collator = vd->vd_collate;
418,377✔
5115
            cols[col].vc_comment = vd->vd_description;
418,377✔
5116
        }
5117
    }
51,639✔
5118

5119
    void get_foreign_keys(
39,960✔
5120
        std::unordered_set<std::string>& keys_inout) const override
5121
    {
5122
        log_vtab_impl::get_foreign_keys(keys_inout);
39,960✔
5123

5124
        for (const auto& elf_value_def : this->elt_format->elf_value_defs) {
579,786✔
5125
            if (elf_value_def.second->vd_meta.lvm_foreign_key
539,826✔
5126
                || elf_value_def.second->vd_meta.lvm_identifier)
539,826✔
5127
            {
5128
                keys_inout.emplace(elf_value_def.first.to_string());
195,786✔
5129
            }
5130
        }
5131
    }
39,960✔
5132

5133
    bool next(log_cursor& lc, logfile_sub_source& lss) override
3,572✔
5134
    {
5135
        if (lc.is_eof()) {
3,572✔
5136
            return true;
×
5137
        }
5138

5139
        content_line_t cl(lss.at(lc.lc_curr_line));
3,572✔
5140
        auto* lf = lss.find_file_ptr(cl);
3,572✔
5141
        auto lf_iter = lf->begin() + cl;
3,572✔
5142
        if (lf_iter->is_continued()) {
3,572✔
5143
            return false;
×
5144
        }
5145

5146
        if (lf->get_format_name() == this->lfvi_format->get_name()) {
3,572✔
5147
            return true;
3,566✔
5148
        }
5149

5150
        return false;
6✔
5151
    }
5152

5153
    void extract(logfile* lf,
3,404✔
5154
                 uint64_t line_number,
5155
                 string_attrs_t& sa,
5156
                 logline_value_vector& values) override
5157
    {
5158
        auto format = lf->get_format();
3,404✔
5159

5160
        sa.clear();
3,404✔
5161
        format->annotate(lf, line_number, sa, values);
3,404✔
5162
    }
3,404✔
5163

5164
    const external_log_format* elt_format;
5165
    line_range elt_container_body;
5166
};
5167

5168
std::shared_ptr<log_vtab_impl>
5169
external_log_format::get_vtab_impl() const
51,214✔
5170
{
5171
    return std::make_shared<external_log_table>(this->shared_from_this());
51,214✔
5172
}
5173

5174
std::shared_ptr<log_format>
5175
external_log_format::specialized(int fmt_lock)
564✔
5176
{
5177
    auto retval = std::make_shared<external_log_format>(*this);
564✔
5178

5179
    retval->lf_specialized = true;
564✔
5180
    if (this->elf_type == elf_type_t::ELF_TYPE_JSON) {
564✔
5181
        this->jlf_parse_context
5182
            = std::make_shared<yajlpp_parse_context>(this->elf_name);
51✔
5183
        this->jlf_yajl_handle.reset(
51✔
5184
            yajl_alloc(&this->jlf_parse_context->ypc_callbacks,
51✔
5185
                       nullptr,
5186
                       this->jlf_parse_context.get()),
51✔
5187
            yajl_handle_deleter());
5188
        yajl_config(this->jlf_yajl_handle.get(), yajl_dont_validate_strings, 1);
51✔
5189
        this->jlf_attr_line.al_string.reserve(16 * 1024);
51✔
5190
    }
5191

5192
    this->elf_specialized_value_defs_state = *this->elf_value_defs_state;
564✔
5193

5194
    return retval;
1,128✔
5195
}
564✔
5196

5197
log_format::match_name_result
5198
external_log_format::match_name(const std::string& filename)
931,752✔
5199
{
5200
    if (this->elf_filename_pcre.pp_value == nullptr) {
931,752✔
5201
        return name_matched{};
930,258✔
5202
    }
5203

5204
    if (this->elf_filename_pcre.pp_value->find_in(filename)
2,988✔
5205
            .ignore_error()
2,988✔
5206
            .has_value())
1,494✔
5207
    {
5208
        return name_matched{};
247✔
5209
    }
5210

5211
    return name_mismatched{
2,494✔
5212
        this->elf_filename_pcre.pp_value->match_partial(filename),
2,494✔
5213
        this->elf_filename_pcre.pp_value->get_pattern(),
1,247✔
5214
    };
1,247✔
5215
}
5216

5217
auto
5218
external_log_format::value_line_count(scan_batch_context& sbc,
327,720✔
5219
                                      const value_def* vd,
5220
                                      bool top_level,
5221
                                      std::optional<double> val,
5222
                                      const unsigned char* str,
5223
                                      ssize_t len,
5224
                                      yajl_string_props_t* props)
5225
    -> value_line_count_result
5226
{
5227
    value_line_count_result retval;
327,720✔
5228

5229
    if (str != nullptr && props != nullptr && !val) {
327,720✔
5230
        auto frag = string_fragment::from_bytes(str, len);
240,495✔
5231
        while (frag.endswith("\n")) {
241,547✔
5232
            frag.pop_back();
1,052✔
5233
            props->line_feeds -= 1;
1,052✔
5234
        }
5235
        retval.vlcr_has_ansi |= props->has_ansi;
240,495✔
5236
        retval.vlcr_count += props->line_feeds;
240,495✔
5237
    }
5238

5239
    if (vd == nullptr) {
327,720✔
5240
        if (this->jlf_hide_extra || !top_level) {
303,279✔
5241
            retval.vlcr_count = 0;
283,735✔
5242
        }
5243

5244
        return retval;
303,279✔
5245
    }
5246

5247
    if (vd->vd_meta.lvm_values_index) {
24,441✔
5248
        auto& lvs = sbc.sbc_value_stats[vd->vd_meta.lvm_values_index.value()];
8,101✔
5249
        if (len > lvs.lvs_width) {
8,101✔
5250
            lvs.lvs_width = len;
4,289✔
5251
        }
5252
        if (val) {
8,101✔
5253
            lvs.add_value(val.value());
182✔
5254
        }
5255
    }
5256

5257
    if (vd->vd_line_format_index) {
24,441✔
5258
        retval.vlcr_line_format_count += 1;
3,989✔
5259
        retval.vlcr_count -= 1;
3,989✔
5260
        retval.vlcr_line_format_index = vd->vd_line_format_index;
3,989✔
5261
    }
5262
    if (vd->vd_meta.is_hidden()) {
24,441✔
5263
        retval.vlcr_count = 0;
3,771✔
5264
        return retval;
3,771✔
5265
    }
5266

5267
    return retval;
20,670✔
5268
}
5269

5270
log_level_t
5271
external_log_format::convert_level(string_fragment sf,
216,307✔
5272
                                   scan_batch_context* sbc) const
5273
{
5274
    auto retval = LEVEL_INFO;
216,307✔
5275

5276
    if (sf.is_valid()) {
216,307✔
5277
        if (sbc != nullptr) {
170,949✔
5278
            auto ssm_res = sbc->sbc_level_cache.lookup(sf);
10,915✔
5279
            if (ssm_res.has_value()) {
10,915✔
5280
                return static_cast<log_level_t>(ssm_res.value());
4,240✔
5281
            }
5282
        }
5283

5284
        if (this->elf_level_patterns.empty()) {
166,709✔
5285
            retval = string2level(sf.data(), sf.length());
37,244✔
5286
        } else {
5287
            for (const auto& elf_level_pattern : this->elf_level_patterns) {
348,920✔
5288
                if (elf_level_pattern.second.lp_pcre.pp_value
640,682✔
5289
                        ->find_in(sf, PCRE2_NO_UTF_CHECK)
640,682✔
5290
                        .ignore_error()
640,682✔
5291
                        .has_value())
320,341✔
5292
                {
5293
                    retval = elf_level_pattern.first;
100,886✔
5294
                    break;
100,886✔
5295
                }
5296
            }
5297
        }
5298

5299
        if (sbc != nullptr
166,709✔
5300
            && sf.length() <= lnav::small_string_map::MAX_KEY_SIZE)
166,709✔
5301
        {
5302
            sbc->sbc_level_cache.insert(sf, retval);
5,808✔
5303
        }
5304
    }
5305

5306
    return retval;
212,067✔
5307
}
5308

5309
logline_value_meta
5310
external_log_format::get_value_meta(intern_string_t field_name,
4,642✔
5311
                                    value_kind_t kind)
5312
{
5313
    const auto iter = this->elf_value_defs.find(field_name);
4,642✔
5314
    if (iter == this->elf_value_defs.end()) {
4,642✔
5315
        auto retval = logline_value_meta(
5316
            field_name, kind, logline_value_meta::external_column{}, this);
394✔
5317

5318
        retval.lvm_hidden = this->jlf_hide_extra;
394✔
5319
        return retval;
394✔
5320
    }
394✔
5321

5322
    auto lvm = iter->second->vd_meta;
4,248✔
5323

5324
    lvm.lvm_kind = kind;
4,248✔
5325
    return lvm;
4,248✔
5326
}
4,248✔
5327

5328
logline_value_meta
5329
external_log_format::get_value_meta(yajlpp_parse_context* ypc,
10,387✔
5330
                                    const value_def* vd,
5331
                                    value_kind_t kind)
5332
{
5333
    if (vd == nullptr) {
10,387✔
5334
        auto retval = logline_value_meta(
5335
            ypc->get_path(), kind, logline_value_meta::external_column{}, this);
748✔
5336

5337
        retval.lvm_hidden = this->jlf_hide_extra;
748✔
5338
        return retval;
748✔
5339
    }
748✔
5340

5341
    auto lvm = vd->vd_meta;
9,639✔
5342

5343
    switch (vd->vd_meta.lvm_kind) {
9,639✔
5344
        case value_kind_t::VALUE_TIMESTAMP:
285✔
5345
            break;
285✔
5346
        default:
9,354✔
5347
            lvm.lvm_kind = kind;
9,354✔
5348
            break;
9,354✔
5349
    }
5350
    return lvm;
9,639✔
5351
}
9,639✔
5352

5353
void
5354
external_log_format::json_append(const log_format_file_state& lffs,
7,670✔
5355
                                 const json_format_element& jfe,
5356
                                 const value_def* vd,
5357
                                 const string_fragment& sf)
5358
{
5359
    if (jfe.jfe_align == json_format_element::align_t::RIGHT) {
7,670✔
5360
        auto sf_width = sf.column_width();
271✔
5361
        if (sf_width < jfe.jfe_min_width) {
271✔
5362
            this->json_append_to_cache(jfe.jfe_min_width - sf_width);
6✔
5363
        } else if (jfe.jfe_auto_width && vd != nullptr
24✔
5364
                   && sf_width
289✔
5365
                       < lffs.lffs_value_stats[vd->vd_meta.lvm_values_index
24✔
5366
                                                   .value()]
24✔
5367
                             .lvs_width)
24✔
5368
        {
5369
            this->json_append_to_cache(
8✔
5370
                lffs.lffs_value_stats[vd->vd_meta.lvm_values_index.value()]
8✔
5371
                    .lvs_width
8✔
5372
                - sf_width);
8✔
5373
        }
5374
    }
5375
    this->json_append_to_cache(sf.data(), sf.length());
7,670✔
5376
    if ((jfe.jfe_align == json_format_element::align_t::LEFT
7,670✔
5377
         || jfe.jfe_align == json_format_element::align_t::NONE)
7,616✔
5378
        && (jfe.jfe_min_width > 0 || jfe.jfe_auto_width))
7,399✔
5379
    {
5380
        auto sf_width = sf.column_width();
1,309✔
5381
        if (sf_width < jfe.jfe_min_width) {
1,309✔
5382
            this->json_append_to_cache(jfe.jfe_min_width - sf_width);
382✔
5383
        } else if (jfe.jfe_auto_width && vd != nullptr
793✔
5384
                   && sf_width
1,720✔
5385
                       < lffs.lffs_value_stats[vd->vd_meta.lvm_values_index
688✔
5386
                                                   .value()]
688✔
5387
                             .lvs_width)
688✔
5388
        {
5389
            this->json_append_to_cache(
276✔
5390
                lffs.lffs_value_stats[vd->vd_meta.lvm_values_index.value()]
276✔
5391
                    .lvs_width
276✔
5392
                - sf_width);
276✔
5393
        }
5394
    }
5395
}
7,670✔
5396

5397
intern_string_t
5398
external_log_format::get_pattern_name(const pattern_locks& pl,
77✔
5399
                                      uint64_t line_number) const
5400
{
5401
    if (this->elf_type != elf_type_t::ELF_TYPE_TEXT) {
77✔
5402
        static auto structured = intern_string::lookup("structured");
5403

5404
        return structured;
×
5405
    }
5406
    auto pat_index = pl.pattern_index_for_line(line_number);
77✔
5407
    return this->elf_pattern_order[pat_index]->p_name;
77✔
5408
}
5409

5410
std::string
5411
log_format::get_pattern_path(const pattern_locks& pl,
×
5412
                             uint64_t line_number) const
5413
{
5414
    auto pat_index = pl.pattern_index_for_line(line_number);
×
5415
    return fmt::format(FMT_STRING("builtin ({})"), pat_index);
×
5416
}
5417

5418
intern_string_t
5419
log_format::get_pattern_name(const pattern_locks& pl,
6✔
5420
                             uint64_t line_number) const
5421
{
5422
    char pat_str[128];
5423

5424
    auto pat_index = pl.pattern_index_for_line(line_number);
6✔
5425
    auto to_n_res = fmt::format_to_n(
×
5426
        pat_str, sizeof(pat_str) - 1, FMT_STRING("builtin ({})"), pat_index);
18✔
5427
    pat_str[to_n_res.size] = '\0';
6✔
5428
    return intern_string::lookup(pat_str);
12✔
5429
}
5430

5431
std::shared_ptr<log_format>
5432
log_format::find_root_format(const char* name)
1,414✔
5433
{
5434
    auto& fmts = get_root_formats();
1,414✔
5435
    for (auto& lf : fmts) {
57,697✔
5436
        if (lf->get_name() == name) {
57,697✔
5437
            return lf;
1,414✔
5438
        }
5439
    }
5440
    return nullptr;
×
5441
}
5442

5443
exttm
5444
log_format::tm_for_display(logfile::iterator ll, string_fragment sf)
1,004✔
5445
{
5446
    auto adjusted_time = ll->get_timeval();
1,004✔
5447
    exttm retval;
1,004✔
5448

5449
    retval.et_nsec = std::chrono::duration_cast<std::chrono::nanoseconds>(
1,004✔
5450
                         std::chrono::microseconds{adjusted_time.tv_usec})
×
5451
                         .count();
1,004✔
5452
    if (this->lf_timestamp_flags & ETF_NANOS_SET) {
1,004✔
5453
        timeval actual_tv;
5454
        exttm tm;
5✔
5455
        if (this->lf_date_time.scan(sf.data(),
10✔
5456
                                    sf.length(),
5✔
5457
                                    this->get_timestamp_formats(),
5458
                                    &tm,
5459
                                    actual_tv,
5460
                                    false))
5461
        {
5462
            adjusted_time.tv_usec = actual_tv.tv_usec;
5✔
5463
            retval.et_nsec = tm.et_nsec;
5✔
5464
        }
5465
    }
5466
    gmtime_r(&adjusted_time.tv_sec, &retval.et_tm);
1,004✔
5467
    retval.et_flags = this->lf_timestamp_flags;
1,004✔
5468
    if (this->lf_timestamp_flags & ETF_ZONE_SET
1,004✔
5469
        && this->lf_date_time.dts_zoned_to_local)
914✔
5470
    {
5471
        retval.et_flags &= ~ETF_Z_IS_UTC;
914✔
5472
    }
5473
    retval.et_gmtoff = this->lf_date_time.dts_local_offset_cache;
1,004✔
5474

5475
    return retval;
2,008✔
5476
}
5477

5478
pattern_for_lines::pattern_for_lines(uint32_t pfl_line, uint32_t pfl_pat_index)
3,198✔
5479
    : pfl_line(pfl_line), pfl_pat_index(pfl_pat_index)
3,198✔
5480
{
5481
}
3,198✔
5482

5483
void
5484
logline_value_stats::merge(const logline_value_stats& other)
16,613✔
5485
{
5486
    if (other.lvs_width > this->lvs_width) {
16,613✔
5487
        this->lvs_width = other.lvs_width;
4,140✔
5488
    }
5489

5490
    if (other.lvs_count == 0) {
16,613✔
5491
        return;
15,459✔
5492
    }
5493

5494
    require(other.lvs_min_value <= other.lvs_max_value);
1,154✔
5495

5496
    if (other.lvs_min_value < this->lvs_min_value) {
1,154✔
5497
        this->lvs_min_value = other.lvs_min_value;
784✔
5498
    }
5499
    if (other.lvs_max_value > this->lvs_max_value) {
1,154✔
5500
        this->lvs_max_value = other.lvs_max_value;
1,082✔
5501
    }
5502
    this->lvs_count += other.lvs_count;
1,154✔
5503
    this->lvs_total += other.lvs_total;
1,154✔
5504
    this->lvs_tdigest.insert(other.lvs_tdigest);
1,154✔
5505
    ensure(this->lvs_count >= 0);
1,154✔
5506
    ensure(this->lvs_min_value <= this->lvs_max_value);
1,154✔
5507
}
5508

5509
void
5510
logline_value_stats::add_value(double value)
31,479✔
5511
{
5512
    if (value < this->lvs_min_value) {
31,479✔
5513
        this->lvs_min_value = value;
1,675✔
5514
    }
5515
    if (value > this->lvs_max_value) {
31,479✔
5516
        this->lvs_max_value = value;
2,397✔
5517
    }
5518
    this->lvs_count += 1;
31,479✔
5519
    this->lvs_total += value;
31,479✔
5520
    this->lvs_tdigest.insert(value);
31,479✔
5521
}
31,479✔
5522

5523
std::vector<logline_value_meta>
5524
external_log_format::get_value_metadata() const
9,828✔
5525
{
5526
    std::vector<logline_value_meta> retval;
9,828✔
5527

5528
    for (const auto& vd : this->elf_value_def_order) {
103,194✔
5529
        retval.emplace_back(vd->vd_meta);
93,366✔
5530
    }
5531

5532
    return retval;
9,828✔
5533
}
×
5534

5535
std::optional<size_t>
5536
external_log_format::stats_index_for_value(const intern_string_t& name) const
73✔
5537
{
5538
    const auto iter = this->elf_value_defs.find(name);
73✔
5539
    if (iter != this->elf_value_defs.end()
73✔
5540
        && iter->second->vd_meta.lvm_values_index)
73✔
5541
    {
5542
        return iter->second->vd_meta.lvm_values_index.value();
73✔
5543
    }
5544

5545
    return std::nullopt;
×
5546
}
5547

5548
std::string
5549
external_log_format::get_pattern_regex(const pattern_locks& pl,
17✔
5550
                                       uint64_t line_number) const
5551
{
5552
    if (this->elf_type != elf_type_t::ELF_TYPE_TEXT) {
17✔
5553
        return "";
2✔
5554
    }
5555
    auto pat_index = pl.pattern_index_for_line(line_number);
16✔
5556
    return this->elf_pattern_order[pat_index]->p_pcre.pp_value->get_pattern();
16✔
5557
}
5558

5559
bool
5560
external_log_format::hide_field(const intern_string_t field_name, bool val)
12✔
5561
{
5562
    const auto vd_iter = this->elf_value_defs.find(field_name);
12✔
5563
    if (vd_iter == this->elf_value_defs.end()) {
12✔
5564
        log_warning("field to hide not found: %s.%s",
1✔
5565
                    this->elf_name.c_str(),
5566
                    field_name.c_str());
5567
        return false;
1✔
5568
    }
5569

5570
    vd_iter->second->vd_meta.lvm_user_hidden = val;
11✔
5571
    if (this->elf_type == elf_type_t::ELF_TYPE_JSON) {
11✔
5572
        bool found = false;
2✔
5573

5574
        if (!field_name.to_string_fragment().find('#')) {
2✔
5575
            for (const auto& jfe : this->jlf_line_format) {
18✔
5576
                if (jfe.jfe_value.pp_value == field_name) {
17✔
5577
                    log_debug(
×
5578
                        "hide-field not triggering rebuild since it is in "
5579
                        "line-format: %s.%s",
5580
                        this->elf_name.c_str(),
5581
                        field_name.c_str());
5582
                    found = true;
×
5583
                    break;
×
5584
                }
5585
            }
5586
        }
5587
        if (!found) {
2✔
5588
            log_info("format field %s.%s changed, rebuilding",
2✔
5589
                     this->elf_name.get(),
5590
                     field_name.get());
5591
            this->elf_value_defs_state->vds_generation += 1;
2✔
5592
        }
5593
    }
5594
    return true;
11✔
5595
}
5596

5597
bool
5598
external_log_format::format_changed()
2,977✔
5599
{
5600
    if (this->elf_specialized_value_defs_state.vds_generation
5,954✔
5601
        != this->elf_value_defs_state->vds_generation)
2,977✔
5602
    {
5603
        this->elf_specialized_value_defs_state = *this->elf_value_defs_state;
2✔
5604
        this->jlf_cached_offset = -1;
2✔
5605
        return true;
2✔
5606
    }
5607

5608
    return false;
2,975✔
5609
}
5610

5611
bool
5612
format_tag_def::path_restriction::matches(const char* fn) const
9✔
5613
{
5614
    return fnmatch(this->p_glob.c_str(), fn, 0) == 0;
9✔
5615
}
5616

5617
bool
5618
format_partition_def::path_restriction::matches(const char* fn) const
9✔
5619
{
5620
    return fnmatch(this->p_glob.c_str(), fn, 0) == 0;
9✔
5621
}
5622

5623
int
5624
pattern_locks::pattern_index_for_line(uint64_t line_number) const
7,099✔
5625
{
5626
    if (this->pl_lines.empty()) {
7,099✔
5627
        return -1;
×
5628
    }
5629

5630
    auto iter
5631
        = std::lower_bound(this->pl_lines.cbegin(),
7,099✔
5632
                           this->pl_lines.cend(),
5633
                           line_number,
5634
                           [](const pattern_for_lines& pfl, uint32_t line) {
7,158✔
5635
                               return pfl.pfl_line < line;
7,158✔
5636
                           });
5637

5638
    if (iter == this->pl_lines.end() || iter->pfl_line != line_number) {
7,099✔
5639
        --iter;
6,341✔
5640
    }
5641

5642
    return iter->pfl_pat_index;
7,099✔
5643
}
5644

5645
/* XXX */
5646
#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