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

tstack / lnav / 23612272155-2889

26 Mar 2026 06:47PM UTC coverage: 69.031% (+0.03%) from 69.004%
23612272155-2889

push

github

tstack
[breakpoints] move breakpoints into logfile_sub_source

127 of 180 new or added lines in 10 files covered. (70.56%)

435 existing lines in 4 files now uncovered.

52876 of 76598 relevant lines covered (69.03%)

528416.58 hits per line

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

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

30
#include <algorithm>
31
#include <chrono>
32
#include <memory>
33
#include <vector>
34

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

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

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

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

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

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

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

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

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

104
    return *this;
898✔
105
}
106

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

117
    return *this;
381✔
118
}
119

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

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

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

149
    return *this;
381✔
150
}
151

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

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

165
    return *this;
517✔
166
}
167

168
void
169
log_level_stats::update_msg_count(log_level_t lvl, int32_t amount)
39,979✔
170
{
171
    switch (lvl) {
39,979✔
172
        case LEVEL_FATAL:
3,240✔
173
        case LEVEL_CRITICAL:
174
        case LEVEL_ERROR:
175
            this->lls_error_count += amount;
3,240✔
176
            break;
3,240✔
177
        case LEVEL_WARNING:
242✔
178
            this->lls_warning_count += amount;
242✔
179
            break;
242✔
180
        default:
36,497✔
181
            break;
36,497✔
182
    }
183
    this->lls_total_count += amount;
39,979✔
184
}
39,979✔
185

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

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

211
    return retval;
12,835✔
212
}
213

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

243
    return retval;
13,284✔
244
}
245

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

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

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

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

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

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

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

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

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

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

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

331
bool
332
logline_value_meta::is_numeric() const
×
333
{
334
    if (this->lvm_identifier || this->lvm_foreign_key) {
×
335
        return false;
×
336
    }
337
    switch (this->lvm_kind) {
×
338
        case value_kind_t::VALUE_FLOAT:
×
339
        case value_kind_t::VALUE_INTEGER:
340
            return true;
×
341
        default:
×
342
            return false;
×
343
    }
344
}
345

346
chart_type_t
347
logline_value_meta::to_chart_type() const
54✔
348
{
349
    auto retval = chart_type_t::hist;
54✔
350
    switch (this->lvm_kind) {
54✔
351
        case value_kind_t::VALUE_NULL:
9✔
352
            retval = chart_type_t::none;
9✔
353
            break;
9✔
354
        case value_kind_t::VALUE_INTEGER:
10✔
355
            if (!this->lvm_identifier && !this->lvm_foreign_key) {
10✔
356
                retval = chart_type_t::spectro;
5✔
357
            }
358
            break;
10✔
359
        case value_kind_t::VALUE_FLOAT:
×
360
            retval = chart_type_t::spectro;
×
361
            break;
×
362
        case value_kind_t::VALUE_XML:
1✔
363
        case value_kind_t::VALUE_JSON:
364
        case value_kind_t::VALUE_BOOLEAN:
365
        case value_kind_t::VALUE_TIMESTAMP:
366
            retval = chart_type_t::none;
1✔
367
            break;
1✔
368
        default:
34✔
369
            break;
34✔
370
    }
371

372
    return retval;
54✔
373
}
374

375
struct line_range
376
logline_value::origin_in_full_msg(const char* msg, ssize_t len) const
×
377
{
378
    if (this->lv_sub_offset == 0) {
×
379
        return this->lv_origin;
×
380
    }
381

382
    if (len == -1) {
×
383
        len = strlen(msg);
×
384
    }
385

386
    struct line_range retval = this->lv_origin;
×
387
    const char *last = msg, *msg_end = msg + len;
×
388

389
    for (int lpc = 0; lpc < this->lv_sub_offset; lpc++) {
×
390
        const auto* next = (const char*) memchr(last, '\n', msg_end - last);
×
391
        require(next != nullptr);
×
392

393
        next += 1;
×
394
        int amount = (next - last);
×
395

396
        retval.lr_start += amount;
×
397
        if (retval.lr_end != -1) {
×
398
            retval.lr_end += amount;
×
399
        }
400

401
        last = next + 1;
×
402
    }
403

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

407
        if (eol == nullptr) {
×
408
            retval.lr_end = len;
×
409
        } else {
410
            retval.lr_end = eol - msg;
×
411
        }
412
    }
413

414
    return retval;
×
415
}
416

417
logline_value::logline_value(logline_value_meta lvm,
641,835✔
418
                             shared_buffer_ref& sbr,
419
                             struct line_range origin)
641,835✔
420
    : lv_meta(std::move(lvm)), lv_origin(origin)
641,835✔
421
{
422
    if (sbr.get_data() == nullptr) {
641,835✔
423
        this->lv_meta.lvm_kind = value_kind_t::VALUE_NULL;
×
424
    }
425

426
    switch (this->lv_meta.lvm_kind) {
641,835✔
427
        case value_kind_t::VALUE_JSON:
327,888✔
428
        case value_kind_t::VALUE_XML:
429
        case value_kind_t::VALUE_STRUCT:
430
        case value_kind_t::VALUE_TEXT:
431
        case value_kind_t::VALUE_QUOTED:
432
        case value_kind_t::VALUE_W3C_QUOTED:
433
        case value_kind_t::VALUE_TIMESTAMP:
434
            require(origin.lr_end != -1);
327,888✔
435
            this->lv_frag = string_fragment::from_byte_range(
327,888✔
436
                sbr.get_data(), origin.lr_start, origin.lr_end);
327,888✔
437
            break;
327,888✔
438

439
        case value_kind_t::VALUE_NULL:
×
440
            break;
×
441

442
        case value_kind_t::VALUE_INTEGER: {
283,650✔
443
            auto scan_res
444
                = scn::scan_value<int64_t>(sbr.to_string_view(origin));
283,650✔
445
            if (scan_res) {
283,650✔
446
                this->lv_value.i = scan_res->value();
283,648✔
447
            } else {
448
                this->lv_value.i = 0;
2✔
449
            }
450
            break;
283,650✔
451
        }
452

453
        case value_kind_t::VALUE_FLOAT: {
30,297✔
454
            auto scan_res = scn::scan_value<double>(sbr.to_string_view(origin));
30,297✔
455
            if (scan_res) {
30,297✔
456
                this->lv_value.d = scan_res->value();
30,297✔
457
            } else {
458
                this->lv_value.d = 0;
×
459
            }
460
            break;
30,297✔
461
        }
462

463
        case value_kind_t::VALUE_BOOLEAN:
×
464
            if (strncmp(
×
465
                    sbr.get_data_at(origin.lr_start), "true", origin.length())
×
466
                    == 0
467
                || strncmp(
×
468
                       sbr.get_data_at(origin.lr_start), "yes", origin.length())
×
469
                    == 0)
470
            {
471
                this->lv_value.i = 1;
×
472
            } else {
473
                this->lv_value.i = 0;
×
474
            }
475
            break;
×
476

477
        case value_kind_t::VALUE_UNKNOWN:
×
478
        case value_kind_t::VALUE__MAX:
479
            ensure(0);
×
480
            break;
481
    }
482
}
641,835✔
483

484
void
485
logline_value::apply_scaling(const scaling_factor* sf)
43,697✔
486
{
487
    if (sf != nullptr) {
43,697✔
488
        switch (this->lv_meta.lvm_kind) {
10✔
489
            case value_kind_t::VALUE_INTEGER:
×
490
                sf->scale(this->lv_value.i);
×
491
                break;
×
492
            case value_kind_t::VALUE_FLOAT:
10✔
493
                sf->scale(this->lv_value.d);
10✔
494
                break;
10✔
495
            default:
×
496
                break;
×
497
        }
498
    }
499
}
43,697✔
500

501
std::string
502
logline_value::to_string() const
9,659✔
503
{
504
    char buffer[128];
505

506
    switch (this->lv_meta.lvm_kind) {
9,659✔
507
        case value_kind_t::VALUE_NULL:
77✔
508
            return "null";
154✔
509

510
        case value_kind_t::VALUE_JSON:
9,091✔
511
        case value_kind_t::VALUE_XML:
512
        case value_kind_t::VALUE_STRUCT:
513
        case value_kind_t::VALUE_TEXT:
514
        case value_kind_t::VALUE_TIMESTAMP:
515
            if (this->lv_str) {
9,091✔
516
                return this->lv_str.value();
1,737✔
517
            }
518
            if (this->lv_frag.empty()) {
7,354✔
519
                return this->lv_intern_string.to_string();
47✔
520
            }
521
            return this->lv_frag.to_string();
7,307✔
522

523
        case value_kind_t::VALUE_QUOTED:
7✔
524
        case value_kind_t::VALUE_W3C_QUOTED:
525
            if (this->lv_frag.empty()) {
7✔
526
                return "";
×
527
            } else {
528
                switch (this->lv_frag.data()[0]) {
7✔
529
                    case '\'':
7✔
530
                    case '"': {
531
                        auto unquote_func = this->lv_meta.lvm_kind
14✔
532
                                == value_kind_t::VALUE_W3C_QUOTED
533
                            ? unquote_w3c
7✔
534
                            : unquote;
535
                        stack_buf allocator;
7✔
536
                        auto* unquoted_str
537
                            = allocator.allocate(this->lv_frag.length());
7✔
538
                        size_t unquoted_len;
539

540
                        unquoted_len = unquote_func(unquoted_str,
7✔
541
                                                    this->lv_frag.data(),
542
                                                    this->lv_frag.length());
7✔
543
                        return {unquoted_str, unquoted_len};
14✔
544
                    }
7✔
545
                    default:
×
546
                        return this->lv_frag.to_string();
×
547
                }
548
            }
549
            break;
550

551
        case value_kind_t::VALUE_INTEGER:
454✔
552
            snprintf(buffer, sizeof(buffer), "%" PRId64, this->lv_value.i);
454✔
553
            break;
454✔
554

555
        case value_kind_t::VALUE_FLOAT:
24✔
556
            snprintf(buffer, sizeof(buffer), "%lf", this->lv_value.d);
24✔
557
            break;
24✔
558

559
        case value_kind_t::VALUE_BOOLEAN:
6✔
560
            if (this->lv_value.i) {
6✔
561
                return "true";
×
562
            } else {
563
                return "false";
12✔
564
            }
565
            break;
566
        case value_kind_t::VALUE_UNKNOWN:
×
567
        case value_kind_t::VALUE__MAX:
568
            ensure(0);
×
569
            break;
570
    }
571

572
    return {buffer};
956✔
573
}
574

575
string_fragment
576
logline_value::to_string_fragment(ArenaAlloc::Alloc<char>& alloc) const
1,093✔
577
{
578
    char buffer[128];
579

580
    switch (this->lv_meta.lvm_kind) {
1,093✔
581
        case value_kind_t::VALUE_NULL:
×
582
            return "null"_frag;
×
583

584
        case value_kind_t::VALUE_JSON:
1,093✔
585
        case value_kind_t::VALUE_XML:
586
        case value_kind_t::VALUE_STRUCT:
587
        case value_kind_t::VALUE_TEXT:
588
        case value_kind_t::VALUE_TIMESTAMP:
589
            if (this->lv_str) {
1,093✔
590
                return string_fragment::from_str(this->lv_str.value())
×
591
                    .to_owned(alloc);
×
592
            }
593
            if (this->lv_frag.empty()) {
1,093✔
594
                return this->lv_intern_string.to_string_fragment().to_owned(
×
595
                    alloc);
×
596
            }
597
            return this->lv_frag.to_owned(alloc);
1,093✔
598

599
        case value_kind_t::VALUE_QUOTED:
×
600
        case value_kind_t::VALUE_W3C_QUOTED:
601
            if (this->lv_frag.empty()) {
×
602
                return string_fragment{};
×
603
            } else {
604
                switch (this->lv_frag.data()[0]) {
×
605
                    case '\'':
×
606
                    case '"': {
607
                        auto unquote_func = this->lv_meta.lvm_kind
×
608
                                == value_kind_t::VALUE_W3C_QUOTED
609
                            ? unquote_w3c
×
610
                            : unquote;
611
                        stack_buf allocator;
×
612
                        auto* unquoted_str
613
                            = allocator.allocate(this->lv_frag.length());
×
614
                        size_t unquoted_len;
615

616
                        unquoted_len = unquote_func(unquoted_str,
×
617
                                                    this->lv_frag.data(),
618
                                                    this->lv_frag.length());
×
619
                        return string_fragment::from_bytes(unquoted_str,
×
620
                                                           unquoted_len)
621
                            .to_owned(alloc);
×
622
                    }
623
                    default:
×
624
                        return this->lv_frag.to_owned(alloc);
×
625
                }
626
            }
627
            break;
628

629
        case value_kind_t::VALUE_INTEGER:
×
630
            snprintf(buffer, sizeof(buffer), "%" PRId64, this->lv_value.i);
×
631
            break;
×
632

633
        case value_kind_t::VALUE_FLOAT:
×
634
            snprintf(buffer, sizeof(buffer), "%lf", this->lv_value.d);
×
635
            break;
×
636

637
        case value_kind_t::VALUE_BOOLEAN:
×
638
            if (this->lv_value.i) {
×
639
                return "true"_frag;
×
640
            }
641
            return "false"_frag;
×
642
            break;
643
        case value_kind_t::VALUE_UNKNOWN:
×
644
        case value_kind_t::VALUE__MAX:
645
            ensure(0);
×
646
            break;
647
    }
648

649
    return string_fragment::from_c_str(buffer).to_owned(alloc);
×
650
}
651

652
const char*
653
logline_value::text_value() const
68,635✔
654
{
655
    if (this->lv_str) {
68,635✔
656
        return this->lv_str->c_str();
186✔
657
    }
658
    if (this->lv_frag.empty()) {
68,449✔
659
        if (this->lv_intern_string.empty()) {
21✔
660
            return "";
21✔
661
        }
662
        return this->lv_intern_string.get();
×
663
    }
664
    return this->lv_frag.data();
68,428✔
665
}
666

667
size_t
668
logline_value::text_length() const
68,668✔
669
{
670
    if (this->lv_str) {
68,668✔
671
        return this->lv_str->size();
186✔
672
    }
673
    if (this->lv_frag.empty()) {
68,482✔
674
        return this->lv_intern_string.size();
21✔
675
    }
676
    return this->lv_frag.length();
68,461✔
677
}
678

679
string_fragment
680
logline_value::text_value_fragment() const
×
681
{
682
    return string_fragment::from_bytes(this->text_value(), this->text_length());
×
683
}
684

685
void
686
logline_value_vector::shift_origins_by(const line_range& cover, int32_t amount)
2✔
687
{
688
    for (auto& lv : this->lvv_values) {
14✔
689
        if (!lv.lv_origin.is_valid()) {
12✔
NEW
690
            continue;
×
691
        }
692
        lv.lv_origin.shift_range(cover, amount);
12✔
693
    }
694
}
2✔
695

696
void
697
logline_value_vector::clear()
42,324✔
698
{
699
    this->lvv_values.clear();
42,324✔
700
    this->lvv_sbr.disown();
42,324✔
701
    this->lvv_opid_value = std::nullopt;
42,324✔
702
    this->lvv_opid_provenance = opid_provenance::none;
42,324✔
703
    this->lvv_thread_id_value = std::nullopt;
42,324✔
704
}
42,324✔
705

706
logline_value_vector::logline_value_vector(const logline_value_vector& other)
906✔
707
    : lvv_sbr(other.lvv_sbr.clone()), lvv_values(other.lvv_values),
906✔
708
      lvv_opid_value(other.lvv_opid_value),
906✔
709
      lvv_opid_provenance(other.lvv_opid_provenance),
906✔
710
      lvv_thread_id_value(other.lvv_thread_id_value)
906✔
711
{
712
}
906✔
713

714
logline_value_vector&
715
logline_value_vector::operator=(const logline_value_vector& other)
425✔
716
{
717
    this->lvv_sbr = other.lvv_sbr.clone();
425✔
718
    this->lvv_values = other.lvv_values;
425✔
719
    this->lvv_opid_value = other.lvv_opid_value;
425✔
720
    this->lvv_opid_provenance = other.lvv_opid_provenance;
425✔
721
    this->lvv_thread_id_value = other.lvv_thread_id_value;
425✔
722

723
    return *this;
425✔
724
}
725

726
std::vector<std::shared_ptr<log_format>> log_format::lf_root_formats;
727

728
date_time_scanner
729
log_format::build_time_scanner() const
241✔
730
{
731
    date_time_scanner retval;
241✔
732

733
    retval.set_base_time(this->lf_date_time.dts_base_time,
241✔
734
                         this->lf_date_time.dts_base_tm.et_tm);
241✔
735
    if (this->lf_date_time.dts_default_zone != nullptr) {
241✔
736
        retval.dts_default_zone = this->lf_date_time.dts_default_zone;
×
737
    }
738
    retval.dts_zoned_to_local = this->lf_date_time.dts_zoned_to_local;
241✔
739

740
    return retval;
241✔
741
}
742

743
std::vector<std::shared_ptr<log_format>>&
744
log_format::get_root_formats()
17,949✔
745
{
746
    return lf_root_formats;
17,949✔
747
}
748

749
void
750
external_log_format::update_op_description(
5,033✔
751
    const std::vector<opid_descriptors*>& desc_defs_vec,
752
    log_op_description& lod,
753
    const pattern* fpat,
754
    const lnav::pcre2pp::match_data& md)
755
{
756
    std::optional<std::string> desc_elem_str;
5,033✔
757
    if (!lod.lod_index) {
5,033✔
758
        for (const auto& desc_defs : desc_defs_vec) {
3,074✔
759
            if (lod.lod_index) {
1,265✔
760
                break;
26✔
761
            }
762
            for (const auto& desc_def : *desc_defs->od_descriptors) {
1,547✔
763
                auto desc_field_index_iter = fpat->p_value_name_to_index.find(
1,337✔
764
                    desc_def.od_field.pp_value);
1,337✔
765

766
                if (desc_field_index_iter == fpat->p_value_name_to_index.end())
1,337✔
767
                {
768
                    continue;
43✔
769
                }
770

771
                auto desc_cap_opt = md[desc_field_index_iter->second];
1,333✔
772
                if (!desc_cap_opt) {
1,333✔
773
                    continue;
39✔
774
                }
775

776
                desc_elem_str = desc_def.matches(desc_cap_opt.value());
1,294✔
777
                if (desc_elem_str) {
1,294✔
778
                    lod.lod_index = desc_defs->od_index;
1,029✔
779
                    break;
1,029✔
780
                }
781
            }
782
        }
783
    }
784
    if (lod.lod_index) {
5,033✔
785
        const auto& desc_def_v
786
            = *desc_defs_vec[lod.lod_index.value()]->od_descriptors;
4,227✔
787
        auto& desc_v = lod.lod_elements;
4,227✔
788

789
        if (desc_def_v.size() == desc_v.size()
4,227✔
790
            || (this->elf_opid_field.empty() && !desc_v.empty()))
4,227✔
791
        {
792
            return;
3,198✔
793
        }
794
        for (size_t desc_def_index = 0; desc_def_index < desc_def_v.size();
2,938✔
795
             desc_def_index++)
796
        {
797
            const auto& desc_def = desc_def_v[desc_def_index];
1,909✔
798
            auto found_desc = desc_v.value_for(desc_def_index);
1,909✔
799
            auto desc_field_index_iter
800
                = fpat->p_value_name_to_index.find(desc_def.od_field.pp_value);
1,909✔
801

802
            if (desc_field_index_iter == fpat->p_value_name_to_index.end()) {
1,909✔
803
                continue;
36✔
804
            }
805
            auto desc_cap_opt = md[desc_field_index_iter->second];
1,909✔
806
            if (!desc_cap_opt) {
1,909✔
807
                continue;
36✔
808
            }
809

810
            if (!desc_elem_str) {
1,873✔
811
                desc_elem_str = desc_def.matches(desc_cap_opt.value());
844✔
812
            }
813
            if (desc_elem_str) {
1,873✔
814
                if (!found_desc) {
1,870✔
815
                    desc_v.insert(desc_def_index, desc_elem_str.value());
1,870✔
816
                } else if (!desc_elem_str->empty()) {
×
817
                    found_desc.value()->append(desc_def.od_joiner);
×
818
                    found_desc.value()->append(desc_elem_str.value());
×
819
                }
820
            }
821
            desc_elem_str = std::nullopt;
1,873✔
822
        }
823
    }
824
}
5,033✔
825

826
void
827
external_log_format::update_op_description(
3,436✔
828
    const std::vector<opid_descriptors*>& desc_defs_vec,
829
    log_op_description& lod)
830
{
831
    std::optional<std::string> desc_elem_str;
3,436✔
832
    if (!lod.lod_index) {
3,436✔
833
        for (const auto& desc_defs : desc_defs_vec) {
3,445✔
834
            if (lod.lod_index) {
15✔
835
                break;
×
836
            }
837
            for (const auto& desc_def : *desc_defs->od_descriptors) {
15✔
838
                auto desc_cap_iter
839
                    = this->lf_desc_captures.find(desc_def.od_field.pp_value);
15✔
840

841
                if (desc_cap_iter == this->lf_desc_captures.end()) {
15✔
842
                    continue;
×
843
                }
844
                desc_elem_str = desc_def.matches(desc_cap_iter->second);
15✔
845
                if (desc_elem_str) {
15✔
846
                    lod.lod_index = desc_defs->od_index;
15✔
847
                    break;
15✔
848
                }
849
            }
850
        }
851
    }
852
    if (lod.lod_index) {
3,436✔
853
        const auto& desc_def_v
854
            = *desc_defs_vec[lod.lod_index.value()]->od_descriptors;
21✔
855
        auto& desc_v = lod.lod_elements;
21✔
856

857
        if (desc_def_v.size() == desc_v.size()
21✔
858
            || (this->elf_opid_field.empty() && !desc_v.empty()))
21✔
859
        {
860
            return;
6✔
861
        }
862
        for (size_t desc_def_index = 0; desc_def_index < desc_def_v.size();
45✔
863
             desc_def_index++)
864
        {
865
            const auto& desc_def = desc_def_v[desc_def_index];
30✔
866
            auto found_desc = desc_v.value_for(desc_def_index);
30✔
867
            auto desc_cap_iter
868
                = this->lf_desc_captures.find(desc_def.od_field.pp_value);
30✔
869
            if (desc_cap_iter == this->lf_desc_captures.end()) {
30✔
870
                continue;
×
871
            }
872

873
            if (!desc_elem_str) {
30✔
874
                desc_elem_str = desc_def.matches(desc_cap_iter->second);
15✔
875
            }
876
            if (desc_elem_str) {
30✔
877
                if (!found_desc) {
30✔
878
                    desc_v.insert(desc_def_index, desc_elem_str.value());
30✔
879
                } else if (!desc_elem_str->empty()) {
×
880
                    found_desc.value()->append(desc_def.od_joiner);
×
881
                    found_desc.value()->append(desc_elem_str.value());
×
882
                }
883
            }
884
            desc_elem_str = std::nullopt;
30✔
885
        }
886
    }
887
}
3,436✔
888

889
static bool
890
next_format(
2,373,097✔
891
    const std::vector<std::shared_ptr<external_log_format::pattern>>& patterns,
892
    int& index,
893
    int& locked_index)
894
{
895
    bool retval = true;
2,373,097✔
896

897
    if (locked_index == -1) {
2,373,097✔
898
        index += 1;
2,365,628✔
899
        if (index >= (int) patterns.size()) {
2,365,628✔
900
            retval = false;
718,219✔
901
        }
902
    } else if (index == locked_index) {
7,469✔
903
        retval = false;
1✔
904
    } else {
905
        index = locked_index;
7,468✔
906
    }
907

908
    return retval;
2,373,097✔
909
}
910

911
bool
912
log_format::next_format(const pcre_format* fmt, int& index, int& locked_index)
181,937✔
913
{
914
    bool retval = true;
181,937✔
915

916
    if (locked_index == -1) {
181,937✔
917
        index += 1;
181,753✔
918
        if (fmt[index].name == nullptr) {
181,753✔
919
            retval = false;
10,219✔
920
        }
921
    } else if (index == locked_index) {
184✔
922
        retval = false;
36✔
923
    } else {
924
        index = locked_index;
148✔
925
    }
926

927
    return retval;
181,937✔
928
}
929

930
const char*
931
log_format::log_scanf(scan_batch_context& sbc,
12,702✔
932
                      uint32_t line_number,
933
                      string_fragment line,
934
                      const pcre_format* fmt,
935
                      const char* time_fmt[],
936
                      exttm* tm_out,
937
                      timeval* tv_out,
938

939
                      string_fragment* ts_out,
940
                      std::optional<string_fragment>* level_out)
941
{
942
    int curr_fmt = -1;
12,702✔
943
    const char* retval = nullptr;
12,702✔
944
    bool done = false;
12,702✔
945
    int pat_index = sbc.sbc_pattern_locks.last_pattern_index();
12,702✔
946

947
    while (!done && next_format(fmt, curr_fmt, pat_index)) {
184,384✔
948
        thread_local auto md = lnav::pcre2pp::match_data::unitialized();
171,682✔
949

950
        auto match_res = fmt[curr_fmt]
171,682✔
951
                             .pcre->capture_from(line)
171,682✔
952
                             .into(md)
171,682✔
953
                             .matches(PCRE2_NO_UTF_CHECK)
343,364✔
954
                             .ignore_error();
171,682✔
955
        if (!match_res) {
171,682✔
956
            retval = nullptr;
147,599✔
957
        } else {
958
            auto ts = md[fmt[curr_fmt].pf_timestamp_index];
24,083✔
959

960
            retval = this->lf_date_time.scan(
24,083✔
961
                ts->data(), ts->length(), nullptr, tm_out, *tv_out);
24,083✔
962

963
            if (retval == nullptr) {
24,083✔
964
                auto ls = this->lf_date_time.unlock();
21,636✔
965
                retval = this->lf_date_time.scan(
21,636✔
966
                    ts->data(), ts->length(), nullptr, tm_out, *tv_out);
21,636✔
967
                if (retval != nullptr) {
21,636✔
968
                    auto old_flags
×
969
                        = this->lf_timestamp_flags & DATE_TIME_SET_FLAGS;
×
970
                    auto new_flags = tm_out->et_flags & DATE_TIME_SET_FLAGS;
×
971

972
                    // It is unlikely a valid timestamp would lose much
973
                    // precision.
974
                    if (new_flags != old_flags) {
×
975
                        retval = nullptr;
×
976
                    }
977
                }
978
                if (retval == nullptr) {
21,636✔
979
                    this->lf_date_time.relock(ls);
21,636✔
980
                } else {
981
                    log_debug(
×
982
                        "%d: changed time format to '%s' due to %.*s",
983
                        line_number,
984
                        PTIMEC_FORMAT_STR[this->lf_date_time.dts_fmt_lock],
985
                        ts->length(),
986
                        ts->data());
987
                }
988
            }
989

990
            if (retval) {
24,083✔
991
                *ts_out = ts.value();
2,447✔
992
                if (md[2]) {
2,447✔
993
                    *level_out = md[2];
260✔
994
                } else {
995
                    *level_out = line.substr(md[0]->sf_end);
2,187✔
996
                }
997
                if (curr_fmt != pat_index) {
2,447✔
998
                    uint32_t lock_line;
999

1000
                    if (sbc.sbc_pattern_locks.empty()) {
2,335✔
1001
                        lock_line = 0;
2,335✔
1002
                    } else {
1003
                        lock_line = line_number;
×
1004
                    }
1005

1006
                    sbc.sbc_pattern_locks.pl_lines.emplace_back(lock_line,
2,335✔
1007
                                                                curr_fmt);
1008
                }
1009
                this->lf_timestamp_flags = tm_out->et_flags;
2,447✔
1010
                done = true;
2,447✔
1011
            }
1012
        }
1013
    }
1014

1015
    return retval;
12,702✔
1016
}
1017

1018
void
1019
log_format::annotate(logfile* lf,
37,706✔
1020
                     uint64_t line_number,
1021
                     string_attrs_t& sa,
1022
                     logline_value_vector& values) const
1023
{
1024
    if (lf != nullptr && !values.lvv_opid_value) {
37,706✔
1025
        const auto& bm = lf->get_bookmark_metadata();
2,997✔
1026
        auto bm_iter = bm.find(line_number);
2,997✔
1027
        if (bm_iter != bm.end() && !bm_iter->second.bm_opid.empty()) {
2,997✔
1028
            values.lvv_opid_value = bm_iter->second.bm_opid;
6✔
1029
            values.lvv_opid_provenance
1030
                = logline_value_vector::opid_provenance::user;
6✔
1031
        }
1032
    }
1033
}
37,706✔
1034

1035
void
1036
log_format::check_for_new_year(std::vector<logline>& dst,
1,698✔
1037
                               exttm etm,
1038
                               timeval log_tv) const
1039
{
1040
    if (dst.empty()) {
1,698✔
1041
        return;
253✔
1042
    }
1043

1044
    time_t diff
1045
        = dst.back().get_time<std::chrono::seconds>().count() - log_tv.tv_sec;
1,445✔
1046
    int off_year = 0, off_month = 0, off_day = 0, off_hour = 0;
1,445✔
1047
    bool do_change = true;
1,445✔
1048

1049
    if (diff <= 0) {
1,445✔
1050
        return;
1,385✔
1051
    }
1052
    if ((etm.et_flags & ETF_MONTH_SET) && diff >= (24 * 60 * 60)) {
60✔
1053
        off_year = 1;
11✔
1054
    } else if (diff >= (24 * 60 * 60)) {
49✔
1055
        off_month = 1;
2✔
1056
    } else if (!(etm.et_flags & ETF_DAY_SET) && (diff >= (60 * 60))) {
47✔
1057
        off_day = 1;
1✔
1058
    } else if (!(etm.et_flags & ETF_HOUR_SET) && (diff >= 60)) {
46✔
1059
        off_hour = 1;
4✔
1060
    } else {
1061
        do_change = false;
42✔
1062
    }
1063

1064
    if (!do_change) {
60✔
1065
        return;
42✔
1066
    }
1067
    log_debug("%zu:detected time rollover; offsets=%d %d %d %d",
18✔
1068
              dst.size(),
1069
              off_year,
1070
              off_month,
1071
              off_day,
1072
              off_hour);
1073
    for (auto& ll : dst) {
68✔
1074
        time_t ot = ll.get_time<std::chrono::seconds>().count();
50✔
1075
        struct tm otm;
1076

1077
        gmtime_r(&ot, &otm);
50✔
1078
        otm.tm_yday = -1;
50✔
1079
        if (otm.tm_year < off_year) {
50✔
1080
            otm.tm_year = 0;
×
1081
        } else {
1082
            otm.tm_year -= off_year;
50✔
1083
        }
1084
        otm.tm_mon -= off_month;
50✔
1085
        if (otm.tm_mon < 0) {
50✔
1086
            otm.tm_mon += 12;
2✔
1087
        }
1088
        auto new_time = tm2sec(&otm);
50✔
1089
        if (new_time == -1) {
50✔
1090
            continue;
×
1091
        }
1092
        new_time -= (off_day * 24 * 60 * 60) + (off_hour * 60 * 60);
50✔
1093
        auto old_sub = ll.get_subsecond_time<std::chrono::microseconds>();
50✔
1094
        ll.set_time(std::chrono::seconds{new_time});
50✔
1095
        ll.set_subsecond_time(old_sub);
50✔
1096
    }
1097
}
1098

1099
/*
1100
 * XXX This needs some cleanup.
1101
 */
1102
struct json_log_userdata {
1103
    json_log_userdata(shared_buffer_ref& sbr, scan_batch_context* sbc)
15,248✔
1104
        : jlu_shared_buffer(sbr), jlu_batch_context(sbc)
30,496✔
1105
    {
1106
    }
15,248✔
1107

1108
    const external_log_format::value_def* get_field_def(
464,059✔
1109
        yajlpp_parse_context* ypc)
1110
    {
1111
        const auto field_frag = ypc->get_path_as_string_fragment();
464,059✔
1112
        auto* format = this->jlu_format;
464,059✔
1113

1114
        if (this->jlu_read_order_index < format->elf_value_def_read_order.size()
464,059✔
1115
            && format->elf_value_def_read_order[this->jlu_read_order_index]
630,565✔
1116
                    .first
1117
                == field_frag)
166,506✔
1118
        {
1119
            auto retval
1120
                = format->elf_value_def_read_order[this->jlu_read_order_index++]
159,536✔
1121
                      .second;
159,536✔
1122
            if (retval != nullptr) {
159,536✔
1123
                this->jlu_precision += 1;
21,475✔
1124
            }
1125
            return retval;
159,536✔
1126
        }
1127

1128
        format->elf_value_def_read_order.resize(this->jlu_read_order_index);
304,523✔
1129
        auto vd_iter = format->elf_value_def_frag_map.find(field_frag);
304,523✔
1130
        if (vd_iter != format->elf_value_def_frag_map.end()) {
304,523✔
1131
            format->elf_value_def_read_order.emplace_back(vd_iter->first,
256,439✔
1132
                                                          vd_iter->second);
256,439✔
1133
            this->jlu_read_order_index += 1;
256,439✔
1134
            if (vd_iter->second != nullptr) {
256,439✔
1135
                this->jlu_precision += 1;
17,095✔
1136
            }
1137
            return vd_iter->second;
256,439✔
1138
        }
1139

1140
        auto owned_frag = field_frag.to_owned(format->elf_allocator);
48,084✔
1141
        format->elf_value_def_frag_map[owned_frag] = nullptr;
48,084✔
1142
        format->elf_value_def_read_order.emplace_back(owned_frag, nullptr);
48,084✔
1143
        this->jlu_read_order_index += 1;
48,084✔
1144
        return nullptr;
48,084✔
1145
    }
1146

1147
    void add_sub_lines_for(const external_log_format::value_def* vd,
326,800✔
1148
                           bool top_level,
1149
                           std::optional<double> val,
1150
                           const unsigned char* str,
1151
                           ssize_t len,
1152
                           yajl_string_props_t* props)
1153
    {
1154
        auto res = this->jlu_format->value_line_count(
326,800✔
1155
            *this->jlu_batch_context, vd, top_level, val, str, len, props);
326,800✔
1156
        this->jlu_has_ansi |= res.vlcr_has_ansi;
326,800✔
1157
        if (!res.vlcr_valid_utf) {
326,800✔
1158
            this->jlu_valid_utf = false;
×
1159
        }
1160
        this->jlu_sub_line_count += res.vlcr_count;
326,800✔
1161
        this->jlu_quality += res.vlcr_line_format_count;
326,800✔
1162
        if (res.vlcr_line_format_index) {
326,800✔
1163
            this->jlu_format_hits[res.vlcr_line_format_index.value()] = true;
3,989✔
1164
        }
1165
    }
326,800✔
1166

1167
    external_log_format* jlu_format{nullptr};
1168
    const logline* jlu_line{nullptr};
1169
    logline* jlu_base_line{nullptr};
1170
    int jlu_sub_line_count{1};
1171
    bool jlu_has_ansi{false};
1172
    bool jlu_valid_utf{true};
1173
    yajl_handle jlu_handle{nullptr};
1174
    const char* jlu_line_value{nullptr};
1175
    size_t jlu_line_size{0};
1176
    std::stack<size_t> jlu_sub_start;
1177
    uint32_t jlu_quality{0};
1178
    uint32_t jlu_strikes{0};
1179
    uint32_t jlu_precision{0};
1180
    std::vector<bool> jlu_format_hits;
1181
    shared_buffer_ref& jlu_shared_buffer;
1182
    scan_batch_context* jlu_batch_context;
1183
    std::optional<string_fragment> jlu_opid_frag;
1184
    std::optional<string_fragment> jlu_opid_desc_frag;
1185
    std::optional<string_fragment> jlu_tid_frag;
1186
    std::optional<int64_t> jlu_tid_number;
1187
    std::optional<std::string> jlu_subid;
1188
    std::optional<log_format::scan_error> jlu_scan_error;
1189
    hasher jlu_opid_hasher;
1190
    std::chrono::microseconds jlu_duration{0};
15,248✔
1191
    exttm jlu_exttm;
1192
    size_t jlu_read_order_index{0};
1193
    subline_options jlu_subline_opts;
1194
};
1195

1196
static int read_json_field(yajlpp_parse_context* ypc,
1197
                           const unsigned char* str,
1198
                           size_t len,
1199
                           yajl_string_props_t*);
1200

1201
static int
1202
read_json_null(yajlpp_parse_context* ypc)
5,054✔
1203
{
1204
    json_log_userdata* jlu = (json_log_userdata*) ypc->ypc_userdata;
5,054✔
1205
    const auto* vd = jlu->get_field_def(ypc);
5,054✔
1206

1207
    jlu->add_sub_lines_for(
10,108✔
1208
        vd, ypc->is_level(1), std::nullopt, nullptr, -1, nullptr);
5,054✔
1209

1210
    return 1;
5,054✔
1211
}
1212

1213
static int
1214
read_json_bool(yajlpp_parse_context* ypc, int val)
24,006✔
1215
{
1216
    json_log_userdata* jlu = (json_log_userdata*) ypc->ypc_userdata;
24,006✔
1217
    const auto* vd = jlu->get_field_def(ypc);
24,006✔
1218

1219
    jlu->add_sub_lines_for(
48,012✔
1220
        vd, ypc->is_level(1), std::nullopt, nullptr, -1, nullptr);
24,006✔
1221

1222
    return 1;
24,006✔
1223
}
1224

1225
static int
1226
read_json_number(yajlpp_parse_context* ypc,
37,803✔
1227
                 const char* numberVal,
1228
                 size_t numberLen)
1229
{
1230
    auto* jlu = (json_log_userdata*) ypc->ypc_userdata;
37,803✔
1231
    auto number_frag = string_fragment::from_bytes(numberVal, numberLen);
37,803✔
1232
    std::optional<double> val;
37,803✔
1233

1234
    intern_string_t field_name;
37,803✔
1235
    const auto* vd = jlu->get_field_def(ypc);
37,803✔
1236
    if (vd != nullptr) {
37,803✔
1237
        field_name = vd->vd_meta.lvm_name;
1,811✔
1238
    }
1239

1240
    if (field_name.empty()) {
37,803✔
1241
    } else if (jlu->jlu_format->lf_timestamp_field == field_name) {
1,811✔
1242
        long long divisor = jlu->jlu_format->elf_timestamp_divisor;
113✔
1243
        auto scan_res = scn::scan_value<double>(number_frag.to_string_view());
113✔
1244
        if (!scan_res) {
113✔
1245
            log_error("invalid number %.*s", (int) numberLen, numberVal);
×
1246
            return 0;
×
1247
        }
1248
        auto ts_val = scan_res.value().value();
113✔
1249
        timeval tv;
1250
        tv.tv_sec = ts_val / divisor;
113✔
1251
        tv.tv_usec = fmod(ts_val, divisor) * (1000000.0 / divisor);
113✔
1252
        jlu->jlu_format->lf_date_time.to_localtime(tv.tv_sec, jlu->jlu_exttm);
113✔
1253
        tv.tv_sec = tm2sec(&jlu->jlu_exttm.et_tm);
113✔
1254
        jlu->jlu_exttm.et_gmtoff
1255
            = jlu->jlu_format->lf_date_time.dts_local_offset_cache;
113✔
1256
        jlu->jlu_exttm.et_flags
113✔
1257
            |= ETF_MACHINE_ORIENTED | ETF_SUB_NOT_IN_FORMAT | ETF_ZONE_SET;
113✔
1258
        if (divisor == 1000) {
113✔
1259
            jlu->jlu_exttm.et_flags |= ETF_MILLIS_SET;
17✔
1260
        } else {
1261
            jlu->jlu_exttm.et_flags |= ETF_MICROS_SET;
96✔
1262
        }
1263
        jlu->jlu_exttm.et_nsec = tv.tv_usec * 1000;
113✔
1264
        jlu->jlu_base_line->set_time(tv);
113✔
1265
    } else if (jlu->jlu_format->lf_subsecond_field == field_name) {
1,698✔
1266
        auto scan_res = scn::scan_value<double>(number_frag.to_string_view());
3✔
1267
        if (!scan_res) {
3✔
1268
            log_error("invalid number %.*s", (int) numberLen, numberVal);
×
1269
            return 0;
×
1270
        }
1271
        auto ts_val = scan_res.value().value();
3✔
1272

1273
        uint64_t millis = 0;
3✔
1274
        jlu->jlu_exttm.et_flags &= ~(ETF_MICROS_SET | ETF_MILLIS_SET);
3✔
1275
        switch (jlu->jlu_format->lf_subsecond_unit.value()) {
3✔
1276
            case log_format::subsecond_unit::milli:
×
1277
                millis = ts_val;
×
1278
                jlu->jlu_exttm.et_nsec = ts_val * 1000000;
×
1279
                jlu->jlu_exttm.et_flags |= ETF_MILLIS_SET;
×
1280
                break;
×
1281
            case log_format::subsecond_unit::micro:
×
1282
                millis = std::chrono::duration_cast<std::chrono::milliseconds>(
×
1283
                             std::chrono::microseconds((int64_t) ts_val))
×
1284
                             .count();
×
1285
                jlu->jlu_exttm.et_nsec = ts_val * 1000;
×
1286
                jlu->jlu_exttm.et_flags |= ETF_MICROS_SET;
×
1287
                break;
×
1288
            case log_format::subsecond_unit::nano:
3✔
1289
                millis = std::chrono::duration_cast<std::chrono::milliseconds>(
3✔
1290
                             std::chrono::nanoseconds((int64_t) ts_val))
3✔
1291
                             .count();
3✔
1292
                jlu->jlu_exttm.et_nsec = ts_val;
3✔
1293
                jlu->jlu_exttm.et_flags |= ETF_NANOS_SET;
3✔
1294
                break;
3✔
1295
        }
1296
        jlu->jlu_exttm.et_flags |= ETF_SUB_NOT_IN_FORMAT;
3✔
1297
        jlu->jlu_base_line->set_subsecond_time(
3✔
1298
            std::chrono::milliseconds(millis));
1299
    } else if (jlu->jlu_format->elf_level_field == field_name) {
1,695✔
1300
        if (jlu->jlu_format->elf_level_pairs.empty()) {
516✔
1301
            jlu->jlu_base_line->set_level(jlu->jlu_format->convert_level(
321✔
1302
                number_frag, jlu->jlu_batch_context));
1303
        } else {
1304
            auto scan_res
1305
                = scn::scan_int<int64_t>(number_frag.to_string_view());
195✔
1306
            if (!scan_res) {
195✔
1307
                log_error("invalid number %.*s", (int) numberLen, numberVal);
×
1308
                return 0;
×
1309
            }
1310
            auto level_int = scan_res.value().value();
195✔
1311

1312
            for (const auto& pair : jlu->jlu_format->elf_level_pairs) {
705✔
1313
                if (pair.first == level_int) {
632✔
1314
                    jlu->jlu_base_line->set_level(pair.second);
122✔
1315
                    break;
122✔
1316
                }
1317
            }
1318
        }
1319
    } else if (vd != nullptr) {
1,179✔
1320
        if (jlu->jlu_format->elf_thread_id_field == field_name) {
1,179✔
1321
            auto& sbc = *jlu->jlu_batch_context;
124✔
1322
            auto tid_iter = sbc.sbc_tids.ltis_tid_ranges.find(number_frag);
124✔
1323
            if (tid_iter == sbc.sbc_tids.ltis_tid_ranges.end()) {
124✔
1324
                jlu->jlu_tid_frag = number_frag.to_owned(sbc.sbc_allocator);
71✔
1325
            } else {
1326
                jlu->jlu_tid_frag = tid_iter->first;
53✔
1327
            }
1328
        }
1329
        if ((vd->vd_meta.lvm_kind == value_kind_t::VALUE_INTEGER
1,179✔
1330
             || vd->vd_meta.lvm_kind == value_kind_t::VALUE_FLOAT)
154✔
1331
            && !vd->vd_meta.lvm_foreign_key && !vd->vd_meta.lvm_identifier)
1,043✔
1332
        {
1333
            auto scan_res
1334
                = scn::scan_value<double>(number_frag.to_string_view());
182✔
1335
            if (!scan_res) {
182✔
1336
                log_error("invalid number %.*s", (int) numberLen, numberVal);
×
1337
                return 0;
×
1338
            }
1339
            val = scan_res.value().value();
182✔
1340
            if (jlu->jlu_format->elf_duration_field == field_name) {
182✔
1341
                jlu->jlu_duration = std::chrono::microseconds(
×
1342
                    static_cast<int64_t>(val.value() * 1000000));
18✔
1343
            }
1344
        }
1345
    }
1346

1347
    jlu->add_sub_lines_for(vd,
37,803✔
1348
                           ypc->is_level(1),
37,803✔
1349
                           val,
1350
                           (const unsigned char*) numberVal,
1351
                           numberLen,
1352
                           nullptr);
1353

1354
    return 1;
37,803✔
1355
}
1356

1357
static int
1358
json_array_start(void* ctx)
65,506✔
1359
{
1360
    auto* ypc = (yajlpp_parse_context*) ctx;
65,506✔
1361
    auto* jlu = (json_log_userdata*) ypc->ypc_userdata;
65,506✔
1362

1363
    jlu->jlu_sub_start.push(yajl_get_bytes_consumed(jlu->jlu_handle) - 1);
65,506✔
1364
    if (ypc->ypc_path_index_stack.size() == 2) {
65,506✔
1365
        const auto* vd = jlu->get_field_def(ypc);
20,162✔
1366

1367
        jlu->add_sub_lines_for(vd, true, std::nullopt, nullptr, -1, nullptr);
20,162✔
1368
    }
1369

1370
    return 1;
65,506✔
1371
}
1372

1373
static int
1374
json_array_start_const(void* ctx)
9,699✔
1375
{
1376
    auto* ypc = (yajlpp_parse_context*) ctx;
9,699✔
1377
    auto* jlu = (json_log_userdata*) ypc->ypc_userdata;
9,699✔
1378

1379
    jlu->jlu_sub_start.push(yajl_get_bytes_consumed(jlu->jlu_handle) - 1);
9,699✔
1380

1381
    return 1;
9,699✔
1382
}
1383

1384
static int
1385
json_array_end(void* ctx)
9,679✔
1386
{
1387
    auto* ypc = (yajlpp_parse_context*) ctx;
9,679✔
1388
    auto* jlu = (json_log_userdata*) ypc->ypc_userdata;
9,679✔
1389
    const auto* vd = jlu->get_field_def(ypc);
9,679✔
1390

1391
    if (ypc->ypc_path_index_stack.size() == 1 || vd != nullptr) {
9,679✔
1392
        intern_string_t field_name;
2,594✔
1393
        if (vd != nullptr) {
2,594✔
1394
            field_name = vd->vd_meta.lvm_name;
2,204✔
1395
        } else {
1396
            field_name = ypc->get_path();
390✔
1397
        }
1398
        auto sub_start = jlu->jlu_sub_start.top();
2,594✔
1399
        size_t sub_end = yajl_get_bytes_consumed(jlu->jlu_handle);
2,594✔
1400
        auto json_frag = string_fragment::from_byte_range(
2,594✔
1401
            jlu->jlu_shared_buffer.get_data(), sub_start, sub_end);
2,594✔
1402
        jlu->jlu_format->jlf_line_values.lvv_values.emplace_back(
2,594✔
1403
            jlu->jlu_format->get_value_meta(field_name,
5,188✔
1404
                                            value_kind_t::VALUE_JSON),
1405
            json_frag);
1406
        if (field_name == jlu->jlu_format->elf_opid_field) {
2,594✔
1407
            jlu->jlu_opid_desc_frag = json_frag;
29✔
1408
        }
1409
    }
1410
    jlu->jlu_sub_start.pop();
9,679✔
1411

1412
    return 1;
9,679✔
1413
}
1414

1415
static int
1416
read_array_end(void* ctx)
65,262✔
1417
{
1418
    auto* ypc = (yajlpp_parse_context*) ctx;
65,262✔
1419
    auto* jlu = (json_log_userdata*) ypc->ypc_userdata;
65,262✔
1420
    const auto* vd = jlu->get_field_def(ypc);
65,262✔
1421

1422
    if (ypc->ypc_path_index_stack.size() == 1 || vd != nullptr) {
65,262✔
1423
        const intern_string_t field_name = ypc->get_path_fragment_i(0);
20,131✔
1424
        auto sub_start = jlu->jlu_sub_start.top();
20,131✔
1425
        jlu->jlu_sub_start.pop();
20,131✔
1426
        size_t sub_end = yajl_get_bytes_consumed(jlu->jlu_handle);
20,131✔
1427
        auto json_frag = string_fragment::from_byte_range(
20,131✔
1428
            jlu->jlu_shared_buffer.get_data(), sub_start, sub_end);
20,131✔
1429
        if (field_name == jlu->jlu_format->elf_opid_field) {
20,131✔
1430
            jlu->jlu_opid_desc_frag = json_frag;
2,403✔
1431
        }
1432
    }
1433

1434
    return 1;
65,262✔
1435
}
1436

1437
static const json_path_container json_log_handlers = {
1438
    yajlpp::pattern_property_handler("\\w+")
1439
        .add_cb(read_json_null)
1440
        .add_cb(read_json_bool)
1441
        .add_cb(read_json_number)
1442
        .add_cb(read_json_field),
1443
};
1444

1445
static int rewrite_json_field(yajlpp_parse_context* ypc,
1446
                              const unsigned char* str,
1447
                              size_t len,
1448
                              yajl_string_props_t*);
1449

1450
static int
1451
rewrite_json_null(yajlpp_parse_context* ypc)
1,290✔
1452
{
1453
    auto jlu = (json_log_userdata*) ypc->ypc_userdata;
1,290✔
1454
    const auto* vd = jlu->get_field_def(ypc);
1,290✔
1455

1456
    if (!ypc->is_level(1) && vd == nullptr) {
1,290✔
1457
        return 1;
1,228✔
1458
    }
1459
    jlu->jlu_format->jlf_line_values.lvv_values.emplace_back(
62✔
1460
        jlu->jlu_format->get_value_meta(ypc, vd, value_kind_t::VALUE_NULL));
124✔
1461

1462
    return 1;
62✔
1463
}
1464

1465
static int
1466
rewrite_json_bool(yajlpp_parse_context* ypc, int val)
5,639✔
1467
{
1468
    auto* jlu = (json_log_userdata*) ypc->ypc_userdata;
5,639✔
1469
    const auto* vd = jlu->get_field_def(ypc);
5,639✔
1470

1471
    if (!ypc->is_level(1) && vd == nullptr) {
5,639✔
1472
        return 1;
5,007✔
1473
    }
1474
    jlu->jlu_format->jlf_line_values.lvv_values.emplace_back(
632✔
1475
        jlu->jlu_format->get_value_meta(ypc, vd, value_kind_t::VALUE_BOOLEAN),
632✔
1476
        (bool) val);
632✔
1477
    return 1;
632✔
1478
}
1479

1480
static int
1481
rewrite_json_int(yajlpp_parse_context* ypc, long long val)
7,462✔
1482
{
1483
    auto* jlu = (json_log_userdata*) ypc->ypc_userdata;
7,462✔
1484
    const auto* vd = jlu->get_field_def(ypc);
7,462✔
1485

1486
    if (vd != nullptr) {
7,462✔
1487
        const intern_string_t field_name = vd->vd_meta.lvm_name;
790✔
1488
        if (jlu->jlu_format->lf_timestamp_field == field_name) {
790✔
1489
            long long divisor = jlu->jlu_format->elf_timestamp_divisor;
22✔
1490
            timeval tv;
1491

1492
            tv.tv_sec = val / divisor;
22✔
1493
            tv.tv_usec = fmod(val, divisor) * (1000000.0 / divisor);
22✔
1494
            jlu->jlu_format->lf_date_time.to_localtime(tv.tv_sec,
22✔
1495
                                                       jlu->jlu_exttm);
22✔
1496
            jlu->jlu_exttm.et_gmtoff
1497
                = jlu->jlu_format->lf_date_time.dts_local_offset_cache;
22✔
1498
            jlu->jlu_exttm.et_flags |= ETF_MACHINE_ORIENTED
22✔
1499
                | ETF_SUB_NOT_IN_FORMAT | ETF_ZONE_SET | ETF_Z_FOR_UTC;
1500
            if (divisor == 1) {
22✔
1501
                jlu->jlu_exttm.et_flags |= ETF_MICROS_SET;
4✔
1502
            } else {
1503
                jlu->jlu_exttm.et_flags |= ETF_MILLIS_SET;
18✔
1504
            }
1505
            jlu->jlu_exttm.et_nsec = tv.tv_usec * 1000;
22✔
1506
        } else if (jlu->jlu_format->lf_subsecond_field == field_name) {
768✔
1507
            jlu->jlu_exttm.et_flags &= ~(ETF_MICROS_SET | ETF_MILLIS_SET);
4✔
1508
            switch (jlu->jlu_format->lf_subsecond_unit.value()) {
4✔
1509
                case log_format::subsecond_unit::milli:
×
1510
                    jlu->jlu_exttm.et_nsec = val * 1000000;
×
1511
                    jlu->jlu_exttm.et_flags |= ETF_MILLIS_SET;
×
1512
                    break;
×
1513
                case log_format::subsecond_unit::micro:
×
1514
                    jlu->jlu_exttm.et_nsec = val * 1000;
×
1515
                    jlu->jlu_exttm.et_flags |= ETF_MICROS_SET;
×
1516
                    break;
×
1517
                case log_format::subsecond_unit::nano:
4✔
1518
                    jlu->jlu_exttm.et_nsec = val;
4✔
1519
                    jlu->jlu_exttm.et_flags |= ETF_NANOS_SET;
4✔
1520
                    break;
4✔
1521
            }
1522
            jlu->jlu_exttm.et_flags |= ETF_SUB_NOT_IN_FORMAT;
4✔
1523
        }
1524
    }
1525

1526
    if (!ypc->is_level(1) && vd == nullptr) {
7,462✔
1527
        return 1;
6,625✔
1528
    }
1529
    if (vd != nullptr
837✔
1530
        && vd->vd_meta.lvm_name == jlu->jlu_format->elf_thread_id_field)
837✔
1531
    {
1532
        jlu->jlu_tid_number = val;
156✔
1533
    }
1534
    jlu->jlu_format->jlf_line_values.lvv_values.emplace_back(
837✔
1535
        jlu->jlu_format->get_value_meta(ypc, vd, value_kind_t::VALUE_INTEGER),
837✔
1536
        (int64_t) val);
837✔
1537
    return 1;
837✔
1538
}
1539

1540
static int
1541
rewrite_json_double(yajlpp_parse_context* ypc, double val)
142✔
1542
{
1543
    auto* jlu = (json_log_userdata*) ypc->ypc_userdata;
142✔
1544
    const auto* vd = jlu->get_field_def(ypc);
142✔
1545

1546
    if (vd != nullptr) {
142✔
1547
        const intern_string_t field_name = vd->vd_meta.lvm_name;
138✔
1548
        if (jlu->jlu_format->lf_timestamp_field == field_name) {
138✔
1549
            long long divisor = jlu->jlu_format->elf_timestamp_divisor;
118✔
1550
            timeval tv;
1551

1552
            tv.tv_sec = val / divisor;
118✔
1553
            tv.tv_usec = fmod(val, divisor) * (1000000.0 / divisor);
118✔
1554
            jlu->jlu_format->lf_date_time.to_localtime(tv.tv_sec,
118✔
1555
                                                       jlu->jlu_exttm);
118✔
1556
            jlu->jlu_exttm.et_gmtoff
1557
                = jlu->jlu_format->lf_date_time.dts_local_offset_cache;
118✔
1558
            jlu->jlu_exttm.et_flags |= ETF_MACHINE_ORIENTED
118✔
1559
                | ETF_SUB_NOT_IN_FORMAT | ETF_ZONE_SET | ETF_Z_FOR_UTC;
1560
            if (divisor == 1) {
118✔
1561
                jlu->jlu_exttm.et_flags |= ETF_MICROS_SET;
110✔
1562
            } else {
1563
                jlu->jlu_exttm.et_flags |= ETF_MILLIS_SET;
8✔
1564
            }
1565
            jlu->jlu_exttm.et_nsec = tv.tv_usec * 1000;
118✔
1566
        } else if (jlu->jlu_format->lf_subsecond_field == field_name) {
20✔
1567
            jlu->jlu_exttm.et_flags &= ~(ETF_MICROS_SET | ETF_MILLIS_SET);
×
1568
            switch (jlu->jlu_format->lf_subsecond_unit.value()) {
×
1569
                case log_format::subsecond_unit::milli:
×
1570
                    jlu->jlu_exttm.et_nsec = val * 1000000;
×
1571
                    jlu->jlu_exttm.et_flags |= ETF_MILLIS_SET;
×
1572
                    break;
×
1573
                case log_format::subsecond_unit::micro:
×
1574
                    jlu->jlu_exttm.et_nsec = val * 1000;
×
1575
                    jlu->jlu_exttm.et_flags |= ETF_MICROS_SET;
×
1576
                    break;
×
1577
                case log_format::subsecond_unit::nano:
×
1578
                    jlu->jlu_exttm.et_nsec = val;
×
1579
                    jlu->jlu_exttm.et_flags |= ETF_NANOS_SET;
×
1580
                    break;
×
1581
            }
1582
            jlu->jlu_exttm.et_flags |= ETF_SUB_NOT_IN_FORMAT;
×
1583
        } else if (jlu->jlu_format->elf_duration_field == field_name) {
20✔
1584
            jlu->jlu_duration = std::chrono::microseconds(
40✔
1585
                static_cast<int64_t>(val * 1000000.0));
20✔
1586
        }
1587
    }
1588

1589
    if (!ypc->is_level(1) && vd == nullptr) {
142✔
1590
        return 1;
×
1591
    }
1592
    jlu->jlu_format->jlf_line_values.lvv_values.emplace_back(
142✔
1593
        jlu->jlu_format->get_value_meta(ypc, vd, value_kind_t::VALUE_FLOAT),
284✔
1594
        val);
1595

1596
    return 1;
142✔
1597
}
1598

1599
static const json_path_container json_log_rewrite_handlers = {
1600
    yajlpp::pattern_property_handler("\\w+")
1601
        .add_cb(rewrite_json_null)
1602
        .add_cb(rewrite_json_bool)
1603
        .add_cb(rewrite_json_int)
1604
        .add_cb(rewrite_json_double)
1605
        .add_cb(rewrite_json_field),
1606
};
1607

1608
bool
1609
external_log_format::scan_for_partial(const log_format_file_state& lffs,
1✔
1610
                                      shared_buffer_ref& sbr,
1611
                                      size_t& len_out) const
1612
{
1613
    if (this->elf_type != elf_type_t::ELF_TYPE_TEXT) {
1✔
1614
        return false;
×
1615
    }
1616

1617
    const auto& pat
1618
        = this->elf_pattern_order[lffs.lffs_pattern_locks.last_pattern_index()];
1✔
1619
    if (!this->lf_multiline) {
1✔
1620
        len_out = pat->p_pcre.pp_value->match_partial(sbr.to_string_fragment());
1✔
1621
        return true;
1✔
1622
    }
1623

1624
    if (pat->p_timestamp_end == -1 || pat->p_timestamp_end > (int) sbr.length())
×
1625
    {
1626
        len_out = 0;
×
1627
        return false;
×
1628
    }
1629

1630
    len_out = pat->p_pcre.pp_value->match_partial(sbr.to_string_fragment());
×
1631
    return (int) len_out > pat->p_timestamp_end;
×
1632
}
1633

1634
std::vector<lnav::console::snippet>
1635
external_log_format::get_snippets() const
23✔
1636
{
1637
    std::vector<lnav::console::snippet> retval;
23✔
1638

1639
    for (const auto& src_pair : this->elf_format_sources) {
46✔
1640
        retval.emplace_back(lnav::console::snippet::from(src_pair.first, "")
46✔
1641
                                .with_line(src_pair.second));
23✔
1642
    }
1643

1644
    return retval;
23✔
1645
}
×
1646

1647
log_format::scan_result_t
1648
external_log_format::scan_json(std::vector<logline>& dst,
181,782✔
1649
                               const line_info& li,
1650
                               shared_buffer_ref& sbr,
1651
                               scan_batch_context& sbc)
1652
{
1653
    logline ll(
1654
        li.li_file_range.fr_offset, std::chrono::microseconds{0}, LEVEL_INFO);
181,782✔
1655
    auto line_frag = sbr.to_string_fragment();
181,782✔
1656

1657
    if (!line_frag.startswith("{")) {
181,782✔
1658
        if (!this->lf_specialized) {
168,803✔
1659
            return scan_no_match{"line is not a JSON object"};
168,798✔
1660
        }
1661

1662
        if (!dst.empty()) {
5✔
1663
            ll.set_time(dst.back().get_time<std::chrono::microseconds>());
5✔
1664
        }
1665
        ll.set_level(LEVEL_INVALID);
5✔
1666
        dst.emplace_back(ll);
5✔
1667
        return scan_match{0};
5✔
1668
    }
1669

1670
    auto& ypc = *(this->jlf_parse_context);
12,979✔
1671
    yajl_handle handle = this->jlf_yajl_handle.get();
12,979✔
1672
    json_log_userdata jlu(sbr, &sbc);
12,979✔
1673

1674
    if (li.li_partial) {
12,979✔
1675
        log_debug("skipping partial line at offset %lld",
28✔
1676
                  li.li_file_range.fr_offset);
1677
        if (this->lf_specialized) {
28✔
1678
            if (!dst.empty()) {
2✔
1679
                ll.set_time(dst.back().get_time<std::chrono::microseconds>());
2✔
1680
            }
1681
            ll.set_level(LEVEL_INVALID);
2✔
1682
            dst.emplace_back(ll);
2✔
1683
        }
1684
        return scan_incomplete{};
28✔
1685
    }
1686

1687
    const auto* line_data = (const unsigned char*) sbr.get_data();
12,951✔
1688

1689
    this->lf_desc_captures.clear();
12,951✔
1690
    this->lf_desc_allocator.reset();
12,951✔
1691

1692
    yajl_reset(handle);
12,951✔
1693
    ypc.set_static_handler(json_log_handlers.jpc_children[0]);
12,951✔
1694
    ypc.ypc_userdata = &jlu;
12,951✔
1695
    ypc.ypc_ignore_unused = true;
12,951✔
1696
    ypc.ypc_alt_callbacks.yajl_start_array = json_array_start;
12,951✔
1697
    ypc.ypc_alt_callbacks.yajl_start_map = json_array_start;
12,951✔
1698
    ypc.ypc_alt_callbacks.yajl_end_array = read_array_end;
12,951✔
1699
    ypc.ypc_alt_callbacks.yajl_end_map = read_array_end;
12,951✔
1700
    jlu.jlu_format = this;
12,951✔
1701
    jlu.jlu_base_line = &ll;
12,951✔
1702
    jlu.jlu_line_value = sbr.get_data();
12,951✔
1703
    jlu.jlu_line_size = sbr.length();
12,951✔
1704
    jlu.jlu_handle = handle;
12,951✔
1705
    jlu.jlu_format_hits.resize(this->jlf_line_format.size());
12,951✔
1706
    if (yajl_parse(handle, line_data, sbr.length()) == yajl_status_ok
12,951✔
1707
        && yajl_complete_parse(handle) == yajl_status_ok)
12,951✔
1708
    {
1709
        if (jlu.jlu_scan_error) {
12,738✔
1710
            if (this->lf_specialized) {
19✔
1711
                if (!dst.empty()) {
10✔
1712
                    ll.set_time(
10✔
1713
                        dst.back().get_time<std::chrono::microseconds>());
10✔
1714
                }
1715
                ll.set_level(LEVEL_INVALID);
10✔
1716
                dst.emplace_back(ll);
10✔
1717
                return scan_match{0};
10✔
1718
            }
1719
            return jlu.jlu_scan_error.value();
9✔
1720
        }
1721
        if (ll.get_time<std::chrono::microseconds>().count() == 0) {
12,719✔
1722
            if (this->lf_specialized) {
8,449✔
1723
                if (!dst.empty()) {
×
1724
                    ll.set_time(
×
1725
                        dst.back().get_time<std::chrono::microseconds>());
×
1726
                }
1727
                ll.set_ignore(true);
×
1728
                dst.emplace_back(ll);
×
1729
                return scan_match{0};
×
1730
            }
1731

1732
            return scan_no_match{
8,449✔
1733
                "JSON message does not have expected timestamp property"};
8,449✔
1734
        }
1735

1736
        if (jlu.jlu_tid_frag) {
4,270✔
1737
            this->jlf_line_values.lvv_thread_id_value
1738
                = jlu.jlu_tid_frag->to_string();
124✔
1739
            auto tid_iter = sbc.sbc_tids.insert_tid(
248✔
1740
                sbc.sbc_allocator,
1741
                jlu.jlu_tid_frag.value(),
124✔
1742
                ll.get_time<std::chrono::microseconds>());
124✔
1743
            tid_iter->second.titr_level_stats.update_msg_count(
124✔
1744
                ll.get_msg_level());
1745
            ll.merge_bloom_bits(jlu.jlu_tid_frag->bloom_bits());
124✔
1746
        } else {
1747
            auto tid_iter = sbc.sbc_tids.insert_tid(
4,146✔
1748
                sbc.sbc_allocator,
1749
                string_fragment{},
×
1750
                ll.get_time<std::chrono::microseconds>());
4,146✔
1751
            tid_iter->second.titr_level_stats.update_msg_count(
4,146✔
1752
                ll.get_msg_level());
1753
        }
1754

1755
        auto found_opid_desc = false;
4,270✔
1756
        if (this->elf_opid_field.empty()
4,270✔
1757
            && this->lf_opid_source.value_or(opid_source_t::from_description)
799✔
1758
                == opid_source_t::from_description
1759
            && this->lf_opid_description_def->size() == 1)
5,069✔
1760
        {
1761
            const auto& od = this->lf_opid_description_def->begin()->second;
322✔
1762
            for (const auto& desc : *od.od_descriptors) {
966✔
1763
                auto desc_iter
1764
                    = this->lf_desc_captures.find(desc.od_field.pp_value);
644✔
1765
                if (desc_iter == this->lf_desc_captures.end()) {
644✔
1766
                    continue;
602✔
1767
                }
1768
                jlu.jlu_opid_hasher.update(desc_iter->second);
42✔
1769
                found_opid_desc = true;
42✔
1770
            }
1771

1772
        } else if (!jlu.jlu_opid_desc_frag && !jlu.jlu_opid_frag
5,493✔
1773
                   && jlu.jlu_duration > 0us)
5,493✔
1774
        {
1775
            jlu.jlu_opid_hasher.update(sbr.to_string_fragment());
×
1776
        }
1777

1778
        if (jlu.jlu_opid_desc_frag || jlu.jlu_duration > 0us
6,137✔
1779
            || (found_opid_desc && this->lf_opid_description_def->size() == 1))
6,137✔
1780
        {
1781
            char buf[hasher::STRING_SIZE];
1782
            jlu.jlu_opid_hasher.to_string(buf);
2,424✔
1783
            auto opid_frag = string_fragment::from_bytes(buf, sizeof(buf) - 1);
2,424✔
1784
            auto opid_iter = sbc.sbc_opids.los_opid_ranges.find(opid_frag);
2,424✔
1785
            if (opid_iter == sbc.sbc_opids.los_opid_ranges.end()) {
2,424✔
1786
                jlu.jlu_opid_frag = opid_frag.to_owned(sbc.sbc_allocator);
2,412✔
1787
            } else {
1788
                jlu.jlu_opid_frag = opid_iter->first;
12✔
1789
            }
1790
        }
1791

1792
        if (jlu.jlu_opid_frag) {
4,270✔
1793
            ll.merge_bloom_bits(jlu.jlu_opid_frag->bloom_bits());
3,436✔
1794
            this->jlf_line_values.lvv_opid_value
1795
                = jlu.jlu_opid_frag->to_string();
3,436✔
1796
            this->jlf_line_values.lvv_opid_provenance
1797
                = logline_value_vector::opid_provenance::file;
3,436✔
1798
            auto opid_iter = sbc.sbc_opids.insert_op(
6,872✔
1799
                sbc.sbc_allocator,
1800
                jlu.jlu_opid_frag.value(),
3,436✔
1801
                ll.get_time<std::chrono::microseconds>(),
3,436✔
1802
                this->lf_timestamp_point_of_reference,
1803
                jlu.jlu_duration);
1804
            opid_iter->second.otr_level_stats.update_msg_count(
3,436✔
1805
                ll.get_msg_level());
1806
            auto& elems = opid_iter->second.otr_description.lod_elements;
3,436✔
1807
            if (jlu.jlu_opid_desc_frag && elems.empty()) {
3,436✔
1808
                elems.insert(0,
×
1809
                             fmt::format(FMT_STRING(" {}"),
9,588✔
1810
                                         jlu.jlu_opid_desc_frag.value()));
1811
            }
1812

1813
            if (jlu.jlu_subid) {
3,436✔
1814
                auto subid_frag
1815
                    = string_fragment::from_str(jlu.jlu_subid.value());
×
1816

1817
                auto* ostr = sbc.sbc_opids.sub_op_in_use(
×
1818
                    sbc.sbc_allocator,
1819
                    opid_iter,
1820
                    subid_frag,
1821
                    ll.get_time<std::chrono::microseconds>(),
×
1822
                    ll.get_msg_level());
1823
                if (ostr != nullptr && ostr->ostr_description.empty()) {
×
1824
                    log_op_description sub_desc;
×
1825
                    this->update_op_description(
×
1826
                        *this->lf_subid_description_def_vec, sub_desc);
×
1827
                    if (!sub_desc.lod_elements.empty()) {
×
1828
                        auto& sub_desc_def
1829
                            = this->lf_subid_description_def_vec->at(
×
1830
                                sub_desc.lod_index.value());
×
1831
                        ostr->ostr_description
1832
                            = sub_desc_def->to_string(sub_desc.lod_elements);
×
1833
                    }
1834
                }
1835
            }
1836

1837
            auto& otr = opid_iter->second;
3,436✔
1838
            this->update_op_description(*this->lf_opid_description_def_vec,
3,436✔
1839
                                        otr.otr_description);
3,436✔
1840
        } else {
1841
            this->jlf_line_values.lvv_opid_value = std::nullopt;
834✔
1842
        }
1843

1844
        jlu.jlu_sub_line_count += this->jlf_line_format_init_count;
4,270✔
1845
        ll.set_has_ansi(jlu.jlu_has_ansi);
4,270✔
1846
        ll.set_valid_utf(jlu.jlu_valid_utf);
4,270✔
1847
        for (int lpc = 0; lpc < jlu.jlu_sub_line_count; lpc++) {
26,301✔
1848
            ll.set_sub_offset(lpc);
22,031✔
1849
            ll.set_continued(lpc > 0);
22,031✔
1850
            dst.emplace_back(ll);
22,031✔
1851
        }
1852
        this->lf_timestamp_flags = jlu.jlu_exttm.et_flags;
4,270✔
1853

1854
        if (!this->lf_specialized) {
4,270✔
1855
            static const intern_string_t ts_field
1856
                = intern_string::lookup("__timestamp__", -1);
3,673✔
1857
            static const intern_string_t level_field
1858
                = intern_string::lookup("__level__");
5,267✔
1859
            for (const auto& [index, jfe] :
34,279✔
1860
                 lnav::itertools::enumerate(this->jlf_line_format))
37,952✔
1861
            {
1862
                if (jfe.jfe_type != json_log_field::VARIABLE
81,674✔
1863
                    || jfe.jfe_value.pp_value == ts_field
19,817✔
1864
                    || jfe.jfe_value.pp_value == level_field
16,225✔
1865
                    || jfe.jfe_default_value != "-")
50,423✔
1866
                {
1867
                    continue;
20,462✔
1868
                }
1869
                if (!jlu.jlu_format_hits[index]) {
10,144✔
1870
                    jlu.jlu_strikes += 1;
9,604✔
1871
                }
1872
            }
1873
        }
1874
    } else {
1875
        unsigned char* msg;
1876
        int line_count = 2;
213✔
1877

1878
        msg = yajl_get_error(
426✔
1879
            handle, 1, (const unsigned char*) sbr.get_data(), sbr.length());
213✔
1880
        if (msg != nullptr) {
213✔
1881
            auto msg_frag = string_fragment::from_c_str(msg);
213✔
1882
            log_debug("Unable to parse line at offset %lld: %.*s",
213✔
1883
                      li.li_file_range.fr_offset,
1884
                      msg_frag.length(),
1885
                      msg_frag.data());
1886
            line_count = msg_frag.count('\n') + 1;
213✔
1887
            yajl_free_error(handle, msg);
213✔
1888
        }
1889
        if (!this->lf_specialized || dst.empty()) {
213✔
1890
            return scan_no_match{"JSON parsing failed"};
210✔
1891
        }
1892
        for (int lpc = 0; lpc < line_count; lpc++) {
15✔
1893
            log_level_t level = LEVEL_INVALID;
12✔
1894

1895
            ll.set_time(dst.back().get_timeval());
12✔
1896
            ll.set_continued(lpc > 0);
12✔
1897
            ll.set_level(level);
12✔
1898
            ll.set_sub_offset(lpc);
12✔
1899
            dst.emplace_back(ll);
12✔
1900
        }
1901
    }
1902

1903
    if (jlu.jlu_quality > 0) {
4,273✔
1904
        jlu.jlu_quality += 3000;
924✔
1905
    }
1906
    return scan_match{jlu.jlu_quality, jlu.jlu_strikes, jlu.jlu_precision};
4,273✔
1907
}
12,979✔
1908

1909
log_format::scan_result_t
1910
external_log_format::scan(logfile& lf,
902,930✔
1911
                          std::vector<logline>& dst,
1912
                          const line_info& li,
1913
                          shared_buffer_ref& sbr,
1914
                          scan_batch_context& sbc)
1915
{
1916
    if (dst.empty()) {
902,930✔
1917
        auto file_options = lf.get_file_options();
36,962✔
1918

1919
        if (file_options) {
36,962✔
1920
            this->lf_date_time.dts_default_zone
1921
                = file_options->second.fo_default_zone.pp_value;
2,400✔
1922
        } else {
1923
            this->lf_date_time.dts_default_zone = nullptr;
34,562✔
1924
        }
1925
    }
36,962✔
1926

1927
    sbc.sbc_value_stats.resize(this->elf_value_defs.size());
902,930✔
1928
    if (this->elf_type == elf_type_t::ELF_TYPE_JSON) {
902,930✔
1929
        return this->scan_json(dst, li, sbr, sbc);
178,592✔
1930
    }
1931

1932
    int curr_fmt = -1, orig_lock = sbc.sbc_pattern_locks.last_pattern_index();
724,338✔
1933
    int pat_index = orig_lock;
724,338✔
1934
    auto line_sf = sbr.to_string_fragment();
724,338✔
1935
    thread_local auto md = lnav::pcre2pp::match_data::unitialized();
724,338✔
1936
    char tmp_opid_buf[hasher::STRING_SIZE];
1937

1938
    while (::next_format(this->elf_pattern_order, curr_fmt, pat_index)) {
2,373,097✔
1939
        auto* fpat = this->elf_pattern_order[curr_fmt].get();
1,654,877✔
1940
        auto* pat = fpat->p_pcre.pp_value.get();
1,654,877✔
1941

1942
        auto found_match
1943
            = pat->capture_from(line_sf).into(md).found_p(PCRE2_NO_UTF_CHECK);
1,654,877✔
1944
        if (!found_match) {
1,654,877✔
1945
            if (!sbc.sbc_pattern_locks.empty() && pat_index != -1) {
1,648,756✔
1946
                curr_fmt = -1;
2,012✔
1947
                pat_index = -1;
2,012✔
1948
            }
1949
            continue;
1,648,759✔
1950
        }
1951

1952
        auto ts = md[fpat->p_timestamp_field_index];
6,121✔
1953
        auto level_cap = md[fpat->p_level_field_index];
6,121✔
1954
        auto opid_cap = md[fpat->p_opid_field_index];
6,121✔
1955
        const char* last;
1956
        exttm log_time_tm;
6,121✔
1957
        timeval log_tv;
1958
        uint64_t opid_bloom = 0;
6,121✔
1959
        char combined_datetime_buf[512];
1960

1961
        if (fpat->p_time_field_index != -1) {
6,121✔
1962
            auto time_cap = md[fpat->p_time_field_index];
×
1963
            if (ts && time_cap) {
×
1964
                auto ts_str_len = snprintf(combined_datetime_buf,
×
1965
                                           sizeof(combined_datetime_buf),
1966
                                           "%.*sT%.*s",
1967
                                           ts->length(),
1968
                                           ts->data(),
1969
                                           time_cap->length(),
1970
                                           time_cap->data());
1971
                ts = string_fragment::from_bytes(combined_datetime_buf,
×
1972
                                                 ts_str_len);
×
1973
            }
1974
        }
1975

1976
        auto level = this->convert_level(
6,121✔
1977
            level_cap.value_or(string_fragment::invalid()), &sbc);
6,121✔
1978

1979
        if (!ts) {
6,121✔
1980
            level = log_level_t::LEVEL_INVALID;
×
1981
        } else if ((last
6,121✔
1982
                    = this->lf_date_time.scan(ts->data(),
12,242✔
1983
                                              ts->length(),
6,121✔
1984
                                              this->get_timestamp_formats(),
1985
                                              &log_time_tm,
1986
                                              log_tv))
1987
                   == nullptr)
6,121✔
1988
        {
1989
            auto ls = this->lf_date_time.unlock();
13✔
1990
            if ((last = this->lf_date_time.scan(ts->data(),
26✔
1991
                                                ts->length(),
13✔
1992
                                                this->get_timestamp_formats(),
1993
                                                &log_time_tm,
1994
                                                log_tv))
1995
                == nullptr)
13✔
1996
            {
1997
                this->lf_date_time.relock(ls);
2✔
1998
                continue;
3✔
1999
            }
2000
            if (last != nullptr) {
11✔
2001
                auto old_flags = this->lf_timestamp_flags & DATE_TIME_SET_FLAGS;
11✔
2002
                auto new_flags = log_time_tm.et_flags & DATE_TIME_SET_FLAGS;
11✔
2003

2004
                // It is unlikely a valid timestamp would lose much
2005
                // precision.
2006
                if (new_flags != old_flags) {
11✔
2007
                    continue;
1✔
2008
                }
2009
            }
2010

2011
            log_debug("%s:%zu: date-time re-locked to %d",
10✔
2012
                      lf.get_unique_path().c_str(),
2013
                      dst.size(),
2014
                      this->lf_date_time.dts_fmt_lock);
2015
        }
2016

2017
        this->lf_timestamp_flags = log_time_tm.et_flags;
6,118✔
2018

2019
        if (!(this->lf_timestamp_flags
12,236✔
2020
              & (ETF_MILLIS_SET | ETF_MICROS_SET | ETF_NANOS_SET))
6,118✔
2021
            && !dst.empty()
5,402✔
2022
            && dst.back().get_time<std::chrono::seconds>().count()
4,626✔
2023
                == log_tv.tv_sec
4,626✔
2024
            && dst.back()
15,012✔
2025
                    .get_subsecond_time<std::chrono::milliseconds>()
9,610✔
2026
                    .count()
3,492✔
2027
                != 0)
2028
        {
2029
            auto log_ms
2030
                = dst.back().get_subsecond_time<std::chrono::microseconds>();
×
2031

2032
            log_time_tm.et_nsec
2033
                = std::chrono::duration_cast<std::chrono::nanoseconds>(log_ms)
×
2034
                      .count();
×
2035
            log_tv.tv_usec
2036
                = std::chrono::duration_cast<std::chrono::microseconds>(log_ms)
×
2037
                      .count();
×
2038
        }
2039

2040
        if (!((log_time_tm.et_flags & ETF_DAY_SET)
6,118✔
2041
              && (log_time_tm.et_flags & ETF_MONTH_SET)
6,077✔
2042
              && (log_time_tm.et_flags & ETF_YEAR_SET)))
6,077✔
2043
        {
2044
            this->check_for_new_year(dst, log_time_tm, log_tv);
966✔
2045
        }
2046

2047
        auto log_us = to_us(log_tv);
6,118✔
2048
        if (this->elf_opid_field.empty()
6,118✔
2049
            && !fpat->p_opid_description_field_indexes.empty())
6,118✔
2050
        {
2051
            auto empty_desc = true;
4,054✔
2052
            hasher h;
4,054✔
2053
            for (const auto& fidx : fpat->p_opid_description_field_indexes) {
12,162✔
2054
                auto desc_cap = md[fidx];
8,108✔
2055
                if (desc_cap) {
8,108✔
2056
                    h.update(desc_cap.value());
8,060✔
2057
                    empty_desc = false;
8,060✔
2058
                }
2059
            }
2060
            if (!empty_desc) {
4,054✔
2061
                h.to_string(tmp_opid_buf);
4,054✔
2062
                opid_cap = string_fragment::from_bytes(
8,108✔
2063
                    tmp_opid_buf, sizeof(tmp_opid_buf) - 1);
4,054✔
2064
            }
2065
        }
2066

2067
        auto duration_cap = md[fpat->p_duration_field_index];
6,118✔
2068
        if (duration_cap && !opid_cap) {
6,118✔
2069
            hasher h;
86✔
2070
            h.update(line_sf);
86✔
2071
            h.to_string(tmp_opid_buf);
86✔
2072
            opid_cap = string_fragment::from_bytes(tmp_opid_buf,
172✔
2073
                                                   sizeof(tmp_opid_buf) - 1);
86✔
2074
        }
2075
        if (opid_cap && !opid_cap->empty()) {
6,118✔
2076
            auto duration = std::chrono::microseconds{0};
4,992✔
2077
            if (duration_cap) {
4,992✔
2078
                auto from_res
2079
                    = humanize::try_from<double>(duration_cap.value());
86✔
2080
                if (from_res) {
86✔
2081
                    duration = std::chrono::microseconds(
×
2082
                        static_cast<int64_t>(from_res.value() * 1000000));
86✔
2083
                }
2084
            }
2085
            auto opid_iter
2086
                = sbc.sbc_opids.insert_op(sbc.sbc_allocator,
9,984✔
2087
                                          opid_cap.value(),
4,992✔
2088
                                          log_us,
2089
                                          this->lf_timestamp_point_of_reference,
2090
                                          duration);
2091
            auto& otr = opid_iter->second;
4,992✔
2092

2093
            otr.otr_level_stats.update_msg_count(level);
4,992✔
2094
            if (fpat->p_subid_field_index != -1) {
4,992✔
2095
                auto subid_cap = md[fpat->p_subid_field_index];
62✔
2096
                if (subid_cap && !subid_cap->empty()) {
62✔
2097
                    auto* ostr = sbc.sbc_opids.sub_op_in_use(sbc.sbc_allocator,
186✔
2098
                                                             opid_iter,
2099
                                                             subid_cap.value(),
62✔
2100
                                                             log_us,
2101
                                                             level);
2102
                    if (ostr != nullptr && ostr->ostr_description.empty()) {
62✔
2103
                        log_op_description sub_desc;
41✔
2104
                        this->update_op_description(
41✔
2105
                            *this->lf_subid_description_def_vec,
41✔
2106
                            sub_desc,
2107
                            fpat,
2108
                            md);
2109
                        if (!sub_desc.lod_elements.empty()) {
41✔
2110
                            auto& sub_desc_def
2111
                                = this->lf_subid_description_def_vec->at(
39✔
2112
                                    sub_desc.lod_index.value());
39✔
2113
                            ostr->ostr_description = sub_desc_def->to_string(
78✔
2114
                                sub_desc.lod_elements);
39✔
2115
                        }
2116
                    }
41✔
2117
                }
2118
            }
2119
            this->update_op_description(*this->lf_opid_description_def_vec,
4,992✔
2120
                                        otr.otr_description,
4,992✔
2121
                                        fpat,
2122
                                        md);
2123
            opid_bloom = opid_cap->bloom_bits();
4,992✔
2124
        }
2125

2126
        for (const auto& ivd : fpat->p_value_by_index) {
68,363✔
2127
            if (!ivd.ivd_value_def->vd_meta.lvm_values_index) {
62,245✔
2128
                continue;
11,543✔
2129
            }
2130

2131
            ssize_t cap_size = md.capture_size(ivd.ivd_index);
50,702✔
2132
            auto& lvs = sbc.sbc_value_stats[ivd.ivd_value_def->vd_meta
50,702✔
2133
                                                .lvm_values_index.value()];
50,702✔
2134

2135
            if (cap_size > lvs.lvs_width) {
50,702✔
2136
                lvs.lvs_width = cap_size;
6,734✔
2137
            }
2138
        }
2139

2140
        for (auto value_index : fpat->p_numeric_value_indexes) {
11,012✔
2141
            const indexed_value_def& ivd = fpat->p_value_by_index[value_index];
4,894✔
2142
            const value_def& vd = *ivd.ivd_value_def;
4,894✔
2143
            auto num_cap = md[ivd.ivd_index];
4,894✔
2144

2145
            if (vd.vd_meta.lvm_identifier) {
4,894✔
2146
                continue;
×
2147
            }
2148

2149
            if (num_cap && num_cap->is_valid()) {
4,894✔
2150
                const scaling_factor* scaling = nullptr;
4,828✔
2151

2152
                if (ivd.ivd_unit_field_index >= 0) {
4,828✔
2153
                    auto unit_cap = md[ivd.ivd_unit_field_index];
80✔
2154

2155
                    if (unit_cap && unit_cap->is_valid()) {
80✔
2156
                        intern_string_t unit_val
2157
                            = intern_string::lookup(unit_cap.value());
80✔
2158

2159
                        auto unit_iter = vd.vd_unit_scaling.find(unit_val);
80✔
2160
                        if (unit_iter != vd.vd_unit_scaling.end()) {
80✔
2161
                            const auto& sf = unit_iter->second;
80✔
2162

2163
                            scaling = &sf;
80✔
2164
                        }
2165
                    }
2166
                }
2167

2168
                std::optional<double> dvalue_opt;
4,828✔
2169
                switch (vd.vd_meta.lvm_kind) {
4,828✔
2170
                    case value_kind_t::VALUE_INTEGER: {
4,662✔
2171
                        auto scan_res
2172
                            = scn::scan_int<int64_t>(num_cap->to_string_view());
4,662✔
2173
                        if (scan_res) {
4,662✔
2174
                            dvalue_opt = scan_res->value();
4,662✔
2175
                        }
2176
                        break;
4,662✔
2177
                    }
2178
                    case value_kind_t::VALUE_FLOAT: {
166✔
2179
                        auto scan_res = scn::scan_value<double>(
2180
                            num_cap->to_string_view());
166✔
2181
                        if (scan_res) {
166✔
2182
                            dvalue_opt = scan_res->value();
166✔
2183
                        }
2184
                        break;
166✔
2185
                    }
2186
                    default:
×
2187
                        break;
×
2188
                }
2189
                if (dvalue_opt) {
4,828✔
2190
                    auto dvalue = dvalue_opt.value();
4,828✔
2191
                    if (scaling != nullptr) {
4,828✔
2192
                        scaling->scale(dvalue);
80✔
2193
                    }
2194
                    sbc.sbc_value_stats[vd.vd_meta.lvm_values_index.value()]
4,828✔
2195
                        .add_value(dvalue);
4,828✔
2196
                }
2197
            }
2198
        }
2199

2200
        dst.emplace_back(li.li_file_range.fr_offset, log_us, level);
6,118✔
2201
        auto& new_line = dst.back();
6,118✔
2202
        new_line.merge_bloom_bits(opid_bloom);
6,118✔
2203

2204
        auto src_file_cap = md[fpat->p_src_file_field_index];
6,118✔
2205
        auto src_line_cap = md[fpat->p_src_line_field_index];
6,118✔
2206
        if (src_file_cap && src_line_cap) {
6,118✔
2207
            auto h = hasher();
222✔
2208
            h.update(this->get_name().c_str());
222✔
2209
            h.update(src_file_cap.value());
222✔
2210
            h.update(src_line_cap.value());
222✔
2211
            new_line.merge_bloom_bits(h.to_bloom_bits());
222✔
2212
            new_line.set_schema_computed(true);
222✔
2213
        }
2214
        auto thread_id_cap = md[fpat->p_thread_id_field_index];
6,118✔
2215
        if (thread_id_cap) {
6,118✔
2216
            auto tid_iter = sbc.sbc_tids.insert_tid(
2,434✔
2217
                sbc.sbc_allocator, thread_id_cap.value(), log_us);
1,217✔
2218
            tid_iter->second.titr_level_stats.update_msg_count(level);
1,217✔
2219
            new_line.merge_bloom_bits(thread_id_cap->bloom_bits());
1,217✔
2220
        } else {
2221
            auto tid_iter = sbc.sbc_tids.insert_tid(
4,901✔
2222
                sbc.sbc_allocator, string_fragment{}, log_us);
×
2223
            tid_iter->second.titr_level_stats.update_msg_count(level);
4,901✔
2224
        }
2225

2226
        if (orig_lock != curr_fmt) {
6,118✔
2227
            uint32_t lock_line;
2228

2229
            if (!this->lf_specialized && orig_lock != -1) {
663✔
2230
                log_debug("%s:%zu: changing pattern lock %d -> (%d)%s",
×
2231
                          lf.get_unique_path().c_str(),
2232
                          dst.size() - 1,
2233
                          orig_lock,
2234
                          curr_fmt,
2235
                          this->elf_pattern_order[curr_fmt]->p_name.c_str());
2236
            }
2237
            if (sbc.sbc_pattern_locks.empty()) {
663✔
2238
                lock_line = 0;
637✔
2239
            } else {
2240
                lock_line = dst.size() - 1;
26✔
2241
            }
2242
            sbc.sbc_pattern_locks.pl_lines.emplace_back(lock_line, curr_fmt);
663✔
2243
        }
2244
        return scan_match{1000};
6,118✔
2245
    }
2246

2247
    if (this->lf_specialized && !this->lf_multiline && !dst.empty()) {
718,220✔
2248
        const auto& last_line = dst.back();
1✔
2249

2250
        log_debug("%s: invalid line %zu file_offset=%" PRIu64,
1✔
2251
                  lf.get_filename().c_str(),
2252
                  dst.size(),
2253
                  li.li_file_range.fr_offset);
2254
        dst.emplace_back(li.li_file_range.fr_offset,
1✔
2255
                         last_line.get_time<std::chrono::microseconds>(),
×
2256
                         log_level_t::LEVEL_INVALID);
1✔
2257

2258
        return scan_match{0};
1✔
2259
    }
2260

2261
    return scan_no_match{"no patterns matched"};
718,219✔
2262
}
2263

2264
void
2265
external_log_format::annotate(logfile* lf,
7,604✔
2266
                              uint64_t line_number,
2267
                              string_attrs_t& sa,
2268
                              logline_value_vector& values) const
2269
{
2270
    thread_local auto md = lnav::pcre2pp::match_data::unitialized();
7,604✔
2271

2272
    auto& line = values.lvv_sbr;
7,604✔
2273
    line_range lr;
7,604✔
2274

2275
    line.erase_ansi();
7,604✔
2276
    if (this->elf_type != elf_type_t::ELF_TYPE_TEXT) {
7,604✔
2277
        if (this->jlf_cached_opts.full_message) {
803✔
2278
            values = this->jlf_line_values;
337✔
2279
            sa = this->jlf_attr_line.al_attrs;
337✔
2280
        } else {
2281
            values.lvv_sbr = this->jlf_line_values.lvv_sbr.clone();
466✔
2282
            for (const auto& llv : this->jlf_line_values.lvv_values) {
4,743✔
2283
                if (this->jlf_cached_sub_range.contains(llv.lv_origin)) {
4,277✔
2284
                    values.lvv_values.emplace_back(llv);
953✔
2285
                    values.lvv_values.back().lv_origin.shift(
953✔
2286
                        this->jlf_cached_sub_range.lr_start,
953✔
2287
                        -this->jlf_cached_sub_range.lr_start);
953✔
2288
                }
2289
            }
2290
            for (const auto& attr : this->jlf_attr_line.al_attrs) {
2,695✔
2291
                if (this->jlf_cached_sub_range.contains(attr.sa_range)) {
2,229✔
2292
                    sa.emplace_back(attr);
757✔
2293
                    sa.back().sa_range.shift(
757✔
2294
                        this->jlf_cached_sub_range.lr_start,
757✔
2295
                        -this->jlf_cached_sub_range.lr_start);
757✔
2296
                }
2297
            }
2298
            values.lvv_opid_value = this->jlf_line_values.lvv_opid_value;
466✔
2299
            values.lvv_opid_provenance
2300
                = this->jlf_line_values.lvv_opid_provenance;
466✔
2301
            values.lvv_thread_id_value
2302
                = this->jlf_line_values.lvv_thread_id_value;
466✔
2303
        }
2304
        log_format::annotate(lf, line_number, sa, values);
803✔
2305
        return;
2,453✔
2306
    }
2307

2308
    if (line.empty()) {
6,801✔
2309
        return;
5✔
2310
    }
2311

2312
    values.lvv_values.reserve(this->elf_value_defs.size());
6,796✔
2313

2314
    auto lffs = lf->get_format_file_state();
6,796✔
2315
    int pat_index = lffs.lffs_pattern_locks.pattern_index_for_line(line_number);
6,796✔
2316
    const auto& pat = *this->elf_pattern_order[pat_index];
6,796✔
2317
    char tmp_opid_buf[hasher::STRING_SIZE];
2318

2319
    sa.reserve(pat.p_pcre.pp_value->get_capture_count());
6,796✔
2320
    auto match_res
2321
        = pat.p_pcre.pp_value->capture_from(line.to_string_fragment())
6,796✔
2322
              .into(md)
6,796✔
2323
              .matches(PCRE2_NO_UTF_CHECK)
13,592✔
2324
              .ignore_error();
6,796✔
2325
    if (!match_res) {
6,796✔
2326
        // A continued line still needs a body.
2327
        lr.lr_start = 0;
1,645✔
2328
        lr.lr_end = line.length();
1,645✔
2329
        sa.emplace_back(lr, SA_BODY.value());
1,645✔
2330
        if (!this->lf_multiline) {
1,645✔
2331
            auto len
2332
                = pat.p_pcre.pp_value->match_partial(line.to_string_fragment());
×
2333
            sa.emplace_back(
×
2334
                line_range{(int) len, -1},
×
2335
                SA_INVALID.value("Log line does not match any pattern"));
×
2336
        }
2337
        return;
1,645✔
2338
    }
2339

2340
    auto duration_cap = md[pat.p_duration_field_index];
5,151✔
2341

2342
    auto ts_cap = md[pat.p_timestamp_field_index];
5,151✔
2343
    if (ts_cap) {
5,151✔
2344
        sa.emplace_back(to_line_range(ts_cap.value()), L_TIMESTAMP.value());
5,151✔
2345
    }
2346

2347
    auto opid_cap = md[pat.p_opid_field_index];
5,151✔
2348

2349
    if (this->elf_opid_field.empty()
5,151✔
2350
        && !pat.p_opid_description_field_indexes.empty())
5,151✔
2351
    {
2352
        auto empty_desc = true;
3,793✔
2353
        hasher h;
3,793✔
2354
        for (const auto& fidx : pat.p_opid_description_field_indexes) {
11,379✔
2355
            auto desc_cap = md[fidx];
7,586✔
2356
            if (desc_cap) {
7,586✔
2357
                h.update(desc_cap.value());
7,558✔
2358
                empty_desc = false;
7,558✔
2359
            }
2360
        }
2361
        if (!empty_desc) {
3,793✔
2362
            h.to_string(tmp_opid_buf);
3,793✔
2363
            opid_cap = string_fragment::from_bytes(tmp_opid_buf,
7,586✔
2364
                                                   sizeof(tmp_opid_buf) - 1);
3,793✔
2365
        }
2366
    } else if (duration_cap && !opid_cap) {
1,358✔
2367
        hasher h;
3✔
2368
        h.update(line.to_string_fragment());
3✔
2369
        h.to_string(tmp_opid_buf);
3✔
2370
        opid_cap = string_fragment::from_bytes(tmp_opid_buf,
6✔
2371
                                               sizeof(tmp_opid_buf) - 1);
3✔
2372
    }
2373
    if (opid_cap && !opid_cap->empty()) {
5,151✔
2374
        sa.emplace_back(to_line_range(opid_cap.value()), L_OPID.value());
4,276✔
2375
        values.lvv_opid_value = opid_cap->to_string();
4,276✔
2376
        values.lvv_opid_provenance
2377
            = logline_value_vector::opid_provenance::file;
4,276✔
2378
    }
2379

2380
    auto body_cap = md[pat.p_body_field_index];
5,151✔
2381
    auto level_cap = md[pat.p_level_field_index];
5,151✔
2382
    auto src_file_cap = md[pat.p_src_file_field_index];
5,151✔
2383
    auto src_line_cap = md[pat.p_src_line_field_index];
5,151✔
2384
    auto thread_id_cap = md[pat.p_thread_id_field_index];
5,151✔
2385

2386
    if (level_cap
5,151✔
2387
        && (!body_cap
10,256✔
2388
            || (body_cap && level_cap->sf_begin != body_cap->sf_begin)))
10,256✔
2389
    {
2390
        sa.emplace_back(to_line_range(level_cap.value()), L_LEVEL.value());
4,677✔
2391
    }
2392

2393
    if (src_file_cap) {
5,151✔
2394
        sa.emplace_back(to_line_range(src_file_cap.value()),
158✔
2395
                        SA_SRC_FILE.value());
316✔
2396
    }
2397
    if (src_line_cap) {
5,151✔
2398
        sa.emplace_back(to_line_range(src_line_cap.value()),
158✔
2399
                        SA_SRC_LINE.value());
316✔
2400
    }
2401
    if (thread_id_cap) {
5,151✔
2402
        sa.emplace_back(to_line_range(thread_id_cap.value()),
703✔
2403
                        SA_THREAD_ID.value());
1,406✔
2404
        values.lvv_thread_id_value = thread_id_cap->to_string();
703✔
2405
    }
2406
    if (duration_cap) {
5,151✔
2407
        sa.emplace_back(to_line_range(duration_cap.value()),
3✔
2408
                        SA_DURATION.value());
6✔
2409
    }
2410

2411
    for (size_t lpc = 0; lpc < pat.p_value_by_index.size(); lpc++) {
58,601✔
2412
        const auto& ivd = pat.p_value_by_index[lpc];
53,450✔
2413
        const scaling_factor* scaling = nullptr;
53,450✔
2414
        auto cap = md[ivd.ivd_index];
53,450✔
2415
        const auto& vd = *ivd.ivd_value_def;
53,450✔
2416

2417
        if (ivd.ivd_unit_field_index >= 0) {
53,450✔
2418
            auto unit_cap = md[ivd.ivd_unit_field_index];
10✔
2419

2420
            if (unit_cap) {
10✔
2421
                intern_string_t unit_val
2422
                    = intern_string::lookup(unit_cap.value());
10✔
2423
                auto unit_iter = vd.vd_unit_scaling.find(unit_val);
10✔
2424
                if (unit_iter != vd.vd_unit_scaling.end()) {
10✔
2425
                    const auto& sf = unit_iter->second;
10✔
2426

2427
                    scaling = &sf;
10✔
2428
                }
2429
            }
2430
        }
2431

2432
        if (cap) {
53,450✔
2433
            if (vd.vd_meta.lvm_kind == value_kind_t::VALUE_TIMESTAMP) {
43,704✔
2434
                auto dts = this->build_time_scanner();
7✔
2435
                exttm tm;
7✔
2436
                timeval tv;
2437
                auto val_sf = cap.value();
7✔
2438

2439
                if (dts.scan(val_sf.data(),
14✔
2440
                             val_sf.length(),
7✔
2441
                             this->get_timestamp_formats(),
2442
                             &tm,
2443
                             tv,
2444
                             true))
2445
                {
2446
                    char ts[64];
2447
                    tm.et_gmtoff = tm.et_orig_gmtoff;
7✔
2448
                    auto len = dts.ftime(
7✔
2449
                        ts, sizeof(ts), this->get_timestamp_formats(), tm);
2450
                    ts[len] = '\0';
7✔
2451
                    values.lvv_values.emplace_back(vd.vd_meta,
7✔
2452
                                                   std::string{ts, len});
21✔
2453
                    values.lvv_values.back().lv_origin
7✔
2454
                        = to_line_range(cap.value());
14✔
2455
                } else {
2456
                    values.lvv_values.emplace_back(
×
2457
                        vd.vd_meta, line, to_line_range(cap.value()));
×
2458
                }
2459
            } else {
2460
                values.lvv_values.emplace_back(
87,394✔
2461
                    vd.vd_meta, line, to_line_range(cap.value()));
43,697✔
2462
                values.lvv_values.back().apply_scaling(scaling);
43,697✔
2463
            }
2464
        } else {
2465
            values.lvv_values.emplace_back(vd.vd_meta);
9,746✔
2466
        }
2467
    }
2468

2469
    if (body_cap && body_cap->is_valid()) {
5,151✔
2470
        lr = to_line_range(body_cap.value());
5,139✔
2471
    } else {
2472
        lr.lr_start = line.length();
12✔
2473
        lr.lr_end = line.length();
12✔
2474
    }
2475
    sa.emplace_back(lr, SA_BODY.value());
5,151✔
2476

2477
    log_format::annotate(lf, line_number, sa, values);
5,151✔
2478
}
2479

2480
void
2481
external_log_format::rewrite(exec_context& ec,
43✔
2482
                             shared_buffer_ref& line,
2483
                             string_attrs_t& sa,
2484
                             std::string& value_out)
2485
{
2486
    auto pg = ec.with_provenance(exec_context::format_rewrite{});
43✔
2487
    auto& values = *ec.ec_line_values;
43✔
2488

2489
    value_out.assign(line.get_data(), line.length());
43✔
2490

2491
    for (auto iter = values.lvv_values.begin(); iter != values.lvv_values.end();
251✔
2492
         ++iter)
208✔
2493
    {
2494
        if (!iter->lv_origin.is_valid()) {
208✔
2495
            log_debug("%d: not rewriting value with invalid origin -- %s",
22✔
2496
                      (int) ec.ec_top_line,
2497
                      iter->lv_meta.lvm_name.get());
2498
            continue;
178✔
2499
        }
2500

2501
        auto vd_iter = this->elf_value_defs.find(iter->lv_meta.lvm_name);
186✔
2502
        if (vd_iter == this->elf_value_defs.end()) {
186✔
2503
            log_debug("%d: not rewriting undefined value -- %s",
×
2504
                      (int) ec.ec_top_line,
2505
                      iter->lv_meta.lvm_name.get());
2506
            continue;
×
2507
        }
2508

2509
        const auto& vd = *vd_iter->second;
186✔
2510

2511
        if (vd.vd_rewriter.empty()) {
186✔
2512
            continue;
156✔
2513
        }
2514

2515
        auto _sg = ec.enter_source(
2516
            vd_iter->second->vd_rewrite_src_name, 1, vd.vd_rewriter);
30✔
2517
        std::string field_value;
30✔
2518

2519
        auto_mem<FILE> tmpout(fclose);
30✔
2520

2521
        tmpout = std::tmpfile();
30✔
2522
        if (!tmpout) {
30✔
2523
            log_error("unable to create temporary file");
×
2524
            return;
×
2525
        }
2526
        fcntl(fileno(tmpout), F_SETFD, FD_CLOEXEC);
30✔
2527
        auto fd_copy = auto_fd::dup_of(fileno(tmpout));
30✔
2528
        fd_copy.close_on_exec();
30✔
2529
        auto ec_out = std::make_pair(tmpout.release(), fclose);
30✔
2530
        {
2531
            exec_context::output_guard og(ec, "tmp", ec_out);
60✔
2532

2533
            auto exec_res = execute_any(ec, vd.vd_rewriter);
30✔
2534
            if (exec_res.isOk()) {
30✔
2535
                field_value = exec_res.unwrap();
30✔
2536
            } else {
2537
                field_value = exec_res.unwrapErr().to_attr_line().get_string();
×
2538
            }
2539
        }
30✔
2540
        struct stat st;
2541
        fstat(fd_copy.get(), &st);
30✔
2542
        if (st.st_size > 0) {
30✔
2543
            auto buf = auto_buffer::alloc(st.st_size);
2✔
2544

2545
            buf.resize(st.st_size);
2✔
2546
            pread(fd_copy.get(), buf.in(), st.st_size, 0);
2✔
2547
            field_value = buf.to_string();
2✔
2548
        }
2✔
2549
        value_out.erase(iter->lv_origin.lr_start, iter->lv_origin.length());
30✔
2550

2551
        int32_t shift_amount
2552
            = ((int32_t) field_value.length()) - iter->lv_origin.length();
30✔
2553
        auto orig_lr = iter->lv_origin;
30✔
2554
        value_out.insert(iter->lv_origin.lr_start, field_value);
30✔
2555
        for (auto shift_iter = values.lvv_values.begin();
30✔
2556
             shift_iter != values.lvv_values.end();
170✔
2557
             ++shift_iter)
140✔
2558
        {
2559
            shift_iter->lv_origin.shift_range(orig_lr, shift_amount);
140✔
2560
        }
2561
        shift_string_attrs(sa, orig_lr, shift_amount);
30✔
2562
    }
30✔
2563
}
43✔
2564

2565
static int
2566
read_json_field(yajlpp_parse_context* ypc,
239,775✔
2567
                const unsigned char* str,
2568
                size_t len,
2569
                yajl_string_props_t* props)
2570
{
2571
    auto* jlu = (json_log_userdata*) ypc->ypc_userdata;
239,775✔
2572
    timeval tv_out;
2573
    const auto frag = string_fragment::from_bytes(str, len);
239,775✔
2574
    intern_string_t field_name;
239,775✔
2575
    const auto* vd = jlu->get_field_def(ypc);
239,775✔
2576

2577
    if (vd != nullptr) {
239,775✔
2578
        field_name = vd->vd_meta.lvm_name;
21,192✔
2579
    }
2580

2581
    if (field_name.empty()) {
239,775✔
2582
        if (!jlu->jlu_format->elf_opid_field.empty()) {
218,583✔
2583
            auto path_sf = ypc->get_path_as_string_fragment();
71,580✔
2584
            if (path_sf.startswith(jlu->jlu_format->elf_opid_field.c_str())) {
71,580✔
2585
                jlu->jlu_opid_hasher.update(path_sf);
9,585✔
2586
                jlu->jlu_opid_hasher.update(frag);
9,585✔
2587
            }
2588
        }
2589
    } else if (jlu->jlu_format->lf_timestamp_field == field_name) {
21,192✔
2590
        const auto* last = jlu->jlu_format->lf_date_time.scan(
4,182✔
2591
            (const char*) str,
2592
            len,
2593
            jlu->jlu_format->get_timestamp_formats(),
4,182✔
2594
            &jlu->jlu_exttm,
2595
            tv_out);
2596
        if (last == nullptr) {
4,182✔
2597
            auto ls = jlu->jlu_format->lf_date_time.unlock();
41✔
2598
            if ((last = jlu->jlu_format->lf_date_time.scan(
41✔
2599
                     (const char*) str,
2600
                     len,
2601
                     jlu->jlu_format->get_timestamp_formats(),
41✔
2602
                     &jlu->jlu_exttm,
2603
                     tv_out))
2604
                == nullptr)
41✔
2605
            {
2606
                jlu->jlu_format->lf_date_time.relock(ls);
19✔
2607
            }
2608
            if (last != nullptr) {
41✔
2609
                auto old_flags
22✔
2610
                    = jlu->jlu_format->lf_timestamp_flags & DATE_TIME_SET_FLAGS;
22✔
2611
                auto new_flags = jlu->jlu_exttm.et_flags & DATE_TIME_SET_FLAGS;
22✔
2612

2613
                // It is unlikely a valid timestamp would lose much
2614
                // precision.
2615
                if (new_flags != old_flags) {
22✔
2616
                    last = nullptr;
×
2617
                }
2618
            }
2619
        }
2620
        if (last != nullptr) {
4,182✔
2621
            jlu->jlu_format->lf_timestamp_flags = jlu->jlu_exttm.et_flags;
4,163✔
2622
            jlu->jlu_base_line->set_time(tv_out);
4,163✔
2623
        } else {
2624
            jlu->jlu_scan_error = log_format::scan_error{fmt::format(
19✔
2625
                "failed to parse timestamp '{}' in string property '{}'",
2626
                frag,
2627
                field_name)};
19✔
2628
        }
2629
    } else if (jlu->jlu_format->elf_level_pointer.pp_value != nullptr) {
17,010✔
2630
        if (jlu->jlu_format->elf_level_pointer.pp_value
230✔
2631
                ->find_in(field_name.to_string_fragment(), PCRE2_NO_UTF_CHECK)
230✔
2632
                .ignore_error()
230✔
2633
                .has_value())
115✔
2634
        {
2635
            jlu->jlu_base_line->set_level(
×
2636
                jlu->jlu_format->convert_level(frag, jlu->jlu_batch_context));
×
2637
        }
2638
    }
2639
    if (!field_name.empty() && jlu->jlu_format->elf_level_field == field_name) {
239,775✔
2640
        jlu->jlu_base_line->set_level(
4,337✔
2641
            jlu->jlu_format->convert_level(frag, jlu->jlu_batch_context));
4,337✔
2642
    }
2643
    if (!field_name.empty() && jlu->jlu_format->elf_opid_field == field_name) {
239,775✔
2644
        jlu->jlu_base_line->merge_bloom_bits(frag.bloom_bits());
1,013✔
2645

2646
        auto& sbc = *jlu->jlu_batch_context;
1,013✔
2647
        auto opid_iter = sbc.sbc_opids.los_opid_ranges.find(frag);
1,013✔
2648
        if (opid_iter == sbc.sbc_opids.los_opid_ranges.end()) {
1,013✔
2649
            jlu->jlu_opid_frag = frag.to_owned(sbc.sbc_allocator);
983✔
2650
        } else {
2651
            jlu->jlu_opid_frag = opid_iter->first;
30✔
2652
        }
2653
    }
2654
    if (!field_name.empty()
239,775✔
2655
        && jlu->jlu_format->elf_thread_id_field == field_name)
239,775✔
2656
    {
2657
        auto& sbc = *jlu->jlu_batch_context;
×
2658
        auto tid_iter = sbc.sbc_tids.ltis_tid_ranges.find(frag);
×
2659
        if (tid_iter == sbc.sbc_tids.ltis_tid_ranges.end()) {
×
2660
            jlu->jlu_tid_frag = frag.to_owned(sbc.sbc_allocator);
×
2661
        } else {
2662
            jlu->jlu_tid_frag = tid_iter->first;
×
2663
        }
2664
    }
2665
    if (!jlu->jlu_format->elf_subid_field.empty()
239,775✔
2666
        && jlu->jlu_format->elf_subid_field == field_name)
239,775✔
2667
    {
2668
        jlu->jlu_subid = frag.to_string();
×
2669
    }
2670
    if (!field_name.empty()
239,775✔
2671
        && jlu->jlu_format->elf_duration_field == field_name)
239,775✔
2672
    {
2673
        auto from_res = humanize::try_from<double>(frag);
×
2674
        if (from_res) {
×
2675
            jlu->jlu_duration = std::chrono::microseconds(
×
2676
                static_cast<int64_t>(from_res.value() * 1000000));
2677
        }
2678
    }
2679

2680
    if (vd != nullptr && vd->vd_is_desc_field) {
239,775✔
2681
        auto frag_copy = frag.to_owned(jlu->jlu_format->lf_desc_allocator);
42✔
2682

2683
        jlu->jlu_format->lf_desc_captures.emplace(field_name, frag_copy);
42✔
2684
    }
2685

2686
    jlu->add_sub_lines_for(vd, ypc->is_level(1), std::nullopt, str, len, props);
239,775✔
2687

2688
    return 1;
239,775✔
2689
}
2690

2691
static int
2692
rewrite_json_field(yajlpp_parse_context* ypc,
47,785✔
2693
                   const unsigned char* str,
2694
                   size_t len,
2695
                   yajl_string_props_t* props)
2696
{
2697
    static const intern_string_t body_name = intern_string::lookup("body", -1);
47,785✔
2698
    auto* jlu = (json_log_userdata*) ypc->ypc_userdata;
47,785✔
2699
    intern_string_t field_name;
47,785✔
2700
    const auto* vd = jlu->get_field_def(ypc);
47,785✔
2701
    auto frag = string_fragment::from_bytes(str, len);
47,785✔
2702

2703
    if (!ypc->is_level(1) && vd == nullptr) {
47,785✔
2704
        if (!jlu->jlu_format->elf_opid_field.empty()) {
37,266✔
2705
            auto path_sf = ypc->get_path_as_string_fragment();
36,642✔
2706
            if (path_sf.startswith(jlu->jlu_format->elf_opid_field.c_str())) {
36,642✔
2707
                jlu->jlu_opid_hasher.update(path_sf);
48✔
2708
                jlu->jlu_opid_hasher.update(frag);
48✔
2709
            }
2710
        }
2711
        return 1;
37,266✔
2712
    }
2713
    if (vd != nullptr) {
10,519✔
2714
        field_name = vd->vd_meta.lvm_name;
9,879✔
2715
    } else {
2716
        field_name = ypc->get_path();
640✔
2717
    }
2718

2719
    if (jlu->jlu_format->elf_opid_field == field_name) {
10,519✔
2720
        jlu->jlu_format->jlf_line_values.lvv_opid_value = frag.to_string();
646✔
2721
        jlu->jlu_format->jlf_line_values.lvv_opid_provenance
646✔
2722
            = logline_value_vector::opid_provenance::file;
646✔
2723
    }
2724
    if (jlu->jlu_format->elf_thread_id_field == field_name) {
10,519✔
2725
        jlu->jlu_format->jlf_line_values.lvv_thread_id_value = frag.to_string();
×
2726
    }
2727
    if (jlu->jlu_format->lf_timestamp_field == field_name) {
10,519✔
2728
        char time_buf[64];
2729

2730
        // TODO add a timeval kind to logline_value
2731
        if (jlu->jlu_line->is_time_skewed()
2,036✔
2732
            || jlu->jlu_line->get_msg_level() == LEVEL_INVALID
2,036✔
2733
            || (jlu->jlu_format->lf_timestamp_flags
4,072✔
2734
                & (ETF_MICROS_SET | ETF_NANOS_SET | ETF_ZONE_SET)))
1,954✔
2735
        {
2736
            timeval tv;
2737

2738
            const auto* last = jlu->jlu_format->lf_date_time.scan(
2,036✔
2739
                (const char*) str,
2740
                len,
2741
                jlu->jlu_format->get_timestamp_formats(),
2,036✔
2742
                &jlu->jlu_exttm,
2743
                tv);
2744
            if (last == nullptr) {
2,036✔
2745
                auto ls = jlu->jlu_format->lf_date_time.unlock();
124✔
2746
                if ((last = jlu->jlu_format->lf_date_time.scan(
124✔
2747
                         (const char*) str,
2748
                         len,
2749
                         jlu->jlu_format->get_timestamp_formats(),
124✔
2750
                         &jlu->jlu_exttm,
2751
                         tv))
2752
                    == nullptr)
124✔
2753
                {
2754
                    jlu->jlu_format->lf_date_time.relock(ls);
66✔
2755
                    jlu->jlu_scan_error = log_format::scan_error{
66✔
2756
                        fmt::format("failed to parse timestamp '{}' in string "
2757
                                    "property '{}'",
2758
                                    frag,
2759
                                    field_name)};
66✔
2760
                }
2761
            }
2762
            if (!jlu->jlu_subline_opts.hash_hack) {
2,036✔
2763
                if (jlu->jlu_exttm.et_flags & ETF_ZONE_SET
2,036✔
2764
                    && jlu->jlu_format->lf_date_time.dts_zoned_to_local)
1,970✔
2765
                {
2766
                    jlu->jlu_exttm.et_flags &= ~ETF_Z_IS_UTC;
1,970✔
2767
                }
2768
                jlu->jlu_exttm.et_gmtoff
2769
                    = jlu->jlu_format->lf_date_time.dts_local_offset_cache;
2,036✔
2770
            }
2771
            jlu->jlu_format->lf_date_time.ftime(
2,036✔
2772
                time_buf,
2773
                sizeof(time_buf),
2774
                jlu->jlu_format->get_timestamp_formats(),
2,036✔
2775
                jlu->jlu_exttm);
2,036✔
2776
        } else {
2777
            sql_strftime(
×
2778
                time_buf, sizeof(time_buf), jlu->jlu_line->get_timeval(), 'T');
×
2779
        }
2780
        jlu->jlu_format->jlf_line_values.lvv_values.emplace_back(
4,072✔
2781
            jlu->jlu_format->get_value_meta(field_name,
4,072✔
2782
                                            value_kind_t::VALUE_TEXT),
2783
            std::string{time_buf});
6,108✔
2784
    } else if (vd != nullptr
8,483✔
2785
               && vd->vd_meta.lvm_kind == value_kind_t::VALUE_TIMESTAMP)
7,843✔
2786
    {
2787
        auto dts = jlu->jlu_format->build_time_scanner();
223✔
2788
        exttm tm;
223✔
2789
        timeval tv;
2790

2791
        if (dts.scan((const char*) str,
223✔
2792
                     len,
2793
                     jlu->jlu_format->get_timestamp_formats(),
223✔
2794
                     &tm,
2795
                     tv,
2796
                     true))
2797
        {
2798
            char ts[64];
2799
            tm.et_gmtoff = tm.et_orig_gmtoff;
223✔
2800
            auto tslen = dts.ftime(
223✔
2801
                ts, sizeof(ts), jlu->jlu_format->get_timestamp_formats(), tm);
223✔
2802
            ts[tslen] = '\0';
223✔
2803
            jlu->jlu_format->jlf_line_values.lvv_values.emplace_back(
223✔
2804
                jlu->jlu_format->get_value_meta(
446✔
2805
                    ypc, vd, value_kind_t::VALUE_TIMESTAMP),
2806
                std::string{(const char*) ts, tslen});
892✔
2807
        } else {
2808
            jlu->jlu_format->jlf_line_values.lvv_values.emplace_back(
×
2809
                jlu->jlu_format->get_value_meta(
×
2810
                    ypc, vd, value_kind_t::VALUE_TEXT),
2811
                std::string{(const char*) str, len});
×
2812
        }
2813
    } else if (jlu->jlu_shared_buffer.contains((const char*) str)) {
8,483✔
2814
        auto str_offset = (int) ((const char*) str - jlu->jlu_line_value);
7,752✔
2815
        if (field_name == jlu->jlu_format->elf_body_field) {
7,752✔
2816
            jlu->jlu_format->jlf_line_values.lvv_values.emplace_back(
881✔
2817
                logline_value_meta(body_name,
1,762✔
2818
                                   value_kind_t::VALUE_TEXT,
2819
                                   logline_value_meta::internal_column{},
×
2820
                                   jlu->jlu_format),
881✔
2821
                string_fragment::from_byte_range(
1,762✔
2822
                    jlu->jlu_shared_buffer.get_data(),
881✔
2823
                    str_offset,
2824
                    str_offset + len));
881✔
2825
        }
2826

2827
        jlu->jlu_format->jlf_line_values.lvv_values.emplace_back(
7,752✔
2828
            jlu->jlu_format->get_value_meta(ypc, vd, value_kind_t::VALUE_TEXT),
15,504✔
2829
            string_fragment::from_byte_range(jlu->jlu_shared_buffer.get_data(),
15,504✔
2830
                                             str_offset,
2831
                                             str_offset + len));
7,752✔
2832
    } else {
2833
        if (field_name == jlu->jlu_format->elf_body_field) {
508✔
2834
            jlu->jlu_format->jlf_line_values.lvv_values.emplace_back(
403✔
2835
                logline_value_meta(body_name,
806✔
2836
                                   value_kind_t::VALUE_TEXT,
2837
                                   logline_value_meta::internal_column{},
×
2838
                                   jlu->jlu_format),
403✔
2839
                std::string{(const char*) str, len});
1,612✔
2840
        }
2841

2842
        jlu->jlu_format->jlf_line_values.lvv_values.emplace_back(
508✔
2843
            jlu->jlu_format->get_value_meta(ypc, vd, value_kind_t::VALUE_TEXT),
1,016✔
2844
            std::string{(const char*) str, len});
2,032✔
2845
    }
2846
    if (vd != nullptr && vd->vd_is_desc_field
9,879✔
2847
        && jlu->jlu_format->elf_opid_field.empty())
20,398✔
2848
    {
2849
        auto frag_copy = frag.to_owned(jlu->jlu_format->lf_desc_allocator);
48✔
2850

2851
        jlu->jlu_format->lf_desc_captures.emplace(field_name, frag_copy);
48✔
2852
    }
2853

2854
    return 1;
10,519✔
2855
}
2856

2857
void
2858
external_log_format::get_subline(const log_format_file_state& lffs,
22,980✔
2859
                                 const logline& ll,
2860
                                 shared_buffer_ref& sbr,
2861
                                 subline_options opts)
2862
{
2863
    if (this->elf_type == elf_type_t::ELF_TYPE_TEXT) {
22,980✔
2864
        return;
18,277✔
2865
    }
2866

2867
    if (this->jlf_cached_offset != ll.get_offset()
4,703✔
2868
        || this->jlf_cached_opts != opts)
4,703✔
2869
    {
2870
        auto& ypc = *(this->jlf_parse_context);
2,269✔
2871
        yajl_handle handle = this->jlf_yajl_handle.get();
2,269✔
2872
        json_log_userdata jlu(sbr, nullptr);
2,269✔
2873

2874
        jlu.jlu_subline_opts = opts;
2,269✔
2875

2876
        this->lf_desc_captures.clear();
2,269✔
2877
        this->lf_desc_allocator.reset();
2,269✔
2878
        this->jlf_share_manager.invalidate_refs();
2,269✔
2879
        this->jlf_attr_line.clear();
2,269✔
2880
        this->jlf_line_values.clear();
2,269✔
2881
        this->jlf_line_offsets.clear();
2,269✔
2882

2883
        auto line_frag = sbr.to_string_fragment();
2,269✔
2884

2885
        if (!line_frag.startswith("{")) {
2,269✔
2886
            this->jlf_attr_line.al_string.assign(line_frag.data(),
93✔
2887
                                                 line_frag.length());
93✔
2888
            this->jlf_line_values.clear();
93✔
2889
            sbr.share(this->jlf_share_manager,
186✔
2890
                      this->jlf_attr_line.al_string.data(),
93✔
2891
                      this->jlf_attr_line.al_string.size());
2892
            this->jlf_line_values.lvv_sbr = sbr.clone();
93✔
2893
            this->jlf_attr_line.al_attrs.emplace_back(
93✔
2894
                line_range{0, -1},
×
2895
                SA_INVALID.value(fmt::format(
186✔
2896
                    FMT_STRING("line at offset {} is not a JSON-line"),
186✔
2897
                    ll.get_offset())));
93✔
2898
            return;
93✔
2899
        }
2900

2901
        yajl_reset(handle);
2,176✔
2902
        ypc.set_static_handler(json_log_rewrite_handlers.jpc_children[0]);
2,176✔
2903
        ypc.ypc_userdata = &jlu;
2,176✔
2904
        ypc.ypc_ignore_unused = true;
2,176✔
2905
        ypc.ypc_alt_callbacks.yajl_start_array = json_array_start_const;
2,176✔
2906
        ypc.ypc_alt_callbacks.yajl_end_array = json_array_end;
2,176✔
2907
        ypc.ypc_alt_callbacks.yajl_start_map = json_array_start_const;
2,176✔
2908
        ypc.ypc_alt_callbacks.yajl_end_map = json_array_end;
2,176✔
2909
        jlu.jlu_format = this;
2,176✔
2910
        jlu.jlu_line = &ll;
2,176✔
2911
        jlu.jlu_handle = handle;
2,176✔
2912
        jlu.jlu_line_value = sbr.get_data();
2,176✔
2913
        jlu.jlu_format_hits.resize(this->jlf_line_format.size());
2,176✔
2914

2915
        yajl_status parse_status = yajl_parse(
4,352✔
2916
            handle, (const unsigned char*) sbr.get_data(), sbr.length());
2,176✔
2917
        if (parse_status != yajl_status_ok
2,176✔
2918
            || yajl_complete_parse(handle) != yajl_status_ok
2,176✔
2919
            || jlu.jlu_scan_error)
4,352✔
2920
        {
2921
            unsigned char* msg;
2922
            std::string full_msg;
80✔
2923

2924
            msg = yajl_get_error(
160✔
2925
                handle, 1, (const unsigned char*) sbr.get_data(), sbr.length());
80✔
2926
            if (msg != nullptr) {
80✔
2927
                full_msg = fmt::format(
80✔
2928
                    FMT_STRING("[offset: {}] {}\n{}"),
160✔
2929
                    ll.get_offset(),
80✔
2930
                    fmt::string_view{sbr.get_data(), sbr.length()},
80✔
2931
                    reinterpret_cast<char*>(msg));
160✔
2932
                yajl_free_error(handle, msg);
80✔
2933
            }
2934

2935
            this->jlf_attr_line.al_string.assign(full_msg.data(),
80✔
2936
                                                 full_msg.size());
2937
            this->jlf_line_values.clear();
80✔
2938
            this->jlf_attr_line.al_attrs.emplace_back(
80✔
2939
                line_range{0, -1},
×
2940
                SA_INVALID.value(jlu.jlu_scan_error
240✔
2941
                                     ? jlu.jlu_scan_error->se_message
174✔
2942
                                     : "JSON line failed to parse"));
2943
        } else {
80✔
2944
            std::vector<logline_value>::iterator lv_iter;
2,096✔
2945
            bool used_values[this->jlf_line_values.lvv_values.size()];
4,192✔
2946
            struct line_range lr;
2,096✔
2947

2948
            memset(used_values, 0, sizeof(used_values));
2,096✔
2949
            for (lv_iter = this->jlf_line_values.lvv_values.begin();
2,096✔
2950
                 lv_iter != this->jlf_line_values.lvv_values.end();
17,879✔
2951
                 ++lv_iter)
15,783✔
2952
            {
2953
                lv_iter->lv_meta.lvm_format = this;
15,783✔
2954
            }
2955

2956
            if (jlu.jlu_tid_number) {
2,096✔
2957
                this->jlf_line_values.lvv_thread_id_value
2958
                    = fmt::to_string(jlu.jlu_tid_number.value());
156✔
2959
            } else if (jlu.jlu_tid_frag) {
1,940✔
2960
                this->jlf_line_values.lvv_thread_id_value
2961
                    = jlu.jlu_tid_frag->to_string();
×
2962
            }
2963

2964
            if (this->elf_opid_field.empty()
2,096✔
2965
                && this->lf_opid_source.value_or(
1,411✔
2966
                       opid_source_t::from_description)
1,411✔
2967
                    == opid_source_t::from_description
2968
                && this->lf_opid_description_def->size() == 1)
3,507✔
2969
            {
2970
                auto found_opid_desc = false;
215✔
2971
                const auto& od = this->lf_opid_description_def->begin()->second;
215✔
2972
                for (const auto& desc : *od.od_descriptors) {
645✔
2973
                    auto desc_iter
2974
                        = this->lf_desc_captures.find(desc.od_field.pp_value);
430✔
2975
                    if (desc_iter == this->lf_desc_captures.end()) {
430✔
2976
                        continue;
382✔
2977
                    }
2978
                    found_opid_desc = true;
48✔
2979
                    jlu.jlu_opid_hasher.update(desc_iter->second);
48✔
2980
                }
2981
                if (found_opid_desc) {
215✔
2982
                    this->jlf_line_values.lvv_opid_value
2983
                        = jlu.jlu_opid_hasher.to_string();
24✔
2984
                    this->jlf_line_values.lvv_opid_provenance
2985
                        = logline_value_vector::opid_provenance::file;
24✔
2986
                }
2987
            } else if (!jlu.jlu_opid_desc_frag && !jlu.jlu_opid_frag
3,733✔
2988
                       && jlu.jlu_duration > 0us)
3,733✔
2989
            {
2990
                jlu.jlu_opid_hasher.update(line_frag);
×
2991
                this->jlf_line_values.lvv_opid_value
2992
                    = jlu.jlu_opid_hasher.to_string();
×
2993
                this->jlf_line_values.lvv_opid_provenance
2994
                    = logline_value_vector::opid_provenance::file;
×
2995
            }
2996
            if (jlu.jlu_opid_desc_frag) {
2,096✔
2997
                this->jlf_line_values.lvv_opid_value
2998
                    = jlu.jlu_opid_hasher.to_string();
29✔
2999
                this->jlf_line_values.lvv_opid_provenance
3000
                    = logline_value_vector::opid_provenance::file;
29✔
3001
            }
3002
            if (jlu.jlu_opid_frag) {
2,096✔
3003
                this->jlf_line_values.lvv_opid_value
3004
                    = jlu.jlu_opid_frag->to_string();
×
3005
                this->jlf_line_values.lvv_opid_provenance
3006
                    = logline_value_vector::opid_provenance::file;
×
3007
            }
3008

3009
            int sub_offset = this->jlf_line_format_init_count;
2,096✔
3010
            for (const auto& jfe : this->jlf_line_format) {
25,147✔
3011
                static const intern_string_t ts_field
3012
                    = intern_string::lookup("__timestamp__", -1);
23,051✔
3013
                static const intern_string_t level_field
3014
                    = intern_string::lookup("__level__");
23,153✔
3015
                size_t begin_size = this->jlf_attr_line.al_string.size();
23,051✔
3016

3017
                switch (jfe.jfe_type) {
23,051✔
3018
                    case json_log_field::CONSTANT:
3,979✔
3019
                        this->json_append_to_cache(
3,979✔
3020
                            jfe.jfe_default_value.c_str(),
3021
                            jfe.jfe_default_value.size());
3,979✔
3022
                        break;
3,979✔
3023
                    case json_log_field::VARIABLE:
19,072✔
3024
                        lv_iter = find_if(
19,072✔
3025
                            this->jlf_line_values.lvv_values.begin(),
3026
                            this->jlf_line_values.lvv_values.end(),
3027
                            logline_value_name_cmp(&jfe.jfe_value.pp_value));
3028
                        if (lv_iter != this->jlf_line_values.lvv_values.end()) {
19,072✔
3029
                            auto str = lv_iter->to_string();
7,425✔
3030
                            value_def* vd = nullptr;
7,425✔
3031

3032
                            if (lv_iter->lv_meta.lvm_values_index) {
7,425✔
3033
                                vd = this->elf_value_def_order
3034
                                         [lv_iter->lv_meta.lvm_values_index
7,425✔
3035
                                              .value()]
7,425✔
3036
                                             .get();
7,425✔
3037
                            }
3038
                            while (endswith(str, "\n")) {
7,707✔
3039
                                str.pop_back();
282✔
3040
                            }
3041
                            size_t nl_pos = str.find('\n');
7,425✔
3042

3043
                            if (!jfe.jfe_prefix.empty()) {
7,425✔
3044
                                this->json_append_to_cache(jfe.jfe_prefix);
3,545✔
3045
                            }
3046
                            lr.lr_start = this->jlf_attr_line.al_string.size();
7,425✔
3047

3048
                            if ((int) str.size() > jfe.jfe_max_width) {
7,425✔
3049
                                switch (jfe.jfe_overflow) {
232✔
3050
                                    case json_format_element::overflow_t::
173✔
3051
                                        ABBREV: {
3052
                                        size_t new_size
3053
                                            = abbreviate_str(&str[0],
173✔
3054
                                                             str.size(),
3055
                                                             jfe.jfe_max_width);
173✔
3056
                                        str.resize(new_size);
173✔
3057
                                        this->json_append(lffs, jfe, vd, str);
173✔
3058
                                        break;
173✔
3059
                                    }
3060
                                    case json_format_element::overflow_t::
53✔
3061
                                        TRUNCATE: {
3062
                                        this->json_append_to_cache(
53✔
3063
                                            str.c_str(), jfe.jfe_max_width);
53✔
3064
                                        break;
53✔
3065
                                    }
3066
                                    case json_format_element::overflow_t::
6✔
3067
                                        DOTDOT: {
3068
                                        size_t middle
6✔
3069
                                            = (jfe.jfe_max_width / 2) - 1;
6✔
3070
                                        this->json_append_to_cache(str.c_str(),
6✔
3071
                                                                   middle);
3072
                                        this->json_append_to_cache("..", 2);
6✔
3073
                                        size_t rest
6✔
3074
                                            = (jfe.jfe_max_width - middle - 2);
6✔
3075
                                        this->json_append_to_cache(
12✔
3076
                                            str.c_str() + str.size() - rest,
6✔
3077
                                            rest);
3078
                                        break;
6✔
3079
                                    }
3080
                                    case json_format_element::overflow_t::
×
3081
                                        LASTWORD: {
3082
                                        size_t new_size
3083
                                            = last_word_str(&str[0],
×
3084
                                                            str.size(),
3085
                                                            jfe.jfe_max_width);
×
3086
                                        str.resize(new_size);
×
3087
                                        this->json_append(lffs, jfe, vd, str);
×
3088
                                        break;
×
3089
                                    }
3090
                                }
3091
                            } else {
3092
                                sub_offset
3093
                                    += std::count(str.begin(), str.end(), '\n');
7,193✔
3094
                                if (vd != nullptr
7,193✔
3095
                                    && vd->vd_meta.lvm_kind
7,193✔
3096
                                        == value_kind_t::VALUE_JSON)
3097
                                {
3098
                                    auto json_al = attr_line_t();
24✔
3099
                                    json_al.append(str);
24✔
3100
                                    highlight_syntax(text_format_t::TF_JSON,
24✔
3101
                                                     json_al,
3102
                                                     std::nullopt);
3103
                                    this->jlf_attr_line.append(json_al);
24✔
3104
                                } else {
24✔
3105
                                    this->json_append(lffs, jfe, vd, str);
7,169✔
3106
                                }
3107
                            }
3108

3109
                            if (nl_pos == std::string::npos
7,425✔
3110
                                || opts.full_message)
3✔
3111
                            {
3112
                                lr.lr_end
3113
                                    = this->jlf_attr_line.al_string.size();
7,422✔
3114
                            } else {
3115
                                lr.lr_end = lr.lr_start + nl_pos;
3✔
3116
                            }
3117

3118
                            if (lv_iter->lv_meta.lvm_name
7,425✔
3119
                                == this->lf_timestamp_field)
7,425✔
3120
                            {
3121
                                this->jlf_attr_line.al_attrs.emplace_back(
1,043✔
3122
                                    lr, L_TIMESTAMP.value());
2,086✔
3123
                            } else if (lv_iter->lv_meta.lvm_name
6,382✔
3124
                                       == this->elf_body_field)
6,382✔
3125
                            {
3126
                                this->jlf_attr_line.al_attrs.emplace_back(
1,223✔
3127
                                    lr, SA_BODY.value());
2,446✔
3128
                            } else if (lv_iter->lv_meta.lvm_name
5,159✔
3129
                                       == this->elf_src_loc_field)
5,159✔
3130
                            {
3131
                                size_t digits = 0;
×
3132
                                for (auto str_iter = str.rbegin();
×
3133
                                     str_iter != str.rend();
×
3134
                                     ++str_iter)
×
3135
                                {
3136
                                    if (isdigit(*str_iter)) {
×
3137
                                        digits += 1;
×
3138
                                    } else {
3139
                                        break;
×
3140
                                    }
3141
                                }
3142
                                auto diff = str.size() - digits;
×
3143
                                auto file_lr = lr;
×
3144
                                file_lr.lr_end -= digits + 1;
×
3145
                                auto line_lr = lr;
×
3146
                                line_lr.lr_start += diff;
×
3147
                                this->jlf_attr_line.al_attrs.emplace_back(
×
3148
                                    lr, SA_SRC_LOC.value());
×
3149
                                this->jlf_attr_line.al_attrs.emplace_back(
×
3150
                                    file_lr, SA_SRC_FILE.value());
×
3151
                                this->jlf_attr_line.al_attrs.emplace_back(
×
3152
                                    line_lr, SA_SRC_LINE.value());
×
3153
                            } else if (lv_iter->lv_meta.lvm_name
5,159✔
3154
                                       == this->elf_src_file_field)
5,159✔
3155
                            {
3156
                                this->jlf_attr_line.al_attrs.emplace_back(
14✔
3157
                                    lr, SA_SRC_FILE.value());
28✔
3158
                            } else if (lv_iter->lv_meta.lvm_name
5,145✔
3159
                                       == this->elf_src_line_field)
5,145✔
3160
                            {
3161
                                this->jlf_attr_line.al_attrs.emplace_back(
14✔
3162
                                    lr, SA_SRC_LINE.value());
28✔
3163
                            } else if (lv_iter->lv_meta.lvm_name
5,131✔
3164
                                       == this->elf_thread_id_field)
5,131✔
3165
                            {
3166
                                this->jlf_attr_line.al_attrs.emplace_back(
105✔
3167
                                    lr, SA_THREAD_ID.value());
210✔
3168
                            } else if (lv_iter->lv_meta.lvm_name
5,026✔
3169
                                       == this->elf_duration_field)
5,026✔
3170
                            {
3171
                                this->jlf_attr_line.al_attrs.emplace_back(
×
3172
                                    lr, SA_DURATION.value());
×
3173
                            } else if (lv_iter->lv_meta.lvm_name
5,026✔
3174
                                       == this->elf_level_field)
5,026✔
3175
                            {
3176
                                this->jlf_attr_line.al_attrs.emplace_back(
1,113✔
3177
                                    lr, L_LEVEL.value());
2,226✔
3178
                            } else if (lv_iter->lv_meta.lvm_name
7,826✔
3179
                                           == this->elf_opid_field
3,913✔
3180
                                       && !lr.empty())
3,913✔
3181
                            {
3182
                                this->jlf_attr_line.al_attrs.emplace_back(
646✔
3183
                                    lr, L_OPID.value());
1,292✔
3184
                            }
3185
                            lv_iter->lv_origin = lr;
7,425✔
3186
                            lv_iter->lv_sub_offset = sub_offset;
7,425✔
3187
                            used_values[std::distance(
7,425✔
3188
                                this->jlf_line_values.lvv_values.begin(),
3189
                                lv_iter)] = true;
7,425✔
3190

3191
                            if (!jfe.jfe_suffix.empty()) {
7,425✔
3192
                                this->json_append_to_cache(jfe.jfe_suffix);
1,014✔
3193
                            }
3194
                        } else if (jfe.jfe_value.pp_value == ts_field) {
19,072✔
3195
                            line_range lr;
1,196✔
3196
                            ssize_t ts_len;
3197
                            char ts[64];
3198
                            exttm et;
1,196✔
3199

3200
                            ll.to_exttm(et);
1,196✔
3201
                            et.et_nsec += jlu.jlu_exttm.et_nsec % 1000;
1,196✔
3202
                            et.et_gmtoff = jlu.jlu_exttm.et_gmtoff;
1,196✔
3203
                            et.et_flags |= jlu.jlu_exttm.et_flags;
1,196✔
3204
                            if (!jfe.jfe_prefix.empty()) {
1,196✔
3205
                                this->json_append_to_cache(jfe.jfe_prefix);
4✔
3206
                            }
3207
                            if (jfe.jfe_ts_format.empty()) {
1,196✔
3208
                                ts_len = this->lf_date_time.ftime(
1,041✔
3209
                                    ts,
3210
                                    sizeof(ts),
3211
                                    this->get_timestamp_formats(),
3212
                                    et);
3213
                            } else {
3214
                                ts_len = ftime_fmt(ts,
155✔
3215
                                                   sizeof(ts),
3216
                                                   jfe.jfe_ts_format.c_str(),
3217
                                                   et);
3218
                            }
3219
                            lr.lr_start = this->jlf_attr_line.al_string.size();
1,196✔
3220
                            this->json_append_to_cache(ts, ts_len);
1,196✔
3221
                            lr.lr_end = this->jlf_attr_line.al_string.size();
1,196✔
3222
                            this->jlf_attr_line.al_attrs.emplace_back(
1,196✔
3223
                                lr, L_TIMESTAMP.value());
2,392✔
3224

3225
                            lv_iter = find_if(
1,196✔
3226
                                this->jlf_line_values.lvv_values.begin(),
3227
                                this->jlf_line_values.lvv_values.end(),
3228
                                logline_value_name_cmp(
3229
                                    &this->lf_timestamp_field));
1,196✔
3230
                            if (lv_iter
1,196✔
3231
                                != this->jlf_line_values.lvv_values.end())
1,196✔
3232
                            {
3233
                                used_values[distance(
1,196✔
3234
                                    this->jlf_line_values.lvv_values.begin(),
3235
                                    lv_iter)] = true;
1,196✔
3236
                            }
3237
                            if (!jfe.jfe_suffix.empty()) {
1,196✔
3238
                                this->json_append_to_cache(jfe.jfe_suffix);
4✔
3239
                            }
3240
                        } else if (jfe.jfe_value.pp_value == level_field
10,451✔
3241
                                   || jfe.jfe_value.pp_value
20,766✔
3242
                                       == this->elf_level_field)
10,315✔
3243
                        {
3244
                            auto level_name = ll.get_level_name();
136✔
3245
                            lr.lr_start = this->jlf_attr_line.al_string.size();
136✔
3246
                            this->json_append(lffs, jfe, nullptr, level_name);
136✔
3247
                            if (jfe.jfe_auto_width) {
136✔
3248
                                this->json_append_to_cache(
105✔
3249
                                    MAX_LEVEL_NAME_LEN - level_name.length());
105✔
3250
                            }
3251
                            lr.lr_end = this->jlf_attr_line.al_string.size();
136✔
3252
                            this->jlf_attr_line.al_attrs.emplace_back(
136✔
3253
                                lr, L_LEVEL.value());
272✔
3254
                        } else if (!jfe.jfe_default_value.empty()) {
10,315✔
3255
                            if (!jfe.jfe_prefix.empty()) {
122✔
3256
                                this->json_append_to_cache(jfe.jfe_prefix);
×
3257
                            }
3258
                            this->json_append(
122✔
3259
                                lffs, jfe, nullptr, jfe.jfe_default_value);
122✔
3260
                            if (!jfe.jfe_suffix.empty()) {
122✔
3261
                                this->json_append_to_cache(jfe.jfe_suffix);
×
3262
                            }
3263
                        }
3264

3265
                        switch (jfe.jfe_text_transform) {
19,072✔
3266
                            case json_format_element::transform_t::NONE:
18,936✔
3267
                                break;
18,936✔
3268
                            case json_format_element::transform_t::UPPERCASE:
136✔
3269
                                for (size_t cindex = begin_size; cindex
136✔
3270
                                     < this->jlf_attr_line.al_string.size();
1,104✔
3271
                                     cindex++)
3272
                                {
3273
                                    this->jlf_attr_line.al_string[cindex]
1,936✔
3274
                                        = toupper(this->jlf_attr_line
968✔
3275
                                                      .al_string[cindex]);
968✔
3276
                                }
3277
                                break;
136✔
3278
                            case json_format_element::transform_t::LOWERCASE:
×
3279
                                for (size_t cindex = begin_size; cindex
×
3280
                                     < this->jlf_attr_line.al_string.size();
×
3281
                                     cindex++)
3282
                                {
3283
                                    this->jlf_attr_line.al_string[cindex]
×
3284
                                        = tolower(this->jlf_attr_line
×
3285
                                                      .al_string[cindex]);
×
3286
                                }
3287
                                break;
×
3288
                            case json_format_element::transform_t::CAPITALIZE:
×
3289
                                for (size_t cindex = begin_size;
×
3290
                                     cindex < begin_size + 1;
×
3291
                                     cindex++)
3292
                                {
3293
                                    this->jlf_attr_line.al_string[cindex]
×
3294
                                        = toupper(this->jlf_attr_line
×
3295
                                                      .al_string[cindex]);
×
3296
                                }
3297
                                for (size_t cindex = begin_size + 1; cindex
×
3298
                                     < this->jlf_attr_line.al_string.size();
×
3299
                                     cindex++)
3300
                                {
3301
                                    this->jlf_attr_line.al_string[cindex]
×
3302
                                        = tolower(this->jlf_attr_line
×
3303
                                                      .al_string[cindex]);
×
3304
                                }
3305
                                break;
×
3306
                        }
3307
                        break;
19,072✔
3308
                }
3309
            }
3310
            this->json_append_to_cache("\n", 1);
2,096✔
3311
            sub_offset += 1;
2,096✔
3312

3313
            for (size_t lpc = 0; lpc < this->jlf_line_values.lvv_values.size();
17,879✔
3314
                 lpc++)
3315
            {
3316
                static const intern_string_t body_name
3317
                    = intern_string::lookup("body", -1);
15,783✔
3318
                auto& lv = this->jlf_line_values.lvv_values[lpc];
15,783✔
3319

3320
                if (lv.lv_meta.is_hidden() || used_values[lpc]
24,855✔
3321
                    || body_name == lv.lv_meta.lvm_name)
24,855✔
3322
                {
3323
                    continue;
14,374✔
3324
                }
3325

3326
                auto str = lv.to_string();
1,409✔
3327
                while (endswith(str, "\n")) {
1,423✔
3328
                    str.pop_back();
14✔
3329
                }
3330

3331
                lv.lv_sub_offset = sub_offset;
1,409✔
3332
                lv.lv_origin.lr_start = this->jlf_attr_line.al_string.size() + 2
1,409✔
3333
                    + lv.lv_meta.lvm_name.size() + 2;
1,409✔
3334
                auto frag = string_fragment::from_str(str);
1,409✔
3335
                while (true) {
3336
                    auto utf_scan_res = is_utf8(frag, '\n');
1,457✔
3337

3338
                    this->json_append_to_cache("  ", 2);
1,457✔
3339
                    this->json_append_to_cache(
1,457✔
3340
                        lv.lv_meta.lvm_name.to_string_fragment());
1,457✔
3341
                    this->json_append_to_cache(": ", 2);
1,457✔
3342
                    lr.lr_start = this->jlf_attr_line.al_string.size();
1,457✔
3343
                    this->json_append_to_cache(utf_scan_res.usr_valid_frag);
1,457✔
3344
                    lr.lr_end = this->jlf_attr_line.al_string.size();
1,457✔
3345
                    if (lv.lv_meta.lvm_name == this->elf_body_field) {
1,457✔
3346
                        this->jlf_attr_line.al_attrs.emplace_back(
×
3347
                            lr, SA_BODY.value());
×
3348
                    } else {
3349
                        this->jlf_attr_line.al_attrs.emplace_back(
1,457✔
3350
                            lr, SA_EXTRA_CONTENT.value());
2,914✔
3351
                    }
3352
                    this->json_append_to_cache("\n", 1);
1,457✔
3353
                    sub_offset += 1;
1,457✔
3354
                    if (utf_scan_res.usr_remaining) {
1,457✔
3355
                        frag = utf_scan_res.usr_remaining.value();
48✔
3356
                    } else {
3357
                        break;
1,409✔
3358
                    }
3359
                }
48✔
3360
                lv.lv_origin.lr_end = this->jlf_attr_line.al_string.size() - 1;
1,409✔
3361
                if (lv.lv_meta.lvm_name == this->elf_opid_field
1,409✔
3362
                    && !lv.lv_origin.empty())
1,409✔
3363
                {
3364
                    this->jlf_attr_line.al_attrs.emplace_back(lv.lv_origin,
×
3365
                                                              L_OPID.value());
×
3366
                }
3367
            }
1,409✔
3368
        }
2,096✔
3369

3370
        this->jlf_line_offsets.push_back(0);
2,176✔
3371
        for (size_t lpc = 0; lpc < this->jlf_attr_line.al_string.size(); lpc++)
261,515✔
3372
        {
3373
            if (this->jlf_attr_line.al_string[lpc] == '\n') {
259,339✔
3374
                this->jlf_line_offsets.push_back(lpc + 1);
4,652✔
3375
            }
3376
        }
3377
        this->jlf_line_offsets.push_back(this->jlf_attr_line.al_string.size());
2,176✔
3378
        this->jlf_cached_offset = ll.get_offset();
2,176✔
3379
        this->jlf_cached_opts = opts;
2,176✔
3380
    }
2,269✔
3381

3382
    off_t this_off = 0, next_off = 0;
4,610✔
3383

3384
    if (!this->jlf_line_offsets.empty()
4,610✔
3385
        && ll.get_sub_offset() < this->jlf_line_offsets.size())
4,610✔
3386
    {
3387
        require(ll.get_sub_offset() < this->jlf_line_offsets.size());
4,610✔
3388

3389
        this_off = this->jlf_line_offsets[ll.get_sub_offset()];
4,610✔
3390
        if ((ll.get_sub_offset() + 1) < (int) this->jlf_line_offsets.size()) {
4,610✔
3391
            next_off = this->jlf_line_offsets[ll.get_sub_offset() + 1];
4,610✔
3392
        } else {
3393
            next_off = this->jlf_attr_line.al_string.size();
×
3394
        }
3395
        if (next_off > 0 && this->jlf_attr_line.al_string[next_off - 1] == '\n'
4,610✔
3396
            && this_off != next_off)
9,220✔
3397
        {
3398
            next_off -= 1;
4,610✔
3399
        }
3400
    }
3401

3402
    if (opts.full_message) {
4,610✔
3403
        sbr.share(this->jlf_share_manager,
664✔
3404
                  this->jlf_attr_line.al_string.data(),
332✔
3405
                  this->jlf_attr_line.al_string.size());
3406
    } else {
3407
        sbr.share(this->jlf_share_manager,
8,556✔
3408
                  this->jlf_attr_line.al_string.data() + this_off,
4,278✔
3409
                  next_off - this_off);
4,278✔
3410
    }
3411
    sbr.get_metadata().m_valid_utf = ll.is_valid_utf();
4,610✔
3412
    sbr.get_metadata().m_has_ansi = ll.has_ansi();
4,610✔
3413
    this->jlf_cached_sub_range.lr_start = this_off;
4,610✔
3414
    this->jlf_cached_sub_range.lr_end = next_off;
4,610✔
3415
    this->jlf_line_values.lvv_sbr = sbr.clone();
4,610✔
3416
}
3417

3418
struct compiled_header_expr {
3419
    auto_mem<sqlite3_stmt> che_stmt{sqlite3_finalize};
3420
    bool che_enabled{true};
3421
};
3422

3423
struct format_header_expressions : public lnav_config_listener {
3424
    format_header_expressions() : lnav_config_listener(__FILE__) {}
1,218✔
3425

3426
    auto_sqlite3 e_db;
3427
    std::map<intern_string_t, std::map<std::string, compiled_header_expr>>
3428
        e_header_exprs;
3429
};
3430

3431
using safe_format_header_expressions = safe::Safe<format_header_expressions>;
3432

3433
static safe_format_header_expressions format_header_exprs;
3434

3435
std::optional<external_file_format>
3436
detect_mime_type(const std::filesystem::path& filename)
659✔
3437
{
3438
    uint8_t buffer[1024];
3439
    size_t buffer_size = 0;
659✔
3440

3441
    {
3442
        auto_fd fd;
659✔
3443

3444
        if ((fd = lnav::filesystem::openp(filename, O_RDONLY)) == -1) {
659✔
3445
            return std::nullopt;
×
3446
        }
3447

3448
        ssize_t rc;
3449

3450
        if ((rc = read(fd, buffer, sizeof(buffer))) == -1) {
659✔
3451
            return std::nullopt;
×
3452
        }
3453
        buffer_size = rc;
659✔
3454
    }
659✔
3455

3456
    auto hexbuf = auto_buffer::alloc(buffer_size * 2);
659✔
3457

3458
    for (size_t lpc = 0; lpc < buffer_size; lpc++) {
349,304✔
3459
        fmt::format_to(
348,645✔
3460
            std::back_inserter(hexbuf), FMT_STRING("{:02x}"), buffer[lpc]);
1,394,580✔
3461
    }
3462

3463
    safe::WriteAccess<safe_format_header_expressions> in(format_header_exprs);
659✔
3464

3465
    for (const auto& format : log_format::get_root_formats()) {
51,331✔
3466
        auto elf = std::dynamic_pointer_cast<external_log_format>(format);
50,672✔
3467
        if (elf == nullptr) {
50,672✔
3468
            continue;
3,295✔
3469
        }
3470

3471
        if (elf->elf_converter.c_header.h_exprs.he_exprs.empty()) {
47,377✔
3472
            continue;
46,059✔
3473
        }
3474

3475
        if (buffer_size < elf->elf_converter.c_header.h_size) {
1,318✔
3476
            log_debug(
108✔
3477
                "%s: file content too small (%zu) for header detection: %s",
3478
                filename.c_str(),
3479
                buffer_size,
3480
                elf->get_name().get());
3481
            continue;
108✔
3482
        }
3483
        for (const auto& hpair : elf->elf_converter.c_header.h_exprs.he_exprs) {
4,334✔
3484
            auto& he = in->e_header_exprs[elf->get_name()][hpair.first];
3,124✔
3485

3486
            if (!he.che_enabled) {
3,124✔
3487
                continue;
×
3488
            }
3489

3490
            auto* stmt = he.che_stmt.in();
3,124✔
3491

3492
            if (stmt == nullptr) {
3,124✔
3493
                continue;
×
3494
            }
3495
            sqlite3_reset(stmt);
3,124✔
3496
            auto count = sqlite3_bind_parameter_count(stmt);
3,124✔
3497
            for (int lpc = 0; lpc < count; lpc++) {
6,248✔
3498
                const auto* name = sqlite3_bind_parameter_name(stmt, lpc + 1);
3,124✔
3499

3500
                if (name[0] == '$') {
3,124✔
3501
                    const char* env_value;
3502

3503
                    if ((env_value = getenv(&name[1])) != nullptr) {
×
3504
                        sqlite3_bind_text(
×
3505
                            stmt, lpc + 1, env_value, -1, SQLITE_STATIC);
3506
                    }
3507
                    continue;
×
3508
                }
3509
                if (strcmp(name, ":header") == 0) {
3,124✔
3510
                    sqlite3_bind_text(stmt,
3,124✔
3511
                                      lpc + 1,
3512
                                      hexbuf.in(),
3,124✔
3513
                                      hexbuf.size(),
3,124✔
3514
                                      SQLITE_STATIC);
3515
                    continue;
3,124✔
3516
                }
3517
                if (strcmp(name, ":filepath") == 0) {
×
3518
                    sqlite3_bind_text(
×
3519
                        stmt, lpc + 1, filename.c_str(), -1, SQLITE_STATIC);
3520
                    continue;
×
3521
                }
3522
            }
3523

3524
            auto step_res = sqlite3_step(stmt);
3,124✔
3525

3526
            switch (step_res) {
3,124✔
3527
                case SQLITE_OK:
3,124✔
3528
                case SQLITE_DONE:
3529
                    continue;
3,124✔
3530
                case SQLITE_ROW:
×
3531
                    break;
×
3532
                default: {
×
3533
                    log_error(
×
3534
                        "failed to execute file-format header expression: "
3535
                        "%s:%s -- %s",
3536
                        elf->get_name().get(),
3537
                        hpair.first.c_str(),
3538
                        sqlite3_errmsg(in->e_db));
3539
                    he.che_enabled = false;
×
3540
                    continue;
×
3541
                }
3542
            }
3543

3544
            log_info("detected format for: %s -- %s (header-expr: %s)",
×
3545
                     filename.c_str(),
3546
                     elf->get_name().get(),
3547
                     hpair.first.c_str());
3548
            return external_file_format{
×
3549
                elf->get_name().to_string(),
×
3550
                elf->elf_converter.c_command.pp_value,
×
3551
                elf->elf_converter.c_command.pp_location.sl_source.to_string(),
×
3552
            };
3553
        }
3554
    }
50,672✔
3555

3556
    return std::nullopt;
659✔
3557
}
659✔
3558

3559
log_format::scan_result_t
3560
log_format::test_line(sample_t& sample,
×
3561
                      std::vector<lnav::console::user_message>& msgs)
3562
{
3563
    return scan_no_match{};
×
3564
}
3565

3566
log_format::scan_result_t
3567
external_log_format::test_line(sample_t& sample,
201,758✔
3568
                               std::vector<lnav::console::user_message>& msgs)
3569
{
3570
    auto lines
3571
        = string_fragment::from_str(sample.s_line.pp_value).split_lines();
201,758✔
3572

3573
    if (this->elf_type == elf_type_t::ELF_TYPE_JSON) {
201,758✔
3574
        auto alloc = ArenaAlloc::Alloc<char>{};
3,190✔
3575
        pattern_locks pats;
3,190✔
3576
        auto sbc = scan_batch_context{
3,190✔
3577
            alloc,
3578
            pats,
3579
        };
3,190✔
3580
        sbc.sbc_value_stats.resize(this->elf_value_defs.size());
3,190✔
3581
        std::vector<logline> dst;
3,190✔
3582
        auto li = line_info{
3,190✔
3583
            {0, lines[0].length()},
3,190✔
3584
        };
3,190✔
3585
        shared_buffer sb;
3,190✔
3586
        shared_buffer_ref sbr;
3,190✔
3587
        sbr.share(sb, lines[0].data(), (size_t) lines[0].length());
3,190✔
3588

3589
        return this->scan_json(dst, li, sbr, sbc);
3,190✔
3590
    }
3,190✔
3591

3592
    scan_result_t retval = scan_no_match{"no patterns matched"};
198,568✔
3593
    auto found = false;
198,568✔
3594

3595
    for (auto pat_iter = this->elf_pattern_order.begin();
198,568✔
3596
         pat_iter != this->elf_pattern_order.end();
1,475,695✔
3597
         ++pat_iter)
1,277,127✔
3598
    {
3599
        auto& pat = *(*pat_iter);
1,277,127✔
3600

3601
        if (!pat.p_pcre.pp_value) {
1,277,127✔
3602
            continue;
1,078,561✔
3603
        }
3604

3605
        auto md = pat.p_pcre.pp_value->create_match_data();
1,277,127✔
3606
        auto match_res = pat.p_pcre.pp_value->capture_from(lines[0])
1,277,127✔
3607
                             .into(md)
1,277,127✔
3608
                             .matches(PCRE2_NO_UTF_CHECK)
2,554,254✔
3609
                             .ignore_error();
1,277,127✔
3610
        if (!match_res) {
1,277,127✔
3611
            continue;
1,078,561✔
3612
        }
3613
        retval = scan_match{1000};
198,566✔
3614
        found = true;
198,566✔
3615

3616
        sample.s_matched_regexes.insert(pat.p_name.to_string());
198,566✔
3617

3618
        const auto ts_cap = md[pat.p_timestamp_field_index];
198,566✔
3619
        const auto level_cap = md[pat.p_level_field_index];
198,566✔
3620
        const char* const* custom_formats = this->get_timestamp_formats();
198,566✔
3621
        date_time_scanner dts;
198,566✔
3622
        timeval tv;
3623
        exttm tm;
198,566✔
3624

3625
        if (ts_cap && ts_cap->sf_begin == 0) {
198,566✔
3626
            pat.p_timestamp_end = ts_cap->sf_end;
123,243✔
3627
        }
3628
        const char* dts_scan_res = nullptr;
198,566✔
3629

3630
        if (ts_cap) {
198,566✔
3631
            dts_scan_res = dts.scan(
198,564✔
3632
                ts_cap->data(), ts_cap->length(), custom_formats, &tm, tv);
198,564✔
3633
        }
3634
        if (dts_scan_res != nullptr) {
198,566✔
3635
            if (dts_scan_res != ts_cap->data() + ts_cap->length()) {
198,563✔
3636
                auto match_len = dts_scan_res - ts_cap->data();
×
3637
                auto notes = attr_line_t("the used timestamp format: ");
×
3638
                if (custom_formats == nullptr) {
×
3639
                    notes.append(PTIMEC_FORMATS[dts.dts_fmt_lock].pf_fmt);
×
3640
                } else {
3641
                    notes.append(custom_formats[dts.dts_fmt_lock]);
×
3642
                }
3643
                notes.append("\n  ")
×
3644
                    .append(ts_cap.value())
×
3645
                    .append("\n")
×
3646
                    .append(2 + match_len, ' ')
×
3647
                    .append("^ matched up to here"_snippet_border);
×
3648
                auto um = lnav::console::user_message::warning(
×
3649
                              attr_line_t("timestamp was not fully matched: ")
×
3650
                                  .append_quoted(ts_cap.value()))
×
3651
                              .with_snippet(sample.s_line.to_snippet())
×
3652
                              .with_note(notes)
×
3653
                              .move();
×
3654

3655
                msgs.emplace_back(um);
×
3656
            }
3657
        } else if (!ts_cap) {
3✔
3658
            msgs.emplace_back(
2✔
3659
                lnav::console::user_message::error(
×
3660
                    attr_line_t("invalid sample log message: ")
4✔
3661
                        .append(lnav::to_json(sample.s_line.pp_value)))
4✔
3662
                    .with_reason(attr_line_t("timestamp was not captured"))
4✔
3663
                    .with_snippet(sample.s_line.to_snippet())
4✔
3664
                    .with_help(attr_line_t(
4✔
3665
                        "A timestamp needs to be captured in order for a "
3666
                        "line to be recognized as a log message")));
3667
        } else {
3668
            attr_line_t notes;
1✔
3669

3670
            if (custom_formats == nullptr) {
1✔
3671
                notes.append("the following built-in formats were tried:");
×
3672
                for (int lpc = 0; PTIMEC_FORMATS[lpc].pf_fmt != nullptr; lpc++)
×
3673
                {
3674
                    off_t off = 0;
×
3675

3676
                    PTIMEC_FORMATS[lpc].pf_func(
×
3677
                        &tm, ts_cap->data(), off, ts_cap->length());
×
3678
                    notes.append("\n  ")
×
3679
                        .append(ts_cap.value())
×
3680
                        .append("\n")
×
3681
                        .append(2 + off, ' ')
×
3682
                        .append("^ "_snippet_border)
×
3683
                        .append_quoted(
×
3684
                            lnav::roles::symbol(PTIMEC_FORMATS[lpc].pf_fmt))
×
3685
                        .append(" matched up to here"_snippet_border);
×
3686
                }
3687
            } else {
3688
                notes.append("the following custom formats were tried:");
1✔
3689
                for (int lpc = 0; custom_formats[lpc] != nullptr; lpc++) {
2✔
3690
                    off_t off = 0;
1✔
3691

3692
                    ptime_fmt(custom_formats[lpc],
1✔
3693
                              &tm,
3694
                              ts_cap->data(),
3695
                              off,
3696
                              ts_cap->length());
1✔
3697
                    notes.append("\n  ")
1✔
3698
                        .append(ts_cap.value())
1✔
3699
                        .append("\n")
1✔
3700
                        .append(2 + off, ' ')
1✔
3701
                        .append("^ "_snippet_border)
1✔
3702
                        .append_quoted(lnav::roles::symbol(custom_formats[lpc]))
2✔
3703
                        .append(" matched up to here"_snippet_border);
1✔
3704
                }
3705
            }
3706

3707
            msgs.emplace_back(
1✔
3708
                lnav::console::user_message::error(
×
3709
                    attr_line_t("invalid sample log message: ")
1✔
3710
                        .append(lnav::to_json(sample.s_line.pp_value)))
2✔
3711
                    .with_reason(attr_line_t("unrecognized timestamp -- ")
2✔
3712
                                     .append(ts_cap.value()))
1✔
3713
                    .with_snippet(sample.s_line.to_snippet())
2✔
3714
                    .with_note(notes)
1✔
3715
                    .with_help(attr_line_t("If the timestamp format is not "
2✔
3716
                                           "supported by default, you can "
3717
                                           "add a custom format with the ")
3718
                                   .append_quoted("timestamp-format"_symbol)
1✔
3719
                                   .append(" property")));
1✔
3720
        }
1✔
3721

3722
        auto level = this->convert_level(
198,566✔
3723
            level_cap.value_or(string_fragment::invalid()), nullptr);
198,566✔
3724

3725
        if (sample.s_level != LEVEL_UNKNOWN && sample.s_level != level) {
198,566✔
3726
            attr_line_t note_al;
1✔
3727

3728
            note_al.append("matched regex = ")
1✔
3729
                .append(lnav::roles::symbol(pat.p_name.to_string()))
2✔
3730
                .append("\n")
1✔
3731
                .append("captured level = ")
1✔
3732
                .append_quoted(level_cap->to_string());
1✔
3733
            if (level_cap && !this->elf_level_patterns.empty()) {
1✔
3734
                thread_local auto md = lnav::pcre2pp::match_data::unitialized();
1✔
3735

3736
                note_al.append("\nlevel regular expression match results:");
1✔
3737
                for (const auto& level_pattern : this->elf_level_patterns) {
3✔
3738
                    attr_line_t regex_al
3739
                        = level_pattern.second.lp_pcre.pp_value->get_pattern();
2✔
3740
                    lnav::snippets::regex_highlighter(
2✔
3741
                        regex_al, -1, line_range{0, (int) regex_al.length()});
2✔
3742
                    note_al.append("\n  ")
2✔
3743
                        .append(lnav::roles::symbol(
4✔
3744
                            level_pattern.second.lp_pcre.pp_path.to_string()))
4✔
3745
                        .append(" = ")
2✔
3746
                        .append(regex_al)
2✔
3747
                        .append("\n    ");
2✔
3748
                    auto match_res = level_pattern.second.lp_pcre.pp_value
2✔
3749
                                         ->capture_from(level_cap.value())
2✔
3750
                                         .into(md)
2✔
3751
                                         .matches(PCRE2_NO_UTF_CHECK)
4✔
3752
                                         .ignore_error();
2✔
3753
                    if (!match_res) {
2✔
3754
                        note_al.append(lnav::roles::warning("no match"));
1✔
3755
                        continue;
1✔
3756
                    }
3757

3758
                    note_al.append(level_cap.value())
1✔
3759
                        .append("\n    ")
1✔
3760
                        .append(md.leading().length(), ' ')
1✔
3761
                        .append("^"_snippet_border);
1✔
3762
                    if (match_res->f_all.length() > 2) {
1✔
3763
                        note_al.append(lnav::roles::snippet_border(
1✔
3764
                            std::string(match_res->f_all.length() - 2, '-')));
3✔
3765
                    }
3766
                    if (match_res->f_all.length() > 1) {
1✔
3767
                        note_al.append("^"_snippet_border);
1✔
3768
                    }
3769
                }
2✔
3770
            }
3771
            auto um
3772
                = lnav::console::user_message::error(
×
3773
                      attr_line_t("invalid sample log message: ")
1✔
3774
                          .append(lnav::to_json(sample.s_line.pp_value)))
2✔
3775
                      .with_reason(attr_line_t()
2✔
3776
                                       .append_quoted(lnav::roles::symbol(
2✔
3777
                                           level_names[level]))
1✔
3778
                                       .append(" does not match the expected "
1✔
3779
                                               "level of ")
3780
                                       .append_quoted(lnav::roles::symbol(
2✔
3781
                                           level_names[sample.s_level])))
1✔
3782
                      .with_snippet(sample.s_line.to_snippet())
2✔
3783
                      .with_note(note_al)
1✔
3784
                      .move();
1✔
3785
            if (!this->elf_level_patterns.empty()) {
1✔
3786
                um.with_help(
1✔
3787
                    attr_line_t("Level regexes are not anchored to the "
2✔
3788
                                "start/end of the string.  Prepend ")
3789
                        .append_quoted("^"_symbol)
1✔
3790
                        .append(" to the expression to match from the "
1✔
3791
                                "start of the string and append ")
3792
                        .append_quoted("$"_symbol)
1✔
3793
                        .append(" to match up to the end of the string."));
1✔
3794
            }
3795
            msgs.emplace_back(um);
1✔
3796
        }
1✔
3797

3798
        {
3799
            auto full_match_res
3800
                = pat.p_pcre.pp_value->capture_from(sample.s_line.pp_value)
198,566✔
3801
                      .into(md)
198,566✔
3802
                      .matches()
397,132✔
3803
                      .ignore_error();
198,566✔
3804
            if (!full_match_res) {
198,566✔
3805
                attr_line_t regex_al = pat.p_pcre.pp_value->get_pattern();
1✔
3806
                lnav::snippets::regex_highlighter(
1✔
3807
                    regex_al, -1, line_range{0, (int) regex_al.length()});
1✔
3808
                msgs.emplace_back(
1✔
3809
                    lnav::console::user_message::error(
×
3810
                        attr_line_t("invalid pattern: ")
1✔
3811
                            .append_quoted(
1✔
3812
                                lnav::roles::symbol(pat.p_name.to_string())))
2✔
3813
                        .with_reason("pattern does not match entire "
2✔
3814
                                     "multiline sample message")
3815
                        .with_snippet(sample.s_line.to_snippet())
2✔
3816
                        .with_note(attr_line_t()
2✔
3817
                                       .append(lnav::roles::symbol(
1✔
3818
                                           pat.p_name.to_string()))
2✔
3819
                                       .append(" = ")
1✔
3820
                                       .append(regex_al))
1✔
3821
                        .with_help(
3822
                            attr_line_t("use ").append_quoted(".*").append(
2✔
3823
                                " to match new-lines")));
3824
            } else if (static_cast<size_t>(full_match_res->f_all.length())
198,566✔
3825
                       != sample.s_line.pp_value.length())
198,565✔
3826
            {
3827
                attr_line_t regex_al = pat.p_pcre.pp_value->get_pattern();
1✔
3828
                lnav::snippets::regex_highlighter(
1✔
3829
                    regex_al, -1, line_range{0, (int) regex_al.length()});
1✔
3830
                auto match_length
3831
                    = static_cast<size_t>(full_match_res->f_all.length());
1✔
3832
                attr_line_t sample_al = sample.s_line.pp_value;
1✔
3833
                sample_al.append("\n")
1✔
3834
                    .append(match_length, ' ')
1✔
3835
                    .append("^ matched up to here"_error)
1✔
3836
                    .with_attr_for_all(VC_ROLE.value(role_t::VCR_QUOTED_CODE));
1✔
3837
                auto sample_snippet = lnav::console::snippet::from(
3838
                    sample.s_line.pp_location, sample_al);
1✔
3839
                msgs.emplace_back(
1✔
3840
                    lnav::console::user_message::error(
×
3841
                        attr_line_t("invalid pattern: ")
1✔
3842
                            .append_quoted(
1✔
3843
                                lnav::roles::symbol(pat.p_name.to_string())))
2✔
3844
                        .with_reason("pattern does not match entire "
2✔
3845
                                     "message")
3846
                        .with_snippet(sample_snippet)
1✔
3847
                        .with_note(attr_line_t()
3✔
3848
                                       .append(lnav::roles::symbol(
2✔
3849
                                           pat.p_name.to_string()))
2✔
3850
                                       .append(" = ")
1✔
3851
                                       .append(regex_al))
1✔
3852
                        .with_help("update the regular expression to fully "
3853
                                   "capture the sample message"));
3854
            }
1✔
3855
        }
3856
    }
1,277,127✔
3857

3858
    if (!found && !this->elf_pattern_order.empty()) {
198,568✔
3859
        std::vector<std::pair<ssize_t, intern_string_t>> partial_indexes;
2✔
3860
        attr_line_t notes;
2✔
3861
        size_t max_name_width = 0;
2✔
3862

3863
        for (const auto& pat_iter : this->elf_pattern_order) {
10✔
3864
            auto& pat = *pat_iter;
8✔
3865

3866
            if (!pat.p_pcre.pp_value) {
8✔
3867
                continue;
×
3868
            }
3869

3870
            partial_indexes.emplace_back(
8✔
3871
                pat.p_pcre.pp_value->match_partial(lines[0]), pat.p_name);
8✔
3872
            max_name_width = std::max(max_name_width, pat.p_name.size());
8✔
3873
        }
3874
        for (const auto& line_frag : lines) {
4✔
3875
            auto src_line = attr_line_t(line_frag.to_string());
2✔
3876
            if (!line_frag.endswith("\n")) {
2✔
3877
                src_line.append("\n");
2✔
3878
            }
3879
            src_line.with_attr_for_all(VC_ROLE.value(role_t::VCR_QUOTED_CODE));
2✔
3880
            notes.append("   ").append(src_line);
2✔
3881
            for (auto& part_pair : partial_indexes) {
10✔
3882
                if (part_pair.first >= 0
16✔
3883
                    && part_pair.first < line_frag.length())
8✔
3884
                {
3885
                    notes.append("   ")
8✔
3886
                        .append(part_pair.first, ' ')
8✔
3887
                        .append("^ "_snippet_border)
8✔
3888
                        .append(
8✔
3889
                            lnav::roles::symbol(part_pair.second.to_string()))
16✔
3890
                        .append(" matched up to here"_snippet_border)
8✔
3891
                        .append("\n");
8✔
3892
                }
3893
                part_pair.first -= line_frag.length();
8✔
3894
            }
3895
        }
2✔
3896
        notes.add_header(
2✔
3897
            "the following shows how each pattern matched this sample:\n");
3898

3899
        attr_line_t regex_note;
2✔
3900
        for (const auto& pat_iter : this->elf_pattern_order) {
10✔
3901
            if (!pat_iter->p_pcre.pp_value) {
8✔
3902
                regex_note
3903
                    .append(lnav::roles::symbol(fmt::format(
×
3904
                        FMT_STRING("{:{}}"), pat_iter->p_name, max_name_width)))
×
3905
                    .append(" is invalid");
×
3906
                continue;
×
3907
            }
3908

3909
            attr_line_t regex_al = pat_iter->p_pcre.pp_value->get_pattern();
8✔
3910
            lnav::snippets::regex_highlighter(
8✔
3911
                regex_al, -1, line_range{0, (int) regex_al.length()});
8✔
3912

3913
            regex_note
3914
                .append(lnav::roles::symbol(fmt::format(
16✔
3915
                    FMT_STRING("{:{}}"), pat_iter->p_name, max_name_width)))
24✔
3916
                .append(" = ")
8✔
3917
                .append_quoted(regex_al)
16✔
3918
                .append("\n");
8✔
3919
        }
8✔
3920

3921
        msgs.emplace_back(
2✔
3922
            lnav::console::user_message::error(
×
3923
                attr_line_t("invalid sample log message: ")
2✔
3924
                    .append(lnav::to_json(sample.s_line.pp_value)))
4✔
3925
                .with_reason("sample does not match any patterns")
4✔
3926
                .with_snippet(sample.s_line.to_snippet())
4✔
3927
                .with_note(notes.rtrim())
4✔
3928
                .with_note(regex_note));
3929
    }
2✔
3930

3931
    return retval;
198,568✔
3932
}
201,758✔
3933

3934
void
3935
external_log_format::build(std::vector<lnav::console::user_message>& errors)
57,797✔
3936
{
3937
    auto& vc = view_colors::singleton();
57,797✔
3938

3939
    if (!this->lf_timestamp_field.empty()) {
57,797✔
3940
        auto& vd = this->elf_value_defs[this->lf_timestamp_field];
57,797✔
3941
        if (vd.get() == nullptr) {
57,797✔
3942
            vd = std::make_shared<value_def>(
42,554✔
3943
                this->lf_timestamp_field,
42,554✔
3944
                value_kind_t::VALUE_TEXT,
×
3945
                logline_value_meta::internal_column{},
×
3946
                this);
42,554✔
3947
            if (this->elf_type == elf_type_t::ELF_TYPE_JSON) {
42,554✔
3948
                this->elf_value_def_order.emplace_back(vd);
7,675✔
3949
            }
3950
        }
3951
        vd->vd_meta.lvm_name = this->lf_timestamp_field;
57,797✔
3952
        vd->vd_meta.lvm_kind = value_kind_t::VALUE_TEXT;
57,797✔
3953
        vd->vd_meta.lvm_column = logline_value_meta::internal_column{};
57,797✔
3954
        vd->vd_internal = true;
57,797✔
3955

3956
        this->elf_value_defs[LOG_TIME_STR] = vd;
57,797✔
3957
    }
3958

3959
    if (!this->lf_subsecond_field.empty()) {
57,797✔
3960
        if (!this->lf_subsecond_unit.has_value()) {
101✔
3961
            errors.emplace_back(
1✔
3962
                lnav::console::user_message::error(
×
3963
                    attr_line_t()
2✔
3964
                        .append_quoted(
1✔
3965
                            lnav::roles::symbol(this->elf_name.to_string()))
2✔
3966
                        .append(" is not a valid log format"))
1✔
3967
                    .with_reason(attr_line_t()
2✔
3968
                                     .append_quoted("subsecond-units"_symbol)
1✔
3969
                                     .append(" must be set when ")
1✔
3970
                                     .append_quoted("subsecond-field"_symbol)
1✔
3971
                                     .append(" is used"))
1✔
3972
                    .with_snippets(this->get_snippets()));
2✔
3973
        } else {
3974
            auto& vd = this->elf_value_defs[this->lf_subsecond_field];
100✔
3975
            if (vd.get() == nullptr) {
100✔
3976
                vd = std::make_shared<value_def>(
100✔
3977
                    this->lf_subsecond_field,
100✔
3978
                    value_kind_t::VALUE_INTEGER,
×
3979
                    logline_value_meta::internal_column{},
×
3980
                    this);
100✔
3981
                if (this->elf_type == elf_type_t::ELF_TYPE_JSON) {
100✔
3982
                    this->elf_value_def_order.emplace_back(vd);
100✔
3983
                }
3984
            }
3985
            vd->vd_meta.lvm_name = this->lf_subsecond_field;
100✔
3986
            vd->vd_meta.lvm_column = logline_value_meta::internal_column{};
100✔
3987
            vd->vd_meta.lvm_hidden = true;
100✔
3988
            vd->vd_internal = true;
100✔
3989
        }
3990
    }
3991

3992
    if (startswith(this->elf_level_field.get(), "/")) {
57,797✔
3993
        this->elf_level_field
3994
            = intern_string::lookup(this->elf_level_field.get() + 1);
200✔
3995
    }
3996
    if (!this->elf_level_field.empty()) {
57,797✔
3997
        auto level_iter = this->elf_value_defs.find(this->elf_level_field);
57,797✔
3998
        if (level_iter == this->elf_value_defs.end()) {
57,797✔
3999
            auto& vd = this->elf_value_defs[this->elf_level_field];
27,310✔
4000
            vd = std::make_shared<value_def>(
27,310✔
4001
                this->elf_level_field,
27,310✔
4002
                value_kind_t::VALUE_TEXT,
×
4003
                logline_value_meta::internal_column{},
×
4004
                this);
27,310✔
4005
            if (this->elf_type == elf_type_t::ELF_TYPE_JSON) {
27,310✔
4006
                this->elf_value_def_order.emplace_back(vd);
2,793✔
4007
            }
4008
            vd->vd_meta.lvm_name = this->elf_level_field;
27,310✔
4009
            vd->vd_meta.lvm_column = logline_value_meta::internal_column{};
27,310✔
4010
            vd->vd_internal = true;
27,310✔
4011

4012
            if (this->elf_level_field != this->elf_body_field) {
27,310✔
4013
                this->elf_value_defs[LOG_LEVEL_STR] = vd;
26,413✔
4014
            }
4015
        } else {
4016
            if (level_iter->second->vd_meta.lvm_kind
30,487✔
4017
                != value_kind_t::VALUE_TEXT)
30,487✔
4018
            {
4019
                this->lf_level_hideable = false;
5,679✔
4020
            }
4021
            this->elf_value_defs[LOG_LEVEL_STR] = level_iter->second;
30,487✔
4022
        }
4023
    }
4024

4025
    auto opid_field_iter = this->elf_value_defs.find(LOG_OPID_STR);
57,797✔
4026
    if (opid_field_iter == this->elf_value_defs.end()) {
57,797✔
4027
        auto vd
4028
            = std::make_shared<value_def>(this->elf_opid_field,
57,797✔
4029
                                          value_kind_t::VALUE_TEXT,
×
4030
                                          logline_value_meta::internal_column{},
×
4031
                                          this);
57,797✔
4032
        if (this->elf_type == elf_type_t::ELF_TYPE_JSON) {
57,797✔
4033
            this->elf_value_def_order.emplace_back(vd);
11,760✔
4034
        }
4035
        vd->vd_meta.lvm_name = LOG_OPID_STR;
57,797✔
4036
        vd->vd_meta.lvm_kind = value_kind_t::VALUE_TEXT;
57,797✔
4037
        vd->vd_meta.lvm_column = logline_value_meta::internal_column{};
57,797✔
4038
        vd->vd_internal = true;
57,797✔
4039

4040
        this->elf_value_defs[LOG_OPID_STR] = vd;
57,797✔
4041
    }
57,797✔
4042

4043
    if (!this->elf_body_field.empty()) {
57,797✔
4044
        auto& vd = this->elf_value_defs[this->elf_body_field];
57,797✔
4045
        if (vd.get() == nullptr) {
57,797✔
4046
            vd = std::make_shared<value_def>(
45,641✔
4047
                this->elf_body_field,
45,641✔
4048
                value_kind_t::VALUE_TEXT,
×
4049
                logline_value_meta::internal_column{},
×
4050
                this);
45,641✔
4051
            if (this->elf_type == elf_type_t::ELF_TYPE_JSON) {
45,641✔
4052
                this->elf_value_def_order.emplace_back(vd);
6,877✔
4053
            }
4054
        }
4055
        vd->vd_meta.lvm_name = this->elf_body_field;
57,797✔
4056
        vd->vd_meta.lvm_kind = value_kind_t::VALUE_TEXT;
57,797✔
4057
        vd->vd_meta.lvm_column = logline_value_meta::internal_column{};
57,797✔
4058
        vd->vd_internal = true;
57,797✔
4059
    }
4060

4061
    if (!this->elf_src_file_field.empty()) {
57,797✔
4062
        auto& vd = this->elf_value_defs[this->elf_src_file_field];
7,971✔
4063
        if (vd.get() == nullptr) {
7,971✔
4064
            vd = std::make_shared<value_def>(
1✔
4065
                this->elf_src_file_field,
1✔
4066
                value_kind_t::VALUE_TEXT,
×
4067
                logline_value_meta::internal_column{},
×
4068
                this);
2✔
4069
        }
4070
        vd->vd_meta.lvm_name = this->elf_src_file_field;
7,971✔
4071
        vd->vd_meta.lvm_kind = value_kind_t::VALUE_TEXT;
7,971✔
4072
        vd->vd_meta.lvm_column = logline_value_meta::internal_column{};
7,971✔
4073
    }
4074

4075
    if (!this->elf_src_line_field.empty()) {
57,797✔
4076
        auto& vd = this->elf_value_defs[this->elf_src_line_field];
7,971✔
4077
        if (vd.get() == nullptr) {
7,971✔
4078
            vd = std::make_shared<value_def>(
1✔
4079
                this->elf_src_line_field,
1✔
4080
                value_kind_t::VALUE_INTEGER,
×
4081
                logline_value_meta::internal_column{},
×
4082
                this);
2✔
4083
        }
4084
        vd->vd_meta.lvm_name = this->elf_src_line_field;
7,971✔
4085
        vd->vd_meta.lvm_kind = value_kind_t::VALUE_INTEGER;
7,971✔
4086
        vd->vd_meta.lvm_column = logline_value_meta::internal_column{};
7,971✔
4087
    }
4088

4089
    if (!this->elf_thread_id_field.empty()) {
57,797✔
4090
        auto& vd = this->elf_value_defs[this->elf_thread_id_field];
18,332✔
4091
        if (vd.get() == nullptr) {
18,332✔
4092
            vd = std::make_shared<value_def>(
1✔
4093
                this->elf_thread_id_field,
1✔
4094
                value_kind_t::VALUE_TEXT,
×
4095
                logline_value_meta::internal_column{},
×
4096
                this);
2✔
4097
        }
4098
        vd->vd_meta.lvm_name = this->elf_thread_id_field;
18,332✔
4099
        vd->vd_meta.lvm_kind = value_kind_t::VALUE_TEXT;
18,332✔
4100
        vd->vd_meta.lvm_column = logline_value_meta::internal_column{};
18,332✔
4101
    }
4102

4103
    if (!this->elf_duration_field.empty()) {
57,797✔
4104
        auto& vd = this->elf_value_defs[this->elf_duration_field];
2,391✔
4105
        if (vd.get() == nullptr) {
2,391✔
4106
            vd = std::make_shared<value_def>(
×
4107
                this->elf_duration_field,
×
4108
                value_kind_t::VALUE_FLOAT,
×
4109
                logline_value_meta::internal_column{},
×
4110
                this);
×
4111
        }
4112
        vd->vd_meta.lvm_name = this->elf_duration_field;
2,391✔
4113
        vd->vd_meta.lvm_kind = value_kind_t::VALUE_FLOAT;
2,391✔
4114
        vd->vd_meta.lvm_column = logline_value_meta::internal_column{};
2,391✔
4115
    }
4116

4117
    for (auto& od_pair : *this->lf_opid_description_def) {
81,709✔
4118
        od_pair.second.od_index = this->lf_opid_description_def_vec->size();
23,912✔
4119
        this->lf_opid_description_def_vec->emplace_back(&od_pair.second);
23,912✔
4120
    }
4121

4122
    for (auto& od_pair : *this->lf_subid_description_def) {
58,594✔
4123
        od_pair.second.od_index = this->lf_subid_description_def_vec->size();
797✔
4124
        this->lf_subid_description_def_vec->emplace_back(&od_pair.second);
797✔
4125
    }
4126

4127
    if (!this->lf_timestamp_format.empty()) {
57,797✔
4128
        this->lf_timestamp_format.push_back(nullptr);
7,277✔
4129
    }
4130
    auto src_file_found = 0;
57,797✔
4131
    auto src_line_found = 0;
57,797✔
4132
    auto thread_id_found = 0;
57,797✔
4133
    auto duration_found = 0;
57,797✔
4134
    for (auto& elf_pattern : this->elf_patterns) {
162,916✔
4135
        auto& pat = *elf_pattern.second;
105,119✔
4136

4137
        if (pat.p_pcre.pp_value == nullptr) {
105,119✔
4138
            continue;
1✔
4139
        }
4140

4141
        if (pat.p_opid_field_index == -1
210,236✔
4142
            && this->lf_opid_source.value_or(opid_source_t::from_description)
105,118✔
4143
                == opid_source_t::from_description
4144
            && this->lf_opid_description_def->size() == 1)
210,236✔
4145
        {
4146
            const auto& opid_def
4147
                = this->lf_opid_description_def->begin()->second;
26,301✔
4148
            for (const auto& desc : *opid_def.od_descriptors) {
58,978✔
4149
                for (auto named_cap : pat.p_pcre.pp_value->get_named_captures())
380,966✔
4150
                {
4151
                    const intern_string_t name
4152
                        = intern_string::lookup(named_cap.get_name());
348,289✔
4153

4154
                    if (name == desc.od_field.pp_value) {
348,289✔
4155
                        pat.p_opid_description_field_indexes.emplace_back(
60,572✔
4156
                            named_cap.get_index());
30,286✔
4157
                    }
4158
                }
4159
            }
4160
        }
4161

4162
        for (auto named_cap : pat.p_pcre.pp_value->get_named_captures()) {
905,438✔
4163
            const intern_string_t name
4164
                = intern_string::lookup(named_cap.get_name());
800,320✔
4165

4166
            if (name == this->lf_timestamp_field) {
800,320✔
4167
                pat.p_timestamp_field_index = named_cap.get_index();
105,116✔
4168
            }
4169
            if (name == this->lf_time_field) {
800,320✔
4170
                pat.p_time_field_index = named_cap.get_index();
797✔
4171
            }
4172
            if (name == this->elf_level_field) {
800,320✔
4173
                pat.p_level_field_index = named_cap.get_index();
80,202✔
4174
            }
4175
            if (name == this->elf_opid_field) {
800,320✔
4176
                pat.p_opid_field_index = named_cap.get_index();
21,519✔
4177
            }
4178
            if (name == this->elf_subid_field) {
800,320✔
4179
                pat.p_subid_field_index = named_cap.get_index();
11,955✔
4180
            }
4181
            if (name == this->elf_body_field) {
800,320✔
4182
                pat.p_body_field_index = named_cap.get_index();
90,770✔
4183
            }
4184
            if (name == this->elf_src_file_field) {
800,320✔
4185
                pat.p_src_file_field_index = named_cap.get_index();
12,752✔
4186
                src_file_found += 1;
12,752✔
4187
            }
4188
            if (name == this->elf_src_line_field) {
800,320✔
4189
                pat.p_src_line_field_index = named_cap.get_index();
14,346✔
4190
                src_line_found += 1;
14,346✔
4191
            }
4192
            if (name == this->elf_thread_id_field) {
800,320✔
4193
                pat.p_thread_id_field_index = named_cap.get_index();
44,632✔
4194
                thread_id_found += 1;
44,632✔
4195
            }
4196
            if (name == this->elf_duration_field) {
800,320✔
4197
                pat.p_duration_field_index = named_cap.get_index();
4,782✔
4198
                duration_found += 1;
4,782✔
4199
            }
4200

4201
            auto value_iter = this->elf_value_defs.find(name);
800,320✔
4202
            if (value_iter != this->elf_value_defs.end()) {
800,320✔
4203
                auto vd = value_iter->second;
788,263✔
4204
                indexed_value_def ivd;
788,263✔
4205

4206
                ivd.ivd_index = named_cap.get_index();
788,263✔
4207
                if (!vd->vd_unit_field.empty()) {
788,263✔
4208
                    ivd.ivd_unit_field_index = pat.p_pcre.pp_value->name_index(
1,594✔
4209
                        vd->vd_unit_field.get());
797✔
4210
                } else {
4211
                    ivd.ivd_unit_field_index = -1;
787,466✔
4212
                }
4213
                if (!vd->vd_internal
788,263✔
4214
                    && !vd->vd_meta.lvm_column
1,354,735✔
4215
                            .is<logline_value_meta::table_column>())
566,472✔
4216
                {
4217
                    vd->vd_meta.lvm_column = logline_value_meta::table_column{
317,808✔
4218
                        this->elf_column_count++};
317,808✔
4219
                }
4220
                ivd.ivd_value_def = vd;
788,263✔
4221
                pat.p_value_by_index.push_back(ivd);
788,263✔
4222
            }
788,263✔
4223
            pat.p_value_name_to_index[name] = named_cap.get_index();
800,320✔
4224
        }
4225

4226
        stable_sort(pat.p_value_by_index.begin(), pat.p_value_by_index.end());
105,118✔
4227

4228
        for (int lpc = 0; lpc < (int) pat.p_value_by_index.size(); lpc++) {
893,381✔
4229
            auto& ivd = pat.p_value_by_index[lpc];
788,263✔
4230
            auto vd = ivd.ivd_value_def;
788,263✔
4231

4232
            if (!vd->vd_meta.lvm_foreign_key && !vd->vd_meta.lvm_identifier) {
788,263✔
4233
                switch (vd->vd_meta.lvm_kind) {
401,417✔
4234
                    case value_kind_t::VALUE_INTEGER:
55,093✔
4235
                    case value_kind_t::VALUE_FLOAT:
4236
                        pat.p_numeric_value_indexes.push_back(lpc);
55,093✔
4237
                        break;
55,093✔
4238
                    default:
346,324✔
4239
                        break;
346,324✔
4240
                }
4241
            }
4242
        }
788,263✔
4243

4244
        if (pat.p_timestamp_field_index == -1) {
105,118✔
4245
            errors.emplace_back(
2✔
4246
                lnav::console::user_message::error(
×
4247
                    attr_line_t("invalid pattern: ")
4✔
4248
                        .append_quoted(lnav::roles::symbol(pat.p_config_path)))
4✔
4249
                    .with_reason("no timestamp capture found in the pattern")
4✔
4250
                    .with_snippets(this->get_snippets())
4✔
4251
                    .with_help("all log messages need a timestamp"));
4252
        }
4253

4254
        if (!this->elf_level_field.empty() && pat.p_level_field_index == -1) {
105,118✔
4255
            log_warning("%s:level field '%s' not found in pattern",
24,916✔
4256
                        pat.p_config_path.c_str(),
4257
                        this->elf_level_field.get());
4258
        }
4259
        if (!this->elf_body_field.empty() && pat.p_body_field_index == -1) {
105,118✔
4260
            log_warning("%s:body field '%s' not found in pattern",
14,348✔
4261
                        pat.p_config_path.c_str(),
4262
                        this->elf_body_field.get());
4263
        }
4264

4265
        this->elf_pattern_order.push_back(elf_pattern.second);
105,118✔
4266
    }
4267
    if (this->elf_type == elf_type_t::ELF_TYPE_TEXT
115,594✔
4268
        && !this->elf_src_file_field.empty() && src_file_found == 0)
57,797✔
4269
    {
4270
        errors.emplace_back(
1✔
4271
            lnav::console::user_message::error(
×
4272
                attr_line_t("invalid pattern: ")
2✔
4273
                    .append_quoted(
1✔
4274
                        lnav::roles::symbol(this->elf_name.to_string())))
2✔
4275
                .with_reason("no source file capture found in the pattern")
2✔
4276
                .with_snippets(this->get_snippets())
2✔
4277
                .with_help(attr_line_t("at least one pattern needs a source "
2✔
4278
                                       "file capture named ")
4279
                               .append_quoted(this->elf_src_file_field.get())));
1✔
4280
    }
4281
    if (this->elf_type == elf_type_t::ELF_TYPE_TEXT
115,594✔
4282
        && !this->elf_src_line_field.empty() && src_line_found == 0)
57,797✔
4283
    {
4284
        errors.emplace_back(
1✔
4285
            lnav::console::user_message::error(
×
4286
                attr_line_t("invalid pattern: ")
2✔
4287
                    .append_quoted(
1✔
4288
                        lnav::roles::symbol(this->elf_name.to_string())))
2✔
4289
                .with_reason("no source line capture found in the pattern")
2✔
4290
                .with_snippets(this->get_snippets())
2✔
4291
                .with_help(attr_line_t("at least one pattern needs a source "
2✔
4292
                                       "line capture named ")
4293
                               .append_quoted(this->elf_src_line_field.get())));
1✔
4294
    }
4295
    if (this->elf_type == elf_type_t::ELF_TYPE_TEXT
115,594✔
4296
        && !this->elf_thread_id_field.empty() && thread_id_found == 0)
57,797✔
4297
    {
4298
        errors.emplace_back(
1✔
4299
            lnav::console::user_message::error(
×
4300
                attr_line_t("invalid pattern: ")
2✔
4301
                    .append_quoted(
1✔
4302
                        lnav::roles::symbol(this->elf_name.to_string())))
2✔
4303
                .with_reason("no thread ID capture found in the pattern")
2✔
4304
                .with_snippets(this->get_snippets())
2✔
4305
                .with_help(
4306
                    attr_line_t(
2✔
4307
                        "at least one pattern needs a thread ID capture named ")
4308
                        .append_quoted(this->elf_thread_id_field.get())));
1✔
4309
    }
4310
    if (this->elf_type == elf_type_t::ELF_TYPE_TEXT
115,594✔
4311
        && !this->elf_duration_field.empty() && duration_found == 0)
57,797✔
4312
    {
4313
        errors.emplace_back(
×
4314
            lnav::console::user_message::error(
×
4315
                attr_line_t("invalid pattern: ")
×
4316
                    .append_quoted(
×
4317
                        lnav::roles::symbol(this->elf_name.to_string())))
×
4318
                .with_reason("no duration capture found in the pattern")
×
4319
                .with_snippets(this->get_snippets())
×
4320
                .with_help(
4321
                    attr_line_t(
×
4322
                        "at least one pattern needs a duration capture named ")
4323
                        .append_quoted(this->elf_duration_field.get())));
×
4324
    }
4325

4326
    if (this->elf_type != elf_type_t::ELF_TYPE_TEXT) {
57,797✔
4327
        if (!this->elf_patterns.empty()) {
11,760✔
4328
            errors.emplace_back(
1✔
4329
                lnav::console::user_message::error(
×
4330
                    attr_line_t()
2✔
4331
                        .append_quoted(
1✔
4332
                            lnav::roles::symbol(this->elf_name.to_string()))
2✔
4333
                        .append(" is not a valid log format"))
1✔
4334
                    .with_reason("structured logs cannot have regexes")
2✔
4335
                    .with_snippets(this->get_snippets()));
2✔
4336
        }
4337
        if (this->elf_type == elf_type_t::ELF_TYPE_JSON) {
11,760✔
4338
            this->lf_multiline = true;
11,760✔
4339
            this->lf_structured = true;
11,760✔
4340
            this->lf_formatted_lines = true;
11,760✔
4341
            this->jlf_parse_context
4342
                = std::make_shared<yajlpp_parse_context>(this->elf_name);
11,760✔
4343
            this->jlf_yajl_handle.reset(
11,760✔
4344
                yajl_alloc(&this->jlf_parse_context->ypc_callbacks,
11,760✔
4345
                           nullptr,
4346
                           this->jlf_parse_context.get()),
11,760✔
4347
                yajl_handle_deleter());
4348
            yajl_config(
11,760✔
4349
                this->jlf_yajl_handle.get(), yajl_dont_validate_strings, 1);
4350
        }
4351
    } else {
4352
        if (this->elf_patterns.empty()) {
46,037✔
4353
            errors.emplace_back(lnav::console::user_message::error(
2✔
4354
                                    attr_line_t()
4✔
4355
                                        .append_quoted(lnav::roles::symbol(
4✔
4356
                                            this->elf_name.to_string()))
4✔
4357
                                        .append(" is not a valid log format"))
2✔
4358
                                    .with_reason("no regexes specified")
4✔
4359
                                    .with_snippets(this->get_snippets()));
4✔
4360
        }
4361
    }
4362

4363
    stable_sort(this->elf_level_pairs.begin(), this->elf_level_pairs.end());
57,797✔
4364

4365
    {
4366
        safe::WriteAccess<safe_format_header_expressions> hexprs(
4367
            format_header_exprs);
57,797✔
4368

4369
        if (hexprs->e_db.in() == nullptr) {
57,797✔
4370
            if (sqlite3_open(":memory:", hexprs->e_db.out()) != SQLITE_OK) {
797✔
4371
                log_error("unable to open memory DB");
×
4372
                return;
×
4373
            }
4374
            register_sqlite_funcs(hexprs->e_db.in(), sqlite_registration_funcs);
797✔
4375
        }
4376

4377
        for (const auto& hpair : this->elf_converter.c_header.h_exprs.he_exprs)
61,783✔
4378
        {
4379
            auto stmt_str
4380
                = fmt::format(FMT_STRING("SELECT 1 WHERE {}"), hpair.second);
11,958✔
4381
            compiled_header_expr che;
3,986✔
4382

4383
            log_info("preparing file-format header expression: %s",
3,986✔
4384
                     stmt_str.c_str());
4385
            auto retcode = sqlite3_prepare_v2(hexprs->e_db.in(),
7,972✔
4386
                                              stmt_str.c_str(),
4387
                                              stmt_str.size(),
3,986✔
4388
                                              che.che_stmt.out(),
4389
                                              nullptr);
4390
            if (retcode != SQLITE_OK) {
3,986✔
4391
                auto sql_al = attr_line_t(hpair.second)
2✔
4392
                                  .with_attr_for_all(SA_PREFORMATTED.value())
2✔
4393
                                  .with_attr_for_all(
1✔
4394
                                      VC_ROLE.value(role_t::VCR_QUOTED_CODE))
2✔
4395
                                  .move();
1✔
4396
                readline_sql_highlighter(
1✔
4397
                    sql_al, lnav::sql::dialect::sqlite, std::nullopt);
4398
                intern_string_t watch_expr_path = intern_string::lookup(
4399
                    fmt::format(FMT_STRING("/{}/converter/header/expr/{}"),
3✔
4400
                                this->elf_name,
1✔
4401
                                hpair.first));
2✔
4402
                auto snippet = lnav::console::snippet::from(
4403
                    source_location(watch_expr_path), sql_al);
1✔
4404

4405
                auto um = lnav::console::user_message::error(
2✔
4406
                              "SQL expression is invalid")
4407
                              .with_reason(sqlite3_errmsg(hexprs->e_db.in()))
2✔
4408
                              .with_snippet(snippet)
1✔
4409
                              .move();
1✔
4410

4411
                errors.emplace_back(um);
1✔
4412
                continue;
1✔
4413
            }
1✔
4414

4415
            hexprs->e_header_exprs[this->elf_name][hpair.first]
3,985✔
4416
                = std::move(che);
7,970✔
4417
        }
3,987✔
4418

4419
        if (!this->elf_converter.c_header.h_exprs.he_exprs.empty()
57,797✔
4420
            && this->elf_converter.c_command.pp_value.empty())
57,797✔
4421
        {
4422
            auto um = lnav::console::user_message::error(
2✔
4423
                          "A command is required when a converter is defined")
4424
                          .with_help(
2✔
4425
                              "The converter command transforms the file "
4426
                              "into a format that can be consumed by lnav")
4427
                          .with_snippets(this->get_snippets())
2✔
4428
                          .move();
1✔
4429
            errors.emplace_back(um);
1✔
4430
        }
1✔
4431
    }
57,797✔
4432

4433
    for (auto& vd : this->elf_value_def_order) {
584,248✔
4434
        std::vector<std::string>::iterator act_iter;
526,451✔
4435

4436
        if (!vd->vd_internal
526,451✔
4437
            && log_vtab_impl::RESERVED_COLUMNS.count(
997,195✔
4438
                vd->vd_meta.lvm_name.to_string_fragment()))
997,195✔
4439
        {
4440
            auto um = lnav::console::user_message::error(
×
4441
                          attr_line_t("value name ")
1✔
4442
                              .append_quoted(lnav::roles::symbol(
2✔
4443
                                  fmt::format(FMT_STRING("/{}/value/{}"),
4✔
4444
                                              this->elf_name,
1✔
4445
                                              vd->vd_meta.lvm_name)))
1✔
4446
                              .append(" is reserved and cannot be used"))
1✔
4447
                          .with_reason(
2✔
4448
                              "lnav automatically defines several columns in "
4449
                              "the log virtual table")
4450
                          .with_snippets(this->get_snippets())
2✔
4451
                          .with_help("Choose another name")
2✔
4452
                          .move();
1✔
4453
            errors.emplace_back(um);
1✔
4454
        }
1✔
4455

4456
        vd->vd_meta.lvm_format = this;
526,451✔
4457
        if (!vd->vd_internal
526,451✔
4458
            && !vd->vd_meta.lvm_column.is<logline_value_meta::table_column>())
526,451✔
4459
        {
4460
            vd->vd_meta.lvm_column
152,936✔
4461
                = logline_value_meta::table_column{this->elf_column_count++};
152,936✔
4462
        }
4463

4464
        if (vd->vd_meta.lvm_kind == value_kind_t::VALUE_UNKNOWN) {
526,451✔
4465
            log_warning("no kind set for %s, assuming text",
×
4466
                        vd->vd_meta.lvm_name.c_str());
4467
            vd->vd_meta.lvm_kind = value_kind_t::VALUE_TEXT;
×
4468
        }
4469

4470
        if (this->elf_type == elf_type_t::ELF_TYPE_TEXT) {
526,451✔
4471
            std::set<std::string> available_captures;
335,344✔
4472

4473
            bool found_in_pattern = false;
335,344✔
4474
            for (const auto& pat : this->elf_patterns) {
564,087✔
4475
                if (pat.second->p_pcre.pp_value == nullptr) {
564,085✔
4476
                    continue;
×
4477
                }
4478

4479
                auto cap_index = pat.second->p_pcre.pp_value->name_index(
1,128,170✔
4480
                    vd->vd_meta.lvm_name.get());
564,085✔
4481
                if (cap_index >= 0) {
564,085✔
4482
                    found_in_pattern = true;
335,342✔
4483
                    break;
335,342✔
4484
                }
4485

4486
                for (auto named_cap :
228,743✔
4487
                     pat.second->p_pcre.pp_value->get_named_captures())
2,084,173✔
4488
                {
4489
                    available_captures.insert(named_cap.get_name().to_string());
1,626,687✔
4490
                }
4491
            }
4492
            if (!found_in_pattern) {
335,344✔
4493
                auto notes
4494
                    = attr_line_t("the following captures are available:\n  ")
2✔
4495
                          .join(available_captures,
2✔
4496
                                VC_ROLE.value(role_t::VCR_SYMBOL),
4✔
4497
                                ", ")
4498
                          .move();
2✔
4499
                errors.emplace_back(
2✔
4500
                    lnav::console::user_message::warning(
×
4501
                        attr_line_t("invalid value ")
2✔
4502
                            .append_quoted(lnav::roles::symbol(
4✔
4503
                                fmt::format(FMT_STRING("/{}/value/{}"),
8✔
4504
                                            this->elf_name,
2✔
4505
                                            vd->vd_meta.lvm_name.get()))))
4✔
4506
                        .with_reason(
4✔
4507
                            attr_line_t("no patterns have a capture named ")
4✔
4508
                                .append_quoted(vd->vd_meta.lvm_name.get()))
2✔
4509
                        .with_note(notes)
2✔
4510
                        .with_snippets(this->get_snippets())
4✔
4511
                        .with_help("values are populated from captures in "
4512
                                   "patterns, so at least one pattern must "
4513
                                   "have a capture with this value name"));
4514
            }
2✔
4515
        }
335,344✔
4516

4517
        for (act_iter = vd->vd_action_list.begin();
526,451✔
4518
             act_iter != vd->vd_action_list.end();
527,248✔
4519
             ++act_iter)
797✔
4520
        {
4521
            if (this->lf_action_defs.find(*act_iter)
797✔
4522
                == this->lf_action_defs.end())
1,594✔
4523
            {
4524
#if 0
4525
                errors.push_back("error:" + this->elf_name.to_string() + ":"
4526
                                 + vd->vd_meta.lvm_name.get()
4527
                                 + ": cannot find action -- " + (*act_iter));
4528
#endif
4529
            }
4530
        }
4531

4532
        vd->set_rewrite_src_name();
526,451✔
4533

4534
        for (auto& hd_pair : vd->vd_highlighter_patterns) {
528,942✔
4535
            auto& hd = hd_pair.second;
2,491✔
4536
            text_attrs attrs;
2,491✔
4537

4538
            if (hd.hd_pattern.pp_value == nullptr) {
2,491✔
4539
                hd.hd_pattern.pp_value
4540
                    = lnav::pcre2pp::code::from_const(".*").to_shared();
2,391✔
4541
            }
4542

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

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

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

4592
            if (hd.hd_pattern.pp_value != nullptr) {
2,491✔
4593
                this->lf_highlighters.emplace_back(hd.hd_pattern.pp_value);
2,491✔
4594
                this->lf_highlighters.back()
2,491✔
4595
                    .with_field(vd->vd_meta.lvm_name)
2,491✔
4596
                    .with_name(hd_pair.first.to_string())
4,982✔
4597
                    .with_attrs(attrs)
2,491✔
4598
                    .with_nestable(hd.hd_nestable);
2,491✔
4599
            }
4600
        }
4601
    }
4602

4603
    if (this->elf_type == elf_type_t::ELF_TYPE_JSON) {
57,797✔
4604
        for (const auto& vd : this->elf_value_def_order) {
202,867✔
4605
            this->elf_value_def_frag_map[vd->vd_meta.lvm_name
191,107✔
4606
                                             .to_string_fragment()] = vd.get();
191,107✔
4607
        }
4608
    }
4609

4610
    for (const auto& td_pair : this->lf_tag_defs) {
60,291✔
4611
        const auto& td = td_pair.second;
2,494✔
4612

4613
        if (td->ftd_pattern.pp_value == nullptr
2,494✔
4614
            || td->ftd_pattern.pp_value->get_pattern().empty())
2,494✔
4615
        {
4616
            errors.emplace_back(
3✔
4617
                lnav::console::user_message::error(
×
4618
                    attr_line_t("invalid tag definition ")
6✔
4619
                        .append_quoted(lnav::roles::symbol(
6✔
4620
                            fmt::format(FMT_STRING("/{}/tags/{}"),
12✔
4621
                                        this->elf_name,
3✔
4622
                                        td_pair.first))))
3✔
4623
                    .with_reason(
6✔
4624
                        "tag definitions must have a non-empty pattern")
4625
                    .with_snippets(this->get_snippets()));
6✔
4626
        }
4627
    }
4628

4629
    if (this->elf_opid_field.empty()
57,797✔
4630
        && this->lf_opid_description_def->size() > 1)
57,797✔
4631
    {
4632
        errors.emplace_back(
1✔
4633
            lnav::console::user_message::error(
×
4634
                attr_line_t("too many opid descriptions")
2✔
4635
                    .append_quoted(lnav::roles::symbol(fmt::format(
2✔
4636
                        FMT_STRING("/{}/opid/description"), this->elf_name))))
4✔
4637
                .with_reason(attr_line_t("when no ")
2✔
4638
                                 .append("opid-field"_symbol)
1✔
4639
                                 .append(" is specified, only a single "
1✔
4640
                                         "description is supported"))
4641
                .with_snippets(this->get_snippets()));
2✔
4642
    }
4643

4644
    for (const auto& opid_desc_pair : *this->lf_opid_description_def) {
81,709✔
4645
        for (const auto& opid_desc : *opid_desc_pair.second.od_descriptors) {
56,591✔
4646
            auto iter = this->elf_value_defs.find(opid_desc.od_field.pp_value);
32,679✔
4647
            if (iter == this->elf_value_defs.end()) {
32,679✔
4648
                errors.emplace_back(
2✔
4649
                    lnav::console::user_message::error(
×
4650
                        attr_line_t("invalid opid description field ")
4✔
4651
                            .append_quoted(lnav::roles::symbol(
4✔
4652
                                opid_desc.od_field.pp_path.to_string())))
4✔
4653
                        .with_reason(
4✔
4654
                            attr_line_t("unknown value name ")
4✔
4655
                                .append_quoted(opid_desc.od_field.pp_value))
2✔
4656
                        .with_snippets(this->get_snippets()));
4✔
4657
            } else {
4658
                this->lf_desc_fields.insert(iter->first);
32,677✔
4659
                iter->second->vd_is_desc_field = true;
32,677✔
4660
            }
4661
        }
4662
    }
4663

4664
    for (const auto& subid_desc_pair : *this->lf_subid_description_def) {
58,594✔
4665
        for (const auto& subid_desc : *subid_desc_pair.second.od_descriptors) {
1,594✔
4666
            auto iter = this->elf_value_defs.find(subid_desc.od_field.pp_value);
797✔
4667
            if (iter == this->elf_value_defs.end()) {
797✔
4668
                errors.emplace_back(
×
4669
                    lnav::console::user_message::error(
×
4670
                        attr_line_t("invalid subid description field ")
×
4671
                            .append_quoted(lnav::roles::symbol(
×
4672
                                subid_desc.od_field.pp_path.to_string())))
×
4673
                        .with_reason(
×
4674
                            attr_line_t("unknown value name ")
×
4675
                                .append_quoted(subid_desc.od_field.pp_value))
×
4676
                        .with_snippets(this->get_snippets()));
×
4677
            } else {
4678
                this->lf_desc_fields.insert(iter->first);
797✔
4679
                iter->second->vd_is_desc_field = true;
797✔
4680
            }
4681
        }
4682
    }
4683

4684
    if (this->elf_type == elf_type_t::ELF_TYPE_TEXT
115,594✔
4685
        && this->elf_samples.empty())
57,797✔
4686
    {
4687
        errors.emplace_back(
3✔
4688
            lnav::console::user_message::error(
×
4689
                attr_line_t()
6✔
4690
                    .append_quoted(
3✔
4691
                        lnav::roles::symbol(this->elf_name.to_string()))
6✔
4692
                    .append(" is not a valid log format"))
3✔
4693
                .with_reason("log message samples must be included in a format "
6✔
4694
                             "definition")
4695
                .with_snippets(this->get_snippets()));
6✔
4696
    }
4697

4698
    for (const auto& pat : this->elf_pattern_order) {
162,915✔
4699
        if (this->elf_type != elf_type_t::ELF_TYPE_TEXT) {
105,118✔
4700
            continue;
1✔
4701
        }
4702
        if (pat->p_pcre.pp_value->name_index(this->lf_timestamp_field.get())
105,117✔
4703
            < 0)
105,117✔
4704
        {
4705
            attr_line_t notes;
1✔
4706
            bool first_note = true;
1✔
4707

4708
            if (pat->p_pcre.pp_value->get_capture_count() > 0) {
1✔
4709
                notes.append("the following captures are available:\n  ");
1✔
4710
            }
4711
            for (auto named_cap : pat->p_pcre.pp_value->get_named_captures()) {
4✔
4712
                if (!first_note) {
3✔
4713
                    notes.append(", ");
2✔
4714
                }
4715
                notes.append(
3✔
4716
                    lnav::roles::symbol(named_cap.get_name().to_string()));
6✔
4717
                first_note = false;
3✔
4718
            }
4719
            errors.emplace_back(
1✔
4720
                lnav::console::user_message::error(
×
4721
                    attr_line_t("invalid value for property ")
1✔
4722
                        .append_quoted(lnav::roles::symbol(
2✔
4723
                            fmt::format(FMT_STRING("/{}/timestamp-field"),
4✔
4724
                                        this->elf_name))))
1✔
4725
                    .with_reason(
2✔
4726
                        attr_line_t()
2✔
4727
                            .append_quoted(this->lf_timestamp_field)
1✔
4728
                            .append(" was not found in the pattern at ")
1✔
4729
                            .append(lnav::roles::symbol(pat->p_config_path)))
2✔
4730
                    .with_note(notes)
1✔
4731
                    .with_snippets(this->get_snippets()));
2✔
4732
        }
1✔
4733
    }
4734

4735
    for (size_t sample_index = 0; sample_index < this->elf_samples.size();
259,551✔
4736
         sample_index += 1)
201,754✔
4737
    {
4738
        auto& elf_sample = this->elf_samples[sample_index];
201,754✔
4739
        auto sample_lines
4740
            = string_fragment(elf_sample.s_line.pp_value).split_lines();
201,754✔
4741

4742
        if (this->test_line(elf_sample, errors).is<scan_match>()) {
201,754✔
4743
            for (const auto& pat_name : elf_sample.s_matched_regexes) {
400,318✔
4744
                this->elf_patterns[pat_name]->p_matched_samples.emplace(
198,565✔
4745
                    sample_index);
4746
            }
4747
        }
4748
    }
201,754✔
4749

4750
    if (!this->elf_samples.empty()) {
57,797✔
4751
        for (const auto& elf_sample : this->elf_samples) {
249,382✔
4752
            if (elf_sample.s_matched_regexes.size() <= 1) {
201,754✔
4753
                continue;
201,754✔
4754
            }
4755

4756
            errors.emplace_back(
×
4757
                lnav::console::user_message::warning(
×
4758
                    attr_line_t("invalid log format: ")
×
4759
                        .append_quoted(
×
4760
                            lnav::roles::symbol(this->elf_name.to_string())))
×
4761
                    .with_reason(
×
4762
                        attr_line_t(
×
4763
                            "sample is matched by more than one regex: ")
4764
                            .join(elf_sample.s_matched_regexes,
×
4765
                                  VC_ROLE.value(role_t::VCR_SYMBOL),
×
4766
                                  ", "))
4767
                    .with_snippet(lnav::console::snippet::from(
×
4768
                        elf_sample.s_line.pp_location,
4769
                        attr_line_t().append(lnav::roles::quoted_code(
×
4770
                            elf_sample.s_line.pp_value))))
×
4771
                    .with_help("log format regexes must match a single type "
4772
                               "of log message"));
4773
        }
4774

4775
        for (const auto& pat : this->elf_pattern_order) {
152,743✔
4776
            if (pat->p_matched_samples.empty()) {
105,115✔
4777
                errors.emplace_back(
2✔
4778
                    lnav::console::user_message::warning(
×
4779
                        attr_line_t("invalid pattern: ")
4✔
4780
                            .append_quoted(
2✔
4781
                                lnav::roles::symbol(pat->p_config_path)))
4✔
4782
                        .with_reason("pattern does not match any samples")
4✔
4783
                        .with_snippet(lnav::console::snippet::from(
6✔
4784
                            pat->p_pcre.pp_location, ""))
2✔
4785
                        .with_help(
4786
                            "every pattern should have at least one sample "
4787
                            "that it matches"));
4788
            }
4789
        }
4790
    }
4791

4792
    size_t value_def_index = 0;
57,797✔
4793
    for (auto& elf_value_def : this->elf_value_def_order) {
584,248✔
4794
        elf_value_def->vd_meta.lvm_values_index
526,451✔
4795
            = std::make_optional(value_def_index++);
526,451✔
4796

4797
        if (elf_value_def->vd_meta.lvm_foreign_key
526,451✔
4798
            || elf_value_def->vd_meta.lvm_identifier)
526,451✔
4799
        {
4800
            continue;
275,569✔
4801
        }
4802

4803
        switch (elf_value_def->vd_meta.lvm_kind) {
250,882✔
4804
            case value_kind_t::VALUE_INTEGER:
59,378✔
4805
            case value_kind_t::VALUE_FLOAT:
4806
                this->elf_numeric_value_defs.push_back(elf_value_def);
59,378✔
4807
                break;
59,378✔
4808
            default:
191,504✔
4809
                break;
191,504✔
4810
        }
4811
    }
4812

4813
    int format_index = 0;
57,797✔
4814
    for (auto iter = this->jlf_line_format.begin();
57,797✔
4815
         iter != this->jlf_line_format.end();
199,084✔
4816
         ++iter, format_index++)
141,287✔
4817
    {
4818
        static const intern_string_t ts
4819
            = intern_string::lookup("__timestamp__");
142,881✔
4820
        static const intern_string_t level_field
4821
            = intern_string::lookup("__level__");
142,881✔
4822
        auto& jfe = *iter;
141,287✔
4823

4824
        if (startswith(jfe.jfe_value.pp_value.get(), "/")) {
141,287✔
4825
            jfe.jfe_value.pp_value
4826
                = intern_string::lookup(jfe.jfe_value.pp_value.get() + 1);
200✔
4827
        }
4828
        if (!jfe.jfe_ts_format.empty()) {
141,287✔
4829
            if (!jfe.jfe_value.pp_value.empty() && jfe.jfe_value.pp_value != ts)
897✔
4830
            {
4831
                log_warning(
100✔
4832
                    "%s:line-format[%d]:ignoring field '%s' since "
4833
                    "timestamp-format was used",
4834
                    this->elf_name.get(),
4835
                    format_index,
4836
                    jfe.jfe_value.pp_value.get());
4837
            }
4838
            jfe.jfe_value.pp_value = ts;
897✔
4839
        }
4840

4841
        switch (jfe.jfe_type) {
141,287✔
4842
            case json_log_field::VARIABLE: {
97,246✔
4843
                auto vd_iter
4844
                    = this->elf_value_defs.find(jfe.jfe_value.pp_value);
97,246✔
4845
                if (jfe.jfe_value.pp_value == ts) {
97,246✔
4846
                    this->elf_value_defs[this->lf_timestamp_field]
9,865✔
4847
                        ->vd_meta.lvm_hidden = true;
9,865✔
4848
                } else if (jfe.jfe_value.pp_value == level_field) {
87,381✔
4849
                    this->elf_value_defs[this->elf_level_field]
3,985✔
4850
                        ->vd_meta.lvm_hidden = true;
3,985✔
4851
                } else if (vd_iter == this->elf_value_defs.end()) {
83,396✔
4852
                    errors.emplace_back(
2✔
4853
                        lnav::console::user_message::error(
×
4854
                            attr_line_t("invalid line format element ")
4✔
4855
                                .append_quoted(lnav::roles::symbol(fmt::format(
4✔
4856
                                    FMT_STRING("/{}/line-format/{}/field"),
6✔
4857
                                    this->elf_name,
2✔
4858
                                    format_index))))
4859
                            .with_reason(
4✔
4860
                                attr_line_t()
4✔
4861
                                    .append_quoted(jfe.jfe_value.pp_value)
2✔
4862
                                    .append(" is not a defined value"))
2✔
4863
                            .with_snippet(jfe.jfe_value.to_snippet()));
4✔
4864
                } else {
4865
                    vd_iter->second->vd_line_format_index = format_index;
83,394✔
4866
                    switch (vd_iter->second->vd_meta.lvm_kind) {
83,394✔
4867
                        case value_kind_t::VALUE_INTEGER:
12,952✔
4868
                        case value_kind_t::VALUE_FLOAT:
4869
                            if (jfe.jfe_align
12,952✔
4870
                                == json_format_element::align_t::NONE)
4871
                            {
4872
                                jfe.jfe_align
4873
                                    = json_format_element::align_t::RIGHT;
12,155✔
4874
                            }
4875
                            break;
12,952✔
4876
                        default:
70,442✔
4877
                            break;
70,442✔
4878
                    }
4879
                }
4880
                break;
97,246✔
4881
            }
4882
            case json_log_field::CONSTANT:
44,041✔
4883
                this->jlf_line_format_init_count
44,041✔
4884
                    += std::count(jfe.jfe_default_value.begin(),
44,041✔
4885
                                  jfe.jfe_default_value.end(),
4886
                                  '\n');
44,041✔
4887
                break;
44,041✔
4888
            default:
×
4889
                break;
×
4890
        }
4891
    }
4892

4893
    for (auto& hd_pair : this->elf_highlighter_patterns) {
58,996✔
4894
        auto& hd = hd_pair.second;
1,199✔
4895
        text_attrs attrs;
1,199✔
4896

4897
        if (!hd.hd_color.pp_value.empty()) {
1,199✔
4898
            attrs.ta_fg_color = vc.match_color(
1,198✔
4899
                styling::color_unit::from_str(hd.hd_color.pp_value)
2,396✔
4900
                    .unwrapOrElse([&](const auto& msg) {
1✔
4901
                        errors.emplace_back(
1✔
4902
                            lnav::console::user_message::error(
4903
                                attr_line_t()
1✔
4904
                                    .append_quoted(hd.hd_color.pp_value)
2✔
4905
                                    .append(" is not a valid color value for "
1✔
4906
                                            "property ")
4907
                                    .append_quoted(lnav::roles::symbol(
2✔
4908
                                        hd.hd_color.pp_path.to_string())))
1✔
4909
                                .with_reason(msg)
2✔
4910
                                .with_snippet(hd.hd_color.to_snippet()));
1✔
4911
                        return styling::color_unit::EMPTY;
1✔
4912
                    }));
4913
        }
4914

4915
        if (!hd.hd_background_color.pp_value.empty()) {
1,199✔
4916
            attrs.ta_bg_color = vc.match_color(
1✔
4917
                styling::color_unit::from_str(hd.hd_background_color.pp_value)
2✔
4918
                    .unwrapOrElse([&](const auto& msg) {
1✔
4919
                        errors.emplace_back(
1✔
4920
                            lnav::console::user_message::error(
4921
                                attr_line_t()
1✔
4922
                                    .append_quoted(
2✔
4923
                                        hd.hd_background_color.pp_value)
1✔
4924
                                    .append(" is not a valid color value for "
1✔
4925
                                            "property ")
4926
                                    .append_quoted(lnav::roles::symbol(
2✔
4927
                                        hd.hd_background_color.pp_path
1✔
4928
                                            .to_string())))
4929
                                .with_reason(msg)
2✔
4930
                                .with_snippet(
4931
                                    hd.hd_background_color.to_snippet()));
1✔
4932
                        return styling::color_unit::EMPTY;
1✔
4933
                    }));
4934
        }
4935

4936
        if (hd.hd_underline) {
1,199✔
4937
            attrs |= text_attrs::style::underline;
100✔
4938
        }
4939
        if (hd.hd_blink) {
1,199✔
4940
            attrs |= text_attrs::style::blink;
×
4941
        }
4942

4943
        if (hd.hd_pattern.pp_value != nullptr) {
1,199✔
4944
            this->lf_highlighters.emplace_back(hd.hd_pattern.pp_value);
1,197✔
4945
            this->lf_highlighters.back()
1,197✔
4946
                .with_name(hd_pair.first.to_string())
2,394✔
4947
                .with_attrs(attrs)
1,197✔
4948
                .with_nestable(hd.hd_nestable);
1,197✔
4949
        }
4950
    }
4951
}
4952

4953
void
4954
external_log_format::register_vtabs(
49,068✔
4955
    log_vtab_manager* vtab_manager,
4956
    std::vector<lnav::console::user_message>& errors)
4957
{
4958
    for (auto& elf_search_table : this->elf_search_tables) {
58,617✔
4959
        if (elf_search_table.second.std_pattern.pp_value == nullptr) {
9,549✔
4960
            continue;
1✔
4961
        }
4962

4963
        auto lst = std::make_shared<log_search_table>(
4964
            elf_search_table.second.std_pattern.pp_value,
9,548✔
4965
            elf_search_table.first);
9,548✔
4966
        lst->lst_format = this;
9,548✔
4967
        lst->lst_log_path_glob = elf_search_table.second.std_glob;
9,548✔
4968
        if (elf_search_table.second.std_level != LEVEL_UNKNOWN) {
9,548✔
4969
            lst->lst_log_level = elf_search_table.second.std_level;
3,410✔
4970
        }
4971
        auto errmsg = vtab_manager->register_vtab(lst);
9,548✔
4972
        if (!errmsg.empty()) {
9,548✔
4973
#if 0
4974
            errors.push_back("error:" + this->elf_name.to_string() + ":"
4975
                             + search_iter->first.to_string()
4976
                             + ":unable to register table -- " + errmsg);
4977
#endif
4978
        }
4979
    }
9,548✔
4980
}
49,068✔
4981

4982
bool
4983
external_log_format::match_samples(const std::vector<sample_t>& samples) const
4,146,168✔
4984
{
4985
    for (const auto& sample_iter : samples) {
18,447,338✔
4986
        for (const auto& pat_iter : this->elf_pattern_order) {
38,780,588✔
4987
            auto& pat = *pat_iter;
24,479,418✔
4988

4989
            if (!pat.p_pcre.pp_value) {
24,479,418✔
4990
                continue;
×
4991
            }
4992

4993
            if (pat.p_pcre.pp_value
48,958,836✔
4994
                    ->find_in(sample_iter.s_line.pp_value, PCRE2_NO_UTF_CHECK)
48,958,836✔
4995
                    .ignore_error())
48,958,836✔
4996
            {
4997
                return true;
16,663✔
4998
            }
4999
        }
5000
    }
5001

5002
    return false;
4,129,505✔
5003
}
5004

5005
class external_log_table : public log_format_vtab_impl {
5006
public:
5007
    explicit external_log_table(std::shared_ptr<const log_format> elf)
49,068✔
5008
        : log_format_vtab_impl(elf),
49,068✔
5009
          elt_format(dynamic_cast<const external_log_format*>(elf.get()))
49,068✔
5010
    {
5011
    }
49,068✔
5012

5013
    void get_columns(std::vector<vtab_column>& cols) const override
49,479✔
5014
    {
5015
        const auto& elf = this->elt_format;
49,479✔
5016

5017
        cols.resize(elf->elf_column_count);
49,479✔
5018
        for (const auto& vd : elf->elf_value_def_order) {
502,338✔
5019
            auto type_pair = logline_value_to_sqlite_type(vd->vd_meta.lvm_kind);
452,859✔
5020

5021
            if (!vd->vd_meta.lvm_column.is<logline_value_meta::table_column>())
452,859✔
5022
            {
5023
                continue;
47,049✔
5024
            }
5025

5026
            auto col
5027
                = vd->vd_meta.lvm_column.get<logline_value_meta::table_column>()
405,810✔
5028
                      .value;
405,810✔
5029
            require(0 <= col && col < elf->elf_column_count);
405,810✔
5030

5031
            cols[col].vc_name = vd->vd_meta.lvm_name.get();
405,810✔
5032
            cols[col].vc_type = type_pair.first;
405,810✔
5033
            cols[col].vc_subtype = type_pair.second;
405,810✔
5034
            cols[col].vc_collator = vd->vd_collate;
405,810✔
5035
            cols[col].vc_comment = vd->vd_description;
405,810✔
5036
        }
5037
    }
49,479✔
5038

5039
    void get_foreign_keys(
38,408✔
5040
        std::unordered_set<std::string>& keys_inout) const override
5041
    {
5042
        log_vtab_impl::get_foreign_keys(keys_inout);
38,408✔
5043

5044
        for (const auto& elf_value_def : this->elt_format->elf_value_defs) {
561,238✔
5045
            if (elf_value_def.second->vd_meta.lvm_foreign_key
522,830✔
5046
                || elf_value_def.second->vd_meta.lvm_identifier)
522,830✔
5047
            {
5048
                keys_inout.emplace(elf_value_def.first.to_string());
190,772✔
5049
            }
5050
        }
5051
    }
38,408✔
5052

5053
    bool next(log_cursor& lc, logfile_sub_source& lss) override
3,488✔
5054
    {
5055
        if (lc.is_eof()) {
3,488✔
5056
            return true;
×
5057
        }
5058

5059
        content_line_t cl(lss.at(lc.lc_curr_line));
3,488✔
5060
        auto* lf = lss.find_file_ptr(cl);
3,488✔
5061
        auto lf_iter = lf->begin() + cl;
3,488✔
5062
        if (lf_iter->is_continued()) {
3,488✔
5063
            return false;
×
5064
        }
5065

5066
        if (lf->get_format_name() == this->lfvi_format->get_name()) {
3,488✔
5067
            return true;
3,482✔
5068
        }
5069

5070
        return false;
6✔
5071
    }
5072

5073
    void extract(logfile* lf,
3,404✔
5074
                 uint64_t line_number,
5075
                 string_attrs_t& sa,
5076
                 logline_value_vector& values) override
5077
    {
5078
        auto format = lf->get_format();
3,404✔
5079

5080
        sa.clear();
3,404✔
5081
        format->annotate(lf, line_number, sa, values);
3,404✔
5082
    }
3,404✔
5083

5084
    const external_log_format* elt_format;
5085
    line_range elt_container_body;
5086
};
5087

5088
std::shared_ptr<log_vtab_impl>
5089
external_log_format::get_vtab_impl() const
49,068✔
5090
{
5091
    return std::make_shared<external_log_table>(this->shared_from_this());
49,068✔
5092
}
5093

5094
std::shared_ptr<log_format>
5095
external_log_format::specialized(int fmt_lock)
546✔
5096
{
5097
    auto retval = std::make_shared<external_log_format>(*this);
546✔
5098

5099
    retval->lf_specialized = true;
546✔
5100
    if (this->elf_type == elf_type_t::ELF_TYPE_JSON) {
546✔
5101
        this->jlf_parse_context
5102
            = std::make_shared<yajlpp_parse_context>(this->elf_name);
51✔
5103
        this->jlf_yajl_handle.reset(
51✔
5104
            yajl_alloc(&this->jlf_parse_context->ypc_callbacks,
51✔
5105
                       nullptr,
5106
                       this->jlf_parse_context.get()),
51✔
5107
            yajl_handle_deleter());
5108
        yajl_config(this->jlf_yajl_handle.get(), yajl_dont_validate_strings, 1);
51✔
5109
        this->jlf_attr_line.al_string.reserve(16 * 1024);
51✔
5110
    }
5111

5112
    this->elf_specialized_value_defs_state = *this->elf_value_defs_state;
546✔
5113

5114
    return retval;
1,092✔
5115
}
546✔
5116

5117
log_format::match_name_result
5118
external_log_format::match_name(const std::string& filename)
897,860✔
5119
{
5120
    if (this->elf_filename_pcre.pp_value == nullptr) {
897,860✔
5121
        return name_matched{};
896,398✔
5122
    }
5123

5124
    if (this->elf_filename_pcre.pp_value->find_in(filename)
2,924✔
5125
            .ignore_error()
2,924✔
5126
            .has_value())
1,462✔
5127
    {
5128
        return name_matched{};
247✔
5129
    }
5130

5131
    return name_mismatched{
2,430✔
5132
        this->elf_filename_pcre.pp_value->match_partial(filename),
2,430✔
5133
        this->elf_filename_pcre.pp_value->get_pattern(),
1,215✔
5134
    };
1,215✔
5135
}
5136

5137
auto
5138
external_log_format::value_line_count(scan_batch_context& sbc,
326,800✔
5139
                                      const value_def* vd,
5140
                                      bool top_level,
5141
                                      std::optional<double> val,
5142
                                      const unsigned char* str,
5143
                                      ssize_t len,
5144
                                      yajl_string_props_t* props)
5145
    -> value_line_count_result
5146
{
5147
    value_line_count_result retval;
326,800✔
5148

5149
    if (str != nullptr && props != nullptr && !val) {
326,800✔
5150
        auto frag = string_fragment::from_bytes(str, len);
239,775✔
5151
        while (frag.endswith("\n")) {
240,827✔
5152
            frag.pop_back();
1,052✔
5153
            props->line_feeds -= 1;
1,052✔
5154
        }
5155
        retval.vlcr_has_ansi |= props->has_ansi;
239,775✔
5156
        retval.vlcr_count += props->line_feeds;
239,775✔
5157
    }
5158

5159
    if (vd == nullptr) {
326,800✔
5160
        if (this->jlf_hide_extra || !top_level) {
302,759✔
5161
            retval.vlcr_count = 0;
283,215✔
5162
        }
5163

5164
        return retval;
302,759✔
5165
    }
5166

5167
    if (vd->vd_meta.lvm_values_index) {
24,041✔
5168
        auto& lvs = sbc.sbc_value_stats[vd->vd_meta.lvm_values_index.value()];
8,101✔
5169
        if (len > lvs.lvs_width) {
8,101✔
5170
            lvs.lvs_width = len;
4,289✔
5171
        }
5172
        if (val) {
8,101✔
5173
            lvs.add_value(val.value());
182✔
5174
        }
5175
    }
5176

5177
    if (vd->vd_line_format_index) {
24,041✔
5178
        retval.vlcr_line_format_count += 1;
3,989✔
5179
        retval.vlcr_count -= 1;
3,989✔
5180
        retval.vlcr_line_format_index = vd->vd_line_format_index;
3,989✔
5181
    }
5182
    if (vd->vd_meta.is_hidden()) {
24,041✔
5183
        retval.vlcr_count = 0;
3,771✔
5184
        return retval;
3,771✔
5185
    }
5186

5187
    return retval;
20,270✔
5188
}
5189

5190
log_level_t
5191
external_log_format::convert_level(string_fragment sf,
209,345✔
5192
                                   scan_batch_context* sbc) const
5193
{
5194
    auto retval = LEVEL_INFO;
209,345✔
5195

5196
    if (sf.is_valid()) {
209,345✔
5197
        if (sbc != nullptr) {
165,091✔
5198
            auto ssm_res = sbc->sbc_level_cache.lookup(sf);
10,565✔
5199
            if (ssm_res.has_value()) {
10,565✔
5200
                return static_cast<log_level_t>(ssm_res.value());
4,194✔
5201
            }
5202
        }
5203

5204
        if (this->elf_level_patterns.empty()) {
160,897✔
5205
            retval = string2level(sf.data(), sf.length());
34,768✔
5206
        } else {
5207
            for (const auto& elf_level_pattern : this->elf_level_patterns) {
339,880✔
5208
                if (elf_level_pattern.second.lp_pcre.pp_value
624,266✔
5209
                        ->find_in(sf, PCRE2_NO_UTF_CHECK)
624,266✔
5210
                        .ignore_error()
624,266✔
5211
                        .has_value())
312,133✔
5212
                {
5213
                    retval = elf_level_pattern.first;
98,382✔
5214
                    break;
98,382✔
5215
                }
5216
            }
5217
        }
5218

5219
        if (sbc != nullptr
160,897✔
5220
            && sf.length() <= lnav::small_string_map::MAX_KEY_SIZE)
160,897✔
5221
        {
5222
            sbc->sbc_level_cache.insert(sf, retval);
5,666✔
5223
        }
5224
    }
5225

5226
    return retval;
205,151✔
5227
}
5228

5229
logline_value_meta
5230
external_log_format::get_value_meta(intern_string_t field_name,
4,630✔
5231
                                    value_kind_t kind)
5232
{
5233
    const auto iter = this->elf_value_defs.find(field_name);
4,630✔
5234
    if (iter == this->elf_value_defs.end()) {
4,630✔
5235
        auto retval = logline_value_meta(
5236
            field_name, kind, logline_value_meta::external_column{}, this);
390✔
5237

5238
        retval.lvm_hidden = this->jlf_hide_extra;
390✔
5239
        return retval;
390✔
5240
    }
390✔
5241

5242
    auto lvm = iter->second->vd_meta;
4,240✔
5243

5244
    lvm.lvm_kind = kind;
4,240✔
5245
    return lvm;
4,240✔
5246
}
4,240✔
5247

5248
logline_value_meta
5249
external_log_format::get_value_meta(yajlpp_parse_context* ypc,
10,156✔
5250
                                    const value_def* vd,
5251
                                    value_kind_t kind)
5252
{
5253
    if (vd == nullptr) {
10,156✔
5254
        auto retval = logline_value_meta(
5255
            ypc->get_path(), kind, logline_value_meta::external_column{}, this);
697✔
5256

5257
        retval.lvm_hidden = this->jlf_hide_extra;
697✔
5258
        return retval;
697✔
5259
    }
697✔
5260

5261
    auto lvm = vd->vd_meta;
9,459✔
5262

5263
    switch (vd->vd_meta.lvm_kind) {
9,459✔
5264
        case value_kind_t::VALUE_TIMESTAMP:
285✔
5265
            break;
285✔
5266
        default:
9,174✔
5267
            lvm.lvm_kind = kind;
9,174✔
5268
            break;
9,174✔
5269
    }
5270
    return lvm;
9,459✔
5271
}
9,459✔
5272

5273
void
5274
external_log_format::json_append(const log_format_file_state& lffs,
7,600✔
5275
                                 const json_format_element& jfe,
5276
                                 const value_def* vd,
5277
                                 const string_fragment& sf)
5278
{
5279
    if (jfe.jfe_align == json_format_element::align_t::RIGHT) {
7,600✔
5280
        auto sf_width = sf.column_width();
263✔
5281
        if (sf_width < jfe.jfe_min_width) {
263✔
5282
            this->json_append_to_cache(jfe.jfe_min_width - sf_width);
6✔
5283
        } else if (jfe.jfe_auto_width && vd != nullptr
24✔
5284
                   && sf_width
281✔
5285
                       < lffs.lffs_value_stats[vd->vd_meta.lvm_values_index
24✔
5286
                                                   .value()]
24✔
5287
                             .lvs_width)
24✔
5288
        {
5289
            this->json_append_to_cache(
8✔
5290
                lffs.lffs_value_stats[vd->vd_meta.lvm_values_index.value()]
8✔
5291
                    .lvs_width
8✔
5292
                - sf_width);
8✔
5293
        }
5294
    }
5295
    this->json_append_to_cache(sf.data(), sf.length());
7,600✔
5296
    if ((jfe.jfe_align == json_format_element::align_t::LEFT
7,600✔
5297
         || jfe.jfe_align == json_format_element::align_t::NONE)
7,546✔
5298
        && (jfe.jfe_min_width > 0 || jfe.jfe_auto_width))
7,337✔
5299
    {
5300
        auto sf_width = sf.column_width();
1,309✔
5301
        if (sf_width < jfe.jfe_min_width) {
1,309✔
5302
            this->json_append_to_cache(jfe.jfe_min_width - sf_width);
382✔
5303
        } else if (jfe.jfe_auto_width && vd != nullptr
793✔
5304
                   && sf_width
1,720✔
5305
                       < lffs.lffs_value_stats[vd->vd_meta.lvm_values_index
688✔
5306
                                                   .value()]
688✔
5307
                             .lvs_width)
688✔
5308
        {
5309
            this->json_append_to_cache(
276✔
5310
                lffs.lffs_value_stats[vd->vd_meta.lvm_values_index.value()]
276✔
5311
                    .lvs_width
276✔
5312
                - sf_width);
276✔
5313
        }
5314
    }
5315
}
7,600✔
5316

5317
intern_string_t
5318
external_log_format::get_pattern_name(const pattern_locks& pl,
59✔
5319
                                      uint64_t line_number) const
5320
{
5321
    if (this->elf_type != elf_type_t::ELF_TYPE_TEXT) {
59✔
5322
        static auto structured = intern_string::lookup("structured");
5323

5324
        return structured;
×
5325
    }
5326
    auto pat_index = pl.pattern_index_for_line(line_number);
59✔
5327
    return this->elf_pattern_order[pat_index]->p_name;
59✔
5328
}
5329

5330
std::string
5331
log_format::get_pattern_path(const pattern_locks& pl,
×
5332
                             uint64_t line_number) const
5333
{
5334
    auto pat_index = pl.pattern_index_for_line(line_number);
×
5335
    return fmt::format(FMT_STRING("builtin ({})"), pat_index);
×
5336
}
5337

5338
intern_string_t
5339
log_format::get_pattern_name(const pattern_locks& pl,
6✔
5340
                             uint64_t line_number) const
5341
{
5342
    char pat_str[128];
5343

5344
    auto pat_index = pl.pattern_index_for_line(line_number);
6✔
5345
    auto to_n_res = fmt::format_to_n(
×
5346
        pat_str, sizeof(pat_str) - 1, FMT_STRING("builtin ({})"), pat_index);
18✔
5347
    pat_str[to_n_res.size] = '\0';
6✔
5348
    return intern_string::lookup(pat_str);
12✔
5349
}
5350

5351
std::shared_ptr<log_format>
5352
log_format::find_root_format(const char* name)
1,374✔
5353
{
5354
    auto& fmts = get_root_formats();
1,374✔
5355
    for (auto& lf : fmts) {
55,369✔
5356
        if (lf->get_name() == name) {
55,369✔
5357
            return lf;
1,374✔
5358
        }
5359
    }
5360
    return nullptr;
×
5361
}
5362

5363
exttm
5364
log_format::tm_for_display(logfile::iterator ll, string_fragment sf)
986✔
5365
{
5366
    auto adjusted_time = ll->get_timeval();
986✔
5367
    exttm retval;
986✔
5368

5369
    retval.et_nsec = std::chrono::duration_cast<std::chrono::nanoseconds>(
986✔
5370
                         std::chrono::microseconds{adjusted_time.tv_usec})
×
5371
                         .count();
986✔
5372
    if (this->lf_timestamp_flags & ETF_NANOS_SET) {
986✔
5373
        timeval actual_tv;
5374
        exttm tm;
5✔
5375
        if (this->lf_date_time.scan(sf.data(),
10✔
5376
                                    sf.length(),
5✔
5377
                                    this->get_timestamp_formats(),
5378
                                    &tm,
5379
                                    actual_tv,
5380
                                    false))
5381
        {
5382
            adjusted_time.tv_usec = actual_tv.tv_usec;
5✔
5383
            retval.et_nsec = tm.et_nsec;
5✔
5384
        }
5385
    }
5386
    gmtime_r(&adjusted_time.tv_sec, &retval.et_tm);
986✔
5387
    retval.et_flags = this->lf_timestamp_flags;
986✔
5388
    if (this->lf_timestamp_flags & ETF_ZONE_SET
986✔
5389
        && this->lf_date_time.dts_zoned_to_local)
896✔
5390
    {
5391
        retval.et_flags &= ~ETF_Z_IS_UTC;
896✔
5392
    }
5393
    retval.et_gmtoff = this->lf_date_time.dts_local_offset_cache;
986✔
5394

5395
    return retval;
1,972✔
5396
}
5397

5398
pattern_for_lines::pattern_for_lines(uint32_t pfl_line, uint32_t pfl_pat_index)
2,998✔
5399
    : pfl_line(pfl_line), pfl_pat_index(pfl_pat_index)
2,998✔
5400
{
5401
}
2,998✔
5402

5403
void
5404
logline_value_stats::merge(const logline_value_stats& other)
16,161✔
5405
{
5406
    if (other.lvs_width > this->lvs_width) {
16,161✔
5407
        this->lvs_width = other.lvs_width;
4,046✔
5408
    }
5409

5410
    if (other.lvs_count == 0) {
16,161✔
5411
        return;
15,011✔
5412
    }
5413

5414
    require(other.lvs_min_value <= other.lvs_max_value);
1,150✔
5415

5416
    if (other.lvs_min_value < this->lvs_min_value) {
1,150✔
5417
        this->lvs_min_value = other.lvs_min_value;
782✔
5418
    }
5419
    if (other.lvs_max_value > this->lvs_max_value) {
1,150✔
5420
        this->lvs_max_value = other.lvs_max_value;
1,078✔
5421
    }
5422
    this->lvs_count += other.lvs_count;
1,150✔
5423
    this->lvs_total += other.lvs_total;
1,150✔
5424
    this->lvs_tdigest.insert(other.lvs_tdigest);
1,150✔
5425
    ensure(this->lvs_count >= 0);
1,150✔
5426
    ensure(this->lvs_min_value <= this->lvs_max_value);
1,150✔
5427
}
5428

5429
void
5430
logline_value_stats::add_value(double value)
31,471✔
5431
{
5432
    if (value < this->lvs_min_value) {
31,471✔
5433
        this->lvs_min_value = value;
1,671✔
5434
    }
5435
    if (value > this->lvs_max_value) {
31,471✔
5436
        this->lvs_max_value = value;
2,389✔
5437
    }
5438
    this->lvs_count += 1;
31,471✔
5439
    this->lvs_total += value;
31,471✔
5440
    this->lvs_tdigest.insert(value);
31,471✔
5441
}
31,471✔
5442

5443
std::vector<logline_value_meta>
5444
external_log_format::get_value_metadata() const
9,548✔
5445
{
5446
    std::vector<logline_value_meta> retval;
9,548✔
5447

5448
    for (const auto& vd : this->elf_value_def_order) {
100,254✔
5449
        retval.emplace_back(vd->vd_meta);
90,706✔
5450
    }
5451

5452
    return retval;
9,548✔
5453
}
×
5454

5455
std::optional<size_t>
5456
external_log_format::stats_index_for_value(const intern_string_t& name) const
73✔
5457
{
5458
    const auto iter = this->elf_value_defs.find(name);
73✔
5459
    if (iter != this->elf_value_defs.end()
73✔
5460
        && iter->second->vd_meta.lvm_values_index)
73✔
5461
    {
5462
        return iter->second->vd_meta.lvm_values_index.value();
73✔
5463
    }
5464

5465
    return std::nullopt;
×
5466
}
5467

5468
std::string
5469
external_log_format::get_pattern_regex(const pattern_locks& pl,
17✔
5470
                                       uint64_t line_number) const
5471
{
5472
    if (this->elf_type != elf_type_t::ELF_TYPE_TEXT) {
17✔
5473
        return "";
2✔
5474
    }
5475
    auto pat_index = pl.pattern_index_for_line(line_number);
16✔
5476
    return this->elf_pattern_order[pat_index]->p_pcre.pp_value->get_pattern();
16✔
5477
}
5478

5479
bool
5480
external_log_format::hide_field(const intern_string_t field_name, bool val)
12✔
5481
{
5482
    const auto vd_iter = this->elf_value_defs.find(field_name);
12✔
5483
    if (vd_iter == this->elf_value_defs.end()) {
12✔
5484
        log_warning("field to hide not found: %s.%s",
1✔
5485
                    this->elf_name.c_str(),
5486
                    field_name.c_str());
5487
        return false;
1✔
5488
    }
5489

5490
    vd_iter->second->vd_meta.lvm_user_hidden = val;
11✔
5491
    if (this->elf_type == elf_type_t::ELF_TYPE_JSON) {
11✔
5492
        bool found = false;
2✔
5493

5494
        if (!field_name.to_string_fragment().find('#')) {
2✔
5495
            for (const auto& jfe : this->jlf_line_format) {
18✔
5496
                if (jfe.jfe_value.pp_value == field_name) {
17✔
5497
                    log_debug(
×
5498
                        "hide-field not triggering rebuild since it is in "
5499
                        "line-format: %s.%s",
5500
                        this->elf_name.c_str(),
5501
                        field_name.c_str());
5502
                    found = true;
×
5503
                    break;
×
5504
                }
5505
            }
5506
        }
5507
        if (!found) {
2✔
5508
            log_info("format field %s.%s changed, rebuilding",
2✔
5509
                     this->elf_name.get(),
5510
                     field_name.get());
5511
            this->elf_value_defs_state->vds_generation += 1;
2✔
5512
        }
5513
    }
5514
    return true;
11✔
5515
}
5516

5517
bool
5518
external_log_format::format_changed()
2,847✔
5519
{
5520
    if (this->elf_specialized_value_defs_state.vds_generation
5,694✔
5521
        != this->elf_value_defs_state->vds_generation)
2,847✔
5522
    {
5523
        this->elf_specialized_value_defs_state = *this->elf_value_defs_state;
2✔
5524
        this->jlf_cached_offset = -1;
2✔
5525
        return true;
2✔
5526
    }
5527

5528
    return false;
2,845✔
5529
}
5530

5531
bool
5532
format_tag_def::path_restriction::matches(const char* fn) const
5✔
5533
{
5534
    return fnmatch(this->p_glob.c_str(), fn, 0) == 0;
5✔
5535
}
5536

5537
bool
5538
format_partition_def::path_restriction::matches(const char* fn) const
5✔
5539
{
5540
    return fnmatch(this->p_glob.c_str(), fn, 0) == 0;
5✔
5541
}
5542

5543
int
5544
pattern_locks::pattern_index_for_line(uint64_t line_number) const
6,977✔
5545
{
5546
    if (this->pl_lines.empty()) {
6,977✔
5547
        return -1;
×
5548
    }
5549

5550
    auto iter
5551
        = std::lower_bound(this->pl_lines.cbegin(),
6,977✔
5552
                           this->pl_lines.cend(),
5553
                           line_number,
5554
                           [](const pattern_for_lines& pfl, uint32_t line) {
7,036✔
5555
                               return pfl.pfl_line < line;
7,036✔
5556
                           });
5557

5558
    if (iter == this->pl_lines.end() || iter->pfl_line != line_number) {
6,977✔
5559
        --iter;
6,275✔
5560
    }
5561

5562
    return iter->pfl_pat_index;
6,977✔
5563
}
5564

5565
/* XXX */
5566
#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