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

tstack / lnav / 21083135857-2765

16 Jan 2026 10:37PM UTC coverage: 68.999% (+0.05%) from 68.95%
21083135857-2765

push

github

tstack
[otel_format] minor fixes to get this working

The ":header" variable is hex-encoded since it can
be binary.  So, the regex needs to match that.

Since "timestamp_ns" is a string and not a number,
a "timestamp-format" of "%9" should be used instead
of "timestamp-divisor".

Related to #1613

51841 of 75133 relevant lines covered (69.0%)

437291.43 hits per line

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

85.34
/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

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

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

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

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

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

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

103
    return *this;
841✔
104
}
105

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

116
    return *this;
364✔
117
}
118

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

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

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

148
    return *this;
364✔
149
}
150

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

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

164
    return *this;
477✔
165
}
166

167
void
168
log_level_stats::update_msg_count(log_level_t lvl, int32_t amount)
38,715✔
169
{
170
    switch (lvl) {
38,715✔
171
        case LEVEL_FATAL:
3,085✔
172
        case LEVEL_CRITICAL:
173
        case LEVEL_ERROR:
174
            this->lls_error_count += amount;
3,085✔
175
            break;
3,085✔
176
        case LEVEL_WARNING:
196✔
177
            this->lls_warning_count += amount;
196✔
178
            break;
196✔
179
        default:
35,434✔
180
            break;
35,434✔
181
    }
182
    this->lls_total_count += amount;
38,715✔
183
}
38,715✔
184

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

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

210
    return retval;
12,267✔
211
}
212

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

242
    return retval;
12,942✔
243
}
244

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

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

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

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

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

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

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

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

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

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

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

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

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

371
    return retval;
54✔
372
}
373

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

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

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

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

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

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

400
        last = next + 1;
×
401
    }
402

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

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

413
    return retval;
×
414
}
415

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

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

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

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

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

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

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

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

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

505
    switch (this->lv_meta.lvm_kind) {
9,895✔
506
        case value_kind_t::VALUE_NULL:
15✔
507
            return "null";
30✔
508

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

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

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

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

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

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

571
    return {buffer};
1,050✔
572
}
573

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

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

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

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

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

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

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

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

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

651
const char*
652
logline_value::text_value() const
68,530✔
653
{
654
    if (this->lv_str) {
68,530✔
655
        return this->lv_str->c_str();
159✔
656
    }
657
    if (this->lv_frag.empty()) {
68,371✔
658
        if (this->lv_intern_string.empty()) {
15✔
659
            return "";
15✔
660
        }
661
        return this->lv_intern_string.get();
×
662
    }
663
    return this->lv_frag.data();
68,356✔
664
}
665

666
size_t
667
logline_value::text_length() const
68,563✔
668
{
669
    if (this->lv_str) {
68,563✔
670
        return this->lv_str->size();
159✔
671
    }
672
    if (this->lv_frag.empty()) {
68,404✔
673
        return this->lv_intern_string.size();
15✔
674
    }
675
    return this->lv_frag.length();
68,389✔
676
}
677

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

684
void
685
logline_value_vector::clear()
42,151✔
686
{
687
    this->lvv_values.clear();
42,151✔
688
    this->lvv_sbr.disown();
42,151✔
689
    this->lvv_opid_value = std::nullopt;
42,151✔
690
    this->lvv_opid_provenance = opid_provenance::none;
42,151✔
691
    this->lvv_thread_id_value = std::nullopt;
42,151✔
692
}
42,151✔
693

694
logline_value_vector::logline_value_vector(const logline_value_vector& other)
803✔
695
    : lvv_sbr(other.lvv_sbr.clone()), lvv_values(other.lvv_values),
803✔
696
      lvv_opid_value(other.lvv_opid_value),
803✔
697
      lvv_opid_provenance(other.lvv_opid_provenance),
803✔
698
      lvv_thread_id_value(other.lvv_thread_id_value)
803✔
699
{
700
}
803✔
701

702
logline_value_vector&
703
logline_value_vector::operator=(const logline_value_vector& other)
406✔
704
{
705
    this->lvv_sbr = other.lvv_sbr.clone();
406✔
706
    this->lvv_values = other.lvv_values;
406✔
707
    this->lvv_opid_value = other.lvv_opid_value;
406✔
708
    this->lvv_opid_provenance = other.lvv_opid_provenance;
406✔
709
    this->lvv_thread_id_value = other.lvv_thread_id_value;
406✔
710

711
    return *this;
406✔
712
}
713

714
std::vector<std::shared_ptr<log_format>> log_format::lf_root_formats;
715

716
std::vector<std::shared_ptr<log_format>>&
717
log_format::get_root_formats()
17,268✔
718
{
719
    return lf_root_formats;
17,268✔
720
}
721

722
void
723
external_log_format::update_op_description(
4,935✔
724
    const std::vector<opid_descriptors*>& desc_defs_vec,
725
    log_op_description& lod,
726
    const pattern* fpat,
727
    const lnav::pcre2pp::match_data& md)
728
{
729
    std::optional<std::string> desc_elem_str;
4,935✔
730
    if (!lod.lod_index) {
4,935✔
731
        for (const auto& desc_defs : desc_defs_vec) {
2,975✔
732
            if (lod.lod_index) {
1,240✔
733
                break;
26✔
734
            }
735
            for (const auto& desc_def : *desc_defs->od_descriptors) {
1,522✔
736
                auto desc_field_index_iter = fpat->p_value_name_to_index.find(
1,312✔
737
                    desc_def.od_field.pp_value);
1,312✔
738

739
                if (desc_field_index_iter == fpat->p_value_name_to_index.end())
1,312✔
740
                {
741
                    continue;
43✔
742
                }
743

744
                auto desc_cap_opt = md[desc_field_index_iter->second];
1,308✔
745
                if (!desc_cap_opt) {
1,308✔
746
                    continue;
39✔
747
                }
748

749
                desc_elem_str = desc_def.matches(desc_cap_opt.value());
1,269✔
750
                if (desc_elem_str) {
1,269✔
751
                    lod.lod_index = desc_defs->od_index;
1,004✔
752
                    break;
1,004✔
753
                }
754
            }
755
        }
756
    }
757
    if (lod.lod_index) {
4,935✔
758
        const auto& desc_def_v
759
            = *desc_defs_vec[lod.lod_index.value()]->od_descriptors;
4,178✔
760
        auto& desc_v = lod.lod_elements;
4,178✔
761

762
        if (desc_def_v.size() == desc_v.size()
4,178✔
763
            || (this->elf_opid_field.empty() && !desc_v.empty()))
4,178✔
764
        {
765
            return;
3,174✔
766
        }
767
        for (size_t desc_def_index = 0; desc_def_index < desc_def_v.size();
2,863✔
768
             desc_def_index++)
769
        {
770
            const auto& desc_def = desc_def_v[desc_def_index];
1,859✔
771
            auto found_desc = desc_v.value_for(desc_def_index);
1,859✔
772
            auto desc_field_index_iter
773
                = fpat->p_value_name_to_index.find(desc_def.od_field.pp_value);
1,859✔
774

775
            if (desc_field_index_iter == fpat->p_value_name_to_index.end()) {
1,859✔
776
                continue;
35✔
777
            }
778
            auto desc_cap_opt = md[desc_field_index_iter->second];
1,859✔
779
            if (!desc_cap_opt) {
1,859✔
780
                continue;
35✔
781
            }
782

783
            if (!desc_elem_str) {
1,824✔
784
                desc_elem_str = desc_def.matches(desc_cap_opt.value());
820✔
785
            }
786
            if (desc_elem_str) {
1,824✔
787
                if (!found_desc) {
1,821✔
788
                    desc_v.insert(desc_def_index, desc_elem_str.value());
1,821✔
789
                } else if (!desc_elem_str->empty()) {
×
790
                    found_desc.value()->append(desc_def.od_joiner);
×
791
                    found_desc.value()->append(desc_elem_str.value());
×
792
                }
793
            }
794
            desc_elem_str = std::nullopt;
1,824✔
795
        }
796
    }
797
}
4,935✔
798

799
void
800
external_log_format::update_op_description(
3,201✔
801
    const std::vector<opid_descriptors*>& desc_defs_vec,
802
    log_op_description& lod)
803
{
804
    std::optional<std::string> desc_elem_str;
3,201✔
805
    if (!lod.lod_index) {
3,201✔
806
        for (const auto& desc_defs : desc_defs_vec) {
3,210✔
807
            if (lod.lod_index) {
15✔
808
                break;
×
809
            }
810
            for (const auto& desc_def : *desc_defs->od_descriptors) {
15✔
811
                auto desc_cap_iter
812
                    = this->lf_desc_captures.find(desc_def.od_field.pp_value);
15✔
813

814
                if (desc_cap_iter == this->lf_desc_captures.end()) {
15✔
815
                    continue;
×
816
                }
817
                desc_elem_str = desc_def.matches(desc_cap_iter->second);
15✔
818
                if (desc_elem_str) {
15✔
819
                    lod.lod_index = desc_defs->od_index;
15✔
820
                    break;
15✔
821
                }
822
            }
823
        }
824
    }
825
    if (lod.lod_index) {
3,201✔
826
        const auto& desc_def_v
827
            = *desc_defs_vec[lod.lod_index.value()]->od_descriptors;
21✔
828
        auto& desc_v = lod.lod_elements;
21✔
829

830
        if (desc_def_v.size() == desc_v.size()
21✔
831
            || (this->elf_opid_field.empty() && !desc_v.empty()))
21✔
832
        {
833
            return;
6✔
834
        }
835
        for (size_t desc_def_index = 0; desc_def_index < desc_def_v.size();
45✔
836
             desc_def_index++)
837
        {
838
            const auto& desc_def = desc_def_v[desc_def_index];
30✔
839
            auto found_desc = desc_v.value_for(desc_def_index);
30✔
840
            auto desc_cap_iter
841
                = this->lf_desc_captures.find(desc_def.od_field.pp_value);
30✔
842
            if (desc_cap_iter == this->lf_desc_captures.end()) {
30✔
843
                continue;
×
844
            }
845

846
            if (!desc_elem_str) {
30✔
847
                desc_elem_str = desc_def.matches(desc_cap_iter->second);
15✔
848
            }
849
            if (desc_elem_str) {
30✔
850
                if (!found_desc) {
30✔
851
                    desc_v.insert(desc_def_index, desc_elem_str.value());
30✔
852
                } else if (!desc_elem_str->empty()) {
×
853
                    found_desc.value()->append(desc_def.od_joiner);
×
854
                    found_desc.value()->append(desc_elem_str.value());
×
855
                }
856
            }
857
            desc_elem_str = std::nullopt;
30✔
858
        }
859
    }
860
}
3,201✔
861

862
static bool
863
next_format(
2,266,108✔
864
    const std::vector<std::shared_ptr<external_log_format::pattern>>& patterns,
865
    int& index,
866
    int& locked_index)
867
{
868
    bool retval = true;
2,266,108✔
869

870
    if (locked_index == -1) {
2,266,108✔
871
        index += 1;
2,258,834✔
872
        if (index >= (int) patterns.size()) {
2,258,834✔
873
            retval = false;
684,353✔
874
        }
875
    } else if (index == locked_index) {
7,274✔
876
        retval = false;
1✔
877
    } else {
878
        index = locked_index;
7,273✔
879
    }
880

881
    return retval;
2,266,108✔
882
}
883

884
bool
885
log_format::next_format(const pcre_format* fmt, int& index, int& locked_index)
176,483✔
886
{
887
    bool retval = true;
176,483✔
888

889
    if (locked_index == -1) {
176,483✔
890
        index += 1;
176,301✔
891
        if (fmt[index].name == nullptr) {
176,301✔
892
            retval = false;
9,907✔
893
        }
894
    } else if (index == locked_index) {
182✔
895
        retval = false;
36✔
896
    } else {
897
        index = locked_index;
146✔
898
    }
899

900
    return retval;
176,483✔
901
}
902

903
const char*
904
log_format::log_scanf(scan_batch_context& sbc,
12,338✔
905
                      uint32_t line_number,
906
                      string_fragment line,
907
                      const pcre_format* fmt,
908
                      const char* time_fmt[],
909
                      exttm* tm_out,
910
                      timeval* tv_out,
911

912
                      string_fragment* ts_out,
913
                      std::optional<string_fragment>* level_out)
914
{
915
    int curr_fmt = -1;
12,338✔
916
    const char* retval = nullptr;
12,338✔
917
    bool done = false;
12,338✔
918
    int pat_index = sbc.sbc_pattern_locks.last_pattern_index();
12,338✔
919

920
    while (!done && next_format(fmt, curr_fmt, pat_index)) {
178,878✔
921
        thread_local auto md = lnav::pcre2pp::match_data::unitialized();
166,540✔
922

923
        auto match_res = fmt[curr_fmt]
166,540✔
924
                             .pcre->capture_from(line)
166,540✔
925
                             .into(md)
166,540✔
926
                             .matches(PCRE2_NO_UTF_CHECK)
333,080✔
927
                             .ignore_error();
166,540✔
928
        if (!match_res) {
166,540✔
929
            retval = nullptr;
143,130✔
930
        } else {
931
            auto ts = md[fmt[curr_fmt].pf_timestamp_index];
23,410✔
932

933
            retval = this->lf_date_time.scan(
23,410✔
934
                ts->data(), ts->length(), nullptr, tm_out, *tv_out);
23,410✔
935

936
            if (retval == nullptr) {
23,410✔
937
                auto ls = this->lf_date_time.unlock();
21,015✔
938
                retval = this->lf_date_time.scan(
21,015✔
939
                    ts->data(), ts->length(), nullptr, tm_out, *tv_out);
21,015✔
940
                if (retval != nullptr) {
21,015✔
941
                    auto old_flags
×
942
                        = this->lf_timestamp_flags & DATE_TIME_SET_FLAGS;
×
943
                    auto new_flags = tm_out->et_flags & DATE_TIME_SET_FLAGS;
×
944

945
                    // It is unlikely a valid timestamp would lose much
946
                    // precision.
947
                    if (new_flags != old_flags) {
×
948
                        retval = nullptr;
×
949
                    }
950
                }
951
                if (retval == nullptr) {
21,015✔
952
                    this->lf_date_time.relock(ls);
21,015✔
953
                } else {
954
                    log_debug(
×
955
                        "%d: changed time format to '%s' due to %.*s",
956
                        line_number,
957
                        PTIMEC_FORMAT_STR[this->lf_date_time.dts_fmt_lock],
958
                        ts->length(),
959
                        ts->data());
960
                }
961
            }
962

963
            if (retval) {
23,410✔
964
                *ts_out = ts.value();
2,395✔
965
                if (md[2]) {
2,395✔
966
                    *level_out = md[2];
248✔
967
                } else {
968
                    *level_out = line.substr(md[0]->sf_end);
2,147✔
969
                }
970
                if (curr_fmt != pat_index) {
2,395✔
971
                    uint32_t lock_line;
972

973
                    if (sbc.sbc_pattern_locks.empty()) {
2,285✔
974
                        lock_line = 0;
2,285✔
975
                    } else {
976
                        lock_line = line_number;
×
977
                    }
978

979
                    sbc.sbc_pattern_locks.pl_lines.emplace_back(lock_line,
2,285✔
980
                                                                curr_fmt);
981
                }
982
                this->lf_timestamp_flags = tm_out->et_flags;
2,395✔
983
                done = true;
2,395✔
984
            }
985
        }
986
    }
987

988
    return retval;
12,338✔
989
}
990

991
void
992
log_format::annotate(logfile* lf,
37,522✔
993
                     uint64_t line_number,
994
                     string_attrs_t& sa,
995
                     logline_value_vector& values) const
996
{
997
    if (lf != nullptr && !values.lvv_opid_value) {
37,522✔
998
        const auto& bm = lf->get_bookmark_metadata();
2,895✔
999
        auto bm_iter = bm.find(line_number);
2,895✔
1000
        if (bm_iter != bm.end() && !bm_iter->second.bm_opid.empty()) {
2,895✔
1001
            values.lvv_opid_value = bm_iter->second.bm_opid;
6✔
1002
            values.lvv_opid_provenance
1003
                = logline_value_vector::opid_provenance::user;
6✔
1004
        }
1005
    }
1006
}
37,522✔
1007

1008
void
1009
log_format::check_for_new_year(std::vector<logline>& dst,
1,494✔
1010
                               exttm etm,
1011
                               timeval log_tv) const
1012
{
1013
    if (dst.empty()) {
1,494✔
1014
        return;
219✔
1015
    }
1016

1017
    time_t diff
1018
        = dst.back().get_time<std::chrono::seconds>().count() - log_tv.tv_sec;
1,275✔
1019
    int off_year = 0, off_month = 0, off_day = 0, off_hour = 0;
1,275✔
1020
    bool do_change = true;
1,275✔
1021

1022
    if (diff <= 0) {
1,275✔
1023
        return;
1,215✔
1024
    }
1025
    if ((etm.et_flags & ETF_MONTH_SET) && diff >= (24 * 60 * 60)) {
60✔
1026
        off_year = 1;
11✔
1027
    } else if (diff >= (24 * 60 * 60)) {
49✔
1028
        off_month = 1;
2✔
1029
    } else if (!(etm.et_flags & ETF_DAY_SET) && (diff >= (60 * 60))) {
47✔
1030
        off_day = 1;
1✔
1031
    } else if (!(etm.et_flags & ETF_HOUR_SET) && (diff >= 60)) {
46✔
1032
        off_hour = 1;
4✔
1033
    } else {
1034
        do_change = false;
42✔
1035
    }
1036

1037
    if (!do_change) {
60✔
1038
        return;
42✔
1039
    }
1040
    log_debug("%zu:detected time rollover; offsets=%d %d %d %d",
18✔
1041
              dst.size(),
1042
              off_year,
1043
              off_month,
1044
              off_day,
1045
              off_hour);
1046
    for (auto& ll : dst) {
68✔
1047
        time_t ot = ll.get_time<std::chrono::seconds>().count();
50✔
1048
        struct tm otm;
1049

1050
        gmtime_r(&ot, &otm);
50✔
1051
        otm.tm_yday = -1;
50✔
1052
        if (otm.tm_year < off_year) {
50✔
1053
            otm.tm_year = 0;
×
1054
        } else {
1055
            otm.tm_year -= off_year;
50✔
1056
        }
1057
        otm.tm_mon -= off_month;
50✔
1058
        if (otm.tm_mon < 0) {
50✔
1059
            otm.tm_mon += 12;
2✔
1060
        }
1061
        auto new_time = tm2sec(&otm);
50✔
1062
        if (new_time == -1) {
50✔
1063
            continue;
×
1064
        }
1065
        new_time -= (off_day * 24 * 60 * 60) + (off_hour * 60 * 60);
50✔
1066
        auto old_sub = ll.get_subsecond_time<std::chrono::microseconds>();
50✔
1067
        ll.set_time(std::chrono::seconds{new_time});
50✔
1068
        ll.set_subsecond_time(old_sub);
50✔
1069
    }
1070
}
1071

1072
/*
1073
 * XXX This needs some cleanup.
1074
 */
1075
struct json_log_userdata {
1076
    json_log_userdata(shared_buffer_ref& sbr, scan_batch_context* sbc)
13,486✔
1077
        : jlu_shared_buffer(sbr), jlu_batch_context(sbc)
26,972✔
1078
    {
1079
    }
13,486✔
1080

1081
    const external_log_format::value_def* get_field_def(
336,662✔
1082
        yajlpp_parse_context* ypc)
1083
    {
1084
        const auto field_frag = ypc->get_path_as_string_fragment();
336,662✔
1085
        auto* format = this->jlu_format;
336,662✔
1086

1087
        if (this->jlu_read_order_index < format->elf_value_def_read_order.size()
336,662✔
1088
            && format->elf_value_def_read_order[this->jlu_read_order_index]
451,435✔
1089
                    .first
1090
                == field_frag)
114,773✔
1091
        {
1092
            return format
1093
                ->elf_value_def_read_order[this->jlu_read_order_index++]
109,788✔
1094
                .second;
109,788✔
1095
        }
1096

1097
        format->elf_value_def_read_order.resize(this->jlu_read_order_index);
226,874✔
1098
        auto vd_iter = format->elf_value_def_frag_map.find(field_frag);
226,874✔
1099
        if (vd_iter != format->elf_value_def_frag_map.end()) {
226,874✔
1100
            format->elf_value_def_read_order.emplace_back(vd_iter->first,
196,595✔
1101
                                                          vd_iter->second);
196,595✔
1102
            this->jlu_read_order_index += 1;
196,595✔
1103
            return vd_iter->second;
196,595✔
1104
        }
1105

1106
        auto owned_frag = field_frag.to_owned(format->elf_allocator);
30,279✔
1107
        format->elf_value_def_frag_map[owned_frag] = nullptr;
30,279✔
1108
        format->elf_value_def_read_order.emplace_back(owned_frag, nullptr);
30,279✔
1109
        this->jlu_read_order_index += 1;
30,279✔
1110
        return nullptr;
30,279✔
1111
    }
1112

1113
    void add_sub_lines_for(const external_log_format::value_def* vd,
245,909✔
1114
                           bool top_level,
1115
                           std::optional<double> val,
1116
                           const unsigned char* str,
1117
                           ssize_t len,
1118
                           yajl_string_props_t* props)
1119
    {
1120
        auto res = this->jlu_format->value_line_count(
245,909✔
1121
            *this->jlu_batch_context, vd, top_level, val, str, len, props);
245,909✔
1122
        this->jlu_has_ansi |= res.vlcr_has_ansi;
245,909✔
1123
        if (!res.vlcr_valid_utf) {
245,909✔
1124
            this->jlu_valid_utf = false;
×
1125
        }
1126
        this->jlu_sub_line_count += res.vlcr_count;
245,909✔
1127
        this->jlu_quality += res.vlcr_line_format_count;
245,909✔
1128
        if (res.vlcr_line_format_index) {
245,909✔
1129
            this->jlu_format_hits[res.vlcr_line_format_index.value()] = true;
3,207✔
1130
        }
1131
    }
245,909✔
1132

1133
    external_log_format* jlu_format{nullptr};
1134
    const logline* jlu_line{nullptr};
1135
    logline* jlu_base_line{nullptr};
1136
    int jlu_sub_line_count{1};
1137
    bool jlu_has_ansi{false};
1138
    bool jlu_valid_utf{true};
1139
    yajl_handle jlu_handle{nullptr};
1140
    const char* jlu_line_value{nullptr};
1141
    size_t jlu_line_size{0};
1142
    size_t jlu_sub_start{0};
1143
    uint32_t jlu_quality{0};
1144
    uint32_t jlu_strikes{0};
1145
    std::vector<bool> jlu_format_hits;
1146
    shared_buffer_ref& jlu_shared_buffer;
1147
    scan_batch_context* jlu_batch_context;
1148
    std::optional<string_fragment> jlu_opid_frag;
1149
    std::optional<string_fragment> jlu_opid_desc_frag;
1150
    std::optional<string_fragment> jlu_tid_frag;
1151
    std::optional<int64_t> jlu_tid_number;
1152
    std::optional<std::string> jlu_subid;
1153
    hasher jlu_opid_hasher;
1154
    std::chrono::microseconds jlu_duration{0};
13,486✔
1155
    exttm jlu_exttm;
1156
    size_t jlu_read_order_index{0};
1157
    subline_options jlu_subline_opts;
1158
};
1159

1160
static int read_json_field(yajlpp_parse_context* ypc,
1161
                           const unsigned char* str,
1162
                           size_t len,
1163
                           yajl_string_props_t*);
1164

1165
static int
1166
read_json_null(yajlpp_parse_context* ypc)
3,536✔
1167
{
1168
    json_log_userdata* jlu = (json_log_userdata*) ypc->ypc_userdata;
3,536✔
1169
    const auto* vd = jlu->get_field_def(ypc);
3,536✔
1170

1171
    jlu->add_sub_lines_for(
7,072✔
1172
        vd, ypc->is_level(1), std::nullopt, nullptr, -1, nullptr);
3,536✔
1173

1174
    return 1;
3,536✔
1175
}
1176

1177
static int
1178
read_json_bool(yajlpp_parse_context* ypc, int val)
16,832✔
1179
{
1180
    json_log_userdata* jlu = (json_log_userdata*) ypc->ypc_userdata;
16,832✔
1181
    const auto* vd = jlu->get_field_def(ypc);
16,832✔
1182

1183
    jlu->add_sub_lines_for(
33,664✔
1184
        vd, ypc->is_level(1), std::nullopt, nullptr, -1, nullptr);
16,832✔
1185

1186
    return 1;
16,832✔
1187
}
1188

1189
static int
1190
read_json_number(yajlpp_parse_context* ypc,
27,827✔
1191
                 const char* numberVal,
1192
                 size_t numberLen)
1193
{
1194
    auto* jlu = (json_log_userdata*) ypc->ypc_userdata;
27,827✔
1195
    auto number_frag = string_fragment::from_bytes(numberVal, numberLen);
27,827✔
1196
    std::optional<double> val;
27,827✔
1197

1198
    intern_string_t field_name;
27,827✔
1199
    const auto* vd = jlu->get_field_def(ypc);
27,827✔
1200
    if (vd != nullptr) {
27,827✔
1201
        field_name = vd->vd_meta.lvm_name;
1,619✔
1202
    }
1203

1204
    if (field_name.empty()) {
27,827✔
1205
    } else if (jlu->jlu_format->lf_timestamp_field == field_name) {
1,619✔
1206
        long long divisor = jlu->jlu_format->elf_timestamp_divisor;
113✔
1207
        auto scan_res = scn::scan_value<double>(number_frag.to_string_view());
113✔
1208
        if (!scan_res) {
113✔
1209
            log_error("invalid number %.*s", (int) numberLen, numberVal);
×
1210
            return 0;
×
1211
        }
1212
        auto ts_val = scan_res.value().value();
113✔
1213
        timeval tv;
1214
        tv.tv_sec = ts_val / divisor;
113✔
1215
        tv.tv_usec = fmod(ts_val, divisor) * (1000000.0 / divisor);
113✔
1216
        jlu->jlu_format->lf_date_time.to_localtime(tv.tv_sec, jlu->jlu_exttm);
113✔
1217
        tv.tv_sec = tm2sec(&jlu->jlu_exttm.et_tm);
113✔
1218
        jlu->jlu_exttm.et_gmtoff
1219
            = jlu->jlu_format->lf_date_time.dts_local_offset_cache;
113✔
1220
        jlu->jlu_exttm.et_flags
113✔
1221
            |= ETF_MACHINE_ORIENTED | ETF_SUB_NOT_IN_FORMAT | ETF_ZONE_SET;
113✔
1222
        if (divisor == 1000) {
113✔
1223
            jlu->jlu_exttm.et_flags |= ETF_MILLIS_SET;
17✔
1224
        } else {
1225
            jlu->jlu_exttm.et_flags |= ETF_MICROS_SET;
96✔
1226
        }
1227
        jlu->jlu_exttm.et_nsec = tv.tv_usec * 1000;
113✔
1228
        jlu->jlu_base_line->set_time(tv);
113✔
1229
    } else if (jlu->jlu_format->lf_subsecond_field == field_name) {
1,506✔
1230
        auto scan_res = scn::scan_value<double>(number_frag.to_string_view());
3✔
1231
        if (!scan_res) {
3✔
1232
            log_error("invalid number %.*s", (int) numberLen, numberVal);
×
1233
            return 0;
×
1234
        }
1235
        auto ts_val = scan_res.value().value();
3✔
1236

1237
        uint64_t millis = 0;
3✔
1238
        jlu->jlu_exttm.et_flags &= ~(ETF_MICROS_SET | ETF_MILLIS_SET);
3✔
1239
        switch (jlu->jlu_format->lf_subsecond_unit.value()) {
3✔
1240
            case log_format::subsecond_unit::milli:
×
1241
                millis = ts_val;
×
1242
                jlu->jlu_exttm.et_nsec = ts_val * 1000000;
×
1243
                jlu->jlu_exttm.et_flags |= ETF_MILLIS_SET;
×
1244
                break;
×
1245
            case log_format::subsecond_unit::micro:
×
1246
                millis = std::chrono::duration_cast<std::chrono::milliseconds>(
×
1247
                             std::chrono::microseconds((int64_t) ts_val))
×
1248
                             .count();
×
1249
                jlu->jlu_exttm.et_nsec = ts_val * 1000;
×
1250
                jlu->jlu_exttm.et_flags |= ETF_MICROS_SET;
×
1251
                break;
×
1252
            case log_format::subsecond_unit::nano:
3✔
1253
                millis = std::chrono::duration_cast<std::chrono::milliseconds>(
3✔
1254
                             std::chrono::nanoseconds((int64_t) ts_val))
3✔
1255
                             .count();
3✔
1256
                jlu->jlu_exttm.et_nsec = ts_val;
3✔
1257
                jlu->jlu_exttm.et_flags |= ETF_NANOS_SET;
3✔
1258
                break;
3✔
1259
        }
1260
        jlu->jlu_exttm.et_flags |= ETF_SUB_NOT_IN_FORMAT;
3✔
1261
        jlu->jlu_base_line->set_subsecond_time(
3✔
1262
            std::chrono::milliseconds(millis));
1263
    } else if (jlu->jlu_format->elf_level_field == field_name) {
1,503✔
1264
        if (jlu->jlu_format->elf_level_pairs.empty()) {
428✔
1265
            jlu->jlu_base_line->set_level(jlu->jlu_format->convert_level(
266✔
1266
                number_frag, jlu->jlu_batch_context));
1267
        } else {
1268
            auto scan_res
1269
                = scn::scan_int<int64_t>(number_frag.to_string_view());
162✔
1270
            if (!scan_res) {
162✔
1271
                log_error("invalid number %.*s", (int) numberLen, numberVal);
×
1272
                return 0;
×
1273
            }
1274
            auto level_int = scan_res.value().value();
162✔
1275

1276
            for (const auto& pair : jlu->jlu_format->elf_level_pairs) {
593✔
1277
                if (pair.first == level_int) {
531✔
1278
                    jlu->jlu_base_line->set_level(pair.second);
100✔
1279
                    break;
100✔
1280
                }
1281
            }
1282
        }
1283
    } else if (vd != nullptr) {
1,075✔
1284
        if (jlu->jlu_format->elf_thread_id_field == field_name) {
1,075✔
1285
            auto& sbc = *jlu->jlu_batch_context;
102✔
1286
            auto tid_iter = sbc.sbc_tids.ltis_tid_ranges.find(number_frag);
102✔
1287
            if (tid_iter == sbc.sbc_tids.ltis_tid_ranges.end()) {
102✔
1288
                jlu->jlu_tid_frag = number_frag.to_owned(sbc.sbc_allocator);
58✔
1289
            } else {
1290
                jlu->jlu_tid_frag = tid_iter->first;
44✔
1291
            }
1292
        }
1293
        if ((vd->vd_meta.lvm_kind == value_kind_t::VALUE_INTEGER
1,075✔
1294
             || vd->vd_meta.lvm_kind == value_kind_t::VALUE_FLOAT)
126✔
1295
            && !vd->vd_meta.lvm_foreign_key && !vd->vd_meta.lvm_identifier)
967✔
1296
        {
1297
            auto scan_res
1298
                = scn::scan_value<double>(number_frag.to_string_view());
168✔
1299
            if (!scan_res) {
168✔
1300
                log_error("invalid number %.*s", (int) numberLen, numberVal);
×
1301
                return 0;
×
1302
            }
1303
            val = scan_res.value().value();
168✔
1304
            if (jlu->jlu_format->elf_duration_field == field_name) {
168✔
1305
                jlu->jlu_duration = std::chrono::microseconds(
×
1306
                    static_cast<int64_t>(val.value() * 1000000));
18✔
1307
            }
1308
        }
1309
    }
1310

1311
    jlu->add_sub_lines_for(vd,
27,827✔
1312
                           ypc->is_level(1),
27,827✔
1313
                           val,
1314
                           (const unsigned char*) numberVal,
1315
                           numberLen,
1316
                           nullptr);
1317

1318
    return 1;
27,827✔
1319
}
1320

1321
static int
1322
json_array_start(void* ctx)
53,317✔
1323
{
1324
    auto* ypc = (yajlpp_parse_context*) ctx;
53,317✔
1325
    auto* jlu = (json_log_userdata*) ypc->ypc_userdata;
53,317✔
1326

1327
    if (ypc->ypc_path_index_stack.size() == 2) {
53,317✔
1328
        const auto* vd = jlu->get_field_def(ypc);
16,502✔
1329

1330
        jlu->add_sub_lines_for(vd, true, std::nullopt, nullptr, -1, nullptr);
16,502✔
1331
        jlu->jlu_sub_start = yajl_get_bytes_consumed(jlu->jlu_handle) - 1;
16,502✔
1332
    }
1333

1334
    return 1;
53,317✔
1335
}
1336

1337
static int
1338
json_array_start_const(void* ctx)
13,250✔
1339
{
1340
    auto* ypc = (yajlpp_parse_context*) ctx;
13,250✔
1341
    auto* jlu = (json_log_userdata*) ypc->ypc_userdata;
13,250✔
1342

1343
    if (ypc->ypc_path_index_stack.size() == 2) {
13,250✔
1344
        jlu->jlu_sub_start = yajl_get_bytes_consumed(jlu->jlu_handle) - 1;
3,579✔
1345
    }
1346

1347
    return 1;
13,250✔
1348
}
1349

1350
static int
1351
json_array_end(void* ctx)
13,230✔
1352
{
1353
    auto* ypc = (yajlpp_parse_context*) ctx;
13,230✔
1354
    auto* jlu = (json_log_userdata*) ypc->ypc_userdata;
13,230✔
1355

1356
    if (ypc->ypc_path_index_stack.size() == 1) {
13,230✔
1357
        const intern_string_t field_name = ypc->get_path_fragment_i(0);
3,573✔
1358
        size_t sub_end = yajl_get_bytes_consumed(jlu->jlu_handle);
3,573✔
1359
        auto json_frag = string_fragment::from_byte_range(
3,573✔
1360
            jlu->jlu_shared_buffer.get_data(), jlu->jlu_sub_start, sub_end);
3,573✔
1361
        jlu->jlu_format->jlf_line_values.lvv_values.emplace_back(
3,573✔
1362
            jlu->jlu_format->get_value_meta(field_name,
7,146✔
1363
                                            value_kind_t::VALUE_JSON),
1364
            json_frag);
1365
        if (field_name == jlu->jlu_format->elf_opid_field) {
3,573✔
1366
            jlu->jlu_opid_desc_frag = json_frag;
29✔
1367
        }
1368
    }
1369

1370
    return 1;
13,230✔
1371
}
1372

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

1379
    if (ypc->ypc_path_index_stack.size() == 1) {
53,090✔
1380
        const intern_string_t field_name = ypc->get_path_fragment_i(0);
16,473✔
1381
        size_t sub_end = yajl_get_bytes_consumed(jlu->jlu_handle);
16,473✔
1382
        auto json_frag = string_fragment::from_byte_range(
16,473✔
1383
            jlu->jlu_shared_buffer.get_data(), jlu->jlu_sub_start, sub_end);
16,473✔
1384
        if (field_name == jlu->jlu_format->elf_opid_field) {
16,473✔
1385
            jlu->jlu_opid_desc_frag = json_frag;
2,265✔
1386
        }
1387
    }
1388

1389
    return 1;
53,090✔
1390
}
1391

1392
static const json_path_container json_log_handlers = {
1393
    yajlpp::pattern_property_handler("\\w+")
1394
        .add_cb(read_json_null)
1395
        .add_cb(read_json_bool)
1396
        .add_cb(read_json_number)
1397
        .add_cb(read_json_field),
1398
};
1399

1400
static int rewrite_json_field(yajlpp_parse_context* ypc,
1401
                              const unsigned char* str,
1402
                              size_t len,
1403
                              yajl_string_props_t*);
1404

1405
static int
1406
rewrite_json_null(yajlpp_parse_context* ypc)
1,957✔
1407
{
1408
    auto jlu = (json_log_userdata*) ypc->ypc_userdata;
1,957✔
1409
    const auto* vd = jlu->get_field_def(ypc);
1,957✔
1410

1411
    if (!ypc->is_level(1) && vd == nullptr) {
1,957✔
1412
        return 1;
1,957✔
1413
    }
1414
    jlu->jlu_format->jlf_line_values.lvv_values.emplace_back(
×
1415
        jlu->jlu_format->get_value_meta(ypc, vd, value_kind_t::VALUE_NULL));
×
1416

1417
    return 1;
×
1418
}
1419

1420
static int
1421
rewrite_json_bool(yajlpp_parse_context* ypc, int val)
8,797✔
1422
{
1423
    auto* jlu = (json_log_userdata*) ypc->ypc_userdata;
8,797✔
1424
    const auto* vd = jlu->get_field_def(ypc);
8,797✔
1425

1426
    if (!ypc->is_level(1) && vd == nullptr) {
8,797✔
1427
        return 1;
7,855✔
1428
    }
1429
    jlu->jlu_format->jlf_line_values.lvv_values.emplace_back(
942✔
1430
        jlu->jlu_format->get_value_meta(ypc, vd, value_kind_t::VALUE_BOOLEAN),
942✔
1431
        (bool) val);
942✔
1432
    return 1;
942✔
1433
}
1434

1435
static int
1436
rewrite_json_int(yajlpp_parse_context* ypc, long long val)
10,795✔
1437
{
1438
    auto* jlu = (json_log_userdata*) ypc->ypc_userdata;
10,795✔
1439
    const auto* vd = jlu->get_field_def(ypc);
10,795✔
1440

1441
    if (vd != nullptr) {
10,795✔
1442
        const intern_string_t field_name = vd->vd_meta.lvm_name;
797✔
1443
        if (jlu->jlu_format->lf_timestamp_field == field_name) {
797✔
1444
            long long divisor = jlu->jlu_format->elf_timestamp_divisor;
22✔
1445
            timeval tv;
1446

1447
            tv.tv_sec = val / divisor;
22✔
1448
            tv.tv_usec = fmod(val, divisor) * (1000000.0 / divisor);
22✔
1449
            jlu->jlu_format->lf_date_time.to_localtime(tv.tv_sec,
22✔
1450
                                                       jlu->jlu_exttm);
22✔
1451
            jlu->jlu_exttm.et_gmtoff
1452
                = jlu->jlu_format->lf_date_time.dts_local_offset_cache;
22✔
1453
            jlu->jlu_exttm.et_flags |= ETF_MACHINE_ORIENTED
22✔
1454
                | ETF_SUB_NOT_IN_FORMAT | ETF_ZONE_SET | ETF_Z_FOR_UTC;
1455
            if (divisor == 1) {
22✔
1456
                jlu->jlu_exttm.et_flags |= ETF_MICROS_SET;
4✔
1457
            } else {
1458
                jlu->jlu_exttm.et_flags |= ETF_MILLIS_SET;
18✔
1459
            }
1460
            jlu->jlu_exttm.et_nsec = tv.tv_usec * 1000;
22✔
1461
        } else if (jlu->jlu_format->lf_subsecond_field == field_name) {
775✔
1462
            jlu->jlu_exttm.et_flags &= ~(ETF_MICROS_SET | ETF_MILLIS_SET);
4✔
1463
            switch (jlu->jlu_format->lf_subsecond_unit.value()) {
4✔
1464
                case log_format::subsecond_unit::milli:
×
1465
                    jlu->jlu_exttm.et_nsec = val * 1000000;
×
1466
                    jlu->jlu_exttm.et_flags |= ETF_MILLIS_SET;
×
1467
                    break;
×
1468
                case log_format::subsecond_unit::micro:
×
1469
                    jlu->jlu_exttm.et_nsec = val * 1000;
×
1470
                    jlu->jlu_exttm.et_flags |= ETF_MICROS_SET;
×
1471
                    break;
×
1472
                case log_format::subsecond_unit::nano:
4✔
1473
                    jlu->jlu_exttm.et_nsec = val;
4✔
1474
                    jlu->jlu_exttm.et_flags |= ETF_NANOS_SET;
4✔
1475
                    break;
4✔
1476
            }
1477
            jlu->jlu_exttm.et_flags |= ETF_SUB_NOT_IN_FORMAT;
4✔
1478
        }
1479
    }
1480

1481
    if (!ypc->is_level(1) && vd == nullptr) {
10,795✔
1482
        return 1;
9,951✔
1483
    }
1484
    if (vd != nullptr
844✔
1485
        && vd->vd_meta.lvm_name == jlu->jlu_format->elf_thread_id_field)
844✔
1486
    {
1487
        jlu->jlu_tid_number = val;
140✔
1488
    }
1489
    jlu->jlu_format->jlf_line_values.lvv_values.emplace_back(
844✔
1490
        jlu->jlu_format->get_value_meta(ypc, vd, value_kind_t::VALUE_INTEGER),
844✔
1491
        (int64_t) val);
844✔
1492
    return 1;
844✔
1493
}
1494

1495
static int
1496
rewrite_json_double(yajlpp_parse_context* ypc, double val)
142✔
1497
{
1498
    auto* jlu = (json_log_userdata*) ypc->ypc_userdata;
142✔
1499
    const auto* vd = jlu->get_field_def(ypc);
142✔
1500

1501
    if (vd != nullptr) {
142✔
1502
        const intern_string_t field_name = vd->vd_meta.lvm_name;
138✔
1503
        if (jlu->jlu_format->lf_timestamp_field == field_name) {
138✔
1504
            long long divisor = jlu->jlu_format->elf_timestamp_divisor;
118✔
1505
            timeval tv;
1506

1507
            tv.tv_sec = val / divisor;
118✔
1508
            tv.tv_usec = fmod(val, divisor) * (1000000.0 / divisor);
118✔
1509
            jlu->jlu_format->lf_date_time.to_localtime(tv.tv_sec,
118✔
1510
                                                       jlu->jlu_exttm);
118✔
1511
            jlu->jlu_exttm.et_gmtoff
1512
                = jlu->jlu_format->lf_date_time.dts_local_offset_cache;
118✔
1513
            jlu->jlu_exttm.et_flags |= ETF_MACHINE_ORIENTED
118✔
1514
                | ETF_SUB_NOT_IN_FORMAT | ETF_ZONE_SET | ETF_Z_FOR_UTC;
1515
            if (divisor == 1) {
118✔
1516
                jlu->jlu_exttm.et_flags |= ETF_MICROS_SET;
110✔
1517
            } else {
1518
                jlu->jlu_exttm.et_flags |= ETF_MILLIS_SET;
8✔
1519
            }
1520
            jlu->jlu_exttm.et_nsec = tv.tv_usec * 1000;
118✔
1521
        } else if (jlu->jlu_format->lf_subsecond_field == field_name) {
20✔
1522
            jlu->jlu_exttm.et_flags &= ~(ETF_MICROS_SET | ETF_MILLIS_SET);
×
1523
            switch (jlu->jlu_format->lf_subsecond_unit.value()) {
×
1524
                case log_format::subsecond_unit::milli:
×
1525
                    jlu->jlu_exttm.et_nsec = val * 1000000;
×
1526
                    jlu->jlu_exttm.et_flags |= ETF_MILLIS_SET;
×
1527
                    break;
×
1528
                case log_format::subsecond_unit::micro:
×
1529
                    jlu->jlu_exttm.et_nsec = val * 1000;
×
1530
                    jlu->jlu_exttm.et_flags |= ETF_MICROS_SET;
×
1531
                    break;
×
1532
                case log_format::subsecond_unit::nano:
×
1533
                    jlu->jlu_exttm.et_nsec = val;
×
1534
                    jlu->jlu_exttm.et_flags |= ETF_NANOS_SET;
×
1535
                    break;
×
1536
            }
1537
            jlu->jlu_exttm.et_flags |= ETF_SUB_NOT_IN_FORMAT;
×
1538
        } else if (jlu->jlu_format->elf_duration_field == field_name) {
20✔
1539
            jlu->jlu_duration = std::chrono::microseconds(
40✔
1540
                static_cast<int64_t>(val * 1000000.0));
20✔
1541
        }
1542
    }
1543

1544
    if (!ypc->is_level(1) && vd == nullptr) {
142✔
1545
        return 1;
×
1546
    }
1547
    jlu->jlu_format->jlf_line_values.lvv_values.emplace_back(
142✔
1548
        jlu->jlu_format->get_value_meta(ypc, vd, value_kind_t::VALUE_FLOAT),
284✔
1549
        val);
1550

1551
    return 1;
142✔
1552
}
1553

1554
static const json_path_container json_log_rewrite_handlers = {
1555
    yajlpp::pattern_property_handler("\\w+")
1556
        .add_cb(rewrite_json_null)
1557
        .add_cb(rewrite_json_bool)
1558
        .add_cb(rewrite_json_int)
1559
        .add_cb(rewrite_json_double)
1560
        .add_cb(rewrite_json_field),
1561
};
1562

1563
bool
1564
external_log_format::scan_for_partial(const log_format_file_state& lffs,
1✔
1565
                                      shared_buffer_ref& sbr,
1566
                                      size_t& len_out) const
1567
{
1568
    if (this->elf_type != elf_type_t::ELF_TYPE_TEXT) {
1✔
1569
        return false;
×
1570
    }
1571

1572
    const auto& pat
1573
        = this->elf_pattern_order[lffs.lffs_pattern_locks.last_pattern_index()];
1✔
1574
    if (!this->lf_multiline) {
1✔
1575
        len_out = pat->p_pcre.pp_value->match_partial(sbr.to_string_fragment());
1✔
1576
        return true;
1✔
1577
    }
1578

1579
    if (pat->p_timestamp_end == -1 || pat->p_timestamp_end > (int) sbr.length())
×
1580
    {
1581
        len_out = 0;
×
1582
        return false;
×
1583
    }
1584

1585
    len_out = pat->p_pcre.pp_value->match_partial(sbr.to_string_fragment());
×
1586
    return (int) len_out > pat->p_timestamp_end;
×
1587
}
1588

1589
std::vector<lnav::console::snippet>
1590
external_log_format::get_snippets() const
23✔
1591
{
1592
    std::vector<lnav::console::snippet> retval;
23✔
1593

1594
    for (const auto& src_pair : this->elf_format_sources) {
46✔
1595
        retval.emplace_back(lnav::console::snippet::from(src_pair.first, "")
46✔
1596
                                .with_line(src_pair.second));
23✔
1597
    }
1598

1599
    return retval;
23✔
1600
}
×
1601

1602
log_format::scan_result_t
1603
external_log_format::scan_json(std::vector<logline>& dst,
164,118✔
1604
                               const line_info& li,
1605
                               shared_buffer_ref& sbr,
1606
                               scan_batch_context& sbc)
1607
{
1608
    logline ll(
1609
        li.li_file_range.fr_offset, std::chrono::microseconds{0}, LEVEL_INFO);
164,118✔
1610
    auto line_frag = sbr.to_string_fragment();
164,118✔
1611

1612
    if (!line_frag.startswith("{")) {
164,118✔
1613
        if (!this->lf_specialized) {
153,054✔
1614
            return scan_no_match{"line is not a JSON object"};
153,049✔
1615
        }
1616

1617
        ll.set_time(dst.back().get_time<std::chrono::microseconds>());
5✔
1618
        ll.set_level(LEVEL_INVALID);
5✔
1619
        dst.emplace_back(ll);
5✔
1620
        return scan_match{0};
5✔
1621
    }
1622

1623
    auto& ypc = *(this->jlf_parse_context);
11,064✔
1624
    yajl_handle handle = this->jlf_yajl_handle.get();
11,064✔
1625
    json_log_userdata jlu(sbr, &sbc);
11,064✔
1626

1627
    if (li.li_partial) {
11,064✔
1628
        log_debug("skipping partial line at offset %lld",
×
1629
                  li.li_file_range.fr_offset);
1630
        if (this->lf_specialized) {
×
1631
            if (!dst.empty()) {
×
1632
                ll.set_time(dst.back().get_time<std::chrono::microseconds>());
×
1633
            }
1634
            ll.set_level(LEVEL_INVALID);
×
1635
            dst.emplace_back(ll);
×
1636
        }
1637
        return scan_incomplete{};
×
1638
    }
1639

1640
    const auto* line_data = (const unsigned char*) sbr.get_data();
11,064✔
1641

1642
    this->lf_desc_captures.clear();
11,064✔
1643
    this->lf_desc_allocator.reset();
11,064✔
1644

1645
    yajl_reset(handle);
11,064✔
1646
    ypc.set_static_handler(json_log_handlers.jpc_children[0]);
11,064✔
1647
    ypc.ypc_userdata = &jlu;
11,064✔
1648
    ypc.ypc_ignore_unused = true;
11,064✔
1649
    ypc.ypc_alt_callbacks.yajl_start_array = json_array_start;
11,064✔
1650
    ypc.ypc_alt_callbacks.yajl_start_map = json_array_start;
11,064✔
1651
    ypc.ypc_alt_callbacks.yajl_end_array = read_array_end;
11,064✔
1652
    ypc.ypc_alt_callbacks.yajl_end_map = read_array_end;
11,064✔
1653
    jlu.jlu_format = this;
11,064✔
1654
    jlu.jlu_base_line = &ll;
11,064✔
1655
    jlu.jlu_line_value = sbr.get_data();
11,064✔
1656
    jlu.jlu_line_size = sbr.length();
11,064✔
1657
    jlu.jlu_handle = handle;
11,064✔
1658
    jlu.jlu_format_hits.resize(this->jlf_line_format.size());
11,064✔
1659
    if (yajl_parse(handle, line_data, sbr.length()) == yajl_status_ok
11,064✔
1660
        && yajl_complete_parse(handle) == yajl_status_ok)
11,064✔
1661
    {
1662
        if (ll.get_time<std::chrono::microseconds>().count() == 0) {
10,866✔
1663
            if (this->lf_specialized) {
6,881✔
1664
                if (!dst.empty()) {
×
1665
                    ll.set_time(
×
1666
                        dst.back().get_time<std::chrono::microseconds>());
×
1667
                }
1668
                ll.set_ignore(true);
×
1669
                dst.emplace_back(ll);
×
1670
                return scan_match{0};
×
1671
            }
1672

1673
            return scan_no_match{
6,881✔
1674
                "JSON message does not have expected timestamp property"};
6,881✔
1675
        }
1676

1677
        if (jlu.jlu_tid_frag) {
3,985✔
1678
            this->jlf_line_values.lvv_thread_id_value
1679
                = jlu.jlu_tid_frag->to_string();
102✔
1680
            auto tid_iter = sbc.sbc_tids.insert_tid(
204✔
1681
                sbc.sbc_allocator,
1682
                jlu.jlu_tid_frag.value(),
102✔
1683
                ll.get_time<std::chrono::microseconds>());
102✔
1684
            tid_iter->second.titr_level_stats.update_msg_count(
102✔
1685
                ll.get_msg_level());
1686
            ll.merge_bloom_bits(jlu.jlu_tid_frag->bloom_bits());
102✔
1687
        } else {
1688
            auto tid_iter = sbc.sbc_tids.insert_tid(
3,883✔
1689
                sbc.sbc_allocator,
1690
                string_fragment{},
×
1691
                ll.get_time<std::chrono::microseconds>());
3,883✔
1692
            tid_iter->second.titr_level_stats.update_msg_count(
3,883✔
1693
                ll.get_msg_level());
1694
        }
1695

1696
        auto found_opid_desc = false;
3,985✔
1697
        if (this->elf_opid_field.empty()
3,985✔
1698
            && this->lf_opid_source.value_or(opid_source_t::from_description)
760✔
1699
                == opid_source_t::from_description
1700
            && this->lf_opid_description_def->size() == 1)
4,745✔
1701
        {
1702
            const auto& od = this->lf_opid_description_def->begin()->second;
322✔
1703
            for (const auto& desc : *od.od_descriptors) {
966✔
1704
                auto desc_iter
1705
                    = this->lf_desc_captures.find(desc.od_field.pp_value);
644✔
1706
                if (desc_iter == this->lf_desc_captures.end()) {
644✔
1707
                    continue;
602✔
1708
                }
1709
                jlu.jlu_opid_hasher.update(desc_iter->second);
42✔
1710
                found_opid_desc = true;
42✔
1711
            }
1712

1713
        } else if (!jlu.jlu_opid_desc_frag && !jlu.jlu_opid_frag
5,061✔
1714
                   && jlu.jlu_duration > 0us)
5,061✔
1715
        {
1716
            jlu.jlu_opid_hasher.update(sbr.to_string_fragment());
×
1717
        }
1718

1719
        if (jlu.jlu_opid_desc_frag || jlu.jlu_duration > 0us
5,705✔
1720
            || (found_opid_desc && this->lf_opid_description_def->size() == 1))
5,705✔
1721
        {
1722
            char buf[hasher::STRING_SIZE];
1723
            jlu.jlu_opid_hasher.to_string(buf);
2,286✔
1724
            auto opid_frag = string_fragment::from_bytes(buf, sizeof(buf) - 1);
2,286✔
1725
            auto opid_iter = sbc.sbc_opids.los_opid_ranges.find(opid_frag);
2,286✔
1726
            if (opid_iter == sbc.sbc_opids.los_opid_ranges.end()) {
2,286✔
1727
                jlu.jlu_opid_frag = opid_frag.to_owned(sbc.sbc_allocator);
2,274✔
1728
            } else {
1729
                jlu.jlu_opid_frag = opid_iter->first;
12✔
1730
            }
1731
        }
1732

1733
        if (jlu.jlu_opid_frag) {
3,985✔
1734
            ll.merge_bloom_bits(jlu.jlu_opid_frag->bloom_bits());
3,201✔
1735
            this->jlf_line_values.lvv_opid_value
1736
                = jlu.jlu_opid_frag->to_string();
3,201✔
1737
            this->jlf_line_values.lvv_opid_provenance
1738
                = logline_value_vector::opid_provenance::file;
3,201✔
1739
            auto opid_iter = sbc.sbc_opids.insert_op(
6,402✔
1740
                sbc.sbc_allocator,
1741
                jlu.jlu_opid_frag.value(),
3,201✔
1742
                ll.get_time<std::chrono::microseconds>(),
3,201✔
1743
                this->lf_timestamp_point_of_reference,
1744
                jlu.jlu_duration);
1745
            opid_iter->second.otr_level_stats.update_msg_count(
3,201✔
1746
                ll.get_msg_level());
1747
            auto& elems = opid_iter->second.otr_description.lod_elements;
3,201✔
1748
            if (jlu.jlu_opid_desc_frag && elems.empty()) {
3,201✔
1749
                elems.insert(0,
×
1750
                             fmt::format(FMT_STRING(" {}"),
9,036✔
1751
                                         jlu.jlu_opid_desc_frag.value()));
1752
            }
1753

1754
            if (jlu.jlu_subid) {
3,201✔
1755
                auto subid_frag
1756
                    = string_fragment::from_str(jlu.jlu_subid.value());
×
1757

1758
                auto* ostr = sbc.sbc_opids.sub_op_in_use(
×
1759
                    sbc.sbc_allocator,
1760
                    opid_iter,
1761
                    subid_frag,
1762
                    ll.get_time<std::chrono::microseconds>(),
×
1763
                    ll.get_msg_level());
1764
                if (ostr != nullptr && ostr->ostr_description.empty()) {
×
1765
                    log_op_description sub_desc;
×
1766
                    this->update_op_description(
×
1767
                        *this->lf_subid_description_def_vec, sub_desc);
×
1768
                    if (!sub_desc.lod_elements.empty()) {
×
1769
                        auto& sub_desc_def
1770
                            = this->lf_subid_description_def_vec->at(
×
1771
                                sub_desc.lod_index.value());
×
1772
                        ostr->ostr_description
1773
                            = sub_desc_def->to_string(sub_desc.lod_elements);
×
1774
                    }
1775
                }
1776
            }
1777

1778
            auto& otr = opid_iter->second;
3,201✔
1779
            this->update_op_description(*this->lf_opid_description_def_vec,
3,201✔
1780
                                        otr.otr_description);
3,201✔
1781
        } else {
1782
            this->jlf_line_values.lvv_opid_value = std::nullopt;
784✔
1783
        }
1784

1785
        jlu.jlu_sub_line_count += this->jlf_line_format_init_count;
3,985✔
1786
        ll.set_has_ansi(jlu.jlu_has_ansi);
3,985✔
1787
        ll.set_valid_utf(jlu.jlu_valid_utf);
3,985✔
1788
        for (int lpc = 0; lpc < jlu.jlu_sub_line_count; lpc++) {
24,547✔
1789
            ll.set_sub_offset(lpc);
20,562✔
1790
            ll.set_continued(lpc > 0);
20,562✔
1791
            dst.emplace_back(ll);
20,562✔
1792
        }
1793
        this->lf_timestamp_flags = jlu.jlu_exttm.et_flags;
3,985✔
1794

1795
        if (!this->lf_specialized) {
3,985✔
1796
            static const intern_string_t ts_field
1797
                = intern_string::lookup("__timestamp__", -1);
3,452✔
1798
            static const intern_string_t level_field
1799
                = intern_string::lookup("__level__");
4,954✔
1800
            for (const auto& [index, jfe] :
32,121✔
1801
                 lnav::itertools::enumerate(this->jlf_line_format))
35,573✔
1802
            {
1803
                if (jfe.jfe_type != json_log_field::VARIABLE
76,525✔
1804
                    || jfe.jfe_value.pp_value == ts_field
18,609✔
1805
                    || jfe.jfe_value.pp_value == level_field
15,227✔
1806
                    || jfe.jfe_default_value != "-")
47,278✔
1807
                {
1808
                    continue;
19,187✔
1809
                }
1810
                if (!jlu.jlu_format_hits[index]) {
9,482✔
1811
                    jlu.jlu_strikes += 1;
8,999✔
1812
                }
1813
            }
1814
        }
1815
    } else {
1816
        unsigned char* msg;
1817
        int line_count = 2;
198✔
1818

1819
        msg = yajl_get_error(
396✔
1820
            handle, 1, (const unsigned char*) sbr.get_data(), sbr.length());
198✔
1821
        if (msg != nullptr) {
198✔
1822
            auto msg_frag = string_fragment::from_c_str(msg);
198✔
1823
            log_debug("Unable to parse line at offset %lld: %.*s",
198✔
1824
                      li.li_file_range.fr_offset,
1825
                      msg_frag.length(),
1826
                      msg_frag.data());
1827
            line_count = msg_frag.count('\n') + 1;
198✔
1828
            yajl_free_error(handle, msg);
198✔
1829
        }
1830
        if (!this->lf_specialized) {
198✔
1831
            return scan_no_match{"JSON parsing failed"};
195✔
1832
        }
1833
        for (int lpc = 0; lpc < line_count; lpc++) {
15✔
1834
            log_level_t level = LEVEL_INVALID;
12✔
1835

1836
            ll.set_time(dst.back().get_timeval());
12✔
1837
            ll.set_continued(lpc > 0);
12✔
1838
            ll.set_level(level);
12✔
1839
            ll.set_sub_offset(lpc);
12✔
1840
            dst.emplace_back(ll);
12✔
1841
        }
1842
    }
1843

1844
    if (jlu.jlu_quality > 0) {
3,988✔
1845
        jlu.jlu_quality += 3000;
834✔
1846
    }
1847
    return scan_match{jlu.jlu_quality, jlu.jlu_strikes};
3,988✔
1848
}
11,064✔
1849

1850
log_format::scan_result_t
1851
external_log_format::scan(logfile& lf,
851,353✔
1852
                          std::vector<logline>& dst,
1853
                          const line_info& li,
1854
                          shared_buffer_ref& sbr,
1855
                          scan_batch_context& sbc)
1856
{
1857
    if (dst.empty()) {
851,353✔
1858
        auto file_options = lf.get_file_options();
33,943✔
1859

1860
        if (file_options) {
33,943✔
1861
            this->lf_date_time.dts_default_zone
1862
                = file_options->second.fo_default_zone.pp_value;
2,328✔
1863
        } else {
1864
            this->lf_date_time.dts_default_zone = nullptr;
31,615✔
1865
        }
1866
    }
33,943✔
1867

1868
    sbc.sbc_value_stats.resize(this->elf_value_defs.size());
851,353✔
1869
    if (this->elf_type == elf_type_t::ELF_TYPE_JSON) {
851,353✔
1870
        return this->scan_json(dst, li, sbr, sbc);
161,112✔
1871
    }
1872

1873
    int curr_fmt = -1, orig_lock = sbc.sbc_pattern_locks.last_pattern_index();
690,241✔
1874
    int pat_index = orig_lock;
690,241✔
1875
    auto line_sf = sbr.to_string_fragment();
690,241✔
1876
    thread_local auto md = lnav::pcre2pp::match_data::unitialized();
690,241✔
1877
    char tmp_opid_buf[hasher::STRING_SIZE];
1878

1879
    while (::next_format(this->elf_pattern_order, curr_fmt, pat_index)) {
2,266,108✔
1880
        auto* fpat = this->elf_pattern_order[curr_fmt].get();
1,581,754✔
1881
        auto* pat = fpat->p_pcre.pp_value.get();
1,581,754✔
1882

1883
        auto found_match
1884
            = pat->capture_from(line_sf).into(md).found_p(PCRE2_NO_UTF_CHECK);
1,581,754✔
1885
        if (!found_match) {
1,581,754✔
1886
            if (!sbc.sbc_pattern_locks.empty() && pat_index != -1) {
1,575,864✔
1887
                curr_fmt = -1;
2,011✔
1888
                pat_index = -1;
2,011✔
1889
            }
1890
            continue;
1,575,867✔
1891
        }
1892

1893
        auto ts = md[fpat->p_timestamp_field_index];
5,890✔
1894
        auto level_cap = md[fpat->p_level_field_index];
5,890✔
1895
        auto opid_cap = md[fpat->p_opid_field_index];
5,890✔
1896
        const char* last;
1897
        exttm log_time_tm;
5,890✔
1898
        timeval log_tv;
1899
        uint64_t opid_bloom = 0;
5,890✔
1900
        char combined_datetime_buf[512];
1901

1902
        if (fpat->p_time_field_index != -1) {
5,890✔
1903
            auto time_cap = md[fpat->p_time_field_index];
×
1904
            if (ts && time_cap) {
×
1905
                auto ts_str_len = snprintf(combined_datetime_buf,
×
1906
                                           sizeof(combined_datetime_buf),
1907
                                           "%.*sT%.*s",
1908
                                           ts->length(),
1909
                                           ts->data(),
1910
                                           time_cap->length(),
1911
                                           time_cap->data());
1912
                ts = string_fragment::from_bytes(combined_datetime_buf,
×
1913
                                                 ts_str_len);
×
1914
            }
1915
        }
1916

1917
        auto level = this->convert_level(
5,890✔
1918
            level_cap.value_or(string_fragment::invalid()), &sbc);
5,890✔
1919

1920
        if (!ts) {
5,890✔
1921
            level = log_level_t::LEVEL_INVALID;
×
1922
        } else if ((last
5,890✔
1923
                    = this->lf_date_time.scan(ts->data(),
11,780✔
1924
                                              ts->length(),
5,890✔
1925
                                              this->get_timestamp_formats(),
1926
                                              &log_time_tm,
1927
                                              log_tv))
1928
                   == nullptr)
5,890✔
1929
        {
1930
            auto ls = this->lf_date_time.unlock();
13✔
1931
            if ((last = this->lf_date_time.scan(ts->data(),
26✔
1932
                                                ts->length(),
13✔
1933
                                                this->get_timestamp_formats(),
1934
                                                &log_time_tm,
1935
                                                log_tv))
1936
                == nullptr)
13✔
1937
            {
1938
                this->lf_date_time.relock(ls);
2✔
1939
                continue;
3✔
1940
            }
1941
            if (last != nullptr) {
11✔
1942
                auto old_flags = this->lf_timestamp_flags & DATE_TIME_SET_FLAGS;
11✔
1943
                auto new_flags = log_time_tm.et_flags & DATE_TIME_SET_FLAGS;
11✔
1944

1945
                // It is unlikely a valid timestamp would lose much
1946
                // precision.
1947
                if (new_flags != old_flags) {
11✔
1948
                    continue;
1✔
1949
                }
1950
            }
1951

1952
            log_debug("%s:%zu: date-time re-locked to %d",
10✔
1953
                      lf.get_unique_path().c_str(),
1954
                      dst.size(),
1955
                      this->lf_date_time.dts_fmt_lock);
1956
        }
1957

1958
        this->lf_timestamp_flags = log_time_tm.et_flags;
5,887✔
1959

1960
        if (!(this->lf_timestamp_flags
11,774✔
1961
              & (ETF_MILLIS_SET | ETF_MICROS_SET | ETF_NANOS_SET))
5,887✔
1962
            && !dst.empty()
5,299✔
1963
            && dst.back().get_time<std::chrono::seconds>().count()
4,556✔
1964
                == log_tv.tv_sec
4,556✔
1965
            && dst.back()
14,627✔
1966
                    .get_subsecond_time<std::chrono::milliseconds>()
9,328✔
1967
                    .count()
3,441✔
1968
                != 0)
1969
        {
1970
            auto log_ms
1971
                = dst.back().get_subsecond_time<std::chrono::microseconds>();
×
1972

1973
            log_time_tm.et_nsec
1974
                = std::chrono::duration_cast<std::chrono::nanoseconds>(log_ms)
×
1975
                      .count();
×
1976
            log_tv.tv_usec
1977
                = std::chrono::duration_cast<std::chrono::microseconds>(log_ms)
×
1978
                      .count();
×
1979
        }
1980

1981
        if (!((log_time_tm.et_flags & ETF_DAY_SET)
5,887✔
1982
              && (log_time_tm.et_flags & ETF_MONTH_SET)
5,846✔
1983
              && (log_time_tm.et_flags & ETF_YEAR_SET)))
5,846✔
1984
        {
1985
            this->check_for_new_year(dst, log_time_tm, log_tv);
802✔
1986
        }
1987

1988
        auto log_us = to_us(log_tv);
5,887✔
1989
        if (this->elf_opid_field.empty()
5,887✔
1990
            && !fpat->p_opid_description_field_indexes.empty())
5,887✔
1991
        {
1992
            auto empty_desc = true;
4,005✔
1993
            hasher h;
4,005✔
1994
            for (const auto& fidx : fpat->p_opid_description_field_indexes) {
12,015✔
1995
                auto desc_cap = md[fidx];
8,010✔
1996
                if (desc_cap) {
8,010✔
1997
                    h.update(desc_cap.value());
7,963✔
1998
                    empty_desc = false;
7,963✔
1999
                }
2000
            }
2001
            if (!empty_desc) {
4,005✔
2002
                h.to_string(tmp_opid_buf);
4,005✔
2003
                opid_cap = string_fragment::from_bytes(
8,010✔
2004
                    tmp_opid_buf, sizeof(tmp_opid_buf) - 1);
4,005✔
2005
            }
2006
        }
2007

2008
        auto duration_cap = md[fpat->p_duration_field_index];
5,887✔
2009
        if (duration_cap && !opid_cap) {
5,887✔
2010
            hasher h;
86✔
2011
            h.update(line_sf);
86✔
2012
            h.to_string(tmp_opid_buf);
86✔
2013
            opid_cap = string_fragment::from_bytes(tmp_opid_buf,
172✔
2014
                                                   sizeof(tmp_opid_buf) - 1);
86✔
2015
        }
2016
        if (opid_cap && !opid_cap->empty()) {
5,887✔
2017
            auto duration = std::chrono::microseconds{0};
4,894✔
2018
            if (duration_cap) {
4,894✔
2019
                auto from_res
2020
                    = humanize::try_from<double>(duration_cap.value());
86✔
2021
                if (from_res) {
86✔
2022
                    duration = std::chrono::microseconds(
×
2023
                        static_cast<int64_t>(from_res.value() * 1000000));
86✔
2024
                }
2025
            }
2026
            auto opid_iter
2027
                = sbc.sbc_opids.insert_op(sbc.sbc_allocator,
9,788✔
2028
                                          opid_cap.value(),
4,894✔
2029
                                          log_us,
2030
                                          this->lf_timestamp_point_of_reference,
2031
                                          duration);
2032
            auto& otr = opid_iter->second;
4,894✔
2033

2034
            otr.otr_level_stats.update_msg_count(level);
4,894✔
2035
            if (fpat->p_subid_field_index != -1) {
4,894✔
2036
                auto subid_cap = md[fpat->p_subid_field_index];
62✔
2037
                if (subid_cap && !subid_cap->empty()) {
62✔
2038
                    auto* ostr = sbc.sbc_opids.sub_op_in_use(sbc.sbc_allocator,
186✔
2039
                                                             opid_iter,
2040
                                                             subid_cap.value(),
62✔
2041
                                                             log_us,
2042
                                                             level);
2043
                    if (ostr != nullptr && ostr->ostr_description.empty()) {
62✔
2044
                        log_op_description sub_desc;
41✔
2045
                        this->update_op_description(
41✔
2046
                            *this->lf_subid_description_def_vec,
41✔
2047
                            sub_desc,
2048
                            fpat,
2049
                            md);
2050
                        if (!sub_desc.lod_elements.empty()) {
41✔
2051
                            auto& sub_desc_def
2052
                                = this->lf_subid_description_def_vec->at(
39✔
2053
                                    sub_desc.lod_index.value());
39✔
2054
                            ostr->ostr_description = sub_desc_def->to_string(
78✔
2055
                                sub_desc.lod_elements);
39✔
2056
                        }
2057
                    }
41✔
2058
                }
2059
            }
2060
            this->update_op_description(*this->lf_opid_description_def_vec,
4,894✔
2061
                                        otr.otr_description,
4,894✔
2062
                                        fpat,
2063
                                        md);
2064
            opid_bloom = opid_cap->bloom_bits();
4,894✔
2065
        }
2066

2067
        for (const auto& ivd : fpat->p_value_by_index) {
66,569✔
2068
            if (!ivd.ivd_value_def->vd_meta.lvm_values_index) {
60,682✔
2069
                continue;
11,068✔
2070
            }
2071

2072
            ssize_t cap_size = md.capture_size(ivd.ivd_index);
49,614✔
2073
            auto& lvs = sbc.sbc_value_stats[ivd.ivd_value_def->vd_meta
49,614✔
2074
                                                .lvm_values_index.value()];
49,614✔
2075

2076
            if (cap_size > lvs.lvs_width) {
49,614✔
2077
                lvs.lvs_width = cap_size;
6,458✔
2078
            }
2079
        }
2080

2081
        for (auto value_index : fpat->p_numeric_value_indexes) {
10,760✔
2082
            const indexed_value_def& ivd = fpat->p_value_by_index[value_index];
4,873✔
2083
            const value_def& vd = *ivd.ivd_value_def;
4,873✔
2084
            auto num_cap = md[ivd.ivd_index];
4,873✔
2085

2086
            if (vd.vd_meta.lvm_identifier) {
4,873✔
2087
                continue;
×
2088
            }
2089

2090
            if (num_cap && num_cap->is_valid()) {
4,873✔
2091
                const scaling_factor* scaling = nullptr;
4,807✔
2092

2093
                if (ivd.ivd_unit_field_index >= 0) {
4,807✔
2094
                    auto unit_cap = md[ivd.ivd_unit_field_index];
80✔
2095

2096
                    if (unit_cap && unit_cap->is_valid()) {
80✔
2097
                        intern_string_t unit_val
2098
                            = intern_string::lookup(unit_cap.value());
80✔
2099

2100
                        auto unit_iter = vd.vd_unit_scaling.find(unit_val);
80✔
2101
                        if (unit_iter != vd.vd_unit_scaling.end()) {
80✔
2102
                            const auto& sf = unit_iter->second;
80✔
2103

2104
                            scaling = &sf;
80✔
2105
                        }
2106
                    }
2107
                }
2108

2109
                std::optional<double> dvalue_opt;
4,807✔
2110
                switch (vd.vd_meta.lvm_kind) {
4,807✔
2111
                    case value_kind_t::VALUE_INTEGER: {
4,641✔
2112
                        auto scan_res
2113
                            = scn::scan_int<int64_t>(num_cap->to_string_view());
4,641✔
2114
                        if (scan_res) {
4,641✔
2115
                            dvalue_opt = scan_res->value();
4,641✔
2116
                        }
2117
                        break;
4,641✔
2118
                    }
2119
                    case value_kind_t::VALUE_FLOAT: {
166✔
2120
                        auto scan_res = scn::scan_value<double>(
2121
                            num_cap->to_string_view());
166✔
2122
                        if (scan_res) {
166✔
2123
                            dvalue_opt = scan_res->value();
166✔
2124
                        }
2125
                        break;
166✔
2126
                    }
2127
                    default:
×
2128
                        break;
×
2129
                }
2130
                if (dvalue_opt) {
4,807✔
2131
                    auto dvalue = dvalue_opt.value();
4,807✔
2132
                    if (scaling != nullptr) {
4,807✔
2133
                        scaling->scale(dvalue);
80✔
2134
                    }
2135
                    sbc.sbc_value_stats[vd.vd_meta.lvm_values_index.value()]
4,807✔
2136
                        .add_value(dvalue);
4,807✔
2137
                }
2138
            }
2139
        }
2140

2141
        dst.emplace_back(li.li_file_range.fr_offset, log_us, level);
5,887✔
2142
        auto& new_line = dst.back();
5,887✔
2143
        new_line.merge_bloom_bits(opid_bloom);
5,887✔
2144

2145
        auto src_file_cap = md[fpat->p_src_file_field_index];
5,887✔
2146
        auto src_line_cap = md[fpat->p_src_line_field_index];
5,887✔
2147
        if (src_file_cap && src_line_cap) {
5,887✔
2148
            auto h = hasher();
94✔
2149
            h.update(this->get_name().c_str());
94✔
2150
            h.update(src_file_cap.value());
94✔
2151
            h.update(src_line_cap.value());
94✔
2152
            new_line.merge_bloom_bits(h.to_bloom_bits());
94✔
2153
            new_line.set_schema_computed(true);
94✔
2154
        }
2155
        auto thread_id_cap = md[fpat->p_thread_id_field_index];
5,887✔
2156
        if (thread_id_cap) {
5,887✔
2157
            auto tid_iter = sbc.sbc_tids.insert_tid(
2,098✔
2158
                sbc.sbc_allocator, thread_id_cap.value(), log_us);
1,049✔
2159
            tid_iter->second.titr_level_stats.update_msg_count(level);
1,049✔
2160
            new_line.merge_bloom_bits(thread_id_cap->bloom_bits());
1,049✔
2161
        } else {
2162
            auto tid_iter = sbc.sbc_tids.insert_tid(
4,838✔
2163
                sbc.sbc_allocator, string_fragment{}, log_us);
×
2164
            tid_iter->second.titr_level_stats.update_msg_count(level);
4,838✔
2165
        }
2166

2167
        if (orig_lock != curr_fmt) {
5,887✔
2168
            uint32_t lock_line;
2169

2170
            if (!this->lf_specialized && orig_lock != -1) {
626✔
2171
                log_debug("%s:%zu: changing pattern lock %d -> (%d)%s",
×
2172
                          lf.get_unique_path().c_str(),
2173
                          dst.size() - 1,
2174
                          orig_lock,
2175
                          curr_fmt,
2176
                          this->elf_pattern_order[curr_fmt]->p_name.c_str());
2177
            }
2178
            if (sbc.sbc_pattern_locks.empty()) {
626✔
2179
                lock_line = 0;
600✔
2180
            } else {
2181
                lock_line = dst.size() - 1;
26✔
2182
            }
2183
            sbc.sbc_pattern_locks.pl_lines.emplace_back(lock_line, curr_fmt);
626✔
2184
        }
2185
        return scan_match{1000};
5,887✔
2186
    }
2187

2188
    if (this->lf_specialized && !this->lf_multiline && !dst.empty()) {
684,354✔
2189
        const auto& last_line = dst.back();
1✔
2190

2191
        log_debug("%s: invalid line %zu file_offset=%" PRIu64,
1✔
2192
                  lf.get_filename().c_str(),
2193
                  dst.size(),
2194
                  li.li_file_range.fr_offset);
2195
        dst.emplace_back(li.li_file_range.fr_offset,
1✔
2196
                         last_line.get_time<std::chrono::microseconds>(),
×
2197
                         log_level_t::LEVEL_INVALID);
1✔
2198

2199
        return scan_match{0};
1✔
2200
    }
2201

2202
    return scan_no_match{"no patterns matched"};
684,353✔
2203
}
2204

2205
void
2206
external_log_format::annotate(logfile* lf,
7,408✔
2207
                              uint64_t line_number,
2208
                              string_attrs_t& sa,
2209
                              logline_value_vector& values) const
2210
{
2211
    thread_local auto md = lnav::pcre2pp::match_data::unitialized();
7,408✔
2212

2213
    auto& line = values.lvv_sbr;
7,408✔
2214
    line_range lr;
7,408✔
2215

2216
    line.erase_ansi();
7,408✔
2217
    if (this->elf_type != elf_type_t::ELF_TYPE_TEXT) {
7,408✔
2218
        if (this->jlf_cached_opts.full_message) {
749✔
2219
            values = this->jlf_line_values;
318✔
2220
            sa = this->jlf_line_attrs;
318✔
2221
        } else {
2222
            values.lvv_sbr = this->jlf_line_values.lvv_sbr.clone();
431✔
2223
            for (const auto& llv : this->jlf_line_values.lvv_values) {
4,274✔
2224
                if (this->jlf_cached_sub_range.contains(llv.lv_origin)) {
3,843✔
2225
                    values.lvv_values.emplace_back(llv);
911✔
2226
                    values.lvv_values.back().lv_origin.shift(
911✔
2227
                        this->jlf_cached_sub_range.lr_start,
911✔
2228
                        -this->jlf_cached_sub_range.lr_start);
911✔
2229
                }
2230
            }
2231
            for (const auto& attr : this->jlf_line_attrs) {
2,436✔
2232
                if (this->jlf_cached_sub_range.contains(attr.sa_range)) {
2,005✔
2233
                    sa.emplace_back(attr);
702✔
2234
                    sa.back().sa_range.shift(
702✔
2235
                        this->jlf_cached_sub_range.lr_start,
702✔
2236
                        -this->jlf_cached_sub_range.lr_start);
702✔
2237
                }
2238
            }
2239
            values.lvv_opid_value = this->jlf_line_values.lvv_opid_value;
431✔
2240
            values.lvv_opid_provenance
2241
                = this->jlf_line_values.lvv_opid_provenance;
431✔
2242
            values.lvv_thread_id_value
2243
                = this->jlf_line_values.lvv_thread_id_value;
431✔
2244
        }
2245
        log_format::annotate(lf, line_number, sa, values);
749✔
2246
        return;
2,382✔
2247
    }
2248

2249
    if (line.empty()) {
6,659✔
2250
        return;
5✔
2251
    }
2252

2253
    values.lvv_values.reserve(this->elf_value_defs.size());
6,654✔
2254

2255
    auto lffs = lf->get_format_file_state();
6,654✔
2256
    int pat_index = lffs.lffs_pattern_locks.pattern_index_for_line(line_number);
6,654✔
2257
    const auto& pat = *this->elf_pattern_order[pat_index];
6,654✔
2258
    char tmp_opid_buf[hasher::STRING_SIZE];
2259

2260
    sa.reserve(pat.p_pcre.pp_value->get_capture_count());
6,654✔
2261
    auto match_res
2262
        = pat.p_pcre.pp_value->capture_from(line.to_string_fragment())
6,654✔
2263
              .into(md)
6,654✔
2264
              .matches(PCRE2_NO_UTF_CHECK)
13,308✔
2265
              .ignore_error();
6,654✔
2266
    if (!match_res) {
6,654✔
2267
        // A continued line still needs a body.
2268
        lr.lr_start = 0;
1,628✔
2269
        lr.lr_end = line.length();
1,628✔
2270
        sa.emplace_back(lr, SA_BODY.value());
1,628✔
2271
        if (!this->lf_multiline) {
1,628✔
2272
            auto len
2273
                = pat.p_pcre.pp_value->match_partial(line.to_string_fragment());
×
2274
            sa.emplace_back(
×
2275
                line_range{(int) len, -1},
×
2276
                SA_INVALID.value("Log line does not match any pattern"));
×
2277
        }
2278
        return;
1,628✔
2279
    }
2280

2281
    auto duration_cap = md[pat.p_duration_field_index];
5,026✔
2282

2283
    auto ts_cap = md[pat.p_timestamp_field_index];
5,026✔
2284
    if (ts_cap) {
5,026✔
2285
        sa.emplace_back(to_line_range(ts_cap.value()), L_TIMESTAMP.value());
5,026✔
2286
    }
2287

2288
    auto opid_cap = md[pat.p_opid_field_index];
5,026✔
2289

2290
    if (this->elf_opid_field.empty()
5,026✔
2291
        && !pat.p_opid_description_field_indexes.empty())
5,026✔
2292
    {
2293
        auto empty_desc = true;
3,765✔
2294
        hasher h;
3,765✔
2295
        for (const auto& fidx : pat.p_opid_description_field_indexes) {
11,295✔
2296
            auto desc_cap = md[fidx];
7,530✔
2297
            if (desc_cap) {
7,530✔
2298
                h.update(desc_cap.value());
7,502✔
2299
                empty_desc = false;
7,502✔
2300
            }
2301
        }
2302
        if (!empty_desc) {
3,765✔
2303
            h.to_string(tmp_opid_buf);
3,765✔
2304
            opid_cap = string_fragment::from_bytes(tmp_opid_buf,
7,530✔
2305
                                                   sizeof(tmp_opid_buf) - 1);
3,765✔
2306
        }
2307
    } else if (duration_cap && !opid_cap) {
1,261✔
2308
        hasher h;
3✔
2309
        h.update(line.to_string_fragment());
3✔
2310
        h.to_string(tmp_opid_buf);
3✔
2311
        opid_cap = string_fragment::from_bytes(tmp_opid_buf,
6✔
2312
                                               sizeof(tmp_opid_buf) - 1);
3✔
2313
    }
2314
    if (opid_cap && !opid_cap->empty()) {
5,026✔
2315
        sa.emplace_back(to_line_range(opid_cap.value()), L_OPID.value());
4,220✔
2316
        values.lvv_opid_value = opid_cap->to_string();
4,220✔
2317
        values.lvv_opid_provenance
2318
            = logline_value_vector::opid_provenance::file;
4,220✔
2319
    }
2320

2321
    auto body_cap = md[pat.p_body_field_index];
5,026✔
2322
    auto level_cap = md[pat.p_level_field_index];
5,026✔
2323
    auto src_file_cap = md[pat.p_src_file_field_index];
5,026✔
2324
    auto src_line_cap = md[pat.p_src_line_field_index];
5,026✔
2325
    auto thread_id_cap = md[pat.p_thread_id_field_index];
5,026✔
2326

2327
    if (level_cap
5,026✔
2328
        && (!body_cap
10,006✔
2329
            || (body_cap && level_cap->sf_begin != body_cap->sf_begin)))
10,006✔
2330
    {
2331
        sa.emplace_back(to_line_range(level_cap.value()), L_LEVEL.value());
4,580✔
2332
    }
2333

2334
    if (src_file_cap) {
5,026✔
2335
        sa.emplace_back(to_line_range(src_file_cap.value()),
97✔
2336
                        SA_SRC_FILE.value());
194✔
2337
    }
2338
    if (src_line_cap) {
5,026✔
2339
        sa.emplace_back(to_line_range(src_line_cap.value()),
97✔
2340
                        SA_SRC_LINE.value());
194✔
2341
    }
2342
    if (thread_id_cap) {
5,026✔
2343
        sa.emplace_back(to_line_range(thread_id_cap.value()),
614✔
2344
                        SA_THREAD_ID.value());
1,228✔
2345
        values.lvv_thread_id_value = thread_id_cap->to_string();
614✔
2346
    }
2347
    if (duration_cap) {
5,026✔
2348
        sa.emplace_back(to_line_range(duration_cap.value()),
3✔
2349
                        SA_DURATION.value());
6✔
2350
    }
2351

2352
    for (size_t lpc = 0; lpc < pat.p_value_by_index.size(); lpc++) {
57,565✔
2353
        const auto& ivd = pat.p_value_by_index[lpc];
52,539✔
2354
        const scaling_factor* scaling = nullptr;
52,539✔
2355
        auto cap = md[ivd.ivd_index];
52,539✔
2356
        const auto& vd = *ivd.ivd_value_def;
52,539✔
2357

2358
        if (ivd.ivd_unit_field_index >= 0) {
52,539✔
2359
            auto unit_cap = md[ivd.ivd_unit_field_index];
10✔
2360

2361
            if (unit_cap) {
10✔
2362
                intern_string_t unit_val
2363
                    = intern_string::lookup(unit_cap.value());
10✔
2364
                auto unit_iter = vd.vd_unit_scaling.find(unit_val);
10✔
2365
                if (unit_iter != vd.vd_unit_scaling.end()) {
10✔
2366
                    const auto& sf = unit_iter->second;
10✔
2367

2368
                    scaling = &sf;
10✔
2369
                }
2370
            }
2371
        }
2372

2373
        if (cap) {
52,539✔
2374
            values.lvv_values.emplace_back(
85,754✔
2375
                vd.vd_meta, line, to_line_range(cap.value()));
42,877✔
2376
            values.lvv_values.back().apply_scaling(scaling);
42,877✔
2377
        } else {
2378
            values.lvv_values.emplace_back(vd.vd_meta);
9,662✔
2379
        }
2380
    }
2381

2382
    if (body_cap && body_cap->is_valid()) {
5,026✔
2383
        lr = to_line_range(body_cap.value());
5,014✔
2384
    } else {
2385
        lr.lr_start = line.length();
12✔
2386
        lr.lr_end = line.length();
12✔
2387
    }
2388
    sa.emplace_back(lr, SA_BODY.value());
5,026✔
2389

2390
    log_format::annotate(lf, line_number, sa, values);
5,026✔
2391
}
2392

2393
void
2394
external_log_format::rewrite(exec_context& ec,
43✔
2395
                             shared_buffer_ref& line,
2396
                             string_attrs_t& sa,
2397
                             std::string& value_out)
2398
{
2399
    auto& values = *ec.ec_line_values;
43✔
2400

2401
    value_out.assign(line.get_data(), line.length());
43✔
2402

2403
    for (auto iter = values.lvv_values.begin(); iter != values.lvv_values.end();
259✔
2404
         ++iter)
216✔
2405
    {
2406
        if (!iter->lv_origin.is_valid()) {
216✔
2407
            log_debug("%d: not rewriting value with invalid origin -- %s",
24✔
2408
                      (int) ec.ec_top_line,
2409
                      iter->lv_meta.lvm_name.get());
2410
            continue;
184✔
2411
        }
2412

2413
        auto vd_iter = this->elf_value_defs.find(iter->lv_meta.lvm_name);
192✔
2414
        if (vd_iter == this->elf_value_defs.end()) {
192✔
2415
            log_debug("%d: not rewriting undefined value -- %s",
×
2416
                      (int) ec.ec_top_line,
2417
                      iter->lv_meta.lvm_name.get());
2418
            continue;
×
2419
        }
2420

2421
        const auto& vd = *vd_iter->second;
192✔
2422

2423
        if (vd.vd_rewriter.empty()) {
192✔
2424
            continue;
160✔
2425
        }
2426

2427
        auto _sg = ec.enter_source(
2428
            vd_iter->second->vd_rewrite_src_name, 1, vd.vd_rewriter);
32✔
2429
        std::string field_value;
32✔
2430

2431
        auto_mem<FILE> tmpout(fclose);
32✔
2432

2433
        tmpout = std::tmpfile();
32✔
2434
        if (!tmpout) {
32✔
2435
            log_error("unable to create temporary file");
×
2436
            return;
×
2437
        }
2438
        fcntl(fileno(tmpout), F_SETFD, FD_CLOEXEC);
32✔
2439
        auto fd_copy = auto_fd::dup_of(fileno(tmpout));
32✔
2440
        fd_copy.close_on_exec();
32✔
2441
        auto ec_out = std::make_pair(tmpout.release(), fclose);
32✔
2442
        {
2443
            exec_context::output_guard og(ec, "tmp", ec_out);
64✔
2444

2445
            auto exec_res = execute_any(ec, vd.vd_rewriter);
32✔
2446
            if (exec_res.isOk()) {
32✔
2447
                field_value = exec_res.unwrap();
32✔
2448
            } else {
2449
                field_value = exec_res.unwrapErr().to_attr_line().get_string();
×
2450
            }
2451
        }
32✔
2452
        struct stat st;
2453
        fstat(fd_copy.get(), &st);
32✔
2454
        if (st.st_size > 0) {
32✔
2455
            auto buf = auto_buffer::alloc(st.st_size);
2✔
2456

2457
            buf.resize(st.st_size);
2✔
2458
            pread(fd_copy.get(), buf.in(), st.st_size, 0);
2✔
2459
            field_value = buf.to_string();
2✔
2460
        }
2✔
2461
        value_out.erase(iter->lv_origin.lr_start, iter->lv_origin.length());
32✔
2462

2463
        int32_t shift_amount
2464
            = ((int32_t) field_value.length()) - iter->lv_origin.length();
32✔
2465
        auto orig_lr = iter->lv_origin;
32✔
2466
        value_out.insert(iter->lv_origin.lr_start, field_value);
32✔
2467
        for (auto shift_iter = values.lvv_values.begin();
32✔
2468
             shift_iter != values.lvv_values.end();
180✔
2469
             ++shift_iter)
148✔
2470
        {
2471
            shift_iter->lv_origin.shift_range(orig_lr, shift_amount);
148✔
2472
        }
2473
        shift_string_attrs(sa, orig_lr, shift_amount);
32✔
2474
    }
32✔
2475
}
2476

2477
static int
2478
read_json_field(yajlpp_parse_context* ypc,
181,212✔
2479
                const unsigned char* str,
2480
                size_t len,
2481
                yajl_string_props_t* props)
2482
{
2483
    auto* jlu = (json_log_userdata*) ypc->ypc_userdata;
181,212✔
2484
    timeval tv_out;
2485
    const auto frag = string_fragment::from_bytes(str, len);
181,212✔
2486
    intern_string_t field_name;
181,212✔
2487
    const auto* vd = jlu->get_field_def(ypc);
181,212✔
2488

2489
    if (vd != nullptr) {
181,212✔
2490
        field_name = vd->vd_meta.lvm_name;
19,153✔
2491
    }
2492

2493
    if (field_name.empty()) {
181,212✔
2494
        if (!jlu->jlu_format->elf_opid_field.empty()) {
162,059✔
2495
            auto path_sf = ypc->get_path_as_string_fragment();
58,322✔
2496
            if (path_sf.startswith(jlu->jlu_format->elf_opid_field.c_str())) {
58,322✔
2497
                jlu->jlu_opid_hasher.update(path_sf);
9,033✔
2498
                jlu->jlu_opid_hasher.update(frag);
9,033✔
2499
            }
2500
        }
2501
    } else if (jlu->jlu_format->lf_timestamp_field == field_name) {
19,153✔
2502
        const auto* last = jlu->jlu_format->lf_date_time.scan(
3,878✔
2503
            (const char*) str,
2504
            len,
2505
            jlu->jlu_format->get_timestamp_formats(),
3,878✔
2506
            &jlu->jlu_exttm,
2507
            tv_out);
2508
        if (last == nullptr) {
3,878✔
2509
            auto ls = jlu->jlu_format->lf_date_time.unlock();
22✔
2510
            if ((last = jlu->jlu_format->lf_date_time.scan(
22✔
2511
                     (const char*) str,
2512
                     len,
2513
                     jlu->jlu_format->get_timestamp_formats(),
22✔
2514
                     &jlu->jlu_exttm,
2515
                     tv_out))
2516
                == nullptr)
22✔
2517
            {
2518
                jlu->jlu_format->lf_date_time.relock(ls);
×
2519
            }
2520
            if (last != nullptr) {
22✔
2521
                auto old_flags
22✔
2522
                    = jlu->jlu_format->lf_timestamp_flags & DATE_TIME_SET_FLAGS;
22✔
2523
                auto new_flags = jlu->jlu_exttm.et_flags & DATE_TIME_SET_FLAGS;
22✔
2524

2525
                // It is unlikely a valid timestamp would lose much
2526
                // precision.
2527
                if (new_flags != old_flags) {
22✔
2528
                    last = nullptr;
×
2529
                }
2530
            }
2531
        }
2532
        if (last != nullptr) {
3,878✔
2533
            jlu->jlu_format->lf_timestamp_flags = jlu->jlu_exttm.et_flags;
3,878✔
2534
            jlu->jlu_base_line->set_time(tv_out);
3,878✔
2535
        }
2536
    } else if (jlu->jlu_format->elf_level_pointer.pp_value != nullptr) {
15,275✔
2537
        if (jlu->jlu_format->elf_level_pointer.pp_value
230✔
2538
                ->find_in(field_name.to_string_fragment(), PCRE2_NO_UTF_CHECK)
230✔
2539
                .ignore_error()
230✔
2540
                .has_value())
115✔
2541
        {
2542
            jlu->jlu_base_line->set_level(
×
2543
                jlu->jlu_format->convert_level(frag, jlu->jlu_batch_context));
×
2544
        }
2545
    }
2546
    if (!field_name.empty() && jlu->jlu_format->elf_level_field == field_name) {
181,212✔
2547
        jlu->jlu_base_line->set_level(
4,138✔
2548
            jlu->jlu_format->convert_level(frag, jlu->jlu_batch_context));
4,138✔
2549
    }
2550
    if (!field_name.empty() && jlu->jlu_format->elf_opid_field == field_name) {
181,212✔
2551
        jlu->jlu_base_line->merge_bloom_bits(frag.bloom_bits());
916✔
2552

2553
        auto& sbc = *jlu->jlu_batch_context;
916✔
2554
        auto opid_iter = sbc.sbc_opids.los_opid_ranges.find(frag);
916✔
2555
        if (opid_iter == sbc.sbc_opids.los_opid_ranges.end()) {
916✔
2556
            jlu->jlu_opid_frag = frag.to_owned(sbc.sbc_allocator);
893✔
2557
        } else {
2558
            jlu->jlu_opid_frag = opid_iter->first;
23✔
2559
        }
2560
    }
2561
    if (!field_name.empty()
181,212✔
2562
        && jlu->jlu_format->elf_thread_id_field == field_name)
181,212✔
2563
    {
2564
        auto& sbc = *jlu->jlu_batch_context;
×
2565
        auto tid_iter = sbc.sbc_tids.ltis_tid_ranges.find(frag);
×
2566
        if (tid_iter == sbc.sbc_tids.ltis_tid_ranges.end()) {
×
2567
            jlu->jlu_tid_frag = frag.to_owned(sbc.sbc_allocator);
×
2568
        } else {
2569
            jlu->jlu_tid_frag = tid_iter->first;
×
2570
        }
2571
    }
2572
    if (!jlu->jlu_format->elf_subid_field.empty()
181,212✔
2573
        && jlu->jlu_format->elf_subid_field == field_name)
181,212✔
2574
    {
2575
        jlu->jlu_subid = frag.to_string();
×
2576
    }
2577
    if (!field_name.empty()
181,212✔
2578
        && jlu->jlu_format->elf_duration_field == field_name)
181,212✔
2579
    {
2580
        auto from_res = humanize::try_from<double>(frag);
×
2581
        if (from_res) {
×
2582
            jlu->jlu_duration = std::chrono::microseconds(
×
2583
                static_cast<int64_t>(from_res.value() * 1000000));
2584
        }
2585
    }
2586

2587
    if (vd != nullptr && vd->vd_is_desc_field) {
181,212✔
2588
        auto frag_copy = frag.to_owned(jlu->jlu_format->lf_desc_allocator);
42✔
2589

2590
        jlu->jlu_format->lf_desc_captures.emplace(field_name, frag_copy);
42✔
2591
    }
2592

2593
    jlu->add_sub_lines_for(vd, ypc->is_level(1), std::nullopt, str, len, props);
181,212✔
2594

2595
    return 1;
181,212✔
2596
}
2597

2598
static int
2599
rewrite_json_field(yajlpp_parse_context* ypc,
69,062✔
2600
                   const unsigned char* str,
2601
                   size_t len,
2602
                   yajl_string_props_t* props)
2603
{
2604
    static const intern_string_t body_name = intern_string::lookup("body", -1);
69,062✔
2605
    auto* jlu = (json_log_userdata*) ypc->ypc_userdata;
69,062✔
2606
    intern_string_t field_name;
69,062✔
2607
    const auto* vd = jlu->get_field_def(ypc);
69,062✔
2608
    auto frag = string_fragment::from_bytes(str, len);
69,062✔
2609

2610
    if (!ypc->is_level(1) && vd == nullptr) {
69,062✔
2611
        if (!jlu->jlu_format->elf_opid_field.empty()) {
57,218✔
2612
            auto path_sf = ypc->get_path_as_string_fragment();
56,598✔
2613
            if (path_sf.startswith(jlu->jlu_format->elf_opid_field.c_str())) {
56,598✔
2614
                jlu->jlu_opid_hasher.update(path_sf);
48✔
2615
                jlu->jlu_opid_hasher.update(frag);
48✔
2616
            }
2617
        }
2618
        return 1;
57,218✔
2619
    }
2620
    if (vd != nullptr) {
11,844✔
2621
        field_name = vd->vd_meta.lvm_name;
11,263✔
2622
    } else {
2623
        field_name = ypc->get_path();
581✔
2624
    }
2625

2626
    if (jlu->jlu_format->elf_opid_field == field_name) {
11,844✔
2627
        jlu->jlu_format->jlf_line_values.lvv_opid_value = frag.to_string();
956✔
2628
        jlu->jlu_format->jlf_line_values.lvv_opid_provenance
956✔
2629
            = logline_value_vector::opid_provenance::file;
956✔
2630
    }
2631
    if (jlu->jlu_format->elf_thread_id_field == field_name) {
11,844✔
2632
        jlu->jlu_format->jlf_line_values.lvv_thread_id_value = frag.to_string();
×
2633
    }
2634
    if (jlu->jlu_format->lf_timestamp_field == field_name) {
11,844✔
2635
        char time_buf[64];
2636

2637
        // TODO add a timeval kind to logline_value
2638
        if (jlu->jlu_line->is_time_skewed()
2,210✔
2639
            || (jlu->jlu_format->lf_timestamp_flags
4,420✔
2640
                & (ETF_MICROS_SET | ETF_NANOS_SET | ETF_ZONE_SET)))
2,210✔
2641
        {
2642
            timeval tv;
2643

2644
            const auto* last = jlu->jlu_format->lf_date_time.scan(
2,210✔
2645
                (const char*) str,
2646
                len,
2647
                jlu->jlu_format->get_timestamp_formats(),
2,210✔
2648
                &jlu->jlu_exttm,
2649
                tv);
2650
            if (last == nullptr) {
2,210✔
2651
                auto ls = jlu->jlu_format->lf_date_time.unlock();
58✔
2652
                if ((last = jlu->jlu_format->lf_date_time.scan(
58✔
2653
                         (const char*) str,
2654
                         len,
2655
                         jlu->jlu_format->get_timestamp_formats(),
58✔
2656
                         &jlu->jlu_exttm,
2657
                         tv))
2658
                    == nullptr)
58✔
2659
                {
2660
                    jlu->jlu_format->lf_date_time.relock(ls);
×
2661
                }
2662
            }
2663
            if (!jlu->jlu_subline_opts.hash_hack) {
2,210✔
2664
                if (jlu->jlu_exttm.et_flags & ETF_ZONE_SET
2,210✔
2665
                    && jlu->jlu_format->lf_date_time.dts_zoned_to_local)
2,210✔
2666
                {
2667
                    jlu->jlu_exttm.et_flags &= ~ETF_Z_IS_UTC;
2,210✔
2668
                }
2669
                jlu->jlu_exttm.et_gmtoff
2670
                    = jlu->jlu_format->lf_date_time.dts_local_offset_cache;
2,210✔
2671
            }
2672
            jlu->jlu_format->lf_date_time.ftime(
2,210✔
2673
                time_buf,
2674
                sizeof(time_buf),
2675
                jlu->jlu_format->get_timestamp_formats(),
2,210✔
2676
                jlu->jlu_exttm);
2,210✔
2677
        } else {
2678
            sql_strftime(
×
2679
                time_buf, sizeof(time_buf), jlu->jlu_line->get_timeval(), 'T');
×
2680
        }
2681
        jlu->jlu_format->jlf_line_values.lvv_values.emplace_back(
4,420✔
2682
            jlu->jlu_format->get_value_meta(field_name,
4,420✔
2683
                                            value_kind_t::VALUE_TEXT),
2684
            std::string{time_buf});
6,630✔
2685
    } else if (jlu->jlu_shared_buffer.contains((const char*) str)) {
9,634✔
2686
        auto str_offset = (int) ((const char*) str - jlu->jlu_line_value);
9,236✔
2687
        if (field_name == jlu->jlu_format->elf_body_field) {
9,236✔
2688
            jlu->jlu_format->jlf_line_values.lvv_values.emplace_back(
843✔
2689
                logline_value_meta(body_name,
1,686✔
2690
                                   value_kind_t::VALUE_TEXT,
2691
                                   logline_value_meta::internal_column{},
×
2692
                                   jlu->jlu_format),
843✔
2693
                string_fragment::from_byte_range(
1,686✔
2694
                    jlu->jlu_shared_buffer.get_data(),
843✔
2695
                    str_offset,
2696
                    str_offset + len));
843✔
2697
        }
2698

2699
        jlu->jlu_format->jlf_line_values.lvv_values.emplace_back(
9,236✔
2700
            jlu->jlu_format->get_value_meta(field_name,
18,472✔
2701
                                            value_kind_t::VALUE_TEXT),
2702
            string_fragment::from_byte_range(jlu->jlu_shared_buffer.get_data(),
18,472✔
2703
                                             str_offset,
2704
                                             str_offset + len));
9,236✔
2705
    } else {
2706
        if (field_name == jlu->jlu_format->elf_body_field) {
398✔
2707
            jlu->jlu_format->jlf_line_values.lvv_values.emplace_back(
315✔
2708
                logline_value_meta(body_name,
630✔
2709
                                   value_kind_t::VALUE_TEXT,
2710
                                   logline_value_meta::internal_column{},
×
2711
                                   jlu->jlu_format),
315✔
2712
                std::string{(const char*) str, len});
1,260✔
2713
        }
2714

2715
        jlu->jlu_format->jlf_line_values.lvv_values.emplace_back(
398✔
2716
            jlu->jlu_format->get_value_meta(ypc, vd, value_kind_t::VALUE_TEXT),
796✔
2717
            std::string{(const char*) str, len});
1,592✔
2718
    }
2719
    if (vd != nullptr && vd->vd_is_desc_field
11,263✔
2720
        && jlu->jlu_format->elf_opid_field.empty())
23,107✔
2721
    {
2722
        auto frag_copy = frag.to_owned(jlu->jlu_format->lf_desc_allocator);
48✔
2723

2724
        jlu->jlu_format->lf_desc_captures.emplace(field_name, frag_copy);
48✔
2725
    }
2726

2727
    return 1;
11,844✔
2728
}
2729

2730
void
2731
external_log_format::get_subline(const log_format_file_state& lffs,
21,971✔
2732
                                 const logline& ll,
2733
                                 shared_buffer_ref& sbr,
2734
                                 subline_options opts)
2735
{
2736
    if (this->elf_type == elf_type_t::ELF_TYPE_TEXT) {
21,971✔
2737
        return;
17,456✔
2738
    }
2739

2740
    if (this->jlf_cached_offset != ll.get_offset()
4,515✔
2741
        || this->jlf_cached_opts != opts)
4,515✔
2742
    {
2743
        auto& ypc = *(this->jlf_parse_context);
2,422✔
2744
        yajl_handle handle = this->jlf_yajl_handle.get();
2,422✔
2745
        json_log_userdata jlu(sbr, nullptr);
2,422✔
2746

2747
        jlu.jlu_subline_opts = opts;
2,422✔
2748

2749
        this->lf_desc_captures.clear();
2,422✔
2750
        this->lf_desc_allocator.reset();
2,422✔
2751
        this->jlf_share_manager.invalidate_refs();
2,422✔
2752
        this->jlf_cached_line.clear();
2,422✔
2753
        this->jlf_line_values.clear();
2,422✔
2754
        this->jlf_line_offsets.clear();
2,422✔
2755
        this->jlf_line_attrs.clear();
2,422✔
2756

2757
        auto line_frag = sbr.to_string_fragment();
2,422✔
2758

2759
        if (!line_frag.startswith("{")) {
2,422✔
2760
            this->jlf_cached_line.resize(line_frag.length());
72✔
2761
            memcpy(this->jlf_cached_line.data(),
72✔
2762
                   line_frag.data(),
72✔
2763
                   line_frag.length());
72✔
2764
            this->jlf_line_values.clear();
72✔
2765
            sbr.share(this->jlf_share_manager,
144✔
2766
                      &this->jlf_cached_line[0],
72✔
2767
                      this->jlf_cached_line.size());
2768
            this->jlf_line_values.lvv_sbr = sbr.clone();
72✔
2769
            this->jlf_line_attrs.emplace_back(
72✔
2770
                line_range{0, -1},
×
2771
                SA_INVALID.value(fmt::format(
144✔
2772
                    FMT_STRING("line at offset {} is not a JSON-line"),
144✔
2773
                    ll.get_offset())));
72✔
2774
            return;
72✔
2775
        }
2776

2777
        yajl_reset(handle);
2,350✔
2778
        ypc.set_static_handler(json_log_rewrite_handlers.jpc_children[0]);
2,350✔
2779
        ypc.ypc_userdata = &jlu;
2,350✔
2780
        ypc.ypc_ignore_unused = true;
2,350✔
2781
        ypc.ypc_alt_callbacks.yajl_start_array = json_array_start_const;
2,350✔
2782
        ypc.ypc_alt_callbacks.yajl_end_array = json_array_end;
2,350✔
2783
        ypc.ypc_alt_callbacks.yajl_start_map = json_array_start_const;
2,350✔
2784
        ypc.ypc_alt_callbacks.yajl_end_map = json_array_end;
2,350✔
2785
        jlu.jlu_format = this;
2,350✔
2786
        jlu.jlu_line = &ll;
2,350✔
2787
        jlu.jlu_handle = handle;
2,350✔
2788
        jlu.jlu_line_value = sbr.get_data();
2,350✔
2789
        jlu.jlu_format_hits.resize(this->jlf_line_format.size());
2,350✔
2790

2791
        yajl_status parse_status = yajl_parse(
4,700✔
2792
            handle, (const unsigned char*) sbr.get_data(), sbr.length());
2,350✔
2793
        if (parse_status != yajl_status_ok
2,350✔
2794
            || yajl_complete_parse(handle) != yajl_status_ok)
2,350✔
2795
        {
2796
            unsigned char* msg;
2797
            std::string full_msg;
14✔
2798

2799
            msg = yajl_get_error(
28✔
2800
                handle, 1, (const unsigned char*) sbr.get_data(), sbr.length());
14✔
2801
            if (msg != nullptr) {
14✔
2802
                full_msg = fmt::format(
14✔
2803
                    FMT_STRING("[offset: {}] {}\n{}"),
28✔
2804
                    ll.get_offset(),
14✔
2805
                    fmt::string_view{sbr.get_data(), sbr.length()},
14✔
2806
                    reinterpret_cast<char*>(msg));
28✔
2807
                yajl_free_error(handle, msg);
14✔
2808
            }
2809

2810
            this->jlf_cached_line.resize(full_msg.size());
14✔
2811
            memcpy(
14✔
2812
                this->jlf_cached_line.data(), full_msg.data(), full_msg.size());
14✔
2813
            this->jlf_line_values.clear();
14✔
2814
            this->jlf_line_attrs.emplace_back(
14✔
2815
                line_range{0, -1},
×
2816
                SA_INVALID.value("JSON line failed to parse"));
28✔
2817
        } else {
14✔
2818
            std::vector<logline_value>::iterator lv_iter;
2,336✔
2819
            bool used_values[this->jlf_line_values.lvv_values.size()];
4,672✔
2820
            struct line_range lr;
2,336✔
2821

2822
            memset(used_values, 0, sizeof(used_values));
2,336✔
2823
            for (lv_iter = this->jlf_line_values.lvv_values.begin();
2,336✔
2824
                 lv_iter != this->jlf_line_values.lvv_values.end();
20,811✔
2825
                 ++lv_iter)
18,475✔
2826
            {
2827
                lv_iter->lv_meta.lvm_format = this;
18,475✔
2828
            }
2829

2830
            if (jlu.jlu_tid_number) {
2,336✔
2831
                this->jlf_line_values.lvv_thread_id_value
2832
                    = fmt::to_string(jlu.jlu_tid_number.value());
140✔
2833
            } else if (jlu.jlu_tid_frag) {
2,196✔
2834
                this->jlf_line_values.lvv_thread_id_value
2835
                    = jlu.jlu_tid_frag->to_string();
×
2836
            }
2837

2838
            if (this->elf_opid_field.empty()
2,336✔
2839
                && this->lf_opid_source.value_or(
1,341✔
2840
                       opid_source_t::from_description)
1,341✔
2841
                    == opid_source_t::from_description
2842
                && this->lf_opid_description_def->size() == 1)
3,677✔
2843
            {
2844
                auto found_opid_desc = false;
210✔
2845
                const auto& od = this->lf_opid_description_def->begin()->second;
210✔
2846
                for (const auto& desc : *od.od_descriptors) {
630✔
2847
                    auto desc_iter
2848
                        = this->lf_desc_captures.find(desc.od_field.pp_value);
420✔
2849
                    if (desc_iter == this->lf_desc_captures.end()) {
420✔
2850
                        continue;
372✔
2851
                    }
2852
                    found_opid_desc = true;
48✔
2853
                    jlu.jlu_opid_hasher.update(desc_iter->second);
48✔
2854
                }
2855
                if (found_opid_desc) {
210✔
2856
                    this->jlf_line_values.lvv_opid_value
2857
                        = jlu.jlu_opid_hasher.to_string();
24✔
2858
                    this->jlf_line_values.lvv_opid_provenance
2859
                        = logline_value_vector::opid_provenance::file;
24✔
2860
                }
2861
            } else if (!jlu.jlu_opid_desc_frag && !jlu.jlu_opid_frag
4,223✔
2862
                       && jlu.jlu_duration > 0us)
4,223✔
2863
            {
2864
                jlu.jlu_opid_hasher.update(line_frag);
×
2865
                this->jlf_line_values.lvv_opid_value
2866
                    = jlu.jlu_opid_hasher.to_string();
×
2867
                this->jlf_line_values.lvv_opid_provenance
2868
                    = logline_value_vector::opid_provenance::file;
×
2869
            }
2870
            if (jlu.jlu_opid_desc_frag) {
2,336✔
2871
                this->jlf_line_values.lvv_opid_value
2872
                    = jlu.jlu_opid_hasher.to_string();
29✔
2873
                this->jlf_line_values.lvv_opid_provenance
2874
                    = logline_value_vector::opid_provenance::file;
29✔
2875
            }
2876
            if (jlu.jlu_opid_frag) {
2,336✔
2877
                this->jlf_line_values.lvv_opid_value
2878
                    = jlu.jlu_opid_frag->to_string();
×
2879
                this->jlf_line_values.lvv_opid_provenance
2880
                    = logline_value_vector::opid_provenance::file;
×
2881
            }
2882

2883
            int sub_offset = this->jlf_line_format_init_count;
2,336✔
2884
            for (const auto& jfe : this->jlf_line_format) {
30,238✔
2885
                static const intern_string_t ts_field
2886
                    = intern_string::lookup("__timestamp__", -1);
27,902✔
2887
                static const intern_string_t level_field
2888
                    = intern_string::lookup("__level__");
27,996✔
2889
                size_t begin_size = this->jlf_cached_line.size();
27,902✔
2890

2891
                switch (jfe.jfe_type) {
27,902✔
2892
                    case json_log_field::CONSTANT:
3,760✔
2893
                        this->json_append_to_cache(
3,760✔
2894
                            jfe.jfe_default_value.c_str(),
2895
                            jfe.jfe_default_value.size());
3,760✔
2896
                        break;
3,760✔
2897
                    case json_log_field::VARIABLE:
24,142✔
2898
                        lv_iter = find_if(
24,142✔
2899
                            this->jlf_line_values.lvv_values.begin(),
2900
                            this->jlf_line_values.lvv_values.end(),
2901
                            logline_value_name_cmp(&jfe.jfe_value.pp_value));
2902
                        if (lv_iter != this->jlf_line_values.lvv_values.end()) {
24,142✔
2903
                            auto str = lv_iter->to_string();
8,570✔
2904
                            value_def* vd = nullptr;
8,570✔
2905

2906
                            if (lv_iter->lv_meta.lvm_values_index) {
8,570✔
2907
                                vd = this->elf_value_def_order
2908
                                         [lv_iter->lv_meta.lvm_values_index
8,570✔
2909
                                              .value()]
8,570✔
2910
                                             .get();
8,570✔
2911
                            }
2912
                            while (endswith(str, "\n")) {
8,832✔
2913
                                str.pop_back();
262✔
2914
                            }
2915
                            size_t nl_pos = str.find('\n');
8,570✔
2916

2917
                            if (!jfe.jfe_prefix.empty()) {
8,570✔
2918
                                this->json_append_to_cache(jfe.jfe_prefix);
4,893✔
2919
                            }
2920
                            lr.lr_start = this->jlf_cached_line.size();
8,570✔
2921

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

2971
                            if (nl_pos == std::string::npos
8,570✔
2972
                                || opts.full_message)
3✔
2973
                            {
2974
                                lr.lr_end = this->jlf_cached_line.size();
8,567✔
2975
                            } else {
2976
                                lr.lr_end = lr.lr_start + nl_pos;
3✔
2977
                            }
2978

2979
                            if (lv_iter->lv_meta.lvm_name
8,570✔
2980
                                == this->lf_timestamp_field)
8,570✔
2981
                            {
2982
                                this->jlf_line_attrs.emplace_back(
1,002✔
2983
                                    lr, L_TIMESTAMP.value());
2,004✔
2984
                            } else if (lv_iter->lv_meta.lvm_name
7,568✔
2985
                                       == this->elf_body_field)
7,568✔
2986
                            {
2987
                                this->jlf_line_attrs.emplace_back(
1,158✔
2988
                                    lr, SA_BODY.value());
2,316✔
2989
                            } else if (lv_iter->lv_meta.lvm_name
6,410✔
2990
                                       == this->elf_src_file_field)
6,410✔
2991
                            {
2992
                                this->jlf_line_attrs.emplace_back(
11✔
2993
                                    lr, SA_SRC_FILE.value());
22✔
2994
                            } else if (lv_iter->lv_meta.lvm_name
6,399✔
2995
                                       == this->elf_src_line_field)
6,399✔
2996
                            {
2997
                                this->jlf_line_attrs.emplace_back(
11✔
2998
                                    lr, SA_SRC_LINE.value());
22✔
2999
                            } else if (lv_iter->lv_meta.lvm_name
6,388✔
3000
                                       == this->elf_thread_id_field)
6,388✔
3001
                            {
3002
                                this->jlf_line_attrs.emplace_back(
89✔
3003
                                    lr, SA_THREAD_ID.value());
178✔
3004
                            } else if (lv_iter->lv_meta.lvm_name
6,299✔
3005
                                       == this->elf_duration_field)
6,299✔
3006
                            {
3007
                                this->jlf_line_attrs.emplace_back(
×
3008
                                    lr, SA_DURATION.value());
×
3009
                            } else if (lv_iter->lv_meta.lvm_name
6,299✔
3010
                                       == this->elf_level_field)
6,299✔
3011
                            {
3012
                                this->jlf_line_attrs.emplace_back(
1,064✔
3013
                                    lr, L_LEVEL.value());
2,128✔
3014
                            } else if (lv_iter->lv_meta.lvm_name
10,470✔
3015
                                           == this->elf_opid_field
5,235✔
3016
                                       && !lr.empty())
5,235✔
3017
                            {
3018
                                this->jlf_line_attrs.emplace_back(
956✔
3019
                                    lr, L_OPID.value());
1,912✔
3020
                            }
3021
                            lv_iter->lv_origin = lr;
8,570✔
3022
                            lv_iter->lv_sub_offset = sub_offset;
8,570✔
3023
                            used_values[std::distance(
8,570✔
3024
                                this->jlf_line_values.lvv_values.begin(),
3025
                                lv_iter)]
3026
                                = true;
8,570✔
3027

3028
                            if (!jfe.jfe_suffix.empty()) {
8,570✔
3029
                                this->json_append_to_cache(jfe.jfe_suffix);
1,305✔
3030
                            }
3031
                        } else if (jfe.jfe_value.pp_value == ts_field) {
24,142✔
3032
                            line_range lr;
1,477✔
3033
                            ssize_t ts_len;
3034
                            char ts[64];
3035
                            exttm et;
1,477✔
3036

3037
                            ll.to_exttm(et);
1,477✔
3038
                            et.et_nsec += jlu.jlu_exttm.et_nsec % 1000;
1,477✔
3039
                            et.et_gmtoff = jlu.jlu_exttm.et_gmtoff;
1,477✔
3040
                            et.et_flags |= jlu.jlu_exttm.et_flags;
1,477✔
3041
                            if (!jfe.jfe_prefix.empty()) {
1,477✔
3042
                                this->json_append_to_cache(jfe.jfe_prefix);
4✔
3043
                            }
3044
                            if (jfe.jfe_ts_format.empty()) {
1,477✔
3045
                                ts_len = this->lf_date_time.ftime(
1,322✔
3046
                                    ts,
3047
                                    sizeof(ts),
3048
                                    this->get_timestamp_formats(),
3049
                                    et);
3050
                            } else {
3051
                                ts_len = ftime_fmt(ts,
155✔
3052
                                                   sizeof(ts),
3053
                                                   jfe.jfe_ts_format.c_str(),
3054
                                                   et);
3055
                            }
3056
                            lr.lr_start = this->jlf_cached_line.size();
1,477✔
3057
                            this->json_append_to_cache(ts, ts_len);
1,477✔
3058
                            lr.lr_end = this->jlf_cached_line.size();
1,477✔
3059
                            this->jlf_line_attrs.emplace_back(
1,477✔
3060
                                lr, L_TIMESTAMP.value());
2,954✔
3061

3062
                            lv_iter = find_if(
1,477✔
3063
                                this->jlf_line_values.lvv_values.begin(),
3064
                                this->jlf_line_values.lvv_values.end(),
3065
                                logline_value_name_cmp(
3066
                                    &this->lf_timestamp_field));
1,477✔
3067
                            if (lv_iter
1,477✔
3068
                                != this->jlf_line_values.lvv_values.end())
1,477✔
3069
                            {
3070
                                used_values[distance(
1,477✔
3071
                                    this->jlf_line_values.lvv_values.begin(),
3072
                                    lv_iter)]
3073
                                    = true;
1,477✔
3074
                            }
3075
                            if (!jfe.jfe_suffix.empty()) {
1,477✔
3076
                                this->json_append_to_cache(jfe.jfe_suffix);
4✔
3077
                            }
3078
                        } else if (jfe.jfe_value.pp_value == level_field
14,095✔
3079
                                   || jfe.jfe_value.pp_value
28,070✔
3080
                                       == this->elf_level_field)
13,975✔
3081
                        {
3082
                            auto level_name = ll.get_level_name();
120✔
3083
                            lr.lr_start = this->jlf_cached_line.size();
120✔
3084
                            this->json_append(lffs, jfe, nullptr, level_name);
120✔
3085
                            if (jfe.jfe_auto_width) {
120✔
3086
                                this->json_append_to_cache(
89✔
3087
                                    MAX_LEVEL_NAME_LEN - level_name.length());
89✔
3088
                            }
3089
                            lr.lr_end = this->jlf_cached_line.size();
120✔
3090
                            this->jlf_line_attrs.emplace_back(lr,
120✔
3091
                                                              L_LEVEL.value());
240✔
3092
                        } else if (!jfe.jfe_default_value.empty()) {
13,975✔
3093
                            if (!jfe.jfe_prefix.empty()) {
122✔
3094
                                this->json_append_to_cache(jfe.jfe_prefix);
×
3095
                            }
3096
                            this->json_append(
122✔
3097
                                lffs, jfe, nullptr, jfe.jfe_default_value);
122✔
3098
                            if (!jfe.jfe_suffix.empty()) {
122✔
3099
                                this->json_append_to_cache(jfe.jfe_suffix);
×
3100
                            }
3101
                        }
3102

3103
                        switch (jfe.jfe_text_transform) {
24,142✔
3104
                            case json_format_element::transform_t::NONE:
24,022✔
3105
                                break;
24,022✔
3106
                            case json_format_element::transform_t::UPPERCASE:
120✔
3107
                                for (size_t cindex = begin_size;
120✔
3108
                                     cindex < this->jlf_cached_line.size();
960✔
3109
                                     cindex++)
3110
                                {
3111
                                    this->jlf_cached_line[cindex] = toupper(
840✔
3112
                                        this->jlf_cached_line[cindex]);
840✔
3113
                                }
3114
                                break;
120✔
3115
                            case json_format_element::transform_t::LOWERCASE:
×
3116
                                for (size_t cindex = begin_size;
×
3117
                                     cindex < this->jlf_cached_line.size();
×
3118
                                     cindex++)
3119
                                {
3120
                                    this->jlf_cached_line[cindex] = tolower(
×
3121
                                        this->jlf_cached_line[cindex]);
×
3122
                                }
3123
                                break;
×
3124
                            case json_format_element::transform_t::CAPITALIZE:
×
3125
                                for (size_t cindex = begin_size;
×
3126
                                     cindex < begin_size + 1;
×
3127
                                     cindex++)
3128
                                {
3129
                                    this->jlf_cached_line[cindex] = toupper(
×
3130
                                        this->jlf_cached_line[cindex]);
×
3131
                                }
3132
                                for (size_t cindex = begin_size + 1;
×
3133
                                     cindex < this->jlf_cached_line.size();
×
3134
                                     cindex++)
3135
                                {
3136
                                    this->jlf_cached_line[cindex] = tolower(
×
3137
                                        this->jlf_cached_line[cindex]);
×
3138
                                }
3139
                                break;
×
3140
                        }
3141
                        break;
24,142✔
3142
                }
3143
            }
3144
            this->json_append_to_cache("\n", 1);
2,336✔
3145
            sub_offset += 1;
2,336✔
3146

3147
            for (size_t lpc = 0; lpc < this->jlf_line_values.lvv_values.size();
20,811✔
3148
                 lpc++)
3149
            {
3150
                static const intern_string_t body_name
3151
                    = intern_string::lookup("body", -1);
18,475✔
3152
                auto& lv = this->jlf_line_values.lvv_values[lpc];
18,475✔
3153

3154
                if (lv.lv_meta.is_hidden() || used_values[lpc]
28,340✔
3155
                    || body_name == lv.lv_meta.lvm_name)
28,340✔
3156
                {
3157
                    continue;
17,326✔
3158
                }
3159

3160
                auto str = lv.to_string();
1,149✔
3161
                while (endswith(str, "\n")) {
1,149✔
3162
                    str.pop_back();
×
3163
                }
3164

3165
                lv.lv_sub_offset = sub_offset;
1,149✔
3166
                lv.lv_origin.lr_start = this->jlf_cached_line.size() + 2
1,149✔
3167
                    + lv.lv_meta.lvm_name.size() + 2;
1,149✔
3168
                auto frag = string_fragment::from_str(str);
1,149✔
3169
                while (true) {
3170
                    auto utf_scan_res = is_utf8(frag, '\n');
1,155✔
3171

3172
                    this->json_append_to_cache("  ", 2);
1,155✔
3173
                    this->json_append_to_cache(
1,155✔
3174
                        lv.lv_meta.lvm_name.to_string_fragment());
1,155✔
3175
                    this->json_append_to_cache(": ", 2);
1,155✔
3176
                    lr.lr_start = this->jlf_cached_line.size();
1,155✔
3177
                    this->json_append_to_cache(utf_scan_res.usr_valid_frag);
1,155✔
3178
                    lr.lr_end = this->jlf_cached_line.size();
1,155✔
3179
                    if (lv.lv_meta.lvm_name == this->elf_body_field) {
1,155✔
3180
                        this->jlf_line_attrs.emplace_back(lr, SA_BODY.value());
×
3181
                    } else {
3182
                        this->jlf_line_attrs.emplace_back(
1,155✔
3183
                            lr, SA_EXTRA_CONTENT.value());
2,310✔
3184
                    }
3185
                    this->json_append_to_cache("\n", 1);
1,155✔
3186
                    sub_offset += 1;
1,155✔
3187
                    if (utf_scan_res.usr_remaining) {
1,155✔
3188
                        frag = utf_scan_res.usr_remaining.value();
6✔
3189
                    } else {
3190
                        break;
1,149✔
3191
                    }
3192
                }
6✔
3193
                lv.lv_origin.lr_end = this->jlf_cached_line.size() - 1;
1,149✔
3194
                if (lv.lv_meta.lvm_name == this->elf_opid_field
1,149✔
3195
                    && !lv.lv_origin.empty())
1,149✔
3196
                {
3197
                    this->jlf_line_attrs.emplace_back(lv.lv_origin,
×
3198
                                                      L_OPID.value());
×
3199
                }
3200
            }
1,149✔
3201
        }
2,336✔
3202

3203
        this->jlf_line_offsets.push_back(0);
2,350✔
3204
        for (size_t lpc = 0; lpc < this->jlf_cached_line.size(); lpc++) {
265,887✔
3205
            if (this->jlf_cached_line[lpc] == '\n') {
263,537✔
3206
                this->jlf_line_offsets.push_back(lpc + 1);
4,285✔
3207
            }
3208
        }
3209
        this->jlf_line_offsets.push_back(this->jlf_cached_line.size());
2,350✔
3210
        this->jlf_cached_offset = ll.get_offset();
2,350✔
3211
        this->jlf_cached_opts = opts;
2,350✔
3212
    }
2,422✔
3213

3214
    off_t this_off = 0, next_off = 0;
4,443✔
3215

3216
    if (!this->jlf_line_offsets.empty()
4,443✔
3217
        && ll.get_sub_offset() < this->jlf_line_offsets.size())
4,443✔
3218
    {
3219
        require(ll.get_sub_offset() < this->jlf_line_offsets.size());
4,443✔
3220

3221
        this_off = this->jlf_line_offsets[ll.get_sub_offset()];
4,443✔
3222
        if ((ll.get_sub_offset() + 1) < (int) this->jlf_line_offsets.size()) {
4,443✔
3223
            next_off = this->jlf_line_offsets[ll.get_sub_offset() + 1];
4,443✔
3224
        } else {
3225
            next_off = this->jlf_cached_line.size();
×
3226
        }
3227
        if (next_off > 0 && this->jlf_cached_line[next_off - 1] == '\n'
4,443✔
3228
            && this_off != next_off)
8,886✔
3229
        {
3230
            next_off -= 1;
4,443✔
3231
        }
3232
    }
3233

3234
    if (opts.full_message) {
4,443✔
3235
        sbr.share(this->jlf_share_manager,
642✔
3236
                  &this->jlf_cached_line[0],
321✔
3237
                  this->jlf_cached_line.size());
3238
    } else {
3239
        sbr.share(this->jlf_share_manager,
8,244✔
3240
                  this->jlf_cached_line.data() + this_off,
4,122✔
3241
                  next_off - this_off);
4,122✔
3242
    }
3243
    sbr.get_metadata().m_valid_utf = ll.is_valid_utf();
4,443✔
3244
    sbr.get_metadata().m_has_ansi = ll.has_ansi();
4,443✔
3245
    this->jlf_cached_sub_range.lr_start = this_off;
4,443✔
3246
    this->jlf_cached_sub_range.lr_end = next_off;
4,443✔
3247
    this->jlf_line_values.lvv_sbr = sbr.clone();
4,443✔
3248
}
3249

3250
struct compiled_header_expr {
3251
    auto_mem<sqlite3_stmt> che_stmt{sqlite3_finalize};
3252
    bool che_enabled{true};
3253
};
3254

3255
struct format_header_expressions : public lnav_config_listener {
3256
    format_header_expressions() : lnav_config_listener(__FILE__) {}
1,171✔
3257

3258
    auto_sqlite3 e_db;
3259
    std::map<intern_string_t, std::map<std::string, compiled_header_expr>>
3260
        e_header_exprs;
3261
};
3262

3263
using safe_format_header_expressions = safe::Safe<format_header_expressions>;
3264

3265
static safe_format_header_expressions format_header_exprs;
3266

3267
std::optional<external_file_format>
3268
detect_mime_type(const std::filesystem::path& filename)
617✔
3269
{
3270
    uint8_t buffer[1024];
3271
    size_t buffer_size = 0;
617✔
3272

3273
    {
3274
        auto_fd fd;
617✔
3275

3276
        if ((fd = lnav::filesystem::openp(filename, O_RDONLY)) == -1) {
617✔
3277
            return std::nullopt;
×
3278
        }
3279

3280
        ssize_t rc;
3281

3282
        if ((rc = read(fd, buffer, sizeof(buffer))) == -1) {
617✔
3283
            return std::nullopt;
×
3284
        }
3285
        buffer_size = rc;
617✔
3286
    }
617✔
3287

3288
    auto hexbuf = auto_buffer::alloc(buffer_size * 2);
617✔
3289

3290
    for (size_t lpc = 0; lpc < buffer_size; lpc++) {
330,397✔
3291
        fmt::format_to(
329,780✔
3292
            std::back_inserter(hexbuf), FMT_STRING("{:02x}"), buffer[lpc]);
1,319,120✔
3293
    }
3294

3295
    safe::WriteAccess<safe_format_header_expressions> in(format_header_exprs);
617✔
3296

3297
    for (const auto& format : log_format::get_root_formats()) {
46,781✔
3298
        auto elf = std::dynamic_pointer_cast<external_log_format>(format);
46,164✔
3299
        if (elf == nullptr) {
46,164✔
3300
            continue;
3,085✔
3301
        }
3302

3303
        if (elf->elf_converter.c_header.h_exprs.he_exprs.empty()) {
43,079✔
3304
            continue;
41,845✔
3305
        }
3306

3307
        if (buffer_size < elf->elf_converter.c_header.h_size) {
1,234✔
3308
            log_debug(
106✔
3309
                "%s: file content too small (%zu) for header detection: %s",
3310
                filename.c_str(),
3311
                buffer_size,
3312
                elf->get_name().get());
3313
            continue;
106✔
3314
        }
3315
        for (const auto& hpair : elf->elf_converter.c_header.h_exprs.he_exprs) {
4,044✔
3316
            auto& he = in->e_header_exprs[elf->get_name()][hpair.first];
2,916✔
3317

3318
            if (!he.che_enabled) {
2,916✔
3319
                continue;
×
3320
            }
3321

3322
            auto* stmt = he.che_stmt.in();
2,916✔
3323

3324
            if (stmt == nullptr) {
2,916✔
3325
                continue;
×
3326
            }
3327
            sqlite3_reset(stmt);
2,916✔
3328
            auto count = sqlite3_bind_parameter_count(stmt);
2,916✔
3329
            for (int lpc = 0; lpc < count; lpc++) {
5,832✔
3330
                const auto* name = sqlite3_bind_parameter_name(stmt, lpc + 1);
2,916✔
3331

3332
                if (name[0] == '$') {
2,916✔
3333
                    const char* env_value;
3334

3335
                    if ((env_value = getenv(&name[1])) != nullptr) {
×
3336
                        sqlite3_bind_text(
×
3337
                            stmt, lpc + 1, env_value, -1, SQLITE_STATIC);
3338
                    }
3339
                    continue;
×
3340
                }
3341
                if (strcmp(name, ":header") == 0) {
2,916✔
3342
                    sqlite3_bind_text(stmt,
2,916✔
3343
                                      lpc + 1,
3344
                                      hexbuf.in(),
2,916✔
3345
                                      hexbuf.size(),
2,916✔
3346
                                      SQLITE_STATIC);
3347
                    continue;
2,916✔
3348
                }
3349
                if (strcmp(name, ":filepath") == 0) {
×
3350
                    sqlite3_bind_text(
×
3351
                        stmt, lpc + 1, filename.c_str(), -1, SQLITE_STATIC);
3352
                    continue;
×
3353
                }
3354
            }
3355

3356
            auto step_res = sqlite3_step(stmt);
2,916✔
3357

3358
            switch (step_res) {
2,916✔
3359
                case SQLITE_OK:
2,916✔
3360
                case SQLITE_DONE:
3361
                    continue;
2,916✔
3362
                case SQLITE_ROW:
×
3363
                    break;
×
3364
                default: {
×
3365
                    log_error(
×
3366
                        "failed to execute file-format header expression: "
3367
                        "%s:%s -- %s",
3368
                        elf->get_name().get(),
3369
                        hpair.first.c_str(),
3370
                        sqlite3_errmsg(in->e_db));
3371
                    he.che_enabled = false;
×
3372
                    continue;
×
3373
                }
3374
            }
3375

3376
            log_info("detected format for: %s -- %s (header-expr: %s)",
×
3377
                     filename.c_str(),
3378
                     elf->get_name().get(),
3379
                     hpair.first.c_str());
3380
            return external_file_format{
×
3381
                elf->get_name().to_string(),
×
3382
                elf->elf_converter.c_command.pp_value,
×
3383
                elf->elf_converter.c_command.pp_location.sl_source.to_string(),
×
3384
            };
3385
        }
3386
    }
46,164✔
3387

3388
    return std::nullopt;
617✔
3389
}
617✔
3390

3391
log_format::scan_result_t
3392
log_format::test_line(sample_t& sample,
×
3393
                      std::vector<lnav::console::user_message>& msgs)
3394
{
3395
    return scan_no_match{};
×
3396
}
3397

3398
log_format::scan_result_t
3399
external_log_format::test_line(sample_t& sample,
186,279✔
3400
                               std::vector<lnav::console::user_message>& msgs)
3401
{
3402
    auto lines
3403
        = string_fragment::from_str(sample.s_line.pp_value).split_lines();
186,279✔
3404

3405
    if (this->elf_type == elf_type_t::ELF_TYPE_JSON) {
186,279✔
3406
        auto alloc = ArenaAlloc::Alloc<char>{};
3,006✔
3407
        pattern_locks pats;
3,006✔
3408
        auto sbc = scan_batch_context{
3,006✔
3409
            alloc,
3410
            pats,
3411
        };
3,006✔
3412
        sbc.sbc_value_stats.resize(this->elf_value_defs.size());
3,006✔
3413
        std::vector<logline> dst;
3,006✔
3414
        auto li = line_info{
3,006✔
3415
            {0, lines[0].length()},
3,006✔
3416
        };
3,006✔
3417
        shared_buffer sb;
3,006✔
3418
        shared_buffer_ref sbr;
3,006✔
3419
        sbr.share(sb, lines[0].data(), (size_t) lines[0].length());
3,006✔
3420

3421
        return this->scan_json(dst, li, sbr, sbc);
3,006✔
3422
    }
3,006✔
3423

3424
    scan_result_t retval = scan_no_match{"no patterns matched"};
183,273✔
3425
    auto found = false;
183,273✔
3426

3427
    for (auto pat_iter = this->elf_pattern_order.begin();
183,273✔
3428
         pat_iter != this->elf_pattern_order.end();
1,379,104✔
3429
         ++pat_iter)
1,195,831✔
3430
    {
3431
        auto& pat = *(*pat_iter);
1,195,831✔
3432

3433
        if (!pat.p_pcre.pp_value) {
1,195,831✔
3434
            continue;
1,012,560✔
3435
        }
3436

3437
        auto md = pat.p_pcre.pp_value->create_match_data();
1,195,831✔
3438
        auto match_res = pat.p_pcre.pp_value->capture_from(lines[0])
1,195,831✔
3439
                             .into(md)
1,195,831✔
3440
                             .matches(PCRE2_NO_UTF_CHECK)
2,391,662✔
3441
                             .ignore_error();
1,195,831✔
3442
        if (!match_res) {
1,195,831✔
3443
            continue;
1,012,560✔
3444
        }
3445
        retval = scan_match{1000};
183,271✔
3446
        found = true;
183,271✔
3447

3448
        sample.s_matched_regexes.insert(pat.p_name.to_string());
183,271✔
3449

3450
        const auto ts_cap = md[pat.p_timestamp_field_index];
183,271✔
3451
        const auto level_cap = md[pat.p_level_field_index];
183,271✔
3452
        const char* const* custom_formats = this->get_timestamp_formats();
183,271✔
3453
        date_time_scanner dts;
183,271✔
3454
        timeval tv;
3455
        exttm tm;
183,271✔
3456

3457
        if (ts_cap && ts_cap->sf_begin == 0) {
183,271✔
3458
            pat.p_timestamp_end = ts_cap->sf_end;
112,288✔
3459
        }
3460
        const char* dts_scan_res = nullptr;
183,271✔
3461

3462
        if (ts_cap) {
183,271✔
3463
            dts_scan_res = dts.scan(
183,269✔
3464
                ts_cap->data(), ts_cap->length(), custom_formats, &tm, tv);
183,269✔
3465
        }
3466
        if (dts_scan_res != nullptr) {
183,271✔
3467
            if (dts_scan_res != ts_cap->data() + ts_cap->length()) {
183,268✔
3468
                auto match_len = dts_scan_res - ts_cap->data();
×
3469
                auto notes = attr_line_t("the used timestamp format: ");
×
3470
                if (custom_formats == nullptr) {
×
3471
                    notes.append(PTIMEC_FORMATS[dts.dts_fmt_lock].pf_fmt);
×
3472
                } else {
3473
                    notes.append(custom_formats[dts.dts_fmt_lock]);
×
3474
                }
3475
                notes.append("\n  ")
×
3476
                    .append(ts_cap.value())
×
3477
                    .append("\n")
×
3478
                    .append(2 + match_len, ' ')
×
3479
                    .append("^ matched up to here"_snippet_border);
×
3480
                auto um = lnav::console::user_message::warning(
×
3481
                              attr_line_t("timestamp was not fully matched: ")
×
3482
                                  .append_quoted(ts_cap.value()))
×
3483
                              .with_snippet(sample.s_line.to_snippet())
×
3484
                              .with_note(notes)
×
3485
                              .move();
×
3486

3487
                msgs.emplace_back(um);
×
3488
            }
3489
        } else if (!ts_cap) {
3✔
3490
            msgs.emplace_back(
2✔
3491
                lnav::console::user_message::error(
×
3492
                    attr_line_t("invalid sample log message: ")
4✔
3493
                        .append(lnav::to_json(sample.s_line.pp_value)))
4✔
3494
                    .with_reason(attr_line_t("timestamp was not captured"))
4✔
3495
                    .with_snippet(sample.s_line.to_snippet())
4✔
3496
                    .with_help(attr_line_t(
4✔
3497
                        "A timestamp needs to be captured in order for a "
3498
                        "line to be recognized as a log message")));
3499
        } else {
3500
            attr_line_t notes;
1✔
3501

3502
            if (custom_formats == nullptr) {
1✔
3503
                notes.append("the following built-in formats were tried:");
×
3504
                for (int lpc = 0; PTIMEC_FORMATS[lpc].pf_fmt != nullptr; lpc++)
×
3505
                {
3506
                    off_t off = 0;
×
3507

3508
                    PTIMEC_FORMATS[lpc].pf_func(
×
3509
                        &tm, ts_cap->data(), off, ts_cap->length());
×
3510
                    notes.append("\n  ")
×
3511
                        .append(ts_cap.value())
×
3512
                        .append("\n")
×
3513
                        .append(2 + off, ' ')
×
3514
                        .append("^ "_snippet_border)
×
3515
                        .append_quoted(
×
3516
                            lnav::roles::symbol(PTIMEC_FORMATS[lpc].pf_fmt))
×
3517
                        .append(" matched up to here"_snippet_border);
×
3518
                }
3519
            } else {
3520
                notes.append("the following custom formats were tried:");
1✔
3521
                for (int lpc = 0; custom_formats[lpc] != nullptr; lpc++) {
2✔
3522
                    off_t off = 0;
1✔
3523

3524
                    ptime_fmt(custom_formats[lpc],
1✔
3525
                              &tm,
3526
                              ts_cap->data(),
3527
                              off,
3528
                              ts_cap->length());
1✔
3529
                    notes.append("\n  ")
1✔
3530
                        .append(ts_cap.value())
1✔
3531
                        .append("\n")
1✔
3532
                        .append(2 + off, ' ')
1✔
3533
                        .append("^ "_snippet_border)
1✔
3534
                        .append_quoted(lnav::roles::symbol(custom_formats[lpc]))
2✔
3535
                        .append(" matched up to here"_snippet_border);
1✔
3536
                }
3537
            }
3538

3539
            msgs.emplace_back(
1✔
3540
                lnav::console::user_message::error(
×
3541
                    attr_line_t("invalid sample log message: ")
1✔
3542
                        .append(lnav::to_json(sample.s_line.pp_value)))
2✔
3543
                    .with_reason(attr_line_t("unrecognized timestamp -- ")
2✔
3544
                                     .append(ts_cap.value()))
1✔
3545
                    .with_snippet(sample.s_line.to_snippet())
2✔
3546
                    .with_note(notes)
1✔
3547
                    .with_help(attr_line_t("If the timestamp format is not "
2✔
3548
                                           "supported by default, you can "
3549
                                           "add a custom format with the ")
3550
                                   .append_quoted("timestamp-format"_symbol)
1✔
3551
                                   .append(" property")));
1✔
3552
        }
1✔
3553

3554
        auto level = this->convert_level(
183,271✔
3555
            level_cap.value_or(string_fragment::invalid()), nullptr);
183,271✔
3556

3557
        if (sample.s_level != LEVEL_UNKNOWN && sample.s_level != level) {
183,271✔
3558
            attr_line_t note_al;
1✔
3559

3560
            note_al.append("matched regex = ")
1✔
3561
                .append(lnav::roles::symbol(pat.p_name.to_string()))
2✔
3562
                .append("\n")
1✔
3563
                .append("captured level = ")
1✔
3564
                .append_quoted(level_cap->to_string());
1✔
3565
            if (level_cap && !this->elf_level_patterns.empty()) {
1✔
3566
                thread_local auto md = lnav::pcre2pp::match_data::unitialized();
1✔
3567

3568
                note_al.append("\nlevel regular expression match results:");
1✔
3569
                for (const auto& level_pattern : this->elf_level_patterns) {
3✔
3570
                    attr_line_t regex_al
3571
                        = level_pattern.second.lp_pcre.pp_value->get_pattern();
2✔
3572
                    lnav::snippets::regex_highlighter(
2✔
3573
                        regex_al, -1, line_range{0, (int) regex_al.length()});
2✔
3574
                    note_al.append("\n  ")
2✔
3575
                        .append(lnav::roles::symbol(
4✔
3576
                            level_pattern.second.lp_pcre.pp_path.to_string()))
4✔
3577
                        .append(" = ")
2✔
3578
                        .append(regex_al)
2✔
3579
                        .append("\n    ");
2✔
3580
                    auto match_res = level_pattern.second.lp_pcre.pp_value
2✔
3581
                                         ->capture_from(level_cap.value())
2✔
3582
                                         .into(md)
2✔
3583
                                         .matches(PCRE2_NO_UTF_CHECK)
4✔
3584
                                         .ignore_error();
2✔
3585
                    if (!match_res) {
2✔
3586
                        note_al.append(lnav::roles::warning("no match"));
1✔
3587
                        continue;
1✔
3588
                    }
3589

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

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

3690
    if (!found && !this->elf_pattern_order.empty()) {
183,273✔
3691
        std::vector<std::pair<ssize_t, intern_string_t>> partial_indexes;
2✔
3692
        attr_line_t notes;
2✔
3693
        size_t max_name_width = 0;
2✔
3694

3695
        for (const auto& pat_iter : this->elf_pattern_order) {
10✔
3696
            auto& pat = *pat_iter;
8✔
3697

3698
            if (!pat.p_pcre.pp_value) {
8✔
3699
                continue;
×
3700
            }
3701

3702
            partial_indexes.emplace_back(
8✔
3703
                pat.p_pcre.pp_value->match_partial(lines[0]), pat.p_name);
8✔
3704
            max_name_width = std::max(max_name_width, pat.p_name.size());
8✔
3705
        }
3706
        for (const auto& line_frag : lines) {
4✔
3707
            auto src_line = attr_line_t(line_frag.to_string());
2✔
3708
            if (!line_frag.endswith("\n")) {
2✔
3709
                src_line.append("\n");
2✔
3710
            }
3711
            src_line.with_attr_for_all(VC_ROLE.value(role_t::VCR_QUOTED_CODE));
2✔
3712
            notes.append("   ").append(src_line);
2✔
3713
            for (auto& part_pair : partial_indexes) {
10✔
3714
                if (part_pair.first >= 0
16✔
3715
                    && part_pair.first < line_frag.length())
8✔
3716
                {
3717
                    notes.append("   ")
8✔
3718
                        .append(part_pair.first, ' ')
8✔
3719
                        .append("^ "_snippet_border)
8✔
3720
                        .append(
8✔
3721
                            lnav::roles::symbol(part_pair.second.to_string()))
16✔
3722
                        .append(" matched up to here"_snippet_border)
8✔
3723
                        .append("\n");
8✔
3724
                }
3725
                part_pair.first -= line_frag.length();
8✔
3726
            }
3727
        }
2✔
3728
        notes.add_header(
2✔
3729
            "the following shows how each pattern matched this sample:\n");
3730

3731
        attr_line_t regex_note;
2✔
3732
        for (const auto& pat_iter : this->elf_pattern_order) {
10✔
3733
            if (!pat_iter->p_pcre.pp_value) {
8✔
3734
                regex_note
3735
                    .append(lnav::roles::symbol(fmt::format(
×
3736
                        FMT_STRING("{:{}}"), pat_iter->p_name, max_name_width)))
×
3737
                    .append(" is invalid");
×
3738
                continue;
×
3739
            }
3740

3741
            attr_line_t regex_al = pat_iter->p_pcre.pp_value->get_pattern();
8✔
3742
            lnav::snippets::regex_highlighter(
8✔
3743
                regex_al, -1, line_range{0, (int) regex_al.length()});
8✔
3744

3745
            regex_note
3746
                .append(lnav::roles::symbol(fmt::format(
16✔
3747
                    FMT_STRING("{:{}}"), pat_iter->p_name, max_name_width)))
24✔
3748
                .append(" = ")
8✔
3749
                .append_quoted(regex_al)
16✔
3750
                .append("\n");
8✔
3751
        }
8✔
3752

3753
        msgs.emplace_back(
2✔
3754
            lnav::console::user_message::error(
×
3755
                attr_line_t("invalid sample log message: ")
2✔
3756
                    .append(lnav::to_json(sample.s_line.pp_value)))
4✔
3757
                .with_reason("sample does not match any patterns")
4✔
3758
                .with_snippet(sample.s_line.to_snippet())
4✔
3759
                .with_note(notes.rtrim())
4✔
3760
                .with_note(regex_note));
3761
    }
2✔
3762

3763
    return retval;
183,273✔
3764
}
186,279✔
3765

3766
void
3767
external_log_format::build(std::vector<lnav::console::user_message>& errors)
52,885✔
3768
{
3769
    auto& vc = view_colors::singleton();
52,885✔
3770

3771
    if (!this->lf_timestamp_field.empty()) {
52,885✔
3772
        auto& vd = this->elf_value_defs[this->lf_timestamp_field];
52,885✔
3773
        if (vd.get() == nullptr) {
52,885✔
3774
            vd = std::make_shared<value_def>(
38,520✔
3775
                this->lf_timestamp_field,
38,520✔
3776
                value_kind_t::VALUE_TEXT,
×
3777
                logline_value_meta::internal_column{},
×
3778
                this);
38,520✔
3779
            if (this->elf_type == elf_type_t::ELF_TYPE_JSON) {
38,520✔
3780
                this->elf_value_def_order.emplace_back(vd);
6,490✔
3781
            }
3782
        }
3783
        vd->vd_meta.lvm_name = this->lf_timestamp_field;
52,885✔
3784
        vd->vd_meta.lvm_kind = value_kind_t::VALUE_TEXT;
52,885✔
3785
        vd->vd_meta.lvm_column = logline_value_meta::internal_column{};
52,885✔
3786
        vd->vd_internal = true;
52,885✔
3787

3788
        this->elf_value_defs[LOG_TIME_STR] = vd;
52,885✔
3789
    }
3790

3791
    if (!this->lf_subsecond_field.empty()) {
52,885✔
3792
        if (!this->lf_subsecond_unit.has_value()) {
97✔
3793
            errors.emplace_back(
1✔
3794
                lnav::console::user_message::error(
×
3795
                    attr_line_t()
2✔
3796
                        .append_quoted(
1✔
3797
                            lnav::roles::symbol(this->elf_name.to_string()))
2✔
3798
                        .append(" is not a valid log format"))
1✔
3799
                    .with_reason(attr_line_t()
2✔
3800
                                     .append_quoted("subsecond-units"_symbol)
1✔
3801
                                     .append(" must be set when ")
1✔
3802
                                     .append_quoted("subsecond-field"_symbol)
1✔
3803
                                     .append(" is used"))
1✔
3804
                    .with_snippets(this->get_snippets()));
2✔
3805
        } else {
3806
            auto& vd = this->elf_value_defs[this->lf_subsecond_field];
96✔
3807
            if (vd.get() == nullptr) {
96✔
3808
                vd = std::make_shared<value_def>(
96✔
3809
                    this->lf_subsecond_field,
96✔
3810
                    value_kind_t::VALUE_INTEGER,
×
3811
                    logline_value_meta::internal_column{},
×
3812
                    this);
96✔
3813
                if (this->elf_type == elf_type_t::ELF_TYPE_JSON) {
96✔
3814
                    this->elf_value_def_order.emplace_back(vd);
96✔
3815
                }
3816
            }
3817
            vd->vd_meta.lvm_name = this->lf_subsecond_field;
96✔
3818
            vd->vd_meta.lvm_column = logline_value_meta::internal_column{};
96✔
3819
            vd->vd_meta.lvm_hidden = true;
96✔
3820
            vd->vd_internal = true;
96✔
3821
        }
3822
    }
3823

3824
    if (startswith(this->elf_level_field.get(), "/")) {
52,885✔
3825
        this->elf_level_field
3826
            = intern_string::lookup(this->elf_level_field.get() + 1);
192✔
3827
    }
3828
    if (!this->elf_level_field.empty()) {
52,885✔
3829
        auto level_iter = this->elf_value_defs.find(this->elf_level_field);
52,885✔
3830
        if (level_iter == this->elf_value_defs.end()) {
52,885✔
3831
            auto& vd = this->elf_value_defs[this->elf_level_field];
25,656✔
3832
            vd = std::make_shared<value_def>(
25,656✔
3833
                this->elf_level_field,
25,656✔
3834
                value_kind_t::VALUE_TEXT,
×
3835
                logline_value_meta::internal_column{},
×
3836
                this);
25,656✔
3837
            if (this->elf_type == elf_type_t::ELF_TYPE_JSON) {
25,656✔
3838
                this->elf_value_def_order.emplace_back(vd);
2,639✔
3839
            }
3840
            vd->vd_meta.lvm_name = this->elf_level_field;
25,656✔
3841
            vd->vd_meta.lvm_column = logline_value_meta::internal_column{};
25,656✔
3842
            vd->vd_internal = true;
25,656✔
3843

3844
            if (this->elf_level_field != this->elf_body_field) {
25,656✔
3845
                this->elf_value_defs[LOG_LEVEL_STR] = vd;
24,809✔
3846
            }
3847
        } else {
3848
            if (level_iter->second->vd_meta.lvm_kind
27,229✔
3849
                != value_kind_t::VALUE_TEXT)
27,229✔
3850
            {
3851
                this->lf_level_hideable = false;
5,353✔
3852
            }
3853
            this->elf_value_defs[LOG_LEVEL_STR] = level_iter->second;
27,229✔
3854
        }
3855
    }
3856

3857
    auto opid_field_iter = this->elf_value_defs.find(LOG_OPID_STR);
52,885✔
3858
    if (opid_field_iter == this->elf_value_defs.end()) {
52,885✔
3859
        auto vd
3860
            = std::make_shared<value_def>(this->elf_opid_field,
52,885✔
3861
                                          value_kind_t::VALUE_TEXT,
×
3862
                                          logline_value_meta::internal_column{},
×
3863
                                          this);
52,885✔
3864
        if (this->elf_type == elf_type_t::ELF_TYPE_JSON) {
52,885✔
3865
            this->elf_value_def_order.emplace_back(vd);
10,341✔
3866
        }
3867
        vd->vd_meta.lvm_name = LOG_OPID_STR;
52,885✔
3868
        vd->vd_meta.lvm_kind = value_kind_t::VALUE_TEXT;
52,885✔
3869
        vd->vd_meta.lvm_column = logline_value_meta::internal_column{};
52,885✔
3870
        vd->vd_internal = true;
52,885✔
3871

3872
        this->elf_value_defs[LOG_OPID_STR] = vd;
52,885✔
3873
    }
52,885✔
3874

3875
    if (!this->elf_body_field.empty()) {
52,885✔
3876
        auto& vd = this->elf_value_defs[this->elf_body_field];
52,885✔
3877
        if (vd.get() == nullptr) {
52,885✔
3878
            vd = std::make_shared<value_def>(
42,929✔
3879
                this->elf_body_field,
42,929✔
3880
                value_kind_t::VALUE_TEXT,
×
3881
                logline_value_meta::internal_column{},
×
3882
                this);
42,929✔
3883
            if (this->elf_type == elf_type_t::ELF_TYPE_JSON) {
42,929✔
3884
                this->elf_value_def_order.emplace_back(vd);
6,489✔
3885
            }
3886
        }
3887
        vd->vd_meta.lvm_name = this->elf_body_field;
52,885✔
3888
        vd->vd_meta.lvm_kind = value_kind_t::VALUE_TEXT;
52,885✔
3889
        vd->vd_meta.lvm_column = logline_value_meta::internal_column{};
52,885✔
3890
        vd->vd_internal = true;
52,885✔
3891
    }
3892

3893
    if (!this->elf_src_file_field.empty()) {
52,885✔
3894
        auto& vd = this->elf_value_defs[this->elf_src_file_field];
7,511✔
3895
        if (vd.get() == nullptr) {
7,511✔
3896
            vd = std::make_shared<value_def>(
1✔
3897
                this->elf_src_file_field,
1✔
3898
                value_kind_t::VALUE_TEXT,
×
3899
                logline_value_meta::internal_column{},
×
3900
                this);
2✔
3901
        }
3902
        vd->vd_meta.lvm_name = this->elf_src_file_field;
7,511✔
3903
        vd->vd_meta.lvm_kind = value_kind_t::VALUE_TEXT;
7,511✔
3904
        vd->vd_meta.lvm_column = logline_value_meta::internal_column{};
7,511✔
3905
    }
3906

3907
    if (!this->elf_src_line_field.empty()) {
52,885✔
3908
        auto& vd = this->elf_value_defs[this->elf_src_line_field];
7,511✔
3909
        if (vd.get() == nullptr) {
7,511✔
3910
            vd = std::make_shared<value_def>(
1✔
3911
                this->elf_src_line_field,
1✔
3912
                value_kind_t::VALUE_INTEGER,
×
3913
                logline_value_meta::internal_column{},
×
3914
                this);
2✔
3915
        }
3916
        vd->vd_meta.lvm_name = this->elf_src_line_field;
7,511✔
3917
        vd->vd_meta.lvm_kind = value_kind_t::VALUE_INTEGER;
7,511✔
3918
        vd->vd_meta.lvm_column = logline_value_meta::internal_column{};
7,511✔
3919
    }
3920

3921
    if (!this->elf_thread_id_field.empty()) {
52,885✔
3922
        auto& vd = this->elf_value_defs[this->elf_thread_id_field];
17,274✔
3923
        if (vd.get() == nullptr) {
17,274✔
3924
            vd = std::make_shared<value_def>(
1✔
3925
                this->elf_thread_id_field,
1✔
3926
                value_kind_t::VALUE_TEXT,
×
3927
                logline_value_meta::internal_column{},
×
3928
                this);
2✔
3929
        }
3930
        vd->vd_meta.lvm_name = this->elf_thread_id_field;
17,274✔
3931
        vd->vd_meta.lvm_kind = value_kind_t::VALUE_TEXT;
17,274✔
3932
        vd->vd_meta.lvm_column = logline_value_meta::internal_column{};
17,274✔
3933
    }
3934

3935
    if (!this->elf_duration_field.empty()) {
52,885✔
3936
        auto& vd = this->elf_value_defs[this->elf_duration_field];
2,253✔
3937
        if (vd.get() == nullptr) {
2,253✔
3938
            vd = std::make_shared<value_def>(
×
3939
                this->elf_duration_field,
×
3940
                value_kind_t::VALUE_FLOAT,
×
3941
                logline_value_meta::internal_column{},
×
3942
                this);
×
3943
        }
3944
        vd->vd_meta.lvm_name = this->elf_duration_field;
2,253✔
3945
        vd->vd_meta.lvm_kind = value_kind_t::VALUE_FLOAT;
2,253✔
3946
        vd->vd_meta.lvm_column = logline_value_meta::internal_column{};
2,253✔
3947
    }
3948

3949
    for (auto& od_pair : *this->lf_opid_description_def) {
75,417✔
3950
        od_pair.second.od_index = this->lf_opid_description_def_vec->size();
22,532✔
3951
        this->lf_opid_description_def_vec->emplace_back(&od_pair.second);
22,532✔
3952
    }
3953

3954
    for (auto& od_pair : *this->lf_subid_description_def) {
53,636✔
3955
        od_pair.second.od_index = this->lf_subid_description_def_vec->size();
751✔
3956
        this->lf_subid_description_def_vec->emplace_back(&od_pair.second);
751✔
3957
    }
3958

3959
    if (!this->lf_timestamp_format.empty()) {
52,885✔
3960
        this->lf_timestamp_format.push_back(nullptr);
6,108✔
3961
    }
3962
    auto src_file_found = 0;
52,885✔
3963
    auto src_line_found = 0;
52,885✔
3964
    auto thread_id_found = 0;
52,885✔
3965
    auto duration_found = 0;
52,885✔
3966
    for (auto& elf_pattern : this->elf_patterns) {
150,352✔
3967
        auto& pat = *elf_pattern.second;
97,467✔
3968

3969
        if (pat.p_pcre.pp_value == nullptr) {
97,467✔
3970
            continue;
1✔
3971
        }
3972

3973
        if (pat.p_opid_field_index == -1
194,932✔
3974
            && this->lf_opid_source.value_or(opid_source_t::from_description)
97,466✔
3975
                == opid_source_t::from_description
3976
            && this->lf_opid_description_def->size() == 1)
194,932✔
3977
        {
3978
            const auto& opid_def
3979
                = this->lf_opid_description_def->begin()->second;
24,783✔
3980
            for (const auto& desc : *opid_def.od_descriptors) {
55,574✔
3981
                for (auto named_cap : pat.p_pcre.pp_value->get_named_captures())
358,978✔
3982
                {
3983
                    const intern_string_t name
3984
                        = intern_string::lookup(named_cap.get_name());
328,187✔
3985

3986
                    if (name == desc.od_field.pp_value) {
328,187✔
3987
                        pat.p_opid_description_field_indexes.emplace_back(
57,076✔
3988
                            named_cap.get_index());
28,538✔
3989
                    }
3990
                }
3991
            }
3992
        }
3993

3994
        for (auto named_cap : pat.p_pcre.pp_value->get_named_captures()) {
842,148✔
3995
            const intern_string_t name
3996
                = intern_string::lookup(named_cap.get_name());
744,682✔
3997

3998
            if (name == this->lf_timestamp_field) {
744,682✔
3999
                pat.p_timestamp_field_index = named_cap.get_index();
97,464✔
4000
            }
4001
            if (name == this->lf_time_field) {
744,682✔
4002
                pat.p_time_field_index = named_cap.get_index();
751✔
4003
            }
4004
            if (name == this->elf_level_field) {
744,682✔
4005
                pat.p_level_field_index = named_cap.get_index();
73,984✔
4006
            }
4007
            if (name == this->elf_opid_field) {
744,682✔
4008
                pat.p_opid_field_index = named_cap.get_index();
20,277✔
4009
            }
4010
            if (name == this->elf_subid_field) {
744,682✔
4011
                pat.p_subid_field_index = named_cap.get_index();
11,265✔
4012
            }
4013
            if (name == this->elf_body_field) {
744,682✔
4014
                pat.p_body_field_index = named_cap.get_index();
83,946✔
4015
            }
4016
            if (name == this->elf_src_file_field) {
744,682✔
4017
                pat.p_src_file_field_index = named_cap.get_index();
12,016✔
4018
                src_file_found += 1;
12,016✔
4019
            }
4020
            if (name == this->elf_src_line_field) {
744,682✔
4021
                pat.p_src_line_field_index = named_cap.get_index();
13,518✔
4022
                src_line_found += 1;
13,518✔
4023
            }
4024
            if (name == this->elf_thread_id_field) {
744,682✔
4025
                pat.p_thread_id_field_index = named_cap.get_index();
42,056✔
4026
                thread_id_found += 1;
42,056✔
4027
            }
4028
            if (name == this->elf_duration_field) {
744,682✔
4029
                pat.p_duration_field_index = named_cap.get_index();
4,506✔
4030
                duration_found += 1;
4,506✔
4031
            }
4032

4033
            auto value_iter = this->elf_value_defs.find(name);
744,682✔
4034
            if (value_iter != this->elf_value_defs.end()) {
744,682✔
4035
                auto vd = value_iter->second;
733,319✔
4036
                indexed_value_def ivd;
733,319✔
4037

4038
                ivd.ivd_index = named_cap.get_index();
733,319✔
4039
                if (!vd->vd_unit_field.empty()) {
733,319✔
4040
                    ivd.ivd_unit_field_index = pat.p_pcre.pp_value->name_index(
1,502✔
4041
                        vd->vd_unit_field.get());
751✔
4042
                } else {
4043
                    ivd.ivd_unit_field_index = -1;
732,568✔
4044
                }
4045
                if (!vd->vd_internal
733,319✔
4046
                    && !vd->vd_meta.lvm_column
1,260,907✔
4047
                            .is<logline_value_meta::table_column>())
527,588✔
4048
                {
4049
                    vd->vd_meta.lvm_column = logline_value_meta::table_column{
295,529✔
4050
                        this->elf_column_count++};
295,529✔
4051
                }
4052
                ivd.ivd_value_def = vd;
733,319✔
4053
                pat.p_value_by_index.push_back(ivd);
733,319✔
4054
            }
733,319✔
4055
            pat.p_value_name_to_index[name] = named_cap.get_index();
744,682✔
4056
        }
4057

4058
        stable_sort(pat.p_value_by_index.begin(), pat.p_value_by_index.end());
97,466✔
4059

4060
        for (int lpc = 0; lpc < (int) pat.p_value_by_index.size(); lpc++) {
830,785✔
4061
            auto& ivd = pat.p_value_by_index[lpc];
733,319✔
4062
            auto vd = ivd.ivd_value_def;
733,319✔
4063

4064
            if (!vd->vd_meta.lvm_foreign_key && !vd->vd_meta.lvm_identifier) {
733,319✔
4065
                switch (vd->vd_meta.lvm_kind) {
371,895✔
4066
                    case value_kind_t::VALUE_INTEGER:
51,915✔
4067
                    case value_kind_t::VALUE_FLOAT:
4068
                        pat.p_numeric_value_indexes.push_back(lpc);
51,915✔
4069
                        break;
51,915✔
4070
                    default:
319,980✔
4071
                        break;
319,980✔
4072
                }
4073
            }
4074
        }
733,319✔
4075

4076
        if (pat.p_timestamp_field_index == -1) {
97,466✔
4077
            errors.emplace_back(
2✔
4078
                lnav::console::user_message::error(
×
4079
                    attr_line_t("invalid pattern: ")
4✔
4080
                        .append_quoted(lnav::roles::symbol(pat.p_config_path)))
4✔
4081
                    .with_reason("no timestamp capture found in the pattern")
4✔
4082
                    .with_snippets(this->get_snippets())
4✔
4083
                    .with_help("all log messages need a timestamp"));
4084
        }
4085

4086
        if (!this->elf_level_field.empty() && pat.p_level_field_index == -1) {
97,466✔
4087
            log_warning("%s:level field '%s' not found in pattern",
23,482✔
4088
                        pat.p_config_path.c_str(),
4089
                        this->elf_level_field.get());
4090
        }
4091
        if (!this->elf_body_field.empty() && pat.p_body_field_index == -1) {
97,466✔
4092
            log_warning("%s:body field '%s' not found in pattern",
13,520✔
4093
                        pat.p_config_path.c_str(),
4094
                        this->elf_body_field.get());
4095
        }
4096

4097
        this->elf_pattern_order.push_back(elf_pattern.second);
97,466✔
4098
    }
4099
    if (this->elf_type == elf_type_t::ELF_TYPE_TEXT
105,770✔
4100
        && !this->elf_src_file_field.empty() && src_file_found == 0)
52,885✔
4101
    {
4102
        errors.emplace_back(
1✔
4103
            lnav::console::user_message::error(
×
4104
                attr_line_t("invalid pattern: ")
2✔
4105
                    .append_quoted(
1✔
4106
                        lnav::roles::symbol(this->elf_name.to_string())))
2✔
4107
                .with_reason("no source file capture found in the pattern")
2✔
4108
                .with_snippets(this->get_snippets())
2✔
4109
                .with_help(attr_line_t("at least one pattern needs a source "
2✔
4110
                                       "file capture named ")
4111
                               .append_quoted(this->elf_src_file_field.get())));
1✔
4112
    }
4113
    if (this->elf_type == elf_type_t::ELF_TYPE_TEXT
105,770✔
4114
        && !this->elf_src_line_field.empty() && src_line_found == 0)
52,885✔
4115
    {
4116
        errors.emplace_back(
1✔
4117
            lnav::console::user_message::error(
×
4118
                attr_line_t("invalid pattern: ")
2✔
4119
                    .append_quoted(
1✔
4120
                        lnav::roles::symbol(this->elf_name.to_string())))
2✔
4121
                .with_reason("no source line capture found in the pattern")
2✔
4122
                .with_snippets(this->get_snippets())
2✔
4123
                .with_help(attr_line_t("at least one pattern needs a source "
2✔
4124
                                       "line capture named ")
4125
                               .append_quoted(this->elf_src_line_field.get())));
1✔
4126
    }
4127
    if (this->elf_type == elf_type_t::ELF_TYPE_TEXT
105,770✔
4128
        && !this->elf_thread_id_field.empty() && thread_id_found == 0)
52,885✔
4129
    {
4130
        errors.emplace_back(
1✔
4131
            lnav::console::user_message::error(
×
4132
                attr_line_t("invalid pattern: ")
2✔
4133
                    .append_quoted(
1✔
4134
                        lnav::roles::symbol(this->elf_name.to_string())))
2✔
4135
                .with_reason("no thread ID capture found in the pattern")
2✔
4136
                .with_snippets(this->get_snippets())
2✔
4137
                .with_help(
4138
                    attr_line_t(
2✔
4139
                        "at least one pattern needs a thread ID capture named ")
4140
                        .append_quoted(this->elf_thread_id_field.get())));
1✔
4141
    }
4142
    if (this->elf_type == elf_type_t::ELF_TYPE_TEXT
105,770✔
4143
        && !this->elf_duration_field.empty() && duration_found == 0)
52,885✔
4144
    {
4145
        errors.emplace_back(
×
4146
            lnav::console::user_message::error(
×
4147
                attr_line_t("invalid pattern: ")
×
4148
                    .append_quoted(
×
4149
                        lnav::roles::symbol(this->elf_name.to_string())))
×
4150
                .with_reason("no duration capture found in the pattern")
×
4151
                .with_snippets(this->get_snippets())
×
4152
                .with_help(
4153
                    attr_line_t(
×
4154
                        "at least one pattern needs a duration capture named ")
4155
                        .append_quoted(this->elf_duration_field.get())));
×
4156
    }
4157

4158
    if (this->elf_type != elf_type_t::ELF_TYPE_TEXT) {
52,885✔
4159
        if (!this->elf_patterns.empty()) {
10,341✔
4160
            errors.emplace_back(
1✔
4161
                lnav::console::user_message::error(
×
4162
                    attr_line_t()
2✔
4163
                        .append_quoted(
1✔
4164
                            lnav::roles::symbol(this->elf_name.to_string()))
2✔
4165
                        .append(" is not a valid log format"))
1✔
4166
                    .with_reason("structured logs cannot have regexes")
2✔
4167
                    .with_snippets(this->get_snippets()));
2✔
4168
        }
4169
        if (this->elf_type == elf_type_t::ELF_TYPE_JSON) {
10,341✔
4170
            this->lf_multiline = true;
10,341✔
4171
            this->lf_structured = true;
10,341✔
4172
            this->lf_formatted_lines = true;
10,341✔
4173
            this->jlf_parse_context
4174
                = std::make_shared<yajlpp_parse_context>(this->elf_name);
10,341✔
4175
            this->jlf_yajl_handle.reset(
10,341✔
4176
                yajl_alloc(&this->jlf_parse_context->ypc_callbacks,
10,341✔
4177
                           nullptr,
4178
                           this->jlf_parse_context.get()),
10,341✔
4179
                yajl_handle_deleter());
4180
            yajl_config(
10,341✔
4181
                this->jlf_yajl_handle.get(), yajl_dont_validate_strings, 1);
4182
        }
4183
    } else {
4184
        if (this->elf_patterns.empty()) {
42,544✔
4185
            errors.emplace_back(lnav::console::user_message::error(
2✔
4186
                                    attr_line_t()
4✔
4187
                                        .append_quoted(lnav::roles::symbol(
4✔
4188
                                            this->elf_name.to_string()))
4✔
4189
                                        .append(" is not a valid log format"))
2✔
4190
                                    .with_reason("no regexes specified")
4✔
4191
                                    .with_snippets(this->get_snippets()));
4✔
4192
        }
4193
    }
4194

4195
    stable_sort(this->elf_level_pairs.begin(), this->elf_level_pairs.end());
52,885✔
4196

4197
    {
4198
        safe::WriteAccess<safe_format_header_expressions> hexprs(
4199
            format_header_exprs);
52,885✔
4200

4201
        if (hexprs->e_db.in() == nullptr) {
52,885✔
4202
            if (sqlite3_open(":memory:", hexprs->e_db.out()) != SQLITE_OK) {
751✔
4203
                log_error("unable to open memory DB");
×
4204
                return;
×
4205
            }
4206
            register_sqlite_funcs(hexprs->e_db.in(), sqlite_registration_funcs);
751✔
4207
        }
4208

4209
        for (const auto& hpair : this->elf_converter.c_header.h_exprs.he_exprs)
56,641✔
4210
        {
4211
            auto stmt_str
4212
                = fmt::format(FMT_STRING("SELECT 1 WHERE {}"), hpair.second);
11,268✔
4213
            compiled_header_expr che;
3,756✔
4214

4215
            log_info("preparing file-format header expression: %s",
3,756✔
4216
                     stmt_str.c_str());
4217
            auto retcode = sqlite3_prepare_v2(hexprs->e_db.in(),
7,512✔
4218
                                              stmt_str.c_str(),
4219
                                              stmt_str.size(),
3,756✔
4220
                                              che.che_stmt.out(),
4221
                                              nullptr);
4222
            if (retcode != SQLITE_OK) {
3,756✔
4223
                auto sql_al = attr_line_t(hpair.second)
2✔
4224
                                  .with_attr_for_all(SA_PREFORMATTED.value())
2✔
4225
                                  .with_attr_for_all(
1✔
4226
                                      VC_ROLE.value(role_t::VCR_QUOTED_CODE))
2✔
4227
                                  .move();
1✔
4228
                readline_sql_highlighter(
1✔
4229
                    sql_al, lnav::sql::dialect::sqlite, std::nullopt);
4230
                intern_string_t watch_expr_path = intern_string::lookup(
4231
                    fmt::format(FMT_STRING("/{}/converter/header/expr/{}"),
3✔
4232
                                this->elf_name,
1✔
4233
                                hpair.first));
2✔
4234
                auto snippet = lnav::console::snippet::from(
4235
                    source_location(watch_expr_path), sql_al);
1✔
4236

4237
                auto um = lnav::console::user_message::error(
2✔
4238
                              "SQL expression is invalid")
4239
                              .with_reason(sqlite3_errmsg(hexprs->e_db.in()))
2✔
4240
                              .with_snippet(snippet)
1✔
4241
                              .move();
1✔
4242

4243
                errors.emplace_back(um);
1✔
4244
                continue;
1✔
4245
            }
1✔
4246

4247
            hexprs->e_header_exprs[this->elf_name][hpair.first]
3,755✔
4248
                = std::move(che);
7,510✔
4249
        }
3,757✔
4250

4251
        if (!this->elf_converter.c_header.h_exprs.he_exprs.empty()
52,885✔
4252
            && this->elf_converter.c_command.pp_value.empty())
52,885✔
4253
        {
4254
            auto um = lnav::console::user_message::error(
2✔
4255
                          "A command is required when a converter is defined")
4256
                          .with_help(
2✔
4257
                              "The converter command transforms the file "
4258
                              "into a format that can be consumed by lnav")
4259
                          .with_snippets(this->get_snippets())
2✔
4260
                          .move();
1✔
4261
            errors.emplace_back(um);
1✔
4262
        }
1✔
4263
    }
52,885✔
4264

4265
    for (auto& vd : this->elf_value_def_order) {
523,318✔
4266
        std::vector<std::string>::iterator act_iter;
470,433✔
4267

4268
        if (!vd->vd_internal
470,433✔
4269
            && log_vtab_impl::RESERVED_COLUMNS.count(
891,337✔
4270
                vd->vd_meta.lvm_name.to_string_fragment()))
891,337✔
4271
        {
4272
            auto um = lnav::console::user_message::error(
×
4273
                          attr_line_t("value name ")
1✔
4274
                              .append_quoted(lnav::roles::symbol(
2✔
4275
                                  fmt::format(FMT_STRING("/{}/value/{}"),
4✔
4276
                                              this->elf_name,
1✔
4277
                                              vd->vd_meta.lvm_name)))
1✔
4278
                              .append(" is reserved and cannot be used"))
1✔
4279
                          .with_reason(
2✔
4280
                              "lnav automatically defines several columns in "
4281
                              "the log virtual table")
4282
                          .with_snippets(this->get_snippets())
2✔
4283
                          .with_help("Choose another name")
2✔
4284
                          .move();
1✔
4285
            errors.emplace_back(um);
1✔
4286
        }
1✔
4287

4288
        vd->vd_meta.lvm_format = this;
470,433✔
4289
        if (!vd->vd_internal
470,433✔
4290
            && !vd->vd_meta.lvm_column.is<logline_value_meta::table_column>())
470,433✔
4291
        {
4292
            vd->vd_meta.lvm_column
125,375✔
4293
                = logline_value_meta::table_column{this->elf_column_count++};
125,375✔
4294
        }
4295

4296
        if (vd->vd_meta.lvm_kind == value_kind_t::VALUE_UNKNOWN) {
470,433✔
4297
            vd->vd_meta.lvm_kind = value_kind_t::VALUE_TEXT;
×
4298
        }
4299

4300
        if (this->elf_type == elf_type_t::ELF_TYPE_TEXT) {
470,433✔
4301
            std::set<std::string> available_captures;
311,302✔
4302

4303
            bool found_in_pattern = false;
311,302✔
4304
            for (const auto& pat : this->elf_patterns) {
525,341✔
4305
                if (pat.second->p_pcre.pp_value == nullptr) {
525,339✔
4306
                    continue;
×
4307
                }
4308

4309
                auto cap_index = pat.second->p_pcre.pp_value->name_index(
1,050,678✔
4310
                    vd->vd_meta.lvm_name.get());
525,339✔
4311
                if (cap_index >= 0) {
525,339✔
4312
                    found_in_pattern = true;
311,300✔
4313
                    break;
311,300✔
4314
                }
4315

4316
                for (auto named_cap :
214,039✔
4317
                     pat.second->p_pcre.pp_value->get_named_captures())
1,953,369✔
4318
                {
4319
                    available_captures.insert(named_cap.get_name().to_string());
1,525,291✔
4320
                }
4321
            }
4322
            if (!found_in_pattern) {
311,302✔
4323
                auto notes
4324
                    = attr_line_t("the following captures are available:\n  ")
2✔
4325
                          .join(available_captures,
2✔
4326
                                VC_ROLE.value(role_t::VCR_SYMBOL),
4✔
4327
                                ", ")
4328
                          .move();
2✔
4329
                errors.emplace_back(
2✔
4330
                    lnav::console::user_message::warning(
×
4331
                        attr_line_t("invalid value ")
2✔
4332
                            .append_quoted(lnav::roles::symbol(
4✔
4333
                                fmt::format(FMT_STRING("/{}/value/{}"),
8✔
4334
                                            this->elf_name,
2✔
4335
                                            vd->vd_meta.lvm_name.get()))))
4✔
4336
                        .with_reason(
4✔
4337
                            attr_line_t("no patterns have a capture named ")
4✔
4338
                                .append_quoted(vd->vd_meta.lvm_name.get()))
2✔
4339
                        .with_note(notes)
2✔
4340
                        .with_snippets(this->get_snippets())
4✔
4341
                        .with_help("values are populated from captures in "
4342
                                   "patterns, so at least one pattern must "
4343
                                   "have a capture with this value name"));
4344
            }
2✔
4345
        }
311,302✔
4346

4347
        for (act_iter = vd->vd_action_list.begin();
470,433✔
4348
             act_iter != vd->vd_action_list.end();
471,184✔
4349
             ++act_iter)
751✔
4350
        {
4351
            if (this->lf_action_defs.find(*act_iter)
751✔
4352
                == this->lf_action_defs.end())
1,502✔
4353
            {
4354
#if 0
4355
                errors.push_back("error:" + this->elf_name.to_string() + ":"
4356
                                 + vd->vd_meta.lvm_name.get()
4357
                                 + ": cannot find action -- " + (*act_iter));
4358
#endif
4359
            }
4360
        }
4361

4362
        vd->set_rewrite_src_name();
470,433✔
4363
    }
4364

4365
    if (this->elf_type == elf_type_t::ELF_TYPE_JSON) {
52,885✔
4366
        for (const auto& vd : this->elf_value_def_order) {
169,472✔
4367
            this->elf_value_def_frag_map[vd->vd_meta.lvm_name
159,131✔
4368
                                             .to_string_fragment()]
159,131✔
4369
                = vd.get();
318,262✔
4370
        }
4371
    }
4372

4373
    for (const auto& td_pair : this->lf_tag_defs) {
55,237✔
4374
        const auto& td = td_pair.second;
2,352✔
4375

4376
        if (td->ftd_pattern.pp_value == nullptr
2,352✔
4377
            || td->ftd_pattern.pp_value->get_pattern().empty())
2,352✔
4378
        {
4379
            errors.emplace_back(
3✔
4380
                lnav::console::user_message::error(
×
4381
                    attr_line_t("invalid tag definition ")
6✔
4382
                        .append_quoted(lnav::roles::symbol(
6✔
4383
                            fmt::format(FMT_STRING("/{}/tags/{}"),
12✔
4384
                                        this->elf_name,
3✔
4385
                                        td_pair.first))))
3✔
4386
                    .with_reason(
6✔
4387
                        "tag definitions must have a non-empty pattern")
4388
                    .with_snippets(this->get_snippets()));
6✔
4389
        }
4390
    }
4391

4392
    if (this->elf_opid_field.empty()
52,885✔
4393
        && this->lf_opid_description_def->size() > 1)
52,885✔
4394
    {
4395
        errors.emplace_back(
1✔
4396
            lnav::console::user_message::error(
×
4397
                attr_line_t("too many opid descriptions")
2✔
4398
                    .append_quoted(lnav::roles::symbol(fmt::format(
2✔
4399
                        FMT_STRING("/{}/opid/description"), this->elf_name))))
4✔
4400
                .with_reason(attr_line_t("when no ")
2✔
4401
                                 .append("opid-field"_symbol)
1✔
4402
                                 .append(" is specified, only a single "
1✔
4403
                                         "description is supported"))
4404
                .with_snippets(this->get_snippets()));
2✔
4405
    }
4406

4407
    for (const auto& opid_desc_pair : *this->lf_opid_description_def) {
75,417✔
4408
        for (const auto& opid_desc : *opid_desc_pair.second.od_descriptors) {
53,325✔
4409
            auto iter = this->elf_value_defs.find(opid_desc.od_field.pp_value);
30,793✔
4410
            if (iter == this->elf_value_defs.end()) {
30,793✔
4411
                errors.emplace_back(
2✔
4412
                    lnav::console::user_message::error(
×
4413
                        attr_line_t("invalid opid description field ")
4✔
4414
                            .append_quoted(lnav::roles::symbol(
4✔
4415
                                opid_desc.od_field.pp_path.to_string())))
4✔
4416
                        .with_reason(
4✔
4417
                            attr_line_t("unknown value name ")
4✔
4418
                                .append_quoted(opid_desc.od_field.pp_value))
2✔
4419
                        .with_snippets(this->get_snippets()));
4✔
4420
            } else {
4421
                this->lf_desc_fields.insert(iter->first);
30,791✔
4422
                iter->second->vd_is_desc_field = true;
30,791✔
4423
            }
4424
        }
4425
    }
4426

4427
    for (const auto& subid_desc_pair : *this->lf_subid_description_def) {
53,636✔
4428
        for (const auto& subid_desc : *subid_desc_pair.second.od_descriptors) {
1,502✔
4429
            auto iter = this->elf_value_defs.find(subid_desc.od_field.pp_value);
751✔
4430
            if (iter == this->elf_value_defs.end()) {
751✔
4431
                errors.emplace_back(
×
4432
                    lnav::console::user_message::error(
×
4433
                        attr_line_t("invalid subid description field ")
×
4434
                            .append_quoted(lnav::roles::symbol(
×
4435
                                subid_desc.od_field.pp_path.to_string())))
×
4436
                        .with_reason(
×
4437
                            attr_line_t("unknown value name ")
×
4438
                                .append_quoted(subid_desc.od_field.pp_value))
×
4439
                        .with_snippets(this->get_snippets()));
×
4440
            } else {
4441
                this->lf_desc_fields.insert(iter->first);
751✔
4442
                iter->second->vd_is_desc_field = true;
751✔
4443
            }
4444
        }
4445
    }
4446

4447
    if (this->elf_type == elf_type_t::ELF_TYPE_TEXT
105,770✔
4448
        && this->elf_samples.empty())
52,885✔
4449
    {
4450
        errors.emplace_back(
3✔
4451
            lnav::console::user_message::error(
×
4452
                attr_line_t()
6✔
4453
                    .append_quoted(
3✔
4454
                        lnav::roles::symbol(this->elf_name.to_string()))
6✔
4455
                    .append(" is not a valid log format"))
3✔
4456
                .with_reason("log message samples must be included in a format "
6✔
4457
                             "definition")
4458
                .with_snippets(this->get_snippets()));
6✔
4459
    }
4460

4461
    for (const auto& pat : this->elf_pattern_order) {
150,351✔
4462
        if (this->elf_type != elf_type_t::ELF_TYPE_TEXT) {
97,466✔
4463
            continue;
1✔
4464
        }
4465
        if (pat->p_pcre.pp_value->name_index(this->lf_timestamp_field.get())
97,465✔
4466
            < 0)
97,465✔
4467
        {
4468
            attr_line_t notes;
1✔
4469
            bool first_note = true;
1✔
4470

4471
            if (pat->p_pcre.pp_value->get_capture_count() > 0) {
1✔
4472
                notes.append("the following captures are available:\n  ");
1✔
4473
            }
4474
            for (auto named_cap : pat->p_pcre.pp_value->get_named_captures()) {
4✔
4475
                if (!first_note) {
3✔
4476
                    notes.append(", ");
2✔
4477
                }
4478
                notes.append(
3✔
4479
                    lnav::roles::symbol(named_cap.get_name().to_string()));
6✔
4480
                first_note = false;
3✔
4481
            }
4482
            errors.emplace_back(
1✔
4483
                lnav::console::user_message::error(
×
4484
                    attr_line_t("invalid value for property ")
1✔
4485
                        .append_quoted(lnav::roles::symbol(
2✔
4486
                            fmt::format(FMT_STRING("/{}/timestamp-field"),
4✔
4487
                                        this->elf_name))))
1✔
4488
                    .with_reason(
2✔
4489
                        attr_line_t()
2✔
4490
                            .append_quoted(this->lf_timestamp_field)
1✔
4491
                            .append(" was not found in the pattern at ")
1✔
4492
                            .append(lnav::roles::symbol(pat->p_config_path)))
2✔
4493
                    .with_note(notes)
1✔
4494
                    .with_snippets(this->get_snippets()));
2✔
4495
        }
1✔
4496
    }
4497

4498
    for (size_t sample_index = 0; sample_index < this->elf_samples.size();
239,160✔
4499
         sample_index += 1)
186,275✔
4500
    {
4501
        auto& elf_sample = this->elf_samples[sample_index];
186,275✔
4502
        auto sample_lines
4503
            = string_fragment(elf_sample.s_line.pp_value).split_lines();
186,275✔
4504

4505
        if (this->test_line(elf_sample, errors).is<scan_match>()) {
186,275✔
4506
            for (const auto& pat_name : elf_sample.s_matched_regexes) {
369,544✔
4507
                this->elf_patterns[pat_name]->p_matched_samples.emplace(
183,270✔
4508
                    sample_index);
4509
            }
4510
        }
4511
    }
186,275✔
4512

4513
    if (!this->elf_samples.empty()) {
52,885✔
4514
        for (const auto& elf_sample : this->elf_samples) {
230,318✔
4515
            if (elf_sample.s_matched_regexes.size() <= 1) {
186,275✔
4516
                continue;
186,275✔
4517
            }
4518

4519
            errors.emplace_back(
×
4520
                lnav::console::user_message::warning(
×
4521
                    attr_line_t("invalid log format: ")
×
4522
                        .append_quoted(
×
4523
                            lnav::roles::symbol(this->elf_name.to_string())))
×
4524
                    .with_reason(
×
4525
                        attr_line_t(
×
4526
                            "sample is matched by more than one regex: ")
4527
                            .join(elf_sample.s_matched_regexes,
×
4528
                                  VC_ROLE.value(role_t::VCR_SYMBOL),
×
4529
                                  ", "))
4530
                    .with_snippet(lnav::console::snippet::from(
×
4531
                        elf_sample.s_line.pp_location,
4532
                        attr_line_t().append(lnav::roles::quoted_code(
×
4533
                            elf_sample.s_line.pp_value))))
×
4534
                    .with_help("log format regexes must match a single type "
4535
                               "of log message"));
4536
        }
4537

4538
        for (const auto& pat : this->elf_pattern_order) {
141,506✔
4539
            if (pat->p_matched_samples.empty()) {
97,463✔
4540
                errors.emplace_back(
2✔
4541
                    lnav::console::user_message::warning(
×
4542
                        attr_line_t("invalid pattern: ")
4✔
4543
                            .append_quoted(
2✔
4544
                                lnav::roles::symbol(pat->p_config_path)))
4✔
4545
                        .with_reason("pattern does not match any samples")
4✔
4546
                        .with_snippet(lnav::console::snippet::from(
6✔
4547
                            pat->p_pcre.pp_location, ""))
2✔
4548
                        .with_help(
4549
                            "every pattern should have at least one sample "
4550
                            "that it matches"));
4551
            }
4552
        }
4553
    }
4554

4555
    size_t value_def_index = 0;
52,885✔
4556
    for (auto& elf_value_def : this->elf_value_def_order) {
523,318✔
4557
        elf_value_def->vd_meta.lvm_values_index
470,433✔
4558
            = std::make_optional(value_def_index++);
470,433✔
4559

4560
        if (elf_value_def->vd_meta.lvm_foreign_key
470,433✔
4561
            || elf_value_def->vd_meta.lvm_identifier)
470,433✔
4562
        {
4563
            continue;
252,083✔
4564
        }
4565

4566
        switch (elf_value_def->vd_meta.lvm_kind) {
218,350✔
4567
            case value_kind_t::VALUE_INTEGER:
54,456✔
4568
            case value_kind_t::VALUE_FLOAT:
4569
                this->elf_numeric_value_defs.push_back(elf_value_def);
54,456✔
4570
                break;
54,456✔
4571
            default:
163,894✔
4572
                break;
163,894✔
4573
        }
4574
    }
4575

4576
    int format_index = 0;
52,885✔
4577
    for (auto iter = this->jlf_line_format.begin();
52,885✔
4578
         iter != this->jlf_line_format.end();
167,317✔
4579
         ++iter, format_index++)
114,432✔
4580
    {
4581
        static const intern_string_t ts
4582
            = intern_string::lookup("__timestamp__");
115,934✔
4583
        static const intern_string_t level_field
4584
            = intern_string::lookup("__level__");
115,934✔
4585
        auto& jfe = *iter;
114,432✔
4586

4587
        if (startswith(jfe.jfe_value.pp_value.get(), "/")) {
114,432✔
4588
            jfe.jfe_value.pp_value
4589
                = intern_string::lookup(jfe.jfe_value.pp_value.get() + 1);
192✔
4590
        }
4591
        if (!jfe.jfe_ts_format.empty()) {
114,432✔
4592
            if (!jfe.jfe_value.pp_value.empty() && jfe.jfe_value.pp_value != ts)
847✔
4593
            {
4594
                log_warning(
96✔
4595
                    "%s:line-format[%d]:ignoring field '%s' since "
4596
                    "timestamp-format was used",
4597
                    this->elf_name.get(),
4598
                    format_index,
4599
                    jfe.jfe_value.pp_value.get());
4600
            }
4601
            jfe.jfe_value.pp_value = ts;
847✔
4602
        }
4603

4604
        switch (jfe.jfe_type) {
114,432✔
4605
            case json_log_field::VARIABLE: {
75,905✔
4606
                auto vd_iter
4607
                    = this->elf_value_defs.find(jfe.jfe_value.pp_value);
75,905✔
4608
                if (jfe.jfe_value.pp_value == ts) {
75,905✔
4609
                    this->elf_value_defs[this->lf_timestamp_field]
8,550✔
4610
                        ->vd_meta.lvm_hidden
8,550✔
4611
                        = true;
8,550✔
4612
                } else if (jfe.jfe_value.pp_value == level_field) {
67,355✔
4613
                    this->elf_value_defs[this->elf_level_field]
3,755✔
4614
                        ->vd_meta.lvm_hidden
3,755✔
4615
                        = true;
3,755✔
4616
                } else if (vd_iter == this->elf_value_defs.end()) {
63,600✔
4617
                    errors.emplace_back(
2✔
4618
                        lnav::console::user_message::error(
×
4619
                            attr_line_t("invalid line format element ")
4✔
4620
                                .append_quoted(lnav::roles::symbol(fmt::format(
4✔
4621
                                    FMT_STRING("/{}/line-format/{}/field"),
6✔
4622
                                    this->elf_name,
2✔
4623
                                    format_index))))
4624
                            .with_reason(
4✔
4625
                                attr_line_t()
4✔
4626
                                    .append_quoted(jfe.jfe_value.pp_value)
2✔
4627
                                    .append(" is not a defined value"))
2✔
4628
                            .with_snippet(jfe.jfe_value.to_snippet()));
4✔
4629
                } else {
4630
                    vd_iter->second->vd_line_format_index = format_index;
63,598✔
4631
                    switch (vd_iter->second->vd_meta.lvm_kind) {
63,598✔
4632
                        case value_kind_t::VALUE_INTEGER:
10,706✔
4633
                        case value_kind_t::VALUE_FLOAT:
4634
                            if (jfe.jfe_align
10,706✔
4635
                                == json_format_element::align_t::NONE)
4636
                            {
4637
                                jfe.jfe_align
4638
                                    = json_format_element::align_t::RIGHT;
9,955✔
4639
                            }
4640
                            break;
10,706✔
4641
                        default:
52,892✔
4642
                            break;
52,892✔
4643
                    }
4644
                }
4645
                break;
75,905✔
4646
            }
4647
            case json_log_field::CONSTANT:
38,527✔
4648
                this->jlf_line_format_init_count
38,527✔
4649
                    += std::count(jfe.jfe_default_value.begin(),
38,527✔
4650
                                  jfe.jfe_default_value.end(),
4651
                                  '\n');
38,527✔
4652
                break;
38,527✔
4653
            default:
×
4654
                break;
×
4655
        }
4656
    }
4657

4658
    for (auto& hd_pair : this->elf_highlighter_patterns) {
53,271✔
4659
        auto& hd = hd_pair.second;
386✔
4660
        text_attrs attrs;
386✔
4661

4662
        if (!hd.hd_color.pp_value.empty()) {
386✔
4663
            attrs.ta_fg_color = vc.match_color(
385✔
4664
                styling::color_unit::from_str(hd.hd_color.pp_value)
770✔
4665
                    .unwrapOrElse([&](const auto& msg) {
1✔
4666
                        errors.emplace_back(
1✔
4667
                            lnav::console::user_message::error(
4668
                                attr_line_t()
1✔
4669
                                    .append_quoted(hd.hd_color.pp_value)
2✔
4670
                                    .append(" is not a valid color value for "
1✔
4671
                                            "property ")
4672
                                    .append_quoted(lnav::roles::symbol(
2✔
4673
                                        hd.hd_color.pp_path.to_string())))
1✔
4674
                                .with_reason(msg)
2✔
4675
                                .with_snippet(hd.hd_color.to_snippet()));
1✔
4676
                        return styling::color_unit::EMPTY;
1✔
4677
                    }));
4678
        }
4679

4680
        if (!hd.hd_background_color.pp_value.empty()) {
386✔
4681
            attrs.ta_bg_color = vc.match_color(
1✔
4682
                styling::color_unit::from_str(hd.hd_background_color.pp_value)
2✔
4683
                    .unwrapOrElse([&](const auto& msg) {
1✔
4684
                        errors.emplace_back(
1✔
4685
                            lnav::console::user_message::error(
4686
                                attr_line_t()
1✔
4687
                                    .append_quoted(
2✔
4688
                                        hd.hd_background_color.pp_value)
1✔
4689
                                    .append(" is not a valid color value for "
1✔
4690
                                            "property ")
4691
                                    .append_quoted(lnav::roles::symbol(
2✔
4692
                                        hd.hd_background_color.pp_path
1✔
4693
                                            .to_string())))
4694
                                .with_reason(msg)
2✔
4695
                                .with_snippet(
4696
                                    hd.hd_background_color.to_snippet()));
1✔
4697
                        return styling::color_unit::EMPTY;
1✔
4698
                    }));
4699
        }
4700

4701
        if (hd.hd_underline) {
386✔
4702
            attrs |= text_attrs::style::underline;
96✔
4703
        }
4704
        if (hd.hd_blink) {
386✔
4705
            attrs |= text_attrs::style::blink;
×
4706
        }
4707

4708
        if (hd.hd_pattern.pp_value != nullptr) {
386✔
4709
            this->lf_highlighters.emplace_back(hd.hd_pattern.pp_value);
384✔
4710
            this->lf_highlighters.back()
384✔
4711
                .with_name(hd_pair.first.to_string())
768✔
4712
                .with_format_name(this->elf_name)
384✔
4713
                .with_attrs(attrs)
384✔
4714
                .with_nestable(hd.hd_nestable);
384✔
4715
        }
4716
    }
4717
}
4718

4719
void
4720
external_log_format::register_vtabs(
44,513✔
4721
    log_vtab_manager* vtab_manager,
4722
    std::vector<lnav::console::user_message>& errors)
4723
{
4724
    for (auto& elf_search_table : this->elf_search_tables) {
53,432✔
4725
        if (elf_search_table.second.std_pattern.pp_value == nullptr) {
8,919✔
4726
            continue;
1✔
4727
        }
4728

4729
        auto lst = std::make_shared<log_search_table>(
4730
            elf_search_table.second.std_pattern.pp_value,
8,918✔
4731
            elf_search_table.first);
8,918✔
4732
        lst->lst_format = this;
8,918✔
4733
        lst->lst_log_path_glob = elf_search_table.second.std_glob;
8,918✔
4734
        if (elf_search_table.second.std_level != LEVEL_UNKNOWN) {
8,918✔
4735
            lst->lst_log_level = elf_search_table.second.std_level;
3,185✔
4736
        }
4737
        auto errmsg = vtab_manager->register_vtab(lst);
8,918✔
4738
        if (!errmsg.empty()) {
8,918✔
4739
#if 0
4740
            errors.push_back("error:" + this->elf_name.to_string() + ":"
4741
                             + search_iter->first.to_string()
4742
                             + ":unable to register table -- " + errmsg);
4743
#endif
4744
        }
4745
    }
8,918✔
4746
}
44,513✔
4747

4748
bool
4749
external_log_format::match_samples(const std::vector<sample_t>& samples) const
3,681,418✔
4750
{
4751
    for (const auto& sample_iter : samples) {
16,490,456✔
4752
        for (const auto& pat_iter : this->elf_pattern_order) {
34,982,908✔
4753
            auto& pat = *pat_iter;
22,173,870✔
4754

4755
            if (!pat.p_pcre.pp_value) {
22,173,870✔
4756
                continue;
×
4757
            }
4758

4759
            if (pat.p_pcre.pp_value
44,347,740✔
4760
                    ->find_in(sample_iter.s_line.pp_value, PCRE2_NO_UTF_CHECK)
44,347,740✔
4761
                    .ignore_error())
44,347,740✔
4762
            {
4763
                return true;
15,633✔
4764
            }
4765
        }
4766
    }
4767

4768
    return false;
3,665,785✔
4769
}
4770

4771
class external_log_table : public log_format_vtab_impl {
4772
public:
4773
    explicit external_log_table(std::shared_ptr<const log_format> elf)
44,513✔
4774
        : log_format_vtab_impl(elf),
44,513✔
4775
          elt_format(dynamic_cast<const external_log_format*>(elf.get()))
44,513✔
4776
    {
4777
    }
44,513✔
4778

4779
    void get_columns(std::vector<vtab_column>& cols) const override
44,906✔
4780
    {
4781
        const auto& elf = this->elt_format;
44,906✔
4782

4783
        cols.resize(elf->elf_column_count);
44,906✔
4784
        for (const auto& vd : elf->elf_value_def_order) {
446,308✔
4785
            auto type_pair = logline_value_to_sqlite_type(vd->vd_meta.lvm_kind);
401,402✔
4786

4787
            if (!vd->vd_meta.lvm_column.is<logline_value_meta::table_column>())
401,402✔
4788
            {
4789
                continue;
41,416✔
4790
            }
4791

4792
            auto col
4793
                = vd->vd_meta.lvm_column.get<logline_value_meta::table_column>()
359,986✔
4794
                      .value;
359,986✔
4795
            require(0 <= col && col < elf->elf_column_count);
359,986✔
4796

4797
            cols[col].vc_name = vd->vd_meta.lvm_name.get();
359,986✔
4798
            cols[col].vc_type = type_pair.first;
359,986✔
4799
            cols[col].vc_subtype = type_pair.second;
359,986✔
4800
            cols[col].vc_collator = vd->vd_collate;
359,986✔
4801
            cols[col].vc_comment = vd->vd_description;
359,986✔
4802
        }
4803
    }
44,906✔
4804

4805
    void get_foreign_keys(
36,060✔
4806
        std::unordered_set<std::string>& keys_inout) const override
4807
    {
4808
        log_vtab_impl::get_foreign_keys(keys_inout);
36,060✔
4809

4810
        for (const auto& elf_value_def : this->elt_format->elf_value_defs) {
520,482✔
4811
            if (elf_value_def.second->vd_meta.lvm_foreign_key
484,422✔
4812
                || elf_value_def.second->vd_meta.lvm_identifier)
484,422✔
4813
            {
4814
                keys_inout.emplace(elf_value_def.first.to_string());
178,654✔
4815
            }
4816
        }
4817
    }
36,060✔
4818

4819
    bool next(log_cursor& lc, logfile_sub_source& lss) override
3,476✔
4820
    {
4821
        if (lc.is_eof()) {
3,476✔
4822
            return true;
×
4823
        }
4824

4825
        content_line_t cl(lss.at(lc.lc_curr_line));
3,476✔
4826
        auto* lf = lss.find_file_ptr(cl);
3,476✔
4827
        auto lf_iter = lf->begin() + cl;
3,476✔
4828
        if (lf_iter->is_continued()) {
3,476✔
4829
            return false;
×
4830
        }
4831

4832
        if (lf->get_format_name() == this->lfvi_format->get_name()) {
3,476✔
4833
            return true;
3,470✔
4834
        }
4835

4836
        return false;
6✔
4837
    }
4838

4839
    void extract(logfile* lf,
3,385✔
4840
                 uint64_t line_number,
4841
                 string_attrs_t& sa,
4842
                 logline_value_vector& values) override
4843
    {
4844
        auto format = lf->get_format();
3,385✔
4845

4846
        sa.clear();
3,385✔
4847
        format->annotate(lf, line_number, sa, values);
3,385✔
4848
    }
3,385✔
4849

4850
    const external_log_format* elt_format;
4851
    line_range elt_container_body;
4852
};
4853

4854
std::shared_ptr<log_vtab_impl>
4855
external_log_format::get_vtab_impl() const
44,513✔
4856
{
4857
    return std::make_shared<external_log_table>(this->shared_from_this());
44,513✔
4858
}
4859

4860
std::shared_ptr<log_format>
4861
external_log_format::specialized(int fmt_lock)
510✔
4862
{
4863
    auto retval = std::make_shared<external_log_format>(*this);
510✔
4864

4865
    retval->lf_specialized = true;
510✔
4866
    if (this->elf_type == elf_type_t::ELF_TYPE_JSON) {
510✔
4867
        this->jlf_parse_context
4868
            = std::make_shared<yajlpp_parse_context>(this->elf_name);
47✔
4869
        this->jlf_yajl_handle.reset(
47✔
4870
            yajl_alloc(&this->jlf_parse_context->ypc_callbacks,
47✔
4871
                       nullptr,
4872
                       this->jlf_parse_context.get()),
47✔
4873
            yajl_handle_deleter());
4874
        yajl_config(this->jlf_yajl_handle.get(), yajl_dont_validate_strings, 1);
47✔
4875
        this->jlf_cached_line.reserve(16 * 1024);
47✔
4876
    }
4877

4878
    this->elf_specialized_value_defs_state = *this->elf_value_defs_state;
510✔
4879

4880
    return retval;
1,020✔
4881
}
510✔
4882

4883
log_format::match_name_result
4884
external_log_format::match_name(const std::string& filename)
846,294✔
4885
{
4886
    if (this->elf_filename_pcre.pp_value == nullptr) {
846,294✔
4887
        return name_matched{};
844,908✔
4888
    }
4889

4890
    if (this->elf_filename_pcre.pp_value->find_in(filename)
2,772✔
4891
            .ignore_error()
2,772✔
4892
            .has_value())
1,386✔
4893
    {
4894
        return name_matched{};
238✔
4895
    }
4896

4897
    return name_mismatched{
2,296✔
4898
        this->elf_filename_pcre.pp_value->match_partial(filename),
2,296✔
4899
        this->elf_filename_pcre.pp_value->get_pattern(),
1,148✔
4900
    };
1,148✔
4901
}
4902

4903
auto
4904
external_log_format::value_line_count(scan_batch_context& sbc,
245,909✔
4905
                                      const value_def* vd,
4906
                                      bool top_level,
4907
                                      std::optional<double> val,
4908
                                      const unsigned char* str,
4909
                                      ssize_t len,
4910
                                      yajl_string_props_t* props)
4911
    -> value_line_count_result
4912
{
4913
    value_line_count_result retval;
245,909✔
4914

4915
    if (vd == nullptr) {
245,909✔
4916
        if (this->jlf_hide_extra || !top_level) {
224,348✔
4917
            retval.vlcr_count = 0;
210,519✔
4918
        }
4919

4920
        return retval;
224,348✔
4921
    }
4922

4923
    if (str != nullptr && props != nullptr && !val) {
21,561✔
4924
        auto frag = string_fragment::from_bytes(str, len);
19,153✔
4925
        while (frag.endswith("\n")) {
19,322✔
4926
            frag.pop_back();
169✔
4927
            props->line_feeds -= 1;
169✔
4928
        }
4929
        retval.vlcr_has_ansi |= props->has_ansi;
19,153✔
4930
        retval.vlcr_count += props->line_feeds;
19,153✔
4931
    }
4932

4933
    if (vd->vd_meta.lvm_values_index) {
21,561✔
4934
        auto& lvs = sbc.sbc_value_stats[vd->vd_meta.lvm_values_index.value()];
6,541✔
4935
        if (len > lvs.lvs_width) {
6,541✔
4936
            lvs.lvs_width = len;
3,431✔
4937
        }
4938
        if (val) {
6,541✔
4939
            lvs.add_value(val.value());
168✔
4940
        }
4941
    }
4942

4943
    if (vd->vd_line_format_index) {
21,561✔
4944
        retval.vlcr_line_format_count += 1;
3,207✔
4945
        retval.vlcr_count -= 1;
3,207✔
4946
        retval.vlcr_line_format_index = vd->vd_line_format_index;
3,207✔
4947
    }
4948
    if (vd->vd_meta.is_hidden()) {
21,561✔
4949
        retval.vlcr_count = 0;
3,090✔
4950
        return retval;
3,090✔
4951
    }
4952

4953
    return retval;
18,471✔
4954
}
4955

4956
log_level_t
4957
external_log_format::convert_level(string_fragment sf,
193,565✔
4958
                                   scan_batch_context* sbc) const
4959
{
4960
    auto retval = LEVEL_INFO;
193,565✔
4961

4962
    if (sf.is_valid()) {
193,565✔
4963
        if (sbc != nullptr) {
151,845✔
4964
            auto ssm_res = sbc->sbc_level_cache.lookup(sf);
10,076✔
4965
            if (ssm_res.has_value()) {
10,076✔
4966
                return static_cast<log_level_t>(ssm_res.value());
4,107✔
4967
            }
4968
        }
4969

4970
        if (this->elf_level_patterns.empty()) {
147,738✔
4971
            retval = string2level(sf.data(), sf.length());
32,683✔
4972
        } else {
4973
            for (const auto& elf_level_pattern : this->elf_level_patterns) {
310,390✔
4974
                if (elf_level_pattern.second.lp_pcre.pp_value
568,498✔
4975
                        ->find_in(sf, PCRE2_NO_UTF_CHECK)
568,498✔
4976
                        .ignore_error()
568,498✔
4977
                        .has_value())
284,249✔
4978
                {
4979
                    retval = elf_level_pattern.first;
88,914✔
4980
                    break;
88,914✔
4981
                }
4982
            }
4983
        }
4984

4985
        if (sbc != nullptr
147,738✔
4986
            && sf.length() <= lnav::small_string_map::MAX_KEY_SIZE)
147,738✔
4987
        {
4988
            sbc->sbc_level_cache.insert(sf, retval);
5,313✔
4989
        }
4990
    }
4991

4992
    return retval;
189,458✔
4993
}
4994

4995
logline_value_meta
4996
external_log_format::get_value_meta(intern_string_t field_name,
15,019✔
4997
                                    value_kind_t kind)
4998
{
4999
    const auto iter = this->elf_value_defs.find(field_name);
15,019✔
5000
    if (iter == this->elf_value_defs.end()) {
15,019✔
5001
        auto retval = logline_value_meta(
5002
            field_name, kind, logline_value_meta::external_column{}, this);
969✔
5003

5004
        retval.lvm_hidden = this->jlf_hide_extra;
969✔
5005
        return retval;
969✔
5006
    }
969✔
5007

5008
    auto lvm = iter->second->vd_meta;
14,050✔
5009

5010
    lvm.lvm_kind = kind;
14,050✔
5011
    return lvm;
14,050✔
5012
}
14,050✔
5013

5014
logline_value_meta
5015
external_log_format::get_value_meta(yajlpp_parse_context* ypc,
2,326✔
5016
                                    const value_def* vd,
5017
                                    value_kind_t kind)
5018
{
5019
    if (vd == nullptr) {
2,326✔
5020
        auto retval = logline_value_meta(
5021
            ypc->get_path(), kind, logline_value_meta::external_column{}, this);
57✔
5022

5023
        retval.lvm_hidden = this->jlf_hide_extra;
57✔
5024
        return retval;
57✔
5025
    }
57✔
5026

5027
    auto lvm = vd->vd_meta;
2,269✔
5028

5029
    lvm.lvm_kind = kind;
2,269✔
5030
    return lvm;
2,269✔
5031
}
2,269✔
5032

5033
void
5034
external_log_format::json_append(const log_format_file_state& lffs,
8,753✔
5035
                                 const json_format_element& jfe,
5036
                                 const value_def* vd,
5037
                                 const string_fragment& sf)
5038
{
5039
    if (jfe.jfe_align == json_format_element::align_t::RIGHT) {
8,753✔
5040
        auto sf_width = sf.column_width();
318✔
5041
        if (sf_width < jfe.jfe_min_width) {
318✔
5042
            this->json_append_to_cache(jfe.jfe_min_width - sf_width);
×
5043
        } else if (jfe.jfe_auto_width && vd != nullptr
24✔
5044
                   && sf_width
342✔
5045
                       < lffs.lffs_value_stats[vd->vd_meta.lvm_values_index
24✔
5046
                                                   .value()]
24✔
5047
                             .lvs_width)
24✔
5048
        {
5049
            this->json_append_to_cache(
8✔
5050
                lffs.lffs_value_stats[vd->vd_meta.lvm_values_index.value()]
8✔
5051
                    .lvs_width
8✔
5052
                - sf_width);
8✔
5053
        }
5054
    }
5055
    this->json_append_to_cache(sf.data(), sf.length());
8,753✔
5056
    if ((jfe.jfe_align == json_format_element::align_t::LEFT
8,753✔
5057
         || jfe.jfe_align == json_format_element::align_t::NONE)
8,699✔
5058
        && (jfe.jfe_min_width > 0 || jfe.jfe_auto_width))
8,435✔
5059
    {
5060
        auto sf_width = sf.column_width();
1,216✔
5061
        if (sf_width < jfe.jfe_min_width) {
1,216✔
5062
            this->json_append_to_cache(jfe.jfe_min_width - sf_width);
358✔
5063
        } else if (jfe.jfe_auto_width && vd != nullptr
732✔
5064
                   && sf_width
1,590✔
5065
                       < lffs.lffs_value_stats[vd->vd_meta.lvm_values_index
643✔
5066
                                                   .value()]
643✔
5067
                             .lvs_width)
643✔
5068
        {
5069
            this->json_append_to_cache(
×
5070
                lffs.lffs_value_stats[vd->vd_meta.lvm_values_index.value()]
×
5071
                    .lvs_width
×
5072
                - sf_width);
×
5073
        }
5074
    }
5075
}
8,753✔
5076

5077
intern_string_t
5078
external_log_format::get_pattern_name(const pattern_locks& pl,
50✔
5079
                                      uint64_t line_number) const
5080
{
5081
    if (this->elf_type != elf_type_t::ELF_TYPE_TEXT) {
50✔
5082
        static auto structured = intern_string::lookup("structured");
5083

5084
        return structured;
×
5085
    }
5086
    auto pat_index = pl.pattern_index_for_line(line_number);
50✔
5087
    return this->elf_pattern_order[pat_index]->p_name;
50✔
5088
}
5089

5090
std::string
5091
log_format::get_pattern_path(const pattern_locks& pl,
×
5092
                             uint64_t line_number) const
5093
{
5094
    auto pat_index = pl.pattern_index_for_line(line_number);
×
5095
    return fmt::format(FMT_STRING("builtin ({})"), pat_index);
×
5096
}
5097

5098
intern_string_t
5099
log_format::get_pattern_name(const pattern_locks& pl,
6✔
5100
                             uint64_t line_number) const
5101
{
5102
    char pat_str[128];
5103

5104
    auto pat_index = pl.pattern_index_for_line(line_number);
6✔
5105
    auto to_n_res = fmt::format_to_n(
×
5106
        pat_str, sizeof(pat_str) - 1, FMT_STRING("builtin ({})"), pat_index);
18✔
5107
    pat_str[to_n_res.size] = '\0';
6✔
5108
    return intern_string::lookup(pat_str);
12✔
5109
}
5110

5111
std::shared_ptr<log_format>
5112
log_format::find_root_format(const char* name)
1,284✔
5113
{
5114
    auto& fmts = get_root_formats();
1,284✔
5115
    for (auto& lf : fmts) {
50,405✔
5116
        if (lf->get_name() == name) {
50,405✔
5117
            return lf;
1,284✔
5118
        }
5119
    }
5120
    return nullptr;
×
5121
}
5122

5123
exttm
5124
log_format::tm_for_display(logfile::iterator ll, string_fragment sf)
959✔
5125
{
5126
    auto adjusted_time = ll->get_timeval();
959✔
5127
    exttm retval;
959✔
5128

5129
    retval.et_nsec = std::chrono::duration_cast<std::chrono::nanoseconds>(
959✔
5130
                         std::chrono::microseconds{adjusted_time.tv_usec})
×
5131
                         .count();
959✔
5132
    if (this->lf_timestamp_flags & ETF_NANOS_SET) {
959✔
5133
        timeval actual_tv;
5134
        exttm tm;
×
5135
        if (this->lf_date_time.scan(sf.data(),
×
5136
                                    sf.length(),
×
5137
                                    this->get_timestamp_formats(),
5138
                                    &tm,
5139
                                    actual_tv,
5140
                                    false))
5141
        {
5142
            adjusted_time.tv_usec = actual_tv.tv_usec;
×
5143
            retval.et_nsec = tm.et_nsec;
×
5144
        }
5145
    }
5146
    gmtime_r(&adjusted_time.tv_sec, &retval.et_tm);
959✔
5147
    retval.et_flags = this->lf_timestamp_flags;
959✔
5148
    if (this->lf_timestamp_flags & ETF_ZONE_SET
959✔
5149
        && this->lf_date_time.dts_zoned_to_local)
869✔
5150
    {
5151
        retval.et_flags &= ~ETF_Z_IS_UTC;
869✔
5152
    }
5153
    retval.et_gmtoff = this->lf_date_time.dts_local_offset_cache;
959✔
5154

5155
    return retval;
1,918✔
5156
}
5157

5158
pattern_for_lines::pattern_for_lines(uint32_t pfl_line, uint32_t pfl_pat_index)
2,911✔
5159
    : pfl_line(pfl_line), pfl_pat_index(pfl_pat_index)
2,911✔
5160
{
5161
}
2,911✔
5162

5163
void
5164
logline_value_stats::merge(const logline_value_stats& other)
15,281✔
5165
{
5166
    if (other.lvs_count == 0) {
15,281✔
5167
        return;
14,134✔
5168
    }
5169

5170
    require(other.lvs_min_value <= other.lvs_max_value);
1,147✔
5171

5172
    if (other.lvs_width > this->lvs_width) {
1,147✔
5173
        this->lvs_width = other.lvs_width;
766✔
5174
    }
5175
    if (other.lvs_min_value < this->lvs_min_value) {
1,147✔
5176
        this->lvs_min_value = other.lvs_min_value;
792✔
5177
    }
5178
    if (other.lvs_max_value > this->lvs_max_value) {
1,147✔
5179
        this->lvs_max_value = other.lvs_max_value;
1,076✔
5180
    }
5181
    this->lvs_count += other.lvs_count;
1,147✔
5182
    this->lvs_total += other.lvs_total;
1,147✔
5183
    this->lvs_tdigest.insert(other.lvs_tdigest);
1,147✔
5184
    ensure(this->lvs_count >= 0);
1,147✔
5185
    ensure(this->lvs_min_value <= this->lvs_max_value);
1,147✔
5186
}
5187

5188
void
5189
logline_value_stats::add_value(double value)
31,436✔
5190
{
5191
    if (value < this->lvs_min_value) {
31,436✔
5192
        this->lvs_min_value = value;
1,669✔
5193
    }
5194
    if (value > this->lvs_max_value) {
31,436✔
5195
        this->lvs_max_value = value;
2,363✔
5196
    }
5197
    this->lvs_count += 1;
31,436✔
5198
    this->lvs_total += value;
31,436✔
5199
    this->lvs_tdigest.insert(value);
31,436✔
5200
}
31,436✔
5201

5202
std::vector<logline_value_meta>
5203
external_log_format::get_value_metadata() const
8,918✔
5204
{
5205
    std::vector<logline_value_meta> retval;
8,918✔
5206

5207
    for (const auto& vd : this->elf_value_def_order) {
93,639✔
5208
        retval.emplace_back(vd->vd_meta);
84,721✔
5209
    }
5210

5211
    return retval;
8,918✔
5212
}
×
5213

5214
std::optional<size_t>
5215
external_log_format::stats_index_for_value(const intern_string_t& name) const
72✔
5216
{
5217
    const auto iter = this->elf_value_defs.find(name);
72✔
5218
    if (iter != this->elf_value_defs.end()
72✔
5219
        && iter->second->vd_meta.lvm_values_index)
72✔
5220
    {
5221
        return iter->second->vd_meta.lvm_values_index.value();
72✔
5222
    }
5223

5224
    return std::nullopt;
×
5225
}
5226

5227
std::string
5228
external_log_format::get_pattern_regex(const pattern_locks& pl,
17✔
5229
                                       uint64_t line_number) const
5230
{
5231
    if (this->elf_type != elf_type_t::ELF_TYPE_TEXT) {
17✔
5232
        return "";
2✔
5233
    }
5234
    auto pat_index = pl.pattern_index_for_line(line_number);
16✔
5235
    return this->elf_pattern_order[pat_index]->p_pcre.pp_value->get_pattern();
16✔
5236
}
5237

5238
bool
5239
external_log_format::hide_field(const intern_string_t field_name, bool val)
12✔
5240
{
5241
    const auto vd_iter = this->elf_value_defs.find(field_name);
12✔
5242
    if (vd_iter == this->elf_value_defs.end()) {
12✔
5243
        log_warning("field to hide not found: %s.%s",
1✔
5244
                    this->elf_name.c_str(),
5245
                    field_name.c_str());
5246
        return false;
1✔
5247
    }
5248

5249
    vd_iter->second->vd_meta.lvm_user_hidden = val;
11✔
5250
    if (this->elf_type == elf_type_t::ELF_TYPE_JSON) {
11✔
5251
        bool found = false;
2✔
5252

5253
        if (!field_name.to_string_fragment().find('#')) {
2✔
5254
            for (const auto& jfe : this->jlf_line_format) {
18✔
5255
                if (jfe.jfe_value.pp_value == field_name) {
17✔
5256
                    log_debug(
×
5257
                        "hide-field not triggering rebuild since it is in "
5258
                        "line-format: %s.%s",
5259
                        this->elf_name.c_str(),
5260
                        field_name.c_str());
5261
                    found = true;
×
5262
                    break;
×
5263
                }
5264
            }
5265
        }
5266
        if (!found) {
2✔
5267
            log_info("format field %s.%s changed, rebuilding",
2✔
5268
                     this->elf_name.get(),
5269
                     field_name.get());
5270
            this->elf_value_defs_state->vds_generation += 1;
2✔
5271
        }
5272
    }
5273
    return true;
11✔
5274
}
5275

5276
bool
5277
external_log_format::format_changed()
2,599✔
5278
{
5279
    if (this->elf_specialized_value_defs_state.vds_generation
5,198✔
5280
        != this->elf_value_defs_state->vds_generation)
2,599✔
5281
    {
5282
        this->elf_specialized_value_defs_state = *this->elf_value_defs_state;
2✔
5283
        this->jlf_cached_offset = -1;
2✔
5284
        return true;
2✔
5285
    }
5286

5287
    return false;
2,597✔
5288
}
5289

5290
bool
5291
format_tag_def::path_restriction::matches(const char* fn) const
5✔
5292
{
5293
    return fnmatch(this->p_glob.c_str(), fn, 0) == 0;
5✔
5294
}
5295

5296
bool
5297
format_partition_def::path_restriction::matches(const char* fn) const
5✔
5298
{
5299
    return fnmatch(this->p_glob.c_str(), fn, 0) == 0;
5✔
5300
}
5301

5302
int
5303
pattern_locks::pattern_index_for_line(uint64_t line_number) const
6,826✔
5304
{
5305
    if (this->pl_lines.empty()) {
6,826✔
5306
        return -1;
×
5307
    }
5308

5309
    auto iter
5310
        = std::lower_bound(this->pl_lines.cbegin(),
6,826✔
5311
                           this->pl_lines.cend(),
5312
                           line_number,
5313
                           [](const pattern_for_lines& pfl, uint32_t line) {
6,885✔
5314
                               return pfl.pfl_line < line;
6,885✔
5315
                           });
5316

5317
    if (iter == this->pl_lines.end() || iter->pfl_line != line_number) {
6,826✔
5318
        --iter;
6,187✔
5319
    }
5320

5321
    return iter->pfl_pat_index;
6,826✔
5322
}
5323

5324
/* XXX */
5325
#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