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

tstack / lnav / 18885671937-2611

28 Oct 2025 06:41PM UTC coverage: 68.891% (+0.001%) from 68.89%
18885671937-2611

push

github

tstack
[time_util] use std::chrono::microseconds instead of timeval

115 of 163 new or added lines in 15 files covered. (70.55%)

5 existing lines in 4 files now uncovered.

50216 of 72892 relevant lines covered (68.89%)

424384.78 hits per line

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

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

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

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

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

67
using namespace lnav::roles::literals;
68
using namespace std::chrono_literals;
69

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

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

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

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

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

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

104
    return *this;
839✔
105
}
106

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

117
    return *this;
377✔
118
}
119

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

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

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

149
    return *this;
377✔
150
}
151

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

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

165
    return *this;
462✔
166
}
167

168
void
169
log_level_stats::update_msg_count(log_level_t lvl, int32_t amount)
17,965✔
170
{
171
    switch (lvl) {
17,965✔
172
        case LEVEL_FATAL:
1,209✔
173
        case LEVEL_CRITICAL:
174
        case LEVEL_ERROR:
175
            this->lls_error_count += amount;
1,209✔
176
            break;
1,209✔
177
        case LEVEL_WARNING:
102✔
178
            this->lls_warning_count += amount;
102✔
179
            break;
102✔
180
        default:
16,654✔
181
            break;
16,654✔
182
    }
183
    this->lls_total_count += amount;
17,965✔
184
}
17,965✔
185

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

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

211
    return retval;
8,189✔
212
}
213

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

243
    return retval;
9,712✔
244
}
245

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

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

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

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

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

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

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

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

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

319
    for (size_t lpc = 0; lpc < this->od_descriptors->size(); lpc++) {
2,162✔
320
        retval.append(this->od_descriptors->at(lpc).od_prefix);
1,087✔
321
        auto iter = lod.find(lpc);
1,087✔
322
        if (iter != lod.end()) {
1,087✔
323
            retval.append(iter->second);
1,086✔
324
        }
325
        retval.append(this->od_descriptors->at(lpc).od_suffix);
1,087✔
326
    }
327

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

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

357
    return retval;
38✔
358
}
359

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

367
    if (len == -1) {
×
368
        len = strlen(msg);
×
369
    }
370

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

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

378
        next += 1;
×
379
        int amount = (next - last);
×
380

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

386
        last = next + 1;
×
387
    }
388

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

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

399
    return retval;
×
400
}
401

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

411
    switch (this->lv_meta.lvm_kind) {
612,667✔
412
        case value_kind_t::VALUE_JSON:
309,560✔
413
        case value_kind_t::VALUE_XML:
414
        case value_kind_t::VALUE_STRUCT:
415
        case value_kind_t::VALUE_TEXT:
416
        case value_kind_t::VALUE_QUOTED:
417
        case value_kind_t::VALUE_W3C_QUOTED:
418
        case value_kind_t::VALUE_TIMESTAMP:
419
            require(origin.lr_end != -1);
309,560✔
420
            this->lv_frag = string_fragment::from_byte_range(
309,560✔
421
                sbr.get_data(), origin.lr_start, origin.lr_end);
309,560✔
422
            break;
309,560✔
423

424
        case value_kind_t::VALUE_NULL:
×
425
            break;
×
426

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

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

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

462
        case value_kind_t::VALUE_UNKNOWN:
×
463
        case value_kind_t::VALUE__MAX:
464
            ensure(0);
×
465
            break;
466
    }
467
}
612,667✔
468

469
std::string
470
logline_value::to_string() const
9,720✔
471
{
472
    char buffer[128];
473

474
    switch (this->lv_meta.lvm_kind) {
9,720✔
475
        case value_kind_t::VALUE_NULL:
15✔
476
            return "null";
30✔
477

478
        case value_kind_t::VALUE_JSON:
9,153✔
479
        case value_kind_t::VALUE_XML:
480
        case value_kind_t::VALUE_STRUCT:
481
        case value_kind_t::VALUE_TEXT:
482
        case value_kind_t::VALUE_TIMESTAMP:
483
            if (this->lv_str) {
9,153✔
484
                return this->lv_str.value();
1,418✔
485
            }
486
            if (this->lv_frag.empty()) {
7,735✔
487
                return this->lv_intern_string.to_string();
55✔
488
            }
489
            return this->lv_frag.to_string();
7,680✔
490

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

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

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

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

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

540
    return {buffer};
1,078✔
541
}
542

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

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

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

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

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

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

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

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

617
    return string_fragment::from_c_str(buffer).to_owned(alloc);
×
618
}
619

620
std::vector<std::shared_ptr<log_format>> log_format::lf_root_formats;
621

622
std::vector<std::shared_ptr<log_format>>&
623
log_format::get_root_formats()
15,560✔
624
{
625
    return lf_root_formats;
15,560✔
626
}
627

628
void
629
external_log_format::update_op_description(
5,407✔
630
    const std::map<intern_string_t, opid_descriptors>& desc_defs,
631
    log_op_description& lod,
632
    const pattern* fpat,
633
    const lnav::pcre2pp::match_data& md)
634
{
635
    std::optional<std::string> desc_elem_str;
5,407✔
636
    if (!lod.lod_id) {
5,407✔
637
        for (const auto& desc_def_pair : desc_defs) {
3,862✔
638
            if (lod.lod_id) {
1,686✔
639
                break;
26✔
640
            }
641
            for (const auto& desc_def : *desc_def_pair.second.od_descriptors) {
2,471✔
642
                auto desc_field_index_iter = fpat->p_value_name_to_index.find(
1,758✔
643
                    desc_def.od_field.pp_value);
1,758✔
644

645
                if (desc_field_index_iter == fpat->p_value_name_to_index.end())
1,758✔
646
                {
647
                    continue;
43✔
648
                }
649

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

655
                desc_elem_str = desc_def.matches(desc_cap_opt.value());
1,715✔
656
                if (desc_elem_str) {
1,715✔
657
                    lod.lod_id = desc_def_pair.first;
947✔
658
                    break;
947✔
659
                }
660
            }
661
        }
662
    }
663
    if (lod.lod_id) {
5,407✔
664
        const auto& desc_def_v
665
            = *desc_defs.find(lod.lod_id.value())->second.od_descriptors;
4,152✔
666
        auto& desc_v = lod.lod_elements;
4,152✔
667

668
        if (desc_def_v.size() == desc_v.size()
4,152✔
669
            || (this->elf_opid_field.empty() && !desc_v.empty()))
4,152✔
670
        {
671
            return;
3,205✔
672
        }
673
        for (size_t desc_def_index = 0; desc_def_index < desc_def_v.size();
2,733✔
674
             desc_def_index++)
675
        {
676
            const auto& desc_def = desc_def_v[desc_def_index];
1,786✔
677
            auto found_desc = desc_v.begin();
1,786✔
678

679
            for (; found_desc != desc_v.end(); ++found_desc) {
2,625✔
680
                if (found_desc->first == desc_def_index) {
839✔
681
                    break;
×
682
                }
683
            }
684
            auto desc_field_index_iter
685
                = fpat->p_value_name_to_index.find(desc_def.od_field.pp_value);
1,786✔
686

687
            if (desc_field_index_iter == fpat->p_value_name_to_index.end()) {
1,786✔
688
                continue;
35✔
689
            }
690
            auto desc_cap_opt = md[desc_field_index_iter->second];
1,786✔
691
            if (!desc_cap_opt) {
1,786✔
692
                continue;
35✔
693
            }
694

695
            if (!desc_elem_str) {
1,751✔
696
                desc_elem_str = desc_def.matches(desc_cap_opt.value());
804✔
697
            }
698
            if (desc_elem_str) {
1,751✔
699
                if (found_desc == desc_v.end()) {
1,748✔
700
                    desc_v.emplace_back(desc_def_index, desc_elem_str.value());
1,748✔
701
                } else if (!desc_elem_str->empty()) {
×
702
                    found_desc->second.append(desc_def.od_joiner);
×
703
                    found_desc->second.append(desc_elem_str.value());
×
704
                }
705
            }
706
            desc_elem_str = std::nullopt;
1,751✔
707
        }
708
    }
709
}
5,407✔
710

711
void
712
external_log_format::update_op_description(
185✔
713
    const std::map<intern_string_t, opid_descriptors>& desc_defs,
714
    log_op_description& lod)
715
{
716
    std::optional<std::string> desc_elem_str;
185✔
717
    if (!lod.lod_id) {
185✔
718
        for (const auto& desc_def_pair : desc_defs) {
194✔
719
            if (lod.lod_id) {
15✔
720
                break;
×
721
            }
722
            for (const auto& desc_def : *desc_def_pair.second.od_descriptors) {
15✔
723
                auto desc_cap_iter
724
                    = this->lf_desc_captures.find(desc_def.od_field.pp_value);
15✔
725

726
                if (desc_cap_iter == this->lf_desc_captures.end()) {
15✔
727
                    continue;
×
728
                }
729
                desc_elem_str = desc_def.matches(desc_cap_iter->second);
15✔
730
                if (desc_elem_str) {
15✔
731
                    lod.lod_id = desc_def_pair.first;
15✔
732
                    break;
15✔
733
                }
734
            }
735
        }
736
    }
737
    if (lod.lod_id) {
185✔
738
        const auto& desc_def_v
739
            = *desc_defs.find(lod.lod_id.value())->second.od_descriptors;
21✔
740
        auto& desc_v = lod.lod_elements;
21✔
741

742
        if (desc_def_v.size() == desc_v.size()
21✔
743
            || (this->elf_opid_field.empty() && !desc_v.empty()))
21✔
744
        {
745
            return;
6✔
746
        }
747
        for (size_t desc_def_index = 0; desc_def_index < desc_def_v.size();
45✔
748
             desc_def_index++)
749
        {
750
            const auto& desc_def = desc_def_v[desc_def_index];
30✔
751
            auto found_desc = desc_v.begin();
30✔
752

753
            for (; found_desc != desc_v.end(); ++found_desc) {
45✔
754
                if (found_desc->first == desc_def_index) {
15✔
755
                    break;
×
756
                }
757
            }
758
            auto desc_cap_iter
759
                = this->lf_desc_captures.find(desc_def.od_field.pp_value);
30✔
760
            if (desc_cap_iter == this->lf_desc_captures.end()) {
30✔
761
                continue;
×
762
            }
763

764
            if (!desc_elem_str) {
30✔
765
                desc_elem_str = desc_def.matches(desc_cap_iter->second);
15✔
766
            }
767
            if (desc_elem_str) {
30✔
768
                if (found_desc == desc_v.end()) {
30✔
769
                    desc_v.emplace_back(desc_def_index, desc_elem_str.value());
30✔
770
                } else if (!desc_elem_str->empty()) {
×
771
                    found_desc->second.append(desc_def.od_joiner);
×
772
                    found_desc->second.append(desc_elem_str.value());
×
773
                }
774
            }
775
            desc_elem_str = std::nullopt;
30✔
776
        }
777
    }
778
}
185✔
779

780
static bool
781
next_format(
2,027,507✔
782
    const std::vector<std::shared_ptr<external_log_format::pattern>>& patterns,
783
    int& index,
784
    int& locked_index)
785
{
786
    bool retval = true;
2,027,507✔
787

788
    if (locked_index == -1) {
2,027,507✔
789
        index += 1;
2,020,294✔
790
        if (index >= (int) patterns.size()) {
2,020,294✔
791
            retval = false;
607,918✔
792
        }
793
    } else if (index == locked_index) {
7,213✔
794
        retval = false;
1✔
795
    } else {
796
        index = locked_index;
7,212✔
797
    }
798

799
    return retval;
2,027,507✔
800
}
801

802
bool
803
log_format::next_format(const pcre_format* fmt, int& index, int& locked_index)
160,074✔
804
{
805
    bool retval = true;
160,074✔
806

807
    if (locked_index == -1) {
160,074✔
808
        index += 1;
159,903✔
809
        if (fmt[index].name == nullptr) {
159,903✔
810
            retval = false;
9,210✔
811
        }
812
    } else if (index == locked_index) {
171✔
813
        retval = false;
31✔
814
    } else {
815
        index = locked_index;
140✔
816
    }
817

818
    return retval;
160,074✔
819
}
820

821
const char*
822
log_format::log_scanf(uint32_t line_number,
10,766✔
823
                      string_fragment line,
824
                      const pcre_format* fmt,
825
                      const char* time_fmt[],
826
                      struct exttm* tm_out,
827
                      struct timeval* tv_out,
828

829
                      string_fragment* ts_out,
830
                      std::optional<string_fragment>* level_out)
831
{
832
    int curr_fmt = -1;
10,766✔
833
    const char* retval = nullptr;
10,766✔
834
    bool done = false;
10,766✔
835
    int pat_index = this->last_pattern_index();
10,766✔
836

837
    while (!done && next_format(fmt, curr_fmt, pat_index)) {
161,599✔
838
        thread_local auto md = lnav::pcre2pp::match_data::unitialized();
150,833✔
839

840
        auto match_res = fmt[curr_fmt]
150,833✔
841
                             .pcre->capture_from(line)
150,833✔
842
                             .into(md)
150,833✔
843
                             .matches(PCRE2_NO_UTF_CHECK)
301,666✔
844
                             .ignore_error();
150,833✔
845
        if (!match_res) {
150,833✔
846
            retval = nullptr;
129,377✔
847
        } else {
848
            auto ts = md[fmt[curr_fmt].pf_timestamp_index];
21,456✔
849

850
            retval = this->lf_date_time.scan(
21,456✔
851
                ts->data(), ts->length(), nullptr, tm_out, *tv_out);
21,456✔
852

853
            if (retval == nullptr) {
21,456✔
854
                auto ls = this->lf_date_time.unlock();
19,931✔
855
                retval = this->lf_date_time.scan(
19,931✔
856
                    ts->data(), ts->length(), nullptr, tm_out, *tv_out);
19,931✔
857
                if (retval != nullptr) {
19,931✔
858
                    auto old_flags
×
859
                        = this->lf_timestamp_flags & DATE_TIME_SET_FLAGS;
×
860
                    auto new_flags = tm_out->et_flags & DATE_TIME_SET_FLAGS;
×
861

862
                    // It is unlikely a valid timestamp would lose much
863
                    // precision.
864
                    if (new_flags != old_flags) {
×
865
                        retval = nullptr;
×
866
                    }
867
                }
868
                if (retval == nullptr) {
19,931✔
869
                    this->lf_date_time.relock(ls);
19,931✔
870
                } else {
871
                    log_debug(
×
872
                        "%d: changed time format to '%s' due to %.*s",
873
                        line_number,
874
                        PTIMEC_FORMAT_STR[this->lf_date_time.dts_fmt_lock],
875
                        ts->length(),
876
                        ts->data());
877
                }
878
            }
879

880
            if (retval) {
21,456✔
881
                *ts_out = ts.value();
1,525✔
882
                if (md[2]) {
1,525✔
883
                    *level_out = md[2];
246✔
884
                } else {
885
                    *level_out = line.substr(md[0]->sf_end);
1,279✔
886
                }
887
                if (curr_fmt != pat_index) {
1,525✔
888
                    uint32_t lock_line;
889

890
                    if (this->lf_pattern_locks.empty()) {
1,416✔
891
                        lock_line = 0;
1,416✔
892
                    } else {
893
                        lock_line = line_number;
×
894
                    }
895

896
                    this->lf_pattern_locks.emplace_back(lock_line, curr_fmt);
1,416✔
897
                }
898
                this->lf_timestamp_flags = tm_out->et_flags;
1,525✔
899
                done = true;
1,525✔
900
            }
901
        }
902
    }
903

904
    return retval;
10,766✔
905
}
906

907
void
908
log_format::annotate(logfile* lf,
35,679✔
909
                     uint64_t line_number,
910
                     string_attrs_t& sa,
911
                     logline_value_vector& values,
912
                     bool annotate_module) const
913
{
914
    if (lf != nullptr && !values.lvv_opid_value) {
35,679✔
915
        const auto& bm = lf->get_bookmark_metadata();
1,177✔
916
        auto bm_iter = bm.find(line_number);
1,177✔
917
        if (bm_iter != bm.end() && !bm_iter->second.bm_opid.empty()) {
1,177✔
918
            values.lvv_opid_value = bm_iter->second.bm_opid;
18✔
919
            values.lvv_opid_provenance
920
                = logline_value_vector::opid_provenance::user;
18✔
921
        }
922
    }
923
}
35,679✔
924

925
void
926
log_format::check_for_new_year(std::vector<logline>& dst,
1,478✔
927
                               exttm etm,
928
                               struct timeval log_tv)
929
{
930
    if (dst.empty()) {
1,478✔
931
        return;
215✔
932
    }
933

934
    time_t diff
935
        = dst.back().get_time<std::chrono::seconds>().count() - log_tv.tv_sec;
1,263✔
936
    int off_year = 0, off_month = 0, off_day = 0, off_hour = 0;
1,263✔
937
    bool do_change = true;
1,263✔
938

939
    if (diff <= 0) {
1,263✔
940
        return;
1,204✔
941
    }
942
    if ((etm.et_flags & ETF_MONTH_SET) && diff >= (24 * 60 * 60)) {
59✔
943
        off_year = 1;
10✔
944
    } else if (diff >= (24 * 60 * 60)) {
49✔
945
        off_month = 1;
2✔
946
    } else if (!(etm.et_flags & ETF_DAY_SET) && (diff >= (60 * 60))) {
47✔
947
        off_day = 1;
1✔
948
    } else if (!(etm.et_flags & ETF_HOUR_SET) && (diff >= 60)) {
46✔
949
        off_hour = 1;
4✔
950
    } else {
951
        do_change = false;
42✔
952
    }
953

954
    if (!do_change) {
59✔
955
        return;
42✔
956
    }
957
    log_debug("%d:detected time rollover; offsets=%d %d %d %d",
17✔
958
              dst.size(),
959
              off_year,
960
              off_month,
961
              off_day,
962
              off_hour);
963
    for (auto& ll : dst) {
66✔
964
        time_t ot = ll.get_time<std::chrono::seconds>().count();
49✔
965
        struct tm otm;
966

967
        gmtime_r(&ot, &otm);
49✔
968
        otm.tm_yday = -1;
49✔
969
        if (otm.tm_year < off_year) {
49✔
970
            otm.tm_year = 0;
×
971
        } else {
972
            otm.tm_year -= off_year;
49✔
973
        }
974
        otm.tm_mon -= off_month;
49✔
975
        if (otm.tm_mon < 0) {
49✔
976
            otm.tm_mon += 12;
2✔
977
        }
978
        auto new_time = tm2sec(&otm);
49✔
979
        if (new_time == -1) {
49✔
980
            continue;
×
981
        }
982
        new_time -= (off_day * 24 * 60 * 60) + (off_hour * 60 * 60);
49✔
983
        auto old_sub = ll.get_subsecond_time<std::chrono::microseconds>();
49✔
984
        ll.set_time(std::chrono::seconds{new_time});
49✔
985
        ll.set_subsecond_time(old_sub);
49✔
986
    }
987
}
988

989
/*
990
 * XXX This needs some cleanup.
991
 */
992
struct json_log_userdata {
993
    json_log_userdata(shared_buffer_ref& sbr, scan_batch_context* sbc)
8,287✔
994
        : jlu_shared_buffer(sbr), jlu_batch_context(sbc)
16,574✔
995
    {
996
    }
8,287✔
997

998
    const external_log_format::value_def* get_field_def(
253,354✔
999
        yajlpp_parse_context* ypc)
1000
    {
1001
        const auto field_frag = ypc->get_path_as_string_fragment();
253,354✔
1002
        auto* format = this->jlu_format;
253,354✔
1003

1004
        if (this->jlu_read_order_index < format->elf_value_def_read_order.size()
253,354✔
1005
            && format->elf_value_def_read_order[this->jlu_read_order_index]
342,959✔
1006
                    .first
1007
                == field_frag)
89,605✔
1008
        {
1009
            return format
1010
                ->elf_value_def_read_order[this->jlu_read_order_index++]
86,575✔
1011
                .second;
86,575✔
1012
        }
1013

1014
        format->elf_value_def_read_order.resize(this->jlu_read_order_index);
166,779✔
1015
        auto vd_iter = format->elf_value_def_frag_map.find(field_frag);
166,779✔
1016
        if (vd_iter != format->elf_value_def_frag_map.end()) {
166,779✔
1017
            format->elf_value_def_read_order.emplace_back(vd_iter->first,
148,778✔
1018
                                                          vd_iter->second);
148,778✔
1019
            this->jlu_read_order_index += 1;
148,778✔
1020
            return vd_iter->second;
148,778✔
1021
        }
1022

1023
        auto owned_frag = field_frag.to_owned(format->elf_allocator);
18,001✔
1024
        format->elf_value_def_frag_map[owned_frag] = nullptr;
18,001✔
1025
        format->elf_value_def_read_order.emplace_back(owned_frag, nullptr);
18,001✔
1026
        this->jlu_read_order_index += 1;
18,001✔
1027
        return nullptr;
18,001✔
1028
    }
1029

1030
    void add_sub_lines_for(const external_log_format::value_def* vd,
162,681✔
1031
                           bool top_level,
1032
                           std::optional<double> val,
1033
                           const unsigned char* str,
1034
                           ssize_t len,
1035
                           yajl_string_props_t* props)
1036
    {
1037
        auto res = this->jlu_format->value_line_count(
162,681✔
1038
            vd, top_level, val, str, len, props);
1039
        this->jlu_has_ansi |= res.vlcr_has_ansi;
162,681✔
1040
        if (!res.vlcr_valid_utf) {
162,681✔
1041
            this->jlu_valid_utf = false;
×
1042
        }
1043
        this->jlu_sub_line_count += res.vlcr_count;
162,681✔
1044
        this->jlu_quality += res.vlcr_line_format_count;
162,681✔
1045
        if (res.vlcr_line_format_index) {
162,681✔
1046
            this->jlu_format_hits[res.vlcr_line_format_index.value()] = true;
2,883✔
1047
        }
1048
    }
162,681✔
1049

1050
    external_log_format* jlu_format{nullptr};
1051
    const logline* jlu_line{nullptr};
1052
    logline* jlu_base_line{nullptr};
1053
    int jlu_sub_line_count{1};
1054
    bool jlu_has_ansi{false};
1055
    bool jlu_valid_utf{true};
1056
    yajl_handle jlu_handle{nullptr};
1057
    const char* jlu_line_value{nullptr};
1058
    size_t jlu_line_size{0};
1059
    size_t jlu_sub_start{0};
1060
    uint32_t jlu_quality{0};
1061
    uint32_t jlu_strikes{0};
1062
    std::vector<bool> jlu_format_hits;
1063
    shared_buffer_ref& jlu_shared_buffer;
1064
    scan_batch_context* jlu_batch_context;
1065
    std::optional<string_fragment> jlu_opid_frag;
1066
    std::optional<string_fragment> jlu_opid_desc_frag;
1067
    std::optional<string_fragment> jlu_tid_frag;
1068
    std::optional<std::string> jlu_subid;
1069
    hasher jlu_opid_hasher;
1070
    std::chrono::microseconds jlu_duration{0};
8,287✔
1071
    exttm jlu_exttm;
1072
    size_t jlu_read_order_index{0};
1073
    subline_options jlu_subline_opts;
1074
};
1075

1076
static int read_json_field(yajlpp_parse_context* ypc,
1077
                           const unsigned char* str,
1078
                           size_t len,
1079
                           yajl_string_props_t*);
1080

1081
static int
1082
read_json_null(yajlpp_parse_context* ypc)
2,720✔
1083
{
1084
    json_log_userdata* jlu = (json_log_userdata*) ypc->ypc_userdata;
2,720✔
1085
    const auto* vd = jlu->get_field_def(ypc);
2,720✔
1086

1087
    jlu->add_sub_lines_for(
5,440✔
1088
        vd, ypc->is_level(1), std::nullopt, nullptr, -1, nullptr);
2,720✔
1089

1090
    return 1;
2,720✔
1091
}
1092

1093
static int
1094
read_json_bool(yajlpp_parse_context* ypc, int val)
12,950✔
1095
{
1096
    json_log_userdata* jlu = (json_log_userdata*) ypc->ypc_userdata;
12,950✔
1097
    const auto* vd = jlu->get_field_def(ypc);
12,950✔
1098

1099
    jlu->add_sub_lines_for(
25,900✔
1100
        vd, ypc->is_level(1), std::nullopt, nullptr, -1, nullptr);
12,950✔
1101

1102
    return 1;
12,950✔
1103
}
1104

1105
static int
1106
read_json_number(yajlpp_parse_context* ypc,
19,828✔
1107
                 const char* numberVal,
1108
                 size_t numberLen)
1109
{
1110
    auto* jlu = (json_log_userdata*) ypc->ypc_userdata;
19,828✔
1111
    auto number_frag = string_fragment::from_bytes(numberVal, numberLen);
19,828✔
1112
    std::optional<double> val;
19,828✔
1113

1114
    intern_string_t field_name;
19,828✔
1115
    const auto* vd = jlu->get_field_def(ypc);
19,828✔
1116
    if (vd != nullptr) {
19,828✔
1117
        field_name = vd->vd_meta.lvm_name;
639✔
1118
    }
1119

1120
    if (field_name.empty()) {
19,828✔
1121
    } else if (jlu->jlu_format->lf_timestamp_field == field_name) {
639✔
1122
        long long divisor = jlu->jlu_format->elf_timestamp_divisor;
113✔
1123
        auto scan_res = scn::scan_value<double>(number_frag.to_string_view());
113✔
1124
        if (!scan_res) {
113✔
1125
            log_error("invalid number %.*s", numberLen, numberVal);
×
1126
            return 0;
×
1127
        }
1128
        auto ts_val = scan_res.value().value();
113✔
1129
        timeval tv;
1130
        tv.tv_sec = ts_val / divisor;
113✔
1131
        tv.tv_usec = fmod(ts_val, divisor) * (1000000.0 / divisor);
113✔
1132
        jlu->jlu_format->lf_date_time.to_localtime(tv.tv_sec, jlu->jlu_exttm);
113✔
1133
        tv.tv_sec = tm2sec(&jlu->jlu_exttm.et_tm);
113✔
1134
        jlu->jlu_exttm.et_gmtoff
1135
            = jlu->jlu_format->lf_date_time.dts_local_offset_cache;
113✔
1136
        jlu->jlu_exttm.et_flags
113✔
1137
            |= ETF_MACHINE_ORIENTED | ETF_SUB_NOT_IN_FORMAT | ETF_ZONE_SET;
113✔
1138
        if (divisor == 1000) {
113✔
1139
            jlu->jlu_exttm.et_flags |= ETF_MILLIS_SET;
17✔
1140
        } else {
1141
            jlu->jlu_exttm.et_flags |= ETF_MICROS_SET;
96✔
1142
        }
1143
        jlu->jlu_exttm.et_nsec = tv.tv_usec * 1000;
113✔
1144
        jlu->jlu_base_line->set_time(tv);
113✔
1145
    } else if (jlu->jlu_format->lf_subsecond_field == field_name) {
526✔
1146
        auto scan_res = scn::scan_value<double>(number_frag.to_string_view());
3✔
1147
        if (!scan_res) {
3✔
1148
            log_error("invalid number %.*s", numberLen, numberVal);
×
1149
            return 0;
×
1150
        }
1151
        auto ts_val = scan_res.value().value();
3✔
1152

1153
        uint64_t millis = 0;
3✔
1154
        jlu->jlu_exttm.et_flags &= ~(ETF_MICROS_SET | ETF_MILLIS_SET);
3✔
1155
        switch (jlu->jlu_format->lf_subsecond_unit.value()) {
3✔
1156
            case log_format::subsecond_unit::milli:
×
1157
                millis = ts_val;
×
1158
                jlu->jlu_exttm.et_nsec = ts_val * 1000000;
×
1159
                jlu->jlu_exttm.et_flags |= ETF_MILLIS_SET;
×
1160
                break;
×
1161
            case log_format::subsecond_unit::micro:
×
1162
                millis = std::chrono::duration_cast<std::chrono::milliseconds>(
×
1163
                             std::chrono::microseconds((int64_t) ts_val))
×
1164
                             .count();
×
1165
                jlu->jlu_exttm.et_nsec = ts_val * 1000;
×
1166
                jlu->jlu_exttm.et_flags |= ETF_MICROS_SET;
×
1167
                break;
×
1168
            case log_format::subsecond_unit::nano:
3✔
1169
                millis = std::chrono::duration_cast<std::chrono::milliseconds>(
3✔
1170
                             std::chrono::nanoseconds((int64_t) ts_val))
3✔
1171
                             .count();
3✔
1172
                jlu->jlu_exttm.et_nsec = ts_val;
3✔
1173
                jlu->jlu_exttm.et_flags |= ETF_NANOS_SET;
3✔
1174
                break;
3✔
1175
        }
1176
        jlu->jlu_exttm.et_flags |= ETF_SUB_NOT_IN_FORMAT;
3✔
1177
        jlu->jlu_base_line->set_subsecond_time(
3✔
1178
            std::chrono::milliseconds(millis));
1179
    } else if (jlu->jlu_format->elf_level_field == field_name) {
523✔
1180
        if (jlu->jlu_format->elf_level_pairs.empty()) {
266✔
1181
            jlu->jlu_base_line->set_level(jlu->jlu_format->convert_level(
152✔
1182
                number_frag, jlu->jlu_batch_context));
1183
        } else {
1184
            auto scan_res
1185
                = scn::scan_int<int64_t>(number_frag.to_string_view());
114✔
1186
            if (!scan_res) {
114✔
1187
                log_error("invalid number %.*s", numberLen, numberVal);
×
1188
                return 0;
×
1189
            }
1190
            auto level_int = scan_res.value().value();
114✔
1191

1192
            for (const auto& pair : jlu->jlu_format->elf_level_pairs) {
431✔
1193
                if (pair.first == level_int) {
385✔
1194
                    jlu->jlu_base_line->set_level(pair.second);
68✔
1195
                    break;
68✔
1196
                }
1197
            }
1198
        }
1199
    } else if (vd != nullptr) {
257✔
1200
        if ((vd->vd_meta.lvm_kind == value_kind_t::VALUE_INTEGER
257✔
1201
             || vd->vd_meta.lvm_kind == value_kind_t::VALUE_FLOAT)
76✔
1202
            && !vd->vd_meta.lvm_foreign_key && !vd->vd_meta.lvm_identifier)
199✔
1203
        {
1204
            auto scan_res
1205
                = scn::scan_value<double>(number_frag.to_string_view());
151✔
1206
            if (!scan_res) {
151✔
1207
                log_error("invalid number %.*s", numberLen, numberVal);
×
1208
                return 0;
×
1209
            }
1210
            val = scan_res.value().value();
151✔
1211
            if (jlu->jlu_format->elf_duration_field == field_name) {
151✔
1212
                jlu->jlu_duration = std::chrono::microseconds(
×
1213
                    static_cast<int64_t>(val.value() * 1000000));
18✔
1214
            }
1215
        }
1216
    }
1217

1218
    jlu->add_sub_lines_for(vd,
19,828✔
1219
                           ypc->is_level(1),
19,828✔
1220
                           val,
1221
                           (const unsigned char*) numberVal,
1222
                           numberLen,
1223
                           nullptr);
1224

1225
    return 1;
19,828✔
1226
}
1227

1228
static int
1229
json_array_start(void* ctx)
41,377✔
1230
{
1231
    auto* ypc = (yajlpp_parse_context*) ctx;
41,377✔
1232
    auto* jlu = (json_log_userdata*) ypc->ypc_userdata;
41,377✔
1233

1234
    if (ypc->ypc_path_index_stack.size() == 2) {
41,377✔
1235
        intern_string_t field_name;
10,770✔
1236
        const auto* vd = jlu->get_field_def(ypc);
10,770✔
1237

1238
        jlu->add_sub_lines_for(vd, true, std::nullopt, nullptr, -1, nullptr);
10,770✔
1239
        jlu->jlu_sub_start = yajl_get_bytes_consumed(jlu->jlu_handle) - 1;
10,770✔
1240
    }
1241

1242
    return 1;
41,377✔
1243
}
1244

1245
static int
1246
json_array_end(void* ctx)
13,309✔
1247
{
1248
    auto* ypc = (yajlpp_parse_context*) ctx;
13,309✔
1249
    auto* jlu = (json_log_userdata*) ypc->ypc_userdata;
13,309✔
1250

1251
    if (ypc->ypc_path_index_stack.size() == 1) {
13,309✔
1252
        const intern_string_t field_name = ypc->get_path_fragment_i(0);
3,561✔
1253
        size_t sub_end = yajl_get_bytes_consumed(jlu->jlu_handle);
3,561✔
1254
        auto json_frag = string_fragment::from_byte_range(
3,561✔
1255
            jlu->jlu_shared_buffer.get_data(), jlu->jlu_sub_start, sub_end);
3,561✔
1256
        jlu->jlu_format->jlf_line_values.lvv_values.emplace_back(
3,561✔
1257
            jlu->jlu_format->get_value_meta(field_name,
7,122✔
1258
                                            value_kind_t::VALUE_JSON),
1259
            json_frag);
1260
        if (field_name == jlu->jlu_format->elf_opid_field) {
3,561✔
1261
            jlu->jlu_opid_desc_frag = json_frag;
×
1262
        }
1263
    }
1264

1265
    return 1;
13,309✔
1266
}
1267

1268
static int
1269
read_array_end(void* ctx)
27,882✔
1270
{
1271
    auto* ypc = (yajlpp_parse_context*) ctx;
27,882✔
1272
    auto* jlu = (json_log_userdata*) ypc->ypc_userdata;
27,882✔
1273

1274
    if (ypc->ypc_path_index_stack.size() == 1) {
27,882✔
1275
        const intern_string_t field_name = ypc->get_path_fragment_i(0);
7,180✔
1276
        size_t sub_end = yajl_get_bytes_consumed(jlu->jlu_handle);
7,180✔
1277
        auto json_frag = string_fragment::from_byte_range(
7,180✔
1278
            jlu->jlu_shared_buffer.get_data(), jlu->jlu_sub_start, sub_end);
7,180✔
1279
        if (field_name == jlu->jlu_format->elf_opid_field) {
7,180✔
1280
            jlu->jlu_opid_desc_frag = json_frag;
×
1281
        }
1282
    }
1283

1284
    return 1;
27,882✔
1285
}
1286

1287
static const json_path_container json_log_handlers = {
1288
    yajlpp::pattern_property_handler("\\w+")
1289
        .add_cb(read_json_null)
1290
        .add_cb(read_json_bool)
1291
        .add_cb(read_json_number)
1292
        .add_cb(read_json_field),
1293
};
1294

1295
static int rewrite_json_field(yajlpp_parse_context* ypc,
1296
                              const unsigned char* str,
1297
                              size_t len,
1298
                              yajl_string_props_t*);
1299

1300
static int
1301
rewrite_json_null(yajlpp_parse_context* ypc)
1,957✔
1302
{
1303
    auto jlu = (json_log_userdata*) ypc->ypc_userdata;
1,957✔
1304
    const auto* vd = jlu->get_field_def(ypc);
1,957✔
1305

1306
    if (!ypc->is_level(1) && vd == nullptr) {
1,957✔
1307
        return 1;
1,957✔
1308
    }
1309
    jlu->jlu_format->jlf_line_values.lvv_values.emplace_back(
×
1310
        jlu->jlu_format->get_value_meta(ypc, vd, value_kind_t::VALUE_NULL));
×
1311

1312
    return 1;
×
1313
}
1314

1315
static int
1316
rewrite_json_bool(yajlpp_parse_context* ypc, int val)
8,809✔
1317
{
1318
    auto* jlu = (json_log_userdata*) ypc->ypc_userdata;
8,809✔
1319
    const auto* vd = jlu->get_field_def(ypc);
8,809✔
1320

1321
    if (!ypc->is_level(1) && vd == nullptr) {
8,809✔
1322
        return 1;
7,861✔
1323
    }
1324
    jlu->jlu_format->jlf_line_values.lvv_values.emplace_back(
948✔
1325
        jlu->jlu_format->get_value_meta(ypc, vd, value_kind_t::VALUE_BOOLEAN),
948✔
1326
        (bool) val);
948✔
1327
    return 1;
948✔
1328
}
1329

1330
static int
1331
rewrite_json_int(yajlpp_parse_context* ypc, long long val)
10,660✔
1332
{
1333
    auto* jlu = (json_log_userdata*) ypc->ypc_userdata;
10,660✔
1334
    const auto* vd = jlu->get_field_def(ypc);
10,660✔
1335

1336
    if (vd != nullptr) {
10,660✔
1337
        const intern_string_t field_name = vd->vd_meta.lvm_name;
708✔
1338
        if (jlu->jlu_format->lf_timestamp_field == field_name) {
708✔
1339
            long long divisor = jlu->jlu_format->elf_timestamp_divisor;
25✔
1340
            timeval tv;
1341

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

1376
    if (!ypc->is_level(1) && vd == nullptr) {
10,660✔
1377
        return 1;
9,905✔
1378
    }
1379
    jlu->jlu_format->jlf_line_values.lvv_values.emplace_back(
755✔
1380
        jlu->jlu_format->get_value_meta(ypc, vd, value_kind_t::VALUE_INTEGER),
755✔
1381
        (int64_t) val);
755✔
1382
    return 1;
755✔
1383
}
1384

1385
static int
1386
rewrite_json_double(yajlpp_parse_context* ypc, double val)
162✔
1387
{
1388
    auto* jlu = (json_log_userdata*) ypc->ypc_userdata;
162✔
1389
    const auto* vd = jlu->get_field_def(ypc);
162✔
1390

1391
    if (vd != nullptr) {
162✔
1392
        const intern_string_t field_name = vd->vd_meta.lvm_name;
158✔
1393
        if (jlu->jlu_format->lf_timestamp_field == field_name) {
158✔
1394
            long long divisor = jlu->jlu_format->elf_timestamp_divisor;
130✔
1395
            timeval tv;
1396

1397
            tv.tv_sec = val / divisor;
130✔
1398
            tv.tv_usec = fmod(val, divisor) * (1000000.0 / divisor);
130✔
1399
            jlu->jlu_format->lf_date_time.to_localtime(tv.tv_sec,
130✔
1400
                                                       jlu->jlu_exttm);
130✔
1401
            jlu->jlu_exttm.et_gmtoff
1402
                = jlu->jlu_format->lf_date_time.dts_local_offset_cache;
130✔
1403
            jlu->jlu_exttm.et_flags |= ETF_MACHINE_ORIENTED
130✔
1404
                | ETF_SUB_NOT_IN_FORMAT | ETF_ZONE_SET | ETF_Z_FOR_UTC;
1405
            if (divisor == 1) {
130✔
1406
                jlu->jlu_exttm.et_flags |= ETF_MICROS_SET;
118✔
1407
            } else {
1408
                jlu->jlu_exttm.et_flags |= ETF_MILLIS_SET;
12✔
1409
            }
1410
            jlu->jlu_exttm.et_nsec = tv.tv_usec * 1000;
130✔
1411
        } else if (jlu->jlu_format->lf_subsecond_field == field_name) {
28✔
1412
            jlu->jlu_exttm.et_flags &= ~(ETF_MICROS_SET | ETF_MILLIS_SET);
×
1413
            switch (jlu->jlu_format->lf_subsecond_unit.value()) {
×
1414
                case log_format::subsecond_unit::milli:
×
1415
                    jlu->jlu_exttm.et_nsec = val * 1000000;
×
1416
                    jlu->jlu_exttm.et_flags |= ETF_MILLIS_SET;
×
1417
                    break;
×
1418
                case log_format::subsecond_unit::micro:
×
1419
                    jlu->jlu_exttm.et_nsec = val * 1000;
×
1420
                    jlu->jlu_exttm.et_flags |= ETF_MICROS_SET;
×
1421
                    break;
×
1422
                case log_format::subsecond_unit::nano:
×
1423
                    jlu->jlu_exttm.et_nsec = val;
×
1424
                    jlu->jlu_exttm.et_flags |= ETF_NANOS_SET;
×
1425
                    break;
×
1426
            }
1427
            jlu->jlu_exttm.et_flags |= ETF_SUB_NOT_IN_FORMAT;
×
1428
        } else if (jlu->jlu_format->elf_duration_field == field_name) {
28✔
1429
            jlu->jlu_duration = std::chrono::microseconds(
56✔
1430
                static_cast<int64_t>(val * 1000000.0));
28✔
1431
        }
1432
    }
1433

1434
    if (!ypc->is_level(1) && vd == nullptr) {
162✔
1435
        return 1;
×
1436
    }
1437
    jlu->jlu_format->jlf_line_values.lvv_values.emplace_back(
162✔
1438
        jlu->jlu_format->get_value_meta(ypc, vd, value_kind_t::VALUE_FLOAT),
324✔
1439
        val);
1440

1441
    return 1;
162✔
1442
}
1443

1444
static const json_path_container json_log_rewrite_handlers = {
1445
    yajlpp::pattern_property_handler("\\w+")
1446
        .add_cb(rewrite_json_null)
1447
        .add_cb(rewrite_json_bool)
1448
        .add_cb(rewrite_json_int)
1449
        .add_cb(rewrite_json_double)
1450
        .add_cb(rewrite_json_field),
1451
};
1452

1453
bool
1454
external_log_format::scan_for_partial(shared_buffer_ref& sbr,
1✔
1455
                                      size_t& len_out) const
1456
{
1457
    if (this->elf_type != elf_type_t::ELF_TYPE_TEXT) {
1✔
1458
        return false;
×
1459
    }
1460

1461
    const auto& pat = this->elf_pattern_order[this->last_pattern_index()];
1✔
1462
    if (!this->lf_multiline) {
1✔
1463
        len_out = pat->p_pcre.pp_value->match_partial(sbr.to_string_fragment());
1✔
1464
        return true;
1✔
1465
    }
1466

1467
    if (pat->p_timestamp_end == -1 || pat->p_timestamp_end > (int) sbr.length())
×
1468
    {
1469
        len_out = 0;
×
1470
        return false;
×
1471
    }
1472

1473
    len_out = pat->p_pcre.pp_value->match_partial(sbr.to_string_fragment());
×
1474
    return (int) len_out > pat->p_timestamp_end;
×
1475
}
1476

1477
std::vector<lnav::console::snippet>
1478
external_log_format::get_snippets() const
23✔
1479
{
1480
    std::vector<lnav::console::snippet> retval;
23✔
1481

1482
    for (const auto& src_pair : this->elf_format_sources) {
46✔
1483
        retval.emplace_back(lnav::console::snippet::from(src_pair.first, "")
46✔
1484
                                .with_line(src_pair.second));
23✔
1485
    }
1486

1487
    return retval;
23✔
1488
}
×
1489

1490
log_format::scan_result_t
1491
external_log_format::scan_json(std::vector<logline>& dst,
108,269✔
1492
                               const line_info& li,
1493
                               shared_buffer_ref& sbr,
1494
                               scan_batch_context& sbc)
1495
{
1496
    logline ll(
1497
        li.li_file_range.fr_offset, std::chrono::microseconds{0}, LEVEL_INFO);
108,269✔
1498
    auto line_frag = sbr.to_string_fragment();
108,269✔
1499

1500
    if (!line_frag.startswith("{")) {
108,269✔
1501
        if (!this->lf_specialized) {
102,365✔
1502
            return scan_no_match{"line is not a JSON object"};
102,360✔
1503
        }
1504

1505
        ll.set_time(dst.back().get_time<std::chrono::microseconds>());
5✔
1506
        ll.set_level(LEVEL_INVALID);
5✔
1507
        dst.emplace_back(ll);
5✔
1508
        return scan_match{0};
5✔
1509
    }
1510

1511
    auto& ypc = *(this->jlf_parse_context);
5,904✔
1512
    yajl_handle handle = this->jlf_yajl_handle.get();
5,904✔
1513
    json_log_userdata jlu(sbr, &sbc);
5,904✔
1514

1515
    if (li.li_partial) {
5,904✔
1516
        log_debug("skipping partial line at offset %d",
×
1517
                  li.li_file_range.fr_offset);
1518
        if (this->lf_specialized) {
×
1519
            if (!dst.empty()) {
×
1520
                ll.set_time(dst.back().get_time<std::chrono::microseconds>());
×
1521
            }
1522
            ll.set_level(LEVEL_INVALID);
×
1523
            dst.emplace_back(ll);
×
1524
        }
1525
        return scan_incomplete{};
×
1526
    }
1527

1528
    const auto* line_data = (const unsigned char*) sbr.get_data();
5,904✔
1529

1530
    this->lf_desc_captures.clear();
5,904✔
1531
    this->lf_desc_allocator.reset();
5,904✔
1532

1533
    yajl_reset(handle);
5,904✔
1534
    ypc.set_static_handler(json_log_handlers.jpc_children[0]);
5,904✔
1535
    ypc.ypc_userdata = &jlu;
5,904✔
1536
    ypc.ypc_ignore_unused = true;
5,904✔
1537
    ypc.ypc_alt_callbacks.yajl_start_array = json_array_start;
5,904✔
1538
    ypc.ypc_alt_callbacks.yajl_start_map = json_array_start;
5,904✔
1539
    ypc.ypc_alt_callbacks.yajl_end_array = read_array_end;
5,904✔
1540
    ypc.ypc_alt_callbacks.yajl_end_map = read_array_end;
5,904✔
1541
    jlu.jlu_format = this;
5,904✔
1542
    jlu.jlu_base_line = &ll;
5,904✔
1543
    jlu.jlu_line_value = sbr.get_data();
5,904✔
1544
    jlu.jlu_line_size = sbr.length();
5,904✔
1545
    jlu.jlu_handle = handle;
5,904✔
1546
    jlu.jlu_format_hits.resize(this->jlf_line_format.size());
5,904✔
1547
    if (yajl_parse(handle, line_data, sbr.length()) == yajl_status_ok
5,904✔
1548
        && yajl_complete_parse(handle) == yajl_status_ok)
5,904✔
1549
    {
1550
        if (ll.get_time<std::chrono::microseconds>().count() == 0) {
5,761✔
1551
            if (this->lf_specialized) {
4,898✔
1552
                if (!dst.empty()) {
×
1553
                    ll.set_time(
×
1554
                        dst.back().get_time<std::chrono::microseconds>());
×
1555
                }
1556
                ll.set_ignore(true);
×
1557
                dst.emplace_back(ll);
×
1558
                return scan_match{0};
×
1559
            }
1560

1561
            return scan_no_match{
4,898✔
1562
                "JSON message does not have expected timestamp property"};
4,898✔
1563
        }
1564

1565
        if (jlu.jlu_tid_frag) {
863✔
1566
            this->jlf_line_values.lvv_thread_id_value
1567
                = jlu.jlu_tid_frag->to_string();
×
1568
            auto tid_iter = sbc.sbc_tids.insert_tid(
×
1569
                sbc.sbc_allocator,
NEW
1570
                jlu.jlu_tid_frag.value(),
×
NEW
1571
                ll.get_time<std::chrono::microseconds>());
×
UNCOV
1572
            tid_iter->second.titr_level_stats.update_msg_count(
×
1573
                ll.get_msg_level());
1574
        } else {
1575
            auto tid_iter = sbc.sbc_tids.insert_tid(
863✔
1576
                sbc.sbc_allocator,
NEW
1577
                string_fragment{},
×
1578
                ll.get_time<std::chrono::microseconds>());
863✔
1579
            tid_iter->second.titr_level_stats.update_msg_count(
863✔
1580
                ll.get_msg_level());
1581
        }
1582

1583
        auto found_opid_desc = false;
863✔
1584
        if (this->elf_opid_field.empty()
863✔
1585
            && this->lf_opid_description_def->size() == 1)
863✔
1586
        {
1587
            const auto& od = this->lf_opid_description_def->begin()->second;
322✔
1588
            for (const auto& desc : *od.od_descriptors) {
966✔
1589
                auto desc_iter
1590
                    = this->lf_desc_captures.find(desc.od_field.pp_value);
644✔
1591
                if (desc_iter == this->lf_desc_captures.end()) {
644✔
1592
                    continue;
602✔
1593
                }
1594
                jlu.jlu_opid_hasher.update(desc_iter->second);
42✔
1595
                found_opid_desc = true;
42✔
1596
            }
1597

1598
        } else if (!jlu.jlu_opid_desc_frag && !jlu.jlu_opid_frag
1,082✔
1599
                   && jlu.jlu_duration > 0us)
1,082✔
1600
        {
1601
            jlu.jlu_opid_hasher.update(sbr.to_string_fragment());
×
1602
        }
1603

1604
        if (jlu.jlu_opid_desc_frag || jlu.jlu_duration > 0us
1,726✔
1605
            || (found_opid_desc && this->lf_opid_description_def->size() == 1))
1,726✔
1606
        {
1607
            char buf[hasher::STRING_SIZE];
1608
            jlu.jlu_opid_hasher.to_string(buf);
21✔
1609
            auto opid_frag = string_fragment::from_bytes(buf, sizeof(buf) - 1);
21✔
1610
            jlu.jlu_base_line->set_opid(opid_frag.hash());
21✔
1611
            auto opid_iter = sbc.sbc_opids.los_opid_ranges.find(opid_frag);
21✔
1612
            if (opid_iter == sbc.sbc_opids.los_opid_ranges.end()) {
21✔
1613
                jlu.jlu_opid_frag = opid_frag.to_owned(sbc.sbc_allocator);
15✔
1614
            } else {
1615
                jlu.jlu_opid_frag = opid_iter->first;
6✔
1616
            }
1617
        }
1618

1619
        if (jlu.jlu_opid_frag) {
863✔
1620
            this->jlf_line_values.lvv_opid_value
1621
                = jlu.jlu_opid_frag->to_string();
185✔
1622
            this->jlf_line_values.lvv_opid_provenance
1623
                = logline_value_vector::opid_provenance::file;
185✔
1624
            auto opid_iter = sbc.sbc_opids.insert_op(
370✔
1625
                sbc.sbc_allocator,
1626
                jlu.jlu_opid_frag.value(),
185✔
1627
                ll.get_time<std::chrono::microseconds>(),
185✔
1628
                this->lf_timestamp_point_of_reference,
1629
                jlu.jlu_duration);
1630
            opid_iter->second.otr_level_stats.update_msg_count(
185✔
1631
                ll.get_msg_level());
1632
            if (jlu.jlu_opid_desc_frag
185✔
1633
                && opid_iter->second.otr_description.lod_elements[0].empty())
185✔
1634
            {
1635
                opid_iter->second.otr_description.lod_elements[0].push_back(
×
1636
                    ' ');
1637
                opid_iter->second.otr_description.lod_elements[0]
×
1638
                    += jlu.jlu_opid_desc_frag.value();
×
1639
            }
1640

1641
            if (jlu.jlu_subid) {
185✔
1642
                auto subid_frag
1643
                    = string_fragment::from_str(jlu.jlu_subid.value());
×
1644

NEW
1645
                auto* ostr = sbc.sbc_opids.sub_op_in_use(
×
1646
                    sbc.sbc_allocator,
1647
                    opid_iter,
1648
                    subid_frag,
NEW
1649
                    ll.get_time<std::chrono::microseconds>(),
×
1650
                    ll.get_msg_level());
1651
                if (ostr != nullptr && ostr->ostr_description.empty()) {
×
1652
                    log_op_description sub_desc;
×
1653
                    this->update_op_description(*this->lf_subid_description_def,
×
1654
                                                sub_desc);
1655
                    if (!sub_desc.lod_elements.empty()) {
×
1656
                        auto& sub_desc_def = this->lf_subid_description_def->at(
×
1657
                            sub_desc.lod_id.value());
×
1658
                        ostr->ostr_description
1659
                            = sub_desc_def.to_string(sub_desc.lod_elements);
×
1660
                    }
1661
                }
1662
            }
1663

1664
            auto& otr = opid_iter->second;
185✔
1665
            this->update_op_description(*this->lf_opid_description_def,
185✔
1666
                                        otr.otr_description);
185✔
1667
        } else {
1668
            this->jlf_line_values.lvv_opid_value = std::nullopt;
678✔
1669
        }
1670

1671
        jlu.jlu_sub_line_count += this->jlf_line_format_init_count;
863✔
1672
        for (int lpc = 0; lpc < jlu.jlu_sub_line_count; lpc++) {
3,042✔
1673
            ll.set_sub_offset(lpc);
2,179✔
1674
            if (lpc > 0) {
2,179✔
1675
                ll.set_level(
1,316✔
1676
                    (log_level_t) (ll.get_level_and_flags() | LEVEL_CONTINUED));
1,316✔
1677
            }
1678
            ll.set_has_ansi(jlu.jlu_has_ansi);
2,179✔
1679
            ll.set_valid_utf(jlu.jlu_valid_utf);
2,179✔
1680
            dst.emplace_back(ll);
2,179✔
1681
        }
1682

1683
        if (!this->lf_specialized) {
863✔
1684
            static const intern_string_t ts_field
1685
                = intern_string::lookup("__timestamp__", -1);
360✔
1686
            static const intern_string_t level_field
1687
                = intern_string::lookup("__level__");
448✔
1688
            for (const auto& [index, jfe] :
4,059✔
1689
                 lnav::itertools::enumerate(this->jlf_line_format))
4,419✔
1690
            {
1691
                if (jfe.jfe_type != json_log_field::VARIABLE
10,276✔
1692
                    || jfe.jfe_value.pp_value == ts_field
3,030✔
1693
                    || jfe.jfe_value.pp_value == level_field
2,724✔
1694
                    || jfe.jfe_default_value != "-")
6,729✔
1695
                {
1696
                    continue;
2,878✔
1697
                }
1698
                if (!jlu.jlu_format_hits[index]) {
821✔
1699
                    jlu.jlu_strikes += 1;
414✔
1700
                }
1701
            }
1702
        }
1703
    } else {
1704
        unsigned char* msg;
1705
        int line_count = 2;
143✔
1706

1707
        msg = yajl_get_error(
286✔
1708
            handle, 1, (const unsigned char*) sbr.get_data(), sbr.length());
143✔
1709
        if (msg != nullptr) {
143✔
1710
            auto msg_frag = string_fragment::from_c_str(msg);
143✔
1711
            log_debug("Unable to parse line at offset %d: %s",
143✔
1712
                      li.li_file_range.fr_offset,
1713
                      msg);
1714
            line_count = msg_frag.count('\n') + 1;
143✔
1715
            yajl_free_error(handle, msg);
143✔
1716
        }
1717
        if (!this->lf_specialized) {
143✔
1718
            return scan_no_match{"JSON parsing failed"};
140✔
1719
        }
1720
        for (int lpc = 0; lpc < line_count; lpc++) {
15✔
1721
            log_level_t level = LEVEL_INVALID;
12✔
1722

1723
            ll.set_time(dst.back().get_timeval());
12✔
1724
            if (lpc > 0) {
12✔
1725
                level = (log_level_t) (level | LEVEL_CONTINUED);
9✔
1726
            }
1727
            ll.set_level(level);
12✔
1728
            ll.set_sub_offset(lpc);
12✔
1729
            dst.emplace_back(ll);
12✔
1730
        }
1731
    }
1732

1733
    if (jlu.jlu_quality > 0) {
866✔
1734
        jlu.jlu_quality += 3000;
768✔
1735
    }
1736
    return scan_match{jlu.jlu_quality, jlu.jlu_strikes};
866✔
1737
}
5,904✔
1738

1739
log_format::scan_result_t
1740
external_log_format::scan(logfile& lf,
712,046✔
1741
                          std::vector<logline>& dst,
1742
                          const line_info& li,
1743
                          shared_buffer_ref& sbr,
1744
                          scan_batch_context& sbc)
1745
{
1746
    if (dst.empty()) {
712,046✔
1747
        auto file_options = lf.get_file_options();
31,295✔
1748

1749
        if (file_options) {
31,295✔
1750
            this->lf_date_time.dts_default_zone
1751
                = file_options->second.fo_default_zone.pp_value;
2,215✔
1752
        } else {
1753
            this->lf_date_time.dts_default_zone = nullptr;
29,080✔
1754
        }
1755
    }
31,295✔
1756

1757
    if (this->elf_type == elf_type_t::ELF_TYPE_JSON) {
712,046✔
1758
        return this->scan_json(dst, li, sbr, sbc);
108,267✔
1759
    }
1760

1761
    int curr_fmt = -1, orig_lock = this->last_pattern_index();
603,779✔
1762
    int pat_index = orig_lock;
603,779✔
1763
    auto line_sf = sbr.to_string_fragment();
603,779✔
1764
    thread_local auto md = lnav::pcre2pp::match_data::unitialized();
603,779✔
1765
    char tmp_opid_buf[hasher::STRING_SIZE];
1766

1767
    while (::next_format(this->elf_pattern_order, curr_fmt, pat_index)) {
1,999,121✔
1768
        auto* fpat = this->elf_pattern_order[curr_fmt].get();
1,401,143✔
1769
        auto* pat = fpat->p_pcre.pp_value.get();
1,401,143✔
1770

1771
        if (fpat->p_module_format) {
1,401,143✔
1772
            continue;
1,395,342✔
1773
        }
1774

1775
        auto found_match
1776
            = pat->capture_from(line_sf).into(md).found_p(PCRE2_NO_UTF_CHECK);
1,370,189✔
1777
        if (!found_match) {
1,370,189✔
1778
            if (!this->lf_pattern_locks.empty() && pat_index != -1) {
1,364,385✔
1779
                curr_fmt = -1;
1,994✔
1780
                pat_index = -1;
1,994✔
1781
            }
1782
            continue;
1,364,385✔
1783
        }
1784

1785
        auto ts = md[fpat->p_timestamp_field_index];
5,804✔
1786
        auto level_cap = md[fpat->p_level_field_index];
5,804✔
1787
        auto opid_cap = md[fpat->p_opid_field_index];
5,804✔
1788
        auto body_cap = md[fpat->p_body_field_index];
5,804✔
1789
        const char* last;
1790
        exttm log_time_tm;
5,804✔
1791
        timeval log_tv;
1792
        uint8_t mod_index = 0;
5,804✔
1793
        uint16_t opid = 0;
5,804✔
1794
        char combined_datetime_buf[512];
1795

1796
        if (fpat->p_time_field_index != -1) {
5,804✔
1797
            auto time_cap = md[fpat->p_time_field_index];
×
1798
            if (ts && time_cap) {
×
1799
                auto ts_str_len = snprintf(combined_datetime_buf,
×
1800
                                           sizeof(combined_datetime_buf),
1801
                                           "%.*sT%.*s",
1802
                                           ts->length(),
1803
                                           ts->data(),
1804
                                           time_cap->length(),
1805
                                           time_cap->data());
1806
                ts = string_fragment::from_bytes(combined_datetime_buf,
×
1807
                                                 ts_str_len);
×
1808
            }
1809
        }
1810

1811
        auto level = this->convert_level(
5,804✔
1812
            level_cap.value_or(string_fragment::invalid()), &sbc);
5,804✔
1813

1814
        if (!ts) {
5,804✔
1815
            level = log_level_t::LEVEL_INVALID;
×
1816
        } else if ((last
5,804✔
1817
                    = this->lf_date_time.scan(ts->data(),
11,608✔
1818
                                              ts->length(),
5,804✔
1819
                                              this->get_timestamp_formats(),
1820
                                              &log_time_tm,
1821
                                              log_tv))
1822
                   == nullptr)
5,804✔
1823
        {
1824
            auto ls = this->lf_date_time.unlock();
13✔
1825
            if ((last = this->lf_date_time.scan(ts->data(),
26✔
1826
                                                ts->length(),
13✔
1827
                                                this->get_timestamp_formats(),
1828
                                                &log_time_tm,
1829
                                                log_tv))
1830
                == nullptr)
13✔
1831
            {
1832
                this->lf_date_time.relock(ls);
2✔
1833
                continue;
3✔
1834
            }
1835
            if (last != nullptr) {
11✔
1836
                auto old_flags = this->lf_timestamp_flags & DATE_TIME_SET_FLAGS;
11✔
1837
                auto new_flags = log_time_tm.et_flags & DATE_TIME_SET_FLAGS;
11✔
1838

1839
                // It is unlikely a valid timestamp would lose much
1840
                // precision.
1841
                if (new_flags != old_flags) {
11✔
1842
                    continue;
1✔
1843
                }
1844
            }
1845

1846
            log_debug("%s:%d: date-time re-locked to %d",
10✔
1847
                      lf.get_unique_path().c_str(),
1848
                      dst.size(),
1849
                      this->lf_date_time.dts_fmt_lock);
1850
        }
1851

1852
        this->lf_timestamp_flags = log_time_tm.et_flags;
5,801✔
1853

1854
        if (!(this->lf_timestamp_flags
11,602✔
1855
              & (ETF_MILLIS_SET | ETF_MICROS_SET | ETF_NANOS_SET))
5,801✔
1856
            && !dst.empty()
5,257✔
1857
            && dst.back().get_time<std::chrono::seconds>().count()
4,534✔
1858
                == log_tv.tv_sec
4,534✔
1859
            && dst.back()
14,484✔
1860
                    .get_subsecond_time<std::chrono::milliseconds>()
9,227✔
1861
                    .count()
3,426✔
1862
                != 0)
1863
        {
1864
            auto log_ms
1865
                = dst.back().get_subsecond_time<std::chrono::microseconds>();
×
1866

1867
            log_time_tm.et_nsec
1868
                = std::chrono::duration_cast<std::chrono::nanoseconds>(log_ms)
×
1869
                      .count();
×
1870
            log_tv.tv_usec
1871
                = std::chrono::duration_cast<std::chrono::microseconds>(log_ms)
×
1872
                      .count();
×
1873
        }
1874

1875
        if (!((log_time_tm.et_flags & ETF_DAY_SET)
5,801✔
1876
              && (log_time_tm.et_flags & ETF_MONTH_SET)
5,760✔
1877
              && (log_time_tm.et_flags & ETF_YEAR_SET)))
5,760✔
1878
        {
1879
            this->check_for_new_year(dst, log_time_tm, log_tv);
792✔
1880
        }
1881

1882
        auto log_us = to_us(log_tv);
5,801✔
1883
        if (!fpat->p_opid_description_field_indexes.empty()) {
5,801✔
1884
            hasher h;
4,568✔
1885
            for (auto& fidx : fpat->p_opid_description_field_indexes) {
13,109✔
1886
                auto desc_cap = md[fidx];
8,541✔
1887
                if (desc_cap) {
8,541✔
1888
                    h.update(desc_cap.value());
8,494✔
1889
                }
1890
            }
1891
            h.to_string(tmp_opid_buf);
4,568✔
1892
            opid_cap = string_fragment::from_bytes(tmp_opid_buf,
9,136✔
1893
                                                   sizeof(tmp_opid_buf) - 1);
4,568✔
1894
        }
1895

1896
        auto duration_cap = md[fpat->p_duration_field_index];
5,801✔
1897
        if (duration_cap && !opid_cap) {
5,801✔
1898
            hasher h;
×
1899
            h.update(line_sf);
×
1900
            h.to_string(tmp_opid_buf);
×
1901
            opid_cap = string_fragment::from_bytes(tmp_opid_buf,
×
1902
                                                   sizeof(tmp_opid_buf) - 1);
×
1903
        }
1904
        if (opid_cap && !opid_cap->empty()) {
5,801✔
1905
            auto duration = std::chrono::microseconds{0};
5,364✔
1906
            if (duration_cap) {
5,364✔
1907
                auto from_res
1908
                    = humanize::try_from<double>(duration_cap.value());
86✔
1909
                if (from_res) {
86✔
1910
                    duration = std::chrono::microseconds(
×
1911
                        static_cast<int64_t>(from_res.value() * 1000000));
86✔
1912
                }
1913
            }
1914
            auto opid_iter
1915
                = sbc.sbc_opids.insert_op(sbc.sbc_allocator,
10,728✔
1916
                                          opid_cap.value(),
5,364✔
1917
                                          log_us,
1918
                                          this->lf_timestamp_point_of_reference,
1919
                                          duration);
1920
            auto& otr = opid_iter->second;
5,364✔
1921

1922
            otr.otr_level_stats.update_msg_count(level);
5,364✔
1923
            if (fpat->p_subid_field_index != -1) {
5,364✔
1924
                auto subid_cap = md[fpat->p_subid_field_index];
64✔
1925
                if (subid_cap && !subid_cap->empty()) {
64✔
1926
                    auto* ostr = sbc.sbc_opids.sub_op_in_use(sbc.sbc_allocator,
192✔
1927
                                                             opid_iter,
1928
                                                             subid_cap.value(),
64✔
1929
                                                             log_us,
1930
                                                             level);
1931
                    if (ostr != nullptr && ostr->ostr_description.empty()) {
64✔
1932
                        log_op_description sub_desc;
43✔
1933
                        this->update_op_description(
43✔
1934
                            *this->lf_subid_description_def,
43✔
1935
                            sub_desc,
1936
                            fpat,
1937
                            md);
1938
                        if (!sub_desc.lod_elements.empty()) {
43✔
1939
                            auto& sub_desc_def
1940
                                = this->lf_subid_description_def->at(
39✔
1941
                                    sub_desc.lod_id.value());
39✔
1942
                            ostr->ostr_description
1943
                                = sub_desc_def.to_string(sub_desc.lod_elements);
39✔
1944
                        }
1945
                    }
43✔
1946
                }
1947
            }
1948
            this->update_op_description(
5,364✔
1949
                *this->lf_opid_description_def, otr.otr_description, fpat, md);
5,364✔
1950
            opid = opid_cap->hash();
5,364✔
1951
        }
1952
        if (fpat->p_module_field_index != -1) {
5,801✔
1953
            auto mod_cap = md[fpat->p_module_field_index];
637✔
1954
            if (mod_cap && body_cap) {
637✔
1955
                intern_string_t mod_name
1956
                    = intern_string::lookup(mod_cap.value());
634✔
1957
                auto mod_iter = MODULE_FORMATS.find(mod_name);
634✔
1958

1959
                if (mod_iter == MODULE_FORMATS.end()) {
634✔
1960
                    mod_index = this->module_scan(body_cap.value(), mod_name);
163✔
1961
                    mod_iter = MODULE_FORMATS.find(mod_name);
163✔
1962
                } else if (mod_iter->second.mf_mod_format) {
471✔
1963
                    mod_index = mod_iter->second.mf_mod_format->lf_mod_index;
4✔
1964
                }
1965

1966
                if (mod_index && level_cap && body_cap) {
634✔
1967
                    auto mod_elf
1968
                        = std::dynamic_pointer_cast<external_log_format>(
1969
                            mod_iter->second.mf_mod_format);
47✔
1970

1971
                    if (mod_elf) {
47✔
1972
                        thread_local auto mod_md
1973
                            = lnav::pcre2pp::match_data::unitialized();
47✔
1974

1975
                        shared_buffer_ref body_ref;
47✔
1976

1977
                        body_cap->trim();
47✔
1978

1979
                        int mod_pat_index = mod_elf->last_pattern_index();
47✔
1980
                        auto& mod_pat
1981
                            = *mod_elf->elf_pattern_order[mod_pat_index];
47✔
1982
                        auto match_res = mod_pat.p_pcre.pp_value
47✔
1983
                                             ->capture_from(body_cap.value())
47✔
1984
                                             .into(mod_md)
47✔
1985
                                             .matches(PCRE2_NO_UTF_CHECK)
94✔
1986
                                             .ignore_error();
47✔
1987
                        if (match_res) {
47✔
1988
                            auto mod_level_cap
1989
                                = mod_md[mod_pat.p_level_field_index];
47✔
1990

1991
                            level = mod_elf->convert_level(
94✔
1992
                                mod_level_cap.value_or(
1993
                                    string_fragment::invalid()),
94✔
1994
                                &sbc);
1995
                        }
1996
                    }
47✔
1997
                }
47✔
1998
            }
1999
        }
2000

2001
        for (const auto& ivd : fpat->p_value_by_index) {
65,623✔
2002
            if (!ivd.ivd_value_def->vd_meta.lvm_values_index) {
59,822✔
2003
                continue;
10,944✔
2004
            }
2005

2006
            ssize_t cap_size = md.capture_size(ivd.ivd_index);
48,878✔
2007
            auto& lvs = this->lf_value_stats[ivd.ivd_value_def->vd_meta
48,878✔
2008
                                                 .lvm_values_index.value()];
48,878✔
2009

2010
            if (cap_size > lvs.lvs_width) {
48,878✔
2011
                lvs.lvs_width = cap_size;
3,621✔
2012
            }
2013
        }
2014

2015
        for (auto value_index : fpat->p_numeric_value_indexes) {
10,592✔
2016
            const indexed_value_def& ivd = fpat->p_value_by_index[value_index];
4,791✔
2017
            const value_def& vd = *ivd.ivd_value_def;
4,791✔
2018
            auto num_cap = md[ivd.ivd_index];
4,791✔
2019

2020
            if (num_cap && num_cap->is_valid()) {
4,791✔
2021
                const struct scaling_factor* scaling = nullptr;
4,747✔
2022

2023
                if (ivd.ivd_unit_field_index >= 0) {
4,747✔
2024
                    auto unit_cap = md[ivd.ivd_unit_field_index];
80✔
2025

2026
                    if (unit_cap && unit_cap->is_valid()) {
80✔
2027
                        intern_string_t unit_val
2028
                            = intern_string::lookup(unit_cap.value());
80✔
2029

2030
                        auto unit_iter = vd.vd_unit_scaling.find(unit_val);
80✔
2031
                        if (unit_iter != vd.vd_unit_scaling.end()) {
80✔
2032
                            const auto& sf = unit_iter->second;
80✔
2033

2034
                            scaling = &sf;
80✔
2035
                        }
2036
                    }
2037
                }
2038

2039
                std::optional<double> dvalue_opt;
4,747✔
2040
                switch (vd.vd_meta.lvm_kind) {
4,747✔
2041
                    case value_kind_t::VALUE_INTEGER: {
4,581✔
2042
                        auto scan_res
2043
                            = scn::scan_int<int64_t>(num_cap->to_string_view());
4,581✔
2044
                        if (scan_res) {
4,581✔
2045
                            dvalue_opt = scan_res->value();
4,581✔
2046
                        }
2047
                        break;
4,581✔
2048
                    }
2049
                    case value_kind_t::VALUE_FLOAT: {
166✔
2050
                        auto scan_res = scn::scan_value<double>(
2051
                            num_cap->to_string_view());
166✔
2052
                        if (scan_res) {
166✔
2053
                            dvalue_opt = scan_res->value();
166✔
2054
                        }
2055
                        break;
166✔
2056
                    }
2057
                    default:
×
2058
                        break;
×
2059
                }
2060
                if (dvalue_opt) {
4,747✔
2061
                    auto dvalue = dvalue_opt.value();
4,747✔
2062
                    if (scaling != nullptr) {
4,747✔
2063
                        scaling->scale(dvalue);
80✔
2064
                    }
2065
                    this->lf_value_stats[vd.vd_meta.lvm_values_index.value()]
4,747✔
2066
                        .add_value(dvalue);
4,747✔
2067
                }
2068
            }
2069
        }
2070

2071
        dst.emplace_back(
5,801✔
2072
            li.li_file_range.fr_offset, log_us, level, mod_index, opid);
5,801✔
2073

2074
        auto src_file_cap = md[fpat->p_src_file_field_index];
5,801✔
2075
        auto src_line_cap = md[fpat->p_src_line_field_index];
5,801✔
2076
        if (src_file_cap && src_line_cap) {
5,801✔
2077
            auto h = hasher();
94✔
2078
            h.update(this->get_name().c_str());
94✔
2079
            h.update(src_file_cap.value());
94✔
2080
            h.update(src_line_cap.value());
94✔
2081
            dst.back().set_schema(h.to_array());
94✔
2082
        }
2083
        auto thread_id_cap = md[fpat->p_thread_id_field_index];
5,801✔
2084
        if (thread_id_cap) {
5,801✔
2085
            auto tid_iter = sbc.sbc_tids.insert_tid(
1,988✔
2086
                sbc.sbc_allocator, thread_id_cap.value(), log_us);
994✔
2087
            tid_iter->second.titr_level_stats.update_msg_count(level);
994✔
2088
        } else {
2089
            auto tid_iter = sbc.sbc_tids.insert_tid(
4,807✔
NEW
2090
                sbc.sbc_allocator, string_fragment{}, log_us);
×
2091
            tid_iter->second.titr_level_stats.update_msg_count(level);
4,807✔
2092
        }
2093

2094
        if (orig_lock != curr_fmt) {
5,801✔
2095
            uint32_t lock_line;
2096

2097
            log_debug("%s:%zu: changing pattern lock %d -> (%d)%s",
584✔
2098
                      lf.get_unique_path().c_str(),
2099
                      dst.size() - 1,
2100
                      orig_lock,
2101
                      curr_fmt,
2102
                      this->elf_pattern_order[curr_fmt]->p_name.c_str());
2103
            if (this->lf_pattern_locks.empty()) {
584✔
2104
                lock_line = 0;
558✔
2105
            } else {
2106
                lock_line = dst.size() - 1;
26✔
2107
            }
2108
            this->lf_pattern_locks.emplace_back(lock_line, curr_fmt);
584✔
2109
        }
2110
        return scan_match{1000};
5,801✔
2111
    }
2112

2113
    if (this->lf_specialized && !this->lf_multiline) {
597,978✔
2114
        const auto& last_line = dst.back();
1✔
2115

2116
        log_debug("%s: invalid line %d file_offset=%" PRIu64,
1✔
2117
                  lf.get_filename().c_str(),
2118
                  dst.size(),
2119
                  li.li_file_range.fr_offset);
2120
        dst.emplace_back(li.li_file_range.fr_offset,
1✔
2121
                         last_line.get_timeval(),
×
2122
                         log_level_t::LEVEL_INVALID);
1✔
2123

2124
        return scan_match{0};
1✔
2125
    }
2126

2127
    return scan_no_match{"no patterns matched"};
597,977✔
2128
}
2129

2130
uint8_t
2131
external_log_format::module_scan(string_fragment body_cap,
163✔
2132
                                 const intern_string_t& mod_name)
2133
{
2134
    uint8_t mod_index;
2135
    body_cap = body_cap.trim();
163✔
2136
    auto& ext_fmts = GRAPH_ORDERED_FORMATS;
163✔
2137
    module_format mf;
163✔
2138

2139
    for (auto& elf : ext_fmts) {
10,104✔
2140
        int curr_fmt = -1, fmt_lock = -1;
9,984✔
2141

2142
        while (::next_format(elf->elf_pattern_order, curr_fmt, fmt_lock)) {
28,386✔
2143
            thread_local auto md = lnav::pcre2pp::match_data::unitialized();
18,445✔
2144

2145
            auto& fpat = elf->elf_pattern_order[curr_fmt];
18,445✔
2146
            auto& pat = fpat->p_pcre;
18,445✔
2147

2148
            if (!fpat->p_module_format) {
18,445✔
2149
                continue;
18,402✔
2150
            }
2151

2152
            auto match_res = pat.pp_value->capture_from(body_cap)
445✔
2153
                                 .into(md)
445✔
2154
                                 .matches(PCRE2_NO_UTF_CHECK)
890✔
2155
                                 .ignore_error();
445✔
2156
            if (!match_res) {
445✔
2157
                continue;
402✔
2158
            }
2159

2160
            log_debug("%s:module format found -- %s (%d)",
43✔
2161
                      mod_name.get(),
2162
                      elf->get_name().get(),
2163
                      elf->lf_mod_index);
2164

2165
            mod_index = elf->lf_mod_index;
43✔
2166
            mf.mf_mod_format = elf->specialized(curr_fmt);
43✔
2167
            MODULE_FORMATS[mod_name] = mf;
43✔
2168

2169
            return mod_index;
43✔
2170
        }
2171
    }
2172

2173
    MODULE_FORMATS[mod_name] = mf;
120✔
2174

2175
    return 0;
120✔
2176
}
163✔
2177

2178
void
2179
external_log_format::annotate(logfile* lf,
7,333✔
2180
                              uint64_t line_number,
2181
                              string_attrs_t& sa,
2182
                              logline_value_vector& values,
2183
                              bool annotate_module) const
2184
{
2185
    thread_local auto md = lnav::pcre2pp::match_data::unitialized();
7,333✔
2186

2187
    auto& line = values.lvv_sbr;
7,333✔
2188
    line_range lr;
7,333✔
2189

2190
    line.erase_ansi();
7,333✔
2191
    if (this->elf_type != elf_type_t::ELF_TYPE_TEXT) {
7,333✔
2192
        if (this->jlf_cached_opts.full_message) {
723✔
2193
            values = this->jlf_line_values;
305✔
2194
            sa = this->jlf_line_attrs;
305✔
2195
        } else {
2196
            values.lvv_sbr = this->jlf_line_values.lvv_sbr.clone();
418✔
2197
            for (const auto& llv : this->jlf_line_values.lvv_values) {
4,198✔
2198
                if (this->jlf_cached_sub_range.contains(llv.lv_origin)) {
3,780✔
2199
                    values.lvv_values.emplace_back(llv);
908✔
2200
                    values.lvv_values.back().lv_origin.shift(
908✔
2201
                        this->jlf_cached_sub_range.lr_start,
908✔
2202
                        -this->jlf_cached_sub_range.lr_start);
908✔
2203
                }
2204
            }
2205
            for (const auto& attr : this->jlf_line_attrs) {
2,275✔
2206
                if (this->jlf_cached_sub_range.contains(attr.sa_range)) {
1,857✔
2207
                    sa.emplace_back(attr);
666✔
2208
                    sa.back().sa_range.shift(
666✔
2209
                        this->jlf_cached_sub_range.lr_start,
666✔
2210
                        -this->jlf_cached_sub_range.lr_start);
666✔
2211
                }
2212
            }
2213
            values.lvv_opid_value = this->jlf_line_values.lvv_opid_value;
418✔
2214
            values.lvv_opid_provenance
2215
                = this->jlf_line_values.lvv_opid_provenance;
418✔
2216
            values.lvv_thread_id_value
2217
                = this->jlf_line_values.lvv_thread_id_value;
418✔
2218
        }
2219
        log_format::annotate(lf, line_number, sa, values, annotate_module);
723✔
2220
        return;
2,346✔
2221
    }
2222

2223
    if (line.empty()) {
6,610✔
2224
        return;
5✔
2225
    }
2226

2227
    values.lvv_values.reserve(this->elf_value_defs.size());
6,605✔
2228

2229
    int pat_index = this->pattern_index_for_line(line_number);
6,605✔
2230
    const auto& pat = *this->elf_pattern_order[pat_index];
6,605✔
2231
    char tmp_opid_buf[hasher::STRING_SIZE];
2232

2233
    sa.reserve(pat.p_pcre.pp_value->get_capture_count());
6,605✔
2234
    auto match_res
2235
        = pat.p_pcre.pp_value->capture_from(line.to_string_fragment())
6,605✔
2236
              .into(md)
6,605✔
2237
              .matches(PCRE2_NO_UTF_CHECK)
13,210✔
2238
              .ignore_error();
6,605✔
2239
    if (!match_res) {
6,605✔
2240
        // A continued line still needs a body.
2241
        lr.lr_start = 0;
1,618✔
2242
        lr.lr_end = line.length();
1,618✔
2243
        sa.emplace_back(lr, SA_BODY.value());
1,618✔
2244
        if (!this->lf_multiline) {
1,618✔
2245
            auto len
2246
                = pat.p_pcre.pp_value->match_partial(line.to_string_fragment());
×
2247
            sa.emplace_back(
×
2248
                line_range{(int) len, -1},
×
2249
                SA_INVALID.value("Log line does not match any pattern"));
×
2250
        }
2251
        return;
1,618✔
2252
    }
2253

2254
    auto duration_cap = md[pat.p_duration_field_index];
4,987✔
2255

2256
    std::optional<string_fragment> module_cap;
4,987✔
2257
    if (!pat.p_module_format) {
4,987✔
2258
        auto ts_cap = md[pat.p_timestamp_field_index];
4,978✔
2259
        if (ts_cap) {
4,978✔
2260
            sa.emplace_back(to_line_range(ts_cap.value()), L_TIMESTAMP.value());
4,978✔
2261
        }
2262

2263
        if (pat.p_module_field_index != -1) {
4,978✔
2264
            module_cap = md[pat.p_module_field_index];
392✔
2265
            if (module_cap) {
392✔
2266
                sa.emplace_back(to_line_range(module_cap.value()),
390✔
2267
                                L_MODULE.value());
780✔
2268
            }
2269
        }
2270

2271
        auto opid_cap = md[pat.p_opid_field_index];
4,978✔
2272

2273
        if (!pat.p_opid_description_field_indexes.empty()) {
4,978✔
2274
            hasher h;
4,181✔
2275
            for (auto& fidx : pat.p_opid_description_field_indexes) {
12,100✔
2276
                auto desc_cap = md[fidx];
7,919✔
2277
                if (desc_cap) {
7,919✔
2278
                    h.update(desc_cap.value());
7,891✔
2279
                }
2280
            }
2281
            h.to_string(tmp_opid_buf);
4,181✔
2282
            opid_cap = string_fragment::from_bytes(tmp_opid_buf,
8,362✔
2283
                                                   sizeof(tmp_opid_buf) - 1);
4,181✔
2284
        } else if (duration_cap && !opid_cap) {
797✔
2285
            hasher h;
×
2286
            h.update(line.to_string_fragment());
×
2287
            h.to_string(tmp_opid_buf);
×
2288
            opid_cap = string_fragment::from_bytes(tmp_opid_buf,
×
2289
                                                   sizeof(tmp_opid_buf) - 1);
×
2290
        }
2291
        if (opid_cap && !opid_cap->empty()) {
4,978✔
2292
            sa.emplace_back(to_line_range(opid_cap.value()), L_OPID.value());
4,625✔
2293
            values.lvv_opid_value = opid_cap->to_string();
4,625✔
2294
            values.lvv_opid_provenance
2295
                = logline_value_vector::opid_provenance::file;
4,625✔
2296
        }
2297
    }
2298

2299
    auto body_cap = md[pat.p_body_field_index];
4,987✔
2300
    auto level_cap = md[pat.p_level_field_index];
4,987✔
2301
    auto src_file_cap = md[pat.p_src_file_field_index];
4,987✔
2302
    auto src_line_cap = md[pat.p_src_line_field_index];
4,987✔
2303
    auto thread_id_cap = md[pat.p_thread_id_field_index];
4,987✔
2304

2305
    if (level_cap
4,987✔
2306
        && (!body_cap
9,921✔
2307
            || (body_cap && level_cap->sf_begin != body_cap->sf_begin)))
9,921✔
2308
    {
2309
        sa.emplace_back(to_line_range(level_cap.value()), L_LEVEL.value());
4,542✔
2310
    }
2311

2312
    if (src_file_cap) {
4,987✔
2313
        sa.emplace_back(to_line_range(src_file_cap.value()),
105✔
2314
                        SA_SRC_FILE.value());
210✔
2315
    }
2316
    if (src_line_cap) {
4,987✔
2317
        sa.emplace_back(to_line_range(src_line_cap.value()),
105✔
2318
                        SA_SRC_LINE.value());
210✔
2319
    }
2320
    if (thread_id_cap) {
4,987✔
2321
        sa.emplace_back(to_line_range(thread_id_cap.value()),
590✔
2322
                        SA_THREAD_ID.value());
1,180✔
2323
        values.lvv_thread_id_value = thread_id_cap->to_string();
590✔
2324
    }
2325
    if (duration_cap) {
4,987✔
2326
        sa.emplace_back(to_line_range(duration_cap.value()),
3✔
2327
                        SA_DURATION.value());
6✔
2328
    }
2329

2330
    for (size_t lpc = 0; lpc < pat.p_value_by_index.size(); lpc++) {
57,077✔
2331
        const auto& ivd = pat.p_value_by_index[lpc];
52,090✔
2332
        const scaling_factor* scaling = nullptr;
52,090✔
2333
        auto cap = md[ivd.ivd_index];
52,090✔
2334
        const auto& vd = *ivd.ivd_value_def;
52,090✔
2335

2336
        if (ivd.ivd_unit_field_index >= 0) {
52,090✔
2337
            auto unit_cap = md[ivd.ivd_unit_field_index];
10✔
2338

2339
            if (unit_cap) {
10✔
2340
                intern_string_t unit_val
2341
                    = intern_string::lookup(unit_cap.value());
10✔
2342
                auto unit_iter = vd.vd_unit_scaling.find(unit_val);
10✔
2343
                if (unit_iter != vd.vd_unit_scaling.end()) {
10✔
2344
                    const auto& sf = unit_iter->second;
10✔
2345

2346
                    scaling = &sf;
10✔
2347
                }
2348
            }
2349
        }
2350

2351
        if (cap) {
52,090✔
2352
            values.lvv_values.emplace_back(
85,196✔
2353
                vd.vd_meta, line, to_line_range(cap.value()));
42,598✔
2354
            values.lvv_values.back().apply_scaling(scaling);
42,598✔
2355
        } else {
2356
            values.lvv_values.emplace_back(vd.vd_meta);
9,492✔
2357
        }
2358
        if (pat.p_module_format) {
52,090✔
2359
            values.lvv_values.back().lv_meta.lvm_from_module = true;
64✔
2360
        }
2361
    }
2362

2363
    bool did_mod_annotate_body = false;
4,987✔
2364
    if (annotate_module && module_cap && body_cap && body_cap->is_valid()) {
4,987✔
2365
        intern_string_t mod_name = intern_string::lookup(module_cap.value());
102✔
2366
        auto mod_iter = MODULE_FORMATS.find(mod_name);
102✔
2367

2368
        if (mod_iter != MODULE_FORMATS.end()
102✔
2369
            && mod_iter->second.mf_mod_format != nullptr)
102✔
2370
        {
2371
            auto& mf = mod_iter->second;
7✔
2372
            auto narrow_res
2373
                = line.narrow(body_cap->sf_begin, body_cap->length());
7✔
2374
            auto pre_mod_values_size = values.lvv_values.size();
7✔
2375
            auto pre_mod_sa_size = sa.size();
7✔
2376
            mf.mf_mod_format->annotate(lf, line_number, sa, values, false);
7✔
2377
            for (size_t lpc = pre_mod_values_size;
49✔
2378
                 lpc < values.lvv_values.size();
49✔
2379
                 lpc++)
2380
            {
2381
                values.lvv_values[lpc].lv_origin.shift(0, body_cap->sf_begin);
42✔
2382
            }
2383
            for (size_t lpc = pre_mod_sa_size; lpc < sa.size(); lpc++) {
14✔
2384
                sa[lpc].sa_range.shift(0, body_cap->sf_begin);
7✔
2385
            }
2386
            line.widen(narrow_res);
7✔
2387
            did_mod_annotate_body = true;
7✔
2388
        }
2389
    }
2390
    if (!did_mod_annotate_body) {
4,987✔
2391
        if (body_cap && body_cap->is_valid()) {
4,980✔
2392
            lr = to_line_range(body_cap.value());
4,961✔
2393
        } else {
2394
            lr.lr_start = line.length();
19✔
2395
            lr.lr_end = line.length();
19✔
2396
        }
2397
        sa.emplace_back(lr, SA_BODY.value());
4,980✔
2398
    }
2399

2400
    log_format::annotate(lf, line_number, sa, values, annotate_module);
4,987✔
2401
}
2402

2403
void
2404
external_log_format::rewrite(exec_context& ec,
43✔
2405
                             shared_buffer_ref& line,
2406
                             string_attrs_t& sa,
2407
                             std::string& value_out)
2408
{
2409
    auto& values = *ec.ec_line_values;
43✔
2410

2411
    value_out.assign(line.get_data(), line.length());
43✔
2412

2413
    for (auto iter = values.lvv_values.begin(); iter != values.lvv_values.end();
259✔
2414
         ++iter)
216✔
2415
    {
2416
        if (!iter->lv_origin.is_valid()) {
216✔
2417
            log_debug("%d: not rewriting value with invalid origin -- %s",
24✔
2418
                      ec.ec_top_line,
2419
                      iter->lv_meta.lvm_name.get());
2420
            continue;
184✔
2421
        }
2422

2423
        auto vd_iter = this->elf_value_defs.find(iter->lv_meta.lvm_name);
192✔
2424
        if (vd_iter == this->elf_value_defs.end()) {
192✔
2425
            log_debug("not rewriting undefined value -- %s",
×
2426
                      iter->lv_meta.lvm_name.get());
2427
            continue;
×
2428
        }
2429

2430
        const auto& vd = *vd_iter->second;
192✔
2431

2432
        if (vd.vd_rewriter.empty()) {
192✔
2433
            continue;
160✔
2434
        }
2435

2436
        auto _sg = ec.enter_source(
2437
            vd_iter->second->vd_rewrite_src_name, 1, vd.vd_rewriter);
32✔
2438
        std::string field_value;
32✔
2439

2440
        auto_mem<FILE> tmpout(fclose);
32✔
2441

2442
        tmpout = std::tmpfile();
32✔
2443
        if (!tmpout) {
32✔
2444
            log_error("unable to create temporary file");
×
2445
            return;
×
2446
        }
2447
        fcntl(fileno(tmpout), F_SETFD, FD_CLOEXEC);
32✔
2448
        auto fd_copy = auto_fd::dup_of(fileno(tmpout));
32✔
2449
        fd_copy.close_on_exec();
32✔
2450
        auto ec_out = std::make_pair(tmpout.release(), fclose);
32✔
2451
        {
2452
            exec_context::output_guard og(ec, "tmp", ec_out);
64✔
2453

2454
            auto exec_res = execute_any(ec, vd.vd_rewriter);
32✔
2455
            if (exec_res.isOk()) {
32✔
2456
                field_value = exec_res.unwrap();
32✔
2457
            } else {
2458
                field_value = exec_res.unwrapErr().to_attr_line().get_string();
×
2459
            }
2460
        }
32✔
2461
        struct stat st;
2462
        fstat(fd_copy.get(), &st);
32✔
2463
        if (st.st_size > 0) {
32✔
2464
            auto buf = auto_buffer::alloc(st.st_size);
2✔
2465

2466
            buf.resize(st.st_size);
2✔
2467
            pread(fd_copy.get(), buf.in(), st.st_size, 0);
2✔
2468
            field_value = buf.to_string();
2✔
2469
        }
2✔
2470
        value_out.erase(iter->lv_origin.lr_start, iter->lv_origin.length());
32✔
2471

2472
        int32_t shift_amount
2473
            = ((int32_t) field_value.length()) - iter->lv_origin.length();
32✔
2474
        auto orig_lr = iter->lv_origin;
32✔
2475
        value_out.insert(iter->lv_origin.lr_start, field_value);
32✔
2476
        for (auto shift_iter = values.lvv_values.begin();
32✔
2477
             shift_iter != values.lvv_values.end();
180✔
2478
             ++shift_iter)
148✔
2479
        {
2480
            shift_iter->lv_origin.shift_range(orig_lr, shift_amount);
148✔
2481
        }
2482
        shift_string_attrs(sa, orig_lr, shift_amount);
32✔
2483
    }
32✔
2484
}
2485

2486
static int
2487
read_json_field(yajlpp_parse_context* ypc,
116,413✔
2488
                const unsigned char* str,
2489
                size_t len,
2490
                yajl_string_props_t* props)
2491
{
2492
    auto* jlu = (json_log_userdata*) ypc->ypc_userdata;
116,413✔
2493
    timeval tv_out;
2494
    const auto frag = string_fragment::from_bytes(str, len);
116,413✔
2495
    intern_string_t field_name;
116,413✔
2496
    const auto* vd = jlu->get_field_def(ypc);
116,413✔
2497

2498
    if (vd != nullptr) {
116,413✔
2499
        field_name = vd->vd_meta.lvm_name;
4,346✔
2500
    }
2501

2502
    if (field_name.empty()) {
116,413✔
2503
        if (!jlu->jlu_format->elf_opid_field.empty()) {
112,067✔
2504
            auto path_sf = ypc->get_path_as_string_fragment();
21,761✔
2505
            if (path_sf.startswith(jlu->jlu_format->elf_opid_field.c_str())) {
21,761✔
2506
                jlu->jlu_opid_hasher.update(path_sf);
×
2507
                jlu->jlu_opid_hasher.update(frag);
×
2508
            }
2509
        }
2510
    } else if (jlu->jlu_format->lf_timestamp_field == field_name) {
4,346✔
2511
        const auto* last = jlu->jlu_format->lf_date_time.scan(
756✔
2512
            (const char*) str,
2513
            len,
2514
            jlu->jlu_format->get_timestamp_formats(),
756✔
2515
            &jlu->jlu_exttm,
2516
            tv_out);
2517
        if (last == nullptr) {
756✔
2518
            auto ls = jlu->jlu_format->lf_date_time.unlock();
22✔
2519
            if ((last = jlu->jlu_format->lf_date_time.scan(
22✔
2520
                     (const char*) str,
2521
                     len,
2522
                     jlu->jlu_format->get_timestamp_formats(),
22✔
2523
                     &jlu->jlu_exttm,
2524
                     tv_out))
2525
                == nullptr)
22✔
2526
            {
2527
                jlu->jlu_format->lf_date_time.relock(ls);
×
2528
            }
2529
            if (last != nullptr) {
22✔
2530
                auto old_flags
22✔
2531
                    = jlu->jlu_format->lf_timestamp_flags & DATE_TIME_SET_FLAGS;
22✔
2532
                auto new_flags = jlu->jlu_exttm.et_flags & DATE_TIME_SET_FLAGS;
22✔
2533

2534
                // It is unlikely a valid timestamp would lose much
2535
                // precision.
2536
                if (new_flags != old_flags) {
22✔
2537
                    last = nullptr;
×
2538
                }
2539
            }
2540
        }
2541
        if (last != nullptr) {
756✔
2542
            jlu->jlu_format->lf_timestamp_flags = jlu->jlu_exttm.et_flags;
756✔
2543
            jlu->jlu_base_line->set_time(tv_out);
756✔
2544
        }
2545
    } else if (jlu->jlu_format->elf_level_pointer.pp_value != nullptr) {
3,590✔
2546
        if (jlu->jlu_format->elf_level_pointer.pp_value
194✔
2547
                ->find_in(field_name.to_string_fragment(), PCRE2_NO_UTF_CHECK)
194✔
2548
                .ignore_error()
194✔
2549
                .has_value())
97✔
2550
        {
2551
            jlu->jlu_base_line->set_level(
×
2552
                jlu->jlu_format->convert_level(frag, jlu->jlu_batch_context));
×
2553
        }
2554
    }
2555
    if (!field_name.empty() && jlu->jlu_format->elf_level_field == field_name) {
116,413✔
2556
        jlu->jlu_base_line->set_level(
893✔
2557
            jlu->jlu_format->convert_level(frag, jlu->jlu_batch_context));
893✔
2558
    }
2559
    if (!field_name.empty() && jlu->jlu_format->elf_opid_field == field_name) {
116,413✔
2560
        jlu->jlu_base_line->set_opid(frag.hash());
164✔
2561

2562
        auto& sbc = *jlu->jlu_batch_context;
164✔
2563
        auto opid_iter = sbc.sbc_opids.los_opid_ranges.find(frag);
164✔
2564
        if (opid_iter == sbc.sbc_opids.los_opid_ranges.end()) {
164✔
2565
            jlu->jlu_opid_frag = frag.to_owned(sbc.sbc_allocator);
141✔
2566
        } else {
2567
            jlu->jlu_opid_frag = opid_iter->first;
23✔
2568
        }
2569
    }
2570
    if (!field_name.empty()
116,413✔
2571
        && jlu->jlu_format->elf_thread_id_field == field_name)
116,413✔
2572
    {
2573
        auto& sbc = *jlu->jlu_batch_context;
×
2574
        auto tid_iter = sbc.sbc_tids.ltis_tid_ranges.find(frag);
×
2575
        if (tid_iter == sbc.sbc_tids.ltis_tid_ranges.end()) {
×
2576
            jlu->jlu_tid_frag = frag.to_owned(sbc.sbc_allocator);
×
2577
        } else {
2578
            jlu->jlu_tid_frag = tid_iter->first;
×
2579
        }
2580
    }
2581
    if (!jlu->jlu_format->elf_subid_field.empty()
116,413✔
2582
        && jlu->jlu_format->elf_subid_field == field_name)
116,413✔
2583
    {
2584
        jlu->jlu_subid = frag.to_string();
×
2585
    }
2586
    if (!field_name.empty()
116,413✔
2587
        && jlu->jlu_format->elf_duration_field == field_name)
116,413✔
2588
    {
2589
        auto from_res = humanize::try_from<double>(frag);
×
2590
        if (from_res) {
×
2591
            jlu->jlu_duration = std::chrono::microseconds(
×
2592
                static_cast<int64_t>(from_res.value() * 1000000));
2593
        }
2594
    }
2595

2596
    if (vd != nullptr && vd->vd_is_desc_field) {
116,413✔
2597
        auto frag_copy = frag.to_owned(jlu->jlu_format->lf_desc_allocator);
42✔
2598

2599
        jlu->jlu_format->lf_desc_captures.emplace(field_name, frag_copy);
42✔
2600
    }
2601

2602
    jlu->add_sub_lines_for(vd, ypc->is_level(1), std::nullopt, str, len, props);
116,413✔
2603

2604
    return 1;
116,413✔
2605
}
2606

2607
static int
2608
rewrite_json_field(yajlpp_parse_context* ypc,
69,085✔
2609
                   const unsigned char* str,
2610
                   size_t len,
2611
                   yajl_string_props_t* props)
2612
{
2613
    static const intern_string_t body_name = intern_string::lookup("body", -1);
69,085✔
2614
    auto* jlu = (json_log_userdata*) ypc->ypc_userdata;
69,085✔
2615
    intern_string_t field_name;
69,085✔
2616
    const auto* vd = jlu->get_field_def(ypc);
69,085✔
2617
    auto frag = string_fragment::from_bytes(str, len);
69,085✔
2618

2619
    if (!ypc->is_level(1) && vd == nullptr) {
69,085✔
2620
        if (!jlu->jlu_format->elf_opid_field.empty()) {
57,473✔
2621
            auto path_sf = ypc->get_path_as_string_fragment();
56,667✔
2622
            if (path_sf.startswith(jlu->jlu_format->elf_opid_field.c_str())) {
56,667✔
2623
                jlu->jlu_opid_hasher.update(path_sf);
×
2624
                jlu->jlu_opid_hasher.update(frag);
×
2625
            }
2626
        }
2627
        return 1;
57,473✔
2628
    }
2629
    if (vd != nullptr) {
11,612✔
2630
        field_name = vd->vd_meta.lvm_name;
11,138✔
2631
    } else {
2632
        field_name = ypc->get_path();
474✔
2633
    }
2634

2635
    if (jlu->jlu_format->elf_opid_field == field_name) {
11,612✔
2636
        jlu->jlu_format->jlf_line_values.lvv_opid_value = frag.to_string();
967✔
2637
        jlu->jlu_format->jlf_line_values.lvv_opid_provenance
967✔
2638
            = logline_value_vector::opid_provenance::file;
967✔
2639
    }
2640
    if (jlu->jlu_format->lf_timestamp_field == field_name) {
11,612✔
2641
        char time_buf[64];
2642

2643
        // TODO add a timeval kind to logline_value
2644
        if (jlu->jlu_line->is_time_skewed()
2,156✔
2645
            || (jlu->jlu_format->lf_timestamp_flags
4,312✔
2646
                & (ETF_MICROS_SET | ETF_NANOS_SET | ETF_ZONE_SET)))
2,156✔
2647
        {
2648
            timeval tv;
2649

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

2705
        jlu->jlu_format->jlf_line_values.lvv_values.emplace_back(
9,050✔
2706
            jlu->jlu_format->get_value_meta(field_name,
18,100✔
2707
                                            value_kind_t::VALUE_TEXT),
2708
            string_fragment::from_byte_range(jlu->jlu_shared_buffer.get_data(),
18,100✔
2709
                                             str_offset,
2710
                                             str_offset + len));
9,050✔
2711
    } else {
2712
        if (field_name == jlu->jlu_format->elf_body_field) {
406✔
2713
            jlu->jlu_format->jlf_line_values.lvv_values.emplace_back(
315✔
2714
                logline_value_meta(body_name,
630✔
2715
                                   value_kind_t::VALUE_TEXT,
2716
                                   logline_value_meta::internal_column{},
×
2717
                                   jlu->jlu_format),
315✔
2718
                std::string{(const char*) str, len});
1,260✔
2719
        }
2720

2721
        jlu->jlu_format->jlf_line_values.lvv_values.emplace_back(
406✔
2722
            jlu->jlu_format->get_value_meta(ypc, vd, value_kind_t::VALUE_TEXT),
812✔
2723
            std::string{(const char*) str, len});
1,624✔
2724
    }
2725
    if (vd != nullptr && vd->vd_is_desc_field
11,138✔
2726
        && jlu->jlu_format->elf_opid_field.empty())
22,750✔
2727
    {
2728
        auto frag_copy = frag.to_owned(jlu->jlu_format->lf_desc_allocator);
62✔
2729

2730
        jlu->jlu_format->lf_desc_captures.emplace(field_name, frag_copy);
62✔
2731
    }
2732

2733
    return 1;
11,612✔
2734
}
2735

2736
void
2737
external_log_format::get_subline(const logline& ll,
23,384✔
2738
                                 shared_buffer_ref& sbr,
2739
                                 subline_options opts)
2740
{
2741
    if (this->elf_type == elf_type_t::ELF_TYPE_TEXT) {
23,384✔
2742
        return;
18,915✔
2743
    }
2744

2745
    if (this->jlf_cached_offset != ll.get_offset()
4,469✔
2746
        || this->jlf_cached_opts != opts)
4,469✔
2747
    {
2748
        auto& ypc = *(this->jlf_parse_context);
2,383✔
2749
        yajl_handle handle = this->jlf_yajl_handle.get();
2,383✔
2750
        json_log_userdata jlu(sbr, nullptr);
2,383✔
2751

2752
        jlu.jlu_subline_opts = opts;
2,383✔
2753

2754
        this->lf_desc_captures.clear();
2,383✔
2755
        this->lf_desc_allocator.reset();
2,383✔
2756
        this->jlf_share_manager.invalidate_refs();
2,383✔
2757
        this->jlf_cached_line.clear();
2,383✔
2758
        this->jlf_line_values.clear();
2,383✔
2759
        this->jlf_line_offsets.clear();
2,383✔
2760
        this->jlf_line_attrs.clear();
2,383✔
2761

2762
        auto line_frag = sbr.to_string_fragment();
2,383✔
2763

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

2782
        yajl_reset(handle);
2,311✔
2783
        ypc.set_static_handler(json_log_rewrite_handlers.jpc_children[0]);
2,311✔
2784
        ypc.ypc_userdata = &jlu;
2,311✔
2785
        ypc.ypc_ignore_unused = true;
2,311✔
2786
        ypc.ypc_alt_callbacks.yajl_start_array = json_array_start;
2,311✔
2787
        ypc.ypc_alt_callbacks.yajl_end_array = json_array_end;
2,311✔
2788
        ypc.ypc_alt_callbacks.yajl_start_map = json_array_start;
2,311✔
2789
        ypc.ypc_alt_callbacks.yajl_end_map = json_array_end;
2,311✔
2790
        jlu.jlu_format = this;
2,311✔
2791
        jlu.jlu_line = &ll;
2,311✔
2792
        jlu.jlu_handle = handle;
2,311✔
2793
        jlu.jlu_line_value = sbr.get_data();
2,311✔
2794
        jlu.jlu_format_hits.resize(this->jlf_line_format.size());
2,311✔
2795

2796
        yajl_status parse_status = yajl_parse(
4,622✔
2797
            handle, (const unsigned char*) sbr.get_data(), sbr.length());
2,311✔
2798
        if (parse_status != yajl_status_ok
2,311✔
2799
            || yajl_complete_parse(handle) != yajl_status_ok)
2,311✔
2800
        {
2801
            unsigned char* msg;
2802
            std::string full_msg;
14✔
2803

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

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

2827
            memset(used_values, 0, sizeof(used_values));
2,297✔
2828
            for (lv_iter = this->jlf_line_values.lvv_values.begin();
2,297✔
2829
                 lv_iter != this->jlf_line_values.lvv_values.end();
20,402✔
2830
                 ++lv_iter)
18,105✔
2831
            {
2832
                lv_iter->lv_meta.lvm_format = this;
18,105✔
2833
            }
2834
            if (jlu.jlu_tid_frag) {
2,297✔
2835
                this->jlf_line_values.lvv_thread_id_value
2836
                    = jlu.jlu_tid_frag->to_string();
×
2837
            }
2838

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

2881
            int sub_offset = this->jlf_line_format_init_count;
2,297✔
2882
            for (const auto& jfe : this->jlf_line_format) {
29,898✔
2883
                static const intern_string_t ts_field
2884
                    = intern_string::lookup("__timestamp__", -1);
27,601✔
2885
                static const intern_string_t level_field
2886
                    = intern_string::lookup("__level__");
27,687✔
2887
                size_t begin_size = this->jlf_cached_line.size();
27,601✔
2888

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

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

2915
                            if (!jfe.jfe_prefix.empty()) {
8,480✔
2916
                                this->json_append_to_cache(jfe.jfe_prefix);
4,865✔
2917
                            }
2918
                            lr.lr_start = this->jlf_cached_line.size();
8,480✔
2919

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

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

2977
                            if (lv_iter->lv_meta.lvm_name
8,480✔
2978
                                == this->lf_timestamp_field)
8,480✔
2979
                            {
2980
                                this->jlf_line_attrs.emplace_back(
1,003✔
2981
                                    lr, L_TIMESTAMP.value());
2,006✔
2982
                            } else if (lv_iter->lv_meta.lvm_name
7,477✔
2983
                                       == this->elf_body_field)
7,477✔
2984
                            {
2985
                                this->jlf_line_attrs.emplace_back(
1,095✔
2986
                                    lr, SA_BODY.value());
2,190✔
2987
                            } else if (lv_iter->lv_meta.lvm_name
6,382✔
2988
                                       == this->elf_src_file_field)
6,382✔
2989
                            {
2990
                                this->jlf_line_attrs.emplace_back(
8✔
2991
                                    lr, SA_SRC_FILE.value());
16✔
2992
                            } else if (lv_iter->lv_meta.lvm_name
6,374✔
2993
                                       == this->elf_src_line_field)
6,374✔
2994
                            {
2995
                                this->jlf_line_attrs.emplace_back(
8✔
2996
                                    lr, SA_SRC_LINE.value());
16✔
2997
                            } else if (lv_iter->lv_meta.lvm_name
6,366✔
2998
                                       == this->elf_thread_id_field)
6,366✔
2999
                            {
3000
                                this->jlf_line_attrs.emplace_back(
60✔
3001
                                    lr, SA_THREAD_ID.value());
120✔
3002
                            } else if (lv_iter->lv_meta.lvm_name
6,306✔
3003
                                       == this->elf_duration_field)
6,306✔
3004
                            {
3005
                                this->jlf_line_attrs.emplace_back(
×
3006
                                    lr, SA_DURATION.value());
×
3007
                            } else if (lv_iter->lv_meta.lvm_name
6,306✔
3008
                                       == this->elf_level_field)
6,306✔
3009
                            {
3010
                                this->jlf_line_attrs.emplace_back(
1,037✔
3011
                                    lr, L_LEVEL.value());
2,074✔
3012
                            } else if (lv_iter->lv_meta.lvm_name
10,538✔
3013
                                           == this->elf_opid_field
5,269✔
3014
                                       && !lr.empty())
5,269✔
3015
                            {
3016
                                this->jlf_line_attrs.emplace_back(
967✔
3017
                                    lr, L_OPID.value());
1,934✔
3018
                            }
3019
                            lv_iter->lv_origin = lr;
8,480✔
3020
                            lv_iter->lv_sub_offset = sub_offset;
8,480✔
3021
                            used_values[std::distance(
8,480✔
3022
                                this->jlf_line_values.lvv_values.begin(),
3023
                                lv_iter)]
3024
                                = true;
8,480✔
3025

3026
                            if (!jfe.jfe_suffix.empty()) {
8,480✔
3027
                                this->json_append_to_cache(jfe.jfe_suffix);
1,202✔
3028
                            }
3029
                        } else if (jfe.jfe_value.pp_value == ts_field) {
24,010✔
3030
                            struct line_range lr;
1,439✔
3031
                            ssize_t ts_len;
3032
                            char ts[64];
3033
                            struct exttm et;
1,439✔
3034

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

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

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

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

3156
                if (lv.lv_meta.is_hidden() || used_values[lpc]
27,741✔
3157
                    || body_name == lv.lv_meta.lvm_name)
27,741✔
3158
                {
3159
                    continue;
17,025✔
3160
                }
3161

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

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

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

3205
        this->jlf_line_offsets.push_back(0);
2,311✔
3206
        for (size_t lpc = 0; lpc < this->jlf_cached_line.size(); lpc++) {
260,961✔
3207
            if (this->jlf_cached_line[lpc] == '\n') {
258,650✔
3208
                this->jlf_line_offsets.push_back(lpc + 1);
4,177✔
3209
            }
3210
        }
3211
        this->jlf_line_offsets.push_back(this->jlf_cached_line.size());
2,311✔
3212
        this->jlf_cached_offset = ll.get_offset();
2,311✔
3213
        this->jlf_cached_opts = opts;
2,311✔
3214
    }
2,383✔
3215

3216
    off_t this_off = 0, next_off = 0;
4,397✔
3217

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

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

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

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

3257
struct format_header_expressions : public lnav_config_listener {
3258
    format_header_expressions() : lnav_config_listener(__FILE__) {}
1,139✔
3259

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

3265
using safe_format_header_expressions = safe::Safe<format_header_expressions>;
3266

3267
static safe_format_header_expressions format_header_exprs;
3268

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

3275
    {
3276
        auto_fd fd;
589✔
3277

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

3282
        ssize_t rc;
3283

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

3290
    auto hexbuf = auto_buffer::alloc(buffer_size * 2);
589✔
3291

3292
    for (size_t lpc = 0; lpc < buffer_size; lpc++) {
309,564✔
3293
        fmt::format_to(
308,975✔
3294
            std::back_inserter(hexbuf), FMT_STRING("{:02x}"), buffer[lpc]);
1,235,900✔
3295
    }
3296

3297
    safe::WriteAccess<safe_format_header_expressions> in(format_header_exprs);
589✔
3298

3299
    for (const auto& format : log_format::get_root_formats()) {
42,914✔
3300
        auto elf = std::dynamic_pointer_cast<external_log_format>(format);
42,325✔
3301
        if (elf == nullptr) {
42,325✔
3302
            continue;
2,945✔
3303
        }
3304

3305
        if (elf->elf_converter.c_header.h_exprs.he_exprs.empty()) {
39,380✔
3306
            continue;
38,791✔
3307
        }
3308

3309
        if (buffer_size < elf->elf_converter.c_header.h_size) {
589✔
3310
            log_debug(
21✔
3311
                "%s: file content too small (%d) for header detection: %s",
3312
                filename.c_str(),
3313
                buffer_size,
3314
                elf->get_name().get());
3315
            continue;
21✔
3316
        }
3317
        for (const auto& hpair : elf->elf_converter.c_header.h_exprs.he_exprs) {
2,840✔
3318
            auto& he = in->e_header_exprs[elf->get_name()][hpair.first];
2,272✔
3319

3320
            if (!he.che_enabled) {
2,272✔
3321
                continue;
×
3322
            }
3323

3324
            auto* stmt = he.che_stmt.in();
2,272✔
3325

3326
            if (stmt == nullptr) {
2,272✔
3327
                continue;
×
3328
            }
3329
            sqlite3_reset(stmt);
2,272✔
3330
            auto count = sqlite3_bind_parameter_count(stmt);
2,272✔
3331
            for (int lpc = 0; lpc < count; lpc++) {
4,544✔
3332
                const auto* name = sqlite3_bind_parameter_name(stmt, lpc + 1);
2,272✔
3333

3334
                if (name[0] == '$') {
2,272✔
3335
                    const char* env_value;
3336

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

3358
            auto step_res = sqlite3_step(stmt);
2,272✔
3359

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

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

3390
    return std::nullopt;
589✔
3391
}
589✔
3392

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

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

3407
    if (this->elf_type == elf_type_t::ELF_TYPE_JSON) {
175,264✔
3408
        auto alloc = ArenaAlloc::Alloc<char>{};
2✔
3409
        auto sbc = scan_batch_context{
2✔
3410
            alloc,
3411
        };
2✔
3412
        std::vector<logline> dst;
2✔
3413
        auto li = line_info{
2✔
3414
            {0, lines[0].length()},
2✔
3415
        };
2✔
3416
        shared_buffer sb;
2✔
3417
        shared_buffer_ref sbr;
2✔
3418
        sbr.share(sb, lines[0].data(), (size_t) lines[0].length());
2✔
3419

3420
        return this->scan_json(dst, li, sbr, sbc);
2✔
3421
    }
2✔
3422

3423
    scan_result_t retval = scan_no_match{"no patterns matched"};
175,262✔
3424
    auto found = false;
175,262✔
3425

3426
    for (auto pat_iter = this->elf_pattern_order.begin();
175,262✔
3427
         pat_iter != this->elf_pattern_order.end();
1,331,296✔
3428
         ++pat_iter)
1,156,034✔
3429
    {
3430
        auto& pat = *(*pat_iter);
1,156,034✔
3431

3432
        if (!pat.p_pcre.pp_value) {
1,156,034✔
3433
            continue;
982,216✔
3434
        }
3435

3436
        auto md = pat.p_pcre.pp_value->create_match_data();
1,156,034✔
3437
        auto match_res = pat.p_pcre.pp_value->capture_from(lines[0])
1,156,034✔
3438
                             .into(md)
1,156,034✔
3439
                             .matches(PCRE2_NO_UTF_CHECK)
2,312,068✔
3440
                             .ignore_error();
1,156,034✔
3441
        if (!match_res) {
1,156,034✔
3442
            continue;
980,774✔
3443
        }
3444
        retval = scan_match{1000};
175,260✔
3445
        found = true;
175,260✔
3446

3447
        if (pat.p_module_format) {
175,260✔
3448
            continue;
1,442✔
3449
        }
3450

3451
        sample.s_matched_regexes.insert(pat.p_name.to_string());
173,818✔
3452

3453
        const auto ts_cap = md[pat.p_timestamp_field_index];
173,818✔
3454
        const auto level_cap = md[pat.p_level_field_index];
173,818✔
3455
        const char* const* custom_formats = this->get_timestamp_formats();
173,818✔
3456
        date_time_scanner dts;
173,818✔
3457
        timeval tv;
3458
        exttm tm;
173,818✔
3459

3460
        if (ts_cap && ts_cap->sf_begin == 0) {
173,818✔
3461
            pat.p_timestamp_end = ts_cap->sf_end;
105,655✔
3462
        }
3463
        const char* dts_scan_res = nullptr;
173,818✔
3464

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

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

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

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

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

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

3557
        auto level = this->convert_level(
173,818✔
3558
            level_cap.value_or(string_fragment::invalid()), nullptr);
173,818✔
3559

3560
        if (sample.s_level != LEVEL_UNKNOWN && sample.s_level != level) {
173,818✔
3561
            attr_line_t note_al;
1✔
3562

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

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

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

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

3693
    if (!found && !this->elf_pattern_order.empty()) {
175,262✔
3694
        std::vector<std::pair<ssize_t, intern_string_t>> partial_indexes;
2✔
3695
        attr_line_t notes;
2✔
3696
        size_t max_name_width = 0;
2✔
3697

3698
        for (const auto& pat_iter : this->elf_pattern_order) {
11✔
3699
            auto& pat = *pat_iter;
9✔
3700

3701
            if (!pat.p_pcre.pp_value) {
9✔
3702
                continue;
×
3703
            }
3704

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

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

3744
            attr_line_t regex_al = pat_iter->p_pcre.pp_value->get_pattern();
9✔
3745
            lnav::snippets::regex_highlighter(
9✔
3746
                regex_al, -1, line_range{0, (int) regex_al.length()});
9✔
3747

3748
            regex_note
3749
                .append(lnav::roles::symbol(fmt::format(
18✔
3750
                    FMT_STRING("{:{}}"), pat_iter->p_name, max_name_width)))
27✔
3751
                .append(" = ")
9✔
3752
                .append_quoted(regex_al)
18✔
3753
                .append("\n");
9✔
3754
        }
9✔
3755

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

3766
    return retval;
175,262✔
3767
}
175,264✔
3768

3769
void
3770
external_log_format::build(std::vector<lnav::console::user_message>& errors)
48,652✔
3771
{
3772
    auto& vc = view_colors::singleton();
48,652✔
3773

3774
    if (!this->lf_timestamp_field.empty()) {
48,652✔
3775
        auto& vd = this->elf_value_defs[this->lf_timestamp_field];
48,652✔
3776
        if (vd.get() == nullptr) {
48,652✔
3777
            vd = std::make_shared<value_def>(
35,578✔
3778
                this->lf_timestamp_field,
35,578✔
3779
                value_kind_t::VALUE_TEXT,
×
3780
                logline_value_meta::internal_column{},
×
3781
                this);
35,578✔
3782
            if (this->elf_type == elf_type_t::ELF_TYPE_JSON) {
35,578✔
3783
                this->elf_value_def_order.emplace_back(vd);
4,808✔
3784
            }
3785
        }
3786
        vd->vd_meta.lvm_name = this->lf_timestamp_field;
48,652✔
3787
        vd->vd_meta.lvm_kind = value_kind_t::VALUE_TEXT;
48,652✔
3788
        vd->vd_meta.lvm_column = logline_value_meta::internal_column{};
48,652✔
3789
        vd->vd_internal = true;
48,652✔
3790

3791
        this->elf_value_defs[LOG_TIME_STR] = vd;
48,652✔
3792
    }
3793

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

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

3847
            if (this->elf_level_field != this->elf_body_field) {
24,666✔
3848
                this->elf_value_defs[LOG_LEVEL_STR] = vd;
23,849✔
3849
            }
3850
        } else {
3851
            if (level_iter->second->vd_meta.lvm_kind
23,986✔
3852
                != value_kind_t::VALUE_TEXT)
23,986✔
3853
            {
3854
                this->lf_level_hideable = false;
5,143✔
3855
            }
3856
            this->elf_value_defs[LOG_LEVEL_STR] = level_iter->second;
23,986✔
3857
        }
3858
    }
3859

3860
    auto opid_field_iter = this->elf_value_defs.find(LOG_OPID_STR);
48,652✔
3861
    if (opid_field_iter == this->elf_value_defs.end()) {
48,652✔
3862
        auto vd
3863
            = std::make_shared<value_def>(this->elf_opid_field,
48,652✔
3864
                                          value_kind_t::VALUE_TEXT,
×
3865
                                          logline_value_meta::internal_column{},
×
3866
                                          this);
48,652✔
3867
        if (this->elf_type == elf_type_t::ELF_TYPE_JSON) {
48,652✔
3868
            this->elf_value_def_order.emplace_back(vd);
7,788✔
3869
        }
3870
        vd->vd_meta.lvm_name = LOG_OPID_STR;
48,652✔
3871
        vd->vd_meta.lvm_kind = value_kind_t::VALUE_TEXT;
48,652✔
3872
        vd->vd_meta.lvm_column = logline_value_meta::internal_column{};
48,652✔
3873
        vd->vd_internal = true;
48,652✔
3874

3875
        this->elf_value_defs[LOG_OPID_STR] = vd;
48,652✔
3876
    }
48,652✔
3877

3878
    if (!this->elf_body_field.empty()) {
48,652✔
3879
        auto& vd = this->elf_value_defs[this->elf_body_field];
48,652✔
3880
        if (vd.get() == nullptr) {
48,652✔
3881
            vd = std::make_shared<value_def>(
39,807✔
3882
                this->elf_body_field,
39,807✔
3883
                value_kind_t::VALUE_TEXT,
×
3884
                logline_value_meta::internal_column{},
×
3885
                this);
39,807✔
3886
            if (this->elf_type == elf_type_t::ELF_TYPE_JSON) {
39,807✔
3887
                this->elf_value_def_order.emplace_back(vd);
4,807✔
3888
            }
3889
        }
3890
        vd->vd_meta.lvm_name = this->elf_body_field;
48,652✔
3891
        vd->vd_meta.lvm_kind = value_kind_t::VALUE_TEXT;
48,652✔
3892
        vd->vd_meta.lvm_column = logline_value_meta::internal_column{};
48,652✔
3893
        vd->vd_internal = true;
48,652✔
3894
    }
3895

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

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

3924
    if (!this->elf_thread_id_field.empty()) {
48,652✔
3925
        auto& vd = this->elf_value_defs[this->elf_thread_id_field];
15,142✔
3926
        if (vd.get() == nullptr) {
15,142✔
3927
            vd = std::make_shared<value_def>(
1✔
3928
                this->elf_thread_id_field,
1✔
3929
                value_kind_t::VALUE_TEXT,
×
3930
                logline_value_meta::internal_column{},
×
3931
                this);
2✔
3932
        }
3933
        vd->vd_meta.lvm_name = this->elf_thread_id_field;
15,142✔
3934
        vd->vd_meta.lvm_kind = value_kind_t::VALUE_TEXT;
15,142✔
3935
        vd->vd_meta.lvm_column = logline_value_meta::internal_column{};
15,142✔
3936
    }
3937

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

3952
    if (!this->lf_timestamp_format.empty()) {
48,652✔
3953
        this->lf_timestamp_format.push_back(nullptr);
5,147✔
3954
    }
3955
    auto src_file_found = 0;
48,652✔
3956
    auto src_line_found = 0;
48,652✔
3957
    auto thread_id_found = 0;
48,652✔
3958
    auto duration_found = 0;
48,652✔
3959
    for (auto& elf_pattern : this->elf_patterns) {
143,691✔
3960
        auto& pat = *elf_pattern.second;
95,039✔
3961

3962
        if (pat.p_pcre.pp_value == nullptr) {
95,039✔
3963
            continue;
1✔
3964
        }
3965

3966
        if (pat.p_module_format) {
95,038✔
3967
            this->elf_has_module_format = true;
2,163✔
3968
        }
3969

3970
        if (pat.p_opid_field_index == -1
190,076✔
3971
            && this->lf_opid_description_def->size() == 1)
95,038✔
3972
        {
3973
            const auto& opid_def
3974
                = this->lf_opid_description_def->begin()->second;
28,119✔
3975
            for (const auto& desc : *opid_def.od_descriptors) {
62,727✔
3976
                for (auto named_cap : pat.p_pcre.pp_value->get_named_captures())
377,083✔
3977
                {
3978
                    const intern_string_t name
3979
                        = intern_string::lookup(named_cap.get_name());
342,475✔
3980

3981
                    if (name == desc.od_field.pp_value) {
342,475✔
3982
                        pat.p_opid_description_field_indexes.emplace_back(
62,006✔
3983
                            named_cap.get_index());
31,003✔
3984
                    }
3985
                }
3986
            }
3987
        }
3988

3989
        for (auto named_cap : pat.p_pcre.pp_value->get_named_captures()) {
817,981✔
3990
            const intern_string_t name
3991
                = intern_string::lookup(named_cap.get_name());
722,943✔
3992

3993
            if (name == this->lf_timestamp_field) {
722,943✔
3994
                pat.p_timestamp_field_index = named_cap.get_index();
92,873✔
3995
            }
3996
            if (name == this->lf_time_field) {
722,943✔
3997
                pat.p_time_field_index = named_cap.get_index();
721✔
3998
            }
3999
            if (name == this->elf_level_field) {
722,943✔
4000
                pat.p_level_field_index = named_cap.get_index();
72,486✔
4001
            }
4002
            if (name == this->elf_module_id_field) {
722,943✔
4003
                pat.p_module_field_index = named_cap.get_index();
5,769✔
4004
            }
4005
            if (name == this->elf_opid_field) {
722,943✔
4006
                pat.p_opid_field_index = named_cap.get_index();
19,467✔
4007
            }
4008
            if (name == this->elf_subid_field) {
722,943✔
4009
                pat.p_subid_field_index = named_cap.get_index();
10,815✔
4010
            }
4011
            if (name == this->elf_body_field) {
722,943✔
4012
                pat.p_body_field_index = named_cap.get_index();
81,337✔
4013
            }
4014
            if (name == this->elf_src_file_field) {
722,943✔
4015
                pat.p_src_file_field_index = named_cap.get_index();
11,536✔
4016
                src_file_found += 1;
11,536✔
4017
            }
4018
            if (name == this->elf_src_line_field) {
722,943✔
4019
                pat.p_src_line_field_index = named_cap.get_index();
12,978✔
4020
                src_line_found += 1;
12,978✔
4021
            }
4022
            if (name == this->elf_thread_id_field) {
722,943✔
4023
                pat.p_thread_id_field_index = named_cap.get_index();
41,097✔
4024
                thread_id_found += 1;
41,097✔
4025
            }
4026
            if (name == this->elf_duration_field) {
722,943✔
4027
                pat.p_duration_field_index = named_cap.get_index();
4,326✔
4028
                duration_found += 1;
4,326✔
4029
            }
4030

4031
            auto value_iter = this->elf_value_defs.find(name);
722,943✔
4032
            if (value_iter != this->elf_value_defs.end()) {
722,943✔
4033
                auto vd = value_iter->second;
712,030✔
4034
                indexed_value_def ivd;
712,030✔
4035

4036
                ivd.ivd_index = named_cap.get_index();
712,030✔
4037
                if (!vd->vd_unit_field.empty()) {
712,030✔
4038
                    ivd.ivd_unit_field_index = pat.p_pcre.pp_value->name_index(
1,442✔
4039
                        vd->vd_unit_field.get());
721✔
4040
                } else {
4041
                    ivd.ivd_unit_field_index = -1;
711,309✔
4042
                }
4043
                if (!vd->vd_internal
712,030✔
4044
                    && !vd->vd_meta.lvm_column
1,225,768✔
4045
                            .is<logline_value_meta::table_column>())
513,738✔
4046
                {
4047
                    vd->vd_meta.lvm_column = logline_value_meta::table_column{
282,297✔
4048
                        this->elf_column_count++};
282,297✔
4049
                }
4050
                ivd.ivd_value_def = vd;
712,030✔
4051
                pat.p_value_by_index.push_back(ivd);
712,030✔
4052
            }
712,030✔
4053
            pat.p_value_name_to_index[name] = named_cap.get_index();
722,943✔
4054
        }
4055

4056
        stable_sort(pat.p_value_by_index.begin(), pat.p_value_by_index.end());
95,038✔
4057

4058
        for (int lpc = 0; lpc < (int) pat.p_value_by_index.size(); lpc++) {
807,068✔
4059
            auto& ivd = pat.p_value_by_index[lpc];
712,030✔
4060
            auto vd = ivd.ivd_value_def;
712,030✔
4061

4062
            if (!vd->vd_meta.lvm_foreign_key && !vd->vd_meta.lvm_identifier) {
712,030✔
4063
                switch (vd->vd_meta.lvm_kind) {
360,710✔
4064
                    case value_kind_t::VALUE_INTEGER:
49,845✔
4065
                    case value_kind_t::VALUE_FLOAT:
4066
                        pat.p_numeric_value_indexes.push_back(lpc);
49,845✔
4067
                        break;
49,845✔
4068
                    default:
310,865✔
4069
                        break;
310,865✔
4070
                }
4071
            }
4072
        }
712,030✔
4073

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

4084
        if (!this->elf_level_field.empty() && pat.p_level_field_index == -1) {
95,038✔
4085
            log_warning("%s:level field '%s' not found in pattern",
22,552✔
4086
                        pat.p_config_path.c_str(),
4087
                        this->elf_level_field.get());
4088
        }
4089
        if (!this->elf_module_id_field.empty()
95,038✔
4090
            && pat.p_module_field_index == -1)
95,038✔
4091
        {
4092
            log_warning("%s:module field '%s' not found in pattern",
×
4093
                        pat.p_config_path.c_str(),
4094
                        this->elf_module_id_field.get());
4095
        }
4096
        if (!this->elf_body_field.empty() && pat.p_body_field_index == -1) {
95,038✔
4097
            log_warning("%s:body field '%s' not found in pattern",
13,701✔
4098
                        pat.p_config_path.c_str(),
4099
                        this->elf_body_field.get());
4100
        }
4101

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

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

4200
    stable_sort(this->elf_level_pairs.begin(), this->elf_level_pairs.end());
48,652✔
4201

4202
    {
4203
        safe::WriteAccess<safe_format_header_expressions> hexprs(
4204
            format_header_exprs);
48,652✔
4205

4206
        if (hexprs->e_db.in() == nullptr) {
48,652✔
4207
            if (sqlite3_open(":memory:", hexprs->e_db.out()) != SQLITE_OK) {
721✔
4208
                log_error("unable to open memory DB");
×
4209
                return;
×
4210
            }
4211
            register_sqlite_funcs(hexprs->e_db.in(), sqlite_registration_funcs);
721✔
4212
        }
4213

4214
        for (const auto& hpair : this->elf_converter.c_header.h_exprs.he_exprs)
51,537✔
4215
        {
4216
            auto stmt_str
4217
                = fmt::format(FMT_STRING("SELECT 1 WHERE {}"), hpair.second);
8,655✔
4218
            compiled_header_expr che;
2,885✔
4219

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

4242
                auto um = lnav::console::user_message::error(
2✔
4243
                              "SQL expression is invalid")
4244
                              .with_reason(sqlite3_errmsg(hexprs->e_db.in()))
2✔
4245
                              .with_snippet(snippet)
1✔
4246
                              .move();
1✔
4247

4248
                errors.emplace_back(um);
1✔
4249
                continue;
1✔
4250
            }
1✔
4251

4252
            hexprs->e_header_exprs[this->elf_name][hpair.first]
2,884✔
4253
                = std::move(che);
5,768✔
4254
        }
2,886✔
4255

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

4270
    for (auto& vd : this->elf_value_def_order) {
476,692✔
4271
        std::vector<std::string>::iterator act_iter;
428,040✔
4272

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

4293
        vd->vd_meta.lvm_format = this;
428,040✔
4294
        if (!vd->vd_internal
428,040✔
4295
            && !vd->vd_meta.lvm_column.is<logline_value_meta::table_column>())
428,040✔
4296
        {
4297
            vd->vd_meta.lvm_column
104,593✔
4298
                = logline_value_meta::table_column{this->elf_column_count++};
104,593✔
4299
        }
4300

4301
        if (vd->vd_meta.lvm_kind == value_kind_t::VALUE_UNKNOWN) {
428,040✔
4302
            vd->vd_meta.lvm_kind = value_kind_t::VALUE_TEXT;
×
4303
        }
4304

4305
        if (this->elf_type == elf_type_t::ELF_TYPE_TEXT) {
428,040✔
4306
            std::set<std::string> available_captures;
297,440✔
4307

4308
            bool found_in_pattern = false;
297,440✔
4309
            for (const auto& pat : this->elf_patterns) {
504,371✔
4310
                if (pat.second->p_pcre.pp_value == nullptr) {
504,369✔
4311
                    continue;
×
4312
                }
4313

4314
                auto cap_index = pat.second->p_pcre.pp_value->name_index(
1,008,738✔
4315
                    vd->vd_meta.lvm_name.get());
504,369✔
4316
                if (cap_index >= 0) {
504,369✔
4317
                    found_in_pattern = true;
297,438✔
4318
                    break;
297,438✔
4319
                }
4320

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

4352
        for (act_iter = vd->vd_action_list.begin();
428,040✔
4353
             act_iter != vd->vd_action_list.end();
428,761✔
4354
             ++act_iter)
721✔
4355
        {
4356
            if (this->lf_action_defs.find(*act_iter)
721✔
4357
                == this->lf_action_defs.end())
1,442✔
4358
            {
4359
#if 0
4360
                errors.push_back("error:" + this->elf_name.to_string() + ":"
4361
                                 + vd->vd_meta.lvm_name.get()
4362
                                 + ": cannot find action -- " + (*act_iter));
4363
#endif
4364
            }
4365
        }
4366

4367
        vd->set_rewrite_src_name();
428,040✔
4368
    }
4369

4370
    if (this->elf_type == elf_type_t::ELF_TYPE_JSON) {
48,652✔
4371
        for (const auto& vd : this->elf_value_def_order) {
138,388✔
4372
            this->elf_value_def_frag_map[vd->vd_meta.lvm_name
130,600✔
4373
                                             .to_string_fragment()]
130,600✔
4374
                = vd.get();
261,200✔
4375
        }
4376
    }
4377

4378
    for (const auto& td_pair : this->lf_tag_defs) {
50,914✔
4379
        const auto& td = td_pair.second;
2,262✔
4380

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

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

4412
    for (const auto& opid_desc_pair : *this->lf_opid_description_def) {
69,563✔
4413
        for (const auto& opid_desc : *opid_desc_pair.second.od_descriptors) {
49,753✔
4414
            auto iter = this->elf_value_defs.find(opid_desc.od_field.pp_value);
28,842✔
4415
            if (iter == this->elf_value_defs.end()) {
28,842✔
4416
                errors.emplace_back(
2✔
4417
                    lnav::console::user_message::error(
×
4418
                        attr_line_t("invalid opid description field ")
4✔
4419
                            .append_quoted(lnav::roles::symbol(
4✔
4420
                                opid_desc.od_field.pp_path.to_string())))
4✔
4421
                        .with_reason(
4✔
4422
                            attr_line_t("unknown value name ")
4✔
4423
                                .append_quoted(opid_desc.od_field.pp_value))
2✔
4424
                        .with_snippets(this->get_snippets()));
4✔
4425
            } else {
4426
                this->lf_desc_fields.insert(iter->first);
28,840✔
4427
                iter->second->vd_is_desc_field = true;
28,840✔
4428
            }
4429
        }
4430
    }
4431

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

4452
    if (this->elf_type == elf_type_t::ELF_TYPE_TEXT
97,304✔
4453
        && this->elf_samples.empty())
48,652✔
4454
    {
4455
        errors.emplace_back(
3✔
4456
            lnav::console::user_message::error(
×
4457
                attr_line_t()
6✔
4458
                    .append_quoted(
3✔
4459
                        lnav::roles::symbol(this->elf_name.to_string()))
6✔
4460
                    .append(" is not a valid log format"))
3✔
4461
                .with_reason("log message samples must be included in a format "
6✔
4462
                             "definition")
4463
                .with_snippets(this->get_snippets()));
6✔
4464
    }
4465

4466
    for (const auto& pat : this->elf_pattern_order) {
143,690✔
4467
        if (pat->p_module_format || this->elf_type != elf_type_t::ELF_TYPE_TEXT)
95,038✔
4468
        {
4469
            continue;
2,164✔
4470
        }
4471
        if (pat->p_pcre.pp_value->name_index(this->lf_timestamp_field.get())
92,874✔
4472
            < 0)
92,874✔
4473
        {
4474
            attr_line_t notes;
1✔
4475
            bool first_note = true;
1✔
4476

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

4504
    for (size_t sample_index = 0; sample_index < this->elf_samples.size();
223,912✔
4505
         sample_index += 1)
175,260✔
4506
    {
4507
        auto& elf_sample = this->elf_samples[sample_index];
175,260✔
4508
        auto sample_lines
4509
            = string_fragment(elf_sample.s_line.pp_value).split_lines();
175,260✔
4510

4511
        if (this->test_line(elf_sample, errors).is<scan_match>()) {
175,260✔
4512
            for (const auto& pat_name : elf_sample.s_matched_regexes) {
349,076✔
4513
                this->elf_patterns[pat_name]->p_matched_samples.emplace(
173,817✔
4514
                    sample_index);
4515
            }
4516
        }
4517
    }
175,260✔
4518

4519
    if (!this->elf_samples.empty()) {
48,652✔
4520
        for (const auto& elf_sample : this->elf_samples) {
216,121✔
4521
            if (elf_sample.s_matched_regexes.size() <= 1) {
175,260✔
4522
                continue;
175,260✔
4523
            }
4524

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

4544
        for (const auto& pat : this->elf_pattern_order) {
135,896✔
4545
            if (pat->p_module_format) {
95,035✔
4546
                continue;
2,163✔
4547
            }
4548

4549
            if (pat->p_matched_samples.empty()) {
92,872✔
4550
                errors.emplace_back(
2✔
4551
                    lnav::console::user_message::warning(
×
4552
                        attr_line_t("invalid pattern: ")
4✔
4553
                            .append_quoted(
2✔
4554
                                lnav::roles::symbol(pat->p_config_path)))
4✔
4555
                        .with_reason("pattern does not match any samples")
4✔
4556
                        .with_snippet(lnav::console::snippet::from(
6✔
4557
                            pat->p_pcre.pp_location, ""))
2✔
4558
                        .with_help(
4559
                            "every pattern should have at least one sample "
4560
                            "that it matches"));
4561
            }
4562
        }
4563
    }
4564

4565
    size_t value_def_index = 0;
48,652✔
4566
    for (auto& elf_value_def : this->elf_value_def_order) {
476,692✔
4567
        elf_value_def->vd_meta.lvm_values_index
428,040✔
4568
            = std::make_optional(value_def_index++);
428,040✔
4569

4570
        if (elf_value_def->vd_meta.lvm_foreign_key
428,040✔
4571
            || elf_value_def->vd_meta.lvm_identifier)
428,040✔
4572
        {
4573
            continue;
228,364✔
4574
        }
4575

4576
        switch (elf_value_def->vd_meta.lvm_kind) {
199,676✔
4577
            case value_kind_t::VALUE_INTEGER:
50,854✔
4578
            case value_kind_t::VALUE_FLOAT:
4579
                this->elf_numeric_value_defs.push_back(elf_value_def);
50,854✔
4580
                break;
50,854✔
4581
            default:
148,822✔
4582
                break;
148,822✔
4583
        }
4584
    }
4585

4586
    int format_index = 0;
48,652✔
4587
    for (auto iter = this->jlf_line_format.begin();
48,652✔
4588
         iter != this->jlf_line_format.end();
144,254✔
4589
         ++iter, format_index++)
95,602✔
4590
    {
4591
        static const intern_string_t ts
4592
            = intern_string::lookup("__timestamp__");
97,044✔
4593
        static const intern_string_t level_field
4594
            = intern_string::lookup("__level__");
97,044✔
4595
        auto& jfe = *iter;
95,602✔
4596

4597
        if (startswith(jfe.jfe_value.pp_value.get(), "/")) {
95,602✔
4598
            jfe.jfe_value.pp_value
4599
                = intern_string::lookup(jfe.jfe_value.pp_value.get() + 1);
192✔
4600
        }
4601
        if (!jfe.jfe_ts_format.empty()) {
95,602✔
4602
            if (!jfe.jfe_value.pp_value.empty() && jfe.jfe_value.pp_value != ts)
817✔
4603
            {
4604
                log_warning(
96✔
4605
                    "%s:line-format[%d]:ignoring field '%s' since "
4606
                    "timestamp-format was used",
4607
                    this->elf_name.get(),
4608
                    format_index,
4609
                    jfe.jfe_value.pp_value.get());
4610
            }
4611
            jfe.jfe_value.pp_value = ts;
817✔
4612
        }
4613

4614
        switch (jfe.jfe_type) {
95,602✔
4615
            case json_log_field::VARIABLE: {
62,871✔
4616
                auto vd_iter
4617
                    = this->elf_value_defs.find(jfe.jfe_value.pp_value);
62,871✔
4618
                if (jfe.jfe_value.pp_value == ts) {
62,871✔
4619
                    this->elf_value_defs[this->lf_timestamp_field]
6,057✔
4620
                        ->vd_meta.lvm_hidden
6,057✔
4621
                        = true;
6,057✔
4622
                } else if (jfe.jfe_value.pp_value == level_field) {
56,814✔
4623
                    this->elf_value_defs[this->elf_level_field]
2,884✔
4624
                        ->vd_meta.lvm_hidden
2,884✔
4625
                        = true;
2,884✔
4626
                } else if (vd_iter == this->elf_value_defs.end()) {
53,930✔
4627
                    errors.emplace_back(
2✔
4628
                        lnav::console::user_message::error(
×
4629
                            attr_line_t("invalid line format element ")
4✔
4630
                                .append_quoted(lnav::roles::symbol(fmt::format(
4✔
4631
                                    FMT_STRING("/{}/line-format/{}/field"),
6✔
4632
                                    this->elf_name,
2✔
4633
                                    format_index))))
4634
                            .with_reason(
4✔
4635
                                attr_line_t()
4✔
4636
                                    .append_quoted(jfe.jfe_value.pp_value)
2✔
4637
                                    .append(" is not a defined value"))
2✔
4638
                            .with_snippet(jfe.jfe_value.to_snippet()));
4✔
4639
                } else {
4640
                    vd_iter->second->vd_line_format_index = format_index;
53,928✔
4641
                    switch (vd_iter->second->vd_meta.lvm_kind) {
53,928✔
4642
                        case value_kind_t::VALUE_INTEGER:
9,565✔
4643
                        case value_kind_t::VALUE_FLOAT:
4644
                            if (jfe.jfe_align
9,565✔
4645
                                == json_format_element::align_t::NONE)
4646
                            {
4647
                                jfe.jfe_align
4648
                                    = json_format_element::align_t::RIGHT;
8,844✔
4649
                            }
4650
                            break;
9,565✔
4651
                        default:
44,363✔
4652
                            break;
44,363✔
4653
                    }
4654
                }
4655
                break;
62,871✔
4656
            }
4657
            case json_log_field::CONSTANT:
32,731✔
4658
                this->jlf_line_format_init_count
32,731✔
4659
                    += std::count(jfe.jfe_default_value.begin(),
32,731✔
4660
                                  jfe.jfe_default_value.end(),
4661
                                  '\n');
32,731✔
4662
                break;
32,731✔
4663
            default:
×
4664
                break;
×
4665
        }
4666
    }
4667

4668
    for (auto& hd_pair : this->elf_highlighter_patterns) {
49,038✔
4669
        auto& hd = hd_pair.second;
386✔
4670
        text_attrs attrs;
386✔
4671

4672
        if (!hd.hd_color.pp_value.empty()) {
386✔
4673
            attrs.ta_fg_color = vc.match_color(
385✔
4674
                styling::color_unit::from_str(hd.hd_color.pp_value)
770✔
4675
                    .unwrapOrElse([&](const auto& msg) {
1✔
4676
                        errors.emplace_back(
1✔
4677
                            lnav::console::user_message::error(
4678
                                attr_line_t()
1✔
4679
                                    .append_quoted(hd.hd_color.pp_value)
2✔
4680
                                    .append(" is not a valid color value for "
1✔
4681
                                            "property ")
4682
                                    .append_quoted(lnav::roles::symbol(
2✔
4683
                                        hd.hd_color.pp_path.to_string())))
1✔
4684
                                .with_reason(msg)
2✔
4685
                                .with_snippet(hd.hd_color.to_snippet()));
1✔
4686
                        return styling::color_unit::EMPTY;
1✔
4687
                    }));
4688
        }
4689

4690
        if (!hd.hd_background_color.pp_value.empty()) {
386✔
4691
            attrs.ta_bg_color = vc.match_color(
1✔
4692
                styling::color_unit::from_str(hd.hd_background_color.pp_value)
2✔
4693
                    .unwrapOrElse([&](const auto& msg) {
1✔
4694
                        errors.emplace_back(
1✔
4695
                            lnav::console::user_message::error(
4696
                                attr_line_t()
1✔
4697
                                    .append_quoted(
2✔
4698
                                        hd.hd_background_color.pp_value)
1✔
4699
                                    .append(" is not a valid color value for "
1✔
4700
                                            "property ")
4701
                                    .append_quoted(lnav::roles::symbol(
2✔
4702
                                        hd.hd_background_color.pp_path
1✔
4703
                                            .to_string())))
4704
                                .with_reason(msg)
2✔
4705
                                .with_snippet(
4706
                                    hd.hd_background_color.to_snippet()));
1✔
4707
                        return styling::color_unit::EMPTY;
1✔
4708
                    }));
4709
        }
4710

4711
        if (hd.hd_underline) {
386✔
4712
            attrs |= text_attrs::style::underline;
96✔
4713
        }
4714
        if (hd.hd_blink) {
386✔
4715
            attrs |= text_attrs::style::blink;
×
4716
        }
4717

4718
        if (hd.hd_pattern.pp_value != nullptr) {
386✔
4719
            this->lf_highlighters.emplace_back(hd.hd_pattern.pp_value);
384✔
4720
            this->lf_highlighters.back()
384✔
4721
                .with_name(hd_pair.first.to_string())
768✔
4722
                .with_format_name(this->elf_name)
384✔
4723
                .with_attrs(attrs)
384✔
4724
                .with_nestable(hd.hd_nestable);
384✔
4725
        }
4726
    }
4727

4728
    this->lf_value_stats.resize(this->elf_value_defs.size());
48,652✔
4729
}
4730

4731
void
4732
external_log_format::register_vtabs(
40,622✔
4733
    log_vtab_manager* vtab_manager,
4734
    std::vector<lnav::console::user_message>& errors)
4735
{
4736
    for (auto& elf_search_table : this->elf_search_tables) {
47,907✔
4737
        if (elf_search_table.second.std_pattern.pp_value == nullptr) {
7,285✔
4738
            continue;
1✔
4739
        }
4740

4741
        auto lst = std::make_shared<log_search_table>(
4742
            elf_search_table.second.std_pattern.pp_value,
7,284✔
4743
            elf_search_table.first);
7,284✔
4744
        lst->lst_format = this;
7,284✔
4745
        lst->lst_log_path_glob = elf_search_table.second.std_glob;
7,284✔
4746
        if (elf_search_table.second.std_level != LEVEL_UNKNOWN) {
7,284✔
4747
            lst->lst_log_level = elf_search_table.second.std_level;
3,035✔
4748
        }
4749
        auto errmsg = vtab_manager->register_vtab(lst);
7,284✔
4750
        if (!errmsg.empty()) {
7,284✔
4751
#if 0
4752
            errors.push_back("error:" + this->elf_name.to_string() + ":"
4753
                             + search_iter->first.to_string()
4754
                             + ":unable to register table -- " + errmsg);
4755
#endif
4756
        }
4757
    }
7,284✔
4758
}
40,622✔
4759

4760
bool
4761
external_log_format::match_samples(const std::vector<sample_t>& samples) const
3,244,420✔
4762
{
4763
    for (const auto& sample_iter : samples) {
14,776,836✔
4764
        for (const auto& pat_iter : this->elf_pattern_order) {
32,700,960✔
4765
            auto& pat = *pat_iter;
21,168,544✔
4766

4767
            if (!pat.p_pcre.pp_value) {
21,168,544✔
4768
                continue;
×
4769
            }
4770

4771
            if (pat.p_pcre.pp_value
42,337,088✔
4772
                    ->find_in(sample_iter.s_line.pp_value, PCRE2_NO_UTF_CHECK)
42,337,088✔
4773
                    .ignore_error())
42,337,088✔
4774
            {
4775
                return true;
15,784✔
4776
            }
4777
        }
4778
    }
4779

4780
    return false;
3,228,636✔
4781
}
4782

4783
class external_log_table : public log_format_vtab_impl {
4784
public:
4785
    explicit external_log_table(const external_log_format& elf)
40,622✔
4786
        : log_format_vtab_impl(elf), elt_format(elf)
40,622✔
4787
    {
4788
    }
40,622✔
4789

4790
    void get_columns(std::vector<vtab_column>& cols) const override
41,012✔
4791
    {
4792
        const auto& elf = this->elt_format;
41,012✔
4793

4794
        cols.resize(elf.elf_column_count);
41,012✔
4795
        for (const auto& vd : elf.elf_value_def_order) {
403,756✔
4796
            auto type_pair = log_vtab_impl::logline_value_to_sqlite_type(
362,744✔
4797
                vd->vd_meta.lvm_kind);
362,744✔
4798

4799
            if (!vd->vd_meta.lvm_column.is<logline_value_meta::table_column>())
362,744✔
4800
            {
4801
                continue;
34,057✔
4802
            }
4803

4804
            auto col
4805
                = vd->vd_meta.lvm_column.get<logline_value_meta::table_column>()
328,687✔
4806
                      .value;
328,687✔
4807
            require(0 <= col && col < elf.elf_column_count);
328,687✔
4808

4809
            cols[col].vc_name = vd->vd_meta.lvm_name.get();
328,687✔
4810
            cols[col].vc_type = type_pair.first;
328,687✔
4811
            cols[col].vc_subtype = type_pair.second;
328,687✔
4812
            cols[col].vc_collator = vd->vd_collate;
328,687✔
4813
            cols[col].vc_comment = vd->vd_description;
328,687✔
4814
        }
4815
    }
41,012✔
4816

4817
    void get_foreign_keys(
34,254✔
4818
        std::unordered_set<std::string>& keys_inout) const override
4819
    {
4820
        log_vtab_impl::get_foreign_keys(keys_inout);
34,254✔
4821

4822
        for (const auto& elf_value_def : this->elt_format.elf_value_defs) {
495,054✔
4823
            if (elf_value_def.second->vd_meta.lvm_foreign_key
460,800✔
4824
                || elf_value_def.second->vd_meta.lvm_identifier)
460,800✔
4825
            {
4826
                keys_inout.emplace(elf_value_def.first.to_string());
167,070✔
4827
            }
4828
        }
4829
    }
34,254✔
4830

4831
    bool next(log_cursor& lc, logfile_sub_source& lss) override
3,481✔
4832
    {
4833
        if (lc.is_eof()) {
3,481✔
4834
            return true;
×
4835
        }
4836

4837
        content_line_t cl(lss.at(lc.lc_curr_line));
3,481✔
4838
        auto* lf = lss.find_file_ptr(cl);
3,481✔
4839
        auto lf_iter = lf->begin() + cl;
3,481✔
4840
        uint8_t mod_id = lf_iter->get_module_id();
3,481✔
4841

4842
        if (lf_iter->is_continued()) {
3,481✔
4843
            return false;
×
4844
        }
4845

4846
        this->elt_module_format.mf_mod_format = nullptr;
3,481✔
4847
        if (lf->get_format_name() == this->lfvi_format.get_name()) {
3,481✔
4848
            return true;
3,470✔
4849
        }
4850
        if (mod_id && mod_id == this->lfvi_format.lf_mod_index) {
11✔
4851
            auto format = lf->get_format();
2✔
4852

4853
            return lf->read_line(lf_iter)
2✔
4854
                .map([this, format, cl, lf](auto line) {
4✔
4855
                    string_attrs_t sa;
2✔
4856
                    logline_value_vector values;
2✔
4857
                    line_range mod_name_range;
2✔
4858
                    intern_string_t mod_name;
2✔
4859

4860
                    values.lvv_sbr = line.clone();
2✔
4861
                    format->annotate(lf, cl, sa, values, false);
2✔
4862
                    this->elt_container_body
2✔
4863
                        = find_string_attr_range(sa, &SA_BODY);
2✔
4864
                    if (!this->elt_container_body.is_valid()) {
2✔
4865
                        return false;
×
4866
                    }
4867
                    this->elt_container_body.ltrim(line.get_data());
2✔
4868
                    mod_name_range = find_string_attr_range(sa, &L_MODULE);
2✔
4869
                    if (!mod_name_range.is_valid()) {
2✔
4870
                        return false;
×
4871
                    }
4872
                    mod_name = intern_string::lookup(
2✔
4873
                        &line.get_data()[mod_name_range.lr_start],
2✔
4874
                        mod_name_range.length());
2✔
4875
                    this->elt_module_format
2✔
4876
                        = external_log_format::MODULE_FORMATS[mod_name];
2✔
4877
                    if (!this->elt_module_format.mf_mod_format) {
2✔
4878
                        return false;
×
4879
                    }
4880
                    return this->elt_module_format.mf_mod_format->get_name()
2✔
4881
                        == this->lfvi_format.get_name();
4✔
4882
                })
2✔
4883
                .unwrapOr(false);
2✔
4884
        }
2✔
4885

4886
        return false;
9✔
4887
    }
4888

4889
    void extract(logfile* lf,
3,387✔
4890
                 uint64_t line_number,
4891
                 string_attrs_t& sa,
4892
                 logline_value_vector& values) override
4893
    {
4894
        auto& line = values.lvv_sbr;
3,387✔
4895
        auto format = lf->get_format();
3,387✔
4896

4897
        if (this->elt_module_format.mf_mod_format != nullptr) {
3,387✔
4898
            shared_buffer_ref body_ref;
2✔
4899

4900
            body_ref.subset(line,
2✔
4901
                            this->elt_container_body.lr_start,
2✔
4902
                            this->elt_container_body.length());
2✔
4903
            sa.clear();
2✔
4904
            auto narrow_res
4905
                = values.lvv_sbr.narrow(this->elt_container_body.lr_start,
2✔
4906
                                        this->elt_container_body.length());
2✔
4907
            values.lvv_values.clear();
2✔
4908
            this->elt_module_format.mf_mod_format->annotate(
2✔
4909
                lf, line_number, sa, values, false);
4910
            values.lvv_sbr.widen(narrow_res);
2✔
4911
        } else {
2✔
4912
            sa.clear();
3,385✔
4913
            format->annotate(lf, line_number, sa, values, false);
3,385✔
4914
        }
4915
    }
3,387✔
4916

4917
    const external_log_format& elt_format;
4918
    module_format elt_module_format;
4919
    struct line_range elt_container_body;
4920
};
4921

4922
std::shared_ptr<log_vtab_impl>
4923
external_log_format::get_vtab_impl() const
40,622✔
4924
{
4925
    return std::make_shared<external_log_table>(*this);
40,622✔
4926
}
4927

4928
std::shared_ptr<log_format>
4929
external_log_format::specialized(int fmt_lock)
536✔
4930
{
4931
    auto retval = std::make_shared<external_log_format>(*this);
536✔
4932

4933
    retval->lf_specialized = true;
536✔
4934
    this->lf_pattern_locks.clear();
536✔
4935
    if (fmt_lock != -1) {
536✔
4936
        retval->lf_pattern_locks.emplace_back(0, fmt_lock);
43✔
4937
    }
4938

4939
    if (this->elf_type == elf_type_t::ELF_TYPE_JSON) {
536✔
4940
        this->jlf_parse_context
4941
            = std::make_shared<yajlpp_parse_context>(this->elf_name);
43✔
4942
        this->jlf_yajl_handle.reset(
43✔
4943
            yajl_alloc(&this->jlf_parse_context->ypc_callbacks,
43✔
4944
                       nullptr,
4945
                       this->jlf_parse_context.get()),
43✔
4946
            yajl_handle_deleter());
4947
        yajl_config(this->jlf_yajl_handle.get(), yajl_dont_validate_strings, 1);
43✔
4948
        this->jlf_cached_line.reserve(16 * 1024);
43✔
4949
    }
4950

4951
    this->lf_value_stats.clear();
536✔
4952
    this->lf_value_stats.resize(this->elf_value_defs.size());
536✔
4953
    this->elf_specialized_value_defs_state = *this->elf_value_defs_state;
536✔
4954

4955
    return retval;
1,072✔
4956
}
536✔
4957

4958
log_format::match_name_result
4959
external_log_format::match_name(const std::string& filename)
707,079✔
4960
{
4961
    if (this->elf_filename_pcre.pp_value == nullptr) {
707,079✔
4962
        return name_matched{};
705,720✔
4963
    }
4964

4965
    if (this->elf_filename_pcre.pp_value->find_in(filename)
2,718✔
4966
            .ignore_error()
2,718✔
4967
            .has_value())
1,359✔
4968
    {
4969
        return name_matched{};
238✔
4970
    }
4971

4972
    return name_mismatched{
2,242✔
4973
        this->elf_filename_pcre.pp_value->match_partial(filename),
2,242✔
4974
        this->elf_filename_pcre.pp_value->get_pattern(),
1,121✔
4975
    };
1,121✔
4976
}
4977

4978
auto
4979
external_log_format::value_line_count(const value_def* vd,
162,681✔
4980
                                      bool top_level,
4981
                                      std::optional<double> val,
4982
                                      const unsigned char* str,
4983
                                      ssize_t len,
4984
                                      yajl_string_props_t* props)
4985
    -> value_line_count_result
4986
{
4987
    value_line_count_result retval;
162,681✔
4988

4989
    if (vd == nullptr) {
162,681✔
4990
        if (this->jlf_hide_extra || !top_level) {
153,707✔
4991
            retval.vlcr_count = 0;
140,683✔
4992
        }
4993

4994
        return retval;
153,707✔
4995
    }
4996

4997
    if (str != nullptr && props != nullptr && !val) {
8,974✔
4998
        auto frag = string_fragment::from_bytes(str, len);
4,346✔
4999
        while (frag.endswith("\n")) {
4,515✔
5000
            frag.pop_back();
169✔
5001
            props->line_feeds -= 1;
169✔
5002
        }
5003
        retval.vlcr_has_ansi |= props->has_ansi;
4,346✔
5004
        retval.vlcr_count += props->line_feeds;
4,346✔
5005
    }
5006

5007
    if (vd->vd_meta.lvm_values_index) {
8,974✔
5008
        auto& lvs = this->lf_value_stats[vd->vd_meta.lvm_values_index.value()];
8,974✔
5009
        if (len > lvs.lvs_width) {
8,974✔
5010
            lvs.lvs_width = len;
792✔
5011
        }
5012
        if (val) {
8,974✔
5013
            lvs.add_value(val.value());
151✔
5014
        }
5015
    }
5016

5017
    if (vd->vd_line_format_index) {
8,974✔
5018
        retval.vlcr_line_format_count += 1;
2,883✔
5019
        retval.vlcr_count -= 1;
2,883✔
5020
        retval.vlcr_line_format_index = vd->vd_line_format_index;
2,883✔
5021
    }
5022
    if (vd->vd_meta.is_hidden()) {
8,974✔
5023
        retval.vlcr_count = 0;
5,954✔
5024
        return retval;
5,954✔
5025
    }
5026

5027
    return retval;
3,020✔
5028
}
5029

5030
log_level_t
5031
external_log_format::convert_level(string_fragment sf,
180,714✔
5032
                                   scan_batch_context* sbc) const
5033
{
5034
    auto retval = LEVEL_INFO;
180,714✔
5035

5036
    if (sf.is_valid()) {
180,714✔
5037
        if (sbc != nullptr) {
140,602✔
5038
            auto ssm_res = sbc->sbc_level_cache.lookup(sf);
6,636✔
5039
            if (ssm_res.has_value()) {
6,636✔
5040
                return static_cast<log_level_t>(ssm_res.value());
4,078✔
5041
            }
5042
        }
5043

5044
        if (this->elf_level_patterns.empty()) {
136,524✔
5045
            retval = string2level(sf.data(), sf.length());
26,775✔
5046
        } else {
5047
            for (const auto& elf_level_pattern : this->elf_level_patterns) {
295,754✔
5048
                if (elf_level_pattern.second.lp_pcre.pp_value
541,364✔
5049
                        ->find_in(sf, PCRE2_NO_UTF_CHECK)
541,364✔
5050
                        .ignore_error()
541,364✔
5051
                        .has_value())
270,682✔
5052
                {
5053
                    retval = elf_level_pattern.first;
84,677✔
5054
                    break;
84,677✔
5055
                }
5056
            }
5057
        }
5058

5059
        if (sbc != nullptr
136,524✔
5060
            && sf.length() <= lnav::small_string_map::MAX_KEY_SIZE)
136,524✔
5061
        {
5062
            sbc->sbc_level_cache.insert(sf, retval);
1,910✔
5063
        }
5064
    }
5065

5066
    return retval;
176,636✔
5067
}
5068

5069
logline_value_meta
5070
external_log_format::get_value_meta(intern_string_t field_name,
14,767✔
5071
                                    value_kind_t kind)
5072
{
5073
    const auto iter = this->elf_value_defs.find(field_name);
14,767✔
5074
    if (iter == this->elf_value_defs.end()) {
14,767✔
5075
        auto retval = logline_value_meta(
5076
            field_name, kind, logline_value_meta::external_column{}, this);
811✔
5077

5078
        retval.lvm_hidden = this->jlf_hide_extra;
811✔
5079
        return retval;
811✔
5080
    }
811✔
5081

5082
    auto lvm = iter->second->vd_meta;
13,956✔
5083

5084
    lvm.lvm_kind = kind;
13,956✔
5085
    return lvm;
13,956✔
5086
}
13,956✔
5087

5088
logline_value_meta
5089
external_log_format::get_value_meta(yajlpp_parse_context* ypc,
2,271✔
5090
                                    const value_def* vd,
5091
                                    value_kind_t kind)
5092
{
5093
    if (vd == nullptr) {
2,271✔
5094
        auto retval = logline_value_meta(
5095
            ypc->get_path(), kind, logline_value_meta::external_column{}, this);
57✔
5096

5097
        retval.lvm_hidden = this->jlf_hide_extra;
57✔
5098
        return retval;
57✔
5099
    }
57✔
5100

5101
    auto lvm = vd->vd_meta;
2,214✔
5102

5103
    lvm.lvm_kind = kind;
2,214✔
5104
    return lvm;
2,214✔
5105
}
2,214✔
5106

5107
void
5108
external_log_format::json_append(const json_format_element& jfe,
8,630✔
5109
                                 const value_def* vd,
5110
                                 const string_fragment& sf)
5111
{
5112
    if (jfe.jfe_align == json_format_element::align_t::RIGHT) {
8,630✔
5113
        auto sf_width = sf.column_width();
343✔
5114
        if (sf_width < jfe.jfe_min_width) {
343✔
5115
            this->json_append_to_cache(jfe.jfe_min_width - sf_width);
×
5116
        } else if (jfe.jfe_auto_width && vd != nullptr
32✔
5117
                   && sf_width
375✔
5118
                       < this->lf_value_stats[vd->vd_meta.lvm_values_index
5119
                                                  .value()]
32✔
5120
                             .lvs_width)
32✔
5121
        {
5122
            this->json_append_to_cache(
12✔
5123
                this->lf_value_stats[vd->vd_meta.lvm_values_index.value()]
12✔
5124
                    .lvs_width
12✔
5125
                - sf_width);
12✔
5126
        }
5127
    }
5128
    this->json_append_to_cache(sf.data(), sf.length());
8,630✔
5129
    if ((jfe.jfe_align == json_format_element::align_t::LEFT
8,630✔
5130
         || jfe.jfe_align == json_format_element::align_t::NONE)
8,612✔
5131
        && (jfe.jfe_min_width > 0 || jfe.jfe_auto_width))
8,287✔
5132
    {
5133
        auto sf_width = sf.column_width();
1,078✔
5134
        if (sf_width < jfe.jfe_min_width) {
1,078✔
5135
            this->json_append_to_cache(jfe.jfe_min_width - sf_width);
287✔
5136
        } else if (jfe.jfe_auto_width && vd != nullptr
704✔
5137
                   && sf_width
1,495✔
5138
                       < this->lf_value_stats[vd->vd_meta.lvm_values_index
5139
                                                  .value()]
644✔
5140
                             .lvs_width)
644✔
5141
        {
5142
            this->json_append_to_cache(
377✔
5143
                this->lf_value_stats[vd->vd_meta.lvm_values_index.value()]
377✔
5144
                    .lvs_width
377✔
5145
                - sf_width);
377✔
5146
        }
5147
    }
5148
}
8,630✔
5149

5150
intern_string_t
5151
external_log_format::get_pattern_name(uint64_t line_number) const
50✔
5152
{
5153
    if (this->elf_type != elf_type_t::ELF_TYPE_TEXT) {
50✔
5154
        static auto structured = intern_string::lookup("structured");
5155

5156
        return structured;
×
5157
    }
5158
    int pat_index = this->pattern_index_for_line(line_number);
50✔
5159
    return this->elf_pattern_order[pat_index]->p_name;
50✔
5160
}
5161

5162
int
5163
log_format::pattern_index_for_line(uint64_t line_number) const
6,760✔
5164
{
5165
    if (this->lf_pattern_locks.empty()) {
6,760✔
5166
        return -1;
×
5167
    }
5168

5169
    auto iter = lower_bound(this->lf_pattern_locks.cbegin(),
6,760✔
5170
                            this->lf_pattern_locks.cend(),
5171
                            line_number,
5172
                            [](const pattern_for_lines& pfl, uint32_t line) {
6,819✔
5173
                                return pfl.pfl_line < line;
6,819✔
5174
                            });
5175

5176
    if (iter == this->lf_pattern_locks.end() || iter->pfl_line != line_number) {
6,760✔
5177
        --iter;
6,141✔
5178
    }
5179

5180
    return iter->pfl_pat_index;
6,760✔
5181
}
5182

5183
std::string
5184
log_format::get_pattern_path(uint64_t line_number) const
×
5185
{
5186
    int pat_index = this->pattern_index_for_line(line_number);
×
5187
    return fmt::format(FMT_STRING("builtin ({})"), pat_index);
×
5188
}
5189

5190
intern_string_t
5191
log_format::get_pattern_name(uint64_t line_number) const
6✔
5192
{
5193
    char pat_str[128];
5194

5195
    int pat_index = this->pattern_index_for_line(line_number);
6✔
5196
    auto to_n_res = fmt::format_to_n(
×
5197
        pat_str, sizeof(pat_str) - 1, FMT_STRING("builtin ({})"), pat_index);
18✔
5198
    pat_str[to_n_res.size] = '\0';
6✔
5199
    return intern_string::lookup(pat_str);
12✔
5200
}
5201

5202
std::shared_ptr<log_format>
5203
log_format::find_root_format(const char* name)
1,224✔
5204
{
5205
    auto& fmts = get_root_formats();
1,224✔
5206
    for (auto& lf : fmts) {
46,236✔
5207
        if (lf->get_name() == name) {
46,236✔
5208
            return lf;
1,224✔
5209
        }
5210
    }
5211
    return nullptr;
×
5212
}
5213

5214
exttm
5215
log_format::tm_for_display(logfile::iterator ll, string_fragment sf)
377✔
5216
{
5217
    auto adjusted_time = ll->get_timeval();
377✔
5218
    exttm retval;
377✔
5219

5220
    retval.et_nsec = std::chrono::duration_cast<std::chrono::nanoseconds>(
377✔
5221
                         std::chrono::microseconds{adjusted_time.tv_usec})
×
5222
                         .count();
377✔
5223
    if (this->lf_timestamp_flags & ETF_NANOS_SET) {
377✔
5224
        timeval actual_tv;
5225
        exttm tm;
×
5226
        if (this->lf_date_time.scan(sf.data(),
×
5227
                                    sf.length(),
×
5228
                                    this->get_timestamp_formats(),
5229
                                    &tm,
5230
                                    actual_tv,
5231
                                    false))
5232
        {
5233
            adjusted_time.tv_usec = actual_tv.tv_usec;
×
5234
            retval.et_nsec = tm.et_nsec;
×
5235
        }
5236
    }
5237
    gmtime_r(&adjusted_time.tv_sec, &retval.et_tm);
377✔
5238
    retval.et_flags = this->lf_timestamp_flags;
377✔
5239
    if (this->lf_timestamp_flags & ETF_ZONE_SET
377✔
5240
        && this->lf_date_time.dts_zoned_to_local)
287✔
5241
    {
5242
        retval.et_flags &= ~ETF_Z_IS_UTC;
287✔
5243
    }
5244
    retval.et_gmtoff = this->lf_date_time.dts_local_offset_cache;
377✔
5245

5246
    return retval;
754✔
5247
}
5248

5249
log_format::pattern_for_lines::pattern_for_lines(uint32_t pfl_line,
2,043✔
5250
                                                 uint32_t pfl_pat_index)
2,043✔
5251
    : pfl_line(pfl_line), pfl_pat_index(pfl_pat_index)
2,043✔
5252
{
5253
}
2,043✔
5254

5255
void
5256
logline_value_stats::merge(const logline_value_stats& other)
35✔
5257
{
5258
    if (other.lvs_count == 0) {
35✔
5259
        return;
×
5260
    }
5261

5262
    require(other.lvs_min_value <= other.lvs_max_value);
35✔
5263

5264
    if (other.lvs_width > this->lvs_width) {
35✔
5265
        this->lvs_width = other.lvs_width;
×
5266
    }
5267
    if (other.lvs_min_value < this->lvs_min_value) {
35✔
5268
        this->lvs_min_value = other.lvs_min_value;
35✔
5269
    }
5270
    if (other.lvs_max_value > this->lvs_max_value) {
35✔
5271
        this->lvs_max_value = other.lvs_max_value;
35✔
5272
    }
5273
    this->lvs_count += other.lvs_count;
35✔
5274
    this->lvs_total += other.lvs_total;
35✔
5275

5276
    ensure(this->lvs_count >= 0);
35✔
5277
    ensure(this->lvs_min_value <= this->lvs_max_value);
35✔
5278
}
5279

5280
void
5281
logline_value_stats::add_value(double value)
23,560✔
5282
{
5283
    if (value < this->lvs_min_value) {
23,560✔
5284
        this->lvs_min_value = value;
718✔
5285
    }
5286
    if (value > this->lvs_max_value) {
23,560✔
5287
        this->lvs_max_value = value;
1,368✔
5288
    }
5289
    this->lvs_count += 1;
23,560✔
5290
    this->lvs_total += value;
23,560✔
5291
}
23,560✔
5292

5293
std::vector<logline_value_meta>
5294
external_log_format::get_value_metadata() const
7,284✔
5295
{
5296
    std::vector<logline_value_meta> retval;
7,284✔
5297

5298
    for (const auto& vd : this->elf_value_def_order) {
74,661✔
5299
        retval.emplace_back(vd->vd_meta);
67,377✔
5300
    }
5301

5302
    return retval;
7,284✔
5303
}
×
5304

5305
const logline_value_stats*
5306
external_log_format::stats_for_value(const intern_string_t& name) const
×
5307
{
5308
    const auto iter = this->elf_value_defs.find(name);
×
5309
    if (iter != this->elf_value_defs.end()
×
5310
        && iter->second->vd_meta.lvm_values_index)
×
5311
    {
5312
        return &this->lf_value_stats[iter->second->vd_meta.lvm_values_index
×
5313
                                         .value()];
×
5314
    }
5315

5316
    return nullptr;
×
5317
}
5318

5319
std::string
5320
external_log_format::get_pattern_regex(uint64_t line_number) const
9✔
5321
{
5322
    if (this->elf_type != elf_type_t::ELF_TYPE_TEXT) {
9✔
5323
        return "";
2✔
5324
    }
5325
    int pat_index = this->pattern_index_for_line(line_number);
8✔
5326
    return this->elf_pattern_order[pat_index]->p_pcre.pp_value->get_pattern();
8✔
5327
}
5328

5329
bool
5330
external_log_format::hide_field(const intern_string_t field_name, bool val)
12✔
5331
{
5332
    const auto vd_iter = this->elf_value_defs.find(field_name);
12✔
5333
    if (vd_iter == this->elf_value_defs.end()) {
12✔
5334
        log_warning("field to hide not found: %s.%s",
1✔
5335
                    this->elf_name.c_str(),
5336
                    field_name.c_str());
5337
        return false;
1✔
5338
    }
5339

5340
    vd_iter->second->vd_meta.lvm_user_hidden = val;
11✔
5341
    if (this->elf_type == elf_type_t::ELF_TYPE_JSON) {
11✔
5342
        bool found = false;
2✔
5343

5344
        if (!field_name.to_string_fragment().find('#')) {
2✔
5345
            for (const auto& jfe : this->jlf_line_format) {
18✔
5346
                if (jfe.jfe_value.pp_value == field_name) {
17✔
5347
                    log_debug(
×
5348
                        "hide-field not triggering rebuild since it is in "
5349
                        "line-format: %s.%s",
5350
                        this->elf_name.c_str(),
5351
                        field_name.c_str());
5352
                    found = true;
×
5353
                    break;
×
5354
                }
5355
            }
5356
        }
5357
        if (!found) {
2✔
5358
            log_info("format field %s.%s changed, rebuilding",
2✔
5359
                     this->elf_name.get(),
5360
                     field_name.get());
5361
            this->elf_value_defs_state->vds_generation += 1;
2✔
5362
        }
5363
    }
5364
    return true;
11✔
5365
}
5366

5367
bool
5368
external_log_format::format_changed()
2,514✔
5369
{
5370
    if (this->elf_specialized_value_defs_state.vds_generation
5,028✔
5371
        != this->elf_value_defs_state->vds_generation)
2,514✔
5372
    {
5373
        this->elf_specialized_value_defs_state = *this->elf_value_defs_state;
2✔
5374
        this->jlf_cached_offset = -1;
2✔
5375
        return true;
2✔
5376
    }
5377

5378
    return false;
2,512✔
5379
}
5380

5381
bool
5382
format_tag_def::path_restriction::matches(const char* fn) const
5✔
5383
{
5384
    return fnmatch(this->p_glob.c_str(), fn, 0) == 0;
5✔
5385
}
5386

5387
bool
5388
format_partition_def::path_restriction::matches(const char* fn) const
5✔
5389
{
5390
    return fnmatch(this->p_glob.c_str(), fn, 0) == 0;
5✔
5391
}
5392

5393
/* XXX */
5394
#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