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

tstack / lnav / 25203625092-3018

01 May 2026 05:26AM UTC coverage: 69.234% (-0.02%) from 69.25%
25203625092-3018

push

github

tstack
[tests] add timeline test with uwsgi log

54421 of 78605 relevant lines covered (69.23%)

566059.35 hits per line

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

84.0
/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/humanize.time.hh"
42
#include "base/intern_string.hh"
43
#include "base/is_utf8.hh"
44
#include "base/itertools.enumerate.hh"
45
#include "base/itertools.hh"
46
#include "base/map_util.hh"
47
#include "base/opt_util.hh"
48
#include "base/snippet_highlighters.hh"
49
#include "base/string_attr_type.hh"
50
#include "base/string_util.hh"
51
#include "base/time_util.hh"
52
#include "bookmarks.hh"
53
#include "command_executor.hh"
54
#include "config.h"
55
#include "fast_float/single_include/fast_float/fast_float.h"
56
#include "fmt/format.h"
57
#include "lnav_util.hh"
58
#include "log_format_ext.hh"
59
#include "log_search_table.hh"
60
#include "log_vtab_impl.hh"
61
#include "logfile_sub_source.hh"
62
#include "ptimec.hh"
63
#include "readline_highlighters.hh"
64
#include "scn/scan.h"
65
#include "sql_util.hh"
66
#include "sqlite-extension-func.hh"
67
#include "sqlitepp.hh"
68
#include "yajlpp/yajlpp.hh"
69
#include "yajlpp/yajlpp_def.hh"
70

71
using namespace lnav::roles::literals;
72
using namespace std::chrono_literals;
73
using std::string_literals::operator""s;
74

75
static auto intern_lifetime = intern_string::get_table_lifetime();
76

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

85
std::vector<std::shared_ptr<external_log_format>>
86
    external_log_format::GRAPH_ORDERED_FORMATS;
87

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

97
static constexpr uint32_t DATE_TIME_SET_FLAGS = ETF_YEAR_SET | ETF_MONTH_SET
98
    | ETF_DAY_SET | ETF_HOUR_SET | ETF_MINUTE_SET | ETF_SECOND_SET;
99

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

107
    return *this;
932✔
108
}
109

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

120
    return *this;
387✔
121
}
122

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

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

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

156
    return *this;
391✔
157
}
158

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

166
thread_id_time_range&
167
thread_id_time_range::operator|=(const thread_id_time_range& rhs)
541✔
168
{
169
    this->titr_range |= rhs.titr_range;
541✔
170
    this->titr_level_stats |= rhs.titr_level_stats;
541✔
171

172
    return *this;
541✔
173
}
174

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

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

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

218
    return retval;
13,474✔
219
}
220

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

250
    return retval;
13,659✔
251
}
252

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

266
        sub_iter = emp_res.first;
53✔
267
    }
268

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

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

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

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

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

315
        return std::nullopt;
344✔
316
    }
317
    return sf.to_string();
2,091✔
318
}
319

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

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

339
    return retval;
1,076✔
340
}
×
341

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

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

384
    return retval;
54✔
385
}
386

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

394
    if (len == -1) {
×
395
        len = strlen(msg);
×
396
    }
397

398
    struct line_range retval = this->lv_origin;
×
399
    const char *last = msg, *msg_end = msg + len;
×
400

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

405
        next += 1;
×
406
        int amount = (next - last);
×
407

408
        retval.lr_start += amount;
×
409
        if (retval.lr_end != -1) {
×
410
            retval.lr_end += amount;
×
411
        }
412

413
        last = next + 1;
×
414
    }
415

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

419
        if (eol == nullptr) {
×
420
            retval.lr_end = len;
×
421
        } else {
422
            retval.lr_end = eol - msg;
×
423
        }
424
    }
425

426
    return retval;
×
427
}
428

429
logline_value::logline_value(logline_value_meta lvm,
643,256✔
430
                             shared_buffer_ref& sbr,
431
                             struct line_range origin)
643,256✔
432
    : lv_meta(std::move(lvm)), lv_origin(origin)
643,256✔
433
{
434
    if (sbr.get_data() == nullptr) {
643,256✔
435
        this->lv_meta.lvm_kind = value_kind_t::VALUE_NULL;
×
436
    }
437

438
    switch (this->lv_meta.lvm_kind) {
643,256✔
439
        case value_kind_t::VALUE_ANY:
329,183✔
440
        case value_kind_t::VALUE_JSON:
441
        case value_kind_t::VALUE_XML:
442
        case value_kind_t::VALUE_STRUCT:
443
        case value_kind_t::VALUE_TEXT:
444
        case value_kind_t::VALUE_QUOTED:
445
        case value_kind_t::VALUE_W3C_QUOTED:
446
        case value_kind_t::VALUE_TIMESTAMP:
447
            require(origin.lr_end != -1);
329,183✔
448
            this->lv_frag = string_fragment::from_byte_range(
329,183✔
449
                sbr.get_data(), origin.lr_start, origin.lr_end);
329,183✔
450
            break;
329,183✔
451

452
        case value_kind_t::VALUE_NULL:
×
453
            break;
×
454

455
        case value_kind_t::VALUE_INTEGER: {
283,773✔
456
            auto scan_res
457
                = scn::scan_value<int64_t>(sbr.to_string_view(origin));
283,773✔
458
            if (scan_res) {
283,773✔
459
                this->lv_value.i = scan_res->value();
283,771✔
460
            } else {
461
                this->lv_value.i = 0;
2✔
462
            }
463
            break;
283,773✔
464
        }
465

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

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

490
        case value_kind_t::VALUE_UNKNOWN:
×
491
        case value_kind_t::VALUE__MAX:
492
            ensure(0);
×
493
            break;
494
    }
495
}
643,256✔
496

497
void
498
logline_value::apply_scaling(const scaling_factor* sf)
45,104✔
499
{
500
    if (sf != nullptr) {
45,104✔
501
        switch (this->lv_meta.lvm_kind) {
×
502
            case value_kind_t::VALUE_INTEGER:
×
503
                sf->scale(this->lv_value.i);
×
504
                break;
×
505
            case value_kind_t::VALUE_FLOAT:
×
506
                sf->scale(this->lv_value.d);
×
507
                break;
×
508
            default:
×
509
                break;
×
510
        }
511
    }
512
}
45,104✔
513

514
std::string
515
logline_value::to_string() const
10,767✔
516
{
517
    char buffer[128];
518

519
    switch (this->lv_meta.lvm_kind) {
10,767✔
520
        case value_kind_t::VALUE_NULL:
77✔
521
            return "null";
154✔
522

523
        case value_kind_t::VALUE_ANY:
9,933✔
524
        case value_kind_t::VALUE_JSON:
525
        case value_kind_t::VALUE_XML:
526
        case value_kind_t::VALUE_STRUCT:
527
        case value_kind_t::VALUE_TEXT:
528
        case value_kind_t::VALUE_TIMESTAMP:
529
            if (this->lv_str) {
9,933✔
530
                return this->lv_str.value();
1,787✔
531
            }
532
            if (this->lv_frag.empty()) {
8,146✔
533
                return this->lv_intern_string.to_string();
111✔
534
            }
535
            return this->lv_frag.to_string();
8,035✔
536

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

554
                        unquoted_len = unquote_func(unquoted_str,
7✔
555
                                                    this->lv_frag.data(),
556
                                                    this->lv_frag.length());
7✔
557
                        return {unquoted_str, unquoted_len};
14✔
558
                    }
7✔
559
                    default:
×
560
                        return this->lv_frag.to_string();
×
561
                }
562
            }
563
            break;
564

565
        case value_kind_t::VALUE_INTEGER:
705✔
566
            snprintf(buffer, sizeof(buffer), "%" PRId64, this->lv_value.i);
705✔
567
            break;
705✔
568

569
        case value_kind_t::VALUE_FLOAT:
39✔
570
            snprintf(buffer, sizeof(buffer), "%lf", this->lv_value.d);
39✔
571
            break;
39✔
572

573
        case value_kind_t::VALUE_BOOLEAN:
6✔
574
            if (this->lv_value.i) {
6✔
575
                return "true";
×
576
            } else {
577
                return "false";
12✔
578
            }
579
            break;
580
        case value_kind_t::VALUE_UNKNOWN:
×
581
        case value_kind_t::VALUE__MAX:
582
            ensure(0);
×
583
            break;
584
    }
585

586
    return {buffer};
1,488✔
587
}
588

589
string_fragment
590
logline_value::to_string_fragment(ArenaAlloc::Alloc<char>& alloc) const
1,093✔
591
{
592
    char buffer[128];
593

594
    switch (this->lv_meta.lvm_kind) {
1,093✔
595
        case value_kind_t::VALUE_NULL:
×
596
            return "null"_frag;
×
597

598
        case value_kind_t::VALUE_ANY:
1,093✔
599
        case value_kind_t::VALUE_JSON:
600
        case value_kind_t::VALUE_XML:
601
        case value_kind_t::VALUE_STRUCT:
602
        case value_kind_t::VALUE_TEXT:
603
        case value_kind_t::VALUE_TIMESTAMP:
604
            if (this->lv_str) {
1,093✔
605
                return string_fragment::from_str(this->lv_str.value())
×
606
                    .to_owned(alloc);
×
607
            }
608
            if (this->lv_frag.empty()) {
1,093✔
609
                return this->lv_intern_string.to_string_fragment().to_owned(
×
610
                    alloc);
×
611
            }
612
            return this->lv_frag.to_owned(alloc);
1,093✔
613

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

631
                        unquoted_len = unquote_func(unquoted_str,
×
632
                                                    this->lv_frag.data(),
633
                                                    this->lv_frag.length());
×
634
                        return string_fragment::from_bytes(unquoted_str,
×
635
                                                           unquoted_len)
636
                            .to_owned(alloc);
×
637
                    }
638
                    default:
×
639
                        return this->lv_frag.to_owned(alloc);
×
640
                }
641
            }
642
            break;
643

644
        case value_kind_t::VALUE_INTEGER:
×
645
            snprintf(buffer, sizeof(buffer), "%" PRId64, this->lv_value.i);
×
646
            break;
×
647

648
        case value_kind_t::VALUE_FLOAT:
×
649
            snprintf(buffer, sizeof(buffer), "%lf", this->lv_value.d);
×
650
            break;
×
651

652
        case value_kind_t::VALUE_BOOLEAN:
×
653
            if (this->lv_value.i) {
×
654
                return "true"_frag;
×
655
            }
656
            return "false"_frag;
×
657
            break;
658
        case value_kind_t::VALUE_UNKNOWN:
×
659
        case value_kind_t::VALUE__MAX:
660
            ensure(0);
×
661
            break;
662
    }
663

664
    return string_fragment::from_c_str(buffer).to_owned(alloc);
×
665
}
666

667
const char*
668
logline_value::text_value() const
68,701✔
669
{
670
    if (this->lv_str) {
68,701✔
671
        return this->lv_str->c_str();
242✔
672
    }
673
    if (this->lv_frag.empty()) {
68,459✔
674
        if (this->lv_intern_string.empty()) {
21✔
675
            return "";
21✔
676
        }
677
        return this->lv_intern_string.get();
×
678
    }
679
    return this->lv_frag.data();
68,438✔
680
}
681

682
size_t
683
logline_value::text_length() const
68,734✔
684
{
685
    if (this->lv_str) {
68,734✔
686
        return this->lv_str->size();
242✔
687
    }
688
    if (this->lv_frag.empty()) {
68,492✔
689
        return this->lv_intern_string.size();
21✔
690
    }
691
    return this->lv_frag.length();
68,471✔
692
}
693

694
string_fragment
695
logline_value::text_value_fragment() const
×
696
{
697
    return string_fragment::from_bytes(this->text_value(), this->text_length());
×
698
}
699

700
void
701
logline_value_vector::shift_origins_by(const line_range& cover, int32_t amount)
2✔
702
{
703
    for (auto& lv : this->lvv_values) {
14✔
704
        if (!lv.lv_origin.is_valid()) {
12✔
705
            continue;
×
706
        }
707
        lv.lv_origin.shift_range(cover, amount);
12✔
708
    }
709
}
2✔
710

711
void
712
logline_value_vector::clear()
42,788✔
713
{
714
    this->lvv_values.clear();
42,788✔
715
    this->lvv_sbr.disown();
42,788✔
716
    this->lvv_time_value = std::nullopt;
42,788✔
717
    this->lvv_time_exttm = std::nullopt;
42,788✔
718
    this->lvv_opid_value = std::nullopt;
42,788✔
719
    this->lvv_opid_provenance = opid_provenance::none;
42,788✔
720
    this->lvv_thread_id_value = std::nullopt;
42,788✔
721
    this->lvv_src_file_value = std::nullopt;
42,788✔
722
    this->lvv_src_line_value = std::nullopt;
42,788✔
723
    this->lvv_duration_value = std::nullopt;
42,788✔
724
}
42,788✔
725

726
logline_value_vector::logline_value_vector(const logline_value_vector& other)
1,036✔
727
    : lvv_sbr(other.lvv_sbr.clone()), lvv_values(other.lvv_values),
1,036✔
728
      lvv_time_value(other.lvv_time_value),
1,036✔
729
      lvv_time_exttm(other.lvv_time_exttm),
1,036✔
730
      lvv_opid_value(other.lvv_opid_value),
1,036✔
731
      lvv_opid_provenance(other.lvv_opid_provenance),
1,036✔
732
      lvv_thread_id_value(
733
          to_owned(other.lvv_thread_id_value, this->lvv_allocator)),
1,036✔
734
      lvv_src_file_value(
735
          to_owned(other.lvv_src_file_value, this->lvv_allocator)),
1,036✔
736
      lvv_src_line_value(
737
          to_owned(other.lvv_src_line_value, this->lvv_allocator)),
1,036✔
738
      lvv_duration_value(other.lvv_duration_value)
1,036✔
739
{
740
}
1,036✔
741

742
logline_value_vector&
743
logline_value_vector::operator=(const logline_value_vector& other)
441✔
744
{
745
    this->lvv_sbr = other.lvv_sbr.clone();
441✔
746
    this->lvv_values = other.lvv_values;
441✔
747
    this->lvv_time_value = other.lvv_time_value;
441✔
748
    this->lvv_time_exttm = other.lvv_time_exttm;
441✔
749
    this->lvv_opid_value = other.lvv_opid_value;
441✔
750
    this->lvv_opid_provenance = other.lvv_opid_provenance;
441✔
751
    this->lvv_thread_id_value
752
        = to_owned(other.lvv_thread_id_value, this->lvv_allocator);
441✔
753
    this->lvv_src_file_value
754
        = to_owned(other.lvv_src_file_value, this->lvv_allocator);
441✔
755
    this->lvv_src_line_value
756
        = to_owned(other.lvv_src_line_value, this->lvv_allocator);
441✔
757
    this->lvv_duration_value = other.lvv_duration_value;
441✔
758

759
    return *this;
441✔
760
}
761

762
std::vector<std::shared_ptr<log_format>> log_format::lf_root_formats;
763

764
date_time_scanner
765
log_format::build_time_scanner() const
241✔
766
{
767
    date_time_scanner retval;
241✔
768

769
    retval.set_base_time(this->lf_date_time.dts_base_time,
241✔
770
                         this->lf_date_time.dts_base_tm.et_tm);
241✔
771
    if (this->lf_date_time.dts_default_zone != nullptr) {
241✔
772
        retval.dts_default_zone = this->lf_date_time.dts_default_zone;
×
773
    }
774
    retval.dts_zoned_to_local = this->lf_date_time.dts_zoned_to_local;
241✔
775

776
    return retval;
241✔
777
}
778

779
std::vector<std::shared_ptr<log_format>>&
780
log_format::get_root_formats()
18,536✔
781
{
782
    return lf_root_formats;
18,536✔
783
}
784

785
void
786
external_log_format::update_op_description(
5,259✔
787
    const std::vector<opid_descriptors*>& desc_defs_vec,
788
    log_op_description& lod,
789
    const pattern* fpat,
790
    const lnav::pcre2pp::match_data& md)
791
{
792
    std::optional<std::string> desc_elem_str;
5,259✔
793
    if (!lod.lod_index) {
5,259✔
794
        for (const auto& desc_defs : desc_defs_vec) {
3,476✔
795
            if (lod.lod_index) {
1,459✔
796
                break;
34✔
797
            }
798
            for (const auto& desc_def : *desc_defs->od_descriptors) {
1,821✔
799
                auto desc_field_index_iter = fpat->p_value_name_to_index.find(
1,551✔
800
                    desc_def.od_field.pp_value);
1,551✔
801

802
                if (desc_field_index_iter == fpat->p_value_name_to_index.end())
1,551✔
803
                {
804
                    continue;
55✔
805
                }
806

807
                auto desc_cap_opt = md[desc_field_index_iter->second];
1,547✔
808
                if (!desc_cap_opt) {
1,547✔
809
                    continue;
51✔
810
                }
811

812
                desc_elem_str = desc_def.matches(desc_cap_opt.value());
1,496✔
813
                if (desc_elem_str) {
1,496✔
814
                    lod.lod_index = desc_defs->od_index;
1,155✔
815
                    break;
1,155✔
816
                }
817
            }
818
        }
819
    }
820
    if (lod.lod_index) {
5,259✔
821
        const auto& desc_def_v
822
            = *desc_defs_vec[lod.lod_index.value()]->od_descriptors;
4,363✔
823
        auto& desc_v = lod.lod_elements;
4,363✔
824

825
        if (desc_def_v.size() == desc_v.size()
4,363✔
826
            || (this->elf_opid_field.empty() && !desc_v.empty()))
4,363✔
827
        {
828
            return;
3,208✔
829
        }
830
        for (size_t desc_def_index = 0; desc_def_index < desc_def_v.size();
3,296✔
831
             desc_def_index++)
832
        {
833
            const auto& desc_def = desc_def_v[desc_def_index];
2,141✔
834
            auto found_desc = desc_v.value_for(desc_def_index);
2,141✔
835
            auto desc_field_index_iter
836
                = fpat->p_value_name_to_index.find(desc_def.od_field.pp_value);
2,141✔
837

838
            if (desc_field_index_iter == fpat->p_value_name_to_index.end()) {
2,141✔
839
                continue;
36✔
840
            }
841
            auto desc_cap_opt = md[desc_field_index_iter->second];
2,141✔
842
            if (!desc_cap_opt) {
2,141✔
843
                continue;
36✔
844
            }
845

846
            if (!desc_elem_str) {
2,105✔
847
                desc_elem_str = desc_def.matches(desc_cap_opt.value());
950✔
848
            }
849
            if (desc_elem_str) {
2,105✔
850
                if (!found_desc) {
2,102✔
851
                    desc_v.insert(desc_def_index, desc_elem_str.value());
2,102✔
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;
2,105✔
858
        }
859
    }
860
}
5,259✔
861

862
void
863
external_log_format::update_op_description(
3,588✔
864
    const std::vector<opid_descriptors*>& desc_defs_vec,
865
    log_op_description& lod)
866
{
867
    std::optional<std::string> desc_elem_str;
3,588✔
868
    if (!lod.lod_index) {
3,588✔
869
        for (const auto& desc_defs : desc_defs_vec) {
3,621✔
870
            if (lod.lod_index) {
39✔
871
                break;
×
872
            }
873
            for (const auto& desc_def : *desc_defs->od_descriptors) {
39✔
874
                auto desc_cap_iter
875
                    = this->lf_desc_captures.find(desc_def.od_field.pp_value);
39✔
876

877
                if (desc_cap_iter == this->lf_desc_captures.end()) {
39✔
878
                    continue;
×
879
                }
880
                desc_elem_str = desc_def.matches(desc_cap_iter->second);
39✔
881
                if (desc_elem_str) {
39✔
882
                    lod.lod_index = desc_defs->od_index;
39✔
883
                    break;
39✔
884
                }
885
            }
886
        }
887
    }
888
    if (lod.lod_index) {
3,588✔
889
        const auto& desc_def_v
890
            = *desc_defs_vec[lod.lod_index.value()]->od_descriptors;
45✔
891
        auto& desc_v = lod.lod_elements;
45✔
892

893
        if (desc_def_v.size() == desc_v.size()
45✔
894
            || (this->elf_opid_field.empty() && !desc_v.empty()))
45✔
895
        {
896
            return;
6✔
897
        }
898
        for (size_t desc_def_index = 0; desc_def_index < desc_def_v.size();
114✔
899
             desc_def_index++)
900
        {
901
            const auto& desc_def = desc_def_v[desc_def_index];
75✔
902
            auto found_desc = desc_v.value_for(desc_def_index);
75✔
903
            auto desc_cap_iter
904
                = this->lf_desc_captures.find(desc_def.od_field.pp_value);
75✔
905
            if (desc_cap_iter == this->lf_desc_captures.end()) {
75✔
906
                continue;
×
907
            }
908

909
            if (!desc_elem_str) {
75✔
910
                desc_elem_str = desc_def.matches(desc_cap_iter->second);
36✔
911
            }
912
            if (desc_elem_str) {
75✔
913
                if (!found_desc) {
75✔
914
                    desc_v.insert(desc_def_index, desc_elem_str.value());
75✔
915
                } else if (!desc_elem_str->empty()) {
×
916
                    found_desc.value()->append(desc_def.od_joiner);
×
917
                    found_desc.value()->append(desc_elem_str.value());
×
918
                }
919
            }
920
            desc_elem_str = std::nullopt;
75✔
921
        }
922
    }
923
}
3,588✔
924

925
static bool
926
next_format(
2,492,689✔
927
    const std::vector<std::shared_ptr<external_log_format::pattern>>& patterns,
928
    int& index,
929
    int& locked_index)
930
{
931
    bool retval = true;
2,492,689✔
932

933
    if (locked_index == -1) {
2,492,689✔
934
        index += 1;
2,484,945✔
935
        if (index >= (int) patterns.size()) {
2,484,945✔
936
            retval = false;
751,831✔
937
        }
938
    } else if (index == locked_index) {
7,744✔
939
        retval = false;
1✔
940
    } else {
941
        index = locked_index;
7,743✔
942
    }
943

944
    return retval;
2,492,689✔
945
}
946

947
bool
948
log_format::next_format(const pcre_format* fmt, int& index, int& locked_index)
186,411✔
949
{
950
    bool retval = true;
186,411✔
951

952
    if (locked_index == -1) {
186,411✔
953
        index += 1;
186,213✔
954
        if (fmt[index].name == nullptr) {
186,213✔
955
            retval = false;
10,383✔
956
        }
957
    } else if (index == locked_index) {
198✔
958
        retval = false;
40✔
959
    } else {
960
        index = locked_index;
158✔
961
    }
962

963
    return retval;
186,411✔
964
}
965

966
const char*
967
log_format::log_scanf(scan_batch_context& sbc,
13,056✔
968
                      uint32_t line_number,
969
                      string_fragment line,
970
                      const pcre_format* fmt,
971
                      const char* time_fmt[],
972
                      exttm* tm_out,
973
                      timeval* tv_out,
974

975
                      string_fragment* ts_out,
976
                      std::optional<string_fragment>* level_out)
977
{
978
    int curr_fmt = -1;
13,056✔
979
    const char* retval = nullptr;
13,056✔
980
    bool done = false;
13,056✔
981
    int pat_index = sbc.sbc_pattern_locks.last_pattern_index();
13,056✔
982

983
    while (!done && next_format(fmt, curr_fmt, pat_index)) {
189,044✔
984
        thread_local auto md = lnav::pcre2pp::match_data::unitialized();
175,988✔
985

986
        auto match_res = fmt[curr_fmt]
175,988✔
987
                             .pcre->capture_from(line)
175,988✔
988
                             .into(md)
175,988✔
989
                             .matches(PCRE2_NO_UTF_CHECK)
351,976✔
990
                             .ignore_error();
175,988✔
991
        if (!match_res) {
175,988✔
992
            retval = nullptr;
151,285✔
993
        } else {
994
            auto ts = md[fmt[curr_fmt].pf_timestamp_index];
24,703✔
995

996
            retval = this->lf_date_time.scan(
24,703✔
997
                ts->data(), ts->length(), nullptr, tm_out, *tv_out);
24,703✔
998

999
            if (retval == nullptr) {
24,703✔
1000
                auto ls = this->lf_date_time.unlock();
22,070✔
1001
                retval = this->lf_date_time.scan(
22,070✔
1002
                    ts->data(), ts->length(), nullptr, tm_out, *tv_out);
22,070✔
1003
                if (retval != nullptr) {
22,070✔
1004
                    auto old_flags
×
1005
                        = this->lf_timestamp_flags & DATE_TIME_SET_FLAGS;
×
1006
                    auto new_flags = tm_out->et_flags & DATE_TIME_SET_FLAGS;
×
1007

1008
                    // It is unlikely a valid timestamp would lose much
1009
                    // precision.
1010
                    if (new_flags != old_flags) {
×
1011
                        retval = nullptr;
×
1012
                    }
1013
                }
1014
                if (retval == nullptr) {
22,070✔
1015
                    this->lf_date_time.relock(ls);
22,070✔
1016
                } else {
1017
                    log_debug(
×
1018
                        "%d: changed time format to '%s' due to %.*s",
1019
                        line_number,
1020
                        PTIMEC_FORMAT_STR[this->lf_date_time.dts_fmt_lock],
1021
                        ts->length(),
1022
                        ts->data());
1023
                }
1024
            }
1025

1026
            if (retval) {
24,703✔
1027
                ts->sf_end = ts->sf_begin + this->lf_date_time.dts_fmt_len;
2,633✔
1028
                *ts_out = ts.value();
2,633✔
1029
                if (md[2]) {
2,633✔
1030
                    *level_out = md[2];
276✔
1031
                } else {
1032
                    *level_out = line.substr(md[0]->sf_end);
2,357✔
1033
                }
1034
                if (curr_fmt != pat_index) {
2,633✔
1035
                    uint32_t lock_line;
1036

1037
                    if (sbc.sbc_pattern_locks.empty()) {
2,515✔
1038
                        lock_line = 0;
2,515✔
1039
                    } else {
1040
                        lock_line = line_number;
×
1041
                    }
1042

1043
                    sbc.sbc_pattern_locks.pl_lines.emplace_back(lock_line,
2,515✔
1044
                                                                curr_fmt);
1045
                }
1046
                this->lf_timestamp_flags = tm_out->et_flags;
2,633✔
1047
                done = true;
2,633✔
1048
            }
1049
        }
1050
    }
1051

1052
    return retval;
13,056✔
1053
}
1054

1055
void
1056
log_format::annotate(logfile* lf,
37,971✔
1057
                     uint64_t line_number,
1058
                     string_attrs_t& sa,
1059
                     logline_value_vector& values) const
1060
{
1061
    if (lf != nullptr && !values.lvv_opid_value) {
37,971✔
1062
        const auto& bm = lf->get_bookmark_metadata();
3,094✔
1063
        auto bm_iter = bm.find(line_number);
3,094✔
1064
        if (bm_iter != bm.end() && !bm_iter->second.bm_opid.empty()) {
3,094✔
1065
            values.lvv_opid_value = bm_iter->second.bm_opid;
15✔
1066
            values.lvv_opid_provenance
1067
                = logline_value_vector::opid_provenance::user;
15✔
1068
        }
1069
    }
1070
}
37,971✔
1071

1072
void
1073
log_format::check_for_new_year(std::vector<logline>& dst,
2,130✔
1074
                               exttm etm,
1075
                               timeval log_tv) const
1076
{
1077
    if (dst.empty()) {
2,130✔
1078
        return;
283✔
1079
    }
1080

1081
    time_t diff
1082
        = dst.back().get_time<std::chrono::seconds>().count() - log_tv.tv_sec;
1,847✔
1083
    int off_year = 0, off_month = 0, off_day = 0, off_hour = 0;
1,847✔
1084
    bool do_change = true;
1,847✔
1085

1086
    if (diff <= 0) {
1,847✔
1087
        return;
1,787✔
1088
    }
1089
    if ((etm.et_flags & ETF_MONTH_SET) && diff >= (24 * 60 * 60)) {
60✔
1090
        off_year = 1;
11✔
1091
    } else if (diff >= (24 * 60 * 60)) {
49✔
1092
        off_month = 1;
2✔
1093
    } else if (!(etm.et_flags & ETF_DAY_SET) && (diff >= (60 * 60))) {
47✔
1094
        off_day = 1;
1✔
1095
    } else if (!(etm.et_flags & ETF_HOUR_SET) && (diff >= 60)) {
46✔
1096
        off_hour = 1;
4✔
1097
    } else {
1098
        do_change = false;
42✔
1099
    }
1100

1101
    if (!do_change) {
60✔
1102
        return;
42✔
1103
    }
1104
    log_debug("%zu:detected time rollover; offsets=%d %d %d %d",
18✔
1105
              dst.size(),
1106
              off_year,
1107
              off_month,
1108
              off_day,
1109
              off_hour);
1110
    for (auto& ll : dst) {
68✔
1111
        time_t ot = ll.get_time<std::chrono::seconds>().count();
50✔
1112
        struct tm otm;
1113

1114
        gmtime_r(&ot, &otm);
50✔
1115
        otm.tm_yday = -1;
50✔
1116
        if (otm.tm_year < off_year) {
50✔
1117
            otm.tm_year = 0;
×
1118
        } else {
1119
            otm.tm_year -= off_year;
50✔
1120
        }
1121
        otm.tm_mon -= off_month;
50✔
1122
        if (otm.tm_mon < 0) {
50✔
1123
            otm.tm_mon += 12;
2✔
1124
        }
1125
        auto new_time = tm2sec(&otm);
50✔
1126
        if (new_time == -1) {
50✔
1127
            continue;
×
1128
        }
1129
        new_time -= (off_day * 24 * 60 * 60) + (off_hour * 60 * 60);
50✔
1130
        auto old_sub = ll.get_subsecond_time<std::chrono::microseconds>();
50✔
1131
        ll.set_time(std::chrono::seconds{new_time});
50✔
1132
        ll.set_subsecond_time(old_sub);
50✔
1133
    }
1134
}
1135

1136
/*
1137
 * XXX This needs some cleanup.
1138
 */
1139
struct json_log_userdata {
1140
    json_log_userdata(shared_buffer_ref& sbr, scan_batch_context* sbc)
15,753✔
1141
        : jlu_shared_buffer(sbr), jlu_batch_context(sbc)
15,753✔
1142
    {
1143
    }
15,753✔
1144

1145
    const external_log_format::value_def* get_field_def(
418,840✔
1146
        yajlpp_parse_context* ypc)
1147
    {
1148
        const auto field_frag = ypc->get_path_as_string_fragment();
418,840✔
1149
        auto* format = this->jlu_format;
418,840✔
1150

1151
        if (this->jlu_read_order_index < format->elf_value_def_read_order.size()
418,840✔
1152
            && format->elf_value_def_read_order[this->jlu_read_order_index]
573,544✔
1153
                    .first
1154
                == field_frag)
154,704✔
1155
        {
1156
            auto retval
1157
                = format->elf_value_def_read_order[this->jlu_read_order_index]
148,852✔
1158
                      .second;
148,852✔
1159
            if (retval != nullptr) {
148,852✔
1160
                this->jlu_precision += 1;
22,310✔
1161
            }
1162
            this->jlu_read_order_index += 1;
148,852✔
1163
            return retval;
148,852✔
1164
        }
1165

1166
        format->elf_value_def_read_order.resize(this->jlu_read_order_index);
269,988✔
1167
        auto vd_iter = format->elf_value_def_frag_map.find(field_frag);
269,988✔
1168
        if (vd_iter != format->elf_value_def_frag_map.end()) {
269,988✔
1169
            format->elf_value_def_read_order.emplace_back(vd_iter->first,
223,626✔
1170
                                                          vd_iter->second);
223,626✔
1171
            this->jlu_read_order_index += 1;
223,626✔
1172
            if (vd_iter->second != nullptr) {
223,626✔
1173
                this->jlu_precision += 1;
16,295✔
1174
            }
1175
            return vd_iter->second;
223,626✔
1176
        }
1177

1178
        auto owned_frag = field_frag.to_owned(format->elf_allocator);
46,362✔
1179
        format->elf_value_def_frag_map[owned_frag] = nullptr;
46,362✔
1180
        format->elf_value_def_read_order.emplace_back(owned_frag, nullptr);
46,362✔
1181
        this->jlu_read_order_index += 1;
46,362✔
1182
        return nullptr;
46,362✔
1183
    }
1184

1185
    void add_sub_lines_for(const external_log_format::value_def* vd,
316,285✔
1186
                           bool top_level,
1187
                           std::optional<double> val,
1188
                           const unsigned char* str,
1189
                           ssize_t len,
1190
                           yajl_string_props_t* props)
1191
    {
1192
        auto res = this->jlu_format->value_line_count(
316,285✔
1193
            *this->jlu_batch_context, vd, top_level, val, str, len, props);
316,285✔
1194
        this->jlu_has_ansi |= res.vlcr_has_ansi;
316,285✔
1195
        if (!res.vlcr_valid_utf) {
316,285✔
1196
            this->jlu_valid_utf = false;
×
1197
        }
1198
        this->jlu_sub_line_count += res.vlcr_count;
316,285✔
1199
        this->jlu_quality += res.vlcr_line_format_count;
316,285✔
1200
        if (res.vlcr_line_format_index) {
316,285✔
1201
            this->jlu_format_hits[res.vlcr_line_format_index.value()] = true;
4,206✔
1202
        }
1203
    }
316,285✔
1204

1205
    external_log_format* jlu_format{nullptr};
1206
    const logline* jlu_line{nullptr};
1207
    logline* jlu_base_line{nullptr};
1208
    int jlu_sub_line_count{1};
1209
    bool jlu_has_ansi{false};
1210
    bool jlu_valid_utf{true};
1211
    yajl_handle jlu_handle{nullptr};
1212
    const char* jlu_line_value{nullptr};
1213
    size_t jlu_line_size{0};
1214
    std::stack<size_t> jlu_sub_start;
1215
    uint32_t jlu_quality{0};
1216
    uint32_t jlu_strikes{0};
1217
    uint32_t jlu_precision{0};
1218
    std::vector<bool> jlu_format_hits;
1219
    shared_buffer_ref& jlu_shared_buffer;
1220
    scan_batch_context* jlu_batch_context;
1221
    std::optional<string_fragment> jlu_opid_frag;
1222
    std::optional<string_fragment> jlu_opid_desc_frag;
1223
    std::optional<string_fragment> jlu_tid_frag;
1224
    std::optional<int64_t> jlu_tid_number;
1225
    std::optional<std::string> jlu_subid;
1226
    std::optional<log_format::scan_error> jlu_scan_error;
1227
    hasher jlu_opid_hasher;
1228
    std::optional<std::chrono::microseconds> jlu_duration;
1229
    std::optional<std::chrono::microseconds> jlu_start_time;
1230
    std::optional<std::chrono::microseconds> jlu_end_time;
1231
    exttm jlu_exttm;
1232
    size_t jlu_read_order_index{0};
1233
    subline_options jlu_subline_opts;
1234
};
1235

1236
static int read_json_field(yajlpp_parse_context* ypc,
1237
                           const unsigned char* str,
1238
                           size_t len,
1239
                           yajl_string_props_t*);
1240

1241
static int
1242
read_json_null(yajlpp_parse_context* ypc)
4,599✔
1243
{
1244
    json_log_userdata* jlu = (json_log_userdata*) ypc->ypc_userdata;
4,599✔
1245
    const auto* vd = jlu->get_field_def(ypc);
4,599✔
1246

1247
    jlu->add_sub_lines_for(
9,198✔
1248
        vd, ypc->is_level(1), std::nullopt, nullptr, -1, nullptr);
4,599✔
1249

1250
    return 1;
4,599✔
1251
}
1252

1253
static int
1254
read_json_bool(yajlpp_parse_context* ypc, int val)
22,109✔
1255
{
1256
    json_log_userdata* jlu = (json_log_userdata*) ypc->ypc_userdata;
22,109✔
1257
    const auto* vd = jlu->get_field_def(ypc);
22,109✔
1258

1259
    jlu->add_sub_lines_for(
44,218✔
1260
        vd, ypc->is_level(1), std::nullopt, nullptr, -1, nullptr);
22,109✔
1261

1262
    return 1;
22,109✔
1263
}
1264

1265
static int
1266
read_json_number(yajlpp_parse_context* ypc,
37,769✔
1267
                 const char* numberVal,
1268
                 size_t numberLen)
1269
{
1270
    auto* jlu = (json_log_userdata*) ypc->ypc_userdata;
37,769✔
1271
    auto number_frag = string_fragment::from_bytes(numberVal, numberLen);
37,769✔
1272
    std::optional<double> val;
37,769✔
1273

1274
    intern_string_t field_name;
37,769✔
1275
    const auto* vd = jlu->get_field_def(ypc);
37,769✔
1276
    if (vd != nullptr) {
37,769✔
1277
        field_name = vd->vd_meta.lvm_name;
1,956✔
1278
    }
1279

1280
    if (field_name.empty()) {
37,769✔
1281
    } else if (jlu->jlu_format->lf_timestamp_field == field_name) {
1,956✔
1282
        long long divisor = jlu->jlu_format->elf_timestamp_divisor;
134✔
1283
        auto scan_res = scn::scan_value<double>(number_frag.to_string_view());
134✔
1284
        if (!scan_res) {
134✔
1285
            log_error("invalid number %.*s", (int) numberLen, numberVal);
×
1286
            return 0;
×
1287
        }
1288
        auto ts_val = scan_res.value().value();
134✔
1289
        timeval tv;
1290
        tv.tv_sec = ts_val / divisor;
134✔
1291
        tv.tv_usec = fmod(ts_val, divisor) * (1000000.0 / divisor);
134✔
1292
        jlu->jlu_end_time = to_us(tv);
134✔
1293
        jlu->jlu_format->lf_date_time.to_localtime(tv.tv_sec, jlu->jlu_exttm);
134✔
1294
        tv.tv_sec = tm2sec(&jlu->jlu_exttm.et_tm);
134✔
1295
        jlu->jlu_exttm.et_gmtoff
1296
            = jlu->jlu_format->lf_date_time.dts_local_offset_cache;
134✔
1297
        jlu->jlu_exttm.et_flags
134✔
1298
            |= ETF_MACHINE_ORIENTED | ETF_SUB_NOT_IN_FORMAT | ETF_ZONE_SET;
134✔
1299
        if (divisor == 1000) {
134✔
1300
            jlu->jlu_exttm.et_flags |= ETF_MILLIS_SET;
17✔
1301
        } else {
1302
            jlu->jlu_exttm.et_flags |= ETF_MICROS_SET;
117✔
1303
        }
1304
        jlu->jlu_exttm.et_nsec = tv.tv_usec * 1000;
134✔
1305
        if (tv.tv_sec < 0) {
134✔
1306
            jlu->jlu_scan_error = log_format::scan_error{
×
1307
                fmt::format(FMT_STRING("invalid numeric timestamp: {} / {} "
×
1308
                                       "(timestamp-divisor) = {}"),
1309
                            number_frag,
1310
                            divisor,
1311
                            tv.tv_sec),
1312
            };
1313
            return 1;
×
1314
        }
1315
        jlu->jlu_base_line->set_time(tv);
134✔
1316
    } else if (!jlu->jlu_format->lf_start_timestamp_field.empty()
1,822✔
1317
               && jlu->jlu_format->lf_start_timestamp_field == field_name)
1,822✔
1318
    {
1319
        long long divisor = jlu->jlu_format->elf_timestamp_divisor;
16✔
1320
        auto scan_res = scn::scan_value<double>(number_frag.to_string_view());
16✔
1321
        if (!scan_res) {
16✔
1322
            log_error("invalid number %.*s", (int) numberLen, numberVal);
×
1323
            return 0;
×
1324
        }
1325
        auto ts_val = scan_res.value().value();
16✔
1326
        if (ts_val < 0) {
16✔
1327
            jlu->jlu_scan_error = log_format::scan_error{
×
1328
                fmt::format(
1329
                    FMT_STRING("invalid numeric start-timestamp: {} / {} "
×
1330
                               "(timestamp-divisor) = {}"),
1331
                    number_frag,
1332
                    divisor,
1333
                    ts_val),
1334
            };
1335
            return 1;
×
1336
        }
1337
        jlu->jlu_start_time = std::chrono::microseconds(
32✔
1338
            static_cast<int64_t>(ts_val * 1000000.0 / divisor));
16✔
1339
    } else if (jlu->jlu_format->lf_subsecond_field == field_name) {
1,806✔
1340
        auto scan_res = scn::scan_value<double>(number_frag.to_string_view());
3✔
1341
        if (!scan_res) {
3✔
1342
            log_error("invalid number %.*s", (int) numberLen, numberVal);
×
1343
            return 0;
×
1344
        }
1345
        auto ts_val = scan_res.value().value();
3✔
1346

1347
        uint64_t millis = 0;
3✔
1348
        jlu->jlu_exttm.et_flags &= ~(ETF_MICROS_SET | ETF_MILLIS_SET);
3✔
1349
        switch (jlu->jlu_format->lf_subsecond_unit.value()) {
3✔
1350
            case log_format::subsecond_unit::milli:
×
1351
                millis = ts_val;
×
1352
                jlu->jlu_exttm.et_nsec = ts_val * 1000000;
×
1353
                jlu->jlu_exttm.et_flags |= ETF_MILLIS_SET;
×
1354
                break;
×
1355
            case log_format::subsecond_unit::micro:
×
1356
                millis = std::chrono::duration_cast<std::chrono::milliseconds>(
×
1357
                             std::chrono::microseconds((int64_t) ts_val))
×
1358
                             .count();
×
1359
                jlu->jlu_exttm.et_nsec = ts_val * 1000;
×
1360
                jlu->jlu_exttm.et_flags |= ETF_MICROS_SET;
×
1361
                break;
×
1362
            case log_format::subsecond_unit::nano:
3✔
1363
                millis = std::chrono::duration_cast<std::chrono::milliseconds>(
3✔
1364
                             std::chrono::nanoseconds((int64_t) ts_val))
3✔
1365
                             .count();
3✔
1366
                jlu->jlu_exttm.et_nsec = ts_val;
3✔
1367
                jlu->jlu_exttm.et_flags |= ETF_NANOS_SET;
3✔
1368
                break;
3✔
1369
        }
1370
        jlu->jlu_exttm.et_flags |= ETF_SUB_NOT_IN_FORMAT;
3✔
1371
        jlu->jlu_base_line->set_subsecond_time(
3✔
1372
            std::chrono::milliseconds(millis));
1373
    } else if (jlu->jlu_format->elf_level_field == field_name) {
1,803✔
1374
        if (jlu->jlu_format->elf_level_pairs.empty()) {
516✔
1375
            jlu->jlu_base_line->set_level(jlu->jlu_format->convert_level(
321✔
1376
                number_frag, jlu->jlu_batch_context));
1377
        } else {
1378
            auto scan_res
1379
                = scn::scan_int<int64_t>(number_frag.to_string_view());
195✔
1380
            if (!scan_res) {
195✔
1381
                log_error("invalid number %.*s", (int) numberLen, numberVal);
×
1382
                return 0;
×
1383
            }
1384
            auto level_int = scan_res.value().value();
195✔
1385

1386
            for (const auto& pair : jlu->jlu_format->elf_level_pairs) {
705✔
1387
                if (pair.first == level_int) {
632✔
1388
                    jlu->jlu_base_line->set_level(pair.second);
122✔
1389
                    break;
122✔
1390
                }
1391
            }
1392
        }
1393
    } else if (vd != nullptr) {
1,287✔
1394
        if (jlu->jlu_format->elf_thread_id_field == field_name) {
1,287✔
1395
            auto& sbc = *jlu->jlu_batch_context;
124✔
1396
            auto tid_iter = sbc.sbc_tids.ltis_tid_ranges.find(number_frag);
124✔
1397
            if (tid_iter == sbc.sbc_tids.ltis_tid_ranges.end()) {
124✔
1398
                jlu->jlu_tid_frag = number_frag.to_owned(sbc.sbc_allocator);
71✔
1399
            } else {
1400
                jlu->jlu_tid_frag = tid_iter->first;
53✔
1401
            }
1402
        }
1403
        if ((vd->vd_meta.lvm_kind == value_kind_t::VALUE_INTEGER
1,287✔
1404
             || vd->vd_meta.lvm_kind == value_kind_t::VALUE_FLOAT)
165✔
1405
            && !vd->vd_meta.lvm_foreign_key && !vd->vd_meta.lvm_identifier)
1,148✔
1406
        {
1407
            auto scan_res
1408
                = scn::scan_value<double>(number_frag.to_string_view());
247✔
1409
            if (!scan_res) {
247✔
1410
                log_error("invalid number %.*s", (int) numberLen, numberVal);
×
1411
                return 0;
×
1412
            }
1413
            val = scan_res.value().value();
247✔
1414
            if (jlu->jlu_format->elf_duration_field == field_name) {
247✔
1415
                auto dur_secs
1416
                    = val.value() / jlu->jlu_format->elf_duration_divisor;
26✔
1417
                auto us = std::chrono::microseconds(
1418
                    static_cast<int64_t>(dur_secs * 1000000));
26✔
1419
                jlu->jlu_duration = std::max(us, 1us);
26✔
1420
            }
1421
        }
1422
    }
1423

1424
    jlu->add_sub_lines_for(vd,
37,769✔
1425
                           ypc->is_level(1),
37,769✔
1426
                           val,
1427
                           (const unsigned char*) numberVal,
1428
                           numberLen,
1429
                           nullptr);
1430

1431
    return 1;
37,769✔
1432
}
1433

1434
static int
1435
json_array_start(void* ctx)
66,538✔
1436
{
1437
    auto* ypc = (yajlpp_parse_context*) ctx;
66,538✔
1438
    auto* jlu = (json_log_userdata*) ypc->ypc_userdata;
66,538✔
1439

1440
    jlu->jlu_sub_start.push(yajl_get_bytes_consumed(jlu->jlu_handle) - 1);
66,538✔
1441
    if (ypc->ypc_path_index_stack.size() == 2) {
66,538✔
1442
        const auto* vd = jlu->get_field_def(ypc);
20,797✔
1443

1444
        jlu->add_sub_lines_for(vd, true, std::nullopt, nullptr, -1, nullptr);
20,797✔
1445
    }
1446

1447
    return 1;
66,538✔
1448
}
1449

1450
static int
1451
json_array_start_const(void* ctx)
10,401✔
1452
{
1453
    auto* ypc = (yajlpp_parse_context*) ctx;
10,401✔
1454
    auto* jlu = (json_log_userdata*) ypc->ypc_userdata;
10,401✔
1455

1456
    jlu->jlu_sub_start.push(yajl_get_bytes_consumed(jlu->jlu_handle) - 1);
10,401✔
1457

1458
    return 1;
10,401✔
1459
}
1460

1461
static int
1462
json_array_end(void* ctx)
10,381✔
1463
{
1464
    auto* ypc = (yajlpp_parse_context*) ctx;
10,381✔
1465
    auto* jlu = (json_log_userdata*) ypc->ypc_userdata;
10,381✔
1466
    const auto* vd = ypc->ypc_path_index_stack.size() > 1
10,381✔
1467
        ? jlu->get_field_def(ypc)
10,381✔
1468
        : nullptr;
10,381✔
1469

1470
    if (ypc->ypc_path_index_stack.size() == 1 || vd != nullptr) {
10,381✔
1471
        intern_string_t field_name;
2,836✔
1472
        if (vd != nullptr) {
2,836✔
1473
            field_name = vd->vd_meta.lvm_name;
×
1474
        } else {
1475
            field_name = ypc->get_path();
2,836✔
1476
        }
1477
        auto sub_start = jlu->jlu_sub_start.top();
2,836✔
1478
        size_t sub_end = yajl_get_bytes_consumed(jlu->jlu_handle);
2,836✔
1479
        auto json_frag = string_fragment::from_byte_range(
2,836✔
1480
            jlu->jlu_shared_buffer.get_data(), sub_start, sub_end);
2,836✔
1481
        jlu->jlu_format->jlf_line_values.lvv_values.emplace_back(
2,836✔
1482
            jlu->jlu_format->get_value_meta(field_name,
5,672✔
1483
                                            value_kind_t::VALUE_JSON),
1484
            json_frag);
1485
        if (field_name == jlu->jlu_format->elf_opid_field) {
2,836✔
1486
            jlu->jlu_opid_desc_frag = json_frag;
29✔
1487
        }
1488
    }
1489
    jlu->jlu_sub_start.pop();
10,381✔
1490

1491
    return 1;
10,381✔
1492
}
1493

1494
static int
1495
read_array_end(void* ctx)
66,322✔
1496
{
1497
    auto* ypc = (yajlpp_parse_context*) ctx;
66,322✔
1498
    auto* jlu = (json_log_userdata*) ypc->ypc_userdata;
66,322✔
1499
    const auto* vd = ypc->ypc_path_index_stack.size() > 1
66,322✔
1500
        ? jlu->get_field_def(ypc)
66,322✔
1501
        : nullptr;
66,322✔
1502

1503
    if (ypc->ypc_path_index_stack.size() == 1 || vd != nullptr) {
66,322✔
1504
        const intern_string_t field_name = ypc->get_path_fragment_i(0);
20,766✔
1505
        auto sub_start = jlu->jlu_sub_start.top();
20,766✔
1506
        jlu->jlu_sub_start.pop();
20,766✔
1507
        size_t sub_end = yajl_get_bytes_consumed(jlu->jlu_handle);
20,766✔
1508
        auto json_frag = string_fragment::from_byte_range(
20,766✔
1509
            jlu->jlu_shared_buffer.get_data(), sub_start, sub_end);
20,766✔
1510
        if (field_name == jlu->jlu_format->elf_opid_field) {
20,766✔
1511
            jlu->jlu_opid_desc_frag = json_frag;
2,499✔
1512
        }
1513
        if (ypc->ypc_path_index_stack.size() > 1 && vd != nullptr) {
20,766✔
1514
            jlu->add_sub_lines_for(
×
1515
                vd, false, std::nullopt, nullptr, -1, nullptr);
1516
        }
1517
    }
1518

1519
    return 1;
66,322✔
1520
}
1521

1522
static const json_path_container json_log_handlers = {
1523
    yajlpp::pattern_property_handler("\\w+")
1524
        .add_cb(read_json_null)
1525
        .add_cb(read_json_bool)
1526
        .add_cb(read_json_number)
1527
        .add_cb(read_json_field),
1528
};
1529

1530
static int rewrite_json_field(yajlpp_parse_context* ypc,
1531
                              const unsigned char* str,
1532
                              size_t len,
1533
                              yajl_string_props_t*);
1534

1535
static int
1536
rewrite_json_null(yajlpp_parse_context* ypc)
1,290✔
1537
{
1538
    auto jlu = (json_log_userdata*) ypc->ypc_userdata;
1,290✔
1539
    const auto* vd = jlu->get_field_def(ypc);
1,290✔
1540

1541
    if (!ypc->is_level(1) && vd == nullptr) {
1,290✔
1542
        return 1;
1,228✔
1543
    }
1544
    jlu->jlu_format->jlf_line_values.lvv_values.emplace_back(
62✔
1545
        jlu->jlu_format->get_value_meta(ypc, vd, value_kind_t::VALUE_NULL));
124✔
1546

1547
    return 1;
62✔
1548
}
1549

1550
static int
1551
rewrite_json_bool(yajlpp_parse_context* ypc, int val)
5,640✔
1552
{
1553
    auto* jlu = (json_log_userdata*) ypc->ypc_userdata;
5,640✔
1554
    const auto* vd = jlu->get_field_def(ypc);
5,640✔
1555

1556
    if (!ypc->is_level(1) && vd == nullptr) {
5,640✔
1557
        return 1;
5,008✔
1558
    }
1559
    jlu->jlu_format->jlf_line_values.lvv_values.emplace_back(
632✔
1560
        jlu->jlu_format->get_value_meta(ypc, vd, value_kind_t::VALUE_BOOLEAN),
632✔
1561
        (bool) val);
632✔
1562
    return 1;
632✔
1563
}
1564

1565
static int
1566
rewrite_json_int(yajlpp_parse_context* ypc, long long val)
8,041✔
1567
{
1568
    auto* jlu = (json_log_userdata*) ypc->ypc_userdata;
8,041✔
1569
    const auto* vd = jlu->get_field_def(ypc);
8,041✔
1570

1571
    if (vd != nullptr) {
8,041✔
1572
        const intern_string_t field_name = vd->vd_meta.lvm_name;
1,363✔
1573
        if (jlu->jlu_format->lf_timestamp_field == field_name) {
1,363✔
1574
            long long divisor = jlu->jlu_format->elf_timestamp_divisor;
126✔
1575
            timeval tv;
1576

1577
            tv.tv_sec = val / divisor;
126✔
1578
            tv.tv_usec = fmod(val, divisor) * (1000000.0 / divisor);
126✔
1579
            jlu->jlu_end_time = to_us(tv);
126✔
1580
            jlu->jlu_format->lf_date_time.to_localtime(tv.tv_sec,
126✔
1581
                                                       jlu->jlu_exttm);
126✔
1582
            jlu->jlu_exttm.et_gmtoff
1583
                = jlu->jlu_format->lf_date_time.dts_local_offset_cache;
126✔
1584
            jlu->jlu_exttm.et_flags |= ETF_MACHINE_ORIENTED
126✔
1585
                | ETF_SUB_NOT_IN_FORMAT | ETF_ZONE_SET | ETF_Z_FOR_UTC;
1586
            if (divisor == 1) {
126✔
1587
                jlu->jlu_exttm.et_flags |= ETF_MICROS_SET;
4✔
1588
            } else {
1589
                jlu->jlu_exttm.et_flags |= ETF_MILLIS_SET;
122✔
1590
            }
1591
            jlu->jlu_exttm.et_nsec = tv.tv_usec * 1000;
126✔
1592
        } else if (!jlu->jlu_format->lf_start_timestamp_field.empty()
1,237✔
1593
                   && jlu->jlu_format->lf_start_timestamp_field == field_name)
1,237✔
1594
        {
1595
            long long divisor = jlu->jlu_format->elf_timestamp_divisor;
104✔
1596
            jlu->jlu_start_time = std::chrono::microseconds(
208✔
1597
                static_cast<int64_t>(val * 1000000.0 / divisor));
104✔
1598
        } else if (jlu->jlu_format->lf_subsecond_field == field_name) {
1,133✔
1599
            jlu->jlu_exttm.et_flags &= ~(ETF_MICROS_SET | ETF_MILLIS_SET);
4✔
1600
            switch (jlu->jlu_format->lf_subsecond_unit.value()) {
4✔
1601
                case log_format::subsecond_unit::milli:
×
1602
                    jlu->jlu_exttm.et_nsec = val * 1000000;
×
1603
                    jlu->jlu_exttm.et_flags |= ETF_MILLIS_SET;
×
1604
                    break;
×
1605
                case log_format::subsecond_unit::micro:
×
1606
                    jlu->jlu_exttm.et_nsec = val * 1000;
×
1607
                    jlu->jlu_exttm.et_flags |= ETF_MICROS_SET;
×
1608
                    break;
×
1609
                case log_format::subsecond_unit::nano:
4✔
1610
                    jlu->jlu_exttm.et_nsec = val;
4✔
1611
                    jlu->jlu_exttm.et_flags |= ETF_NANOS_SET;
4✔
1612
                    break;
4✔
1613
            }
1614
            jlu->jlu_exttm.et_flags |= ETF_SUB_NOT_IN_FORMAT;
4✔
1615
        } else if (jlu->jlu_format->elf_duration_field == field_name) {
1,129✔
1616
            auto dur_secs = val / jlu->jlu_format->elf_duration_divisor;
2✔
1617
            jlu->jlu_duration
1618
                = std::max(1us,
2✔
1619
                           std::chrono::microseconds(
×
1620
                               static_cast<int64_t>(dur_secs * 1000000.0)));
4✔
1621
        }
1622
    }
1623

1624
    if (!ypc->is_level(1) && vd == nullptr) {
8,041✔
1625
        return 1;
6,631✔
1626
    }
1627
    if (vd != nullptr
1,410✔
1628
        && vd->vd_meta.lvm_name == jlu->jlu_format->elf_thread_id_field)
1,410✔
1629
    {
1630
        jlu->jlu_tid_number = val;
156✔
1631
    }
1632
    jlu->jlu_format->jlf_line_values.lvv_values.emplace_back(
1,410✔
1633
        jlu->jlu_format->get_value_meta(ypc, vd, value_kind_t::VALUE_INTEGER),
1,410✔
1634
        (int64_t) val);
1,410✔
1635
    return 1;
1,410✔
1636
}
1637

1638
static int
1639
rewrite_json_double(yajlpp_parse_context* ypc, double val)
172✔
1640
{
1641
    auto* jlu = (json_log_userdata*) ypc->ypc_userdata;
172✔
1642
    const auto* vd = jlu->get_field_def(ypc);
172✔
1643

1644
    if (vd != nullptr) {
172✔
1645
        const intern_string_t field_name = vd->vd_meta.lvm_name;
168✔
1646
        if (jlu->jlu_format->lf_timestamp_field == field_name) {
168✔
1647
            long long divisor = jlu->jlu_format->elf_timestamp_divisor;
133✔
1648
            timeval tv;
1649

1650
            tv.tv_sec = val / divisor;
133✔
1651
            tv.tv_usec = fmod(val, divisor) * (1000000.0 / divisor);
133✔
1652
            jlu->jlu_end_time = to_us(tv);
133✔
1653
            jlu->jlu_format->lf_date_time.to_localtime(tv.tv_sec,
133✔
1654
                                                       jlu->jlu_exttm);
133✔
1655
            jlu->jlu_exttm.et_gmtoff
1656
                = jlu->jlu_format->lf_date_time.dts_local_offset_cache;
133✔
1657
            jlu->jlu_exttm.et_flags |= ETF_MACHINE_ORIENTED
133✔
1658
                | ETF_SUB_NOT_IN_FORMAT | ETF_ZONE_SET | ETF_Z_FOR_UTC;
1659
            if (divisor == 1) {
133✔
1660
                jlu->jlu_exttm.et_flags |= ETF_MICROS_SET;
125✔
1661
            } else {
1662
                jlu->jlu_exttm.et_flags |= ETF_MILLIS_SET;
8✔
1663
            }
1664
            jlu->jlu_exttm.et_nsec = tv.tv_usec * 1000;
133✔
1665
        } else if (!jlu->jlu_format->lf_start_timestamp_field.empty()
35✔
1666
                   && jlu->jlu_format->lf_start_timestamp_field == field_name)
35✔
1667
        {
1668
            long long divisor = jlu->jlu_format->elf_timestamp_divisor;
×
1669
            jlu->jlu_start_time = std::chrono::microseconds(
×
1670
                static_cast<int64_t>(val * 1000000.0 / divisor));
1671
        } else if (jlu->jlu_format->lf_subsecond_field == field_name) {
35✔
1672
            jlu->jlu_exttm.et_flags &= ~(ETF_MICROS_SET | ETF_MILLIS_SET);
×
1673
            switch (jlu->jlu_format->lf_subsecond_unit.value()) {
×
1674
                case log_format::subsecond_unit::milli:
×
1675
                    jlu->jlu_exttm.et_nsec = val * 1000000;
×
1676
                    jlu->jlu_exttm.et_flags |= ETF_MILLIS_SET;
×
1677
                    break;
×
1678
                case log_format::subsecond_unit::micro:
×
1679
                    jlu->jlu_exttm.et_nsec = val * 1000;
×
1680
                    jlu->jlu_exttm.et_flags |= ETF_MICROS_SET;
×
1681
                    break;
×
1682
                case log_format::subsecond_unit::nano:
×
1683
                    jlu->jlu_exttm.et_nsec = val;
×
1684
                    jlu->jlu_exttm.et_flags |= ETF_NANOS_SET;
×
1685
                    break;
×
1686
            }
1687
            jlu->jlu_exttm.et_flags |= ETF_SUB_NOT_IN_FORMAT;
×
1688
        } else if (jlu->jlu_format->elf_duration_field == field_name) {
35✔
1689
            auto dur_secs = val / jlu->jlu_format->elf_duration_divisor;
35✔
1690
            jlu->jlu_duration
1691
                = std::max(1us,
35✔
1692
                           std::chrono::microseconds(
×
1693
                               static_cast<int64_t>(dur_secs * 1000000.0)));
70✔
1694
        }
1695
    }
1696

1697
    if (!ypc->is_level(1) && vd == nullptr) {
172✔
1698
        return 1;
×
1699
    }
1700
    jlu->jlu_format->jlf_line_values.lvv_values.emplace_back(
172✔
1701
        jlu->jlu_format->get_value_meta(ypc, vd, value_kind_t::VALUE_FLOAT),
344✔
1702
        val);
1703

1704
    return 1;
172✔
1705
}
1706

1707
static const json_path_container json_log_rewrite_handlers = {
1708
    yajlpp::pattern_property_handler("\\w+")
1709
        .add_cb(rewrite_json_null)
1710
        .add_cb(rewrite_json_bool)
1711
        .add_cb(rewrite_json_int)
1712
        .add_cb(rewrite_json_double)
1713
        .add_cb(rewrite_json_field),
1714
};
1715

1716
bool
1717
external_log_format::scan_for_partial(const log_format_file_state& lffs,
1✔
1718
                                      shared_buffer_ref& sbr,
1719
                                      size_t& len_out) const
1720
{
1721
    if (this->elf_type != elf_type_t::ELF_TYPE_TEXT) {
1✔
1722
        return false;
×
1723
    }
1724

1725
    const auto& pat
1726
        = this->elf_pattern_order[lffs.lffs_pattern_locks.last_pattern_index()];
1✔
1727
    if (!this->lf_multiline) {
1✔
1728
        len_out = pat->p_pcre.pp_value->match_partial(sbr.to_string_fragment());
1✔
1729
        return true;
1✔
1730
    }
1731

1732
    if (pat->p_timestamp_end == -1 || pat->p_timestamp_end > (int) sbr.length())
×
1733
    {
1734
        len_out = 0;
×
1735
        return false;
×
1736
    }
1737

1738
    len_out = pat->p_pcre.pp_value->match_partial(sbr.to_string_fragment());
×
1739
    return (int) len_out > pat->p_timestamp_end;
×
1740
}
1741

1742
std::vector<lnav::console::snippet>
1743
external_log_format::get_snippets() const
23✔
1744
{
1745
    std::vector<lnav::console::snippet> retval;
23✔
1746

1747
    for (const auto& src_pair : this->elf_format_sources) {
46✔
1748
        retval.emplace_back(lnav::console::snippet::from(src_pair.first, "")
46✔
1749
                                .with_line(src_pair.second));
23✔
1750
    }
1751

1752
    return retval;
23✔
1753
}
×
1754

1755
log_format::scan_result_t
1756
external_log_format::scan_json(std::vector<logline>& dst,
97,875✔
1757
                               const line_info& li,
1758
                               shared_buffer_ref& sbr,
1759
                               scan_batch_context& sbc)
1760
{
1761
    logline ll(
1762
        li.li_file_range.fr_offset, std::chrono::microseconds{0}, LEVEL_INFO);
97,875✔
1763
    auto line_frag = sbr.to_string_fragment();
97,875✔
1764

1765
    if (!line_frag.startswith("{")) {
97,875✔
1766
        if (!this->lf_specialized) {
84,537✔
1767
            return scan_no_match{"line is not a JSON object"};
84,532✔
1768
        }
1769

1770
        if (!dst.empty()) {
5✔
1771
            ll.set_time(dst.back().get_time<std::chrono::microseconds>());
5✔
1772
        }
1773
        ll.set_level(LEVEL_INVALID);
5✔
1774
        dst.emplace_back(ll);
5✔
1775
        return scan_match{0};
5✔
1776
    }
1777

1778
    auto& ypc = *(this->jlf_parse_context);
13,338✔
1779
    yajl_handle handle = this->jlf_yajl_handle.get();
13,338✔
1780
    json_log_userdata jlu(sbr, &sbc);
13,338✔
1781

1782
    if (li.li_partial) {
13,338✔
1783
        log_debug("skipping partial line at offset %lld",
42✔
1784
                  li.li_file_range.fr_offset);
1785
        if (this->lf_specialized) {
42✔
1786
            if (!dst.empty()) {
3✔
1787
                ll.set_time(dst.back().get_time<std::chrono::microseconds>());
3✔
1788
            }
1789
            ll.set_level(LEVEL_INVALID);
3✔
1790
            dst.emplace_back(ll);
3✔
1791
        }
1792
        return scan_incomplete{};
42✔
1793
    }
1794

1795
    const auto* line_data = (const unsigned char*) sbr.get_data();
13,296✔
1796

1797
    this->lf_desc_captures.clear();
13,296✔
1798
    this->lf_desc_allocator.reset();
13,296✔
1799

1800
    yajl_reset(handle);
13,296✔
1801
    ypc.set_static_handler(json_log_handlers.jpc_children[0]);
13,296✔
1802
    ypc.ypc_userdata = &jlu;
13,296✔
1803
    ypc.ypc_ignore_unused = true;
13,296✔
1804
    ypc.ypc_alt_callbacks.yajl_start_array = json_array_start;
13,296✔
1805
    ypc.ypc_alt_callbacks.yajl_start_map = json_array_start;
13,296✔
1806
    ypc.ypc_alt_callbacks.yajl_end_array = read_array_end;
13,296✔
1807
    ypc.ypc_alt_callbacks.yajl_end_map = read_array_end;
13,296✔
1808
    jlu.jlu_format = this;
13,296✔
1809
    jlu.jlu_base_line = &ll;
13,296✔
1810
    jlu.jlu_line_value = sbr.get_data();
13,296✔
1811
    jlu.jlu_line_size = sbr.length();
13,296✔
1812
    jlu.jlu_handle = handle;
13,296✔
1813
    jlu.jlu_format_hits.resize(this->jlf_line_format.size());
13,296✔
1814
    if (yajl_parse(handle, line_data, sbr.length()) == yajl_status_ok
13,296✔
1815
        && yajl_complete_parse(handle) == yajl_status_ok)
13,296✔
1816
    {
1817
        if (jlu.jlu_scan_error) {
13,111✔
1818
            if (this->lf_specialized) {
19✔
1819
                if (!dst.empty()) {
10✔
1820
                    ll.set_time(
10✔
1821
                        dst.back().get_time<std::chrono::microseconds>());
10✔
1822
                }
1823
                ll.set_level(LEVEL_INVALID);
10✔
1824
                dst.emplace_back(ll);
10✔
1825
                return scan_match{0};
10✔
1826
            }
1827
            return jlu.jlu_scan_error.value();
9✔
1828
        }
1829
        if (ll.get_time<std::chrono::microseconds>().count() == 0) {
13,092✔
1830
            if (this->lf_specialized) {
8,670✔
1831
                if (!dst.empty()) {
×
1832
                    ll.set_time(
×
1833
                        dst.back().get_time<std::chrono::microseconds>());
×
1834
                }
1835
                ll.set_ignore(true);
×
1836
                dst.emplace_back(ll);
×
1837
                return scan_match{0};
×
1838
            }
1839

1840
            return scan_no_match{
8,670✔
1841
                "JSON message does not have expected timestamp property"};
8,670✔
1842
        }
1843

1844
        if (jlu.jlu_tid_frag) {
4,422✔
1845
            this->jlf_line_values.lvv_thread_id_value
1846
                = jlu.jlu_tid_frag->to_owned(
372✔
1847
                    this->jlf_line_values.lvv_allocator);
248✔
1848
            auto tid_iter = sbc.sbc_tids.insert_tid(
248✔
1849
                sbc.sbc_allocator,
1850
                jlu.jlu_tid_frag.value(),
124✔
1851
                ll.get_time<std::chrono::microseconds>());
124✔
1852
            tid_iter->second.titr_level_stats.update_msg_count(
124✔
1853
                ll.get_msg_level());
1854
            ll.merge_bloom_bits(jlu.jlu_tid_frag->bloom_bits());
124✔
1855
        } else {
1856
            auto tid_iter = sbc.sbc_tids.insert_tid(
4,298✔
1857
                sbc.sbc_allocator,
1858
                string_fragment{},
×
1859
                ll.get_time<std::chrono::microseconds>());
4,298✔
1860
            tid_iter->second.titr_level_stats.update_msg_count(
4,298✔
1861
                ll.get_msg_level());
1862
        }
1863

1864
        if (jlu.jlu_start_time && jlu.jlu_end_time && !jlu.jlu_duration) {
4,422✔
1865
            if (jlu.jlu_end_time.value() > jlu.jlu_start_time.value()) {
16✔
1866
                jlu.jlu_duration
1867
                    = jlu.jlu_end_time.value() - jlu.jlu_start_time.value();
16✔
1868
            }
1869
        }
1870

1871
        auto found_opid_desc = false;
4,422✔
1872
        if (this->elf_opid_field.empty()
4,422✔
1873
            && this->lf_opid_source.value_or(opid_source_t::from_description)
823✔
1874
                == opid_source_t::from_description
1875
            && this->lf_opid_description_def->size() == 1)
5,245✔
1876
        {
1877
            const auto& od = this->lf_opid_description_def->begin()->second;
343✔
1878
            for (const auto& desc : *od.od_descriptors) {
1,029✔
1879
                auto desc_iter
1880
                    = this->lf_desc_captures.find(desc.od_field.pp_value);
686✔
1881
                if (desc_iter == this->lf_desc_captures.end()) {
686✔
1882
                    continue;
602✔
1883
                }
1884
                jlu.jlu_opid_hasher.update(desc_iter->second);
84✔
1885
                found_opid_desc = true;
84✔
1886
            }
1887

1888
        } else if (!jlu.jlu_opid_desc_frag && !jlu.jlu_opid_frag
5,659✔
1889
                   && jlu.jlu_duration)
5,659✔
1890
        {
1891
            jlu.jlu_opid_hasher.update(sbr.to_string_fragment());
3✔
1892
        }
1893

1894
        if (jlu.jlu_opid_desc_frag || jlu.jlu_duration
6,345✔
1895
            || (found_opid_desc && this->lf_opid_description_def->size() == 1))
6,345✔
1896
        {
1897
            char buf[hasher::STRING_SIZE];
1898
            jlu.jlu_opid_hasher.to_string(buf);
2,544✔
1899
            auto opid_frag = string_fragment::from_bytes(buf, sizeof(buf) - 1);
2,544✔
1900
            auto opid_iter = sbc.sbc_opids.los_opid_ranges.find(opid_frag);
2,544✔
1901
            if (opid_iter == sbc.sbc_opids.los_opid_ranges.end()) {
2,544✔
1902
                jlu.jlu_opid_frag = opid_frag.to_owned(sbc.sbc_allocator);
2,532✔
1903
            } else {
1904
                jlu.jlu_opid_frag = opid_iter->first;
12✔
1905
            }
1906
        }
1907

1908
        if (jlu.jlu_opid_frag) {
4,422✔
1909
            ll.merge_bloom_bits(jlu.jlu_opid_frag->bloom_bits());
3,588✔
1910
            this->jlf_line_values.lvv_opid_value
1911
                = jlu.jlu_opid_frag->to_string();
3,588✔
1912
            this->jlf_line_values.lvv_opid_provenance
1913
                = logline_value_vector::opid_provenance::file;
3,588✔
1914
            auto opid_iter = sbc.sbc_opids.insert_op(
3,588✔
1915
                sbc.sbc_allocator,
1916
                jlu.jlu_opid_frag.value(),
3,588✔
1917
                ll.get_time<std::chrono::microseconds>(),
3,588✔
1918
                this->lf_timestamp_point_of_reference,
1919
                jlu.jlu_duration.value_or(1us));
3,588✔
1920
            opid_iter->second.otr_level_stats.update_msg_count(
3,588✔
1921
                ll.get_msg_level());
1922
            auto& elems = opid_iter->second.otr_description.lod_elements;
3,588✔
1923
            if (jlu.jlu_opid_desc_frag && elems.empty()) {
3,588✔
1924
                elems.insert(0,
×
1925
                             fmt::format(FMT_STRING(" {}"),
9,972✔
1926
                                         jlu.jlu_opid_desc_frag.value()));
1927
            }
1928

1929
            if (jlu.jlu_subid) {
3,588✔
1930
                auto subid_frag
1931
                    = string_fragment::from_str(jlu.jlu_subid.value());
×
1932

1933
                auto* ostr = sbc.sbc_opids.sub_op_in_use(
×
1934
                    sbc.sbc_allocator,
1935
                    opid_iter,
1936
                    subid_frag,
1937
                    ll.get_time<std::chrono::microseconds>(),
×
1938
                    ll.get_msg_level());
1939
                if (ostr != nullptr && ostr->ostr_description.empty()) {
×
1940
                    log_op_description sub_desc;
×
1941
                    this->update_op_description(
×
1942
                        *this->lf_subid_description_def_vec, sub_desc);
×
1943
                    if (!sub_desc.lod_elements.empty()) {
×
1944
                        auto& sub_desc_def
1945
                            = this->lf_subid_description_def_vec->at(
×
1946
                                sub_desc.lod_index.value());
×
1947
                        ostr->ostr_description
1948
                            = sub_desc_def->to_string(sub_desc.lod_elements);
×
1949
                    }
1950
                }
1951
            }
1952

1953
            auto& otr = opid_iter->second;
3,588✔
1954
            this->update_op_description(*this->lf_opid_description_def_vec,
3,588✔
1955
                                        otr.otr_description);
3,588✔
1956
        } else {
1957
            this->jlf_line_values.lvv_opid_value = std::nullopt;
834✔
1958
        }
1959

1960
        jlu.jlu_sub_line_count += this->jlf_line_format_init_count;
4,422✔
1961
        ll.set_has_ansi(jlu.jlu_has_ansi);
4,422✔
1962
        ll.set_valid_utf(jlu.jlu_valid_utf);
4,422✔
1963
        for (int lpc = 0; lpc < jlu.jlu_sub_line_count; lpc++) {
27,260✔
1964
            ll.set_sub_offset(lpc);
22,838✔
1965
            ll.set_continued(lpc > 0);
22,838✔
1966
            dst.emplace_back(ll);
22,838✔
1967
        }
1968
        this->lf_timestamp_flags = jlu.jlu_exttm.et_flags;
4,422✔
1969

1970
        if (!this->lf_specialized) {
4,422✔
1971
            static const intern_string_t ts_field
1972
                = intern_string::lookup("__timestamp__", -1);
3,804✔
1973
            static const intern_string_t level_field
1974
                = intern_string::lookup("__level__");
5,462✔
1975
            static const intern_string_t duration_field
1976
                = intern_string::lookup("__duration__");
5,462✔
1977
            for (const auto& [index, jfe] :
35,504✔
1978
                 lnav::itertools::enumerate(this->jlf_line_format))
39,308✔
1979
            {
1980
                if (jfe.jfe_type != json_log_field::VARIABLE
84,591✔
1981
                    || jfe.jfe_value.pp_value == ts_field
20,515✔
1982
                    || jfe.jfe_value.pp_value == level_field
16,792✔
1983
                    || jfe.jfe_value.pp_value == duration_field
15,830✔
1984
                    || jfe.jfe_default_value != "-")
52,215✔
1985
                {
1986
                    continue;
21,191✔
1987
                }
1988
                if (!jlu.jlu_format_hits[index]) {
10,509✔
1989
                    jlu.jlu_strikes += 1;
9,956✔
1990
                }
1991
            }
1992
        }
1993
    } else {
1994
        unsigned char* msg;
1995
        int line_count = 2;
185✔
1996

1997
        msg = yajl_get_error(
370✔
1998
            handle, 1, (const unsigned char*) sbr.get_data(), sbr.length());
185✔
1999
        if (msg != nullptr) {
185✔
2000
            auto msg_frag = string_fragment::from_c_str(msg);
185✔
2001
            log_debug("Unable to parse line at offset %lld: %.*s",
185✔
2002
                      li.li_file_range.fr_offset,
2003
                      msg_frag.length(),
2004
                      msg_frag.data());
2005
            line_count = msg_frag.count('\n') + 1;
185✔
2006
            yajl_free_error(handle, msg);
185✔
2007
        }
2008
        if (!this->lf_specialized || dst.empty()) {
185✔
2009
            return scan_no_match{"JSON parsing failed"};
182✔
2010
        }
2011
        for (int lpc = 0; lpc < line_count; lpc++) {
15✔
2012
            log_level_t level = LEVEL_INVALID;
12✔
2013

2014
            ll.set_time(dst.back().get_timeval());
12✔
2015
            ll.set_continued(lpc > 0);
12✔
2016
            ll.set_level(level);
12✔
2017
            ll.set_sub_offset(lpc);
12✔
2018
            dst.emplace_back(ll);
12✔
2019
        }
2020
    }
2021

2022
    if (jlu.jlu_quality > 0) {
4,425✔
2023
        jlu.jlu_quality += 3000;
948✔
2024
    }
2025
    return scan_match{jlu.jlu_quality, jlu.jlu_strikes, jlu.jlu_precision};
4,425✔
2026
}
13,338✔
2027

2028
log_format::scan_result_t
2029
external_log_format::scan(logfile& lf,
852,808✔
2030
                          std::vector<logline>& dst,
2031
                          const line_info& li,
2032
                          shared_buffer_ref& sbr,
2033
                          scan_batch_context& sbc)
2034
{
2035
    if (dst.empty()) {
852,808✔
2036
        auto file_options = lf.get_file_options();
39,638✔
2037

2038
        if (file_options) {
39,638✔
2039
            this->lf_date_time.dts_default_zone
2040
                = file_options->second.fo_default_zone.pp_value;
2,431✔
2041
        } else {
2042
            this->lf_date_time.dts_default_zone = nullptr;
37,207✔
2043
        }
2044
    }
39,638✔
2045

2046
    sbc.sbc_value_stats.resize(this->elf_value_defs.size());
852,808✔
2047
    if (this->elf_type == elf_type_t::ELF_TYPE_JSON) {
852,808✔
2048
        return this->scan_json(dst, li, sbr, sbc);
94,557✔
2049
    }
2050

2051
    int curr_fmt = -1, orig_lock = sbc.sbc_pattern_locks.last_pattern_index();
758,251✔
2052
    int pat_index = orig_lock;
758,251✔
2053
    auto line_sf = sbr.to_string_fragment();
758,251✔
2054
    thread_local auto md = lnav::pcre2pp::match_data::unitialized();
758,251✔
2055
    char tmp_opid_buf[hasher::STRING_SIZE];
2056

2057
    while (::next_format(this->elf_pattern_order, curr_fmt, pat_index)) {
2,492,689✔
2058
        auto* fpat = this->elf_pattern_order[curr_fmt].get();
1,740,857✔
2059
        auto* pat = fpat->p_pcre.pp_value.get();
1,740,857✔
2060

2061
        auto found_match
2062
            = pat->capture_from(line_sf).into(md).found_p(PCRE2_NO_UTF_CHECK);
1,740,857✔
2063
        if (!found_match) {
1,740,857✔
2064
            if (!sbc.sbc_pattern_locks.empty() && pat_index != -1) {
1,734,435✔
2065
                curr_fmt = -1;
2,012✔
2066
                pat_index = -1;
2,012✔
2067
            }
2068
            continue;
1,734,438✔
2069
        }
2070

2071
        auto ts = md[fpat->p_timestamp_field_index];
6,422✔
2072
        auto level_cap = md[fpat->p_level_field_index];
6,422✔
2073
        auto opid_cap = md[fpat->p_opid_field_index];
6,422✔
2074
        const char* last;
2075
        exttm log_time_tm;
6,422✔
2076
        timeval log_tv;
2077
        uint64_t opid_bloom = 0;
6,422✔
2078
        char combined_datetime_buf[512];
2079

2080
        if (fpat->p_time_field_index != -1) {
6,422✔
2081
            auto time_cap = md[fpat->p_time_field_index];
×
2082
            if (ts && time_cap) {
×
2083
                auto ts_str_len = snprintf(combined_datetime_buf,
×
2084
                                           sizeof(combined_datetime_buf),
2085
                                           "%.*sT%.*s",
2086
                                           ts->length(),
2087
                                           ts->data(),
2088
                                           time_cap->length(),
2089
                                           time_cap->data());
2090
                ts = string_fragment::from_bytes(combined_datetime_buf,
×
2091
                                                 ts_str_len);
×
2092
            }
2093
        }
2094

2095
        auto level = this->convert_level(
6,422✔
2096
            level_cap.value_or(string_fragment::invalid()), &sbc);
6,422✔
2097

2098
        if (!ts) {
6,422✔
2099
            level = log_level_t::LEVEL_INVALID;
×
2100
        } else if ((last
6,422✔
2101
                    = this->lf_date_time.scan(ts->data(),
12,844✔
2102
                                              ts->length(),
6,422✔
2103
                                              this->get_timestamp_formats(),
2104
                                              &log_time_tm,
2105
                                              log_tv))
2106
                   == nullptr)
6,422✔
2107
        {
2108
            auto ls = this->lf_date_time.unlock();
13✔
2109
            if ((last = this->lf_date_time.scan(ts->data(),
26✔
2110
                                                ts->length(),
13✔
2111
                                                this->get_timestamp_formats(),
2112
                                                &log_time_tm,
2113
                                                log_tv))
2114
                == nullptr)
13✔
2115
            {
2116
                this->lf_date_time.relock(ls);
2✔
2117
                continue;
3✔
2118
            }
2119
            if (last != nullptr) {
11✔
2120
                auto old_flags = this->lf_timestamp_flags & DATE_TIME_SET_FLAGS;
11✔
2121
                auto new_flags = log_time_tm.et_flags & DATE_TIME_SET_FLAGS;
11✔
2122

2123
                // It is unlikely a valid timestamp would lose much
2124
                // precision.
2125
                if (new_flags != old_flags) {
11✔
2126
                    continue;
1✔
2127
                }
2128
            }
2129

2130
            log_debug("%s:%zu: date-time re-locked to %d",
10✔
2131
                      lf.get_unique_path().c_str(),
2132
                      dst.size(),
2133
                      this->lf_date_time.dts_fmt_lock);
2134
        }
2135

2136
        this->lf_timestamp_flags = log_time_tm.et_flags;
6,419✔
2137

2138
        if (!(this->lf_timestamp_flags
12,838✔
2139
              & (ETF_MILLIS_SET | ETF_MICROS_SET | ETF_NANOS_SET))
6,419✔
2140
            && !dst.empty()
5,599✔
2141
            && dst.back().get_time<std::chrono::seconds>().count()
4,813✔
2142
                == log_tv.tv_sec
4,813✔
2143
            && dst.back()
15,622✔
2144
                    .get_subsecond_time<std::chrono::milliseconds>()
10,023✔
2145
                    .count()
3,604✔
2146
                != 0)
2147
        {
2148
            auto log_ms
2149
                = dst.back().get_subsecond_time<std::chrono::microseconds>();
×
2150

2151
            log_time_tm.et_nsec
2152
                = std::chrono::duration_cast<std::chrono::nanoseconds>(log_ms)
×
2153
                      .count();
×
2154
            log_tv.tv_usec
2155
                = std::chrono::duration_cast<std::chrono::microseconds>(log_ms)
×
2156
                      .count();
×
2157
        }
2158

2159
        if (!((log_time_tm.et_flags & ETF_DAY_SET)
6,419✔
2160
              && (log_time_tm.et_flags & ETF_MONTH_SET)
6,378✔
2161
              && (log_time_tm.et_flags & ETF_YEAR_SET)))
6,378✔
2162
        {
2163
            this->check_for_new_year(dst, log_time_tm, log_tv);
1,228✔
2164
        }
2165

2166
        auto log_us = to_us(log_tv);
6,419✔
2167
        if (this->elf_opid_field.empty()
6,419✔
2168
            && !fpat->p_opid_description_field_indexes.empty())
6,419✔
2169
        {
2170
            auto empty_desc = true;
4,066✔
2171
            hasher h;
4,066✔
2172
            for (const auto& fidx : fpat->p_opid_description_field_indexes) {
12,198✔
2173
                auto desc_cap = md[fidx];
8,132✔
2174
                if (desc_cap) {
8,132✔
2175
                    h.update(desc_cap.value());
8,084✔
2176
                    empty_desc = false;
8,084✔
2177
                }
2178
            }
2179
            if (!empty_desc) {
4,066✔
2180
                h.to_string(tmp_opid_buf);
4,066✔
2181
                opid_cap = string_fragment::from_bytes(
8,132✔
2182
                    tmp_opid_buf, sizeof(tmp_opid_buf) - 1);
4,066✔
2183
            }
2184
        }
2185

2186
        auto duration_cap = md[fpat->p_duration_field_index];
6,419✔
2187
        auto start_ts_cap = md[fpat->p_start_timestamp_field_index];
6,419✔
2188
        if ((duration_cap || start_ts_cap) && !opid_cap) {
6,419✔
2189
            hasher h;
186✔
2190
            h.update(line_sf);
186✔
2191
            h.to_string(tmp_opid_buf);
186✔
2192
            opid_cap = string_fragment::from_bytes(tmp_opid_buf,
372✔
2193
                                                   sizeof(tmp_opid_buf) - 1);
186✔
2194
        }
2195
        if (opid_cap && !opid_cap->empty()) {
6,419✔
2196
            auto duration = std::chrono::microseconds{0};
5,206✔
2197
            if (duration_cap) {
5,206✔
2198
                auto from_res
2199
                    = humanize::try_from<double>(duration_cap.value());
186✔
2200
                if (from_res) {
186✔
2201
                    auto dur_secs
2202
                        = from_res.value() / this->elf_duration_divisor;
186✔
2203
                    duration = std::chrono::microseconds(
372✔
2204
                        static_cast<int64_t>(dur_secs * 1000000));
186✔
2205
                }
2206
            } else if (start_ts_cap) {
5,020✔
2207
                exttm start_tm;
×
2208
                timeval start_tv;
2209
                auto dts = this->build_time_scanner();
×
2210
                if (dts.scan(start_ts_cap->data(),
×
2211
                             start_ts_cap->length(),
×
2212
                             this->get_timestamp_formats(),
2213
                             &start_tm,
2214
                             start_tv))
2215
                {
2216
                    auto start_us = to_us(start_tv);
×
2217
                    if (log_us > start_us) {
×
2218
                        duration = log_us - start_us;
×
2219
                    }
2220
                }
2221
            }
2222
            auto opid_iter
2223
                = sbc.sbc_opids.insert_op(sbc.sbc_allocator,
10,412✔
2224
                                          opid_cap.value(),
5,206✔
2225
                                          log_us,
2226
                                          this->lf_timestamp_point_of_reference,
2227
                                          duration);
2228
            auto& otr = opid_iter->second;
5,206✔
2229

2230
            otr.otr_level_stats.update_msg_count(level);
5,206✔
2231
            if (fpat->p_subid_field_index != -1) {
5,206✔
2232
                auto subid_cap = md[fpat->p_subid_field_index];
78✔
2233
                if (subid_cap && !subid_cap->empty()) {
78✔
2234
                    auto* ostr = sbc.sbc_opids.sub_op_in_use(sbc.sbc_allocator,
234✔
2235
                                                             opid_iter,
2236
                                                             subid_cap.value(),
78✔
2237
                                                             log_us,
2238
                                                             level);
2239
                    if (ostr != nullptr && ostr->ostr_description.empty()) {
78✔
2240
                        log_op_description sub_desc;
53✔
2241
                        this->update_op_description(
53✔
2242
                            *this->lf_subid_description_def_vec,
53✔
2243
                            sub_desc,
2244
                            fpat,
2245
                            md);
2246
                        if (!sub_desc.lod_elements.empty()) {
53✔
2247
                            auto& sub_desc_def
2248
                                = this->lf_subid_description_def_vec->at(
51✔
2249
                                    sub_desc.lod_index.value());
51✔
2250
                            ostr->ostr_description = sub_desc_def->to_string(
102✔
2251
                                sub_desc.lod_elements);
51✔
2252
                        }
2253
                    }
53✔
2254
                }
2255
            }
2256
            this->update_op_description(*this->lf_opid_description_def_vec,
5,206✔
2257
                                        otr.otr_description,
5,206✔
2258
                                        fpat,
2259
                                        md);
2260
            opid_bloom = opid_cap->bloom_bits();
5,206✔
2261
        }
2262

2263
        for (const auto& ivd : fpat->p_value_by_index) {
71,118✔
2264
            if (!ivd.ivd_value_def->vd_meta.lvm_values_index) {
64,699✔
2265
                continue;
11,830✔
2266
            }
2267

2268
            ssize_t cap_size = md.capture_size(ivd.ivd_index);
52,869✔
2269
            auto& lvs = sbc.sbc_value_stats[ivd.ivd_value_def->vd_meta
52,869✔
2270
                                                .lvm_values_index.value()];
52,869✔
2271

2272
            if (cap_size > lvs.lvs_width) {
52,869✔
2273
                lvs.lvs_width = cap_size;
7,155✔
2274
            }
2275
        }
2276

2277
        for (auto value_index : fpat->p_numeric_value_indexes) {
11,486✔
2278
            const indexed_value_def& ivd = fpat->p_value_by_index[value_index];
5,067✔
2279
            const value_def& vd = *ivd.ivd_value_def;
5,067✔
2280
            auto num_cap = md[ivd.ivd_index];
5,067✔
2281

2282
            if (vd.vd_meta.lvm_identifier || vd.vd_meta.lvm_foreign_key) {
5,067✔
2283
                continue;
×
2284
            }
2285

2286
            if (num_cap && num_cap->is_valid()) {
5,067✔
2287
                const scaling_factor* scaling = nullptr;
5,001✔
2288

2289
                if (ivd.ivd_unit_field_index >= 0) {
5,001✔
2290
                    auto unit_cap = md[ivd.ivd_unit_field_index];
×
2291

2292
                    if (unit_cap && unit_cap->is_valid()) {
×
2293
                        intern_string_t unit_val
2294
                            = intern_string::lookup(unit_cap.value());
×
2295

2296
                        auto unit_iter = vd.vd_unit_scaling.find(unit_val);
×
2297
                        if (unit_iter != vd.vd_unit_scaling.end()) {
×
2298
                            const auto& sf = unit_iter->second;
×
2299

2300
                            scaling = &sf;
×
2301
                        }
2302
                    }
2303
                }
2304

2305
                std::optional<double> dvalue_opt;
5,001✔
2306
                switch (vd.vd_meta.lvm_kind) {
5,001✔
2307
                    case value_kind_t::VALUE_INTEGER: {
4,815✔
2308
                        int64_t ivalue;
2309
                        auto from_res = fast_float::from_chars(
4,815✔
2310
                            num_cap->begin(), num_cap->end(), ivalue);
2311
                        if (from_res.ec == std::errc()) {
4,815✔
2312
                            dvalue_opt = ivalue;
4,815✔
2313
                        }
2314
                        break;
4,815✔
2315
                    }
2316
                    case value_kind_t::VALUE_FLOAT: {
186✔
2317
                        auto scan_res = scn::scan_value<double>(
2318
                            num_cap->to_string_view());
186✔
2319
                        if (scan_res) {
186✔
2320
                            dvalue_opt = scan_res->value();
186✔
2321
                        }
2322
                        break;
186✔
2323
                    }
2324
                    default:
×
2325
                        break;
×
2326
                }
2327
                if (dvalue_opt) {
5,001✔
2328
                    auto dvalue = dvalue_opt.value();
5,001✔
2329
                    if (scaling != nullptr) {
5,001✔
2330
                        scaling->scale(dvalue);
×
2331
                    }
2332
                    sbc.sbc_value_stats[vd.vd_meta.lvm_values_index.value()]
5,001✔
2333
                        .add_value(dvalue);
5,001✔
2334
                }
2335
            }
2336
        }
2337

2338
        dst.emplace_back(li.li_file_range.fr_offset, log_us, level);
6,419✔
2339
        auto& new_line = dst.back();
6,419✔
2340
        new_line.merge_bloom_bits(opid_bloom);
6,419✔
2341

2342
        auto src_file_cap = md[fpat->p_src_file_field_index];
6,419✔
2343
        auto src_line_cap = md[fpat->p_src_line_field_index];
6,419✔
2344
        if (src_file_cap && src_line_cap) {
6,419✔
2345
            auto h = hasher();
310✔
2346
            h.update(this->get_name().c_str());
310✔
2347
            h.update(src_file_cap.value());
310✔
2348
            h.update(src_line_cap.value());
310✔
2349
            new_line.merge_bloom_bits(h.to_bloom_bits());
310✔
2350
            new_line.set_schema_computed(true);
310✔
2351
        }
2352
        auto thread_id_cap = md[fpat->p_thread_id_field_index];
6,419✔
2353
        if (thread_id_cap) {
6,419✔
2354
            auto tid_iter = sbc.sbc_tids.insert_tid(
2,846✔
2355
                sbc.sbc_allocator, thread_id_cap.value(), log_us);
1,423✔
2356
            tid_iter->second.titr_level_stats.update_msg_count(level);
1,423✔
2357
            new_line.merge_bloom_bits(thread_id_cap->bloom_bits());
1,423✔
2358
        } else {
2359
            auto tid_iter = sbc.sbc_tids.insert_tid(
4,996✔
2360
                sbc.sbc_allocator, string_fragment{}, log_us);
×
2361
            tid_iter->second.titr_level_stats.update_msg_count(level);
4,996✔
2362
        }
2363

2364
        if (orig_lock != curr_fmt) {
6,419✔
2365
            uint32_t lock_line;
2366

2367
            if (!this->lf_specialized && orig_lock != -1) {
689✔
2368
                log_debug("%s:%zu: changing pattern lock %d -> (%d)%s",
×
2369
                          lf.get_unique_path().c_str(),
2370
                          dst.size() - 1,
2371
                          orig_lock,
2372
                          curr_fmt,
2373
                          this->elf_pattern_order[curr_fmt]->p_name.c_str());
2374
            }
2375
            if (sbc.sbc_pattern_locks.empty()) {
689✔
2376
                lock_line = 0;
663✔
2377
            } else {
2378
                lock_line = dst.size() - 1;
26✔
2379
            }
2380
            sbc.sbc_pattern_locks.pl_lines.emplace_back(lock_line, curr_fmt);
689✔
2381
        }
2382
        return scan_match{1000};
6,419✔
2383
    }
2384

2385
    if (this->lf_specialized && !this->lf_multiline && !dst.empty()) {
751,832✔
2386
        const auto& last_line = dst.back();
1✔
2387

2388
        log_debug("%s: invalid line %zu file_offset=%" PRIu64,
1✔
2389
                  lf.get_filename().c_str(),
2390
                  dst.size(),
2391
                  li.li_file_range.fr_offset);
2392
        dst.emplace_back(li.li_file_range.fr_offset,
1✔
2393
                         last_line.get_time<std::chrono::microseconds>(),
×
2394
                         log_level_t::LEVEL_INVALID);
1✔
2395

2396
        return scan_match{0};
1✔
2397
    }
2398

2399
    return scan_no_match{"no patterns matched"};
751,831✔
2400
}
2401

2402
void
2403
external_log_format::annotate(logfile* lf,
7,819✔
2404
                              uint64_t line_number,
2405
                              string_attrs_t& sa,
2406
                              logline_value_vector& values) const
2407
{
2408
    thread_local auto md = lnav::pcre2pp::match_data::unitialized();
7,819✔
2409

2410
    auto& line = values.lvv_sbr;
7,819✔
2411
    line_range lr;
7,819✔
2412

2413
    line.erase_ansi();
7,819✔
2414
    if (this->elf_type != elf_type_t::ELF_TYPE_TEXT) {
7,819✔
2415
        if (this->jlf_cached_opts.full_message) {
836✔
2416
            values = this->jlf_line_values;
353✔
2417
            sa = this->jlf_attr_line.al_attrs;
353✔
2418
        } else {
2419
            values.lvv_sbr = this->jlf_line_values.lvv_sbr.clone();
483✔
2420
            for (const auto& llv : this->jlf_line_values.lvv_values) {
5,049✔
2421
                if (this->jlf_cached_sub_range.contains(llv.lv_origin)) {
4,566✔
2422
                    values.lvv_values.emplace_back(llv);
1,016✔
2423
                    values.lvv_values.back().lv_origin.shift(
1,016✔
2424
                        this->jlf_cached_sub_range.lr_start,
1,016✔
2425
                        -this->jlf_cached_sub_range.lr_start);
1,016✔
2426
                }
2427
            }
2428
            for (const auto& attr : this->jlf_attr_line.al_attrs) {
2,778✔
2429
                if (this->jlf_cached_sub_range.contains(attr.sa_range)) {
2,295✔
2430
                    sa.emplace_back(attr);
775✔
2431
                    sa.back().sa_range.shift(
775✔
2432
                        this->jlf_cached_sub_range.lr_start,
775✔
2433
                        -this->jlf_cached_sub_range.lr_start);
775✔
2434
                }
2435
            }
2436
            values.lvv_opid_value = this->jlf_line_values.lvv_opid_value;
483✔
2437
            values.lvv_opid_provenance
2438
                = this->jlf_line_values.lvv_opid_provenance;
483✔
2439
            values.lvv_thread_id_value
2440
                = to_owned(this->jlf_line_values.lvv_thread_id_value,
483✔
2441
                           values.lvv_allocator);
483✔
2442
            values.lvv_src_file_value = to_owned(
483✔
2443
                this->jlf_line_values.lvv_src_file_value, values.lvv_allocator);
483✔
2444
            values.lvv_src_line_value = to_owned(
483✔
2445
                this->jlf_line_values.lvv_src_line_value, values.lvv_allocator);
483✔
2446
            values.lvv_duration_value
2447
                = this->jlf_line_values.lvv_duration_value;
483✔
2448
        }
2449
        log_format::annotate(lf, line_number, sa, values);
836✔
2450
        return;
2,468✔
2451
    }
2452

2453
    if (line.empty()) {
6,983✔
2454
        return;
5✔
2455
    }
2456

2457
    values.lvv_values.reserve(this->elf_value_defs.size());
6,978✔
2458

2459
    auto lffs = lf->get_format_file_state();
6,978✔
2460
    int pat_index = lffs.lffs_pattern_locks.pattern_index_for_line(line_number);
6,978✔
2461
    const auto& pat = *this->elf_pattern_order[pat_index];
6,978✔
2462
    char tmp_opid_buf[hasher::STRING_SIZE];
2463

2464
    sa.reserve(pat.p_pcre.pp_value->get_capture_count());
6,978✔
2465
    auto match_res
2466
        = pat.p_pcre.pp_value->capture_from(line.to_string_fragment())
6,978✔
2467
              .into(md)
6,978✔
2468
              .matches(PCRE2_NO_UTF_CHECK)
13,956✔
2469
              .ignore_error();
6,978✔
2470
    if (!match_res) {
6,978✔
2471
        // A continued line still needs a body.
2472
        lr.lr_start = 0;
1,627✔
2473
        lr.lr_end = line.length();
1,627✔
2474
        sa.emplace_back(lr, SA_BODY.value());
1,627✔
2475
        if (!this->lf_multiline) {
1,627✔
2476
            auto len
2477
                = pat.p_pcre.pp_value->match_partial(line.to_string_fragment());
×
2478
            sa.emplace_back(
×
2479
                line_range{(int) len, -1},
×
2480
                SA_INVALID.value("Log line does not match any pattern"));
×
2481
        }
2482
        return;
1,627✔
2483
    }
2484

2485
    auto duration_cap = md[pat.p_duration_field_index];
5,351✔
2486

2487
    auto ts_cap = md[pat.p_timestamp_field_index];
5,351✔
2488
    if (ts_cap) {
5,351✔
2489
        sa.emplace_back(to_line_range(ts_cap.value()), L_TIMESTAMP.value());
5,351✔
2490
        values.lvv_time_value = ts_cap;
5,351✔
2491
    }
2492

2493
    auto opid_cap = md[pat.p_opid_field_index];
5,351✔
2494

2495
    if (this->elf_opid_field.empty()
5,351✔
2496
        && !pat.p_opid_description_field_indexes.empty())
5,351✔
2497
    {
2498
        auto empty_desc = true;
3,807✔
2499
        hasher h;
3,807✔
2500
        for (const auto& fidx : pat.p_opid_description_field_indexes) {
11,421✔
2501
            auto desc_cap = md[fidx];
7,614✔
2502
            if (desc_cap) {
7,614✔
2503
                h.update(desc_cap.value());
7,586✔
2504
                empty_desc = false;
7,586✔
2505
            }
2506
        }
2507
        if (!empty_desc) {
3,807✔
2508
            h.to_string(tmp_opid_buf);
3,807✔
2509
            opid_cap = string_fragment::from_bytes(tmp_opid_buf,
7,614✔
2510
                                                   sizeof(tmp_opid_buf) - 1);
3,807✔
2511
        }
2512
    } else if (duration_cap && !opid_cap) {
1,544✔
2513
        hasher h;
16✔
2514
        h.update(line.to_string_fragment());
16✔
2515
        h.to_string(tmp_opid_buf);
16✔
2516
        opid_cap = string_fragment::from_bytes(tmp_opid_buf,
32✔
2517
                                               sizeof(tmp_opid_buf) - 1);
16✔
2518
    }
2519
    if (opid_cap && !opid_cap->empty()) {
5,351✔
2520
        sa.emplace_back(to_line_range(opid_cap.value()), L_OPID.value());
4,419✔
2521
        values.lvv_opid_value = opid_cap->to_string();
4,419✔
2522
        values.lvv_opid_provenance
2523
            = logline_value_vector::opid_provenance::file;
4,419✔
2524
    }
2525

2526
    auto body_cap = md[pat.p_body_field_index];
5,351✔
2527
    auto level_cap = md[pat.p_level_field_index];
5,351✔
2528
    auto src_file_cap = md[pat.p_src_file_field_index];
5,351✔
2529
    auto src_line_cap = md[pat.p_src_line_field_index];
5,351✔
2530
    auto thread_id_cap = md[pat.p_thread_id_field_index];
5,351✔
2531

2532
    if (level_cap
5,351✔
2533
        && (!body_cap
10,666✔
2534
            || (body_cap && level_cap->sf_begin != body_cap->sf_begin)))
10,666✔
2535
    {
2536
        sa.emplace_back(to_line_range(level_cap.value()), L_LEVEL.value());
4,801✔
2537
    }
2538

2539
    if (src_file_cap) {
5,351✔
2540
        sa.emplace_back(to_line_range(src_file_cap.value()),
229✔
2541
                        SA_SRC_FILE.value());
458✔
2542
        values.lvv_src_file_value
2543
            = src_file_cap->to_owned(values.lvv_allocator);
229✔
2544
    }
2545
    if (src_line_cap) {
5,351✔
2546
        sa.emplace_back(to_line_range(src_line_cap.value()),
229✔
2547
                        SA_SRC_LINE.value());
458✔
2548
        values.lvv_src_line_value
2549
            = src_line_cap->to_owned(values.lvv_allocator);
229✔
2550
    }
2551
    if (thread_id_cap) {
5,351✔
2552
        sa.emplace_back(to_line_range(thread_id_cap.value()),
898✔
2553
                        SA_THREAD_ID.value());
1,796✔
2554
        values.lvv_thread_id_value
2555
            = thread_id_cap->to_owned(values.lvv_allocator);
898✔
2556
    }
2557
    if (duration_cap) {
5,351✔
2558
        sa.emplace_back(to_line_range(duration_cap.value()),
16✔
2559
                        SA_DURATION.value());
32✔
2560
        auto from_res = humanize::try_from<double>(duration_cap.value());
16✔
2561
        if (from_res) {
16✔
2562
            auto dur_secs = from_res.value() / this->elf_duration_divisor;
16✔
2563
            auto duration = std::chrono::microseconds(
2564
                static_cast<int64_t>(dur_secs * 1000000));
16✔
2565
            values.lvv_duration_value = duration;
16✔
2566
        }
2567
    }
2568
    if (!values.lvv_duration_value) {
5,351✔
2569
        auto start_ts_cap = md[pat.p_start_timestamp_field_index];
5,335✔
2570
        if (start_ts_cap) {
5,335✔
2571
            exttm start_tm;
×
2572
            timeval start_tv;
2573
            auto dts = this->build_time_scanner();
×
2574
            if (dts.scan(start_ts_cap->data(),
×
2575
                         start_ts_cap->length(),
×
2576
                         this->get_timestamp_formats(),
2577
                         &start_tm,
2578
                         start_tv))
2579
            {
2580
                auto start_us = to_us(start_tv);
×
2581
                auto end_us
2582
                    = (*lf)[line_number].get_time<std::chrono::microseconds>();
×
2583
                if (end_us > start_us) {
×
2584
                    values.lvv_duration_value = end_us - start_us;
×
2585
                }
2586
            }
2587
        }
2588
    }
2589

2590
    for (size_t lpc = 0; lpc < pat.p_value_by_index.size(); lpc++) {
60,574✔
2591
        const auto& ivd = pat.p_value_by_index[lpc];
55,223✔
2592
        const scaling_factor* scaling = nullptr;
55,223✔
2593
        auto cap = md[ivd.ivd_index];
55,223✔
2594
        const auto& vd = *ivd.ivd_value_def;
55,223✔
2595

2596
        if (ivd.ivd_unit_field_index >= 0) {
55,223✔
2597
            auto unit_cap = md[ivd.ivd_unit_field_index];
×
2598

2599
            if (unit_cap) {
×
2600
                intern_string_t unit_val
2601
                    = intern_string::lookup(unit_cap.value());
×
2602
                auto unit_iter = vd.vd_unit_scaling.find(unit_val);
×
2603
                if (unit_iter != vd.vd_unit_scaling.end()) {
×
2604
                    const auto& sf = unit_iter->second;
×
2605

2606
                    scaling = &sf;
×
2607
                }
2608
            }
2609
        }
2610

2611
        if (cap) {
55,223✔
2612
            if (vd.vd_meta.lvm_kind == value_kind_t::VALUE_TIMESTAMP) {
45,111✔
2613
                auto dts = this->build_time_scanner();
7✔
2614
                exttm tm;
7✔
2615
                timeval tv;
2616
                auto val_sf = cap.value();
7✔
2617

2618
                if (dts.scan(val_sf.data(),
14✔
2619
                             val_sf.length(),
7✔
2620
                             this->get_timestamp_formats(),
2621
                             &tm,
2622
                             tv,
2623
                             true))
2624
                {
2625
                    char ts[64];
2626
                    tm.et_gmtoff = tm.et_orig_gmtoff;
7✔
2627
                    auto len = dts.ftime(
7✔
2628
                        ts, sizeof(ts), this->get_timestamp_formats(), tm);
2629
                    ts[len] = '\0';
7✔
2630
                    values.lvv_values.emplace_back(vd.vd_meta,
7✔
2631
                                                   std::string{ts, len});
21✔
2632
                    values.lvv_values.back().lv_origin
7✔
2633
                        = to_line_range(cap.value());
14✔
2634
                } else {
2635
                    values.lvv_values.emplace_back(
×
2636
                        vd.vd_meta, line, to_line_range(cap.value()));
×
2637
                }
2638
            } else {
2639
                values.lvv_values.emplace_back(
90,208✔
2640
                    vd.vd_meta, line, to_line_range(cap.value()));
45,104✔
2641
                values.lvv_values.back().apply_scaling(scaling);
45,104✔
2642
            }
2643
        } else {
2644
            values.lvv_values.emplace_back(vd.vd_meta);
10,112✔
2645
        }
2646
    }
2647

2648
    if (body_cap && body_cap->is_valid()) {
5,351✔
2649
        lr = to_line_range(body_cap.value());
5,339✔
2650
    } else {
2651
        lr.lr_start = line.length();
12✔
2652
        lr.lr_end = line.length();
12✔
2653
    }
2654
    sa.emplace_back(lr, SA_BODY.value());
5,351✔
2655

2656
    log_format::annotate(lf, line_number, sa, values);
5,351✔
2657
}
2658

2659
void
2660
external_log_format::rewrite(exec_context& ec,
43✔
2661
                             shared_buffer_ref& line,
2662
                             string_attrs_t& sa,
2663
                             std::string& value_out)
2664
{
2665
    auto pg = ec.with_provenance(exec_context::format_rewrite{});
43✔
2666
    auto& values = *ec.ec_line_values;
43✔
2667

2668
    value_out.assign(line.get_data(), line.length());
43✔
2669

2670
    for (auto iter = values.lvv_values.begin(); iter != values.lvv_values.end();
251✔
2671
         ++iter)
208✔
2672
    {
2673
        if (!iter->lv_origin.is_valid()) {
208✔
2674
            log_debug("%d: not rewriting value with invalid origin -- %s",
22✔
2675
                      (int) ec.ec_top_line,
2676
                      iter->lv_meta.lvm_name.get());
2677
            continue;
178✔
2678
        }
2679

2680
        auto vd_iter = this->elf_value_defs.find(iter->lv_meta.lvm_name);
186✔
2681
        if (vd_iter == this->elf_value_defs.end()) {
186✔
2682
            log_debug("%d: not rewriting undefined value -- %s",
×
2683
                      (int) ec.ec_top_line,
2684
                      iter->lv_meta.lvm_name.get());
2685
            continue;
×
2686
        }
2687

2688
        const auto& vd = *vd_iter->second;
186✔
2689

2690
        if (vd.vd_rewriter.empty()) {
186✔
2691
            continue;
156✔
2692
        }
2693

2694
        auto _sg = ec.enter_source(
2695
            vd_iter->second->vd_rewrite_src_name, 1, vd.vd_rewriter);
30✔
2696
        std::string field_value;
30✔
2697

2698
        auto_mem<FILE> tmpout(fclose);
30✔
2699

2700
        tmpout = std::tmpfile();
30✔
2701
        if (!tmpout) {
30✔
2702
            log_error("unable to create temporary file");
×
2703
            return;
×
2704
        }
2705
        fcntl(fileno(tmpout), F_SETFD, FD_CLOEXEC);
30✔
2706
        auto fd_copy = auto_fd::dup_of(fileno(tmpout));
30✔
2707
        fd_copy.close_on_exec();
30✔
2708
        auto ec_out = std::make_pair(tmpout.release(), fclose);
30✔
2709
        {
2710
            exec_context::output_guard og(ec, "tmp", ec_out);
60✔
2711

2712
            auto exec_res = execute_any(ec, vd.vd_rewriter);
30✔
2713
            if (exec_res.isOk()) {
30✔
2714
                field_value = exec_res.unwrap();
30✔
2715
            } else {
2716
                field_value = exec_res.unwrapErr().to_attr_line().get_string();
×
2717
            }
2718
        }
30✔
2719
        struct stat st;
2720
        fstat(fd_copy.get(), &st);
30✔
2721
        if (st.st_size > 0) {
30✔
2722
            auto buf = auto_buffer::alloc(st.st_size);
2✔
2723

2724
            buf.resize(st.st_size);
2✔
2725
            pread(fd_copy.get(), buf.in(), st.st_size, 0);
2✔
2726
            field_value = buf.to_string();
2✔
2727
        }
2✔
2728
        value_out.erase(iter->lv_origin.lr_start, iter->lv_origin.length());
30✔
2729

2730
        int32_t shift_amount
2731
            = ((int32_t) field_value.length()) - iter->lv_origin.length();
30✔
2732
        auto orig_lr = iter->lv_origin;
30✔
2733
        value_out.insert(iter->lv_origin.lr_start, field_value);
30✔
2734
        for (auto shift_iter = values.lvv_values.begin();
30✔
2735
             shift_iter != values.lvv_values.end();
170✔
2736
             ++shift_iter)
140✔
2737
        {
2738
            shift_iter->lv_origin.shift_range(orig_lr, shift_amount);
140✔
2739
        }
2740
        shift_string_attrs(sa, orig_lr, shift_amount);
30✔
2741
    }
30✔
2742
}
43✔
2743

2744
static int
2745
read_json_field(yajlpp_parse_context* ypc,
231,011✔
2746
                const unsigned char* str,
2747
                size_t len,
2748
                yajl_string_props_t* props)
2749
{
2750
    auto* jlu = (json_log_userdata*) ypc->ypc_userdata;
231,011✔
2751
    timeval tv_out;
2752
    const auto frag = string_fragment::from_bytes(str, len);
231,011✔
2753
    intern_string_t field_name;
231,011✔
2754
    const auto* vd = jlu->get_field_def(ypc);
231,011✔
2755

2756
    if (vd != nullptr) {
231,011✔
2757
        field_name = vd->vd_meta.lvm_name;
22,098✔
2758
    }
2759

2760
    if (field_name.empty()) {
231,011✔
2761
        if (!jlu->jlu_format->elf_opid_field.empty()) {
208,913✔
2762
            auto path_sf = ypc->get_path_as_string_fragment();
70,123✔
2763
            if (path_sf.startswith(jlu->jlu_format->elf_opid_field.c_str())) {
70,123✔
2764
                jlu->jlu_opid_hasher.update(path_sf);
9,969✔
2765
                jlu->jlu_opid_hasher.update(frag);
9,969✔
2766
            }
2767
        }
2768
    } else if (jlu->jlu_format->lf_timestamp_field == field_name) {
22,098✔
2769
        const auto* last = jlu->jlu_format->lf_date_time.scan(
4,313✔
2770
            (const char*) str,
2771
            len,
2772
            jlu->jlu_format->get_timestamp_formats(),
4,313✔
2773
            &jlu->jlu_exttm,
2774
            tv_out);
2775
        if (last == nullptr) {
4,313✔
2776
            auto ls = jlu->jlu_format->lf_date_time.unlock();
41✔
2777
            if ((last = jlu->jlu_format->lf_date_time.scan(
41✔
2778
                     (const char*) str,
2779
                     len,
2780
                     jlu->jlu_format->get_timestamp_formats(),
41✔
2781
                     &jlu->jlu_exttm,
2782
                     tv_out))
2783
                == nullptr)
41✔
2784
            {
2785
                jlu->jlu_format->lf_date_time.relock(ls);
19✔
2786
            }
2787
            if (last != nullptr) {
41✔
2788
                auto old_flags
22✔
2789
                    = jlu->jlu_format->lf_timestamp_flags & DATE_TIME_SET_FLAGS;
22✔
2790
                auto new_flags = jlu->jlu_exttm.et_flags & DATE_TIME_SET_FLAGS;
22✔
2791

2792
                // It is unlikely a valid timestamp would lose much
2793
                // precision.
2794
                if (new_flags != old_flags) {
22✔
2795
                    last = nullptr;
×
2796
                }
2797
            }
2798
        }
2799
        if (last != nullptr) {
4,313✔
2800
            jlu->jlu_format->lf_timestamp_flags = jlu->jlu_exttm.et_flags;
4,294✔
2801
            jlu->jlu_base_line->set_time(tv_out);
4,294✔
2802
        } else {
2803
            jlu->jlu_scan_error = log_format::scan_error{fmt::format(
19✔
2804
                FMT_STRING(
76✔
2805
                    "failed to parse timestamp '{}' in string property '{}'"),
2806
                frag,
2807
                field_name)};
19✔
2808
        }
2809
    } else if (!jlu->jlu_format->lf_start_timestamp_field.empty()
17,785✔
2810
               && jlu->jlu_format->lf_start_timestamp_field == field_name)
17,785✔
2811
    {
2812
        exttm start_tm;
3✔
2813
        timeval start_tv;
2814
        const auto* last = jlu->jlu_format->lf_date_time.scan(
3✔
2815
            (const char*) str,
2816
            len,
2817
            jlu->jlu_format->get_timestamp_formats(),
3✔
2818
            &start_tm,
2819
            start_tv);
2820
        if (last != nullptr) {
3✔
2821
            jlu->jlu_start_time = to_us(start_tv);
3✔
2822
        }
2823
    } else if (jlu->jlu_format->elf_level_pointer.pp_value != nullptr) {
17,782✔
2824
        if (jlu->jlu_format->elf_level_pointer.pp_value
240✔
2825
                ->find_in(field_name.to_string_fragment(), PCRE2_NO_UTF_CHECK)
240✔
2826
                .ignore_error()
240✔
2827
                .has_value())
120✔
2828
        {
2829
            jlu->jlu_base_line->set_level(
×
2830
                jlu->jlu_format->convert_level(frag, jlu->jlu_batch_context));
×
2831
        }
2832
    }
2833
    if (!field_name.empty() && jlu->jlu_format->elf_level_field == field_name) {
231,011✔
2834
        jlu->jlu_base_line->set_level(
4,524✔
2835
            jlu->jlu_format->convert_level(frag, jlu->jlu_batch_context));
4,524✔
2836
    }
2837
    if (!field_name.empty() && jlu->jlu_format->elf_opid_field == field_name) {
231,011✔
2838
        jlu->jlu_base_line->merge_bloom_bits(frag.bloom_bits());
1,045✔
2839

2840
        auto& sbc = *jlu->jlu_batch_context;
1,045✔
2841
        auto opid_iter = sbc.sbc_opids.los_opid_ranges.find(frag);
1,045✔
2842
        if (opid_iter == sbc.sbc_opids.los_opid_ranges.end()) {
1,045✔
2843
            jlu->jlu_opid_frag = frag.to_owned(sbc.sbc_allocator);
1,015✔
2844
        } else {
2845
            jlu->jlu_opid_frag = opid_iter->first;
30✔
2846
        }
2847
    }
2848
    if (!field_name.empty()
231,011✔
2849
        && jlu->jlu_format->elf_thread_id_field == field_name)
231,011✔
2850
    {
2851
        auto& sbc = *jlu->jlu_batch_context;
×
2852
        auto tid_iter = sbc.sbc_tids.ltis_tid_ranges.find(frag);
×
2853
        if (tid_iter == sbc.sbc_tids.ltis_tid_ranges.end()) {
×
2854
            jlu->jlu_tid_frag = frag.to_owned(sbc.sbc_allocator);
×
2855
        } else {
2856
            jlu->jlu_tid_frag = tid_iter->first;
×
2857
        }
2858
    }
2859
    if (!jlu->jlu_format->elf_subid_field.empty()
231,011✔
2860
        && jlu->jlu_format->elf_subid_field == field_name)
231,011✔
2861
    {
2862
        jlu->jlu_subid = frag.to_string();
×
2863
    }
2864
    if (!field_name.empty()
231,011✔
2865
        && jlu->jlu_format->elf_duration_field == field_name)
231,011✔
2866
    {
2867
        auto from_res = humanize::try_from<double>(frag);
×
2868
        if (from_res) {
×
2869
            auto dur_secs
2870
                = from_res.value() / jlu->jlu_format->elf_duration_divisor;
×
2871
            jlu->jlu_duration
2872
                = std::max(1us,
×
2873
                           std::chrono::microseconds(
×
2874
                               static_cast<int64_t>(dur_secs * 1000000)));
2875
        }
2876
    }
2877

2878
    if (vd != nullptr && vd->vd_is_desc_field) {
231,011✔
2879
        auto frag_copy = frag.to_owned(jlu->jlu_format->lf_desc_allocator);
87✔
2880

2881
        jlu->jlu_format->lf_desc_captures.emplace(field_name, frag_copy);
87✔
2882
    }
2883

2884
    jlu->add_sub_lines_for(vd, ypc->is_level(1), std::nullopt, str, len, props);
231,011✔
2885

2886
    return 1;
231,011✔
2887
}
2888

2889
static int
2890
rewrite_json_field(yajlpp_parse_context* ypc,
49,730✔
2891
                   const unsigned char* str,
2892
                   size_t len,
2893
                   yajl_string_props_t* props)
2894
{
2895
    static const intern_string_t body_name = intern_string::lookup("body", -1);
49,730✔
2896
    auto* jlu = (json_log_userdata*) ypc->ypc_userdata;
49,730✔
2897
    intern_string_t field_name;
49,730✔
2898
    const auto* vd = jlu->get_field_def(ypc);
49,730✔
2899
    auto frag = string_fragment::from_bytes(str, len);
49,730✔
2900

2901
    if (!ypc->is_level(1) && vd == nullptr) {
49,730✔
2902
        if (!jlu->jlu_format->elf_opid_field.empty()) {
37,609✔
2903
            auto path_sf = ypc->get_path_as_string_fragment();
36,642✔
2904
            if (path_sf.startswith(jlu->jlu_format->elf_opid_field.c_str())) {
36,642✔
2905
                jlu->jlu_opid_hasher.update(path_sf);
48✔
2906
                jlu->jlu_opid_hasher.update(frag);
48✔
2907
            }
2908
        }
2909
        return 1;
37,609✔
2910
    }
2911
    if (vd != nullptr) {
12,121✔
2912
        field_name = vd->vd_meta.lvm_name;
11,273✔
2913
    } else {
2914
        field_name = ypc->get_path();
848✔
2915
    }
2916

2917
    if (jlu->jlu_format->elf_opid_field == field_name) {
12,121✔
2918
        jlu->jlu_format->jlf_line_values.lvv_opid_value = frag.to_string();
646✔
2919
        jlu->jlu_format->jlf_line_values.lvv_opid_provenance
646✔
2920
            = logline_value_vector::opid_provenance::file;
646✔
2921
    }
2922
    if (jlu->jlu_format->elf_thread_id_field == field_name) {
12,121✔
2923
        jlu->jlu_format->jlf_line_values.lvv_thread_id_value
×
2924
            = frag.to_owned(jlu->jlu_format->jlf_line_values.lvv_allocator);
×
2925
    }
2926
    if (jlu->jlu_format->lf_timestamp_field == field_name) {
12,121✔
2927
        char time_buf[64];
2928

2929
        // TODO add a timeval kind to logline_value
2930
        if (jlu->jlu_line->is_time_skewed()
2,063✔
2931
            || jlu->jlu_line->get_msg_level() == LEVEL_INVALID
2,063✔
2932
            || (jlu->jlu_format->lf_timestamp_flags
4,126✔
2933
                & (ETF_MICROS_SET | ETF_NANOS_SET | ETF_ZONE_SET)))
1,981✔
2934
        {
2935
            timeval tv;
2936

2937
            const auto* last = jlu->jlu_format->lf_date_time.scan(
2,063✔
2938
                (const char*) str,
2939
                len,
2940
                jlu->jlu_format->get_timestamp_formats(),
2,063✔
2941
                &jlu->jlu_exttm,
2942
                tv);
2943
            if (last == nullptr) {
2,063✔
2944
                auto ls = jlu->jlu_format->lf_date_time.unlock();
124✔
2945
                if ((last = jlu->jlu_format->lf_date_time.scan(
124✔
2946
                         (const char*) str,
2947
                         len,
2948
                         jlu->jlu_format->get_timestamp_formats(),
124✔
2949
                         &jlu->jlu_exttm,
2950
                         tv))
2951
                    == nullptr)
124✔
2952
                {
2953
                    jlu->jlu_format->lf_date_time.relock(ls);
66✔
2954
                    jlu->jlu_scan_error = log_format::scan_error{
66✔
2955
                        fmt::format(FMT_STRING("failed to parse timestamp "
264✔
2956
                                               "'{}' in string property '{}'"),
2957
                                    frag,
2958
                                    field_name)};
66✔
2959
                }
2960
            }
2961
            if (!jlu->jlu_subline_opts.hash_hack) {
2,063✔
2962
                if (jlu->jlu_exttm.et_flags & ETF_ZONE_SET
2,063✔
2963
                    && jlu->jlu_format->lf_date_time.dts_zoned_to_local)
1,997✔
2964
                {
2965
                    jlu->jlu_exttm.et_flags &= ~ETF_Z_IS_UTC;
1,997✔
2966
                }
2967
                jlu->jlu_exttm.et_gmtoff
2968
                    = jlu->jlu_format->lf_date_time.dts_local_offset_cache;
2,063✔
2969
            }
2970
            jlu->jlu_format->lf_date_time.ftime(
2,063✔
2971
                time_buf,
2972
                sizeof(time_buf),
2973
                jlu->jlu_format->get_timestamp_formats(),
2,063✔
2974
                jlu->jlu_exttm);
2,063✔
2975
        } else {
2976
            sql_strftime(
×
2977
                time_buf, sizeof(time_buf), jlu->jlu_line->get_timeval(), 'T');
×
2978
        }
2979
        jlu->jlu_format->jlf_line_values.lvv_time_value = frag;
2,063✔
2980
        jlu->jlu_format->jlf_line_values.lvv_values.emplace_back(
4,126✔
2981
            jlu->jlu_format->get_value_meta(field_name,
4,126✔
2982
                                            value_kind_t::VALUE_TEXT),
2983
            std::string{time_buf});
6,189✔
2984
    } else if (!jlu->jlu_format->lf_start_timestamp_field.empty()
10,058✔
2985
               && jlu->jlu_format->lf_start_timestamp_field == field_name)
10,058✔
2986
    {
2987
        exttm start_tm;
4✔
2988
        timeval start_tv;
2989
        const auto* last = jlu->jlu_format->lf_date_time.scan(
4✔
2990
            (const char*) str,
2991
            len,
2992
            jlu->jlu_format->get_timestamp_formats(),
4✔
2993
            &start_tm,
2994
            start_tv);
2995
        if (last != nullptr) {
4✔
2996
            jlu->jlu_start_time = to_us(start_tv);
4✔
2997
        }
2998
    } else if (vd != nullptr
10,054✔
2999
               && vd->vd_meta.lvm_kind == value_kind_t::VALUE_TIMESTAMP)
9,206✔
3000
    {
3001
        auto dts = jlu->jlu_format->build_time_scanner();
223✔
3002
        exttm tm;
223✔
3003
        timeval tv;
3004

3005
        if (dts.scan((const char*) str,
223✔
3006
                     len,
3007
                     jlu->jlu_format->get_timestamp_formats(),
223✔
3008
                     &tm,
3009
                     tv,
3010
                     true))
3011
        {
3012
            char ts[64];
3013
            tm.et_gmtoff = tm.et_orig_gmtoff;
223✔
3014
            auto tslen = dts.ftime(
223✔
3015
                ts, sizeof(ts), jlu->jlu_format->get_timestamp_formats(), tm);
223✔
3016
            ts[tslen] = '\0';
223✔
3017
            jlu->jlu_format->jlf_line_values.lvv_values.emplace_back(
223✔
3018
                jlu->jlu_format->get_value_meta(
446✔
3019
                    ypc, vd, value_kind_t::VALUE_TIMESTAMP),
3020
                std::string{(const char*) ts, tslen});
892✔
3021
        } else {
3022
            jlu->jlu_format->jlf_line_values.lvv_values.emplace_back(
×
3023
                jlu->jlu_format->get_value_meta(
×
3024
                    ypc, vd, value_kind_t::VALUE_TEXT),
3025
                std::string{(const char*) str, len});
×
3026
        }
3027
    } else if (jlu->jlu_shared_buffer.contains((const char*) str)) {
10,054✔
3028
        auto str_offset = (int) ((const char*) str - jlu->jlu_line_value);
9,298✔
3029
        if (field_name == jlu->jlu_format->elf_body_field) {
9,298✔
3030
            jlu->jlu_format->jlf_line_values.lvv_values.emplace_back(
883✔
3031
                logline_value_meta(body_name,
1,766✔
3032
                                   value_kind_t::VALUE_TEXT,
3033
                                   logline_value_meta::internal_column{},
×
3034
                                   jlu->jlu_format),
883✔
3035
                string_fragment::from_byte_range(
1,766✔
3036
                    jlu->jlu_shared_buffer.get_data(),
883✔
3037
                    str_offset,
3038
                    str_offset + len));
883✔
3039
        }
3040

3041
        jlu->jlu_format->jlf_line_values.lvv_values.emplace_back(
9,298✔
3042
            jlu->jlu_format->get_value_meta(ypc, vd, value_kind_t::VALUE_TEXT),
18,596✔
3043
            string_fragment::from_byte_range(jlu->jlu_shared_buffer.get_data(),
18,596✔
3044
                                             str_offset,
3045
                                             str_offset + len));
9,298✔
3046
    } else {
3047
        if (field_name == jlu->jlu_format->elf_body_field) {
533✔
3048
            jlu->jlu_format->jlf_line_values.lvv_values.emplace_back(
428✔
3049
                logline_value_meta(body_name,
856✔
3050
                                   value_kind_t::VALUE_TEXT,
3051
                                   logline_value_meta::internal_column{},
×
3052
                                   jlu->jlu_format),
428✔
3053
                std::string{(const char*) str, len});
1,712✔
3054
        }
3055

3056
        jlu->jlu_format->jlf_line_values.lvv_values.emplace_back(
533✔
3057
            jlu->jlu_format->get_value_meta(ypc, vd, value_kind_t::VALUE_TEXT),
1,066✔
3058
            std::string{(const char*) str, len});
2,132✔
3059
    }
3060
    if (vd != nullptr && vd->vd_is_desc_field
11,273✔
3061
        && jlu->jlu_format->elf_opid_field.empty())
23,394✔
3062
    {
3063
        auto frag_copy = frag.to_owned(jlu->jlu_format->lf_desc_allocator);
288✔
3064

3065
        jlu->jlu_format->lf_desc_captures.emplace(field_name, frag_copy);
288✔
3066
    }
3067

3068
    return 1;
12,121✔
3069
}
3070

3071
void
3072
external_log_format::get_subline(const log_format_file_state& lffs,
24,458✔
3073
                                 const logline& ll,
3074
                                 shared_buffer_ref& sbr,
3075
                                 subline_options opts)
3076
{
3077
    if (this->elf_type == elf_type_t::ELF_TYPE_TEXT) {
24,458✔
3078
        return;
19,569✔
3079
    }
3080

3081
    if (this->jlf_cached_offset != ll.get_offset()
4,889✔
3082
        || this->jlf_cached_opts != opts)
4,889✔
3083
    {
3084
        auto& ypc = *(this->jlf_parse_context);
2,415✔
3085
        yajl_handle handle = this->jlf_yajl_handle.get();
2,415✔
3086
        json_log_userdata jlu(sbr, nullptr);
2,415✔
3087

3088
        jlu.jlu_subline_opts = opts;
2,415✔
3089

3090
        this->lf_desc_captures.clear();
2,415✔
3091
        this->lf_desc_allocator.reset();
2,415✔
3092
        this->jlf_share_manager.invalidate_refs();
2,415✔
3093
        this->jlf_attr_line.clear();
2,415✔
3094
        this->jlf_line_values.clear();
2,415✔
3095
        this->jlf_line_offsets.clear();
2,415✔
3096

3097
        auto line_frag = sbr.to_string_fragment();
2,415✔
3098

3099
        if (!line_frag.startswith("{")) {
2,415✔
3100
            this->jlf_attr_line.al_string.assign(line_frag.data(),
93✔
3101
                                                 line_frag.length());
93✔
3102
            this->jlf_line_values.clear();
93✔
3103
            sbr.share(this->jlf_share_manager,
186✔
3104
                      this->jlf_attr_line.al_string.data(),
93✔
3105
                      this->jlf_attr_line.al_string.size());
3106
            this->jlf_line_values.lvv_sbr = sbr.clone();
93✔
3107
            this->jlf_attr_line.al_attrs.emplace_back(
93✔
3108
                line_range{0, -1},
×
3109
                SA_INVALID.value(fmt::format(
186✔
3110
                    FMT_STRING("line at offset {} is not a JSON-line"),
186✔
3111
                    ll.get_offset())));
93✔
3112
            return;
93✔
3113
        }
3114

3115
        yajl_reset(handle);
2,322✔
3116
        ypc.set_static_handler(json_log_rewrite_handlers.jpc_children[0]);
2,322✔
3117
        ypc.ypc_userdata = &jlu;
2,322✔
3118
        ypc.ypc_ignore_unused = true;
2,322✔
3119
        ypc.ypc_alt_callbacks.yajl_start_array = json_array_start_const;
2,322✔
3120
        ypc.ypc_alt_callbacks.yajl_end_array = json_array_end;
2,322✔
3121
        ypc.ypc_alt_callbacks.yajl_start_map = json_array_start_const;
2,322✔
3122
        ypc.ypc_alt_callbacks.yajl_end_map = json_array_end;
2,322✔
3123
        jlu.jlu_format = this;
2,322✔
3124
        jlu.jlu_line = &ll;
2,322✔
3125
        jlu.jlu_handle = handle;
2,322✔
3126
        jlu.jlu_line_value = sbr.get_data();
2,322✔
3127
        jlu.jlu_format_hits.resize(this->jlf_line_format.size());
2,322✔
3128

3129
        yajl_status parse_status = yajl_parse(
4,644✔
3130
            handle, (const unsigned char*) sbr.get_data(), sbr.length());
2,322✔
3131
        if (parse_status != yajl_status_ok
2,322✔
3132
            || yajl_complete_parse(handle) != yajl_status_ok
2,322✔
3133
            || jlu.jlu_scan_error)
4,644✔
3134
        {
3135
            unsigned char* msg;
3136
            std::string full_msg;
80✔
3137

3138
            msg = yajl_get_error(
160✔
3139
                handle, 1, (const unsigned char*) sbr.get_data(), sbr.length());
80✔
3140
            if (msg != nullptr) {
80✔
3141
                full_msg = fmt::format(
80✔
3142
                    FMT_STRING("[offset: {}] {}\n{}"),
160✔
3143
                    ll.get_offset(),
80✔
3144
                    fmt::string_view{sbr.get_data(), sbr.length()},
80✔
3145
                    reinterpret_cast<char*>(msg));
160✔
3146
                yajl_free_error(handle, msg);
80✔
3147
            }
3148

3149
            this->jlf_attr_line.al_string.assign(full_msg.data(),
80✔
3150
                                                 full_msg.size());
3151
            this->jlf_line_values.clear();
80✔
3152
            this->jlf_attr_line.al_attrs.emplace_back(
80✔
3153
                line_range{0, -1},
×
3154
                SA_INVALID.value(jlu.jlu_scan_error
240✔
3155
                                     ? jlu.jlu_scan_error->se_message
174✔
3156
                                     : "JSON line failed to parse"));
3157
        } else {
80✔
3158
            std::vector<logline_value>::iterator lv_iter;
2,242✔
3159
            bool used_values[this->jlf_line_values.lvv_values.size()];
4,484✔
3160
            line_range lr;
2,242✔
3161

3162
            memset(used_values, 0, sizeof(used_values));
2,242✔
3163
            for (lv_iter = this->jlf_line_values.lvv_values.begin();
2,242✔
3164
                 lv_iter != this->jlf_line_values.lvv_values.end();
20,495✔
3165
                 ++lv_iter)
18,253✔
3166
            {
3167
                lv_iter->lv_meta.lvm_format = this;
18,253✔
3168
            }
3169
            if (!this->jlf_line_values.lvv_time_value) {
2,242✔
3170
                this->jlf_line_values.lvv_time_exttm = jlu.jlu_exttm;
259✔
3171
            }
3172

3173
            if (jlu.jlu_tid_number) {
2,242✔
3174
                char tid_buf[128];
3175
                auto to_n_res = fmt::format_to_n(tid_buf,
156✔
3176
                                                 sizeof(tid_buf) - 1,
3177
                                                 FMT_STRING("{}"),
468✔
3178
                                                 jlu.jlu_tid_number.value());
3179
                *to_n_res.out = '\0';
156✔
3180
                this->jlf_line_values.lvv_thread_id_value
3181
                    = string_fragment::from_c_str(tid_buf).to_owned(
468✔
3182
                        this->jlf_line_values.lvv_allocator);
312✔
3183
            } else if (jlu.jlu_tid_frag) {
2,086✔
3184
                this->jlf_line_values.lvv_thread_id_value
3185
                    = jlu.jlu_tid_frag->to_owned(
×
3186
                        this->jlf_line_values.lvv_allocator);
×
3187
            }
3188

3189
            if (jlu.jlu_start_time && jlu.jlu_end_time && !jlu.jlu_duration) {
2,242✔
3190
                if (jlu.jlu_end_time.value() > jlu.jlu_start_time.value()) {
104✔
3191
                    jlu.jlu_duration
3192
                        = jlu.jlu_end_time.value() - jlu.jlu_start_time.value();
104✔
3193
                }
3194
            }
3195

3196
            auto use_opid_hasher = false;
2,242✔
3197
            if (this->elf_opid_field.empty()
2,242✔
3198
                && this->lf_opid_source.value_or(
1,557✔
3199
                       opid_source_t::from_description)
1,557✔
3200
                    == opid_source_t::from_description
3201
                && this->lf_opid_description_def->size() == 1)
3,799✔
3202
            {
3203
                const auto& od = this->lf_opid_description_def->begin()->second;
334✔
3204
                for (const auto& desc : *od.od_descriptors) {
1,002✔
3205
                    auto desc_iter
3206
                        = this->lf_desc_captures.find(desc.od_field.pp_value);
668✔
3207
                    if (desc_iter == this->lf_desc_captures.end()) {
668✔
3208
                        continue;
382✔
3209
                    }
3210
                    jlu.jlu_opid_hasher.update(desc_iter->second);
286✔
3211
                    use_opid_hasher = true;
286✔
3212
                }
3213
            } else if (!jlu.jlu_opid_desc_frag && !jlu.jlu_opid_frag
3,787✔
3214
                       && jlu.jlu_duration)
3,787✔
3215
            {
3216
                jlu.jlu_opid_hasher.update(line_frag);
2✔
3217
                use_opid_hasher = true;
2✔
3218
            } else if (jlu.jlu_opid_frag) {
1,906✔
3219
                this->jlf_line_values.lvv_opid_value
3220
                    = jlu.jlu_opid_frag->to_string();
×
3221
                this->jlf_line_values.lvv_opid_provenance
3222
                    = logline_value_vector::opid_provenance::file;
×
3223
            } else if (jlu.jlu_opid_desc_frag) {
1,906✔
3224
                use_opid_hasher = true;
29✔
3225
            }
3226
            if (use_opid_hasher) {
2,242✔
3227
                this->jlf_line_values.lvv_opid_value
3228
                    = jlu.jlu_opid_hasher.to_string();
174✔
3229
                this->jlf_line_values.lvv_opid_provenance
3230
                    = logline_value_vector::opid_provenance::file;
174✔
3231
            }
3232
            this->jlf_line_values.lvv_duration_value = jlu.jlu_duration;
2,242✔
3233

3234
            int sub_offset = this->jlf_line_format_init_count;
2,242✔
3235
            for (const auto& jfe : this->jlf_line_format) {
27,496✔
3236
                static const intern_string_t ts_field
3237
                    = intern_string::lookup("__timestamp__", -1);
25,254✔
3238
                static const intern_string_t level_field
3239
                    = intern_string::lookup("__level__");
25,362✔
3240
                static const intern_string_t duration_field
3241
                    = intern_string::lookup("__duration__");
25,362✔
3242
                size_t begin_size = this->jlf_attr_line.al_string.size();
25,254✔
3243

3244
                switch (jfe.jfe_type) {
25,254✔
3245
                    case json_log_field::CONSTANT:
4,894✔
3246
                        this->json_append_to_cache(
4,894✔
3247
                            jfe.jfe_default_value.c_str(),
3248
                            jfe.jfe_default_value.size());
4,894✔
3249
                        break;
4,894✔
3250
                    case json_log_field::VARIABLE:
20,360✔
3251
                        lv_iter = find_if(
20,360✔
3252
                            this->jlf_line_values.lvv_values.begin(),
3253
                            this->jlf_line_values.lvv_values.end(),
3254
                            logline_value_name_cmp(&jfe.jfe_value.pp_value));
3255
                        if (lv_iter != this->jlf_line_values.lvv_values.end()) {
20,360✔
3256
                            auto str = lv_iter->to_string();
8,488✔
3257
                            value_def* vd = nullptr;
8,488✔
3258

3259
                            if (lv_iter->lv_meta.lvm_values_index) {
8,488✔
3260
                                vd = this->elf_value_def_order
3261
                                         [lv_iter->lv_meta.lvm_values_index
8,488✔
3262
                                              .value()]
8,488✔
3263
                                             .get();
8,488✔
3264
                            }
3265
                            while (endswith(str, "\n")) {
8,782✔
3266
                                str.pop_back();
294✔
3267
                            }
3268
                            size_t nl_pos = str.find('\n');
8,488✔
3269

3270
                            if (!jfe.jfe_prefix.empty()) {
8,488✔
3271
                                this->json_append_to_cache(jfe.jfe_prefix);
3,678✔
3272
                            }
3273
                            lr.lr_start = this->jlf_attr_line.al_string.size();
8,488✔
3274

3275
                            if ((int) str.size() > jfe.jfe_max_width) {
8,488✔
3276
                                switch (jfe.jfe_overflow) {
232✔
3277
                                    case json_format_element::overflow_t::
173✔
3278
                                        ABBREV: {
3279
                                        size_t new_size
3280
                                            = abbreviate_str(&str[0],
173✔
3281
                                                             str.size(),
3282
                                                             jfe.jfe_max_width);
173✔
3283
                                        str.resize(new_size);
173✔
3284
                                        this->json_append(lffs, jfe, vd, str);
173✔
3285
                                        break;
173✔
3286
                                    }
3287
                                    case json_format_element::overflow_t::
53✔
3288
                                        TRUNCATE: {
3289
                                        this->json_append_to_cache(
53✔
3290
                                            str.c_str(), jfe.jfe_max_width);
53✔
3291
                                        break;
53✔
3292
                                    }
3293
                                    case json_format_element::overflow_t::
6✔
3294
                                        DOTDOT: {
3295
                                        size_t middle
6✔
3296
                                            = (jfe.jfe_max_width / 2) - 1;
6✔
3297
                                        this->json_append_to_cache(str.c_str(),
6✔
3298
                                                                   middle);
3299
                                        this->json_append_to_cache("..", 2);
6✔
3300
                                        size_t rest
6✔
3301
                                            = (jfe.jfe_max_width - middle - 2);
6✔
3302
                                        this->json_append_to_cache(
12✔
3303
                                            str.c_str() + str.size() - rest,
6✔
3304
                                            rest);
3305
                                        break;
6✔
3306
                                    }
3307
                                    case json_format_element::overflow_t::
×
3308
                                        LASTWORD: {
3309
                                        size_t new_size
3310
                                            = last_word_str(&str[0],
×
3311
                                                            str.size(),
3312
                                                            jfe.jfe_max_width);
×
3313
                                        str.resize(new_size);
×
3314
                                        this->json_append(lffs, jfe, vd, str);
×
3315
                                        break;
×
3316
                                    }
3317
                                }
3318
                            } else {
3319
                                sub_offset
3320
                                    += std::count(str.begin(), str.end(), '\n');
8,256✔
3321
                                if (vd != nullptr
8,256✔
3322
                                    && vd->vd_meta.lvm_kind
8,256✔
3323
                                        == value_kind_t::VALUE_JSON)
3324
                                {
3325
                                    auto json_al = attr_line_t();
24✔
3326
                                    json_al.append(str);
24✔
3327
                                    highlight_syntax(text_format_t::TF_JSON,
24✔
3328
                                                     json_al,
3329
                                                     std::nullopt);
3330
                                    this->jlf_attr_line.append(json_al);
24✔
3331
                                } else {
24✔
3332
                                    this->json_append(lffs, jfe, vd, str);
8,232✔
3333
                                }
3334
                            }
3335

3336
                            if (nl_pos == std::string::npos
8,488✔
3337
                                || opts.full_message)
3✔
3338
                            {
3339
                                lr.lr_end
3340
                                    = this->jlf_attr_line.al_string.size();
8,485✔
3341
                            } else {
3342
                                lr.lr_end = lr.lr_start + nl_pos;
3✔
3343
                            }
3344

3345
                            if (lv_iter->lv_meta.lvm_name
8,488✔
3346
                                == this->lf_timestamp_field)
8,488✔
3347
                            {
3348
                                this->jlf_attr_line.al_attrs.emplace_back(
1,068✔
3349
                                    lr, L_TIMESTAMP.value());
2,136✔
3350
                            } else if (lv_iter->lv_meta.lvm_name
7,420✔
3351
                                       == this->elf_body_field)
7,420✔
3352
                            {
3353
                                this->jlf_attr_line.al_attrs.emplace_back(
1,250✔
3354
                                    lr, SA_BODY.value());
2,500✔
3355
                            } else if (lv_iter->lv_meta.lvm_name
6,170✔
3356
                                       == this->elf_src_loc_field)
6,170✔
3357
                            {
3358
                                size_t digits = 0;
×
3359
                                for (auto str_iter = str.rbegin();
×
3360
                                     str_iter != str.rend();
×
3361
                                     ++str_iter)
×
3362
                                {
3363
                                    if (isdigit(*str_iter)) {
×
3364
                                        digits += 1;
×
3365
                                    } else {
3366
                                        break;
×
3367
                                    }
3368
                                }
3369
                                auto diff = str.size() - digits;
×
3370
                                auto file_lr = lr;
×
3371
                                file_lr.lr_end -= digits;
×
3372
                                auto line_lr = lr;
×
3373
                                line_lr.lr_start += diff;
×
3374
                                if (digits > 0) {
×
3375
                                    file_lr.lr_end -= 1;
×
3376
                                }
3377
                                this->jlf_attr_line.al_attrs.emplace_back(
×
3378
                                    lr, SA_SRC_LOC.value());
×
3379
                                this->jlf_attr_line.al_attrs.emplace_back(
×
3380
                                    file_lr, SA_SRC_FILE.value());
×
3381
                                this->jlf_line_values.lvv_src_file_value
3382
                                    = this->jlf_attr_line
3383
                                          .to_string_fragment(
×
3384
                                              this->jlf_attr_line.al_attrs
3385
                                                  .back())
×
3386
                                          .to_owned(this->jlf_line_values
×
3387
                                                        .lvv_allocator);
×
3388
                                this->jlf_attr_line.al_attrs.emplace_back(
×
3389
                                    line_lr, SA_SRC_LINE.value());
×
3390
                                this->jlf_line_values.lvv_src_line_value
3391
                                    = this->jlf_attr_line
3392
                                          .to_string_fragment(
×
3393
                                              this->jlf_attr_line.al_attrs
3394
                                                  .back())
×
3395
                                          .to_owned(this->jlf_line_values
×
3396
                                                        .lvv_allocator);
×
3397
                            } else if (lv_iter->lv_meta.lvm_name
6,170✔
3398
                                       == this->elf_src_file_field)
6,170✔
3399
                            {
3400
                                this->jlf_attr_line.al_attrs.emplace_back(
14✔
3401
                                    lr, SA_SRC_FILE.value());
28✔
3402
                                this->jlf_line_values.lvv_src_file_value
3403
                                    = this->jlf_attr_line
3404
                                          .to_string_fragment(
14✔
3405
                                              this->jlf_attr_line.al_attrs
3406
                                                  .back())
14✔
3407
                                          .to_owned(this->jlf_line_values
28✔
3408
                                                        .lvv_allocator);
28✔
3409
                            } else if (lv_iter->lv_meta.lvm_name
6,156✔
3410
                                       == this->elf_src_line_field)
6,156✔
3411
                            {
3412
                                this->jlf_attr_line.al_attrs.emplace_back(
14✔
3413
                                    lr, SA_SRC_LINE.value());
28✔
3414
                                this->jlf_line_values.lvv_src_line_value
3415
                                    = this->jlf_attr_line
3416
                                          .to_string_fragment(
14✔
3417
                                              this->jlf_attr_line.al_attrs
3418
                                                  .back())
14✔
3419
                                          .to_owned(this->jlf_line_values
28✔
3420
                                                        .lvv_allocator);
28✔
3421
                            } else if (lv_iter->lv_meta.lvm_name
6,142✔
3422
                                       == this->elf_thread_id_field)
6,142✔
3423
                            {
3424
                                this->jlf_attr_line.al_attrs.emplace_back(
105✔
3425
                                    lr, SA_THREAD_ID.value());
210✔
3426
                            } else if (lv_iter->lv_meta.lvm_name
6,037✔
3427
                                       == this->elf_duration_field)
6,037✔
3428
                            {
3429
                                this->jlf_attr_line.al_attrs.emplace_back(
×
3430
                                    lr, SA_DURATION.value());
×
3431
                            } else if (lv_iter->lv_meta.lvm_name
6,037✔
3432
                                       == this->elf_level_field)
6,037✔
3433
                            {
3434
                                this->jlf_attr_line.al_attrs.emplace_back(
1,140✔
3435
                                    lr, L_LEVEL.value());
2,280✔
3436
                            } else if (lv_iter->lv_meta.lvm_name
9,794✔
3437
                                           == this->elf_opid_field
4,897✔
3438
                                       && !lr.empty())
4,897✔
3439
                            {
3440
                                this->jlf_attr_line.al_attrs.emplace_back(
646✔
3441
                                    lr, L_OPID.value());
1,292✔
3442
                            }
3443
                            lv_iter->lv_origin = lr;
8,488✔
3444
                            lv_iter->lv_sub_offset = sub_offset;
8,488✔
3445
                            used_values[std::distance(
8,488✔
3446
                                this->jlf_line_values.lvv_values.begin(),
3447
                                lv_iter)] = true;
8,488✔
3448

3449
                            if (!jfe.jfe_suffix.empty()) {
8,488✔
3450
                                this->json_append_to_cache(jfe.jfe_suffix);
1,040✔
3451
                            }
3452
                        } else if (jfe.jfe_value.pp_value == ts_field) {
20,360✔
3453
                            line_range lr;
1,317✔
3454
                            ssize_t ts_len;
3455
                            char ts[64];
3456
                            exttm et;
1,317✔
3457

3458
                            ll.to_exttm(et);
1,317✔
3459
                            et.et_nsec += jlu.jlu_exttm.et_nsec % 1000;
1,317✔
3460
                            et.et_gmtoff = jlu.jlu_exttm.et_gmtoff;
1,317✔
3461
                            et.et_flags |= jlu.jlu_exttm.et_flags;
1,317✔
3462
                            if (!jfe.jfe_prefix.empty()) {
1,317✔
3463
                                this->json_append_to_cache(jfe.jfe_prefix);
108✔
3464
                            }
3465
                            if (jfe.jfe_ts_format.empty()) {
1,317✔
3466
                                ts_len = this->lf_date_time.ftime(
1,162✔
3467
                                    ts,
3468
                                    sizeof(ts),
3469
                                    this->get_timestamp_formats(),
3470
                                    et);
3471
                            } else {
3472
                                ts_len = ftime_fmt(ts,
155✔
3473
                                                   sizeof(ts),
3474
                                                   jfe.jfe_ts_format.c_str(),
3475
                                                   et);
3476
                            }
3477
                            lr.lr_start = this->jlf_attr_line.al_string.size();
1,317✔
3478
                            this->json_append_to_cache(ts, ts_len);
1,317✔
3479
                            lr.lr_end = this->jlf_attr_line.al_string.size();
1,317✔
3480
                            this->jlf_attr_line.al_attrs.emplace_back(
1,317✔
3481
                                lr, L_TIMESTAMP.value());
2,634✔
3482

3483
                            lv_iter = find_if(
1,317✔
3484
                                this->jlf_line_values.lvv_values.begin(),
3485
                                this->jlf_line_values.lvv_values.end(),
3486
                                logline_value_name_cmp(
3487
                                    &this->lf_timestamp_field));
1,317✔
3488
                            if (lv_iter
1,317✔
3489
                                != this->jlf_line_values.lvv_values.end())
1,317✔
3490
                            {
3491
                                used_values[distance(
1,317✔
3492
                                    this->jlf_line_values.lvv_values.begin(),
3493
                                    lv_iter)] = true;
1,317✔
3494
                            }
3495
                            if (!jfe.jfe_suffix.empty()) {
1,317✔
3496
                                this->json_append_to_cache(jfe.jfe_suffix);
108✔
3497
                            }
3498
                        } else if (jfe.jfe_value.pp_value == level_field
10,555✔
3499
                                   || jfe.jfe_value.pp_value
20,974✔
3500
                                       == this->elf_level_field)
10,419✔
3501
                        {
3502
                            auto level_name = ll.get_level_name();
136✔
3503
                            lr.lr_start = this->jlf_attr_line.al_string.size();
136✔
3504
                            this->json_append(lffs, jfe, nullptr, level_name);
136✔
3505
                            if (jfe.jfe_auto_width) {
136✔
3506
                                this->json_append_to_cache(
105✔
3507
                                    MAX_LEVEL_NAME_LEN - level_name.length());
105✔
3508
                            }
3509
                            lr.lr_end = this->jlf_attr_line.al_string.size();
136✔
3510
                            this->jlf_attr_line.al_attrs.emplace_back(
136✔
3511
                                lr, L_LEVEL.value());
272✔
3512
                        } else if (jfe.jfe_value.pp_value == duration_field) {
10,419✔
3513
                            if (this->jlf_line_values.lvv_duration_value) {
10✔
3514
                                if (!jfe.jfe_prefix.empty()) {
2✔
3515
                                    this->json_append_to_cache(jfe.jfe_prefix);
2✔
3516
                                }
3517
                                lr.lr_start
3518
                                    = this->jlf_attr_line.al_string.size();
2✔
3519
                                auto dur_str
3520
                                    = humanize::time::duration::from_tv(
2✔
3521
                                          to_timeval(
×
3522
                                              this->jlf_line_values
3523
                                                  .lvv_duration_value.value()))
2✔
3524
                                          .with_compact(false)
2✔
3525
                                          .to_string();
2✔
3526
                                this->json_append(lffs, jfe, nullptr, dur_str);
2✔
3527
                                lr.lr_end
3528
                                    = this->jlf_attr_line.al_string.size();
2✔
3529
                                this->jlf_attr_line.al_attrs.emplace_back(
2✔
3530
                                    lr, SA_DURATION.value());
4✔
3531
                                if (!jfe.jfe_suffix.empty()) {
2✔
3532
                                    this->json_append_to_cache(jfe.jfe_suffix);
×
3533
                                }
3534
                            } else if (!jfe.jfe_default_value.empty()) {
10✔
3535
                                if (!jfe.jfe_prefix.empty()) {
×
3536
                                    this->json_append_to_cache(jfe.jfe_prefix);
×
3537
                                }
3538
                                this->json_append(
×
3539
                                    lffs, jfe, nullptr, jfe.jfe_default_value);
×
3540
                                if (!jfe.jfe_suffix.empty()) {
×
3541
                                    this->json_append_to_cache(jfe.jfe_suffix);
×
3542
                                }
3543
                            }
3544
                        } else if (!jfe.jfe_default_value.empty()) {
10,409✔
3545
                            if (!jfe.jfe_prefix.empty()) {
122✔
3546
                                this->json_append_to_cache(jfe.jfe_prefix);
×
3547
                            }
3548
                            this->json_append(
122✔
3549
                                lffs, jfe, nullptr, jfe.jfe_default_value);
122✔
3550
                            if (!jfe.jfe_suffix.empty()) {
122✔
3551
                                this->json_append_to_cache(jfe.jfe_suffix);
×
3552
                            }
3553
                        }
3554

3555
                        switch (jfe.jfe_text_transform) {
20,360✔
3556
                            case json_format_element::transform_t::NONE:
20,224✔
3557
                                break;
20,224✔
3558
                            case json_format_element::transform_t::UPPERCASE:
136✔
3559
                                for (size_t cindex = begin_size; cindex
136✔
3560
                                     < this->jlf_attr_line.al_string.size();
1,104✔
3561
                                     cindex++)
3562
                                {
3563
                                    this->jlf_attr_line.al_string[cindex]
1,936✔
3564
                                        = toupper(this->jlf_attr_line
968✔
3565
                                                      .al_string[cindex]);
968✔
3566
                                }
3567
                                break;
136✔
3568
                            case json_format_element::transform_t::LOWERCASE:
×
3569
                                for (size_t cindex = begin_size; cindex
×
3570
                                     < this->jlf_attr_line.al_string.size();
×
3571
                                     cindex++)
3572
                                {
3573
                                    this->jlf_attr_line.al_string[cindex]
×
3574
                                        = tolower(this->jlf_attr_line
×
3575
                                                      .al_string[cindex]);
×
3576
                                }
3577
                                break;
×
3578
                            case json_format_element::transform_t::CAPITALIZE:
×
3579
                                for (size_t cindex = begin_size;
×
3580
                                     cindex < begin_size + 1;
×
3581
                                     cindex++)
3582
                                {
3583
                                    this->jlf_attr_line.al_string[cindex]
×
3584
                                        = toupper(this->jlf_attr_line
×
3585
                                                      .al_string[cindex]);
×
3586
                                }
3587
                                for (size_t cindex = begin_size + 1; cindex
×
3588
                                     < this->jlf_attr_line.al_string.size();
×
3589
                                     cindex++)
3590
                                {
3591
                                    this->jlf_attr_line.al_string[cindex]
×
3592
                                        = tolower(this->jlf_attr_line
×
3593
                                                      .al_string[cindex]);
×
3594
                                }
3595
                                break;
×
3596
                        }
3597
                        break;
20,360✔
3598
                }
3599
            }
3600
            this->json_append_to_cache("\n", 1);
2,242✔
3601
            sub_offset += 1;
2,242✔
3602

3603
            for (size_t lpc = 0; lpc < this->jlf_line_values.lvv_values.size();
20,495✔
3604
                 lpc++)
3605
            {
3606
                static const intern_string_t body_name
3607
                    = intern_string::lookup("body", -1);
18,253✔
3608
                auto& lv = this->jlf_line_values.lvv_values[lpc];
18,253✔
3609

3610
                if (lv.lv_meta.is_hidden() || used_values[lpc]
28,435✔
3611
                    || body_name == lv.lv_meta.lvm_name)
28,435✔
3612
                {
3613
                    continue;
16,799✔
3614
                }
3615

3616
                auto str = lv.to_string();
1,454✔
3617
                while (endswith(str, "\n")) {
1,468✔
3618
                    str.pop_back();
14✔
3619
                }
3620

3621
                lv.lv_sub_offset = sub_offset;
1,454✔
3622
                lv.lv_origin.lr_start = this->jlf_attr_line.al_string.size() + 2
1,454✔
3623
                    + lv.lv_meta.lvm_name.size() + 2;
1,454✔
3624
                auto frag = string_fragment::from_str(str);
1,454✔
3625
                while (true) {
3626
                    auto utf_scan_res = is_utf8(frag, '\n');
1,502✔
3627

3628
                    this->json_append_to_cache("  ", 2);
1,502✔
3629
                    this->json_append_to_cache(
1,502✔
3630
                        lv.lv_meta.lvm_name.to_string_fragment());
1,502✔
3631
                    this->json_append_to_cache(": ", 2);
1,502✔
3632
                    lr.lr_start = this->jlf_attr_line.al_string.size();
1,502✔
3633
                    this->json_append_to_cache(utf_scan_res.usr_valid_frag);
1,502✔
3634
                    lr.lr_end = this->jlf_attr_line.al_string.size();
1,502✔
3635
                    if (lv.lv_meta.lvm_name == this->elf_body_field) {
1,502✔
3636
                        this->jlf_attr_line.al_attrs.emplace_back(
×
3637
                            lr, SA_BODY.value());
×
3638
                    } else {
3639
                        this->jlf_attr_line.al_attrs.emplace_back(
1,502✔
3640
                            lr, SA_EXTRA_CONTENT.value());
3,004✔
3641
                    }
3642
                    this->json_append_to_cache("\n", 1);
1,502✔
3643
                    sub_offset += 1;
1,502✔
3644
                    if (utf_scan_res.usr_remaining) {
1,502✔
3645
                        frag = utf_scan_res.usr_remaining.value();
48✔
3646
                    } else {
3647
                        break;
1,454✔
3648
                    }
3649
                }
48✔
3650
                lv.lv_origin.lr_end = this->jlf_attr_line.al_string.size() - 1;
1,454✔
3651
                if (lv.lv_meta.lvm_name == this->elf_opid_field
1,454✔
3652
                    && !lv.lv_origin.empty())
1,454✔
3653
                {
3654
                    this->jlf_attr_line.al_attrs.emplace_back(lv.lv_origin,
×
3655
                                                              L_OPID.value());
×
3656
                }
3657
            }
1,454✔
3658
        }
2,242✔
3659

3660
        this->jlf_line_offsets.push_back(0);
2,322✔
3661
        for (size_t lpc = 0; lpc < this->jlf_attr_line.al_string.size(); lpc++)
283,782✔
3662
        {
3663
            if (this->jlf_attr_line.al_string[lpc] == '\n') {
281,460✔
3664
                this->jlf_line_offsets.push_back(lpc + 1);
4,868✔
3665
            }
3666
        }
3667
        this->jlf_line_offsets.push_back(this->jlf_attr_line.al_string.size());
2,322✔
3668
        this->jlf_cached_offset = ll.get_offset();
2,322✔
3669
        this->jlf_cached_opts = opts;
2,322✔
3670
    }
2,415✔
3671

3672
    off_t this_off = 0, next_off = 0;
4,796✔
3673

3674
    if (!this->jlf_line_offsets.empty()
4,796✔
3675
        && ll.get_sub_offset() < this->jlf_line_offsets.size())
4,796✔
3676
    {
3677
        require(ll.get_sub_offset() < this->jlf_line_offsets.size());
4,796✔
3678

3679
        this_off = this->jlf_line_offsets[ll.get_sub_offset()];
4,796✔
3680
        if ((ll.get_sub_offset() + 1) < (int) this->jlf_line_offsets.size()) {
4,796✔
3681
            next_off = this->jlf_line_offsets[ll.get_sub_offset() + 1];
4,796✔
3682
        } else {
3683
            next_off = this->jlf_attr_line.al_string.size();
×
3684
        }
3685
        if (next_off > 0 && this->jlf_attr_line.al_string[next_off - 1] == '\n'
4,796✔
3686
            && this_off != next_off)
9,592✔
3687
        {
3688
            next_off -= 1;
4,796✔
3689
        }
3690
    }
3691

3692
    if (opts.full_message) {
4,796✔
3693
        sbr.share(this->jlf_share_manager,
696✔
3694
                  this->jlf_attr_line.al_string.data(),
348✔
3695
                  this->jlf_attr_line.al_string.size());
3696
    } else {
3697
        sbr.share(this->jlf_share_manager,
8,896✔
3698
                  this->jlf_attr_line.al_string.data() + this_off,
4,448✔
3699
                  next_off - this_off);
4,448✔
3700
    }
3701
    sbr.get_metadata().m_valid_utf = ll.is_valid_utf();
4,796✔
3702
    sbr.get_metadata().m_has_ansi = ll.has_ansi();
4,796✔
3703
    this->jlf_cached_sub_range.lr_start = this_off;
4,796✔
3704
    this->jlf_cached_sub_range.lr_end = next_off;
4,796✔
3705
    this->jlf_line_values.lvv_sbr = sbr.clone();
4,796✔
3706
}
3707

3708
struct compiled_header_expr {
3709
    auto_mem<sqlite3_stmt> che_stmt{sqlite3_finalize};
3710
    bool che_enabled{true};
3711
};
3712

3713
struct format_header_expressions : public lnav_config_listener {
3714
    format_header_expressions() : lnav_config_listener(__FILE__) {}
1,251✔
3715

3716
    auto_sqlite3 e_db;
3717
    std::map<intern_string_t, std::map<std::string, compiled_header_expr>>
3718
        e_header_exprs;
3719
};
3720

3721
using safe_format_header_expressions = safe::Safe<format_header_expressions>;
3722

3723
static safe_format_header_expressions format_header_exprs;
3724

3725
std::optional<external_file_format>
3726
detect_mime_type(const std::filesystem::path& filename)
691✔
3727
{
3728
    uint8_t buffer[1024];
3729
    size_t buffer_size = 0;
691✔
3730

3731
    {
3732
        auto_fd fd;
691✔
3733

3734
        if ((fd = lnav::filesystem::openp(filename, O_RDONLY)) == -1) {
691✔
3735
            return std::nullopt;
×
3736
        }
3737

3738
        ssize_t rc;
3739

3740
        if ((rc = read(fd, buffer, sizeof(buffer))) == -1) {
691✔
3741
            return std::nullopt;
×
3742
        }
3743
        buffer_size = rc;
691✔
3744
    }
691✔
3745

3746
    auto hexbuf = auto_buffer::alloc(buffer_size * 2);
691✔
3747

3748
    for (size_t lpc = 0; lpc < buffer_size; lpc++) {
371,386✔
3749
        fmt::format_to(
370,695✔
3750
            std::back_inserter(hexbuf), FMT_STRING("{:02x}"), buffer[lpc]);
1,482,780✔
3751
    }
3752

3753
    safe::WriteAccess<safe_format_header_expressions> in(format_header_exprs);
691✔
3754

3755
    for (const auto& format : log_format::get_root_formats()) {
54,510✔
3756
        auto elf = std::dynamic_pointer_cast<external_log_format>(format);
53,819✔
3757
        if (elf == nullptr) {
53,819✔
3758
            continue;
3,455✔
3759
        }
3760

3761
        if (elf->elf_converter.c_header.h_exprs.he_exprs.empty()) {
50,364✔
3762
            continue;
48,982✔
3763
        }
3764

3765
        if (buffer_size < elf->elf_converter.c_header.h_size) {
1,382✔
3766
            log_debug(
110✔
3767
                "%s: file content too small (%zu) for header detection: %s",
3768
                filename.c_str(),
3769
                buffer_size,
3770
                elf->get_name().get());
3771
            continue;
110✔
3772
        }
3773
        for (const auto& hpair : elf->elf_converter.c_header.h_exprs.he_exprs) {
4,551✔
3774
            auto& he = in->e_header_exprs[elf->get_name()][hpair.first];
3,279✔
3775

3776
            if (!he.che_enabled) {
3,279✔
3777
                continue;
×
3778
            }
3779

3780
            auto* stmt = he.che_stmt.in();
3,279✔
3781

3782
            if (stmt == nullptr) {
3,279✔
3783
                continue;
×
3784
            }
3785
            sqlite3_reset(stmt);
3,279✔
3786
            auto count = sqlite3_bind_parameter_count(stmt);
3,279✔
3787
            for (int lpc = 0; lpc < count; lpc++) {
6,558✔
3788
                const auto* name = sqlite3_bind_parameter_name(stmt, lpc + 1);
3,279✔
3789

3790
                if (name[0] == '$') {
3,279✔
3791
                    const char* env_value;
3792

3793
                    if ((env_value = getenv(&name[1])) != nullptr) {
×
3794
                        sqlite3_bind_text(
×
3795
                            stmt, lpc + 1, env_value, -1, SQLITE_STATIC);
3796
                    }
3797
                    continue;
×
3798
                }
3799
                if (strcmp(name, ":header") == 0) {
3,279✔
3800
                    sqlite3_bind_text(stmt,
3,279✔
3801
                                      lpc + 1,
3802
                                      hexbuf.in(),
3,279✔
3803
                                      hexbuf.size(),
3,279✔
3804
                                      SQLITE_STATIC);
3805
                    continue;
3,279✔
3806
                }
3807
                if (strcmp(name, ":filepath") == 0) {
×
3808
                    sqlite3_bind_text(
×
3809
                        stmt, lpc + 1, filename.c_str(), -1, SQLITE_STATIC);
3810
                    continue;
×
3811
                }
3812
            }
3813

3814
            auto step_res = sqlite3_step(stmt);
3,279✔
3815

3816
            switch (step_res) {
3,279✔
3817
                case SQLITE_OK:
3,279✔
3818
                case SQLITE_DONE:
3819
                    continue;
3,279✔
3820
                case SQLITE_ROW:
×
3821
                    break;
×
3822
                default: {
×
3823
                    log_error(
×
3824
                        "failed to execute file-format header expression: "
3825
                        "%s:%s -- %s",
3826
                        elf->get_name().get(),
3827
                        hpair.first.c_str(),
3828
                        sqlite3_errmsg(in->e_db));
3829
                    he.che_enabled = false;
×
3830
                    continue;
×
3831
                }
3832
            }
3833

3834
            log_info("detected format for: %s -- %s (header-expr: %s)",
×
3835
                     filename.c_str(),
3836
                     elf->get_name().get(),
3837
                     hpair.first.c_str());
3838
            return external_file_format{
×
3839
                elf->get_name().to_string(),
×
3840
                elf->elf_converter.c_command.pp_value,
×
3841
                elf->elf_converter.c_command.pp_location.sl_source.to_string(),
×
3842
            };
3843
        }
3844
    }
53,819✔
3845

3846
    return std::nullopt;
691✔
3847
}
691✔
3848

3849
log_format::scan_result_t
3850
log_format::test_line(sample_t& sample,
×
3851
                      std::vector<lnav::console::user_message>& msgs)
3852
{
3853
    return scan_no_match{};
×
3854
}
3855

3856
log_format::scan_result_t
3857
external_log_format::test_line(sample_t& sample,
212,327✔
3858
                               std::vector<lnav::console::user_message>& msgs)
3859
{
3860
    auto lines
3861
        = string_fragment::from_str(sample.s_line.pp_value).split_lines();
212,327✔
3862

3863
    if (this->elf_type == elf_type_t::ELF_TYPE_JSON) {
212,327✔
3864
        auto alloc = ArenaAlloc::Alloc<char>{};
3,318✔
3865
        pattern_locks pats;
3,318✔
3866
        auto sbc = scan_batch_context{
3,318✔
3867
            alloc,
3868
            pats,
3869
        };
3,318✔
3870
        sbc.sbc_value_stats.resize(this->elf_value_defs.size());
3,318✔
3871
        std::vector<logline> dst;
3,318✔
3872
        auto li = line_info{
3,318✔
3873
            {0, lines[0].length()},
3,318✔
3874
        };
3,318✔
3875
        shared_buffer sb;
3,318✔
3876
        shared_buffer_ref sbr;
3,318✔
3877
        sbr.share(sb, lines[0].data(), (size_t) lines[0].length());
3,318✔
3878

3879
        return this->scan_json(dst, li, sbr, sbc);
3,318✔
3880
    }
3,318✔
3881

3882
    scan_result_t retval = scan_no_match{"no patterns matched"};
209,009✔
3883
    auto found = false;
209,009✔
3884

3885
    for (auto pat_iter = this->elf_pattern_order.begin();
209,009✔
3886
         pat_iter != this->elf_pattern_order.end();
1,544,851✔
3887
         ++pat_iter)
1,335,842✔
3888
    {
3889
        auto& pat = *(*pat_iter);
1,335,842✔
3890

3891
        if (!pat.p_pcre.pp_value) {
1,335,842✔
3892
            continue;
1,126,835✔
3893
        }
3894

3895
        auto md = pat.p_pcre.pp_value->create_match_data();
1,335,842✔
3896
        auto match_res = pat.p_pcre.pp_value->capture_from(lines[0])
1,335,842✔
3897
                             .into(md)
1,335,842✔
3898
                             .matches(PCRE2_NO_UTF_CHECK)
2,671,684✔
3899
                             .ignore_error();
1,335,842✔
3900
        if (!match_res) {
1,335,842✔
3901
            continue;
1,126,835✔
3902
        }
3903
        retval = scan_match{1000};
209,007✔
3904
        found = true;
209,007✔
3905

3906
        sample.s_matched_regexes.insert(pat.p_name.to_string());
209,007✔
3907

3908
        const auto ts_cap = md[pat.p_timestamp_field_index];
209,007✔
3909
        const auto level_cap = md[pat.p_level_field_index];
209,007✔
3910
        const char* const* custom_formats = this->get_timestamp_formats();
209,007✔
3911
        date_time_scanner dts;
209,007✔
3912
        timeval tv;
3913
        exttm tm;
209,007✔
3914

3915
        if (ts_cap && ts_cap->sf_begin == 0) {
209,007✔
3916
            pat.p_timestamp_end = ts_cap->sf_end;
130,668✔
3917
        }
3918
        const char* dts_scan_res = nullptr;
209,007✔
3919

3920
        if (ts_cap) {
209,007✔
3921
            dts_scan_res = dts.scan(
209,005✔
3922
                ts_cap->data(), ts_cap->length(), custom_formats, &tm, tv);
209,005✔
3923
        }
3924
        if (dts_scan_res != nullptr) {
209,007✔
3925
            if (dts_scan_res != ts_cap->data() + ts_cap->length()) {
209,004✔
3926
                auto match_len = dts_scan_res - ts_cap->data();
×
3927
                auto notes = attr_line_t("the used timestamp format: ");
×
3928
                if (custom_formats == nullptr) {
×
3929
                    notes.append(PTIMEC_FORMATS[dts.dts_fmt_lock].pf_fmt);
×
3930
                } else {
3931
                    notes.append(custom_formats[dts.dts_fmt_lock]);
×
3932
                }
3933
                notes.append("\n  ")
×
3934
                    .append(ts_cap.value())
×
3935
                    .append("\n")
×
3936
                    .append(2 + match_len, ' ')
×
3937
                    .append("^ matched up to here"_snippet_border);
×
3938
                auto um = lnav::console::user_message::warning(
×
3939
                              attr_line_t("timestamp was not fully matched: ")
×
3940
                                  .append_quoted(ts_cap.value()))
×
3941
                              .with_snippet(sample.s_line.to_snippet())
×
3942
                              .with_note(notes)
×
3943
                              .move();
×
3944

3945
                msgs.emplace_back(um);
×
3946
            }
3947
        } else if (!ts_cap) {
3✔
3948
            msgs.emplace_back(
2✔
3949
                lnav::console::user_message::error(
×
3950
                    attr_line_t("invalid sample log message: ")
4✔
3951
                        .append(lnav::to_json(sample.s_line.pp_value)))
4✔
3952
                    .with_reason(attr_line_t("timestamp was not captured"))
4✔
3953
                    .with_snippet(sample.s_line.to_snippet())
4✔
3954
                    .with_help(attr_line_t(
4✔
3955
                        "A timestamp needs to be captured in order for a "
3956
                        "line to be recognized as a log message")));
3957
        } else {
3958
            attr_line_t notes;
1✔
3959

3960
            if (custom_formats == nullptr) {
1✔
3961
                notes.append("the following built-in formats were tried:");
×
3962
                for (int lpc = 0; PTIMEC_FORMATS[lpc].pf_fmt != nullptr; lpc++)
×
3963
                {
3964
                    off_t off = 0;
×
3965

3966
                    PTIMEC_FORMATS[lpc].pf_func(
×
3967
                        &tm, ts_cap->data(), off, ts_cap->length());
×
3968
                    notes.append("\n  ")
×
3969
                        .append(ts_cap.value())
×
3970
                        .append("\n")
×
3971
                        .append(2 + off, ' ')
×
3972
                        .append("^ "_snippet_border)
×
3973
                        .append_quoted(
×
3974
                            lnav::roles::symbol(PTIMEC_FORMATS[lpc].pf_fmt))
×
3975
                        .append(" matched up to here"_snippet_border);
×
3976
                }
3977
            } else {
3978
                notes.append("the following custom formats were tried:");
1✔
3979
                for (int lpc = 0; custom_formats[lpc] != nullptr; lpc++) {
2✔
3980
                    off_t off = 0;
1✔
3981

3982
                    ptime_fmt(custom_formats[lpc],
1✔
3983
                              &tm,
3984
                              ts_cap->data(),
3985
                              off,
3986
                              ts_cap->length());
1✔
3987
                    notes.append("\n  ")
1✔
3988
                        .append(ts_cap.value())
1✔
3989
                        .append("\n")
1✔
3990
                        .append(2 + off, ' ')
1✔
3991
                        .append("^ "_snippet_border)
1✔
3992
                        .append_quoted(lnav::roles::symbol(custom_formats[lpc]))
2✔
3993
                        .append(" matched up to here"_snippet_border);
1✔
3994
                }
3995
            }
3996

3997
            msgs.emplace_back(
1✔
3998
                lnav::console::user_message::error(
×
3999
                    attr_line_t("invalid sample log message: ")
1✔
4000
                        .append(lnav::to_json(sample.s_line.pp_value)))
2✔
4001
                    .with_reason(attr_line_t("unrecognized timestamp -- ")
2✔
4002
                                     .append(ts_cap.value()))
1✔
4003
                    .with_snippet(sample.s_line.to_snippet())
2✔
4004
                    .with_note(notes)
1✔
4005
                    .with_help(attr_line_t("If the timestamp format is not "
2✔
4006
                                           "supported by default, you can "
4007
                                           "add a custom format with the ")
4008
                                   .append_quoted("timestamp-format"_symbol)
1✔
4009
                                   .append(" property")));
1✔
4010
        }
1✔
4011

4012
        auto level = this->convert_level(
209,007✔
4013
            level_cap.value_or(string_fragment::invalid()), nullptr);
209,007✔
4014

4015
        if (sample.s_level != LEVEL_UNKNOWN && sample.s_level != level) {
209,007✔
4016
            attr_line_t note_al;
1✔
4017

4018
            note_al.append("matched regex = ")
1✔
4019
                .append(lnav::roles::symbol(pat.p_name.to_string()))
2✔
4020
                .append("\n")
1✔
4021
                .append("captured level = ")
1✔
4022
                .append_quoted(level_cap->to_string());
1✔
4023
            if (level_cap && !this->elf_level_patterns.empty()) {
1✔
4024
                thread_local auto md = lnav::pcre2pp::match_data::unitialized();
1✔
4025

4026
                note_al.append("\nlevel regular expression match results:");
1✔
4027
                for (const auto& level_pattern : this->elf_level_patterns) {
3✔
4028
                    attr_line_t regex_al
4029
                        = level_pattern.second.lp_pcre.pp_value->get_pattern();
2✔
4030
                    lnav::snippets::regex_highlighter(
2✔
4031
                        regex_al, -1, line_range{0, (int) regex_al.length()});
2✔
4032
                    note_al.append("\n  ")
2✔
4033
                        .append(lnav::roles::symbol(
4✔
4034
                            level_pattern.second.lp_pcre.pp_path.to_string()))
4✔
4035
                        .append(" = ")
2✔
4036
                        .append(regex_al)
2✔
4037
                        .append("\n    ");
2✔
4038
                    auto match_res = level_pattern.second.lp_pcre.pp_value
2✔
4039
                                         ->capture_from(level_cap.value())
2✔
4040
                                         .into(md)
2✔
4041
                                         .matches(PCRE2_NO_UTF_CHECK)
4✔
4042
                                         .ignore_error();
2✔
4043
                    if (!match_res) {
2✔
4044
                        note_al.append(lnav::roles::warning("no match"));
1✔
4045
                        continue;
1✔
4046
                    }
4047

4048
                    note_al.append(level_cap.value())
1✔
4049
                        .append("\n    ")
1✔
4050
                        .append(md.leading().length(), ' ')
1✔
4051
                        .append("^"_snippet_border);
1✔
4052
                    if (match_res->f_all.length() > 2) {
1✔
4053
                        note_al.append(lnav::roles::snippet_border(
1✔
4054
                            std::string(match_res->f_all.length() - 2, '-')));
3✔
4055
                    }
4056
                    if (match_res->f_all.length() > 1) {
1✔
4057
                        note_al.append("^"_snippet_border);
1✔
4058
                    }
4059
                }
2✔
4060
            }
4061
            auto um
4062
                = lnav::console::user_message::error(
×
4063
                      attr_line_t("invalid sample log message: ")
1✔
4064
                          .append(lnav::to_json(sample.s_line.pp_value)))
2✔
4065
                      .with_reason(attr_line_t()
2✔
4066
                                       .append_quoted(lnav::roles::symbol(
2✔
4067
                                           level_names[level]))
1✔
4068
                                       .append(" does not match the expected "
1✔
4069
                                               "level of ")
4070
                                       .append_quoted(lnav::roles::symbol(
2✔
4071
                                           level_names[sample.s_level])))
1✔
4072
                      .with_snippet(sample.s_line.to_snippet())
2✔
4073
                      .with_note(note_al)
1✔
4074
                      .move();
1✔
4075
            if (!this->elf_level_patterns.empty()) {
1✔
4076
                um.with_help(
1✔
4077
                    attr_line_t("Level regexes are not anchored to the "
2✔
4078
                                "start/end of the string.  Prepend ")
4079
                        .append_quoted("^"_symbol)
1✔
4080
                        .append(" to the expression to match from the "
1✔
4081
                                "start of the string and append ")
4082
                        .append_quoted("$"_symbol)
1✔
4083
                        .append(" to match up to the end of the string."));
1✔
4084
            }
4085
            msgs.emplace_back(um);
1✔
4086
        }
1✔
4087

4088
        {
4089
            auto full_match_res
4090
                = pat.p_pcre.pp_value->capture_from(sample.s_line.pp_value)
209,007✔
4091
                      .into(md)
209,007✔
4092
                      .matches()
418,014✔
4093
                      .ignore_error();
209,007✔
4094
            if (!full_match_res) {
209,007✔
4095
                attr_line_t regex_al = pat.p_pcre.pp_value->get_pattern();
1✔
4096
                lnav::snippets::regex_highlighter(
1✔
4097
                    regex_al, -1, line_range{0, (int) regex_al.length()});
1✔
4098
                msgs.emplace_back(
1✔
4099
                    lnav::console::user_message::error(
×
4100
                        attr_line_t("invalid pattern: ")
1✔
4101
                            .append_quoted(
1✔
4102
                                lnav::roles::symbol(pat.p_name.to_string())))
2✔
4103
                        .with_reason("pattern does not match entire "
2✔
4104
                                     "multiline sample message")
4105
                        .with_snippet(sample.s_line.to_snippet())
2✔
4106
                        .with_note(attr_line_t()
2✔
4107
                                       .append(lnav::roles::symbol(
1✔
4108
                                           pat.p_name.to_string()))
2✔
4109
                                       .append(" = ")
1✔
4110
                                       .append(regex_al))
1✔
4111
                        .with_help(
4112
                            attr_line_t("use ").append_quoted(".*").append(
2✔
4113
                                " to match new-lines")));
4114
            } else if (static_cast<size_t>(full_match_res->f_all.length())
209,007✔
4115
                       != sample.s_line.pp_value.length())
209,006✔
4116
            {
4117
                attr_line_t regex_al = pat.p_pcre.pp_value->get_pattern();
1✔
4118
                lnav::snippets::regex_highlighter(
1✔
4119
                    regex_al, -1, line_range{0, (int) regex_al.length()});
1✔
4120
                auto match_length
4121
                    = static_cast<size_t>(full_match_res->f_all.length());
1✔
4122
                attr_line_t sample_al = sample.s_line.pp_value;
1✔
4123
                sample_al.append("\n")
1✔
4124
                    .append(match_length, ' ')
1✔
4125
                    .append("^ matched up to here"_error)
1✔
4126
                    .with_attr_for_all(VC_ROLE.value(role_t::VCR_QUOTED_CODE));
1✔
4127
                auto sample_snippet = lnav::console::snippet::from(
4128
                    sample.s_line.pp_location, sample_al);
1✔
4129
                msgs.emplace_back(
1✔
4130
                    lnav::console::user_message::error(
×
4131
                        attr_line_t("invalid pattern: ")
1✔
4132
                            .append_quoted(
1✔
4133
                                lnav::roles::symbol(pat.p_name.to_string())))
2✔
4134
                        .with_reason("pattern does not match entire "
2✔
4135
                                     "message")
4136
                        .with_snippet(sample_snippet)
1✔
4137
                        .with_note(attr_line_t()
3✔
4138
                                       .append(lnav::roles::symbol(
2✔
4139
                                           pat.p_name.to_string()))
2✔
4140
                                       .append(" = ")
1✔
4141
                                       .append(regex_al))
1✔
4142
                        .with_help("update the regular expression to fully "
4143
                                   "capture the sample message"));
4144
            }
1✔
4145
        }
4146
    }
1,335,842✔
4147

4148
    if (!found && !this->elf_pattern_order.empty()) {
209,009✔
4149
        std::vector<std::pair<ssize_t, intern_string_t>> partial_indexes;
2✔
4150
        attr_line_t notes;
2✔
4151
        size_t max_name_width = 0;
2✔
4152

4153
        for (const auto& pat_iter : this->elf_pattern_order) {
10✔
4154
            auto& pat = *pat_iter;
8✔
4155

4156
            if (!pat.p_pcre.pp_value) {
8✔
4157
                continue;
×
4158
            }
4159

4160
            partial_indexes.emplace_back(
8✔
4161
                pat.p_pcre.pp_value->match_partial(lines[0]), pat.p_name);
8✔
4162
            max_name_width = std::max(max_name_width, pat.p_name.size());
8✔
4163
        }
4164
        for (const auto& line_frag : lines) {
4✔
4165
            auto src_line = attr_line_t(line_frag.to_string());
2✔
4166
            if (!line_frag.endswith("\n")) {
2✔
4167
                src_line.append("\n");
2✔
4168
            }
4169
            src_line.with_attr_for_all(VC_ROLE.value(role_t::VCR_QUOTED_CODE));
2✔
4170
            notes.append("   ").append(src_line);
2✔
4171
            for (auto& part_pair : partial_indexes) {
10✔
4172
                if (part_pair.first >= 0
16✔
4173
                    && part_pair.first < line_frag.length())
8✔
4174
                {
4175
                    notes.append("   ")
8✔
4176
                        .append(part_pair.first, ' ')
8✔
4177
                        .append("^ "_snippet_border)
8✔
4178
                        .append(
8✔
4179
                            lnav::roles::symbol(part_pair.second.to_string()))
16✔
4180
                        .append(" matched up to here"_snippet_border)
8✔
4181
                        .append("\n");
8✔
4182
                }
4183
                part_pair.first -= line_frag.length();
8✔
4184
            }
4185
        }
2✔
4186
        notes.add_header(
2✔
4187
            "the following shows how each pattern matched this sample:\n");
4188

4189
        attr_line_t regex_note;
2✔
4190
        for (const auto& pat_iter : this->elf_pattern_order) {
10✔
4191
            if (!pat_iter->p_pcre.pp_value) {
8✔
4192
                regex_note
4193
                    .append(lnav::roles::symbol(fmt::format(
×
4194
                        FMT_STRING("{:{}}"), pat_iter->p_name, max_name_width)))
×
4195
                    .append(" is invalid");
×
4196
                continue;
×
4197
            }
4198

4199
            attr_line_t regex_al = pat_iter->p_pcre.pp_value->get_pattern();
8✔
4200
            lnav::snippets::regex_highlighter(
8✔
4201
                regex_al, -1, line_range{0, (int) regex_al.length()});
8✔
4202

4203
            regex_note
4204
                .append(lnav::roles::symbol(fmt::format(
16✔
4205
                    FMT_STRING("{:{}}"), pat_iter->p_name, max_name_width)))
24✔
4206
                .append(" = ")
8✔
4207
                .append_quoted(regex_al)
16✔
4208
                .append("\n");
8✔
4209
        }
8✔
4210

4211
        msgs.emplace_back(
2✔
4212
            lnav::console::user_message::error(
×
4213
                attr_line_t("invalid sample log message: ")
2✔
4214
                    .append(lnav::to_json(sample.s_line.pp_value)))
4✔
4215
                .with_reason("sample does not match any patterns")
4✔
4216
                .with_snippet(sample.s_line.to_snippet())
4✔
4217
                .with_note(notes.rtrim())
4✔
4218
                .with_note(regex_note));
4219
    }
2✔
4220

4221
    return retval;
209,009✔
4222
}
212,327✔
4223

4224
void
4225
external_log_format::build(std::vector<lnav::console::user_message>& errors)
60,922✔
4226
{
4227
    auto& vc = view_colors::singleton();
60,922✔
4228

4229
    if (!this->lf_timestamp_field.empty()) {
60,922✔
4230
        auto& vd = this->elf_value_defs[this->lf_timestamp_field];
60,922✔
4231
        if (vd.get() == nullptr) {
60,922✔
4232
            vd = std::make_shared<value_def>(
44,240✔
4233
                this->lf_timestamp_field,
44,240✔
4234
                value_kind_t::VALUE_TEXT,
×
4235
                logline_value_meta::internal_column{},
×
4236
                this);
44,240✔
4237
            if (this->elf_type == elf_type_t::ELF_TYPE_JSON) {
44,240✔
4238
                this->elf_value_def_order.emplace_back(vd);
7,973✔
4239
            }
4240
        }
4241
        vd->vd_meta.lvm_name = this->lf_timestamp_field;
60,922✔
4242
        vd->vd_meta.lvm_kind = value_kind_t::VALUE_TEXT;
60,922✔
4243
        vd->vd_meta.lvm_column = logline_value_meta::internal_column{};
60,922✔
4244
        vd->vd_internal = true;
60,922✔
4245

4246
        this->elf_value_defs[LOG_TIME_STR] = vd;
60,922✔
4247
    }
4248

4249
    if (!this->lf_subsecond_field.empty()) {
60,922✔
4250
        if (!this->lf_subsecond_unit.has_value()) {
103✔
4251
            errors.emplace_back(
1✔
4252
                lnav::console::user_message::error(
×
4253
                    attr_line_t()
2✔
4254
                        .append_quoted(
1✔
4255
                            lnav::roles::symbol(this->elf_name.to_string()))
2✔
4256
                        .append(" is not a valid log format"))
1✔
4257
                    .with_reason(attr_line_t()
2✔
4258
                                     .append_quoted("subsecond-units"_symbol)
1✔
4259
                                     .append(" must be set when ")
1✔
4260
                                     .append_quoted("subsecond-field"_symbol)
1✔
4261
                                     .append(" is used"))
1✔
4262
                    .with_snippets(this->get_snippets()));
2✔
4263
        } else {
4264
            auto& vd = this->elf_value_defs[this->lf_subsecond_field];
102✔
4265
            if (vd.get() == nullptr) {
102✔
4266
                vd = std::make_shared<value_def>(
102✔
4267
                    this->lf_subsecond_field,
102✔
4268
                    value_kind_t::VALUE_INTEGER,
×
4269
                    logline_value_meta::internal_column{},
×
4270
                    this);
102✔
4271
                if (this->elf_type == elf_type_t::ELF_TYPE_JSON) {
102✔
4272
                    this->elf_value_def_order.emplace_back(vd);
102✔
4273
                }
4274
            }
4275
            vd->vd_meta.lvm_name = this->lf_subsecond_field;
102✔
4276
            vd->vd_meta.lvm_column = logline_value_meta::internal_column{};
102✔
4277
            vd->vd_meta.lvm_hidden = true;
102✔
4278
            vd->vd_internal = true;
102✔
4279
        }
4280
    }
4281

4282
    if (startswith(this->elf_level_field.get(), "/")) {
60,922✔
4283
        this->elf_level_field
4284
            = intern_string::lookup(this->elf_level_field.get() + 1);
204✔
4285
    }
4286
    if (!this->elf_level_field.empty()) {
60,922✔
4287
        auto level_iter = this->elf_value_defs.find(this->elf_level_field);
60,922✔
4288
        if (level_iter == this->elf_value_defs.end()) {
60,922✔
4289
            auto& vd = this->elf_value_defs[this->elf_level_field];
28,284✔
4290
            vd = std::make_shared<value_def>(
28,284✔
4291
                this->elf_level_field,
28,284✔
4292
                value_kind_t::VALUE_TEXT,
×
4293
                logline_value_meta::internal_column{},
×
4294
                this);
28,284✔
4295
            if (this->elf_type == elf_type_t::ELF_TYPE_JSON) {
28,284✔
4296
                this->elf_value_def_order.emplace_back(vd);
2,897✔
4297
            }
4298
            vd->vd_meta.lvm_name = this->elf_level_field;
28,284✔
4299
            vd->vd_meta.lvm_column = logline_value_meta::internal_column{};
28,284✔
4300
            vd->vd_internal = true;
28,284✔
4301

4302
            if (this->elf_level_field != this->elf_body_field) {
28,284✔
4303
                this->elf_value_defs[LOG_LEVEL_STR] = vd;
27,455✔
4304
            }
4305
        } else {
4306
            if (level_iter->second->vd_meta.lvm_kind
32,638✔
4307
                != value_kind_t::VALUE_TEXT)
32,638✔
4308
            {
4309
                this->lf_level_hideable = false;
5,905✔
4310
            }
4311
            this->elf_value_defs[LOG_LEVEL_STR] = level_iter->second;
32,638✔
4312
        }
4313
    }
4314

4315
    auto opid_field_iter = this->elf_value_defs.find(LOG_OPID_STR);
60,922✔
4316
    if (opid_field_iter == this->elf_value_defs.end()) {
60,922✔
4317
        auto vd
4318
            = std::make_shared<value_def>(this->elf_opid_field,
60,922✔
4319
                                          value_kind_t::VALUE_TEXT,
×
4320
                                          logline_value_meta::internal_column{},
×
4321
                                          this);
60,922✔
4322
        if (this->elf_type == elf_type_t::ELF_TYPE_JSON) {
60,922✔
4323
            this->elf_value_def_order.emplace_back(vd);
12,220✔
4324
        }
4325
        vd->vd_meta.lvm_name = LOG_OPID_STR;
60,922✔
4326
        vd->vd_meta.lvm_kind = value_kind_t::VALUE_TEXT;
60,922✔
4327
        vd->vd_meta.lvm_column = logline_value_meta::internal_column{};
60,922✔
4328
        vd->vd_internal = true;
60,922✔
4329

4330
        this->elf_value_defs[LOG_OPID_STR] = vd;
60,922✔
4331
    }
60,922✔
4332

4333
    if (!this->elf_body_field.empty()) {
60,922✔
4334
        auto& vd = this->elf_value_defs[this->elf_body_field];
60,922✔
4335
        if (vd.get() == nullptr) {
60,922✔
4336
            vd = std::make_shared<value_def>(
48,282✔
4337
                this->elf_body_field,
48,282✔
4338
                value_kind_t::VALUE_TEXT,
×
4339
                logline_value_meta::internal_column{},
×
4340
                this);
48,282✔
4341
            if (this->elf_type == elf_type_t::ELF_TYPE_JSON) {
48,282✔
4342
                this->elf_value_def_order.emplace_back(vd);
7,143✔
4343
            }
4344
        }
4345
        vd->vd_meta.lvm_name = this->elf_body_field;
60,922✔
4346
        vd->vd_meta.lvm_kind = value_kind_t::VALUE_TEXT;
60,922✔
4347
        vd->vd_meta.lvm_column = logline_value_meta::internal_column{};
60,922✔
4348
        vd->vd_internal = true;
60,922✔
4349
    }
4350

4351
    if (!this->elf_src_file_field.empty()) {
60,922✔
4352
        auto& vd = this->elf_value_defs[this->elf_src_file_field];
8,291✔
4353
        if (vd.get() == nullptr) {
8,291✔
4354
            vd = std::make_shared<value_def>(
1✔
4355
                this->elf_src_file_field,
1✔
4356
                value_kind_t::VALUE_TEXT,
×
4357
                logline_value_meta::internal_column{},
×
4358
                this);
2✔
4359
        }
4360
        vd->vd_meta.lvm_name = this->elf_src_file_field;
8,291✔
4361
        vd->vd_meta.lvm_kind = value_kind_t::VALUE_TEXT;
8,291✔
4362
        vd->vd_meta.lvm_column = logline_value_meta::internal_column{};
8,291✔
4363
    }
4364

4365
    if (!this->elf_src_line_field.empty()) {
60,922✔
4366
        auto& vd = this->elf_value_defs[this->elf_src_line_field];
8,291✔
4367
        if (vd.get() == nullptr) {
8,291✔
4368
            vd = std::make_shared<value_def>(
1✔
4369
                this->elf_src_line_field,
1✔
4370
                value_kind_t::VALUE_INTEGER,
×
4371
                logline_value_meta::internal_column{},
×
4372
                this);
2✔
4373
        }
4374
        vd->vd_meta.lvm_name = this->elf_src_line_field;
8,291✔
4375
        vd->vd_meta.lvm_kind = value_kind_t::VALUE_INTEGER;
8,291✔
4376
        vd->vd_meta.lvm_column = logline_value_meta::internal_column{};
8,291✔
4377
    }
4378

4379
    if (!this->elf_thread_id_field.empty()) {
60,922✔
4380
        auto& vd = this->elf_value_defs[this->elf_thread_id_field];
19,068✔
4381
        if (vd.get() == nullptr) {
19,068✔
4382
            vd = std::make_shared<value_def>(
1✔
4383
                this->elf_thread_id_field,
1✔
4384
                value_kind_t::VALUE_TEXT,
×
4385
                logline_value_meta::internal_column{},
×
4386
                this);
2✔
4387
        }
4388
        vd->vd_meta.lvm_name = this->elf_thread_id_field;
19,068✔
4389
        vd->vd_meta.lvm_kind = value_kind_t::VALUE_TEXT;
19,068✔
4390
        vd->vd_meta.lvm_column = logline_value_meta::internal_column{};
19,068✔
4391
    }
4392

4393
    if (!this->elf_duration_field.empty()) {
60,922✔
4394
        auto& vd = this->elf_value_defs[this->elf_duration_field];
4,145✔
4395
        if (vd.get() == nullptr) {
4,145✔
4396
            vd = std::make_shared<value_def>(
×
4397
                this->elf_duration_field,
×
4398
                value_kind_t::VALUE_FLOAT,
×
4399
                logline_value_meta::internal_column{},
×
4400
                this);
×
4401
        }
4402
        vd->vd_meta.lvm_name = this->elf_duration_field;
4,145✔
4403
        vd->vd_meta.lvm_kind = value_kind_t::VALUE_FLOAT;
4,145✔
4404
        vd->vd_meta.lvm_column = logline_value_meta::internal_column{};
4,145✔
4405
    }
4406

4407
    for (auto& od_pair : *this->lf_opid_description_def) {
87,452✔
4408
        od_pair.second.od_name = od_pair.first;
26,530✔
4409
        od_pair.second.od_index = this->lf_opid_description_def_vec->size();
26,530✔
4410
        this->lf_opid_description_def_vec->emplace_back(&od_pair.second);
26,530✔
4411
    }
4412

4413
    for (auto& od_pair : *this->lf_subid_description_def) {
61,751✔
4414
        od_pair.second.od_name = od_pair.first;
829✔
4415
        od_pair.second.od_index = this->lf_subid_description_def_vec->size();
829✔
4416
        this->lf_subid_description_def_vec->emplace_back(&od_pair.second);
829✔
4417
    }
4418

4419
    if (!this->lf_timestamp_format.empty()) {
60,922✔
4420
        this->lf_timestamp_format.push_back(nullptr);
7,567✔
4421
    }
4422
    auto src_file_found = 0;
60,922✔
4423
    auto src_line_found = 0;
60,922✔
4424
    auto thread_id_found = 0;
60,922✔
4425
    auto duration_found = 0;
60,922✔
4426
    for (auto& elf_pattern : this->elf_patterns) {
172,734✔
4427
        auto& pat = *elf_pattern.second;
111,812✔
4428

4429
        if (pat.p_pcre.pp_value == nullptr) {
111,812✔
4430
            continue;
1✔
4431
        }
4432

4433
        if (pat.p_opid_field_index == -1
223,622✔
4434
            && this->lf_opid_source.value_or(opid_source_t::from_description)
111,811✔
4435
                == opid_source_t::from_description
4436
            && this->lf_opid_description_def->size() == 1)
223,622✔
4437
        {
4438
            const auto& opid_def
4439
                = this->lf_opid_description_def->begin()->second;
27,357✔
4440
            for (const auto& desc : *opid_def.od_descriptors) {
61,346✔
4441
                for (auto named_cap : pat.p_pcre.pp_value->get_named_captures())
396,262✔
4442
                {
4443
                    const intern_string_t name
4444
                        = intern_string::lookup(named_cap.get_name());
362,273✔
4445

4446
                    if (name == desc.od_field.pp_value) {
362,273✔
4447
                        pat.p_opid_description_field_indexes.emplace_back(
63,004✔
4448
                            named_cap.get_index());
31,502✔
4449
                    }
4450
                }
4451
            }
4452
        }
4453

4454
        for (auto named_cap : pat.p_pcre.pp_value->get_named_captures()) {
950,845✔
4455
            const intern_string_t name
4456
                = intern_string::lookup(named_cap.get_name());
839,034✔
4457

4458
            if (name == this->lf_timestamp_field) {
839,034✔
4459
                pat.p_timestamp_field_index = named_cap.get_index();
111,809✔
4460
            }
4461
            if (name == this->lf_time_field) {
839,034✔
4462
                pat.p_time_field_index = named_cap.get_index();
829✔
4463
            }
4464
            if (name == this->elf_level_field) {
839,034✔
4465
                pat.p_level_field_index = named_cap.get_index();
85,070✔
4466
            }
4467
            if (name == this->elf_opid_field) {
839,034✔
4468
                pat.p_opid_field_index = named_cap.get_index();
22,383✔
4469
            }
4470
            if (name == this->elf_subid_field) {
839,034✔
4471
                pat.p_subid_field_index = named_cap.get_index();
12,435✔
4472
            }
4473
            if (name == this->elf_body_field) {
839,034✔
4474
                pat.p_body_field_index = named_cap.get_index();
96,887✔
4475
            }
4476
            if (name == this->elf_src_file_field) {
839,034✔
4477
                pat.p_src_file_field_index = named_cap.get_index();
13,264✔
4478
                src_file_found += 1;
13,264✔
4479
            }
4480
            if (name == this->elf_src_line_field) {
839,034✔
4481
                pat.p_src_line_field_index = named_cap.get_index();
14,922✔
4482
                src_line_found += 1;
14,922✔
4483
            }
4484
            if (name == this->elf_thread_id_field) {
839,034✔
4485
                pat.p_thread_id_field_index = named_cap.get_index();
46,424✔
4486
                thread_id_found += 1;
46,424✔
4487
            }
4488
            if (name == this->elf_duration_field) {
839,034✔
4489
                pat.p_duration_field_index = named_cap.get_index();
5,803✔
4490
                duration_found += 1;
5,803✔
4491
            }
4492
            if (name == this->lf_start_timestamp_field) {
839,034✔
4493
                pat.p_start_timestamp_field_index = named_cap.get_index();
×
4494
            }
4495

4496
            auto value_iter = this->elf_value_defs.find(name);
839,034✔
4497
            if (value_iter != this->elf_value_defs.end()) {
839,034✔
4498
                auto vd = value_iter->second;
826,495✔
4499
                indexed_value_def ivd;
826,495✔
4500

4501
                ivd.ivd_index = named_cap.get_index();
826,495✔
4502
                if (!vd->vd_unit_field.empty()) {
826,495✔
4503
                    ivd.ivd_unit_field_index = pat.p_pcre.pp_value->name_index(
×
4504
                        vd->vd_unit_field.get());
×
4505
                } else {
4506
                    ivd.ivd_unit_field_index = -1;
826,495✔
4507
                }
4508
                if (!vd->vd_internal
826,495✔
4509
                    && !vd->vd_meta.lvm_column
1,417,357✔
4510
                            .is<logline_value_meta::table_column>())
590,862✔
4511
                {
4512
                    vd->vd_meta.lvm_column = logline_value_meta::table_column{
331,385✔
4513
                        this->elf_column_count++};
331,385✔
4514
                }
4515
                ivd.ivd_value_def = vd;
826,495✔
4516
                pat.p_value_by_index.push_back(ivd);
826,495✔
4517
            }
826,495✔
4518
            pat.p_value_name_to_index[name] = named_cap.get_index();
839,034✔
4519
        }
4520

4521
        stable_sort(pat.p_value_by_index.begin(), pat.p_value_by_index.end());
111,811✔
4522

4523
        for (int lpc = 0; lpc < (int) pat.p_value_by_index.size(); lpc++) {
938,306✔
4524
            auto& ivd = pat.p_value_by_index[lpc];
826,495✔
4525
            auto vd = ivd.ivd_value_def;
826,495✔
4526

4527
            if (!vd->vd_meta.lvm_foreign_key && !vd->vd_meta.lvm_identifier) {
826,495✔
4528
                switch (vd->vd_meta.lvm_kind) {
424,123✔
4529
                    case value_kind_t::VALUE_INTEGER:
57,303✔
4530
                    case value_kind_t::VALUE_FLOAT:
4531
                        pat.p_numeric_value_indexes.push_back(lpc);
57,303✔
4532
                        break;
57,303✔
4533
                    default:
366,820✔
4534
                        break;
366,820✔
4535
                }
4536
            }
4537
        }
826,495✔
4538

4539
        if (pat.p_timestamp_field_index == -1) {
111,811✔
4540
            errors.emplace_back(
2✔
4541
                lnav::console::user_message::error(
×
4542
                    attr_line_t("invalid pattern: ")
4✔
4543
                        .append_quoted(lnav::roles::symbol(pat.p_config_path)))
4✔
4544
                    .with_reason("no timestamp capture found in the pattern")
4✔
4545
                    .with_snippets(this->get_snippets())
4✔
4546
                    .with_help("all log messages need a timestamp"));
4547
        }
4548

4549
        if (!this->elf_level_field.empty() && pat.p_level_field_index == -1) {
111,811✔
4550
            log_warning("%s:level field '%s' not found in pattern",
26,741✔
4551
                        pat.p_config_path.c_str(),
4552
                        this->elf_level_field.get());
4553
        }
4554
        if (!this->elf_body_field.empty() && pat.p_body_field_index == -1) {
111,811✔
4555
            log_warning("%s:body field '%s' not found in pattern",
14,924✔
4556
                        pat.p_config_path.c_str(),
4557
                        this->elf_body_field.get());
4558
        }
4559

4560
        this->elf_pattern_order.push_back(elf_pattern.second);
111,811✔
4561
    }
4562
    if (this->elf_type == elf_type_t::ELF_TYPE_TEXT
121,844✔
4563
        && !this->elf_src_file_field.empty() && src_file_found == 0)
60,922✔
4564
    {
4565
        errors.emplace_back(
1✔
4566
            lnav::console::user_message::error(
×
4567
                attr_line_t("invalid pattern: ")
2✔
4568
                    .append_quoted(
1✔
4569
                        lnav::roles::symbol(this->elf_name.to_string())))
2✔
4570
                .with_reason("no source file capture found in the pattern")
2✔
4571
                .with_snippets(this->get_snippets())
2✔
4572
                .with_help(attr_line_t("at least one pattern needs a source "
2✔
4573
                                       "file capture named ")
4574
                               .append_quoted(this->elf_src_file_field.get())));
1✔
4575
    }
4576
    if (this->elf_type == elf_type_t::ELF_TYPE_TEXT
121,844✔
4577
        && !this->elf_src_line_field.empty() && src_line_found == 0)
60,922✔
4578
    {
4579
        errors.emplace_back(
1✔
4580
            lnav::console::user_message::error(
×
4581
                attr_line_t("invalid pattern: ")
2✔
4582
                    .append_quoted(
1✔
4583
                        lnav::roles::symbol(this->elf_name.to_string())))
2✔
4584
                .with_reason("no source line capture found in the pattern")
2✔
4585
                .with_snippets(this->get_snippets())
2✔
4586
                .with_help(attr_line_t("at least one pattern needs a source "
2✔
4587
                                       "line capture named ")
4588
                               .append_quoted(this->elf_src_line_field.get())));
1✔
4589
    }
4590
    if (this->elf_type == elf_type_t::ELF_TYPE_TEXT
121,844✔
4591
        && !this->elf_thread_id_field.empty() && thread_id_found == 0)
60,922✔
4592
    {
4593
        errors.emplace_back(
1✔
4594
            lnav::console::user_message::error(
×
4595
                attr_line_t("invalid pattern: ")
2✔
4596
                    .append_quoted(
1✔
4597
                        lnav::roles::symbol(this->elf_name.to_string())))
2✔
4598
                .with_reason("no thread ID capture found in the pattern")
2✔
4599
                .with_snippets(this->get_snippets())
2✔
4600
                .with_help(
4601
                    attr_line_t(
2✔
4602
                        "at least one pattern needs a thread ID capture named ")
4603
                        .append_quoted(this->elf_thread_id_field.get())));
1✔
4604
    }
4605
    if (this->elf_type == elf_type_t::ELF_TYPE_TEXT
121,844✔
4606
        && !this->elf_duration_field.empty() && duration_found == 0)
60,922✔
4607
    {
4608
        errors.emplace_back(
×
4609
            lnav::console::user_message::error(
×
4610
                attr_line_t("invalid pattern: ")
×
4611
                    .append_quoted(
×
4612
                        lnav::roles::symbol(this->elf_name.to_string())))
×
4613
                .with_reason("no duration capture found in the pattern")
×
4614
                .with_snippets(this->get_snippets())
×
4615
                .with_help(
4616
                    attr_line_t(
×
4617
                        "at least one pattern needs a duration capture named ")
4618
                        .append_quoted(this->elf_duration_field.get())));
×
4619
    }
4620

4621
    if (this->elf_type != elf_type_t::ELF_TYPE_TEXT) {
60,922✔
4622
        if (!this->elf_patterns.empty()) {
12,220✔
4623
            errors.emplace_back(
1✔
4624
                lnav::console::user_message::error(
×
4625
                    attr_line_t()
2✔
4626
                        .append_quoted(
1✔
4627
                            lnav::roles::symbol(this->elf_name.to_string()))
2✔
4628
                        .append(" is not a valid log format"))
1✔
4629
                    .with_reason("structured logs cannot have regexes")
2✔
4630
                    .with_snippets(this->get_snippets()));
2✔
4631
        }
4632
        if (this->elf_type == elf_type_t::ELF_TYPE_JSON) {
12,220✔
4633
            this->lf_multiline = true;
12,220✔
4634
            this->lf_structured = true;
12,220✔
4635
            this->lf_formatted_lines = true;
12,220✔
4636
            this->jlf_parse_context
4637
                = std::make_shared<yajlpp_parse_context>(this->elf_name);
12,220✔
4638
            this->jlf_yajl_handle.reset(
12,220✔
4639
                yajl_alloc(&this->jlf_parse_context->ypc_callbacks,
12,220✔
4640
                           nullptr,
4641
                           this->jlf_parse_context.get()),
12,220✔
4642
                yajl_handle_deleter());
4643
            yajl_config(
12,220✔
4644
                this->jlf_yajl_handle.get(), yajl_dont_validate_strings, 1);
4645
        }
4646
    } else {
4647
        if (this->elf_patterns.empty()) {
48,702✔
4648
            errors.emplace_back(lnav::console::user_message::error(
2✔
4649
                                    attr_line_t()
4✔
4650
                                        .append_quoted(lnav::roles::symbol(
4✔
4651
                                            this->elf_name.to_string()))
4✔
4652
                                        .append(" is not a valid log format"))
2✔
4653
                                    .with_reason("no regexes specified")
4✔
4654
                                    .with_snippets(this->get_snippets()));
4✔
4655
        }
4656
    }
4657

4658
    stable_sort(this->elf_level_pairs.begin(), this->elf_level_pairs.end());
60,922✔
4659

4660
    {
4661
        safe::WriteAccess<safe_format_header_expressions> hexprs(
4662
            format_header_exprs);
60,922✔
4663

4664
        if (hexprs->e_db.in() == nullptr) {
60,922✔
4665
            if (sqlite3_open(":memory:", hexprs->e_db.out()) != SQLITE_OK) {
829✔
4666
                log_error("unable to open memory DB");
×
4667
                return;
×
4668
            }
4669
            register_sqlite_funcs(hexprs->e_db.in(), sqlite_registration_funcs);
829✔
4670
        }
4671

4672
        for (const auto& hpair : this->elf_converter.c_header.h_exprs.he_exprs)
65,068✔
4673
        {
4674
            auto stmt_str
4675
                = fmt::format(FMT_STRING("SELECT 1 WHERE {}"), hpair.second);
12,438✔
4676
            compiled_header_expr che;
4,146✔
4677

4678
            log_info("preparing file-format header expression: %s",
4,146✔
4679
                     stmt_str.c_str());
4680
            auto retcode = sqlite3_prepare_v2(hexprs->e_db.in(),
8,292✔
4681
                                              stmt_str.c_str(),
4682
                                              stmt_str.size(),
4,146✔
4683
                                              che.che_stmt.out(),
4684
                                              nullptr);
4685
            if (retcode != SQLITE_OK) {
4,146✔
4686
                auto sql_al = attr_line_t(hpair.second)
2✔
4687
                                  .with_attr_for_all(SA_PREFORMATTED.value())
2✔
4688
                                  .with_attr_for_all(
1✔
4689
                                      VC_ROLE.value(role_t::VCR_QUOTED_CODE))
2✔
4690
                                  .move();
1✔
4691
                readline_sql_highlighter(
1✔
4692
                    sql_al, lnav::sql::dialect::sqlite, std::nullopt);
4693
                intern_string_t watch_expr_path = intern_string::lookup(
4694
                    fmt::format(FMT_STRING("/{}/converter/header/expr/{}"),
3✔
4695
                                this->elf_name,
1✔
4696
                                hpair.first));
2✔
4697
                auto snippet = lnav::console::snippet::from(
4698
                    source_location(watch_expr_path), sql_al);
1✔
4699

4700
                auto um = lnav::console::user_message::error(
2✔
4701
                              "SQL expression is invalid")
4702
                              .with_reason(sqlite3_errmsg(hexprs->e_db.in()))
2✔
4703
                              .with_snippet(snippet)
1✔
4704
                              .move();
1✔
4705

4706
                errors.emplace_back(um);
1✔
4707
                continue;
1✔
4708
            }
1✔
4709

4710
            hexprs->e_header_exprs[this->elf_name][hpair.first]
4,145✔
4711
                = std::move(che);
8,290✔
4712
        }
4,147✔
4713

4714
        if (!this->elf_converter.c_header.h_exprs.he_exprs.empty()
60,922✔
4715
            && this->elf_converter.c_command.pp_value.empty())
60,922✔
4716
        {
4717
            auto um = lnav::console::user_message::error(
2✔
4718
                          "A command is required when a converter is defined")
4719
                          .with_help(
2✔
4720
                              "The converter command transforms the file "
4721
                              "into a format that can be consumed by lnav")
4722
                          .with_snippets(this->get_snippets())
2✔
4723
                          .move();
1✔
4724
            errors.emplace_back(um);
1✔
4725
        }
1✔
4726
    }
60,922✔
4727

4728
    for (auto& vd : this->elf_value_def_order) {
619,284✔
4729
        std::vector<std::string>::iterator act_iter;
558,362✔
4730

4731
        if (!vd->vd_internal
558,362✔
4732
            && log_vtab_impl::RESERVED_COLUMNS.count(
1,057,896✔
4733
                vd->vd_meta.lvm_name.to_string_fragment()))
1,057,896✔
4734
        {
4735
            auto um = lnav::console::user_message::error(
×
4736
                          attr_line_t("value name ")
1✔
4737
                              .append_quoted(lnav::roles::symbol(
2✔
4738
                                  fmt::format(FMT_STRING("/{}/value/{}"),
4✔
4739
                                              this->elf_name,
1✔
4740
                                              vd->vd_meta.lvm_name)))
1✔
4741
                              .append(" is reserved and cannot be used"))
1✔
4742
                          .with_reason(
2✔
4743
                              "lnav automatically defines several columns in "
4744
                              "the log virtual table")
4745
                          .with_snippets(this->get_snippets())
2✔
4746
                          .with_help("Choose another name")
2✔
4747
                          .move();
1✔
4748
            errors.emplace_back(um);
1✔
4749
        }
1✔
4750

4751
        vd->vd_meta.lvm_format = this;
558,362✔
4752
        if (!vd->vd_internal
558,362✔
4753
            && !vd->vd_meta.lvm_column.is<logline_value_meta::table_column>())
558,362✔
4754
        {
4755
            vd->vd_meta.lvm_column
168,149✔
4756
                = logline_value_meta::table_column{this->elf_column_count++};
168,149✔
4757
        }
4758

4759
        if (vd->vd_meta.lvm_kind == value_kind_t::VALUE_UNKNOWN) {
558,362✔
4760
            log_warning("no kind set for %s, assuming text",
×
4761
                        vd->vd_meta.lvm_name.c_str());
4762
            vd->vd_meta.lvm_kind = value_kind_t::VALUE_TEXT;
×
4763
        }
4764

4765
        if (this->elf_type == elf_type_t::ELF_TYPE_TEXT) {
558,362✔
4766
            std::set<std::string> available_captures;
350,556✔
4767

4768
            bool found_in_pattern = false;
350,556✔
4769
            for (const auto& pat : this->elf_patterns) {
588,483✔
4770
                if (pat.second->p_pcre.pp_value == nullptr) {
588,481✔
4771
                    continue;
×
4772
                }
4773

4774
                auto cap_index = pat.second->p_pcre.pp_value->name_index(
1,176,962✔
4775
                    vd->vd_meta.lvm_name.get());
588,481✔
4776
                if (cap_index >= 0) {
588,481✔
4777
                    found_in_pattern = true;
350,554✔
4778
                    break;
350,554✔
4779
                }
4780

4781
                for (auto named_cap :
237,927✔
4782
                     pat.second->p_pcre.pp_value->get_named_captures())
2,167,853✔
4783
                {
4784
                    available_captures.insert(named_cap.get_name().to_string());
1,691,999✔
4785
                }
4786
            }
4787
            if (!found_in_pattern) {
350,556✔
4788
                auto notes
4789
                    = attr_line_t("the following captures are available:\n  ")
2✔
4790
                          .join(available_captures,
2✔
4791
                                VC_ROLE.value(role_t::VCR_SYMBOL),
4✔
4792
                                ", ")
4793
                          .move();
2✔
4794
                errors.emplace_back(
2✔
4795
                    lnav::console::user_message::warning(
×
4796
                        attr_line_t("invalid value ")
2✔
4797
                            .append_quoted(lnav::roles::symbol(
4✔
4798
                                fmt::format(FMT_STRING("/{}/value/{}"),
8✔
4799
                                            this->elf_name,
2✔
4800
                                            vd->vd_meta.lvm_name.get()))))
4✔
4801
                        .with_reason(
4✔
4802
                            attr_line_t("no patterns have a capture named ")
4✔
4803
                                .append_quoted(vd->vd_meta.lvm_name.get()))
2✔
4804
                        .with_note(notes)
2✔
4805
                        .with_snippets(this->get_snippets())
4✔
4806
                        .with_help("values are populated from captures in "
4807
                                   "patterns, so at least one pattern must "
4808
                                   "have a capture with this value name"));
4809
            }
2✔
4810
        }
350,556✔
4811

4812
        for (act_iter = vd->vd_action_list.begin();
558,362✔
4813
             act_iter != vd->vd_action_list.end();
559,191✔
4814
             ++act_iter)
829✔
4815
        {
4816
            if (this->lf_action_defs.find(*act_iter)
829✔
4817
                == this->lf_action_defs.end())
1,658✔
4818
            {
4819
#if 0
4820
                errors.push_back("error:" + this->elf_name.to_string() + ":"
4821
                                 + vd->vd_meta.lvm_name.get()
4822
                                 + ": cannot find action -- " + (*act_iter));
4823
#endif
4824
            }
4825
        }
4826

4827
        vd->set_rewrite_src_name();
558,362✔
4828

4829
        for (auto& hd_pair : vd->vd_highlighter_patterns) {
563,540✔
4830
            auto& hd = hd_pair.second;
5,178✔
4831
            auto attrs = vc.to_attrs(hd.hd_base_style, errors);
5,178✔
4832

4833
            if (hd.hd_pattern.pp_value == nullptr) {
5,178✔
4834
                hd.hd_pattern.pp_value
4835
                    = lnav::pcre2pp::code::from_const(".*").to_shared();
4,974✔
4836
            }
4837
            if (hd.hd_pattern.pp_value != nullptr) {
5,178✔
4838
                this->lf_highlighters.emplace_back(hd.hd_pattern.pp_value);
5,178✔
4839
                auto& hl = this->lf_highlighters.back();
5,178✔
4840

4841
                hl.with_field(vd->vd_meta.lvm_name)
5,178✔
4842
                    .with_name(hd_pair.first.to_string())
10,356✔
4843
                    .with_attrs(attrs)
5,178✔
4844
                    .with_nestable(hd.hd_base_style.sc_nestable);
5,178✔
4845
                for (const auto& [cap_key, cap_style] : hd.hd_capture_styles) {
5,280✔
4846
                    auto cap_index = hl.h_regex->name_index(cap_key.c_str());
102✔
4847
                    if (cap_index < 0) {
102✔
4848
                        auto um
4849
                            = lnav::console::user_message::error(
×
4850
                                  attr_line_t(
×
4851
                                      "invalid highlight capture named ")
4852
                                      .append_quoted(cap_key)
×
4853
                                      .append(" in highlight ")
×
4854
                                      .append_quoted(fmt::format(
×
4855
                                          FMT_STRING("/{}/value/{}/highlights/"
×
4856
                                                     "{}/captures"),
4857
                                          this->elf_name,
×
4858
                                          vd->vd_meta.lvm_name,
×
4859
                                          hd_pair.first)))
×
4860
                                  .with_reason(
×
4861
                                      attr_line_t("pattern does not have a "
×
4862
                                                  "capture named ")
4863
                                          .append_quoted(cap_key));
×
4864
                        errors.emplace_back(um);
×
4865
                        continue;
×
4866
                    }
4867

4868
                    hl.h_capture_attrs[cap_index - 1]
102✔
4869
                        = vc.to_attrs(cap_style, errors);
204✔
4870
                }
4871
            }
4872
        }
4873
    }
4874

4875
    if (this->elf_type == elf_type_t::ELF_TYPE_JSON) {
60,922✔
4876
        for (const auto& vd : this->elf_value_def_order) {
220,026✔
4877
            this->elf_value_def_frag_map[vd->vd_meta.lvm_name
207,806✔
4878
                                             .to_string_fragment()] = vd.get();
207,806✔
4879
        }
4880
    }
4881

4882
    for (const auto& td_pair : this->lf_tag_defs) {
63,514✔
4883
        const auto& td = td_pair.second;
2,592✔
4884

4885
        if (td->ftd_pattern.pp_value == nullptr
2,592✔
4886
            || td->ftd_pattern.pp_value->get_pattern().empty())
2,592✔
4887
        {
4888
            errors.emplace_back(
3✔
4889
                lnav::console::user_message::error(
×
4890
                    attr_line_t("invalid tag definition ")
6✔
4891
                        .append_quoted(lnav::roles::symbol(
6✔
4892
                            fmt::format(FMT_STRING("/{}/tags/{}"),
12✔
4893
                                        this->elf_name,
3✔
4894
                                        td_pair.first))))
3✔
4895
                    .with_reason(
6✔
4896
                        "tag definitions must have a non-empty pattern")
4897
                    .with_snippets(this->get_snippets()));
6✔
4898
        }
4899
    }
4900

4901
    if (this->elf_opid_field.empty()
60,922✔
4902
        && this->lf_opid_description_def->size() > 1)
60,922✔
4903
    {
4904
        errors.emplace_back(
1✔
4905
            lnav::console::user_message::error(
×
4906
                attr_line_t("too many opid descriptions")
2✔
4907
                    .append_quoted(lnav::roles::symbol(fmt::format(
2✔
4908
                        FMT_STRING("/{}/opid/description"), this->elf_name))))
4✔
4909
                .with_reason(attr_line_t("when no ")
2✔
4910
                                 .append("opid-field"_symbol)
1✔
4911
                                 .append(" is specified, only a single "
1✔
4912
                                         "description is supported"))
4913
                .with_snippets(this->get_snippets()));
2✔
4914
    }
4915

4916
    for (const auto& opid_desc_pair : *this->lf_opid_description_def) {
87,452✔
4917
        for (const auto& opid_desc : *opid_desc_pair.second.od_descriptors) {
63,008✔
4918
            auto iter = this->elf_value_defs.find(opid_desc.od_field.pp_value);
36,478✔
4919
            if (iter == this->elf_value_defs.end()) {
36,478✔
4920
                errors.emplace_back(
2✔
4921
                    lnav::console::user_message::error(
×
4922
                        attr_line_t("invalid opid description field ")
4✔
4923
                            .append_quoted(lnav::roles::symbol(
4✔
4924
                                opid_desc.od_field.pp_path.to_string())))
4✔
4925
                        .with_reason(
4✔
4926
                            attr_line_t("unknown value name ")
4✔
4927
                                .append_quoted(opid_desc.od_field.pp_value))
2✔
4928
                        .with_snippets(this->get_snippets()));
4✔
4929
            } else {
4930
                this->lf_desc_fields.insert(iter->first);
36,476✔
4931
                iter->second->vd_is_desc_field = true;
36,476✔
4932
            }
4933
        }
4934
    }
4935

4936
    for (const auto& subid_desc_pair : *this->lf_subid_description_def) {
61,751✔
4937
        for (const auto& subid_desc : *subid_desc_pair.second.od_descriptors) {
1,658✔
4938
            auto iter = this->elf_value_defs.find(subid_desc.od_field.pp_value);
829✔
4939
            if (iter == this->elf_value_defs.end()) {
829✔
4940
                errors.emplace_back(
×
4941
                    lnav::console::user_message::error(
×
4942
                        attr_line_t("invalid subid description field ")
×
4943
                            .append_quoted(lnav::roles::symbol(
×
4944
                                subid_desc.od_field.pp_path.to_string())))
×
4945
                        .with_reason(
×
4946
                            attr_line_t("unknown value name ")
×
4947
                                .append_quoted(subid_desc.od_field.pp_value))
×
4948
                        .with_snippets(this->get_snippets()));
×
4949
            } else {
4950
                this->lf_desc_fields.insert(iter->first);
829✔
4951
                iter->second->vd_is_desc_field = true;
829✔
4952
            }
4953
        }
4954
    }
4955

4956
    if (this->elf_type == elf_type_t::ELF_TYPE_TEXT
121,844✔
4957
        && this->elf_samples.empty())
60,922✔
4958
    {
4959
        errors.emplace_back(
3✔
4960
            lnav::console::user_message::error(
×
4961
                attr_line_t()
6✔
4962
                    .append_quoted(
3✔
4963
                        lnav::roles::symbol(this->elf_name.to_string()))
6✔
4964
                    .append(" is not a valid log format"))
3✔
4965
                .with_reason("log message samples must be included in a format "
6✔
4966
                             "definition")
4967
                .with_snippets(this->get_snippets()));
6✔
4968
    }
4969

4970
    for (const auto& pat : this->elf_pattern_order) {
172,733✔
4971
        if (this->elf_type != elf_type_t::ELF_TYPE_TEXT) {
111,811✔
4972
            continue;
1✔
4973
        }
4974
        if (pat->p_pcre.pp_value->name_index(this->lf_timestamp_field.get())
111,810✔
4975
            < 0)
111,810✔
4976
        {
4977
            attr_line_t notes;
1✔
4978
            bool first_note = true;
1✔
4979

4980
            if (pat->p_pcre.pp_value->get_capture_count() > 0) {
1✔
4981
                notes.append("the following captures are available:\n  ");
1✔
4982
            }
4983
            for (auto named_cap : pat->p_pcre.pp_value->get_named_captures()) {
4✔
4984
                if (!first_note) {
3✔
4985
                    notes.append(", ");
2✔
4986
                }
4987
                notes.append(
3✔
4988
                    lnav::roles::symbol(named_cap.get_name().to_string()));
6✔
4989
                first_note = false;
3✔
4990
            }
4991
            errors.emplace_back(
1✔
4992
                lnav::console::user_message::error(
×
4993
                    attr_line_t("invalid value for property ")
1✔
4994
                        .append_quoted(lnav::roles::symbol(
2✔
4995
                            fmt::format(FMT_STRING("/{}/timestamp-field"),
4✔
4996
                                        this->elf_name))))
1✔
4997
                    .with_reason(
2✔
4998
                        attr_line_t()
2✔
4999
                            .append_quoted(this->lf_timestamp_field)
1✔
5000
                            .append(" was not found in the pattern at ")
1✔
5001
                            .append(lnav::roles::symbol(pat->p_config_path)))
2✔
5002
                    .with_note(notes)
1✔
5003
                    .with_snippets(this->get_snippets()));
2✔
5004
        }
1✔
5005
    }
5006

5007
    for (size_t sample_index = 0; sample_index < this->elf_samples.size();
273,245✔
5008
         sample_index += 1)
212,323✔
5009
    {
5010
        auto& elf_sample = this->elf_samples[sample_index];
212,323✔
5011
        auto sample_lines
5012
            = string_fragment(elf_sample.s_line.pp_value).split_lines();
212,323✔
5013

5014
        if (this->test_line(elf_sample, errors).is<scan_match>()) {
212,323✔
5015
            for (const auto& pat_name : elf_sample.s_matched_regexes) {
421,328✔
5016
                this->elf_patterns[pat_name]->p_matched_samples.emplace(
209,006✔
5017
                    sample_index);
5018
            }
5019
        }
5020
    }
212,323✔
5021

5022
    if (!this->elf_samples.empty()) {
60,922✔
5023
        for (const auto& elf_sample : this->elf_samples) {
262,680✔
5024
            if (elf_sample.s_matched_regexes.size() <= 1) {
212,323✔
5025
                continue;
212,323✔
5026
            }
5027

5028
            errors.emplace_back(
×
5029
                lnav::console::user_message::warning(
×
5030
                    attr_line_t("invalid log format: ")
×
5031
                        .append_quoted(
×
5032
                            lnav::roles::symbol(this->elf_name.to_string())))
×
5033
                    .with_reason(
×
5034
                        attr_line_t(
×
5035
                            "sample is matched by more than one regex: ")
5036
                            .join(elf_sample.s_matched_regexes,
×
5037
                                  VC_ROLE.value(role_t::VCR_SYMBOL),
×
5038
                                  ", "))
5039
                    .with_snippet(lnav::console::snippet::from(
×
5040
                        elf_sample.s_line.pp_location,
5041
                        attr_line_t().append(lnav::roles::quoted_code(
×
5042
                            elf_sample.s_line.pp_value))))
×
5043
                    .with_help("log format regexes must match a single type "
5044
                               "of log message"));
5045
        }
5046

5047
        for (const auto& pat : this->elf_pattern_order) {
162,165✔
5048
            if (pat->p_matched_samples.empty()) {
111,808✔
5049
                errors.emplace_back(
2✔
5050
                    lnav::console::user_message::warning(
×
5051
                        attr_line_t("invalid pattern: ")
4✔
5052
                            .append_quoted(
2✔
5053
                                lnav::roles::symbol(pat->p_config_path)))
4✔
5054
                        .with_reason("pattern does not match any samples")
4✔
5055
                        .with_snippet(lnav::console::snippet::from(
6✔
5056
                            pat->p_pcre.pp_location, ""))
2✔
5057
                        .with_help(
5058
                            "every pattern should have at least one sample "
5059
                            "that it matches"));
5060
            }
5061
        }
5062
    }
5063

5064
    size_t value_def_index = 0;
60,922✔
5065
    for (auto& elf_value_def : this->elf_value_def_order) {
619,284✔
5066
        elf_value_def->vd_meta.lvm_values_index
558,362✔
5067
            = std::make_optional(value_def_index++);
558,362✔
5068

5069
        if (elf_value_def->vd_meta.lvm_foreign_key
558,362✔
5070
            || elf_value_def->vd_meta.lvm_identifier)
558,362✔
5071
        {
5072
            continue;
286,605✔
5073
        }
5074

5075
        switch (elf_value_def->vd_meta.lvm_kind) {
271,757✔
5076
            case value_kind_t::VALUE_INTEGER:
67,557✔
5077
            case value_kind_t::VALUE_FLOAT:
5078
                this->elf_numeric_value_defs.push_back(elf_value_def);
67,557✔
5079
                break;
67,557✔
5080
            default:
204,200✔
5081
                break;
204,200✔
5082
        }
5083
    }
5084

5085
    int format_index = 0;
60,922✔
5086
    for (auto iter = this->jlf_line_format.begin();
60,922✔
5087
         iter != this->jlf_line_format.end();
212,771✔
5088
         ++iter, format_index++)
151,849✔
5089
    {
5090
        static const intern_string_t ts
5091
            = intern_string::lookup("__timestamp__");
153,507✔
5092
        static const intern_string_t level_field
5093
            = intern_string::lookup("__level__");
153,507✔
5094
        static const intern_string_t duration_field
5095
            = intern_string::lookup("__duration__");
153,507✔
5096
        auto& jfe = *iter;
151,849✔
5097

5098
        if (startswith(jfe.jfe_value.pp_value.get(), "/")) {
151,849✔
5099
            jfe.jfe_value.pp_value
5100
                = intern_string::lookup(jfe.jfe_value.pp_value.get() + 1);
204✔
5101
        }
5102
        if (!jfe.jfe_ts_format.empty()) {
151,849✔
5103
            if (!jfe.jfe_value.pp_value.empty() && jfe.jfe_value.pp_value != ts)
931✔
5104
            {
5105
                log_warning(
102✔
5106
                    "%s:line-format[%d]:ignoring field '%s' since "
5107
                    "timestamp-format was used",
5108
                    this->elf_name.get(),
5109
                    format_index,
5110
                    jfe.jfe_value.pp_value.get());
5111
            }
5112
            jfe.jfe_value.pp_value = ts;
931✔
5113
        }
5114

5115
        switch (jfe.jfe_type) {
151,849✔
5116
            case json_log_field::VARIABLE: {
106,076✔
5117
                auto vd_iter
5118
                    = this->elf_value_defs.find(jfe.jfe_value.pp_value);
106,076✔
5119
                if (jfe.jfe_value.pp_value == ts) {
106,076✔
5120
                    this->elf_value_defs[this->lf_timestamp_field]
10,255✔
5121
                        ->vd_meta.lvm_hidden = true;
10,255✔
5122
                } else if (jfe.jfe_value.pp_value == level_field) {
95,821✔
5123
                    this->elf_value_defs[this->elf_level_field]
4,145✔
5124
                        ->vd_meta.lvm_hidden = true;
4,145✔
5125
                } else if (jfe.jfe_value.pp_value == duration_field) {
91,676✔
5126
                    if (!this->elf_duration_field.empty()) {
829✔
5127
                        this->elf_value_defs[this->elf_duration_field]
829✔
5128
                            ->vd_meta.lvm_hidden = true;
829✔
5129
                    }
5130
                } else if (vd_iter == this->elf_value_defs.end()) {
90,847✔
5131
                    errors.emplace_back(
2✔
5132
                        lnav::console::user_message::error(
×
5133
                            attr_line_t("invalid line format element ")
4✔
5134
                                .append_quoted(lnav::roles::symbol(fmt::format(
4✔
5135
                                    FMT_STRING("/{}/line-format/{}/field"),
6✔
5136
                                    this->elf_name,
2✔
5137
                                    format_index))))
5138
                            .with_reason(
4✔
5139
                                attr_line_t()
4✔
5140
                                    .append_quoted(jfe.jfe_value.pp_value)
2✔
5141
                                    .append(" is not a defined value"))
2✔
5142
                            .with_snippet(jfe.jfe_value.to_snippet()));
4✔
5143
                } else {
5144
                    vd_iter->second->vd_line_format_index = format_index;
90,845✔
5145
                    switch (vd_iter->second->vd_meta.lvm_kind) {
90,845✔
5146
                        case value_kind_t::VALUE_INTEGER:
16,784✔
5147
                        case value_kind_t::VALUE_FLOAT:
5148
                            if (jfe.jfe_align
16,784✔
5149
                                == json_format_element::align_t::NONE)
5150
                            {
5151
                                jfe.jfe_align
5152
                                    = json_format_element::align_t::RIGHT;
15,955✔
5153
                            }
5154
                            break;
16,784✔
5155
                        default:
74,061✔
5156
                            break;
74,061✔
5157
                    }
5158
                }
5159
                break;
106,076✔
5160
            }
5161
            case json_log_field::CONSTANT:
45,773✔
5162
                this->jlf_line_format_init_count
45,773✔
5163
                    += std::count(jfe.jfe_default_value.begin(),
45,773✔
5164
                                  jfe.jfe_default_value.end(),
5165
                                  '\n');
45,773✔
5166
                break;
45,773✔
5167
            default:
×
5168
                break;
×
5169
        }
5170
    }
5171

5172
    for (auto& hd_pair : this->elf_highlighter_patterns) {
62,161✔
5173
        auto& hd = hd_pair.second;
1,239✔
5174
        auto attrs = vc.to_attrs(hd.hd_base_style, errors);
1,239✔
5175
        if (hd.hd_pattern.pp_value != nullptr) {
1,239✔
5176
            this->lf_highlighters.emplace_back(hd.hd_pattern.pp_value);
1,237✔
5177
            this->lf_highlighters.back()
1,237✔
5178
                .with_name(hd_pair.first.to_string())
2,474✔
5179
                .with_attrs(attrs)
1,237✔
5180
                .with_nestable(hd.hd_base_style.sc_nestable);
1,237✔
5181
        }
5182
    }
5183

5184
    switch (this->elf_type) {
60,922✔
5185
        case elf_type_t::ELF_TYPE_JSON:
12,220✔
5186
            this->lf_max_unrecognized_lines = 50;
12,220✔
5187
            break;
12,220✔
5188
        default:
48,702✔
5189
            break;
48,702✔
5190
    }
5191
}
5192

5193
void
5194
external_log_format::register_vtabs(
52,078✔
5195
    log_vtab_manager* vtab_manager,
5196
    std::vector<lnav::console::user_message>& errors)
5197
{
5198
    for (auto& elf_search_table : this->elf_search_tables) {
62,075✔
5199
        if (elf_search_table.second.std_pattern.pp_value == nullptr) {
9,997✔
5200
            continue;
1✔
5201
        }
5202

5203
        auto lst = std::make_shared<log_search_table>(
5204
            elf_search_table.second.std_pattern.pp_value,
9,996✔
5205
            elf_search_table.first);
9,996✔
5206
        lst->lst_format = this;
9,996✔
5207
        lst->lst_log_path_glob = elf_search_table.second.std_glob;
9,996✔
5208
        if (elf_search_table.second.std_level != LEVEL_UNKNOWN) {
9,996✔
5209
            lst->lst_log_level = elf_search_table.second.std_level;
3,570✔
5210
        }
5211
        auto errmsg = vtab_manager->register_vtab(lst);
9,996✔
5212
        if (!errmsg.empty()) {
9,996✔
5213
#if 0
5214
            errors.push_back("error:" + this->elf_name.to_string() + ":"
5215
                             + search_iter->first.to_string()
5216
                             + ":unable to register table -- " + errmsg);
5217
#endif
5218
        }
5219
    }
9,996✔
5220
}
52,078✔
5221

5222
bool
5223
external_log_format::match_samples(const std::vector<sample_t>& samples) const
4,429,066✔
5224
{
5225
    for (const auto& sample_iter : samples) {
19,686,755✔
5226
        for (const auto& pat_iter : this->elf_pattern_order) {
41,671,311✔
5227
            auto& pat = *pat_iter;
26,413,622✔
5228

5229
            if (!pat.p_pcre.pp_value) {
26,413,622✔
5230
                continue;
×
5231
            }
5232

5233
            if (pat.p_pcre.pp_value
52,827,244✔
5234
                    ->find_in(sample_iter.s_line.pp_value, PCRE2_NO_UTF_CHECK)
52,827,244✔
5235
                    .ignore_error())
52,827,244✔
5236
            {
5237
                return true;
17,403✔
5238
            }
5239
        }
5240
    }
5241

5242
    return false;
4,411,663✔
5243
}
5244

5245
class external_log_table : public log_format_vtab_impl {
5246
public:
5247
    explicit external_log_table(std::shared_ptr<const log_format> elf)
52,078✔
5248
        : log_format_vtab_impl(elf),
52,078✔
5249
          elt_format(dynamic_cast<const external_log_format*>(elf.get()))
52,078✔
5250
    {
5251
    }
52,078✔
5252

5253
    void get_columns(std::vector<vtab_column>& cols) const override
52,504✔
5254
    {
5255
        const auto& elf = this->elt_format;
52,504✔
5256

5257
        cols.resize(elf->elf_column_count);
52,504✔
5258
        for (const auto& vd : elf->elf_value_def_order) {
535,861✔
5259
            auto type_pair = logline_value_to_sqlite_type(vd->vd_meta.lvm_kind);
483,357✔
5260

5261
            if (!vd->vd_meta.lvm_column.is<logline_value_meta::table_column>())
483,357✔
5262
            {
5263
                continue;
50,016✔
5264
            }
5265

5266
            auto col
5267
                = vd->vd_meta.lvm_column.get<logline_value_meta::table_column>()
433,341✔
5268
                      .value;
433,341✔
5269
            require(0 <= col && col < elf->elf_column_count);
433,341✔
5270

5271
            cols[col].vc_name = vd->vd_meta.lvm_name.get();
433,341✔
5272
            cols[col].vc_type = type_pair.first;
433,341✔
5273
            cols[col].vc_subtype = type_pair.second;
433,341✔
5274
            cols[col].vc_collator = vd->vd_collate;
433,341✔
5275
            cols[col].vc_comment = vd->vd_description;
433,341✔
5276
        }
5277
    }
52,504✔
5278

5279
    void get_foreign_keys(
40,176✔
5280
        std::unordered_set<std::string>& keys_inout) const override
5281
    {
5282
        log_vtab_impl::get_foreign_keys(keys_inout);
40,176✔
5283

5284
        for (const auto& elf_value_def : this->elt_format->elf_value_defs) {
589,032✔
5285
            if (elf_value_def.second->vd_meta.lvm_foreign_key
548,856✔
5286
                || elf_value_def.second->vd_meta.lvm_identifier)
548,856✔
5287
            {
5288
                keys_inout.emplace(elf_value_def.first.to_string());
196,857✔
5289
            }
5290
        }
5291
    }
40,176✔
5292

5293
    bool next(log_cursor& lc, logfile_sub_source& lss) override
3,574✔
5294
    {
5295
        if (lc.is_eof()) {
3,574✔
5296
            return true;
×
5297
        }
5298

5299
        content_line_t cl(lss.at(lc.lc_curr_line));
3,574✔
5300
        auto* lf = lss.find_file_ptr(cl);
3,574✔
5301
        auto lf_iter = lf->begin() + cl;
3,574✔
5302
        if (lf_iter->is_continued()) {
3,574✔
5303
            return false;
×
5304
        }
5305

5306
        if (lf->get_format_name() == this->lfvi_format->get_name()) {
3,574✔
5307
            return true;
3,568✔
5308
        }
5309

5310
        return false;
6✔
5311
    }
5312

5313
    void extract(logfile* lf,
3,404✔
5314
                 uint64_t line_number,
5315
                 string_attrs_t& sa,
5316
                 logline_value_vector& values) override
5317
    {
5318
        auto format = lf->get_format();
3,404✔
5319

5320
        sa.clear();
3,404✔
5321
        format->annotate(lf, line_number, sa, values);
3,404✔
5322
    }
3,404✔
5323

5324
    const external_log_format* elt_format;
5325
    line_range elt_container_body;
5326
};
5327

5328
std::shared_ptr<log_vtab_impl>
5329
external_log_format::get_vtab_impl() const
52,078✔
5330
{
5331
    return std::make_shared<external_log_table>(this->shared_from_this());
52,078✔
5332
}
5333

5334
std::shared_ptr<log_format>
5335
external_log_format::specialized(int fmt_lock)
571✔
5336
{
5337
    auto retval = std::make_shared<external_log_format>(*this);
571✔
5338

5339
    retval->lf_specialized = true;
571✔
5340
    if (this->elf_type == elf_type_t::ELF_TYPE_JSON) {
571✔
5341
        this->jlf_parse_context
5342
            = std::make_shared<yajlpp_parse_context>(this->elf_name);
54✔
5343
        this->jlf_yajl_handle.reset(
54✔
5344
            yajl_alloc(&this->jlf_parse_context->ypc_callbacks,
54✔
5345
                       nullptr,
5346
                       this->jlf_parse_context.get()),
54✔
5347
            yajl_handle_deleter());
5348
        yajl_config(this->jlf_yajl_handle.get(), yajl_dont_validate_strings, 1);
54✔
5349
        this->jlf_attr_line.al_string.reserve(16 * 1024);
54✔
5350
    }
5351

5352
    this->elf_specialized_value_defs_state = *this->elf_value_defs_state;
571✔
5353

5354
    return retval;
1,142✔
5355
}
571✔
5356

5357
log_format::match_name_result
5358
external_log_format::match_name(const std::string& filename)
847,743✔
5359
{
5360
    if (this->elf_filename_pcre.pp_value == nullptr) {
847,743✔
5361
        return name_matched{};
846,237✔
5362
    }
5363

5364
    if (this->elf_filename_pcre.pp_value->find_in(filename)
3,012✔
5365
            .ignore_error()
3,012✔
5366
            .has_value())
1,506✔
5367
    {
5368
        return name_matched{};
247✔
5369
    }
5370

5371
    return name_mismatched{
2,518✔
5372
        this->elf_filename_pcre.pp_value->match_partial(filename),
2,518✔
5373
        this->elf_filename_pcre.pp_value->get_pattern(),
1,259✔
5374
    };
1,259✔
5375
}
5376

5377
auto
5378
external_log_format::value_line_count(scan_batch_context& sbc,
316,285✔
5379
                                      const value_def* vd,
5380
                                      bool top_level,
5381
                                      std::optional<double> val,
5382
                                      const unsigned char* str,
5383
                                      ssize_t len,
5384
                                      yajl_string_props_t* props)
5385
    -> value_line_count_result
5386
{
5387
    value_line_count_result retval;
316,285✔
5388

5389
    if (str != nullptr && props != nullptr && !val) {
316,285✔
5390
        auto frag = string_fragment::from_bytes(str, len);
231,011✔
5391
        while (frag.endswith("\n")) {
232,063✔
5392
            frag.pop_back();
1,052✔
5393
            props->line_feeds -= 1;
1,052✔
5394
        }
5395
        retval.vlcr_has_ansi |= props->has_ansi;
231,011✔
5396
        retval.vlcr_count += props->line_feeds;
231,011✔
5397
    }
5398

5399
    if (vd == nullptr) {
316,285✔
5400
        if (this->jlf_hide_extra || !top_level) {
291,172✔
5401
            retval.vlcr_count = 0;
269,632✔
5402
        }
5403

5404
        return retval;
291,172✔
5405
    }
5406

5407
    if (vd->vd_meta.lvm_values_index) {
25,113✔
5408
        auto& lvs = sbc.sbc_value_stats[vd->vd_meta.lvm_values_index.value()];
8,533✔
5409
        if (len > lvs.lvs_width) {
8,533✔
5410
            lvs.lvs_width = len;
4,434✔
5411
        }
5412
        if (val) {
8,533✔
5413
            lvs.add_value(val.value());
247✔
5414
        }
5415
    }
5416

5417
    if (vd->vd_line_format_index) {
25,113✔
5418
        retval.vlcr_line_format_count += 1;
4,206✔
5419
        retval.vlcr_count -= 1;
4,206✔
5420
        retval.vlcr_line_format_index = vd->vd_line_format_index;
4,206✔
5421
    }
5422
    if (vd->vd_meta.is_hidden()) {
25,113✔
5423
        retval.vlcr_count = 0;
3,961✔
5424
        return retval;
3,961✔
5425
    }
5426

5427
    return retval;
21,152✔
5428
}
5429

5430
log_level_t
5431
external_log_format::convert_level(string_fragment sf,
220,274✔
5432
                                   scan_batch_context* sbc) const
5433
{
5434
    auto retval = LEVEL_INFO;
220,274✔
5435

5436
    if (sf.is_valid()) {
220,274✔
5437
        if (sbc != nullptr) {
173,432✔
5438
            auto ssm_res = sbc->sbc_level_cache.lookup(sf);
11,058✔
5439
            if (ssm_res.has_value()) {
11,058✔
5440
                return static_cast<log_level_t>(ssm_res.value());
4,279✔
5441
            }
5442
        }
5443

5444
        if (this->elf_level_patterns.empty()) {
169,153✔
5445
            retval = string2level(sf.data(), sf.length());
37,804✔
5446
        } else {
5447
            for (const auto& elf_level_pattern : this->elf_level_patterns) {
354,013✔
5448
                if (elf_level_pattern.second.lp_pcre.pp_value
650,048✔
5449
                        ->find_in(sf, PCRE2_NO_UTF_CHECK)
650,048✔
5450
                        .ignore_error()
650,048✔
5451
                        .has_value())
325,024✔
5452
                {
5453
                    retval = elf_level_pattern.first;
102,360✔
5454
                    break;
102,360✔
5455
                }
5456
            }
5457
        }
5458

5459
        if (sbc != nullptr
169,153✔
5460
            && sf.length() <= lnav::small_string_map::MAX_KEY_SIZE)
169,153✔
5461
        {
5462
            sbc->sbc_level_cache.insert(sf, retval);
5,908✔
5463
        }
5464
    }
5465

5466
    return retval;
215,995✔
5467
}
5468

5469
logline_value_meta
5470
external_log_format::get_value_meta(intern_string_t field_name,
4,899✔
5471
                                    value_kind_t kind)
5472
{
5473
    const auto iter = this->elf_value_defs.find(field_name);
4,899✔
5474
    if (iter == this->elf_value_defs.end()) {
4,899✔
5475
        auto retval = logline_value_meta(
5476
            field_name, kind, logline_value_meta::external_column{}, this);
390✔
5477

5478
        retval.lvm_hidden = this->jlf_hide_extra;
390✔
5479
        return retval;
390✔
5480
    }
390✔
5481

5482
    auto lvm = iter->second->vd_meta;
4,509✔
5483

5484
    lvm.lvm_kind = kind;
4,509✔
5485
    return lvm;
4,509✔
5486
}
4,509✔
5487

5488
logline_value_meta
5489
external_log_format::get_value_meta(yajlpp_parse_context* ypc,
12,330✔
5490
                                    const value_def* vd,
5491
                                    value_kind_t kind)
5492
{
5493
    if (vd == nullptr) {
12,330✔
5494
        auto retval = logline_value_meta(
5495
            ypc->get_path(), kind, logline_value_meta::external_column{}, this);
905✔
5496

5497
        retval.lvm_hidden = this->jlf_hide_extra;
905✔
5498
        return retval;
905✔
5499
    }
905✔
5500

5501
    auto lvm = vd->vd_meta;
11,425✔
5502

5503
    switch (vd->vd_meta.lvm_kind) {
11,425✔
5504
        case value_kind_t::VALUE_TIMESTAMP:
285✔
5505
            break;
285✔
5506
        default:
11,140✔
5507
            lvm.lvm_kind = kind;
11,140✔
5508
            break;
11,140✔
5509
    }
5510
    return lvm;
11,425✔
5511
}
11,425✔
5512

5513
void
5514
external_log_format::json_append(const log_format_file_state& lffs,
8,665✔
5515
                                 const json_format_element& jfe,
5516
                                 const value_def* vd,
5517
                                 const string_fragment& sf)
5518
{
5519
    if (jfe.jfe_align == json_format_element::align_t::RIGHT) {
8,665✔
5520
        auto sf_width = sf.column_width();
501✔
5521
        if (sf_width < jfe.jfe_min_width) {
501✔
5522
            this->json_append_to_cache(jfe.jfe_min_width - sf_width);
8✔
5523
        } else if (jfe.jfe_auto_width && vd != nullptr
24✔
5524
                   && sf_width
517✔
5525
                       < lffs.lffs_value_stats[vd->vd_meta.lvm_values_index
24✔
5526
                                                   .value()]
24✔
5527
                             .lvs_width)
24✔
5528
        {
5529
            this->json_append_to_cache(
8✔
5530
                lffs.lffs_value_stats[vd->vd_meta.lvm_values_index.value()]
8✔
5531
                    .lvs_width
8✔
5532
                - sf_width);
8✔
5533
        }
5534
    }
5535
    this->json_append_to_cache(sf.data(), sf.length());
8,665✔
5536
    if ((jfe.jfe_align == json_format_element::align_t::LEFT
8,665✔
5537
         || jfe.jfe_align == json_format_element::align_t::NONE)
8,611✔
5538
        && (jfe.jfe_min_width > 0 || jfe.jfe_auto_width))
8,164✔
5539
    {
5540
        auto sf_width = sf.column_width();
1,457✔
5541
        if (sf_width < jfe.jfe_min_width) {
1,457✔
5542
            this->json_append_to_cache(jfe.jfe_min_width - sf_width);
397✔
5543
        } else if (jfe.jfe_auto_width && vd != nullptr
924✔
5544
                   && sf_width
1,984✔
5545
                       < lffs.lffs_value_stats[vd->vd_meta.lvm_values_index
819✔
5546
                                                   .value()]
819✔
5547
                             .lvs_width)
819✔
5548
        {
5549
            this->json_append_to_cache(
380✔
5550
                lffs.lffs_value_stats[vd->vd_meta.lvm_values_index.value()]
380✔
5551
                    .lvs_width
380✔
5552
                - sf_width);
380✔
5553
        }
5554
    }
5555
}
8,665✔
5556

5557
intern_string_t
5558
external_log_format::get_pattern_name(const pattern_locks& pl,
77✔
5559
                                      uint64_t line_number) const
5560
{
5561
    if (this->elf_type != elf_type_t::ELF_TYPE_TEXT) {
77✔
5562
        static auto structured = intern_string::lookup("structured");
5563

5564
        return structured;
×
5565
    }
5566
    auto pat_index = pl.pattern_index_for_line(line_number);
77✔
5567
    return this->elf_pattern_order[pat_index]->p_name;
77✔
5568
}
5569

5570
std::string
5571
log_format::get_pattern_path(const pattern_locks& pl,
×
5572
                             uint64_t line_number) const
5573
{
5574
    auto pat_index = pl.pattern_index_for_line(line_number);
×
5575
    return fmt::format(FMT_STRING("builtin ({})"), pat_index);
×
5576
}
5577

5578
intern_string_t
5579
log_format::get_pattern_name(const pattern_locks& pl,
6✔
5580
                             uint64_t line_number) const
5581
{
5582
    char pat_str[128];
5583

5584
    auto pat_index = pl.pattern_index_for_line(line_number);
6✔
5585
    auto to_n_res = fmt::format_to_n(
×
5586
        pat_str, sizeof(pat_str) - 1, FMT_STRING("builtin ({})"), pat_index);
18✔
5587
    pat_str[to_n_res.size] = '\0';
6✔
5588
    return intern_string::lookup(pat_str);
12✔
5589
}
5590

5591
std::shared_ptr<log_format>
5592
log_format::find_root_format(const char* name)
1,438✔
5593
{
5594
    auto& fmts = get_root_formats();
1,438✔
5595
    for (auto& lf : fmts) {
58,669✔
5596
        if (lf->get_name() == name) {
58,669✔
5597
            return lf;
1,438✔
5598
        }
5599
    }
5600
    return nullptr;
×
5601
}
5602

5603
exttm
5604
log_format::tm_for_display(logfile::iterator ll, string_fragment sf)
1,004✔
5605
{
5606
    auto adjusted_time = ll->get_timeval();
1,004✔
5607
    exttm retval;
1,004✔
5608

5609
    retval.et_nsec = std::chrono::duration_cast<std::chrono::nanoseconds>(
1,004✔
5610
                         std::chrono::microseconds{adjusted_time.tv_usec})
×
5611
                         .count();
1,004✔
5612
    if (this->lf_timestamp_flags & ETF_NANOS_SET) {
1,004✔
5613
        timeval actual_tv;
5614
        exttm tm;
5✔
5615
        if (this->lf_date_time.scan(sf.data(),
10✔
5616
                                    sf.length(),
5✔
5617
                                    this->get_timestamp_formats(),
5618
                                    &tm,
5619
                                    actual_tv,
5620
                                    false))
5621
        {
5622
            adjusted_time.tv_usec = actual_tv.tv_usec;
5✔
5623
            retval.et_nsec = tm.et_nsec;
5✔
5624
        }
5625
    }
5626
    gmtime_r(&adjusted_time.tv_sec, &retval.et_tm);
1,004✔
5627
    retval.et_flags = this->lf_timestamp_flags;
1,004✔
5628
    if (this->lf_timestamp_flags & ETF_ZONE_SET
1,004✔
5629
        && this->lf_date_time.dts_zoned_to_local)
914✔
5630
    {
5631
        retval.et_flags &= ~ETF_Z_IS_UTC;
914✔
5632
    }
5633
    retval.et_gmtoff = this->lf_date_time.dts_local_offset_cache;
1,004✔
5634

5635
    return retval;
2,008✔
5636
}
5637

5638
pattern_for_lines::pattern_for_lines(uint32_t pfl_line, uint32_t pfl_pat_index)
3,204✔
5639
    : pfl_line(pfl_line), pfl_pat_index(pfl_pat_index)
3,204✔
5640
{
5641
}
3,204✔
5642

5643
void
5644
logline_value_stats::merge(const logline_value_stats& other)
17,123✔
5645
{
5646
    if (other.lvs_width > this->lvs_width) {
17,123✔
5647
        this->lvs_width = other.lvs_width;
4,337✔
5648
    }
5649

5650
    if (other.lvs_count == 0) {
17,123✔
5651
        return;
15,918✔
5652
    }
5653

5654
    require(other.lvs_min_value <= other.lvs_max_value);
1,205✔
5655

5656
    if (other.lvs_min_value < this->lvs_min_value) {
1,205✔
5657
        this->lvs_min_value = other.lvs_min_value;
828✔
5658
    }
5659
    if (other.lvs_max_value > this->lvs_max_value) {
1,205✔
5660
        this->lvs_max_value = other.lvs_max_value;
1,127✔
5661
    }
5662
    this->lvs_count += other.lvs_count;
1,205✔
5663
    this->lvs_total += other.lvs_total;
1,205✔
5664
    this->lvs_tdigest.insert(other.lvs_tdigest);
1,205✔
5665
    ensure(this->lvs_count >= 0);
1,205✔
5666
    ensure(this->lvs_min_value <= this->lvs_max_value);
1,205✔
5667
}
5668

5669
void
5670
logline_value_stats::add_value(double value)
31,709✔
5671
{
5672
    if (value < this->lvs_min_value) {
31,709✔
5673
        this->lvs_min_value = value;
1,745✔
5674
    }
5675
    if (value > this->lvs_max_value) {
31,709✔
5676
        this->lvs_max_value = value;
2,461✔
5677
    }
5678
    this->lvs_count += 1;
31,709✔
5679
    this->lvs_total += value;
31,709✔
5680
    this->lvs_tdigest.insert(value);
31,709✔
5681
}
31,709✔
5682

5683
std::vector<logline_value_meta>
5684
external_log_format::get_value_metadata() const
9,996✔
5685
{
5686
    std::vector<logline_value_meta> retval;
9,996✔
5687

5688
    for (const auto& vd : this->elf_value_def_order) {
105,013✔
5689
        retval.emplace_back(vd->vd_meta);
95,017✔
5690
    }
5691

5692
    return retval;
9,996✔
5693
}
×
5694

5695
std::optional<size_t>
5696
external_log_format::stats_index_for_value(const intern_string_t& name) const
73✔
5697
{
5698
    const auto iter = this->elf_value_defs.find(name);
73✔
5699
    if (iter != this->elf_value_defs.end()
73✔
5700
        && iter->second->vd_meta.lvm_values_index)
73✔
5701
    {
5702
        return iter->second->vd_meta.lvm_values_index.value();
73✔
5703
    }
5704

5705
    return std::nullopt;
×
5706
}
5707

5708
std::string
5709
external_log_format::get_pattern_regex(const pattern_locks& pl,
17✔
5710
                                       uint64_t line_number) const
5711
{
5712
    if (this->elf_type != elf_type_t::ELF_TYPE_TEXT) {
17✔
5713
        return "";
2✔
5714
    }
5715
    auto pat_index = pl.pattern_index_for_line(line_number);
16✔
5716
    return this->elf_pattern_order[pat_index]->p_pcre.pp_value->get_pattern();
16✔
5717
}
5718

5719
bool
5720
external_log_format::hide_field(const intern_string_t field_name, bool val)
12✔
5721
{
5722
    const auto vd_iter = this->elf_value_defs.find(field_name);
12✔
5723
    if (vd_iter == this->elf_value_defs.end()) {
12✔
5724
        log_warning("field to hide not found: %s.%s",
1✔
5725
                    this->elf_name.c_str(),
5726
                    field_name.c_str());
5727
        return false;
1✔
5728
    }
5729

5730
    vd_iter->second->vd_meta.lvm_user_hidden = val;
11✔
5731
    if (this->elf_type == elf_type_t::ELF_TYPE_JSON) {
11✔
5732
        bool found = false;
2✔
5733

5734
        if (!field_name.to_string_fragment().find('#')) {
2✔
5735
            for (const auto& jfe : this->jlf_line_format) {
18✔
5736
                if (jfe.jfe_value.pp_value == field_name) {
17✔
5737
                    log_debug(
×
5738
                        "hide-field not triggering rebuild since it is in "
5739
                        "line-format: %s.%s",
5740
                        this->elf_name.c_str(),
5741
                        field_name.c_str());
5742
                    found = true;
×
5743
                    break;
×
5744
                }
5745
            }
5746
        }
5747
        if (!found) {
2✔
5748
            log_info("format field %s.%s changed, rebuilding",
2✔
5749
                     this->elf_name.get(),
5750
                     field_name.get());
5751
            this->elf_value_defs_state->vds_generation += 1;
2✔
5752
        }
5753
    }
5754
    return true;
11✔
5755
}
5756

5757
bool
5758
external_log_format::format_changed()
3,009✔
5759
{
5760
    if (this->elf_specialized_value_defs_state.vds_generation
6,018✔
5761
        != this->elf_value_defs_state->vds_generation)
3,009✔
5762
    {
5763
        this->elf_specialized_value_defs_state = *this->elf_value_defs_state;
2✔
5764
        this->jlf_cached_offset = -1;
2✔
5765
        return true;
2✔
5766
    }
5767

5768
    return false;
3,007✔
5769
}
5770

5771
bool
5772
format_tag_def::path_restriction::matches(const char* fn) const
9✔
5773
{
5774
    return fnmatch(this->p_glob.c_str(), fn, 0) == 0;
9✔
5775
}
5776

5777
bool
5778
format_partition_def::path_restriction::matches(const char* fn) const
9✔
5779
{
5780
    return fnmatch(this->p_glob.c_str(), fn, 0) == 0;
9✔
5781
}
5782

5783
int
5784
pattern_locks::pattern_index_for_line(uint64_t line_number) const
7,193✔
5785
{
5786
    if (this->pl_lines.empty()) {
7,193✔
5787
        return -1;
×
5788
    }
5789

5790
    auto iter
5791
        = std::lower_bound(this->pl_lines.cbegin(),
7,193✔
5792
                           this->pl_lines.cend(),
5793
                           line_number,
5794
                           [](const pattern_for_lines& pfl, uint32_t line) {
7,252✔
5795
                               return pfl.pfl_line < line;
7,252✔
5796
                           });
5797

5798
    if (iter == this->pl_lines.end() || iter->pfl_line != line_number) {
7,193✔
5799
        --iter;
6,421✔
5800
    }
5801

5802
    return iter->pfl_pat_index;
7,193✔
5803
}
5804

5805
/* XXX */
5806
#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