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

tstack / lnav / 11922751379-1767

19 Nov 2024 10:13PM UTC coverage: 70.233% (+0.01%) from 70.222%
11922751379-1767

push

github

tstack
[log_format] reduce piper match quality

Sigh, I forget why I set the quality to 100...  Reducing
to 1 for now.

Related to #1339

47 of 61 new or added lines in 6 files covered. (77.05%)

18 existing lines in 2 files now uncovered.

46320 of 65952 relevant lines covered (70.23%)

467543.4 hits per line

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

74.01
/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 <memory>
32

33
#include <fnmatch.h>
34
#include <stdio.h>
35
#include <string.h>
36

37
#include "base/fs_util.hh"
38
#include "base/is_utf8.hh"
39
#include "base/map_util.hh"
40
#include "base/opt_util.hh"
41
#include "base/snippet_highlighters.hh"
42
#include "base/string_util.hh"
43
#include "command_executor.hh"
44
#include "config.h"
45
#include "fmt/format.h"
46
#include "lnav_util.hh"
47
#include "log_format_ext.hh"
48
#include "log_search_table.hh"
49
#include "log_vtab_impl.hh"
50
#include "ptimec.hh"
51
#include "readline_highlighters.hh"
52
#include "scn/scn.h"
53
#include "sql_util.hh"
54
#include "sqlite-extension-func.hh"
55
#include "sqlitepp.hh"
56
#include "yajlpp/yajlpp.hh"
57
#include "yajlpp/yajlpp_def.hh"
58

59
using namespace lnav::roles::literals;
60

61
static auto intern_lifetime = intern_string::get_table_lifetime();
62

63
string_attr_type<void> logline::L_PREFIX("prefix");
64
string_attr_type<void> logline::L_TIMESTAMP("timestamp");
65
string_attr_type<std::shared_ptr<logfile>> logline::L_FILE("file");
66
string_attr_type<bookmark_metadata*> logline::L_PARTITION("partition");
67
string_attr_type<void> logline::L_MODULE("module");
68
string_attr_type<void> logline::L_OPID("opid");
69
string_attr_type<bookmark_metadata*> logline::L_META("meta");
70

71
external_log_format::mod_map_t external_log_format::MODULE_FORMATS;
72
std::vector<std::shared_ptr<external_log_format>>
73
    external_log_format::GRAPH_ORDERED_FORMATS;
74

75
static const uint32_t DATE_TIME_SET_FLAGS = ETF_YEAR_SET | ETF_MONTH_SET
76
    | ETF_DAY_SET | ETF_HOUR_SET | ETF_MINUTE_SET | ETF_SECOND_SET;
77

78
log_level_stats&
79
log_level_stats::operator|=(const log_level_stats& rhs)
308✔
80
{
81
    this->lls_error_count += rhs.lls_error_count;
308✔
82
    this->lls_warning_count += rhs.lls_warning_count;
308✔
83
    this->lls_total_count += rhs.lls_total_count;
308✔
84

85
    return *this;
308✔
86
}
87

88
log_op_description&
89
log_op_description::operator|=(const log_op_description& rhs)
308✔
90
{
91
    if (!this->lod_id && rhs.lod_id) {
308✔
92
        this->lod_id = rhs.lod_id;
×
93
    }
94
    if (this->lod_elements.size() < rhs.lod_elements.size()) {
308✔
95
        this->lod_elements = rhs.lod_elements;
×
96
    }
97

98
    return *this;
308✔
99
}
100

101
void
102
opid_time_range::clear()
×
103
{
104
    this->otr_range.invalidate();
×
105
    this->otr_sub_ops.clear();
×
106
    this->otr_level_stats = {};
×
107
}
108

109
opid_time_range&
110
opid_time_range::operator|=(const opid_time_range& rhs)
308✔
111
{
112
    this->otr_range |= rhs.otr_range;
308✔
113
    this->otr_description |= rhs.otr_description;
308✔
114
    this->otr_level_stats |= rhs.otr_level_stats;
308✔
115
    for (const auto& rhs_sub : rhs.otr_sub_ops) {
311✔
116
        bool found = false;
3✔
117

118
        for (auto& sub : this->otr_sub_ops) {
6✔
119
            if (sub.ostr_subid == rhs_sub.ostr_subid) {
3✔
120
                sub.ostr_range |= rhs_sub.ostr_range;
3✔
121
                found = true;
3✔
122
            }
123
        }
124
        if (!found) {
3✔
125
            this->otr_sub_ops.emplace_back(rhs_sub);
×
126
        }
127
    }
128
    std::stable_sort(this->otr_sub_ops.begin(), this->otr_sub_ops.end());
308✔
129

130
    return *this;
308✔
131
}
132

133
void
134
log_level_stats::update_msg_count(log_level_t lvl, int32_t amount)
5,634✔
135
{
136
    switch (lvl) {
5,634✔
137
        case LEVEL_FATAL:
372✔
138
        case LEVEL_CRITICAL:
139
        case LEVEL_ERROR:
140
            this->lls_error_count += amount;
372✔
141
            break;
372✔
142
        case LEVEL_WARNING:
10✔
143
            this->lls_warning_count += amount;
10✔
144
            break;
10✔
145
        default:
5,252✔
146
            break;
5,252✔
147
    }
148
    this->lls_total_count += amount;
5,634✔
149
}
5,634✔
150

151
void
152
opid_time_range::close_sub_ops(const string_fragment& subid)
×
153
{
154
    for (auto& other_sub : this->otr_sub_ops) {
×
155
        if (other_sub.ostr_subid == subid) {
×
156
            other_sub.ostr_open = false;
×
157
        }
158
    }
159
}
160

161
log_opid_map::iterator
162
log_opid_state::insert_op(ArenaAlloc::Alloc<char>& alloc,
5,598✔
163
                          const string_fragment& opid,
164
                          const struct timeval& log_tv)
165
{
166
    auto retval = this->los_opid_ranges.find(opid);
5,598✔
167
    if (retval == this->los_opid_ranges.end()) {
5,598✔
168
        auto opid_copy = opid.to_owned(alloc);
2,642✔
169
        auto otr = opid_time_range{time_range{log_tv, log_tv}};
2,642✔
170
        auto emplace_res = this->los_opid_ranges.emplace(opid_copy, otr);
2,642✔
171
        retval = emplace_res.first;
2,642✔
172
    } else {
2,642✔
173
        retval->second.otr_range.extend_to(log_tv);
2,956✔
174
    }
175

176
    return retval;
5,598✔
177
}
178

179
opid_sub_time_range*
180
log_opid_state::sub_op_in_use(ArenaAlloc::Alloc<char>& alloc,
36✔
181
                              log_opid_map::iterator& op_iter,
182
                              const string_fragment& subid,
183
                              const timeval& tv,
184
                              log_level_t level)
185
{
186
    const auto& opid = op_iter->first;
36✔
187
    auto sub_iter = this->los_sub_in_use.find(subid);
36✔
188
    if (sub_iter == this->los_sub_in_use.end()) {
36✔
189
        auto emp_res
190
            = this->los_sub_in_use.emplace(subid.to_owned(alloc), opid);
25✔
191

192
        sub_iter = emp_res.first;
25✔
193
    }
194

195
    auto retval = sub_iter->first;
36✔
196
    if (sub_iter->second != opid) {
36✔
197
        auto other_otr
198
            = lnav::map::find(this->los_opid_ranges, sub_iter->second);
×
199
        if (other_otr) {
×
200
            other_otr->get().close_sub_ops(retval);
×
201
        }
202
    }
203
    sub_iter->second = opid;
36✔
204

205
    auto& otr = op_iter->second;
36✔
206
    auto sub_op_iter = otr.otr_sub_ops.rbegin();
36✔
207
    for (; sub_op_iter != otr.otr_sub_ops.rend(); ++sub_op_iter) {
36✔
208
        if (sub_op_iter->ostr_open && sub_op_iter->ostr_subid == retval) {
11✔
209
            break;
11✔
210
        }
211
    }
212
    if (sub_op_iter == otr.otr_sub_ops.rend()) {
36✔
213
        otr.otr_sub_ops.emplace_back(opid_sub_time_range{
25✔
214
            retval,
215
            time_range{tv, tv},
216
        });
217
        otr.otr_sub_ops.back().ostr_level_stats.update_msg_count(level);
25✔
218

219
        return &otr.otr_sub_ops.back();
25✔
220
    } else {
221
        sub_op_iter->ostr_range.extend_to(tv);
11✔
222
        sub_op_iter->ostr_level_stats.update_msg_count(level);
11✔
223
        return &(*sub_op_iter);
11✔
224
    }
225
}
226

227
std::optional<std::string>
228
log_format::opid_descriptor::matches(const string_fragment& sf) const
218✔
229
{
230
    if (this->od_extractor.pp_value) {
218✔
231
        static thread_local auto desc_md
232
            = lnav::pcre2pp::match_data::unitialized();
218✔
233

234
        auto desc_match_res = this->od_extractor.pp_value->capture_from(sf)
218✔
235
                                  .into(desc_md)
218✔
236
                                  .matches(PCRE2_NO_UTF_CHECK | PCRE2_ANCHORED)
436✔
237
                                  .ignore_error();
218✔
238
        if (desc_match_res) {
218✔
239
            return desc_md.to_string();
39✔
240
        }
241

242
        return std::nullopt;
179✔
243
    }
244
    return sf.to_string();
×
245
}
246

247
std::string
248
log_format::opid_descriptors::to_string(
936✔
249
    const lnav::map::small<size_t, std::string>& lod) const
250
{
251
    std::string retval;
936✔
252

253
    for (size_t lpc = 0; lpc < this->od_descriptors->size(); lpc++) {
1,872✔
254
        retval.append(this->od_descriptors->at(lpc).od_prefix);
936✔
255
        auto iter = lod.find(lpc);
936✔
256
        if (iter != lod.end()) {
936✔
257
            retval.append(iter->second);
936✔
258
        }
259
        retval.append(this->od_descriptors->at(lpc).od_suffix);
936✔
260
    }
261

262
    return retval;
936✔
263
}
×
264

265
chart_type_t
266
logline_value_meta::to_chart_type() const
11✔
267
{
268
    auto retval = chart_type_t::hist;
11✔
269
    switch (this->lvm_kind) {
11✔
270
        case value_kind_t::VALUE_NULL:
×
271
            retval = chart_type_t::none;
×
272
            break;
×
273
        case value_kind_t::VALUE_INTEGER:
5✔
274
            if (!this->lvm_identifier && !this->lvm_foreign_key) {
5✔
275
                retval = chart_type_t::spectro;
3✔
276
            }
277
            break;
5✔
278
        case value_kind_t::VALUE_FLOAT:
×
279
            retval = chart_type_t::spectro;
×
280
            break;
×
281
        case value_kind_t::VALUE_XML:
2✔
282
        case value_kind_t::VALUE_JSON:
283
        case value_kind_t::VALUE_BOOLEAN:
284
        case value_kind_t::VALUE_TIMESTAMP:
285
            retval = chart_type_t::none;
2✔
286
            break;
2✔
287
        default:
4✔
288
            break;
4✔
289
    }
290

291
    return retval;
11✔
292
}
293

294
struct line_range
295
logline_value::origin_in_full_msg(const char* msg, ssize_t len) const
×
296
{
297
    if (this->lv_sub_offset == 0) {
×
298
        return this->lv_origin;
×
299
    }
300

301
    if (len == -1) {
×
302
        len = strlen(msg);
×
303
    }
304

305
    struct line_range retval = this->lv_origin;
×
306
    const char *last = msg, *msg_end = msg + len;
×
307

308
    for (int lpc = 0; lpc < this->lv_sub_offset; lpc++) {
×
309
        const auto* next = (const char*) memchr(last, '\n', msg_end - last);
×
310
        require(next != nullptr);
×
311

312
        next += 1;
×
313
        int amount = (next - last);
×
314

315
        retval.lr_start += amount;
×
316
        if (retval.lr_end != -1) {
×
317
            retval.lr_end += amount;
×
318
        }
319

320
        last = next + 1;
×
321
    }
322

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

326
        if (eol == nullptr) {
×
327
            retval.lr_end = len;
×
328
        } else {
329
            retval.lr_end = eol - msg;
×
330
        }
331
    }
332

333
    return retval;
×
334
}
335

336
logline_value::
372,849✔
337
logline_value(logline_value_meta lvm,
338
              shared_buffer_ref& sbr,
339
              struct line_range origin)
372,849✔
340
    : lv_meta(std::move(lvm)), lv_origin(origin)
372,849✔
341
{
342
    if (sbr.get_data() == nullptr) {
372,849✔
343
        this->lv_meta.lvm_kind = value_kind_t::VALUE_NULL;
×
344
    }
345

346
    switch (this->lv_meta.lvm_kind) {
372,849✔
347
        case value_kind_t::VALUE_JSON:
183,484✔
348
        case value_kind_t::VALUE_XML:
349
        case value_kind_t::VALUE_STRUCT:
350
        case value_kind_t::VALUE_TEXT:
351
        case value_kind_t::VALUE_QUOTED:
352
        case value_kind_t::VALUE_W3C_QUOTED:
353
        case value_kind_t::VALUE_TIMESTAMP:
354
            require(origin.lr_end != -1);
183,484✔
355
            this->lv_frag = string_fragment::from_byte_range(
183,484✔
356
                sbr.get_data(), origin.lr_start, origin.lr_end);
183,484✔
357
            break;
183,484✔
358

359
        case value_kind_t::VALUE_NULL:
×
360
            break;
×
361

362
        case value_kind_t::VALUE_INTEGER: {
170,879✔
363
            auto scan_res
364
                = scn::scan_value<int64_t>(sbr.to_string_view(origin));
170,879✔
365
            if (scan_res) {
170,879✔
366
                this->lv_value.i = scan_res.value();
170,877✔
367
            } else {
368
                this->lv_value.i = 0;
2✔
369
            }
370
            break;
170,879✔
371
        }
372

373
        case value_kind_t::VALUE_FLOAT: {
18,486✔
374
            auto scan_res = scn::scan_value<double>(sbr.to_string_view(origin));
18,486✔
375
            if (scan_res) {
18,486✔
376
                this->lv_value.d = scan_res.value();
18,486✔
377
            } else {
378
                this->lv_value.d = 0;
×
379
            }
380
            break;
18,486✔
381
        }
382

383
        case value_kind_t::VALUE_BOOLEAN:
×
384
            if (strncmp(
×
385
                    sbr.get_data_at(origin.lr_start), "true", origin.length())
×
386
                    == 0
387
                || strncmp(
×
388
                       sbr.get_data_at(origin.lr_start), "yes", origin.length())
×
389
                    == 0)
390
            {
391
                this->lv_value.i = 1;
×
392
            } else {
393
                this->lv_value.i = 0;
×
394
            }
395
            break;
×
396

397
        case value_kind_t::VALUE_UNKNOWN:
×
398
        case value_kind_t::VALUE__MAX:
399
            ensure(0);
×
400
            break;
401
    }
402
}
372,849✔
403

404
std::string
405
logline_value::to_string() const
23,858✔
406
{
407
    char buffer[128];
408

409
    switch (this->lv_meta.lvm_kind) {
23,858✔
410
        case value_kind_t::VALUE_NULL:
2✔
411
            return "null";
2✔
412

413
        case value_kind_t::VALUE_JSON:
23,611✔
414
        case value_kind_t::VALUE_XML:
415
        case value_kind_t::VALUE_STRUCT:
416
        case value_kind_t::VALUE_TEXT:
417
        case value_kind_t::VALUE_TIMESTAMP:
418
            if (this->lv_str) {
23,611✔
419
                return this->lv_str.value();
1,369✔
420
            }
421
            if (this->lv_frag.empty()) {
22,242✔
422
                return this->lv_intern_string.to_string();
20✔
423
            }
424
            return this->lv_frag.to_string();
22,222✔
425

426
        case value_kind_t::VALUE_QUOTED:
7✔
427
        case value_kind_t::VALUE_W3C_QUOTED:
428
            if (this->lv_frag.empty()) {
7✔
429
                return "";
×
430
            } else {
431
                switch (this->lv_frag.data()[0]) {
7✔
432
                    case '\'':
7✔
433
                    case '"': {
434
                        auto unquote_func = this->lv_meta.lvm_kind
14✔
435
                                == value_kind_t::VALUE_W3C_QUOTED
436
                            ? unquote_w3c
7✔
437
                            : unquote;
438
                        char unquoted_str[this->lv_frag.length()];
7✔
439
                        size_t unquoted_len;
440

441
                        unquoted_len = unquote_func(unquoted_str,
7✔
442
                                                    this->lv_frag.data(),
443
                                                    this->lv_frag.length());
7✔
444
                        return {unquoted_str, unquoted_len};
7✔
445
                    }
7✔
446
                    default:
×
447
                        return this->lv_frag.to_string();
×
448
                }
449
            }
450
            break;
451

452
        case value_kind_t::VALUE_INTEGER:
238✔
453
            snprintf(buffer, sizeof(buffer), "%" PRId64, this->lv_value.i);
238✔
454
            break;
238✔
455

456
        case value_kind_t::VALUE_FLOAT:
×
457
            snprintf(buffer, sizeof(buffer), "%lf", this->lv_value.d);
×
458
            break;
×
459

460
        case value_kind_t::VALUE_BOOLEAN:
×
461
            if (this->lv_value.i) {
×
462
                return "true";
×
463
            } else {
464
                return "false";
×
465
            }
466
            break;
467
        case value_kind_t::VALUE_UNKNOWN:
×
468
        case value_kind_t::VALUE__MAX:
469
            ensure(0);
×
470
            break;
471
    }
472

473
    return {buffer};
238✔
474
}
475

476
std::vector<std::shared_ptr<log_format>> log_format::lf_root_formats;
477

478
std::vector<std::shared_ptr<log_format>>&
479
log_format::get_root_formats()
14,198✔
480
{
481
    return lf_root_formats;
14,198✔
482
}
483

484
void
485
external_log_format::update_op_description(
1,609✔
486
    const std::map<intern_string_t, opid_descriptors>& desc_defs,
487
    log_op_description& lod,
488
    const pattern* fpat,
489
    const lnav::pcre2pp::match_data& md)
490
{
491
    std::optional<std::string> desc_elem_str;
1,609✔
492
    if (!lod.lod_id) {
1,609✔
493
        for (const auto& desc_def_pair : desc_defs) {
1,770✔
494
            if (lod.lod_id) {
186✔
495
                break;
14✔
496
            }
497
            for (const auto& desc_def : *desc_def_pair.second.od_descriptors) {
413✔
498
                auto desc_field_index_iter = fpat->p_value_name_to_index.find(
241✔
499
                    desc_def.od_field.pp_value);
241✔
500

501
                if (desc_field_index_iter == fpat->p_value_name_to_index.end())
241✔
502
                {
503
                    continue;
27✔
504
                }
505
                auto desc_cap_opt = md[desc_field_index_iter->second];
237✔
506

507
                if (!desc_cap_opt) {
237✔
508
                    continue;
23✔
509
                }
510

511
                desc_elem_str = desc_def.matches(desc_cap_opt.value());
214✔
512
                if (desc_elem_str) {
214✔
513
                    lod.lod_id = desc_def_pair.first;
38✔
514
                }
515
            }
516
        }
517
    }
518
    if (lod.lod_id) {
1,609✔
519
        const auto& desc_def_v
520
            = *desc_defs.find(lod.lod_id.value())->second.od_descriptors;
49✔
521
        auto& desc_v = lod.lod_elements;
49✔
522

523
        if (desc_def_v.size() == desc_v.size()) {
49✔
524
            return;
11✔
525
        }
526
        for (size_t desc_def_index = 0; desc_def_index < desc_def_v.size();
79✔
527
             desc_def_index++)
528
        {
529
            const auto& desc_def = desc_def_v[desc_def_index];
41✔
530
            auto found_desc = desc_v.begin();
41✔
531

532
            for (; found_desc != desc_v.end(); ++found_desc) {
44✔
533
                if (found_desc->first == desc_def_index) {
3✔
534
                    break;
×
535
                }
536
            }
537
            auto desc_field_index_iter
538
                = fpat->p_value_name_to_index.find(desc_def.od_field.pp_value);
41✔
539

540
            if (desc_field_index_iter == fpat->p_value_name_to_index.end()) {
41✔
541
                continue;
×
542
            }
543
            auto desc_cap_opt = md[desc_field_index_iter->second];
41✔
544
            if (!desc_cap_opt) {
41✔
545
                continue;
×
546
            }
547

548
            if (!desc_elem_str) {
41✔
549
                desc_elem_str = desc_def.matches(desc_cap_opt.value());
4✔
550
            }
551
            if (desc_elem_str) {
41✔
552
                if (found_desc == desc_v.end()) {
38✔
553
                    desc_v.emplace_back(desc_def_index, desc_elem_str.value());
38✔
554
                } else if (!desc_elem_str->empty()) {
×
555
                    found_desc->second.append(desc_def.od_joiner);
×
556
                    found_desc->second.append(desc_elem_str.value());
×
557
                }
558
            }
559
            desc_elem_str = std::nullopt;
41✔
560
        }
561
    }
562
}
1,609✔
563

564
void
565
external_log_format::update_op_description(
60✔
566
    const std::map<intern_string_t, opid_descriptors>& desc_defs,
567
    log_op_description& lod)
568
{
569
    std::optional<std::string> desc_elem_str;
60✔
570
    if (!lod.lod_id) {
60✔
571
        for (const auto& desc_def_pair : desc_defs) {
60✔
572
            if (lod.lod_id) {
×
573
                break;
×
574
            }
575
            for (const auto& desc_def : *desc_def_pair.second.od_descriptors) {
×
576
                auto desc_cap_iter
577
                    = this->lf_desc_captures.find(desc_def.od_field.pp_value);
×
578

579
                if (desc_cap_iter == this->lf_desc_captures.end()) {
×
580
                    continue;
×
581
                }
582
                desc_elem_str = desc_def.matches(desc_cap_iter->second);
×
583
                if (desc_elem_str) {
×
584
                    lod.lod_id = desc_def_pair.first;
×
585
                }
586
            }
587
        }
588
    }
589
    if (lod.lod_id) {
60✔
590
        const auto& desc_def_v
591
            = *desc_defs.find(lod.lod_id.value())->second.od_descriptors;
×
592
        auto& desc_v = lod.lod_elements;
×
593

594
        if (desc_def_v.size() != desc_v.size()) {
×
595
            for (size_t desc_def_index = 0; desc_def_index < desc_def_v.size();
×
596
                 desc_def_index++)
597
            {
598
                const auto& desc_def = desc_def_v[desc_def_index];
×
599
                auto found_desc = desc_v.begin();
×
600

601
                for (; found_desc != desc_v.end(); ++found_desc) {
×
602
                    if (found_desc->first == desc_def_index) {
×
603
                        break;
×
604
                    }
605
                }
606
                auto desc_cap_iter
607
                    = this->lf_desc_captures.find(desc_def.od_field.pp_value);
×
608
                if (desc_cap_iter == this->lf_desc_captures.end()) {
×
609
                    continue;
×
610
                }
611

612
                if (!desc_elem_str) {
×
613
                    desc_elem_str = desc_def.matches(desc_cap_iter->second);
×
614
                }
615
                if (desc_elem_str) {
×
616
                    if (found_desc == desc_v.end()) {
×
617
                        desc_v.emplace_back(desc_def_index,
×
618
                                            desc_elem_str.value());
619
                    } else if (!desc_elem_str->empty()) {
×
620
                        found_desc->second.append(desc_def.od_joiner);
×
621
                        found_desc->second.append(desc_elem_str.value());
×
622
                    }
623
                }
624
                desc_elem_str = std::nullopt;
×
625
            }
626
        }
627
    }
628
}
60✔
629

630
static bool
631
next_format(
1,554,802✔
632
    const std::vector<std::shared_ptr<external_log_format::pattern>>& patterns,
633
    int& index,
634
    int& locked_index)
635
{
636
    bool retval = true;
1,554,802✔
637

638
    if (locked_index == -1) {
1,554,802✔
639
        index += 1;
1,553,001✔
640
        if (index >= (int) patterns.size()) {
1,553,001✔
641
            retval = false;
479,605✔
642
        }
643
    } else if (index == locked_index) {
1,801✔
644
        retval = false;
1✔
645
    } else {
646
        index = locked_index;
1,800✔
647
    }
648

649
    return retval;
1,554,802✔
650
}
651

652
bool
653
log_format::next_format(const pcre_format* fmt, int& index, int& locked_index)
131,642✔
654
{
655
    bool retval = true;
131,642✔
656

657
    if (locked_index == -1) {
131,642✔
658
        index += 1;
131,429✔
659
        if (fmt[index].name == nullptr) {
131,429✔
660
            retval = false;
9,030✔
661
        }
662
    } else if (index == locked_index) {
213✔
663
        retval = false;
56✔
664
    } else {
665
        index = locked_index;
157✔
666
    }
667

668
    return retval;
131,642✔
669
}
670

671
const char*
672
log_format::log_scanf(uint32_t line_number,
10,150✔
673
                      string_fragment line,
674
                      const pcre_format* fmt,
675
                      const char* time_fmt[],
676
                      struct exttm* tm_out,
677
                      struct timeval* tv_out,
678

679
                      string_fragment* ts_out,
680
                      std::optional<string_fragment>* level_out)
681
{
682
    int curr_fmt = -1;
10,150✔
683
    const char* retval = nullptr;
10,150✔
684
    bool done = false;
10,150✔
685
    int pat_index = this->last_pattern_index();
10,150✔
686

687
    while (!done && next_format(fmt, curr_fmt, pat_index)) {
132,706✔
688
        static thread_local auto md = lnav::pcre2pp::match_data::unitialized();
122,556✔
689

690
        auto match_res = fmt[curr_fmt]
122,556✔
691
                             .pcre->capture_from(line)
122,556✔
692
                             .into(md)
122,556✔
693
                             .matches(PCRE2_NO_UTF_CHECK)
245,112✔
694
                             .ignore_error();
122,556✔
695
        if (!match_res) {
122,556✔
696
            retval = nullptr;
112,269✔
697
        } else {
698
            auto ts = md[fmt[curr_fmt].pf_timestamp_index];
10,287✔
699

700
            retval = this->lf_date_time.scan(
10,287✔
701
                ts->data(), ts->length(), nullptr, tm_out, *tv_out);
10,287✔
702

703
            if (retval == nullptr) {
10,287✔
704
                auto ls = this->lf_date_time.unlock();
9,229✔
705
                retval = this->lf_date_time.scan(
9,229✔
706
                    ts->data(), ts->length(), nullptr, tm_out, *tv_out);
9,229✔
707
                if (retval != nullptr) {
9,229✔
708
                    auto old_flags
6✔
709
                        = this->lf_timestamp_flags & DATE_TIME_SET_FLAGS;
6✔
710
                    auto new_flags = tm_out->et_flags & DATE_TIME_SET_FLAGS;
6✔
711

712
                    // It is unlikely a valid timestamp would lose much
713
                    // precision.
714
                    if (new_flags != old_flags) {
6✔
715
                        retval = nullptr;
×
716
                    }
717
                }
718
                if (retval == nullptr) {
9,229✔
719
                    this->lf_date_time.relock(ls);
9,223✔
720
                } else {
721
                    log_debug(
6✔
722
                        "%d: changed time format to '%s' due to %.*s",
723
                        line_number,
724
                        PTIMEC_FORMAT_STR[this->lf_date_time.dts_fmt_lock],
725
                        ts->length(),
726
                        ts->data());
727
                }
728
            }
729

730
            if (retval) {
10,287✔
731
                *ts_out = ts.value();
1,064✔
732
                *level_out = md[2];
1,064✔
733
                if (curr_fmt != pat_index) {
1,064✔
734
                    uint32_t lock_line;
735

736
                    if (this->lf_pattern_locks.empty()) {
963✔
737
                        lock_line = 0;
963✔
738
                    } else {
739
                        lock_line = line_number;
×
740
                    }
741

742
                    this->lf_pattern_locks.emplace_back(lock_line, curr_fmt);
963✔
743
                }
744
                this->lf_timestamp_flags = tm_out->et_flags;
1,064✔
745
                done = true;
1,064✔
746
            }
747
        }
748
    }
749

750
    return retval;
10,150✔
751
}
752

753
void
754
log_format::annotate(logfile* lf,
20,671✔
755
                     uint64_t line_number,
756
                     string_attrs_t& sa,
757
                     logline_value_vector& values,
758
                     bool annotate_module) const
759
{
760
    if (lf != nullptr && !values.lvv_opid_value) {
20,671✔
761
        const auto& bm = lf->get_bookmark_metadata();
19,688✔
762
        auto bm_iter = bm.find(line_number);
19,688✔
763
        if (bm_iter != bm.end() && !bm_iter->second.bm_opid.empty()) {
19,688✔
764
            values.lvv_opid_value = bm_iter->second.bm_opid;
9✔
765
            values.lvv_opid_provenance
766
                = logline_value_vector::opid_provenance::user;
9✔
767
        }
768
    }
769
}
20,671✔
770

771
void
772
log_format::check_for_new_year(std::vector<logline>& dst,
1,327✔
773
                               exttm etm,
774
                               struct timeval log_tv)
775
{
776
    if (dst.empty()) {
1,327✔
777
        return;
181✔
778
    }
779

780
    time_t diff = dst.back().get_time() - log_tv.tv_sec;
1,146✔
781
    int off_year = 0, off_month = 0, off_day = 0, off_hour = 0;
1,146✔
782
    bool do_change = true;
1,146✔
783

784
    if (diff <= 0) {
1,146✔
785
        return;
1,087✔
786
    }
787
    if ((etm.et_flags & ETF_MONTH_SET) && diff >= (24 * 60 * 60)) {
59✔
788
        off_year = 1;
10✔
789
    } else if (diff >= (24 * 60 * 60)) {
49✔
790
        off_month = 1;
2✔
791
    } else if (!(etm.et_flags & ETF_DAY_SET) && (diff >= (60 * 60))) {
47✔
792
        off_day = 1;
1✔
793
    } else if (!(etm.et_flags & ETF_HOUR_SET) && (diff >= 60)) {
46✔
794
        off_hour = 1;
4✔
795
    } else {
796
        do_change = false;
42✔
797
    }
798

799
    if (!do_change) {
59✔
800
        return;
42✔
801
    }
802
    log_debug("%d:detected time rollover; offsets=%d %d %d %d",
17✔
803
              dst.size(),
804
              off_year,
805
              off_month,
806
              off_day,
807
              off_hour);
808
    for (auto& ll : dst) {
66✔
809
        time_t ot = ll.get_time();
49✔
810
        struct tm otm;
811

812
        gmtime_r(&ot, &otm);
49✔
813
        otm.tm_yday = -1;
49✔
814
        if (otm.tm_year < off_year) {
49✔
815
            otm.tm_year = 0;
×
816
        } else {
817
            otm.tm_year -= off_year;
49✔
818
        }
819
        otm.tm_mon -= off_month;
49✔
820
        if (otm.tm_mon < 0) {
49✔
821
            otm.tm_mon += 12;
2✔
822
        }
823
        auto new_time = tm2sec(&otm);
49✔
824
        if (new_time == -1) {
49✔
825
            continue;
×
826
        }
827
        new_time -= (off_day * 24 * 60 * 60) + (off_hour * 60 * 60);
49✔
828
        ll.set_time(new_time);
49✔
829
    }
830
}
831

832
/*
833
 * XXX This needs some cleanup.
834
 */
835
struct json_log_userdata {
836
    json_log_userdata(shared_buffer_ref& sbr, scan_batch_context* sbc)
4,139✔
837
        : jlu_shared_buffer(sbr), jlu_batch_context(sbc)
4,139✔
838
    {
839
    }
4,139✔
840

841
    void add_sub_lines_for(const intern_string_t ist,
44,667✔
842
                           bool top_level,
843
                           std::optional<double> val = std::nullopt,
844
                           const unsigned char* str = nullptr,
845
                           ssize_t len = -1)
846
    {
847
        auto res
848
            = this->jlu_format->value_line_count(ist, top_level, val, str, len);
44,667✔
849
        this->jlu_has_ansi |= res.vlcr_has_ansi;
44,667✔
850
        if (!res.vlcr_valid_utf) {
44,667✔
851
            this->jlu_valid_utf = false;
×
852
        }
853
        this->jlu_sub_line_count += res.vlcr_count;
44,667✔
854
        this->jlu_quality += res.vlcr_line_format_count;
44,667✔
855
    }
44,667✔
856

857
    external_log_format* jlu_format{nullptr};
858
    const logline* jlu_line{nullptr};
859
    logline* jlu_base_line{nullptr};
860
    int jlu_sub_line_count{1};
861
    bool jlu_has_ansi{false};
862
    bool jlu_valid_utf{true};
863
    yajl_handle jlu_handle{nullptr};
864
    const char* jlu_line_value{nullptr};
865
    size_t jlu_line_size{0};
866
    size_t jlu_sub_start{0};
867
    uint32_t jlu_quality{0};
868
    shared_buffer_ref& jlu_shared_buffer;
869
    scan_batch_context* jlu_batch_context;
870
    std::optional<string_fragment> jlu_opid_frag;
871
    std::optional<std::string> jlu_subid;
872
    struct exttm jlu_exttm;
873
};
874

875
static int read_json_field(yajlpp_parse_context* ypc,
876
                           const unsigned char* str,
877
                           size_t len);
878

879
static int
880
read_json_null(yajlpp_parse_context* ypc)
644✔
881
{
882
    json_log_userdata* jlu = (json_log_userdata*) ypc->ypc_userdata;
644✔
883
    const intern_string_t field_name = ypc->get_path();
644✔
884

885
    jlu->add_sub_lines_for(field_name, ypc->is_level(1));
644✔
886

887
    return 1;
644✔
888
}
889

890
static int
891
read_json_bool(yajlpp_parse_context* ypc, int val)
3,131✔
892
{
893
    json_log_userdata* jlu = (json_log_userdata*) ypc->ypc_userdata;
3,131✔
894
    const intern_string_t field_name = ypc->get_path();
3,131✔
895

896
    jlu->add_sub_lines_for(field_name, ypc->is_level(1));
3,131✔
897

898
    return 1;
3,131✔
899
}
900

901
static int
902
read_json_number(yajlpp_parse_context* ypc,
5,260✔
903
                 const char* numberVal,
904
                 size_t numberLen)
905
{
906
    json_log_userdata* jlu = (json_log_userdata*) ypc->ypc_userdata;
5,260✔
907
    const intern_string_t field_name = ypc->get_path();
5,260✔
908

909
    auto number_frag = string_fragment::from_bytes(numberVal, numberLen);
5,260✔
910
    auto scan_res = scn::scan_value<double>(number_frag.to_string_view());
5,260✔
911
    if (!scan_res) {
5,260✔
912
        log_error("invalid number %.*s", numberLen, numberVal);
×
913
        return 0;
×
914
    }
915

916
    auto val = scan_res.value();
5,260✔
917
    if (jlu->jlu_format->lf_timestamp_field == field_name) {
5,260✔
918
        long long divisor = jlu->jlu_format->elf_timestamp_divisor;
19✔
919
        struct timeval tv;
920

921
        tv.tv_sec = val / divisor;
19✔
922
        tv.tv_usec = fmod(val, divisor) * (1000000.0 / divisor);
19✔
923
        jlu->jlu_format->lf_date_time.to_localtime(tv.tv_sec, jlu->jlu_exttm);
19✔
924
        tv.tv_sec = tm2sec(&jlu->jlu_exttm.et_tm);
19✔
925
        jlu->jlu_exttm.et_gmtoff
926
            = jlu->jlu_format->lf_date_time.dts_local_offset_cache;
19✔
927
        jlu->jlu_exttm.et_flags
928
            |= ETF_MACHINE_ORIENTED | ETF_SUB_NOT_IN_FORMAT | ETF_ZONE_SET;
19✔
929
        if (divisor == 1000) {
19✔
930
            jlu->jlu_exttm.et_flags |= ETF_MILLIS_SET;
16✔
931
        } else {
932
            jlu->jlu_exttm.et_flags |= ETF_MICROS_SET;
3✔
933
        }
934
        jlu->jlu_exttm.et_nsec = tv.tv_usec * 1000;
19✔
935
        jlu->jlu_base_line->set_time(tv);
19✔
936
    } else if (jlu->jlu_format->lf_subsecond_field == field_name) {
5,241✔
937
        uint64_t millis = 0;
3✔
938
        jlu->jlu_exttm.et_flags &= ~(ETF_MICROS_SET | ETF_MILLIS_SET);
3✔
939
        switch (jlu->jlu_format->lf_subsecond_unit.value()) {
3✔
940
            case log_format::subsecond_unit::milli:
×
941
                millis = val;
×
942
                jlu->jlu_exttm.et_nsec = val * 1000000;
×
943
                jlu->jlu_exttm.et_flags |= ETF_MILLIS_SET;
×
944
                break;
×
945
            case log_format::subsecond_unit::micro:
×
946
                millis = std::chrono::duration_cast<std::chrono::milliseconds>(
×
947
                             std::chrono::microseconds((int64_t) val))
×
948
                             .count();
×
949
                jlu->jlu_exttm.et_nsec = val * 1000;
×
950
                jlu->jlu_exttm.et_flags |= ETF_MICROS_SET;
×
951
                break;
×
952
            case log_format::subsecond_unit::nano:
3✔
953
                millis = std::chrono::duration_cast<std::chrono::milliseconds>(
3✔
954
                             std::chrono::nanoseconds((int64_t) val))
3✔
955
                             .count();
3✔
956
                jlu->jlu_exttm.et_nsec = val;
3✔
957
                jlu->jlu_exttm.et_flags |= ETF_NANOS_SET;
3✔
958
                break;
3✔
959
        }
960
        jlu->jlu_exttm.et_flags |= ETF_SUB_NOT_IN_FORMAT;
3✔
961
        jlu->jlu_base_line->set_millis(millis);
3✔
962
    } else if (jlu->jlu_format->elf_level_field == field_name) {
5,238✔
963
        if (jlu->jlu_format->elf_level_pairs.empty()) {
168✔
964
            jlu->jlu_base_line->set_level(jlu->jlu_format->convert_level(
100✔
965
                number_frag, jlu->jlu_batch_context));
966
        } else {
967
            int64_t level_int = val;
68✔
968

969
            for (const auto& pair : jlu->jlu_format->elf_level_pairs) {
247✔
970
                if (pair.first == level_int) {
219✔
971
                    jlu->jlu_base_line->set_level(pair.second);
40✔
972
                    break;
40✔
973
                }
974
            }
975
        }
976
    }
977

978
    jlu->add_sub_lines_for(field_name,
10,520✔
979
                           ypc->is_level(1),
5,260✔
980
                           val,
981
                           (const unsigned char*) numberVal,
982
                           numberLen);
983

984
    return 1;
5,260✔
985
}
986

987
static int
988
json_array_start(void* ctx)
12,803✔
989
{
990
    yajlpp_parse_context* ypc = (yajlpp_parse_context*) ctx;
12,803✔
991
    json_log_userdata* jlu = (json_log_userdata*) ypc->ypc_userdata;
12,803✔
992

993
    if (ypc->ypc_path_index_stack.size() == 2) {
12,803✔
994
        const intern_string_t field_name = ypc->get_path_fragment_i(0);
3,550✔
995

996
        jlu->add_sub_lines_for(field_name, true);
3,550✔
997
        jlu->jlu_sub_start = yajl_get_bytes_consumed(jlu->jlu_handle) - 1;
3,550✔
998
    }
999

1000
    return 1;
12,803✔
1001
}
1002

1003
static int
1004
json_array_end(void* ctx)
4,725✔
1005
{
1006
    yajlpp_parse_context* ypc = (yajlpp_parse_context*) ctx;
4,725✔
1007
    json_log_userdata* jlu = (json_log_userdata*) ypc->ypc_userdata;
4,725✔
1008

1009
    if (ypc->ypc_path_index_stack.size() == 1) {
4,725✔
1010
        const intern_string_t field_name = ypc->get_path_fragment_i(0);
1,232✔
1011
        size_t sub_end = yajl_get_bytes_consumed(jlu->jlu_handle);
1,232✔
1012
        jlu->jlu_format->jlf_line_values.lvv_values.emplace_back(
1,232✔
1013
            jlu->jlu_format->get_value_meta(field_name,
2,464✔
1014
                                            value_kind_t::VALUE_JSON),
1015
            string_fragment::from_byte_range(jlu->jlu_shared_buffer.get_data(),
2,464✔
1016
                                             jlu->jlu_sub_start,
1017
                                             sub_end));
1018
    }
1019

1020
    return 1;
4,725✔
1021
}
1022

1023
static const struct json_path_container json_log_handlers = {
1024
    yajlpp::pattern_property_handler("\\w+")
1025
        .add_cb(read_json_null)
1026
        .add_cb(read_json_bool)
1027
        .add_cb(read_json_number)
1028
        .add_cb(read_json_field),
1029
};
1030

1031
static int rewrite_json_field(yajlpp_parse_context* ypc,
1032
                              const unsigned char* str,
1033
                              size_t len);
1034

1035
static int
1036
rewrite_json_null(yajlpp_parse_context* ypc)
593✔
1037
{
1038
    json_log_userdata* jlu = (json_log_userdata*) ypc->ypc_userdata;
593✔
1039
    const intern_string_t field_name = ypc->get_path();
593✔
1040

1041
    if (!ypc->is_level(1) && !jlu->jlu_format->has_value_def(field_name)) {
593✔
1042
        return 1;
593✔
1043
    }
1044
    jlu->jlu_format->jlf_line_values.lvv_values.emplace_back(
×
1045
        jlu->jlu_format->get_value_meta(field_name, value_kind_t::VALUE_NULL));
×
1046

1047
    return 1;
×
1048
}
1049

1050
static int
1051
rewrite_json_bool(yajlpp_parse_context* ypc, int val)
2,619✔
1052
{
1053
    json_log_userdata* jlu = (json_log_userdata*) ypc->ypc_userdata;
2,619✔
1054
    const intern_string_t field_name = ypc->get_path();
2,619✔
1055

1056
    if (!ypc->is_level(1) && !jlu->jlu_format->has_value_def(field_name)) {
2,619✔
1057
        return 1;
2,357✔
1058
    }
1059
    jlu->jlu_format->jlf_line_values.lvv_values.emplace_back(
262✔
1060
        jlu->jlu_format->get_value_meta(field_name,
262✔
1061
                                        value_kind_t::VALUE_BOOLEAN),
1062
        (bool) val);
262✔
1063
    return 1;
262✔
1064
}
1065

1066
static int
1067
rewrite_json_int(yajlpp_parse_context* ypc, long long val)
3,339✔
1068
{
1069
    json_log_userdata* jlu = (json_log_userdata*) ypc->ypc_userdata;
3,339✔
1070
    const intern_string_t field_name = ypc->get_path();
3,339✔
1071

1072
    if (jlu->jlu_format->lf_timestamp_field == field_name) {
3,339✔
1073
        long long divisor = jlu->jlu_format->elf_timestamp_divisor;
24✔
1074
        struct timeval tv;
1075

1076
        tv.tv_sec = val / divisor;
24✔
1077
        tv.tv_usec = fmod(val, divisor) * (1000000.0 / divisor);
24✔
1078
        jlu->jlu_format->lf_date_time.to_localtime(tv.tv_sec, jlu->jlu_exttm);
24✔
1079
        jlu->jlu_exttm.et_gmtoff
1080
            = jlu->jlu_format->lf_date_time.dts_local_offset_cache;
24✔
1081
        jlu->jlu_exttm.et_flags |= ETF_MACHINE_ORIENTED | ETF_SUB_NOT_IN_FORMAT
24✔
1082
            | ETF_ZONE_SET | ETF_Z_FOR_UTC;
1083
        if (divisor == 1) {
24✔
1084
            jlu->jlu_exttm.et_flags |= ETF_MICROS_SET;
4✔
1085
        } else {
1086
            jlu->jlu_exttm.et_flags |= ETF_MILLIS_SET;
20✔
1087
        }
1088
        jlu->jlu_exttm.et_nsec = tv.tv_usec * 1000;
24✔
1089
    } else if (jlu->jlu_format->lf_subsecond_field == field_name) {
3,315✔
1090
        jlu->jlu_exttm.et_flags &= ~(ETF_MICROS_SET | ETF_MILLIS_SET);
4✔
1091
        switch (jlu->jlu_format->lf_subsecond_unit.value()) {
4✔
1092
            case log_format::subsecond_unit::milli:
×
1093
                jlu->jlu_exttm.et_nsec = val * 1000000;
×
1094
                jlu->jlu_exttm.et_flags |= ETF_MILLIS_SET;
×
1095
                break;
×
1096
            case log_format::subsecond_unit::micro:
×
1097
                jlu->jlu_exttm.et_nsec = val * 1000;
×
1098
                jlu->jlu_exttm.et_flags |= ETF_MICROS_SET;
×
1099
                break;
×
1100
            case log_format::subsecond_unit::nano:
4✔
1101
                jlu->jlu_exttm.et_nsec = val;
4✔
1102
                jlu->jlu_exttm.et_flags |= ETF_NANOS_SET;
4✔
1103
                break;
4✔
1104
        }
1105
        jlu->jlu_exttm.et_flags |= ETF_SUB_NOT_IN_FORMAT;
4✔
1106
    }
1107

1108
    if (!ypc->is_level(1) && !jlu->jlu_format->has_value_def(field_name)) {
3,339✔
1109
        return 1;
2,909✔
1110
    }
1111
    jlu->jlu_format->jlf_line_values.lvv_values.emplace_back(
430✔
1112
        jlu->jlu_format->get_value_meta(field_name,
430✔
1113
                                        value_kind_t::VALUE_INTEGER),
1114
        (int64_t) val);
430✔
1115
    return 1;
430✔
1116
}
1117

1118
static int
1119
rewrite_json_double(yajlpp_parse_context* ypc, double val)
12✔
1120
{
1121
    json_log_userdata* jlu = (json_log_userdata*) ypc->ypc_userdata;
12✔
1122
    const intern_string_t field_name = ypc->get_path();
12✔
1123

1124
    if (jlu->jlu_format->lf_timestamp_field == field_name) {
12✔
1125
        long long divisor = jlu->jlu_format->elf_timestamp_divisor;
12✔
1126
        struct timeval tv;
1127

1128
        tv.tv_sec = val / divisor;
12✔
1129
        tv.tv_usec = fmod(val, divisor) * (1000000.0 / divisor);
12✔
1130
        jlu->jlu_format->lf_date_time.to_localtime(tv.tv_sec, jlu->jlu_exttm);
12✔
1131
        jlu->jlu_exttm.et_gmtoff
1132
            = jlu->jlu_format->lf_date_time.dts_local_offset_cache;
12✔
1133
        jlu->jlu_exttm.et_flags |= ETF_MACHINE_ORIENTED | ETF_SUB_NOT_IN_FORMAT
12✔
1134
            | ETF_ZONE_SET | ETF_Z_FOR_UTC;
1135
        if (divisor == 1) {
12✔
1136
            jlu->jlu_exttm.et_flags |= ETF_MICROS_SET;
×
1137
        } else {
1138
            jlu->jlu_exttm.et_flags |= ETF_MILLIS_SET;
12✔
1139
        }
1140
        jlu->jlu_exttm.et_nsec = tv.tv_usec * 1000;
12✔
1141
    } else if (jlu->jlu_format->lf_subsecond_field == field_name) {
×
1142
        jlu->jlu_exttm.et_flags &= ~(ETF_MICROS_SET | ETF_MILLIS_SET);
×
1143
        switch (jlu->jlu_format->lf_subsecond_unit.value()) {
×
1144
            case log_format::subsecond_unit::milli:
×
1145
                jlu->jlu_exttm.et_nsec = val * 1000000;
×
1146
                jlu->jlu_exttm.et_flags |= ETF_MILLIS_SET;
×
1147
                break;
×
1148
            case log_format::subsecond_unit::micro:
×
1149
                jlu->jlu_exttm.et_nsec = val * 1000;
×
1150
                jlu->jlu_exttm.et_flags |= ETF_MICROS_SET;
×
1151
                break;
×
1152
            case log_format::subsecond_unit::nano:
×
1153
                jlu->jlu_exttm.et_nsec = val;
×
1154
                jlu->jlu_exttm.et_flags |= ETF_NANOS_SET;
×
1155
                break;
×
1156
        }
1157
        jlu->jlu_exttm.et_flags |= ETF_SUB_NOT_IN_FORMAT;
×
1158
    }
1159

1160
    if (!ypc->is_level(1) && !jlu->jlu_format->has_value_def(field_name)) {
12✔
1161
        return 1;
×
1162
    }
1163
    jlu->jlu_format->jlf_line_values.lvv_values.emplace_back(
12✔
1164
        jlu->jlu_format->get_value_meta(field_name, value_kind_t::VALUE_FLOAT),
24✔
1165
        val);
1166

1167
    return 1;
12✔
1168
}
1169

1170
static const struct json_path_container json_log_rewrite_handlers = {
1171
    yajlpp::pattern_property_handler("\\w+")
1172
        .add_cb(rewrite_json_null)
1173
        .add_cb(rewrite_json_bool)
1174
        .add_cb(rewrite_json_int)
1175
        .add_cb(rewrite_json_double)
1176
        .add_cb(rewrite_json_field),
1177
};
1178

1179
bool
1180
external_log_format::scan_for_partial(shared_buffer_ref& sbr,
1✔
1181
                                      size_t& len_out) const
1182
{
1183
    if (this->elf_type != elf_type_t::ELF_TYPE_TEXT) {
1✔
1184
        return false;
×
1185
    }
1186

1187
    const auto& pat = this->elf_pattern_order[this->last_pattern_index()];
1✔
1188
    if (!this->lf_multiline) {
1✔
1189
        len_out = pat->p_pcre.pp_value->match_partial(sbr.to_string_fragment());
1✔
1190
        return true;
1✔
1191
    }
1192

1193
    if (pat->p_timestamp_end == -1 || pat->p_timestamp_end > (int) sbr.length())
×
1194
    {
1195
        len_out = 0;
×
1196
        return false;
×
1197
    }
1198

1199
    len_out = pat->p_pcre.pp_value->match_partial(sbr.to_string_fragment());
×
1200
    return (int) len_out > pat->p_timestamp_end;
×
1201
}
1202

1203
std::vector<lnav::console::snippet>
1204
external_log_format::get_snippets() const
4✔
1205
{
1206
    std::vector<lnav::console::snippet> retval;
4✔
1207

1208
    for (const auto& src_pair : this->elf_format_sources) {
8✔
1209
        retval.emplace_back(lnav::console::snippet::from(src_pair.first, "")
8✔
1210
                                .with_line(src_pair.second));
4✔
1211
    }
1212

1213
    return retval;
4✔
1214
}
×
1215

1216
log_format::scan_result_t
1217
external_log_format::scan(logfile& lf,
545,548✔
1218
                          std::vector<logline>& dst,
1219
                          const line_info& li,
1220
                          shared_buffer_ref& sbr,
1221
                          scan_batch_context& sbc)
1222
{
1223
    if (dst.empty()) {
545,548✔
1224
        auto file_options = lf.get_file_options();
22,699✔
1225

1226
        if (file_options) {
22,699✔
1227
            this->lf_date_time.dts_default_zone
1228
                = file_options->second.fo_default_zone.pp_value;
834✔
1229
        } else {
1230
            this->lf_date_time.dts_default_zone = nullptr;
21,865✔
1231
        }
1232
    }
22,699✔
1233

1234
    if (this->elf_type == elf_type_t::ELF_TYPE_JSON) {
545,548✔
1235
        logline ll(li.li_file_range.fr_offset, 0, 0, LEVEL_INFO);
71,533✔
1236
        auto line_frag = sbr.to_string_fragment();
71,533✔
1237

1238
        if (!line_frag.startswith("{")) {
71,533✔
1239
            if (!this->lf_specialized) {
68,878✔
1240
                return log_format::scan_no_match{"line is not a JSON object"};
68,873✔
1241
            }
1242

1243
            ll.set_time(dst.back().get_timeval());
5✔
1244
            ll.set_level(LEVEL_INVALID);
5✔
1245
            dst.emplace_back(ll);
5✔
1246
            return scan_match{0};
5✔
1247
        }
1248

1249
        auto& ypc = *(this->jlf_parse_context);
2,655✔
1250
        yajl_handle handle = this->jlf_yajl_handle.get();
2,655✔
1251
        json_log_userdata jlu(sbr, &sbc);
2,655✔
1252

1253
        if (li.li_partial) {
2,655✔
1254
            log_debug("skipping partial line at offset %d",
×
1255
                      li.li_file_range.fr_offset);
1256
            if (this->lf_specialized) {
×
1257
                ll.set_level(LEVEL_INVALID);
×
1258
                dst.emplace_back(ll);
×
1259
            }
NEW
1260
            return scan_incomplete{};
×
1261
        }
1262

1263
        const auto* line_data = (const unsigned char*) sbr.get_data();
2,655✔
1264

1265
        this->lf_desc_captures.clear();
2,655✔
1266
        this->lf_desc_allocator.reset();
2,655✔
1267

1268
        yajl_reset(handle);
2,655✔
1269
        ypc.set_static_handler(json_log_handlers.jpc_children[0]);
2,655✔
1270
        ypc.ypc_userdata = &jlu;
2,655✔
1271
        ypc.ypc_ignore_unused = true;
2,655✔
1272
        ypc.ypc_alt_callbacks.yajl_start_array = json_array_start;
2,655✔
1273
        ypc.ypc_alt_callbacks.yajl_start_map = json_array_start;
2,655✔
1274
        ypc.ypc_alt_callbacks.yajl_end_array = nullptr;
2,655✔
1275
        ypc.ypc_alt_callbacks.yajl_end_map = nullptr;
2,655✔
1276
        jlu.jlu_format = this;
2,655✔
1277
        jlu.jlu_base_line = &ll;
2,655✔
1278
        jlu.jlu_line_value = sbr.get_data();
2,655✔
1279
        jlu.jlu_line_size = sbr.length();
2,655✔
1280
        jlu.jlu_handle = handle;
2,655✔
1281
        if (yajl_parse(handle, line_data, sbr.length()) == yajl_status_ok
2,655✔
1282
            && yajl_complete_parse(handle) == yajl_status_ok)
2,655✔
1283
        {
1284
            if (ll.get_time() == 0) {
2,547✔
1285
                if (this->lf_specialized) {
1,972✔
1286
                    ll.set_ignore(true);
×
1287
                    dst.emplace_back(ll);
×
1288
                    return log_format::scan_match{0};
×
1289
                }
1290

1291
                return log_format::scan_no_match{
1,972✔
1292
                    "JSON message does not have expected timestamp property"};
1,972✔
1293
            }
1294

1295
            if (jlu.jlu_opid_frag) {
575✔
1296
                this->jlf_line_values.lvv_opid_value
1297
                    = jlu.jlu_opid_frag->to_string();
60✔
1298
                this->jlf_line_values.lvv_opid_provenance
1299
                    = logline_value_vector::opid_provenance::file;
60✔
1300
                auto opid_iter
1301
                    = sbc.sbc_opids.insert_op(sbc.sbc_allocator,
120✔
1302
                                              jlu.jlu_opid_frag.value(),
60✔
1303
                                              ll.get_timeval());
60✔
1304
                opid_iter->second.otr_level_stats.update_msg_count(
60✔
1305
                    ll.get_msg_level());
1306

1307
                if (jlu.jlu_subid) {
60✔
1308
                    auto subid_frag
1309
                        = string_fragment::from_str(jlu.jlu_subid.value());
×
1310

1311
                    auto* ostr
1312
                        = sbc.sbc_opids.sub_op_in_use(sbc.sbc_allocator,
×
1313
                                                      opid_iter,
1314
                                                      subid_frag,
1315
                                                      ll.get_timeval(),
×
1316
                                                      ll.get_msg_level());
1317
                    if (ostr != nullptr && ostr->ostr_description.empty()) {
×
1318
                        log_op_description sub_desc;
×
1319
                        this->update_op_description(
×
1320
                            *this->lf_subid_description_def, sub_desc);
×
1321
                        if (!sub_desc.lod_elements.empty()) {
×
1322
                            auto& sub_desc_def
1323
                                = this->lf_subid_description_def->at(
×
1324
                                    sub_desc.lod_id.value());
×
1325
                            ostr->ostr_description
1326
                                = sub_desc_def.to_string(sub_desc.lod_elements);
×
1327
                        }
1328
                    }
1329
                }
1330

1331
                auto& otr = opid_iter->second;
60✔
1332
                this->update_op_description(*this->lf_opid_description_def,
60✔
1333
                                            otr.otr_description);
60✔
1334
            } else {
1335
                this->jlf_line_values.lvv_opid_value = std::nullopt;
515✔
1336
            }
1337

1338
            jlu.jlu_sub_line_count += this->jlf_line_format_init_count;
575✔
1339
            for (int lpc = 0; lpc < jlu.jlu_sub_line_count; lpc++) {
1,954✔
1340
                ll.set_sub_offset(lpc);
1,379✔
1341
                if (lpc > 0) {
1,379✔
1342
                    ll.set_level((log_level_t) (ll.get_level_and_flags()
804✔
1343
                                                | LEVEL_CONTINUED));
804✔
1344
                }
1345
                ll.set_has_ansi(jlu.jlu_has_ansi);
1,379✔
1346
                ll.set_valid_utf(jlu.jlu_valid_utf);
1,379✔
1347
                dst.emplace_back(ll);
1,379✔
1348
            }
1349
        } else {
1350
            unsigned char* msg;
1351
            int line_count = 2;
108✔
1352

1353
            msg = yajl_get_error(
216✔
1354
                handle, 1, (const unsigned char*) sbr.get_data(), sbr.length());
108✔
1355
            if (msg != nullptr) {
108✔
1356
                auto msg_frag = string_fragment::from_c_str(msg);
108✔
1357
                log_debug("Unable to parse line at offset %d: %s",
108✔
1358
                          li.li_file_range.fr_offset,
1359
                          msg);
1360
                line_count = msg_frag.count('\n') + 1;
108✔
1361
                yajl_free_error(handle, msg);
108✔
1362
            }
1363
            if (!this->lf_specialized) {
108✔
1364
                return log_format::scan_no_match{"JSON parsing failed"};
105✔
1365
            }
1366
            for (int lpc = 0; lpc < line_count; lpc++) {
15✔
1367
                log_level_t level = LEVEL_INVALID;
12✔
1368

1369
                ll.set_time(dst.back().get_timeval());
12✔
1370
                if (lpc > 0) {
12✔
1371
                    level = (log_level_t) (level | LEVEL_CONTINUED);
9✔
1372
                }
1373
                ll.set_level(level);
12✔
1374
                ll.set_sub_offset(lpc);
12✔
1375
                dst.emplace_back(ll);
12✔
1376
            }
1377
        }
1378

1379
        return log_format::scan_match{jlu.jlu_quality};
578✔
1380
    }
2,655✔
1381

1382
    int curr_fmt = -1, orig_lock = this->last_pattern_index();
474,015✔
1383
    int pat_index = orig_lock;
474,015✔
1384
    auto line_sf = sbr.to_string_fragment();
474,015✔
1385

1386
    while (::next_format(this->elf_pattern_order, curr_fmt, pat_index)) {
1,533,849✔
1387
        static thread_local auto md = lnav::pcre2pp::match_data::unitialized();
1,061,663✔
1388

1389
        auto* fpat = this->elf_pattern_order[curr_fmt].get();
1,061,663✔
1390
        auto* pat = fpat->p_pcre.pp_value.get();
1,061,663✔
1391

1392
        if (fpat->p_module_format) {
1,061,663✔
1393
            continue;
1,059,834✔
1394
        }
1395

1396
        auto match_res = pat->capture_from(line_sf)
1,031,741✔
1397
                             .into(md)
1,031,741✔
1398
                             .matches(PCRE2_NO_UTF_CHECK)
2,063,482✔
1399
                             .ignore_error();
1,031,741✔
1400
        if (!match_res) {
1,031,741✔
1401
            if (!this->lf_pattern_locks.empty() && pat_index != -1) {
1,029,911✔
1402
                curr_fmt = -1;
405✔
1403
                pat_index = -1;
405✔
1404
            }
1405
            continue;
1,029,911✔
1406
        }
1407

1408
        auto ts = md[fpat->p_timestamp_field_index];
1,830✔
1409
        auto time_cap = md[fpat->p_time_field_index];
1,830✔
1410
        auto level_cap = md[fpat->p_level_field_index];
1,830✔
1411
        auto mod_cap = md[fpat->p_module_field_index];
1,830✔
1412
        auto opid_cap = md[fpat->p_opid_field_index];
1,830✔
1413
        auto subid_cap = md[fpat->p_subid_field_index];
1,830✔
1414
        auto body_cap = md[fpat->p_body_field_index];
1,830✔
1415
        const char* last;
1416
        struct exttm log_time_tm;
1,830✔
1417
        struct timeval log_tv;
1418
        uint8_t mod_index = 0, opid = 0;
1,830✔
1419
        char combined_datetime_buf[512];
1420

1421
        if (ts && time_cap) {
1,830✔
1422
            auto ts_str_len = snprintf(combined_datetime_buf,
×
1423
                                       sizeof(combined_datetime_buf),
1424
                                       "%.*sT%.*s",
1425
                                       ts->length(),
1426
                                       ts->data(),
1427
                                       time_cap->length(),
1428
                                       time_cap->data());
1429
            ts = string_fragment::from_bytes(combined_datetime_buf, ts_str_len);
×
1430
        }
1431

1432
        auto level = this->convert_level(
1,830✔
1433
            level_cap.value_or(string_fragment::invalid()), &sbc);
1,830✔
1434

1435
        if (!ts) {
1,830✔
1436
            level = log_level_t::LEVEL_INVALID;
×
1437
        } else if ((last
1,830✔
1438
                    = this->lf_date_time.scan(ts->data(),
3,660✔
1439
                                              ts->length(),
1,830✔
1440
                                              this->get_timestamp_formats(),
1441
                                              &log_time_tm,
1442
                                              log_tv))
1443
                   == nullptr)
1,830✔
1444
        {
1445
            auto ls = this->lf_date_time.unlock();
11✔
1446
            if ((last = this->lf_date_time.scan(ts->data(),
22✔
1447
                                                ts->length(),
11✔
1448
                                                this->get_timestamp_formats(),
1449
                                                &log_time_tm,
1450
                                                log_tv))
1451
                == nullptr)
11✔
1452
            {
1453
                this->lf_date_time.relock(ls);
×
1454
                continue;
1✔
1455
            }
1456
            if (last != nullptr) {
11✔
1457
                auto old_flags = this->lf_timestamp_flags & DATE_TIME_SET_FLAGS;
11✔
1458
                auto new_flags = log_time_tm.et_flags & DATE_TIME_SET_FLAGS;
11✔
1459

1460
                // It is unlikely a valid timestamp would lose much
1461
                // precision.
1462
                if (new_flags != old_flags) {
11✔
1463
                    continue;
1✔
1464
                }
1465
            }
1466

1467
            log_debug("%s:%d: date-time re-locked to %d",
10✔
1468
                      lf.get_unique_path().c_str(),
1469
                      dst.size(),
1470
                      this->lf_date_time.dts_fmt_lock);
1471
        }
1472

1473
        this->lf_timestamp_flags = log_time_tm.et_flags;
1,829✔
1474

1475
        if (!(this->lf_timestamp_flags
3,658✔
1476
              & (ETF_MILLIS_SET | ETF_MICROS_SET | ETF_NANOS_SET))
1,829✔
1477
            && !dst.empty() && dst.back().get_time() == log_tv.tv_sec
1,630✔
1478
            && dst.back().get_millis() != 0)
3,459✔
1479
        {
1480
            auto log_ms = std::chrono::milliseconds(dst.back().get_millis());
×
1481

1482
            log_time_tm.et_nsec
1483
                = std::chrono::duration_cast<std::chrono::nanoseconds>(log_ms)
×
1484
                      .count();
×
1485
            log_tv.tv_usec
1486
                = std::chrono::duration_cast<std::chrono::microseconds>(log_ms)
×
1487
                      .count();
×
1488
        }
1489

1490
        if (!((log_time_tm.et_flags & ETF_DAY_SET)
1,829✔
1491
              && (log_time_tm.et_flags & ETF_MONTH_SET)
1,788✔
1492
              && (log_time_tm.et_flags & ETF_YEAR_SET)))
1,788✔
1493
        {
1494
            this->check_for_new_year(dst, log_time_tm, log_tv);
689✔
1495
        }
1496

1497
        if (opid_cap && !opid_cap->empty()) {
1,829✔
1498
            auto opid_iter = sbc.sbc_opids.insert_op(
3,168✔
1499
                sbc.sbc_allocator, opid_cap.value(), log_tv);
1,584✔
1500
            auto& otr = opid_iter->second;
1,584✔
1501

1502
            otr.otr_level_stats.update_msg_count(level);
1,584✔
1503

1504
            if (subid_cap && !subid_cap->empty()) {
1,584✔
1505
                auto* ostr = sbc.sbc_opids.sub_op_in_use(sbc.sbc_allocator,
108✔
1506
                                                         opid_iter,
1507
                                                         subid_cap.value(),
36✔
1508
                                                         log_tv,
1509
                                                         level);
1510
                if (ostr != nullptr && ostr->ostr_description.empty()) {
36✔
1511
                    log_op_description sub_desc;
25✔
1512
                    this->update_op_description(
25✔
1513
                        *this->lf_subid_description_def, sub_desc, fpat, md);
25✔
1514
                    if (!sub_desc.lod_elements.empty()) {
25✔
1515
                        auto& sub_desc_def = this->lf_subid_description_def->at(
23✔
1516
                            sub_desc.lod_id.value());
23✔
1517
                        ostr->ostr_description
1518
                            = sub_desc_def.to_string(sub_desc.lod_elements);
23✔
1519
                    }
1520
                }
25✔
1521
            }
1522
            this->update_op_description(
1,584✔
1523
                *this->lf_opid_description_def, otr.otr_description, fpat, md);
1,584✔
1524
            opid = opid_cap->hash();
1,584✔
1525
        }
1526

1527
        if (mod_cap && body_cap) {
1,829✔
1528
            intern_string_t mod_name = intern_string::lookup(mod_cap.value());
587✔
1529
            auto mod_iter = MODULE_FORMATS.find(mod_name);
587✔
1530

1531
            if (mod_iter == MODULE_FORMATS.end()) {
587✔
1532
                mod_index = this->module_scan(body_cap.value(), mod_name);
151✔
1533
                mod_iter = MODULE_FORMATS.find(mod_name);
151✔
1534
            } else if (mod_iter->second.mf_mod_format) {
436✔
1535
                mod_index = mod_iter->second.mf_mod_format->lf_mod_index;
4✔
1536
            }
1537

1538
            if (mod_index && level_cap && body_cap) {
587✔
1539
                auto mod_elf = std::dynamic_pointer_cast<external_log_format>(
1540
                    mod_iter->second.mf_mod_format);
42✔
1541

1542
                if (mod_elf) {
42✔
1543
                    thread_local auto mod_md
1544
                        = lnav::pcre2pp::match_data::unitialized();
42✔
1545

1546
                    shared_buffer_ref body_ref;
42✔
1547

1548
                    body_cap->trim();
42✔
1549

1550
                    int mod_pat_index = mod_elf->last_pattern_index();
42✔
1551
                    auto& mod_pat = *mod_elf->elf_pattern_order[mod_pat_index];
42✔
1552
                    auto match_res = mod_pat.p_pcre.pp_value
42✔
1553
                                         ->capture_from(body_cap.value())
42✔
1554
                                         .into(mod_md)
42✔
1555
                                         .matches(PCRE2_NO_UTF_CHECK)
84✔
1556
                                         .ignore_error();
42✔
1557
                    if (match_res) {
42✔
1558
                        auto mod_level_cap
1559
                            = mod_md[mod_pat.p_level_field_index];
42✔
1560

1561
                        level = mod_elf->convert_level(
84✔
1562
                            mod_level_cap.value_or(string_fragment::invalid()),
84✔
1563
                            &sbc);
1564
                    }
1565
                }
42✔
1566
            }
42✔
1567
        }
1568

1569
        for (const auto& ivd : fpat->p_value_by_index) {
19,875✔
1570
            if (!ivd.ivd_value_def->vd_meta.lvm_values_index) {
18,046✔
1571
                continue;
3,672✔
1572
            }
1573

1574
            auto cap = md[ivd.ivd_index];
14,374✔
1575

1576
            if (cap && cap->is_valid()) {
14,374✔
1577
                auto& lvs = this->lf_value_stats[ivd.ivd_value_def->vd_meta
12,859✔
1578
                                                     .lvm_values_index.value()];
12,859✔
1579

1580
                if (cap->length() > lvs.lvs_width) {
12,859✔
1581
                    lvs.lvs_width = cap->length();
3,612✔
1582
                }
1583
            }
1584
        }
1585

1586
        for (auto value_index : fpat->p_numeric_value_indexes) {
3,428✔
1587
            const indexed_value_def& ivd = fpat->p_value_by_index[value_index];
1,599✔
1588
            const value_def& vd = *ivd.ivd_value_def;
1,599✔
1589
            auto num_cap = md[ivd.ivd_index];
1,599✔
1590

1591
            if (num_cap && num_cap->is_valid()) {
1,599✔
1592
                const struct scaling_factor* scaling = nullptr;
1,599✔
1593

1594
                if (ivd.ivd_unit_field_index >= 0) {
1,599✔
1595
                    auto unit_cap = md[ivd.ivd_unit_field_index];
80✔
1596

1597
                    if (unit_cap && unit_cap->is_valid()) {
80✔
1598
                        intern_string_t unit_val
1599
                            = intern_string::lookup(unit_cap.value());
80✔
1600

1601
                        auto unit_iter = vd.vd_unit_scaling.find(unit_val);
80✔
1602
                        if (unit_iter != vd.vd_unit_scaling.end()) {
80✔
1603
                            const auto& sf = unit_iter->second;
80✔
1604

1605
                            scaling = &sf;
80✔
1606
                        }
1607
                    }
1608
                }
1609

1610
                std::optional<double> dvalue_opt;
1,599✔
1611
                switch (vd.vd_meta.lvm_kind) {
1,599✔
1612
                    case value_kind_t::VALUE_INTEGER: {
1,489✔
1613
                        auto scan_res = scn::scan_value<int64_t>(
1614
                            num_cap->to_string_view());
1,489✔
1615
                        if (scan_res) {
1,489✔
1616
                            dvalue_opt = scan_res.value();
1,489✔
1617
                        }
1618
                        break;
1,489✔
1619
                    }
1620
                    case value_kind_t::VALUE_FLOAT: {
110✔
1621
                        auto scan_res = scn::scan_value<double>(
1622
                            num_cap->to_string_view());
110✔
1623
                        if (scan_res) {
110✔
1624
                            dvalue_opt = scan_res.value();
110✔
1625
                        }
1626
                        break;
110✔
1627
                    }
1628
                    default:
×
1629
                        break;
×
1630
                }
1631
                if (dvalue_opt) {
1,599✔
1632
                    auto dvalue = dvalue_opt.value();
1,599✔
1633
                    if (scaling != nullptr) {
1,599✔
1634
                        scaling->scale(dvalue);
80✔
1635
                    }
1636
                    this->lf_value_stats[vd.vd_meta.lvm_values_index.value()]
1,599✔
1637
                        .add_value(dvalue);
1,599✔
1638
                }
1639
            }
1640
        }
1641

1642
        dst.emplace_back(
1,829✔
1643
            li.li_file_range.fr_offset, log_tv, level, mod_index, opid);
1,829✔
1644

1645
        if (orig_lock != curr_fmt) {
1,829✔
1646
            uint32_t lock_line;
1647

1648
            log_debug("%s:%zu: changing pattern lock %d -> (%d)%s",
435✔
1649
                      lf.get_unique_path().c_str(),
1650
                      dst.size() - 1,
1651
                      orig_lock,
1652
                      curr_fmt,
1653
                      this->elf_pattern_order[curr_fmt]->p_name.c_str());
1654
            if (this->lf_pattern_locks.empty()) {
435✔
1655
                lock_line = 0;
422✔
1656
            } else {
1657
                lock_line = dst.size() - 1;
13✔
1658
            }
1659
            this->lf_pattern_locks.emplace_back(lock_line, curr_fmt);
435✔
1660
        }
1661
        return log_format::scan_match{1000};
1,829✔
1662
    }
1663

1664
    if (this->lf_specialized && !this->lf_multiline) {
472,186✔
1665
        auto& last_line = dst.back();
1✔
1666

1667
        log_debug("invalid line %d %d", dst.size(), li.li_file_range.fr_offset);
1✔
1668
        dst.emplace_back(li.li_file_range.fr_offset,
1✔
1669
                         last_line.get_timeval(),
×
1670
                         log_level_t::LEVEL_INVALID);
1✔
1671

1672
        return scan_match{0};
1✔
1673
    }
1674

1675
    return scan_no_match{"no patterns matched"};
472,185✔
1676
}
1677

1678
uint8_t
1679
external_log_format::module_scan(string_fragment body_cap,
151✔
1680
                                 const intern_string_t& mod_name)
1681
{
1682
    uint8_t mod_index;
1683
    body_cap = body_cap.trim();
151✔
1684
    auto& ext_fmts = GRAPH_ORDERED_FORMATS;
151✔
1685
    module_format mf;
151✔
1686

1687
    for (auto& elf : ext_fmts) {
7,571✔
1688
        int curr_fmt = -1, fmt_lock = -1;
7,458✔
1689

1690
        while (::next_format(elf->elf_pattern_order, curr_fmt, fmt_lock)) {
20,953✔
1691
            static thread_local auto md
1692
                = lnav::pcre2pp::match_data::unitialized();
13,533✔
1693

1694
            auto& fpat = elf->elf_pattern_order[curr_fmt];
13,533✔
1695
            auto& pat = fpat->p_pcre;
13,533✔
1696

1697
            if (!fpat->p_module_format) {
13,533✔
1698
                continue;
13,495✔
1699
            }
1700

1701
            auto match_res = pat.pp_value->capture_from(body_cap)
414✔
1702
                                 .into(md)
414✔
1703
                                 .matches(PCRE2_NO_UTF_CHECK)
828✔
1704
                                 .ignore_error();
414✔
1705
            if (!match_res) {
414✔
1706
                continue;
376✔
1707
            }
1708

1709
            log_debug("%s:module format found -- %s (%d)",
38✔
1710
                      mod_name.get(),
1711
                      elf->get_name().get(),
1712
                      elf->lf_mod_index);
1713

1714
            mod_index = elf->lf_mod_index;
38✔
1715
            mf.mf_mod_format = elf->specialized(curr_fmt);
38✔
1716
            MODULE_FORMATS[mod_name] = mf;
38✔
1717

1718
            return mod_index;
38✔
1719
        }
1720
    }
1721

1722
    MODULE_FORMATS[mod_name] = mf;
113✔
1723

1724
    return 0;
113✔
1725
}
151✔
1726

1727
void
1728
external_log_format::annotate(logfile* lf,
1,651✔
1729
                              uint64_t line_number,
1730
                              string_attrs_t& sa,
1731
                              logline_value_vector& values,
1732
                              bool annotate_module) const
1733
{
1734
    static thread_local auto md = lnav::pcre2pp::match_data::unitialized();
1,651✔
1735

1736
    auto& line = values.lvv_sbr;
1,651✔
1737
    struct line_range lr;
1,651✔
1738

1739
    line.erase_ansi();
1,651✔
1740
    if (this->elf_type != elf_type_t::ELF_TYPE_TEXT) {
1,651✔
1741
        if (this->jlf_cached_full) {
444✔
1742
            values = this->jlf_line_values;
116✔
1743
            sa = this->jlf_line_attrs;
116✔
1744
        } else {
1745
            values.lvv_sbr = this->jlf_line_values.lvv_sbr.clone();
328✔
1746
            for (const auto& llv : this->jlf_line_values.lvv_values) {
3,179✔
1747
                if (this->jlf_cached_sub_range.contains(llv.lv_origin)) {
2,851✔
1748
                    values.lvv_values.emplace_back(llv);
680✔
1749
                    values.lvv_values.back().lv_origin.shift(
680✔
1750
                        this->jlf_cached_sub_range.lr_start,
680✔
1751
                        -this->jlf_cached_sub_range.lr_start);
680✔
1752
                }
1753
            }
1754
            for (const auto& attr : this->jlf_line_attrs) {
972✔
1755
                if (this->jlf_cached_sub_range.contains(attr.sa_range)) {
644✔
1756
                    sa.emplace_back(attr);
311✔
1757
                    sa.back().sa_range.shift(
311✔
1758
                        this->jlf_cached_sub_range.lr_start,
311✔
1759
                        -this->jlf_cached_sub_range.lr_start);
311✔
1760
                }
1761
            }
1762
            values.lvv_opid_value = this->jlf_line_values.lvv_opid_value;
328✔
1763
            values.lvv_opid_provenance
1764
                = this->jlf_line_values.lvv_opid_provenance;
328✔
1765
        }
1766
        log_format::annotate(lf, line_number, sa, values, annotate_module);
444✔
1767
        return;
559✔
1768
    }
1769

1770
    if (line.empty()) {
1,207✔
1771
        return;
7✔
1772
    }
1773

1774
    values.lvv_values.reserve(this->elf_value_defs.size());
1,200✔
1775

1776
    int pat_index = this->pattern_index_for_line(line_number);
1,200✔
1777
    auto& pat = *this->elf_pattern_order[pat_index];
1,200✔
1778

1779
    sa.reserve(pat.p_pcre.pp_value->get_capture_count());
1,200✔
1780
    auto match_res
1781
        = pat.p_pcre.pp_value->capture_from(line.to_string_fragment())
1,200✔
1782
              .into(md)
1,200✔
1783
              .matches(PCRE2_NO_UTF_CHECK)
2,400✔
1784
              .ignore_error();
1,200✔
1785
    if (!match_res) {
1,200✔
1786
        // A continued line still needs a body.
1787
        lr.lr_start = 0;
108✔
1788
        lr.lr_end = line.length();
108✔
1789
        sa.emplace_back(lr, SA_BODY.value());
108✔
1790
        if (!this->lf_multiline) {
108✔
1791
            auto len
1792
                = pat.p_pcre.pp_value->match_partial(line.to_string_fragment());
×
1793
            sa.emplace_back(
×
1794
                line_range{(int) len, -1},
×
1795
                SA_INVALID.value("Log line does not match any pattern"));
×
1796
        }
1797
        return;
108✔
1798
    }
1799

1800
    std::optional<string_fragment> module_cap;
1,092✔
1801
    if (!pat.p_module_format) {
1,092✔
1802
        auto ts_cap = md[pat.p_timestamp_field_index];
1,086✔
1803
        if (ts_cap) {
1,086✔
1804
            sa.emplace_back(to_line_range(ts_cap.value()),
1,086✔
1805
                            logline::L_TIMESTAMP.value());
2,172✔
1806
        }
1807

1808
        if (pat.p_module_field_index != -1) {
1,086✔
1809
            module_cap = md[pat.p_module_field_index];
342✔
1810
            if (module_cap) {
342✔
1811
                sa.emplace_back(to_line_range(module_cap.value()),
340✔
1812
                                logline::L_MODULE.value());
680✔
1813
            }
1814
        }
1815

1816
        auto opid_cap = md[pat.p_opid_field_index];
1,086✔
1817
        if (opid_cap && !opid_cap->empty()) {
1,086✔
1818
            sa.emplace_back(to_line_range(opid_cap.value()),
910✔
1819
                            logline::L_OPID.value());
1,820✔
1820
            values.lvv_opid_value = opid_cap->to_string();
910✔
1821
            values.lvv_opid_provenance
1822
                = logline_value_vector::opid_provenance::file;
910✔
1823
        }
1824
    }
1825

1826
    auto body_cap = md[pat.p_body_field_index];
1,092✔
1827

1828
    for (size_t lpc = 0; lpc < pat.p_value_by_index.size(); lpc++) {
11,309✔
1829
        const indexed_value_def& ivd = pat.p_value_by_index[lpc];
10,217✔
1830
        const struct scaling_factor* scaling = nullptr;
10,217✔
1831
        auto cap = md[ivd.ivd_index];
10,217✔
1832
        const auto& vd = *ivd.ivd_value_def;
10,217✔
1833

1834
        if (ivd.ivd_unit_field_index >= 0) {
10,217✔
1835
            auto unit_cap = md[ivd.ivd_unit_field_index];
10✔
1836

1837
            if (unit_cap) {
10✔
1838
                intern_string_t unit_val
1839
                    = intern_string::lookup(unit_cap.value());
10✔
1840
                auto unit_iter = vd.vd_unit_scaling.find(unit_val);
10✔
1841
                if (unit_iter != vd.vd_unit_scaling.end()) {
10✔
1842
                    const struct scaling_factor& sf = unit_iter->second;
10✔
1843

1844
                    scaling = &sf;
10✔
1845
                }
1846
            }
1847
        }
1848

1849
        if (cap) {
10,217✔
1850
            values.lvv_values.emplace_back(
18,484✔
1851
                vd.vd_meta, line, to_line_range(cap.value()));
9,242✔
1852
            values.lvv_values.back().apply_scaling(scaling);
9,242✔
1853
        } else {
1854
            values.lvv_values.emplace_back(vd.vd_meta);
975✔
1855
        }
1856
        if (pat.p_module_format) {
10,217✔
1857
            values.lvv_values.back().lv_meta.lvm_from_module = true;
46✔
1858
        }
1859
    }
1860

1861
    bool did_mod_annotate_body = false;
1,092✔
1862
    if (annotate_module && module_cap && body_cap && body_cap->is_valid()) {
1,092✔
1863
        intern_string_t mod_name = intern_string::lookup(module_cap.value());
80✔
1864
        auto mod_iter = MODULE_FORMATS.find(mod_name);
80✔
1865

1866
        if (mod_iter != MODULE_FORMATS.end()
80✔
1867
            && mod_iter->second.mf_mod_format != nullptr)
80✔
1868
        {
1869
            auto& mf = mod_iter->second;
4✔
1870
            auto narrow_res
1871
                = line.narrow(body_cap->sf_begin, body_cap->length());
4✔
1872
            auto pre_mod_values_size = values.lvv_values.size();
4✔
1873
            auto pre_mod_sa_size = sa.size();
4✔
1874
            mf.mf_mod_format->annotate(lf, line_number, sa, values, false);
4✔
1875
            for (size_t lpc = pre_mod_values_size;
28✔
1876
                 lpc < values.lvv_values.size();
28✔
1877
                 lpc++)
1878
            {
1879
                values.lvv_values[lpc].lv_origin.shift(0, body_cap->sf_begin);
24✔
1880
            }
1881
            for (size_t lpc = pre_mod_sa_size; lpc < sa.size(); lpc++) {
8✔
1882
                sa[lpc].sa_range.shift(0, body_cap->sf_begin);
4✔
1883
            }
1884
            line.widen(narrow_res);
4✔
1885
            did_mod_annotate_body = true;
4✔
1886
        }
1887
    }
1888
    if (!did_mod_annotate_body) {
1,092✔
1889
        if (body_cap && body_cap->is_valid()) {
1,088✔
1890
            lr = to_line_range(body_cap.value());
1,079✔
1891
        } else {
1892
            lr.lr_start = line.length();
9✔
1893
            lr.lr_end = line.length();
9✔
1894
        }
1895
        sa.emplace_back(lr, SA_BODY.value());
1,088✔
1896
    }
1897

1898
    log_format::annotate(lf, line_number, sa, values, annotate_module);
1,092✔
1899
}
1900

1901
void
1902
external_log_format::rewrite(exec_context& ec,
43✔
1903
                             shared_buffer_ref& line,
1904
                             string_attrs_t& sa,
1905
                             std::string& value_out)
1906
{
1907
    std::vector<logline_value>::iterator shift_iter;
43✔
1908
    auto& values = *ec.ec_line_values;
43✔
1909

1910
    value_out.assign(line.get_data(), line.length());
43✔
1911

1912
    for (auto iter = values.lvv_values.begin(); iter != values.lvv_values.end();
259✔
1913
         ++iter)
216✔
1914
    {
1915
        if (!iter->lv_origin.is_valid()) {
216✔
1916
            log_debug("%d: not rewriting value with invalid origin -- %s",
24✔
1917
                      ec.ec_top_line,
1918
                      iter->lv_meta.lvm_name.get());
1919
            continue;
184✔
1920
        }
1921

1922
        auto vd_iter = this->elf_value_defs.find(iter->lv_meta.lvm_name);
192✔
1923
        if (vd_iter == this->elf_value_defs.end()) {
192✔
1924
            log_debug("not rewriting undefined value -- %s",
×
1925
                      iter->lv_meta.lvm_name.get());
1926
            continue;
×
1927
        }
1928

1929
        const auto& vd = *vd_iter->second;
192✔
1930

1931
        if (vd.vd_rewriter.empty()) {
192✔
1932
            continue;
160✔
1933
        }
1934

1935
        auto _sg = ec.enter_source(
1936
            vd_iter->second->vd_rewrite_src_name, 1, vd.vd_rewriter);
32✔
1937
        std::string field_value;
32✔
1938

1939
        auto_mem<FILE> tmpout(fclose);
32✔
1940

1941
        tmpout = std::tmpfile();
32✔
1942
        if (!tmpout) {
32✔
1943
            log_error("unable to create temporary file");
×
1944
            return;
×
1945
        }
1946
        fcntl(fileno(tmpout), F_SETFD, FD_CLOEXEC);
32✔
1947
        auto fd_copy = auto_fd::dup_of(fileno(tmpout));
32✔
1948
        fd_copy.close_on_exec();
32✔
1949
        auto ec_out = std::make_pair(tmpout.release(), fclose);
32✔
1950
        {
1951
            exec_context::output_guard og(ec, "tmp", ec_out);
64✔
1952

1953
            auto exec_res = execute_any(ec, vd.vd_rewriter);
32✔
1954
            if (exec_res.isOk()) {
32✔
1955
                field_value = exec_res.unwrap();
32✔
1956
            } else {
1957
                field_value = exec_res.unwrapErr().to_attr_line().get_string();
×
1958
            }
1959
        }
32✔
1960
        struct stat st;
1961
        fstat(fd_copy.get(), &st);
32✔
1962
        if (st.st_size > 0) {
32✔
1963
            auto buf = auto_buffer::alloc(st.st_size);
2✔
1964

1965
            buf.resize(st.st_size);
2✔
1966
            pread(fd_copy.get(), buf.in(), st.st_size, 0);
2✔
1967
            field_value = buf.to_string();
2✔
1968
        }
2✔
1969
        value_out.erase(iter->lv_origin.lr_start, iter->lv_origin.length());
32✔
1970

1971
        int32_t shift_amount
1972
            = ((int32_t) field_value.length()) - iter->lv_origin.length();
32✔
1973
        auto orig_lr = iter->lv_origin;
32✔
1974
        value_out.insert(iter->lv_origin.lr_start, field_value);
32✔
1975
        for (shift_iter = values.lvv_values.begin();
32✔
1976
             shift_iter != values.lvv_values.end();
180✔
1977
             ++shift_iter)
148✔
1978
        {
1979
            shift_iter->lv_origin.shift_range(orig_lr, shift_amount);
148✔
1980
        }
1981
        shift_string_attrs(sa, orig_lr, shift_amount);
32✔
1982
    }
32✔
1983
}
1984

1985
static int
1986
read_json_field(yajlpp_parse_context* ypc, const unsigned char* str, size_t len)
32,082✔
1987
{
1988
    json_log_userdata* jlu = (json_log_userdata*) ypc->ypc_userdata;
32,082✔
1989
    const intern_string_t field_name = ypc->get_path();
32,082✔
1990
    struct timeval tv_out;
1991
    auto frag = string_fragment::from_bytes(str, len);
32,082✔
1992

1993
    if (jlu->jlu_format->lf_timestamp_field == field_name) {
32,082✔
1994
        const auto* last = jlu->jlu_format->lf_date_time.scan(
562✔
1995
            (const char*) str,
1996
            len,
1997
            jlu->jlu_format->get_timestamp_formats(),
562✔
1998
            &jlu->jlu_exttm,
1999
            tv_out);
2000
        if (last == nullptr) {
562✔
2001
            auto ls = jlu->jlu_format->lf_date_time.unlock();
20✔
2002
            if ((last = jlu->jlu_format->lf_date_time.scan(
20✔
2003
                     (const char*) str,
2004
                     len,
2005
                     jlu->jlu_format->get_timestamp_formats(),
20✔
2006
                     &jlu->jlu_exttm,
2007
                     tv_out))
2008
                == nullptr)
20✔
2009
            {
2010
                jlu->jlu_format->lf_date_time.relock(ls);
×
2011
            }
2012
            if (last != nullptr) {
20✔
2013
                auto old_flags
20✔
2014
                    = jlu->jlu_format->lf_timestamp_flags & DATE_TIME_SET_FLAGS;
20✔
2015
                auto new_flags = jlu->jlu_exttm.et_flags & DATE_TIME_SET_FLAGS;
20✔
2016

2017
                // It is unlikely a valid timestamp would lose much
2018
                // precision.
2019
                if (new_flags != old_flags) {
20✔
2020
                    last = nullptr;
×
2021
                }
2022
            }
2023
        }
2024
        if (last != nullptr) {
562✔
2025
            jlu->jlu_format->lf_timestamp_flags = jlu->jlu_exttm.et_flags;
562✔
2026
            jlu->jlu_base_line->set_time(tv_out);
562✔
2027
        }
2028
    } else if (jlu->jlu_format->elf_level_pointer.pp_value != nullptr) {
31,520✔
2029
        if (jlu->jlu_format->elf_level_pointer.pp_value
8,870✔
2030
                ->find_in(field_name.to_string_fragment(), PCRE2_NO_UTF_CHECK)
8,870✔
2031
                .ignore_error()
8,870✔
2032
                .has_value())
4,435✔
2033
        {
2034
            jlu->jlu_base_line->set_level(
×
2035
                jlu->jlu_format->convert_level(frag, jlu->jlu_batch_context));
×
2036
        }
2037
    }
2038
    if (jlu->jlu_format->elf_level_field == field_name) {
32,082✔
2039
        jlu->jlu_base_line->set_level(
223✔
2040
            jlu->jlu_format->convert_level(frag, jlu->jlu_batch_context));
223✔
2041
    }
2042
    if (jlu->jlu_format->elf_opid_field == field_name) {
32,082✔
2043
        uint8_t opid = hash_str((const char*) str, len);
60✔
2044
        jlu->jlu_base_line->set_opid(opid);
60✔
2045

2046
        auto& sbc = *jlu->jlu_batch_context;
60✔
2047
        auto opid_iter = sbc.sbc_opids.los_opid_ranges.find(frag);
60✔
2048
        if (opid_iter == sbc.sbc_opids.los_opid_ranges.end()) {
60✔
2049
            jlu->jlu_opid_frag = frag.to_owned(sbc.sbc_allocator);
52✔
2050
        } else {
2051
            jlu->jlu_opid_frag = opid_iter->first;
8✔
2052
        }
2053
    }
2054
    if (jlu->jlu_format->elf_subid_field == field_name) {
32,082✔
2055
        jlu->jlu_subid = frag.to_string();
×
2056
    }
2057

2058
    if (jlu->jlu_format->lf_desc_fields.contains(field_name)) {
32,082✔
2059
        auto frag_copy = frag.to_owned(jlu->jlu_format->lf_desc_allocator);
×
2060

2061
        jlu->jlu_format->lf_desc_captures.emplace(field_name, frag_copy);
×
2062
    }
2063

2064
    jlu->add_sub_lines_for(
64,164✔
2065
        field_name, ypc->is_level(1), std::nullopt, str, len);
32,082✔
2066

2067
    return 1;
32,082✔
2068
}
2069

2070
static int
2071
rewrite_json_field(yajlpp_parse_context* ypc,
23,092✔
2072
                   const unsigned char* str,
2073
                   size_t len)
2074
{
2075
    static const intern_string_t body_name = intern_string::lookup("body", -1);
23,092✔
2076
    json_log_userdata* jlu = (json_log_userdata*) ypc->ypc_userdata;
23,092✔
2077
    const intern_string_t field_name = ypc->get_path();
23,092✔
2078

2079
    if (jlu->jlu_format->elf_opid_field == field_name) {
23,092✔
2080
        auto frag = string_fragment::from_bytes(str, len);
276✔
2081
        jlu->jlu_format->jlf_line_values.lvv_opid_value = frag.to_string();
276✔
2082
        jlu->jlu_format->jlf_line_values.lvv_opid_provenance
276✔
2083
            = logline_value_vector::opid_provenance::file;
276✔
2084
    }
2085
    if (jlu->jlu_format->lf_timestamp_field == field_name) {
23,092✔
2086
        char time_buf[64];
2087

2088
        // TODO add a timeval kind to logline_value
2089
        if (jlu->jlu_line->is_time_skewed()
1,378✔
2090
            || (jlu->jlu_format->lf_timestamp_flags
2,756✔
2091
                & (ETF_MICROS_SET | ETF_NANOS_SET | ETF_ZONE_SET)))
1,378✔
2092
        {
2093
            struct timeval tv;
2094

2095
            const auto* last = jlu->jlu_format->lf_date_time.scan(
1,378✔
2096
                (const char*) str,
2097
                len,
2098
                jlu->jlu_format->get_timestamp_formats(),
1,378✔
2099
                &jlu->jlu_exttm,
2100
                tv);
2101
            if (last == nullptr) {
1,378✔
2102
                auto ls = jlu->jlu_format->lf_date_time.unlock();
52✔
2103
                if ((last = jlu->jlu_format->lf_date_time.scan(
52✔
2104
                         (const char*) str,
2105
                         len,
2106
                         jlu->jlu_format->get_timestamp_formats(),
52✔
2107
                         &jlu->jlu_exttm,
2108
                         tv))
2109
                    == nullptr)
52✔
2110
                {
2111
                    jlu->jlu_format->lf_date_time.relock(ls);
×
2112
                }
2113
            }
2114
            jlu->jlu_format->lf_date_time.ftime(
1,378✔
2115
                time_buf,
2116
                sizeof(time_buf),
2117
                jlu->jlu_format->get_timestamp_formats(),
1,378✔
2118
                jlu->jlu_exttm);
1,378✔
2119
        } else {
2120
            sql_strftime(
×
2121
                time_buf, sizeof(time_buf), jlu->jlu_line->get_timeval(), 'T');
×
2122
        }
2123
        if (jlu->jlu_exttm.et_flags & ETF_ZONE_SET
1,378✔
2124
            && jlu->jlu_format->lf_date_time.dts_zoned_to_local)
1,378✔
2125
        {
2126
            jlu->jlu_exttm.et_flags &= ~ETF_Z_IS_UTC;
1,378✔
2127
        }
2128
        jlu->jlu_exttm.et_gmtoff
2129
            = jlu->jlu_format->lf_date_time.dts_local_offset_cache;
1,378✔
2130
        jlu->jlu_format->jlf_line_values.lvv_values.emplace_back(
1,378✔
2131
            jlu->jlu_format->get_value_meta(field_name,
2,756✔
2132
                                            value_kind_t::VALUE_TEXT),
2133
            std::string{time_buf});
2,756✔
2134
    } else if (jlu->jlu_shared_buffer.contains((const char*) str)) {
21,714✔
2135
        auto str_offset = (int) ((const char*) str - jlu->jlu_line_value);
21,258✔
2136
        if (field_name == jlu->jlu_format->elf_body_field) {
21,258✔
2137
            jlu->jlu_format->jlf_line_values.lvv_values.emplace_back(
731✔
2138
                logline_value_meta(body_name,
1,462✔
2139
                                   value_kind_t::VALUE_TEXT,
2140
                                   logline_value_meta::internal_column{},
×
2141
                                   jlu->jlu_format),
731✔
2142
                string_fragment::from_byte_range(
1,462✔
2143
                    jlu->jlu_shared_buffer.get_data(),
731✔
2144
                    str_offset,
2145
                    str_offset + len));
731✔
2146
        }
2147
        if (!ypc->is_level(1) && !jlu->jlu_format->has_value_def(field_name)) {
21,258✔
2148
            return 1;
16,920✔
2149
        }
2150

2151
        jlu->jlu_format->jlf_line_values.lvv_values.emplace_back(
4,338✔
2152
            jlu->jlu_format->get_value_meta(field_name,
8,676✔
2153
                                            value_kind_t::VALUE_TEXT),
2154
            string_fragment::from_byte_range(jlu->jlu_shared_buffer.get_data(),
8,676✔
2155
                                             str_offset,
2156
                                             str_offset + len));
4,338✔
2157
    } else {
2158
        if (field_name == jlu->jlu_format->elf_body_field) {
456✔
2159
            jlu->jlu_format->jlf_line_values.lvv_values.emplace_back(
279✔
2160
                logline_value_meta(body_name,
558✔
2161
                                   value_kind_t::VALUE_TEXT,
2162
                                   logline_value_meta::internal_column{},
×
2163
                                   jlu->jlu_format),
279✔
2164
                std::string{(const char*) str, len});
558✔
2165
        }
2166
        if (!ypc->is_level(1) && !jlu->jlu_format->has_value_def(field_name)) {
456✔
2167
            return 1;
92✔
2168
        }
2169

2170
        jlu->jlu_format->jlf_line_values.lvv_values.emplace_back(
364✔
2171
            jlu->jlu_format->get_value_meta(field_name,
728✔
2172
                                            value_kind_t::VALUE_TEXT),
2173
            std::string{(const char*) str, len});
728✔
2174
    }
2175

2176
    return 1;
6,080✔
2177
}
2178

2179
void
2180
external_log_format::get_subline(const logline& ll,
14,046✔
2181
                                 shared_buffer_ref& sbr,
2182
                                 bool full_message)
2183
{
2184
    if (this->elf_type == elf_type_t::ELF_TYPE_TEXT) {
14,046✔
2185
        return;
10,854✔
2186
    }
2187

2188
    if (this->jlf_cached_offset != ll.get_offset()
3,192✔
2189
        || this->jlf_cached_full != full_message)
3,192✔
2190
    {
2191
        auto& ypc = *(this->jlf_parse_context);
1,484✔
2192
        yajl_handle handle = this->jlf_yajl_handle.get();
1,484✔
2193
        json_log_userdata jlu(sbr, nullptr);
1,484✔
2194

2195
        this->jlf_share_manager.invalidate_refs();
1,484✔
2196
        this->jlf_cached_line.clear();
1,484✔
2197
        this->jlf_line_values.clear();
1,484✔
2198
        this->jlf_line_offsets.clear();
1,484✔
2199
        this->jlf_line_attrs.clear();
1,484✔
2200

2201
        auto line_frag = sbr.to_string_fragment();
1,484✔
2202

2203
        if (!line_frag.startswith("{")) {
1,484✔
2204
            this->jlf_cached_line.resize(line_frag.length());
70✔
2205
            memcpy(this->jlf_cached_line.data(),
70✔
2206
                   line_frag.data(),
70✔
2207
                   line_frag.length());
70✔
2208
            this->jlf_line_values.clear();
70✔
2209
            sbr.share(this->jlf_share_manager,
140✔
2210
                      &this->jlf_cached_line[0],
70✔
2211
                      this->jlf_cached_line.size());
2212
            this->jlf_line_values.lvv_sbr = sbr.clone();
70✔
2213
            this->jlf_line_attrs.emplace_back(
70✔
2214
                line_range{0, -1},
70✔
2215
                SA_INVALID.value(fmt::format(
140✔
2216
                    FMT_STRING("line at offset {} is not a JSON-line"),
140✔
2217
                    ll.get_offset())));
70✔
2218
            return;
70✔
2219
        }
70✔
2220

70✔
2221
        yajl_reset(handle);
2222
        ypc.set_static_handler(json_log_rewrite_handlers.jpc_children[0]);
2223
        ypc.ypc_userdata = &jlu;
1,414✔
2224
        ypc.ypc_ignore_unused = true;
1,414✔
2225
        ypc.ypc_alt_callbacks.yajl_start_array = json_array_start;
1,414✔
2226
        ypc.ypc_alt_callbacks.yajl_end_array = json_array_end;
1,414✔
2227
        ypc.ypc_alt_callbacks.yajl_start_map = json_array_start;
1,414✔
2228
        ypc.ypc_alt_callbacks.yajl_end_map = json_array_end;
1,414✔
2229
        jlu.jlu_format = this;
1,414✔
2230
        jlu.jlu_line = &ll;
1,414✔
2231
        jlu.jlu_handle = handle;
1,414✔
2232
        jlu.jlu_line_value = sbr.get_data();
1,414✔
2233

1,414✔
2234
        yajl_status parse_status = yajl_parse(
1,414✔
2235
            handle, (const unsigned char*) sbr.get_data(), sbr.length());
2236
        if (parse_status != yajl_status_ok
2,828✔
2237
            || yajl_complete_parse(handle) != yajl_status_ok)
1,414✔
2238
        {
1,414✔
2239
            unsigned char* msg;
1,414✔
2240
            std::string full_msg;
2241

2242
            msg = yajl_get_error(
14✔
2243
                handle, 1, (const unsigned char*) sbr.get_data(), sbr.length());
2244
            if (msg != nullptr) {
28✔
2245
                full_msg = fmt::format(
14✔
2246
                    FMT_STRING("[offset: {}] {}\n{}"),
14✔
2247
                    ll.get_offset(),
14✔
2248
                    fmt::string_view{sbr.get_data(), sbr.length()},
28✔
2249
                    reinterpret_cast<char*>(msg));
14✔
2250
                yajl_free_error(handle, msg);
14✔
2251
            }
14✔
2252

14✔
2253
            this->jlf_cached_line.resize(full_msg.size());
28✔
2254
            memcpy(
14✔
2255
                this->jlf_cached_line.data(), full_msg.data(), full_msg.size());
2256
            this->jlf_line_values.clear();
2257
            this->jlf_line_attrs.emplace_back(
14✔
2258
                line_range{0, -1},
14✔
2259
                SA_INVALID.value("JSON line failed to parse"));
14✔
2260
        } else {
14✔
2261
            std::vector<logline_value>::iterator lv_iter;
14✔
2262
            bool used_values[this->jlf_line_values.lvv_values.size()];
14✔
2263
            struct line_range lr;
28✔
2264

14✔
2265
            memset(used_values, 0, sizeof(used_values));
1,400✔
2266
            for (lv_iter = this->jlf_line_values.lvv_values.begin();
2,800✔
2267
                 lv_iter != this->jlf_line_values.lvv_values.end();
1,400✔
2268
                 ++lv_iter)
2269
            {
1,400✔
2270
                lv_iter->lv_meta.lvm_format = this;
1,400✔
2271
            }
10,398✔
2272
            if (jlu.jlu_opid_frag) {
8,998✔
2273
                this->jlf_line_values.lvv_opid_value
2274
                    = jlu.jlu_opid_frag->to_string();
8,998✔
2275
                this->jlf_line_values.lvv_opid_provenance
2276
                    = logline_value_vector::opid_provenance::file;
1,400✔
2277
            }
2278

×
2279
            int sub_offset = this->jlf_line_format_init_count;
2280
            for (const auto& jfe : this->jlf_line_format) {
×
2281
                static const intern_string_t ts_field
2282
                    = intern_string::lookup("__timestamp__", -1);
2283
                static const intern_string_t level_field
1,400✔
2284
                    = intern_string::lookup("__level__");
14,774✔
2285
                size_t begin_size = this->jlf_cached_line.size();
2286

13,374✔
2287
                switch (jfe.jfe_type) {
2288
                    case json_log_field::CONSTANT:
13,374✔
2289
                        this->json_append_to_cache(
13,374✔
2290
                            jfe.jfe_default_value.c_str(),
2291
                            jfe.jfe_default_value.size());
13,374✔
2292
                        break;
3,251✔
2293
                    case json_log_field::VARIABLE:
3,251✔
2294
                        lv_iter = find_if(
2295
                            this->jlf_line_values.lvv_values.begin(),
3,251✔
2296
                            this->jlf_line_values.lvv_values.end(),
3,251✔
2297
                            logline_value_cmp(&jfe.jfe_value.pp_value));
10,123✔
2298
                        if (lv_iter != this->jlf_line_values.lvv_values.end()) {
10,123✔
2299
                            auto str = lv_iter->to_string();
2300
                            value_def* vd = nullptr;
2301

20,246✔
2302
                            if (lv_iter->lv_meta.lvm_values_index) {
10,123✔
2303
                                vd = this->elf_value_def_order
4,810✔
2304
                                         [lv_iter->lv_meta.lvm_values_index
4,810✔
2305
                                              .value()]
2306
                                             .get();
4,810✔
2307
                            }
2308
                            while (endswith(str, "\n")) {
3,131✔
2309
                                str.pop_back();
3,131✔
2310
                            }
3,131✔
2311
                            size_t nl_pos = str.find('\n');
2312

5,046✔
2313
                            if (!jfe.jfe_prefix.empty()) {
236✔
2314
                                this->json_append_to_cache(jfe.jfe_prefix);
2315
                            }
4,810✔
2316
                            lr.lr_start = this->jlf_cached_line.size();
2317

4,810✔
2318
                            if ((int) str.size() > jfe.jfe_max_width) {
1,475✔
2319
                                switch (jfe.jfe_overflow) {
2320
                                    case json_format_element::overflow_t::
4,810✔
2321
                                        ABBREV: {
2322
                                        size_t new_size
4,810✔
2323
                                            = abbreviate_str(&str[0],
156✔
2324
                                                             str.size(),
127✔
2325
                                                             jfe.jfe_max_width);
2326
                                        str.resize(new_size);
2327
                                        this->json_append(
127✔
2328
                                            jfe, vd, str.data(), str.size());
2329
                                        break;
127✔
2330
                                    }
127✔
2331
                                    case json_format_element::overflow_t::
127✔
2332
                                        TRUNCATE: {
127✔
2333
                                        this->json_append_to_cache(
127✔
2334
                                            str.c_str(), jfe.jfe_max_width);
2335
                                        break;
26✔
2336
                                    }
2337
                                    case json_format_element::overflow_t::
26✔
2338
                                        DOTDOT: {
26✔
2339
                                        size_t middle
26✔
2340
                                            = (jfe.jfe_max_width / 2) - 1;
2341
                                        this->json_append_to_cache(str.c_str(),
3✔
2342
                                                                   middle);
2343
                                        this->json_append_to_cache("..", 2);
3✔
2344
                                        size_t rest
3✔
2345
                                            = (jfe.jfe_max_width - middle - 2);
3✔
2346
                                        this->json_append_to_cache(
2347
                                            str.c_str() + str.size() - rest,
3✔
2348
                                            rest);
3✔
2349
                                        break;
3✔
2350
                                    }
6✔
2351
                                    case json_format_element::overflow_t::
3✔
2352
                                        LASTWORD: {
2353
                                        size_t new_size
3✔
2354
                                            = last_word_str(&str[0],
2355
                                                            str.size(),
×
2356
                                                            jfe.jfe_max_width);
2357
                                        str.resize(new_size);
2358
                                        this->json_append(
×
2359
                                            jfe, vd, str.data(), str.size());
2360
                                        break;
×
2361
                                    }
×
2362
                                }
×
2363
                            } else {
×
2364
                                sub_offset
×
2365
                                    += std::count(str.begin(), str.end(), '\n');
2366
                                this->json_append(
2367
                                    jfe, vd, str.c_str(), str.size());
2368
                            }
2369

4,654✔
2370
                            if (nl_pos == std::string::npos || full_message) {
4,654✔
2371
                                lr.lr_end = this->jlf_cached_line.size();
4,654✔
2372
                            } else {
2373
                                lr.lr_end = lr.lr_start + nl_pos;
2374
                            }
4,810✔
2375

4,807✔
2376
                            if (lv_iter->lv_meta.lvm_name
2377
                                == this->lf_timestamp_field)
3✔
2378
                            {
2379
                                this->jlf_line_attrs.emplace_back(
2380
                                    lr, logline::L_TIMESTAMP.value());
4,810✔
2381
                            } else if (lv_iter->lv_meta.lvm_name
4,810✔
2382
                                       == this->elf_body_field)
2383
                            {
992✔
2384
                                this->jlf_line_attrs.emplace_back(
1,984✔
2385
                                    lr, SA_BODY.value());
3,818✔
2386
                            } else if (lv_iter->lv_meta.lvm_name
3,818✔
2387
                                           == this->elf_opid_field
2388
                                       && !lr.empty())
1,010✔
2389
                            {
2,020✔
2390
                                this->jlf_line_attrs.emplace_back(
5,616✔
2391
                                    lr, logline::L_OPID.value());
2,808✔
2392
                            }
2,808✔
2393
                            lv_iter->lv_origin = lr;
2394
                            lv_iter->lv_sub_offset = sub_offset;
276✔
2395
                            used_values[std::distance(
552✔
2396
                                this->jlf_line_values.lvv_values.begin(),
2397
                                lv_iter)]
4,810✔
2398
                                = true;
4,810✔
2399

4,810✔
2400
                            if (!jfe.jfe_suffix.empty()) {
2401
                                this->json_append_to_cache(jfe.jfe_suffix);
2402
                            }
4,810✔
2403
                        } else if (jfe.jfe_value.pp_value == ts_field) {
2404
                            struct line_range lr;
4,810✔
2405
                            ssize_t ts_len;
452✔
2406
                            char ts[64];
2407
                            struct exttm et;
10,123✔
2408

553✔
2409
                            ll.to_exttm(et);
2410
                            et.et_nsec += jlu.jlu_exttm.et_nsec % 1000000;
2411
                            et.et_gmtoff = jlu.jlu_exttm.et_gmtoff;
553✔
2412
                            et.et_flags |= jlu.jlu_exttm.et_flags;
2413
                            if (!jfe.jfe_prefix.empty()) {
553✔
2414
                                this->json_append_to_cache(jfe.jfe_prefix);
553✔
2415
                            }
553✔
2416
                            if (jfe.jfe_ts_format.empty()) {
553✔
2417
                                ts_len = this->lf_date_time.ftime(
553✔
2418
                                    ts,
3✔
2419
                                    sizeof(ts),
2420
                                    this->get_timestamp_formats(),
553✔
2421
                                    et);
408✔
2422
                            } else {
2423
                                ts_len = ftime_fmt(ts,
2424
                                                   sizeof(ts),
2425
                                                   jfe.jfe_ts_format.c_str(),
2426
                                                   et);
2427
                            }
145✔
2428
                            lr.lr_start = this->jlf_cached_line.size();
2429
                            this->json_append_to_cache(ts, ts_len);
2430
                            lr.lr_end = this->jlf_cached_line.size();
2431
                            this->jlf_line_attrs.emplace_back(
2432
                                lr, logline::L_TIMESTAMP.value());
553✔
2433

553✔
2434
                            lv_iter = find_if(
553✔
2435
                                this->jlf_line_values.lvv_values.begin(),
553✔
2436
                                this->jlf_line_values.lvv_values.end(),
1,106✔
2437
                                logline_value_cmp(&this->lf_timestamp_field));
2438
                            if (lv_iter
553✔
2439
                                != this->jlf_line_values.lvv_values.end())
2440
                            {
2441
                                used_values[distance(
1,106✔
2442
                                    this->jlf_line_values.lvv_values.begin(),
553✔
2443
                                    lv_iter)]
553✔
2444
                                    = true;
2445
                            }
553✔
2446
                            if (!jfe.jfe_suffix.empty()) {
2447
                                this->json_append_to_cache(jfe.jfe_suffix);
2448
                            }
553✔
2449
                        } else if (jfe.jfe_value.pp_value == level_field
2450
                                   || jfe.jfe_value.pp_value
553✔
2451
                                       == this->elf_level_field)
3✔
2452
                        {
2453
                            const auto* level_name = ll.get_level_name();
4,760✔
2454
                            auto level_len = strlen(level_name);
9,441✔
2455
                            this->json_append(
4,681✔
2456
                                jfe, nullptr, level_name, level_len);
2457
                            if (jfe.jfe_auto_width) {
79✔
2458
                                this->json_append_to_cache(MAX_LEVEL_NAME_LEN
79✔
2459
                                                           - level_len);
79✔
2460
                            }
2461
                        } else if (!jfe.jfe_default_value.empty()) {
79✔
2462
                            if (!jfe.jfe_prefix.empty()) {
59✔
2463
                                this->json_append_to_cache(jfe.jfe_prefix);
59✔
2464
                            }
2465
                            this->json_append(jfe,
4,681✔
2466
                                              nullptr,
796✔
2467
                                              jfe.jfe_default_value.c_str(),
576✔
2468
                                              jfe.jfe_default_value.size());
2469
                            if (!jfe.jfe_suffix.empty()) {
796✔
2470
                                this->json_append_to_cache(jfe.jfe_suffix);
2471
                            }
2472
                        }
796✔
2473

796✔
2474
                        switch (jfe.jfe_text_transform) {
96✔
2475
                            case external_log_format::json_format_element::
2476
                                transform_t::NONE:
2477
                                break;
2478
                            case external_log_format::json_format_element::
10,123✔
2479
                                transform_t::UPPERCASE:
10,044✔
2480
                                for (size_t cindex = begin_size;
2481
                                     cindex < this->jlf_cached_line.size();
10,044✔
2482
                                     cindex++)
79✔
2483
                                {
2484
                                    this->jlf_cached_line[cindex] = toupper(
79✔
2485
                                        this->jlf_cached_line[cindex]);
634✔
2486
                                }
2487
                                break;
2488
                            case external_log_format::json_format_element::
555✔
2489
                                transform_t::LOWERCASE:
555✔
2490
                                for (size_t cindex = begin_size;
2491
                                     cindex < this->jlf_cached_line.size();
79✔
2492
                                     cindex++)
×
2493
                                {
2494
                                    this->jlf_cached_line[cindex] = tolower(
×
2495
                                        this->jlf_cached_line[cindex]);
×
2496
                                }
2497
                                break;
2498
                            case external_log_format::json_format_element::
×
2499
                                transform_t::CAPITALIZE:
×
2500
                                for (size_t cindex = begin_size;
2501
                                     cindex < begin_size + 1;
×
2502
                                     cindex++)
×
2503
                                {
2504
                                    this->jlf_cached_line[cindex] = toupper(
×
2505
                                        this->jlf_cached_line[cindex]);
×
2506
                                }
2507
                                for (size_t cindex = begin_size + 1;
2508
                                     cindex < this->jlf_cached_line.size();
×
2509
                                     cindex++)
×
2510
                                {
2511
                                    this->jlf_cached_line[cindex] = tolower(
×
2512
                                        this->jlf_cached_line[cindex]);
×
2513
                                }
2514
                                break;
2515
                        }
×
2516
                        break;
×
2517
                }
2518
            }
×
2519
            this->json_append_to_cache("\n", 1);
2520
            sub_offset += 1;
10,123✔
2521

2522
            for (size_t lpc = 0; lpc < this->jlf_line_values.lvv_values.size();
2523
                 lpc++)
1,400✔
2524
            {
1,400✔
2525
                static const intern_string_t body_name
2526
                    = intern_string::lookup("body", -1);
10,398✔
2527
                auto& lv = this->jlf_line_values.lvv_values[lpc];
2528

2529
                if (lv.lv_meta.is_hidden() || used_values[lpc]
2530
                    || body_name == lv.lv_meta.lvm_name)
8,998✔
2531
                {
8,998✔
2532
                    continue;
2533
                }
14,861✔
2534

14,861✔
2535
                auto str = lv.to_string();
2536
                while (endswith(str, "\n")) {
8,239✔
2537
                    str.pop_back();
2538
                }
2539

759✔
2540
                lv.lv_sub_offset = sub_offset;
759✔
2541
                lv.lv_origin.lr_start = this->jlf_cached_line.size() + 2
×
2542
                    + lv.lv_meta.lvm_name.size() + 2;
2543
                auto frag = string_fragment::from_str(str);
2544
                while (true) {
759✔
2545
                    auto utf_scan_res = is_utf8(frag, '\n');
759✔
2546

759✔
2547
                    this->json_append_to_cache("  ", 2);
759✔
2548
                    this->json_append_to_cache(
2549
                        lv.lv_meta.lvm_name.to_string_fragment());
761✔
2550
                    this->json_append_to_cache(": ", 2);
2551
                    this->json_append_to_cache(utf_scan_res.usr_valid_frag);
761✔
2552
                    this->json_append_to_cache("\n", 1);
761✔
2553
                    sub_offset += 1;
761✔
2554
                    if (utf_scan_res.usr_remaining) {
761✔
2555
                        frag = utf_scan_res.usr_remaining.value();
761✔
2556
                    } else {
761✔
2557
                        break;
761✔
2558
                    }
761✔
2559
                }
2✔
2560
                lv.lv_origin.lr_end = this->jlf_cached_line.size() - 1;
2561
                if (lv.lv_meta.lvm_name == this->elf_opid_field
759✔
2562
                    && !lv.lv_origin.empty())
2563
                {
2✔
2564
                    this->jlf_line_attrs.emplace_back(lv.lv_origin,
759✔
2565
                                                      logline::L_OPID.value());
759✔
2566
                }
759✔
2567
            }
2568
        }
×
2569

×
2570
        this->jlf_line_offsets.push_back(0);
2571
        for (size_t lpc = 0; lpc < this->jlf_cached_line.size(); lpc++) {
759✔
2572
            if (this->jlf_cached_line[lpc] == '\n') {
1,400✔
2573
                this->jlf_line_offsets.push_back(lpc + 1);
2574
            }
1,414✔
2575
        }
149,761✔
2576
        this->jlf_line_offsets.push_back(this->jlf_cached_line.size());
148,347✔
2577
        this->jlf_cached_offset = ll.get_offset();
2,885✔
2578
        this->jlf_cached_full = full_message;
2579
    }
2580

1,414✔
2581
    off_t this_off = 0, next_off = 0;
1,414✔
2582

1,414✔
2583
    if (!this->jlf_line_offsets.empty()
1,484✔
2584
        && ll.get_sub_offset() < this->jlf_line_offsets.size())
2585
    {
3,122✔
2586
        require(ll.get_sub_offset() < this->jlf_line_offsets.size());
2587

3,122✔
2588
        this_off = this->jlf_line_offsets[ll.get_sub_offset()];
3,122✔
2589
        if ((ll.get_sub_offset() + 1) < (int) this->jlf_line_offsets.size()) {
2590
            next_off = this->jlf_line_offsets[ll.get_sub_offset() + 1];
3,122✔
2591
        } else {
2592
            next_off = this->jlf_cached_line.size();
3,122✔
2593
        }
3,122✔
2594
        if (next_off > 0 && this->jlf_cached_line[next_off - 1] == '\n'
3,122✔
2595
            && this_off != next_off)
2596
        {
×
2597
            next_off -= 1;
2598
        }
3,122✔
2599
    }
6,244✔
2600

2601
    if (full_message) {
3,122✔
2602
        sbr.share(this->jlf_share_manager,
2603
                  &this->jlf_cached_line[0],
2604
                  this->jlf_cached_line.size());
2605
    } else {
3,122✔
2606
        sbr.share(this->jlf_share_manager,
238✔
2607
                  this->jlf_cached_line.data() + this_off,
119✔
2608
                  next_off - this_off);
2609
    }
2610
    sbr.get_metadata().m_valid_utf = ll.is_valid_utf();
6,006✔
2611
    sbr.get_metadata().m_has_ansi = ll.has_ansi();
3,003✔
2612
    this->jlf_cached_sub_range.lr_start = this_off;
3,003✔
2613
    this->jlf_cached_sub_range.lr_end = next_off;
2614
    this->jlf_line_values.lvv_sbr = sbr.clone();
3,122✔
2615
}
3,122✔
2616

3,122✔
2617
struct compiled_header_expr {
3,122✔
2618
    auto_mem<sqlite3_stmt> che_stmt{sqlite3_finalize};
3,122✔
2619
    bool che_enabled{true};
2620
};
2621

2622
struct format_header_expressions : public lnav_config_listener {
2623
    format_header_expressions() : lnav_config_listener(__FILE__) {}
2624

2625
    auto_sqlite3 e_db;
2626
    std::map<intern_string_t, std::map<std::string, compiled_header_expr>>
2627
        e_header_exprs;
1,021✔
2628
};
2629

2630
using safe_format_header_expressions = safe::Safe<format_header_expressions>;
2631

2632
static safe_format_header_expressions format_header_exprs;
2633

2634
std::optional<external_file_format>
2635
detect_mime_type(const std::filesystem::path& filename)
2636
{
2637
    uint8_t buffer[1024];
2638
    size_t buffer_size = 0;
2639

502✔
2640
    {
2641
        auto_fd fd;
2642

502✔
2643
        if ((fd = lnav::filesystem::openp(filename, O_RDONLY)) == -1) {
2644
            return std::nullopt;
2645
        }
502✔
2646

2647
        ssize_t rc;
502✔
2648

×
2649
        if ((rc = read(fd, buffer, sizeof(buffer))) == -1) {
2650
            return std::nullopt;
2651
        }
2652
        buffer_size = rc;
2653
    }
502✔
2654

×
2655
    auto hexbuf = auto_buffer::alloc(buffer_size * 2);
2656

502✔
2657
    for (size_t lpc = 0; lpc < buffer_size; lpc++) {
502✔
2658
        fmt::format_to(
2659
            std::back_inserter(hexbuf), FMT_STRING("{:02x}"), buffer[lpc]);
502✔
2660
    }
2661

248,245✔
2662
    safe::WriteAccess<safe_format_header_expressions> in(format_header_exprs);
247,743✔
2663

990,972✔
2664
    for (const auto& format : log_format::get_root_formats()) {
247,743✔
2665
        auto elf = std::dynamic_pointer_cast<external_log_format>(format);
247,743✔
2666
        if (elf == nullptr) {
2667
            continue;
2668
        }
502✔
2669

2670
        if (elf->elf_converter.c_header.h_exprs.he_exprs.empty()) {
30,102✔
2671
            continue;
29,600✔
2672
        }
29,600✔
2673

2,510✔
2674
        if (buffer_size < elf->elf_converter.c_header.h_size) {
2675
            log_debug(
2676
                "%s: file content too small (%d) for header detection: %s",
27,090✔
2677
                filename.c_str(),
26,588✔
2678
                buffer_size,
2679
                elf->get_name().get());
2680
            continue;
502✔
2681
        }
18✔
2682
        for (const auto& hpair : elf->elf_converter.c_header.h_exprs.he_exprs) {
2683
            auto& he = in->e_header_exprs[elf->get_name()][hpair.first];
2684

2685
            if (!he.che_enabled) {
2686
                continue;
18✔
2687
            }
2688

1,452✔
2689
            auto* stmt = he.che_stmt.in();
968✔
2690

2691
            if (stmt == nullptr) {
968✔
2692
                continue;
×
2693
            }
2694
            sqlite3_reset(stmt);
2695
            auto count = sqlite3_bind_parameter_count(stmt);
968✔
2696
            for (int lpc = 0; lpc < count; lpc++) {
2697
                const auto* name = sqlite3_bind_parameter_name(stmt, lpc + 1);
968✔
2698

×
2699
                if (name[0] == '$') {
2700
                    const char* env_value;
968✔
2701

968✔
2702
                    if ((env_value = getenv(&name[1])) != nullptr) {
1,936✔
2703
                        sqlite3_bind_text(
968✔
2704
                            stmt, lpc + 1, env_value, -1, SQLITE_STATIC);
2705
                    }
968✔
2706
                    continue;
2707
                }
2708
                if (strcmp(name, ":header") == 0) {
×
2709
                    sqlite3_bind_text(stmt,
×
2710
                                      lpc + 1,
2711
                                      hexbuf.in(),
2712
                                      hexbuf.size(),
×
2713
                                      SQLITE_STATIC);
2714
                    continue;
968✔
2715
                }
968✔
2716
                if (strcmp(name, ":filepath") == 0) {
2717
                    sqlite3_bind_text(
968✔
2718
                        stmt, lpc + 1, filename.c_str(), -1, SQLITE_STATIC);
968✔
2719
                    continue;
2720
                }
968✔
2721
            }
2722

×
2723
            auto step_res = sqlite3_step(stmt);
×
2724

2725
            switch (step_res) {
×
2726
                case SQLITE_OK:
2727
                case SQLITE_DONE:
2728
                    continue;
2729
                case SQLITE_ROW:
968✔
2730
                    break;
2731
                default: {
968✔
2732
                    log_error(
968✔
2733
                        "failed to execute file-format header expression: "
2734
                        "%s:%s -- %s",
968✔
2735
                        elf->get_name().get(),
×
2736
                        hpair.first.c_str(),
×
2737
                        sqlite3_errmsg(in->e_db));
×
2738
                    he.che_enabled = false;
×
2739
                    continue;
2740
                }
2741
            }
2742

2743
            log_info("detected format for: %s -- %s (header-expr: %s)",
2744
                     filename.c_str(),
×
2745
                     elf->get_name().get(),
×
2746
                     hpair.first.c_str());
2747
            return external_file_format{
2748
                elf->get_name().to_string(),
2749
                elf->elf_converter.c_command.pp_value,
×
2750
                elf->elf_converter.c_command.pp_location.sl_source.to_string(),
2751
            };
2752
        }
2753
    }
×
2754

×
2755
    return std::nullopt;
×
2756
}
×
2757

2758
void
2759
external_log_format::build(std::vector<lnav::console::user_message>& errors)
29,600✔
2760
{
2761
    if (!this->lf_timestamp_field.empty()) {
502✔
2762
        auto& vd = this->elf_value_defs[this->lf_timestamp_field];
502✔
2763
        if (vd.get() == nullptr) {
2764
            vd = std::make_shared<external_log_format::value_def>(
2765
                this->lf_timestamp_field,
34,190✔
2766
                value_kind_t::VALUE_TEXT,
2767
                logline_value_meta::internal_column{},
34,190✔
2768
                this);
34,190✔
2769
        }
34,190✔
2770
        vd->vd_meta.lvm_name = this->lf_timestamp_field;
28,460✔
2771
        vd->vd_meta.lvm_kind = value_kind_t::VALUE_TEXT;
28,460✔
2772
        vd->vd_meta.lvm_column = logline_value_meta::internal_column{};
×
2773
        vd->vd_internal = true;
×
2774
    }
56,920✔
2775
    if (startswith(this->elf_level_field.get(), "/")) {
2776
        this->elf_level_field
34,190✔
2777
            = intern_string::lookup(this->elf_level_field.get() + 1);
34,190✔
2778
    }
34,190✔
2779
    if (!this->elf_level_field.empty()
34,190✔
2780
        && this->elf_value_defs.find(this->elf_level_field)
2781
            == this->elf_value_defs.end())
34,190✔
2782
    {
2783
        auto& vd = this->elf_value_defs[this->elf_level_field];
87✔
2784
        if (vd.get() == nullptr) {
2785
            vd = std::make_shared<external_log_format::value_def>(
34,190✔
2786
                this->elf_level_field,
102,570✔
2787
                value_kind_t::VALUE_TEXT,
102,570✔
2788
                logline_value_meta::internal_column{},
2789
                this);
20,222✔
2790
        }
20,222✔
2791
        vd->vd_meta.lvm_name = this->elf_level_field;
20,222✔
2792
        vd->vd_meta.lvm_kind = value_kind_t::VALUE_TEXT;
20,222✔
2793
        vd->vd_meta.lvm_column = logline_value_meta::internal_column{};
×
2794
        vd->vd_internal = true;
×
2795
    }
40,444✔
2796
    if (!this->elf_body_field.empty()) {
2797
        auto& vd = this->elf_value_defs[this->elf_body_field];
20,222✔
2798
        if (vd.get() == nullptr) {
20,222✔
2799
            vd = std::make_shared<external_log_format::value_def>(
20,222✔
2800
                this->elf_body_field,
20,222✔
2801
                value_kind_t::VALUE_TEXT,
2802
                logline_value_meta::internal_column{},
34,190✔
2803
                this);
34,190✔
2804
        }
34,190✔
2805
        vd->vd_meta.lvm_name = this->elf_body_field;
27,118✔
2806
        vd->vd_meta.lvm_kind = value_kind_t::VALUE_TEXT;
27,118✔
2807
        vd->vd_meta.lvm_column = logline_value_meta::internal_column{};
×
2808
        vd->vd_internal = true;
×
2809
    }
54,236✔
2810

2811
    if (!this->lf_timestamp_format.empty()) {
34,190✔
2812
        this->lf_timestamp_format.push_back(nullptr);
34,190✔
2813
    }
34,190✔
2814
    for (auto iter = this->elf_patterns.begin();
34,190✔
2815
         iter != this->elf_patterns.end();
2816
         ++iter)
2817
    {
34,190✔
2818
        pattern& pat = *iter->second;
3,222✔
2819

2820
        if (pat.p_pcre.pp_value == nullptr) {
34,190✔
2821
            continue;
99,921✔
2822
        }
65,731✔
2823

2824
        if (pat.p_module_format) {
65,731✔
2825
            this->elf_has_module_format = true;
2826
        }
65,731✔
2827

×
2828
        for (auto named_cap : pat.p_pcre.pp_value->get_named_captures()) {
2829
            const intern_string_t name
2830
                = intern_string::lookup(named_cap.get_name());
65,731✔
2831

1,881✔
2832
            if (name == this->lf_timestamp_field) {
2833
                pat.p_timestamp_field_index = named_cap.get_index();
2834
            }
592,270✔
2835
            if (name == this->lf_time_field) {
2836
                pat.p_time_field_index = named_cap.get_index();
526,539✔
2837
            }
2838
            if (name == this->elf_level_field) {
526,539✔
2839
                pat.p_level_field_index = named_cap.get_index();
63,849✔
2840
            }
2841
            if (name == this->elf_module_id_field) {
526,539✔
2842
                pat.p_module_field_index = named_cap.get_index();
627✔
2843
            }
2844
            if (name == this->elf_opid_field) {
526,539✔
2845
                pat.p_opid_field_index = named_cap.get_index();
50,508✔
2846
            }
2847
            if (name == this->elf_subid_field) {
526,539✔
2848
                pat.p_subid_field_index = named_cap.get_index();
5,016✔
2849
            }
2850
            if (name == this->elf_body_field) {
526,539✔
2851
                pat.p_body_field_index = named_cap.get_index();
20,064✔
2852
            }
2853

526,539✔
2854
            auto value_iter = this->elf_value_defs.find(name);
9,405✔
2855
            if (value_iter != this->elf_value_defs.end()) {
2856
                auto vd = value_iter->second;
526,539✔
2857
                indexed_value_def ivd;
56,325✔
2858

2859
                ivd.ivd_index = named_cap.get_index();
2860
                if (!vd->vd_unit_field.empty()) {
526,539✔
2861
                    ivd.ivd_unit_field_index = pat.p_pcre.pp_value->name_index(
526,539✔
2862
                        vd->vd_unit_field.get());
515,166✔
2863
                } else {
515,166✔
2864
                    ivd.ivd_unit_field_index = -1;
2865
                }
515,166✔
2866
                if (!vd->vd_internal
515,166✔
2867
                    && !vd->vd_meta.lvm_column
1,254✔
2868
                            .is<logline_value_meta::table_column>())
627✔
2869
                {
2870
                    vd->vd_meta.lvm_column = logline_value_meta::table_column{
514,539✔
2871
                        this->elf_column_count++};
2872
                }
515,166✔
2873
                ivd.ivd_value_def = vd;
888,579✔
2874
                pat.p_value_by_index.push_back(ivd);
373,413✔
2875
            }
2876
            pat.p_value_name_to_index[name] = named_cap.get_index();
207,258✔
2877
        }
207,258✔
2878

2879
        stable_sort(pat.p_value_by_index.begin(), pat.p_value_by_index.end());
515,166✔
2880

515,166✔
2881
        for (int lpc = 0; lpc < (int) pat.p_value_by_index.size(); lpc++) {
515,166✔
2882
            auto& ivd = pat.p_value_by_index[lpc];
526,539✔
2883
            auto vd = ivd.ivd_value_def;
2884

2885
            if (!vd->vd_meta.lvm_foreign_key && !vd->vd_meta.lvm_identifier) {
65,731✔
2886
                switch (vd->vd_meta.lvm_kind) {
2887
                    case value_kind_t::VALUE_INTEGER:
580,897✔
2888
                    case value_kind_t::VALUE_FLOAT:
515,166✔
2889
                        pat.p_numeric_value_indexes.push_back(lpc);
515,166✔
2890
                        break;
2891
                    default:
515,166✔
2892
                        break;
257,295✔
2893
                }
38,961✔
2894
            }
2895
        }
38,961✔
2896

38,961✔
2897
        if (!pat.p_module_format && pat.p_timestamp_field_index == -1) {
218,334✔
2898
            errors.emplace_back(
218,334✔
2899
                lnav::console::user_message::error(
2900
                    attr_line_t("invalid pattern: ")
2901
                        .append_quoted(lnav::roles::symbol(pat.p_config_path)))
515,166✔
2902
                    .with_reason("no timestamp capture found in the pattern")
2903
                    .with_snippets(this->get_snippets())
65,731✔
2904
                    .with_help("all log messages need a timestamp"));
1✔
2905
        }
×
2906

2✔
2907
        if (!this->elf_level_field.empty() && pat.p_level_field_index == -1) {
2✔
2908
            log_warning("%s:level field '%s' not found in pattern",
2✔
2909
                        pat.p_config_path.c_str(),
2✔
2910
                        this->elf_level_field.get());
2911
        }
2912
        if (!this->elf_module_id_field.empty()
2913
            && pat.p_module_field_index == -1)
65,731✔
2914
        {
15,223✔
2915
            log_warning("%s:module field '%s' not found in pattern",
2916
                        pat.p_config_path.c_str(),
2917
                        this->elf_module_id_field.get());
2918
        }
65,731✔
2919
        if (!this->elf_body_field.empty() && pat.p_body_field_index == -1) {
65,731✔
2920
            log_warning("%s:body field '%s' not found in pattern",
2921
                        pat.p_config_path.c_str(),
×
2922
                        this->elf_body_field.get());
2923
        }
2924

2925
        this->elf_pattern_order.push_back(iter->second);
65,731✔
2926
    }
9,406✔
2927

2928
    if (this->elf_type != elf_type_t::ELF_TYPE_TEXT) {
2929
        if (!this->elf_patterns.empty()) {
2930
            errors.emplace_back(
2931
                lnav::console::user_message::error(
65,731✔
2932
                    attr_line_t()
2933
                        .append_quoted(
2934
                            lnav::roles::symbol(this->elf_name.to_string()))
34,190✔
2935
                        .append(" is not a valid log format"))
4,912✔
2936
                    .with_reason("structured logs cannot have regexes")
1✔
2937
                    .with_snippets(this->get_snippets()));
×
2938
        }
2✔
2939
        if (this->elf_type == elf_type_t::ELF_TYPE_JSON) {
1✔
2940
            this->lf_multiline = true;
2✔
2941
            this->lf_structured = true;
1✔
2942
            this->lf_formatted_lines = true;
2✔
2943
            this->jlf_parse_context
2✔
2944
                = std::make_shared<yajlpp_parse_context>(this->elf_name);
2945
            this->jlf_yajl_handle.reset(
4,912✔
2946
                yajl_alloc(&this->jlf_parse_context->ypc_callbacks,
4,912✔
2947
                           nullptr,
4,912✔
2948
                           this->jlf_parse_context.get()),
4,912✔
2949
                yajl_handle_deleter());
2950
            yajl_config(
4,912✔
2951
                this->jlf_yajl_handle.get(), yajl_dont_validate_strings, 1);
4,912✔
2952
        }
4,912✔
2953
    } else {
2954
        if (this->elf_patterns.empty()) {
4,912✔
2955
            errors.emplace_back(lnav::console::user_message::error(
2956
                                    attr_line_t()
4,912✔
2957
                                        .append_quoted(lnav::roles::symbol(
2958
                                            this->elf_name.to_string()))
2959
                                        .append(" is not a valid log format"))
2960
                                    .with_reason("no regexes specified")
29,278✔
2961
                                    .with_snippets(this->get_snippets()));
1✔
2962
        }
2✔
2963
    }
2✔
2964

2✔
2965
    stable_sort(this->elf_level_pairs.begin(), this->elf_level_pairs.end());
1✔
2966

2✔
2967
    {
2✔
2968
        safe::WriteAccess<safe_format_header_expressions> hexprs(
2969
            format_header_exprs);
2970

2971
        if (hexprs->e_db.in() == nullptr) {
34,190✔
2972
            if (sqlite3_open(":memory:", hexprs->e_db.out()) != SQLITE_OK) {
2973
                log_error("unable to open memory DB");
2974
                return;
2975
            }
34,190✔
2976
            register_sqlite_funcs(hexprs->e_db.in(), sqlite_registration_funcs);
2977
        }
34,190✔
2978

627✔
2979
        for (const auto& hpair : this->elf_converter.c_header.h_exprs.he_exprs)
×
2980
        {
×
2981
            auto stmt_str
2982
                = fmt::format(FMT_STRING("SELECT 1 WHERE {}"), hpair.second);
627✔
2983
            compiled_header_expr che;
2984

2985
            log_info("preparing file-format header expression: %s",
35,444✔
2986
                     stmt_str.c_str());
2987
            auto retcode = sqlite3_prepare_v2(hexprs->e_db.in(),
2988
                                              stmt_str.c_str(),
3,762✔
2989
                                              stmt_str.size(),
1,254✔
2990
                                              che.che_stmt.out(),
1,254✔
2991
                                              nullptr);
1,254✔
2992
            if (retcode != SQLITE_OK) {
2993
                auto sql_al = attr_line_t(hpair.second)
1,254✔
2994
                                  .with_attr_for_all(SA_PREFORMATTED.value())
2995
                                  .with_attr_for_all(
2,508✔
2996
                                      VC_ROLE.value(role_t::VCR_QUOTED_CODE))
2997
                                  .move();
1,254✔
2998
                readline_sqlite_highlighter(sql_al, std::nullopt);
2999
                intern_string_t watch_expr_path = intern_string::lookup(
3000
                    fmt::format(FMT_STRING("/{}/converter/header/expr/{}"),
1,254✔
3001
                                this->elf_name,
×
3002
                                hpair.first));
×
3003
                auto snippet = lnav::console::snippet::from(
×
3004
                    source_location(watch_expr_path), sql_al);
×
3005

×
3006
                auto um = lnav::console::user_message::error(
×
3007
                              "SQL expression is invalid")
3008
                              .with_reason(sqlite3_errmsg(hexprs->e_db.in()))
×
3009
                              .with_snippet(snippet)
×
3010
                              .move();
×
3011

×
3012
                errors.emplace_back(um);
×
3013
                continue;
3014
            }
×
3015

3016
            hexprs->e_header_exprs[this->elf_name][hpair.first]
×
3017
                = std::move(che);
3018
        }
×
3019

×
3020
        if (!this->elf_converter.c_header.h_exprs.he_exprs.empty()
×
3021
            && this->elf_converter.c_command.pp_value.empty())
3022
        {
×
3023
            auto um = lnav::console::user_message::error(
×
3024
                          "A command is required when a converter is defined")
3025
                          .with_help(
3026
                              "The converter command transforms the file "
1,254✔
3027
                              "into a format that can be consumed by lnav")
2,508✔
3028
                          .with_snippets(this->get_snippets())
1,254✔
3029
                          .move();
3030
            errors.emplace_back(um);
34,190✔
3031
        }
34,190✔
3032
    }
3033

×
3034
    for (auto& vd : this->elf_value_def_order) {
3035
        std::vector<std::string>::iterator act_iter;
×
3036

3037
        vd->vd_meta.lvm_format = this;
3038
        if (!vd->vd_internal
×
3039
            && !vd->vd_meta.lvm_column.is<logline_value_meta::table_column>())
×
3040
        {
×
3041
            vd->vd_meta.lvm_column
3042
                = logline_value_meta::table_column{this->elf_column_count++};
34,190✔
3043
        }
3044

326,389✔
3045
        if (vd->vd_meta.lvm_kind == value_kind_t::VALUE_UNKNOWN) {
292,199✔
3046
            vd->vd_meta.lvm_kind = value_kind_t::VALUE_TEXT;
3047
        }
292,199✔
3048

292,199✔
3049
        if (this->elf_type == elf_type_t::ELF_TYPE_TEXT) {
292,199✔
3050
            std::set<std::string> available_captures;
3051

72,853✔
3052
            bool found_in_pattern = false;
72,853✔
3053
            for (const auto& pat : this->elf_patterns) {
3054
                if (pat.second->p_pcre.pp_value == nullptr) {
3055
                    continue;
292,199✔
3056
                }
×
3057

3058
                auto cap_index = pat.second->p_pcre.pp_value->name_index(
3059
                    vd->vd_meta.lvm_name.get());
292,199✔
3060
                if (cap_index >= 0) {
215,409✔
3061
                    found_in_pattern = true;
3062
                    break;
215,409✔
3063
                }
348,333✔
3064

348,333✔
3065
                for (auto named_cap :
×
3066
                     pat.second->p_pcre.pp_value->get_named_captures())
3067
                {
3068
                    available_captures.insert(named_cap.get_name().to_string());
696,666✔
3069
                }
348,333✔
3070
            }
348,333✔
3071
            if (!found_in_pattern) {
215,409✔
3072
                auto notes
215,409✔
3073
                    = attr_line_t("the following captures are available:\n  ")
3074
                          .join(available_captures,
3075
                                VC_ROLE.value(role_t::VCR_SYMBOL),
132,924✔
3076
                                ", ")
1,278,453✔
3077
                          .move();
3078
                errors.emplace_back(
1,012,605✔
3079
                    lnav::console::user_message::warning(
3080
                        attr_line_t("invalid value ")
3081
                            .append_quoted(lnav::roles::symbol(
215,409✔
3082
                                fmt::format(FMT_STRING("/{}/value/{}"),
3083
                                            this->elf_name,
×
3084
                                            vd->vd_meta.lvm_name.get()))))
×
3085
                        .with_reason(
×
3086
                            attr_line_t("no patterns have a capture named ")
3087
                                .append_quoted(vd->vd_meta.lvm_name.get()))
×
3088
                        .with_note(notes)
×
3089
                        .with_snippets(this->get_snippets())
×
3090
                        .with_help("values are populated from captures in "
×
3091
                                   "patterns, so at least one pattern must "
×
3092
                                   "have a capture with this value name"));
×
3093
            }
×
3094
        }
×
3095

×
3096
        for (act_iter = vd->vd_action_list.begin();
×
3097
             act_iter != vd->vd_action_list.end();
×
3098
             ++act_iter)
×
3099
        {
×
3100
            if (this->lf_action_defs.find(*act_iter)
×
3101
                == this->lf_action_defs.end())
×
3102
            {
3103
#if 0
3104
                errors.push_back("error:" + this->elf_name.to_string() + ":"
3105
                                 + vd->vd_meta.lvm_name.get()
3106
                                 + ": cannot find action -- " + (*act_iter));
215,409✔
3107
#endif
3108
            }
292,199✔
3109
        }
292,826✔
3110

627✔
3111
        vd->set_rewrite_src_name();
3112
    }
627✔
3113

1,254✔
3114
    for (const auto& td_pair : this->lf_tag_defs) {
3115
        const auto& td = td_pair.second;
3116

3117
        if (td->ftd_pattern.pp_value == nullptr
3118
            || td->ftd_pattern.pp_value->get_pattern().empty())
3119
        {
3120
            errors.emplace_back(
3121
                lnav::console::user_message::error(
3122
                    attr_line_t("invalid tag definition ")
3123
                        .append_quoted(lnav::roles::symbol(
292,199✔
3124
                            fmt::format(FMT_STRING("/{}/tags/{}"),
3125
                                        this->elf_name,
3126
                                        td_pair.first))))
36,158✔
3127
                    .with_reason(
1,968✔
3128
                        "tag definitions must have a non-empty pattern")
3129
                    .with_snippets(this->get_snippets()));
1,968✔
3130
        }
1,968✔
3131
    }
3132

×
3133
    for (const auto& opid_desc_pair : *this->lf_opid_description_def) {
×
3134
        for (const auto& opid_desc : *opid_desc_pair.second.od_descriptors) {
×
3135
            auto iter = this->elf_value_defs.find(opid_desc.od_field.pp_value);
×
3136
            if (iter == this->elf_value_defs.end()) {
×
3137
                errors.emplace_back(
×
3138
                    lnav::console::user_message::error(
×
3139
                        attr_line_t("invalid opid description field ")
×
3140
                            .append_quoted(lnav::roles::symbol(
×
3141
                                opid_desc.od_field.pp_path.to_string())))
×
3142
                        .with_reason(
3143
                            attr_line_t("unknown value name ")
×
3144
                                .append_quoted(opid_desc.od_field.pp_value))
3145
                        .with_snippets(this->get_snippets()));
3146
            } else {
3147
                this->lf_desc_fields.insert(iter->first);
42,968✔
3148
            }
20,691✔
3149
        }
11,913✔
3150
    }
11,913✔
3151

×
3152
    for (const auto& subid_desc_pair : *this->lf_subid_description_def) {
×
3153
        for (const auto& subid_desc : *subid_desc_pair.second.od_descriptors) {
×
3154
            auto iter = this->elf_value_defs.find(subid_desc.od_field.pp_value);
×
3155
            if (iter == this->elf_value_defs.end()) {
×
3156
                errors.emplace_back(
×
3157
                    lnav::console::user_message::error(
×
3158
                        attr_line_t("invalid subid description field ")
×
3159
                            .append_quoted(lnav::roles::symbol(
×
3160
                                subid_desc.od_field.pp_path.to_string())))
3161
                        .with_reason(
11,913✔
3162
                            attr_line_t("unknown value name ")
3163
                                .append_quoted(subid_desc.od_field.pp_value))
3164
                        .with_snippets(this->get_snippets()));
3165
            } else {
3166
                this->lf_desc_fields.insert(iter->first);
34,817✔
3167
            }
1,254✔
3168
        }
627✔
3169
    }
627✔
3170

×
3171
    if (this->elf_type == elf_type_t::ELF_TYPE_TEXT
×
3172
        && this->elf_samples.empty())
×
3173
    {
×
3174
        errors.emplace_back(
×
3175
            lnav::console::user_message::error(
×
3176
                attr_line_t()
×
3177
                    .append_quoted(
×
3178
                        lnav::roles::symbol(this->elf_name.to_string()))
×
3179
                    .append(" is not a valid log format"))
3180
                .with_reason("log message samples must be included in a format "
627✔
3181
                             "definition")
3182
                .with_snippets(this->get_snippets()));
3183
    }
3184

3185
    if (!this->lf_subsecond_field.empty()
68,380✔
3186
        && !this->lf_subsecond_unit.has_value())
34,190✔
3187
    {
3188
        errors.emplace_back(
1✔
3189
            lnav::console::user_message::error(
×
3190
                attr_line_t()
2✔
3191
                    .append_quoted(
1✔
3192
                        lnav::roles::symbol(this->elf_name.to_string()))
2✔
3193
                    .append(" is not a valid log format"))
1✔
3194
                .with_reason(attr_line_t()
2✔
3195
                                 .append_quoted("subsecond-unit"_symbol)
3196
                                 .append(" must be set when ")
2✔
3197
                                 .append_quoted("subsecond-field"_symbol)
3198
                                 .append(" is used"))
3199
                .with_snippets(this->get_snippets()));
34,190✔
3200
    }
34,190✔
3201

3202
    for (size_t sample_index = 0; sample_index < this->elf_samples.size();
×
3203
         sample_index += 1)
×
3204
    {
×
3205
        auto& elf_sample = this->elf_samples[sample_index];
×
3206
        auto sample_lines
×
3207
            = string_fragment(elf_sample.s_line.pp_value).split_lines();
×
3208
        bool found = false;
×
3209

×
3210
        for (auto pat_iter = this->elf_pattern_order.begin();
×
3211
             pat_iter != this->elf_pattern_order.end();
×
3212
             ++pat_iter)
×
3213
        {
×
3214
            auto& pat = *(*pat_iter);
3215

3216
            if (!pat.p_pcre.pp_value) {
155,897✔
3217
                continue;
121,707✔
3218
            }
3219

121,707✔
3220
            auto md = pat.p_pcre.pp_value->create_match_data();
3221
            auto match_res = pat.p_pcre.pp_value->capture_from(sample_lines[0])
121,707✔
3222
                                 .into(md)
121,707✔
3223
                                 .matches()
3224
                                 .ignore_error();
121,707✔
3225
            if (!match_res) {
795,348✔
3226
                continue;
673,641✔
3227
            }
3228
            found = true;
673,641✔
3229

3230
            if (pat.p_module_format) {
673,641✔
3231
                continue;
553,188✔
3232
            }
3233

3234
            elf_sample.s_matched_regexes.insert(pat.p_name.to_string());
673,641✔
3235
            pat.p_matched_samples.insert(sample_index);
673,641✔
3236

673,641✔
3237
            if (pat.p_pcre.pp_value->name_index(this->lf_timestamp_field.get())
1,347,282✔
3238
                < 0)
673,641✔
3239
            {
673,641✔
3240
                attr_line_t notes;
551,934✔
3241
                bool first_note = true;
3242

121,707✔
3243
                if (pat.p_pcre.pp_value->get_capture_count() > 0) {
3244
                    notes.append("the following captures are available:\n  ");
121,707✔
3245
                }
1,254✔
3246
                for (auto named_cap : pat.p_pcre.pp_value->get_named_captures())
3247
                {
3248
                    if (!first_note) {
120,453✔
3249
                        notes.append(", ");
120,453✔
3250
                    }
3251
                    notes.append(
120,453✔
3252
                        lnav::roles::symbol(named_cap.get_name().to_string()));
120,453✔
3253
                    first_note = false;
3254
                }
×
3255
                errors.emplace_back(
×
3256
                    lnav::console::user_message::error(
3257
                        attr_line_t("invalid value for property ")
×
3258
                            .append_quoted(lnav::roles::symbol(
×
3259
                                fmt::format(FMT_STRING("/{}/timestamp-field"),
3260
                                            this->elf_name))))
×
3261
                        .with_reason(
3262
                            attr_line_t()
×
3263
                                .append_quoted(this->lf_timestamp_field)
×
3264
                                .append(" was not found in the pattern at ")
3265
                                .append(lnav::roles::symbol(pat.p_config_path)))
×
3266
                        .with_note(notes)
×
3267
                        .with_snippets(this->get_snippets()));
×
3268
                continue;
3269
            }
×
3270

×
3271
            const auto ts_cap = md[pat.p_timestamp_field_index];
×
3272
            const auto level_cap = md[pat.p_level_field_index];
×
3273
            const char* const* custom_formats = this->get_timestamp_formats();
×
3274
            date_time_scanner dts;
×
3275
            struct timeval tv;
×
3276
            struct exttm tm;
×
3277

×
3278
            if (ts_cap && ts_cap->sf_begin == 0) {
×
3279
                pat.p_timestamp_end = ts_cap->sf_end;
×
3280
            }
×
3281
            const char* dts_scan_res = nullptr;
×
3282

×
3283
            if (ts_cap) {
×
3284
                dts_scan_res = dts.scan(
×
3285
                    ts_cap->data(), ts_cap->length(), custom_formats, &tm, tv);
3286
            }
3287
            if (dts_scan_res != nullptr) {
120,453✔
3288
                if (dts_scan_res != ts_cap->data() + ts_cap->length()) {
120,453✔
3289
                    auto match_len = dts_scan_res - ts_cap->data();
120,453✔
3290
                    auto notes = attr_line_t("the used timestamp format: ");
120,453✔
3291
                    if (custom_formats == nullptr) {
3292
                        notes.append(PTIMEC_FORMATS[dts.dts_fmt_lock].pf_fmt);
120,453✔
3293
                    } else {
3294
                        notes.append(custom_formats[dts.dts_fmt_lock]);
120,453✔
3295
                    }
74,334✔
3296
                    notes.append("\n  ")
3297
                        .append(ts_cap.value())
120,453✔
3298
                        .append("\n")
3299
                        .append(2 + match_len, ' ')
120,453✔
3300
                        .append("^ matched up to here"_snippet_border);
120,453✔
3301
                    auto um
120,453✔
3302
                        = lnav::console::user_message::warning(
3303
                              attr_line_t("timestamp was not fully matched: ")
120,453✔
3304
                                  .append_quoted(ts_cap.value()))
120,453✔
3305
                              .with_snippet(elf_sample.s_line.to_snippet())
×
3306
                              .with_note(notes)
×
3307
                              .move();
×
3308

×
3309
                    errors.emplace_back(um);
3310
                }
×
3311
            } else if (!ts_cap) {
3312
                errors.emplace_back(
×
3313
                    lnav::console::user_message::error(
×
3314
                        attr_line_t("invalid sample log message: ")
×
3315
                            .append(lnav::to_json(elf_sample.s_line.pp_value)))
×
3316
                        .with_reason(attr_line_t("timestamp was not captured"))
×
3317
                        .with_snippet(elf_sample.s_line.to_snippet())
3318
                        .with_help(attr_line_t(
×
3319
                            "A timestamp needs to be captured in order for a "
×
3320
                            "line to be recognized as a log message")));
×
3321
            } else {
×
3322
                attr_line_t notes;
×
3323

×
3324
                if (custom_formats == nullptr) {
3325
                    notes.append("the following built-in formats were tried:");
×
3326
                    for (int lpc = 0; PTIMEC_FORMATS[lpc].pf_fmt != nullptr;
3327
                         lpc++)
×
3328
                    {
×
3329
                        off_t off = 0;
×
3330

×
3331
                        PTIMEC_FORMATS[lpc].pf_func(
×
3332
                            &tm, ts_cap->data(), off, ts_cap->length());
×
3333
                        notes.append("\n  ")
×
3334
                            .append(ts_cap.value())
×
3335
                            .append("\n")
3336
                            .append(2 + off, ' ')
3337
                            .append("^ "_snippet_border)
3338
                            .append_quoted(
×
3339
                                lnav::roles::symbol(PTIMEC_FORMATS[lpc].pf_fmt))
3340
                            .append(" matched up to here"_snippet_border);
×
3341
                    }
×
3342
                } else {
×
3343
                    notes.append("the following custom formats were tried:");
3344
                    for (int lpc = 0; custom_formats[lpc] != nullptr; lpc++) {
3345
                        off_t off = 0;
×
3346

3347
                        ptime_fmt(custom_formats[lpc],
×
3348
                                  &tm,
×
3349
                                  ts_cap->data(),
×
3350
                                  off,
×
3351
                                  ts_cap->length());
×
3352
                        notes.append("\n  ")
×
3353
                            .append(ts_cap.value())
×
3354
                            .append("\n")
×
3355
                            .append(2 + off, ' ')
×
3356
                            .append("^ "_snippet_border)
×
3357
                            .append_quoted(
3358
                                lnav::roles::symbol(custom_formats[lpc]))
3359
                            .append(" matched up to here"_snippet_border);
×
3360
                    }
×
3361
                }
×
3362

3363
                errors.emplace_back(
×
3364
                    lnav::console::user_message::error(
3365
                        attr_line_t("invalid sample log message: ")
3366
                            .append(lnav::to_json(elf_sample.s_line.pp_value)))
3367
                        .with_reason(attr_line_t("unrecognized timestamp -- ")
×
3368
                                         .append(ts_cap.value()))
×
3369
                        .with_snippet(elf_sample.s_line.to_snippet())
×
3370
                        .with_note(notes)
×
3371
                        .with_help(attr_line_t("If the timestamp format is not "
×
3372
                                               "supported by default, you can "
×
3373
                                               "add a custom format with the ")
×
3374
                                       .append_quoted("timestamp-format"_symbol)
×
3375
                                       .append(" property")));
×
3376
            }
3377

3378
            auto level = this->convert_level(
3379
                level_cap.value_or(string_fragment::invalid()), nullptr);
×
3380

×
3381
            if (elf_sample.s_level != LEVEL_UNKNOWN
×
3382
                && elf_sample.s_level != level)
×
3383
            {
×
3384
                attr_line_t note_al;
×
3385

×
3386
                note_al.append("matched regex = ")
×
3387
                    .append(lnav::roles::symbol(pat.p_name.to_string()))
×
3388
                    .append("\n")
3389
                    .append("captured level = ")
3390
                    .append_quoted(level_cap->to_string());
×
3391
                if (level_cap && !this->elf_level_patterns.empty()) {
×
3392
                    static thread_local auto md
3393
                        = lnav::pcre2pp::match_data::unitialized();
3394

120,453✔
3395
                    note_al.append("\nlevel regular expression match results:");
120,453✔
3396
                    for (const auto& level_pattern : this->elf_level_patterns) {
3397
                        attr_line_t regex_al = level_pattern.second.lp_pcre
120,453✔
3398
                                                   .pp_value->get_pattern();
14,682✔
3399
                        lnav::snippets::regex_highlighter(
3400
                            regex_al,
×
3401
                            -1,
3402
                            line_range{0, (int) regex_al.length()});
×
3403
                        note_al.append("\n  ")
×
3404
                            .append(
×
3405
                                lnav::roles::symbol(level_pattern.second.lp_pcre
×
3406
                                                        .pp_path.to_string()))
×
3407
                            .append(" = ")
×
3408
                            .append(regex_al)
3409
                            .append("\n    ");
×
3410
                        auto match_res = level_pattern.second.lp_pcre.pp_value
3411
                                             ->capture_from(level_cap.value())
×
3412
                                             .into(md)
×
3413
                                             .matches(PCRE2_NO_UTF_CHECK)
3414
                                             .ignore_error();
×
3415
                        if (!match_res) {
×
3416
                            note_al.append(lnav::roles::warning("no match"));
3417
                            continue;
×
3418
                        }
×
3419

×
3420
                        note_al.append(level_cap.value())
×
3421
                            .append("\n    ")
×
3422
                            .append(md.leading().length(), ' ')
×
3423
                            .append("^"_snippet_border);
×
3424
                        if (match_res->f_all.length() > 2) {
×
3425
                            note_al.append(
×
3426
                                lnav::roles::snippet_border(std::string(
×
3427
                                    match_res->f_all.length() - 2, '-')));
×
3428
                        }
×
3429
                        if (match_res->f_all.length() > 1) {
×
3430
                            note_al.append("^"_snippet_border);
×
3431
                        }
×
3432
                    }
×
3433
                }
×
3434
                auto um
3435
                    = lnav::console::user_message::error(
3436
                          attr_line_t("invalid sample log message: ")
×
3437
                              .append(
×
3438
                                  lnav::to_json(elf_sample.s_line.pp_value)))
×
3439
                          .with_reason(
×
3440
                              attr_line_t()
×
3441
                                  .append_quoted(
×
3442
                                      lnav::roles::symbol(level_names[level]))
×
3443
                                  .append(" does not match the expected "
×
3444
                                          "level of ")
3445
                                  .append_quoted(lnav::roles::symbol(
×
3446
                                      level_names[elf_sample.s_level])))
×
3447
                          .with_snippet(elf_sample.s_line.to_snippet())
3448
                          .with_note(note_al)
3449
                          .move();
3450
                if (!this->elf_level_patterns.empty()) {
3451
                    um.with_help(
×
3452
                        attr_line_t("Level regexes are not anchored to the "
×
3453
                                    "start/end of the string.  Prepend ")
×
3454
                            .append_quoted("^"_symbol)
×
3455
                            .append(" to the expression to match from the "
×
3456
                                    "start of the string and append ")
×
3457
                            .append_quoted("$"_symbol)
×
3458
                            .append(" to match up to the end of the string."));
×
3459
                }
×
3460
                errors.emplace_back(um);
3461
            }
×
3462

×
3463
            {
×
3464
                auto full_match_res
×
3465
                    = pat.p_pcre.pp_value
×
3466
                          ->capture_from(elf_sample.s_line.pp_value)
×
3467
                          .into(md)
×
3468
                          .matches()
×
3469
                          .ignore_error();
3470
                if (!full_match_res) {
×
3471
                    attr_line_t regex_al = pat.p_pcre.pp_value->get_pattern();
×
3472
                    lnav::snippets::regex_highlighter(
3473
                        regex_al, -1, line_range{0, (int) regex_al.length()});
×
3474
                    errors.emplace_back(
×
3475
                        lnav::console::user_message::error(
3476
                            attr_line_t("invalid pattern: ")
×
3477
                                .append_quoted(lnav::roles::symbol(
3478
                                    pat.p_name.to_string())))
3479
                            .with_reason("pattern does not match entire "
3480
                                         "multiline sample message")
3481
                            .with_snippet(elf_sample.s_line.to_snippet())
120,453✔
3482
                            .with_note(attr_line_t()
120,453✔
3483
                                           .append(lnav::roles::symbol(
120,453✔
3484
                                               pat.p_name.to_string()))
240,906✔
3485
                                           .append(" = ")
120,453✔
3486
                                           .append(regex_al))
120,453✔
3487
                            .with_help(
×
3488
                                attr_line_t("use ").append_quoted(".*").append(
×
3489
                                    " to match new-lines")));
×
3490
                } else if (static_cast<size_t>(full_match_res->f_all.length())
×
3491
                           != elf_sample.s_line.pp_value.length())
×
3492
                {
×
3493
                    attr_line_t regex_al = pat.p_pcre.pp_value->get_pattern();
×
3494
                    lnav::snippets::regex_highlighter(
×
3495
                        regex_al, -1, line_range{0, (int) regex_al.length()});
×
3496
                    auto match_length
3497
                        = static_cast<size_t>(full_match_res->f_all.length());
×
3498
                    attr_line_t sample_al = elf_sample.s_line.pp_value;
×
3499
                    sample_al.append("\n")
×
3500
                        .append(match_length, ' ')
×
3501
                        .append("^ matched up to here"_error)
×
3502
                        .with_attr_for_all(
×
3503
                            VC_ROLE.value(role_t::VCR_QUOTED_CODE));
3504
                    auto sample_snippet = lnav::console::snippet::from(
×
3505
                        elf_sample.s_line.pp_location, sample_al);
3506
                    errors.emplace_back(
120,453✔
3507
                        lnav::console::user_message::error(
120,453✔
3508
                            attr_line_t("invalid pattern: ")
3509
                                .append_quoted(lnav::roles::symbol(
×
3510
                                    pat.p_name.to_string())))
×
3511
                            .with_reason("pattern does not match entire "
×
3512
                                         "message")
3513
                            .with_snippet(sample_snippet)
×
3514
                            .with_note(attr_line_t()
×
3515
                                           .append(lnav::roles::symbol(
×
3516
                                               pat.p_name.to_string()))
×
3517
                                           .append(" = ")
×
3518
                                           .append(regex_al))
×
3519
                            .with_help("update the regular expression to fully "
×
3520
                                       "capture the sample message"));
3521
                }
×
3522
            }
×
3523
        }
×
3524

×
3525
        if (!found && !this->elf_pattern_order.empty()) {
×
3526
            std::vector<std::pair<ssize_t, intern_string_t>> partial_indexes;
×
3527
            attr_line_t notes;
×
3528
            size_t max_name_width = 0;
3529

×
3530
            for (const auto& pat_iter : this->elf_pattern_order) {
×
3531
                auto& pat = *pat_iter;
×
3532

×
3533
                if (!pat.p_pcre.pp_value) {
×
3534
                    continue;
×
3535
                }
3536

3537
                partial_indexes.emplace_back(
3538
                    pat.p_pcre.pp_value->match_partial(sample_lines[0]),
3539
                    pat.p_name);
673,641✔
3540
                max_name_width = std::max(max_name_width, pat.p_name.size());
3541
            }
121,707✔
3542
            for (const auto& line_frag : sample_lines) {
×
3543
                auto src_line = attr_line_t(line_frag.to_string());
×
3544
                if (!line_frag.endswith("\n")) {
×
3545
                    src_line.append("\n");
3546
                }
×
3547
                src_line.with_attr_for_all(
×
3548
                    VC_ROLE.value(role_t::VCR_QUOTED_CODE));
3549
                notes.append("   ").append(src_line);
×
3550
                for (auto& part_pair : partial_indexes) {
×
3551
                    if (part_pair.first >= 0
3552
                        && part_pair.first < line_frag.length())
3553
                    {
×
3554
                        notes.append("   ")
×
3555
                            .append(part_pair.first, ' ')
×
3556
                            .append("^ "_snippet_border)
×
3557
                            .append(lnav::roles::symbol(
3558
                                part_pair.second.to_string()))
×
3559
                            .append(" matched up to here"_snippet_border)
×
3560
                            .append("\n");
×
3561
                    }
×
3562
                    part_pair.first -= line_frag.length();
3563
                }
×
3564
            }
×
3565
            notes.add_header(
×
3566
                "the following shows how each pattern matched this sample:\n");
×
3567

×
3568
            attr_line_t regex_note;
×
3569
            for (const auto& pat_iter : this->elf_pattern_order) {
3570
                if (!pat_iter->p_pcre.pp_value) {
×
3571
                    regex_note
×
3572
                        .append(
×
3573
                            lnav::roles::symbol(fmt::format(FMT_STRING("{:{}}"),
×
3574
                                                            pat_iter->p_name,
×
3575
                                                            max_name_width)))
×
3576
                        .append(" is invalid");
×
3577
                    continue;
3578
                }
×
3579

3580
                attr_line_t regex_al = pat_iter->p_pcre.pp_value->get_pattern();
3581
                lnav::snippets::regex_highlighter(
×
3582
                    regex_al, -1, line_range{0, (int) regex_al.length()});
3583

3584
                regex_note
×
3585
                    .append(lnav::roles::symbol(fmt::format(
×
3586
                        FMT_STRING("{:{}}"), pat_iter->p_name, max_name_width)))
×
3587
                    .append(" = ")
3588
                    .append_quoted(regex_al)
×
3589
                    .append("\n");
×
3590
            }
×
3591

×
3592
            errors.emplace_back(
×
3593
                lnav::console::user_message::error(
3594
                    attr_line_t("invalid sample log message: ")
×
3595
                        .append(lnav::to_json(elf_sample.s_line.pp_value)))
×
3596
                    .with_reason("sample does not match any patterns")
3597
                    .with_snippet(elf_sample.s_line.to_snippet())
3598
                    .with_note(notes.rtrim())
×
3599
                    .with_note(regex_note));
×
3600
        }
×
3601
    }
3602

3603
    if (!this->elf_samples.empty()) {
×
3604
        for (const auto& elf_sample : this->elf_samples) {
×
3605
            if (elf_sample.s_matched_regexes.size() <= 1) {
×
3606
                continue;
×
3607
            }
×
3608

×
3609
            errors.emplace_back(
×
3610
                lnav::console::user_message::warning(
3611
                    attr_line_t("invalid log format: ")
3612
                        .append_quoted(
×
3613
                            lnav::roles::symbol(this->elf_name.to_string())))
×
3614
                    .with_reason(
×
3615
                        attr_line_t(
×
3616
                            "sample is matched by more than one regex: ")
×
3617
                            .join(elf_sample.s_matched_regexes,
×
3618
                                  VC_ROLE.value(role_t::VCR_SYMBOL),
×
3619
                                  ", "))
3620
                    .with_snippet(lnav::console::snippet::from(
3621
                        elf_sample.s_line.pp_location,
121,707✔
3622
                        attr_line_t().append(lnav::roles::quoted_code(
3623
                            elf_sample.s_line.pp_value))))
34,190✔
3624
                    .with_help("log format regexes must match a single type "
150,984✔
3625
                               "of log message"));
121,707✔
3626
        }
121,707✔
3627

3628
        for (const auto& pat : this->elf_pattern_order) {
3629
            if (pat->p_module_format) {
×
3630
                continue;
×
3631
            }
×
3632

×
3633
            if (pat->p_matched_samples.empty()) {
×
3634
                errors.emplace_back(
×
3635
                    lnav::console::user_message::warning(
×
3636
                        attr_line_t("invalid pattern: ")
3637
                            .append_quoted(
×
3638
                                lnav::roles::symbol(pat->p_config_path)))
×
3639
                        .with_reason("pattern does not match any samples")
3640
                        .with_snippet(lnav::console::snippet::from(
×
3641
                            pat->p_pcre.pp_location, ""))
3642
                        .with_help(
×
3643
                            "every pattern should have at least one sample "
×
3644
                            "that it matches"));
3645
            }
3646
        }
3647
    }
3648

95,007✔
3649
    size_t value_def_index = 0;
65,730✔
3650
    for (auto& elf_value_def : this->elf_value_def_order) {
1,881✔
3651
        elf_value_def->vd_meta.lvm_values_index
3652
            = std::make_optional(value_def_index++);
3653

63,849✔
3654
        if (elf_value_def->vd_meta.lvm_foreign_key
×
3655
            || elf_value_def->vd_meta.lvm_identifier)
×
3656
        {
×
3657
            continue;
×
3658
        }
×
3659

×
3660
        switch (elf_value_def->vd_meta.lvm_kind) {
×
3661
            case value_kind_t::VALUE_INTEGER:
×
3662
            case value_kind_t::VALUE_FLOAT:
3663
                this->elf_numeric_value_defs.push_back(elf_value_def);
3664
                break;
3665
            default:
3666
                break;
3667
        }
3668
    }
3669

34,190✔
3670
    int format_index = 0;
326,389✔
3671
    for (auto iter = this->jlf_line_format.begin();
292,199✔
3672
         iter != this->jlf_line_format.end();
292,199✔
3673
         ++iter, format_index++)
3674
    {
292,199✔
3675
        static const intern_string_t ts
292,199✔
3676
            = intern_string::lookup("__timestamp__");
3677
        static const intern_string_t level_field
167,913✔
3678
            = intern_string::lookup("__level__");
3679
        json_format_element& jfe = *iter;
3680

124,286✔
3681
        if (startswith(jfe.jfe_value.pp_value.get(), "/")) {
39,762✔
3682
            jfe.jfe_value.pp_value
3683
                = intern_string::lookup(jfe.jfe_value.pp_value.get() + 1);
39,762✔
3684
        }
39,762✔
3685
        if (!jfe.jfe_ts_format.empty()) {
84,524✔
3686
            if (!jfe.jfe_value.pp_value.empty() && jfe.jfe_value.pp_value != ts)
84,524✔
3687
            {
3688
                log_warning(
3689
                    "%s:line-format[%d]:ignoring field '%s' since "
3690
                    "timestamp-format was used",
34,190✔
3691
                    this->elf_name.get(),
34,190✔
3692
                    format_index,
98,037✔
3693
                    jfe.jfe_value.pp_value.get());
63,847✔
3694
            }
3695
            jfe.jfe_value.pp_value = ts;
3696
        }
63,847✔
3697

3698
        switch (jfe.jfe_type) {
63,847✔
3699
            case json_log_field::VARIABLE: {
63,847✔
3700
                auto vd_iter
3701
                    = this->elf_value_defs.find(jfe.jfe_value.pp_value);
63,847✔
3702
                if (jfe.jfe_value.pp_value == ts) {
3703
                    this->elf_value_defs[this->lf_timestamp_field]
87✔
3704
                        ->vd_meta.lvm_hidden
3705
                        = true;
63,847✔
3706
                } else if (jfe.jfe_value.pp_value == level_field) {
87✔
3707
                    this->elf_value_defs[this->elf_level_field]
3708
                        ->vd_meta.lvm_hidden
87✔
3709
                        = true;
3710
                } else if (vd_iter == this->elf_value_defs.end()) {
3711
                    errors.emplace_back(
3712
                        lnav::console::user_message::error(
3713
                            attr_line_t("invalid line format element ")
3714
                                .append_quoted(lnav::roles::symbol(fmt::format(
3715
                                    FMT_STRING("/{}/line-format/{}/field"),
87✔
3716
                                    this->elf_name,
3717
                                    format_index))))
3718
                            .with_reason(
63,847✔
3719
                                attr_line_t()
44,098✔
3720
                                    .append_quoted(jfe.jfe_value.pp_value)
3721
                                    .append(" is not a defined value"))
44,098✔
3722
                            .with_snippet(jfe.jfe_value.to_snippet()));
44,098✔
3723
                } else {
2,769✔
3724
                    switch (vd_iter->second->vd_meta.lvm_kind) {
2,769✔
3725
                        case value_kind_t::VALUE_INTEGER:
2,769✔
3726
                        case value_kind_t::VALUE_FLOAT:
41,329✔
3727
                            if (jfe.jfe_align
1,881✔
3728
                                == json_format_element::align_t::NONE)
1,881✔
3729
                            {
1,881✔
3730
                                jfe.jfe_align
39,448✔
3731
                                    = json_format_element::align_t::RIGHT;
1✔
3732
                            }
×
3733
                            break;
2✔
3734
                        default:
2✔
3735
                            break;
3✔
3736
                    }
1✔
3737
                }
1✔
3738
                break;
1✔
3739
            }
3740
            case json_log_field::CONSTANT:
2✔
3741
                this->jlf_line_format_init_count
2✔
3742
                    += std::count(jfe.jfe_default_value.begin(),
1✔
3743
                                  jfe.jfe_default_value.end(),
1✔
3744
                                  '\n');
2✔
3745
                break;
3746
            default:
39,447✔
3747
                break;
7,698✔
3748
        }
3749
    }
7,698✔
3750

3751
    for (auto& hd_pair : this->elf_highlighter_patterns) {
3752
        external_log_format::highlighter_def& hd = hd_pair.second;
3753
        auto fg = styling::color_unit::make_empty();
7,071✔
3754
        auto bg = styling::color_unit::make_empty();
3755
        text_attrs attrs;
7,698✔
3756

31,749✔
3757
        if (!hd.hd_color.pp_value.empty()) {
31,749✔
3758
            fg = styling::color_unit::from_str(hd.hd_color.pp_value)
3759
                     .unwrapOrElse([&](const auto& msg) {
3760
                         errors.emplace_back(
44,098✔
3761
                             lnav::console::user_message::error(
3762
                                 attr_line_t()
19,749✔
3763
                                     .append_quoted(hd.hd_color.pp_value)
3764
                                     .append(" is not a valid color value for "
19,749✔
3765
                                             "property ")
3766
                                     .append_quoted(lnav::roles::symbol(
19,749✔
3767
                                         hd.hd_color.pp_path.to_string())))
19,749✔
3768
                                 .with_reason(msg)
×
3769
                                 .with_snippet(hd.hd_color.to_snippet()));
×
3770
                         return styling::color_unit::make_empty();
3771
                     });
3772
        }
3773

34,538✔
3774
        if (!hd.hd_background_color.pp_value.empty()) {
348✔
3775
            bg = styling::color_unit::from_str(hd.hd_background_color.pp_value)
348✔
3776
                     .unwrapOrElse([&](const auto& msg) {
348✔
3777
                         errors.emplace_back(
348✔
3778
                             lnav::console::user_message::error(
3779
                                 attr_line_t()
348✔
3780
                                     .append_quoted(
696✔
3781
                                         hd.hd_background_color.pp_value)
696✔
3782
                                     .append(" is not a valid color value for "
×
3783
                                             "property ")
3784
                                     .append_quoted(lnav::roles::symbol(
×
3785
                                         hd.hd_background_color.pp_path
×
3786
                                             .to_string())))
×
3787
                                 .with_reason(msg)
3788
                                 .with_snippet(
×
3789
                                     hd.hd_background_color.to_snippet()));
3790
                         return styling::color_unit::make_empty();
×
3791
                     });
3792
        }
×
3793

348✔
3794
        if (hd.hd_underline) {
3795
            attrs.ta_attrs |= A_UNDERLINE;
3796
        }
348✔
3797
        if (hd.hd_blink) {
×
3798
            attrs.ta_attrs |= A_BLINK;
×
3799
        }
×
3800

3801
        if (hd.hd_pattern.pp_value != nullptr) {
×
3802
            this->lf_highlighters.emplace_back(hd.hd_pattern.pp_value);
×
3803
            this->lf_highlighters.back()
×
3804
                .with_name(hd_pair.first.to_string())
×
3805
                .with_format_name(this->elf_name)
3806
                .with_color(fg, bg)
×
3807
                .with_attrs(attrs);
3808
        }
3809
    }
×
3810

3811
    this->lf_value_stats.resize(this->elf_value_defs.size());
3812
}
×
3813

×
3814
void
3815
external_log_format::register_vtabs(
3816
    log_vtab_manager* vtab_manager,
348✔
3817
    std::vector<lnav::console::user_message>& errors)
87✔
3818
{
3819
    for (auto& elf_search_table : this->elf_search_tables) {
348✔
3820
        if (elf_search_table.second.std_pattern.pp_value == nullptr) {
×
3821
            continue;
3822
        }
3823

348✔
3824
        auto lst = std::make_shared<log_search_table>(
348✔
3825
            elf_search_table.second.std_pattern.pp_value,
348✔
3826
            elf_search_table.first);
696✔
3827
        lst->lst_format = this;
348✔
3828
        lst->lst_log_path_glob = elf_search_table.second.std_glob;
348✔
3829
        if (elf_search_table.second.std_level != LEVEL_UNKNOWN) {
348✔
3830
            lst->lst_log_level = elf_search_table.second.std_level;
3831
        }
348✔
3832
        auto errmsg = vtab_manager->register_vtab(lst);
3833
        if (!errmsg.empty()) {
34,190✔
3834
#if 0
3835
            errors.push_back("error:" + this->elf_name.to_string() + ":"
3836
                             + search_iter->first.to_string()
3837
                             + ":unable to register table -- " + errmsg);
28,015✔
3838
#endif
3839
        }
3840
    }
3841
}
33,724✔
3842

5,709✔
3843
bool
×
3844
external_log_format::match_samples(const std::vector<sample>& samples) const
3845
{
3846
    for (const auto& sample_iter : samples) {
3847
        for (const auto& pat_iter : this->elf_pattern_order) {
5,709✔
3848
            auto& pat = *pat_iter;
5,709✔
3849

5,709✔
3850
            if (!pat.p_pcre.pp_value) {
5,709✔
3851
                continue;
5,709✔
3852
            }
2,595✔
3853

3854
            if (pat.p_pcre.pp_value->find_in(sample_iter.s_line.pp_value)
5,709✔
3855
                    .ignore_error())
5,709✔
3856
            {
3857
                return true;
3858
            }
3859
        }
3860
    }
3861

3862
    return false;
5,709✔
3863
}
28,015✔
3864

3865
class external_log_table : public log_format_vtab_impl {
3866
public:
1,839,238✔
3867
    explicit external_log_table(const external_log_format& elf)
3868
        : log_format_vtab_impl(elf), elt_format(elf)
8,291,916✔
3869
    {
18,207,264✔
3870
    }
11,754,586✔
3871

3872
    void get_columns(std::vector<vtab_column>& cols) const override
11,754,586✔
3873
    {
×
3874
        const auto& elf = this->elt_format;
3875

3876
        cols.resize(elf.elf_column_count);
23,509,172✔
3877
        for (const auto& vd : elf.elf_value_def_order) {
11,754,586✔
3878
            auto type_pair = log_vtab_impl::logline_value_to_sqlite_type(
3879
                vd->vd_meta.lvm_kind);
8,307✔
3880

3881
            if (!vd->vd_meta.lvm_column.is<logline_value_meta::table_column>())
3882
            {
3883
                continue;
3884
            }
1,830,931✔
3885

3886
            auto col
3887
                = vd->vd_meta.lvm_column.get<logline_value_meta::table_column>()
3888
                      .value;
3889
            require(0 <= col && col < elf.elf_column_count);
28,015✔
3890

28,015✔
3891
            cols[col].vc_name = vd->vd_meta.lvm_name.get();
3892
            cols[col].vc_type = type_pair.first;
28,015✔
3893
            cols[col].vc_subtype = type_pair.second;
3894
            cols[col].vc_collator = vd->vd_collate;
28,328✔
3895
            cols[col].vc_comment = vd->vd_description;
3896
        }
28,328✔
3897
    }
3898

28,328✔
3899
    void get_foreign_keys(std::vector<std::string>& keys_inout) const override
272,372✔
3900
    {
244,044✔
3901
        log_vtab_impl::get_foreign_keys(keys_inout);
244,044✔
3902

3903
        for (const auto& elf_value_def : this->elt_format.elf_value_defs) {
244,044✔
3904
            if (elf_value_def.second->vd_meta.lvm_foreign_key) {
3905
                keys_inout.emplace_back(elf_value_def.first.to_string());
10,006✔
3906
            }
3907
        }
3908
    }
3909

234,038✔
3910
    bool next(log_cursor& lc, logfile_sub_source& lss) override
234,038✔
3911
    {
234,038✔
3912
        if (lc.is_eof()) {
3913
            return true;
234,038✔
3914
        }
234,038✔
3915

234,038✔
3916
        content_line_t cl(lss.at(lc.lc_curr_line));
234,038✔
3917
        auto* lf = lss.find_file_ptr(cl);
234,038✔
3918
        auto lf_iter = lf->begin() + cl;
3919
        uint8_t mod_id = lf_iter->get_module_id();
28,328✔
3920

3921
        if (lf_iter->is_continued()) {
20,999✔
3922
            return false;
3923
        }
20,999✔
3924

3925
        this->elt_module_format.mf_mod_format = nullptr;
246,992✔
3926
        if (lf->get_format_name() == this->lfvi_format.get_name()) {
225,993✔
3927
            return true;
15,069✔
3928
        } else if (mod_id && mod_id == this->lfvi_format.lf_mod_index) {
3929
            auto format = lf->get_format();
3930

20,999✔
3931
            return lf->read_line(lf_iter)
3932
                .map([this, format, cl, lf](auto line) {
359✔
3933
                    logline_value_vector values;
3934
                    struct line_range mod_name_range;
359✔
3935
                    intern_string_t mod_name;
×
3936

3937
                    this->vi_attrs.clear();
3938
                    values.lvv_sbr = line.clone();
359✔
3939
                    format->annotate(lf, cl, this->vi_attrs, values, false);
359✔
3940
                    this->elt_container_body
359✔
3941
                        = find_string_attr_range(this->vi_attrs, &SA_BODY);
359✔
3942
                    if (!this->elt_container_body.is_valid()) {
3943
                        return false;
359✔
3944
                    }
×
3945
                    this->elt_container_body.ltrim(line.get_data());
3946
                    mod_name_range = find_string_attr_range(this->vi_attrs,
3947
                                                            &logline::L_MODULE);
359✔
3948
                    if (!mod_name_range.is_valid()) {
359✔
3949
                        return false;
354✔
3950
                    }
5✔
3951
                    mod_name = intern_string::lookup(
2✔
3952
                        &line.get_data()[mod_name_range.lr_start],
3953
                        mod_name_range.length());
2✔
3954
                    this->vi_attrs.clear();
4✔
3955
                    this->elt_module_format
2✔
3956
                        = external_log_format::MODULE_FORMATS[mod_name];
2✔
3957
                    if (!this->elt_module_format.mf_mod_format) {
2✔
3958
                        return false;
3959
                    }
2✔
3960
                    return this->elt_module_format.mf_mod_format->get_name()
2✔
3961
                        == this->lfvi_format.get_name();
2✔
3962
                })
3963
                .unwrapOr(false);
2✔
3964
        }
2✔
3965

×
3966
        return false;
3967
    }
2✔
3968

2✔
3969
    void extract(logfile* lf,
3970
                 uint64_t line_number,
2✔
3971
                 logline_value_vector& values) override
×
3972
    {
3973
        auto& line = values.lvv_sbr;
2✔
3974
        auto format = lf->get_format();
2✔
3975

2✔
3976
        if (this->elt_module_format.mf_mod_format != nullptr) {
2✔
3977
            shared_buffer_ref body_ref;
3978

2✔
3979
            body_ref.subset(line,
2✔
3980
                            this->elt_container_body.lr_start,
×
3981
                            this->elt_container_body.length());
3982
            this->vi_attrs.clear();
2✔
3983
            auto narrow_res
4✔
3984
                = values.lvv_sbr.narrow(this->elt_container_body.lr_start,
2✔
3985
                                        this->elt_container_body.length());
2✔
3986
            values.lvv_values.clear();
2✔
3987
            this->elt_module_format.mf_mod_format->annotate(
3988
                lf, line_number, this->vi_attrs, values, false);
3✔
3989
            values.lvv_sbr.widen(narrow_res);
3990
        } else {
3991
            this->vi_attrs.clear();
276✔
3992
            format->annotate(lf, line_number, this->vi_attrs, values, false);
3993
        }
3994
    }
3995

276✔
3996
    const external_log_format& elt_format;
276✔
3997
    module_format elt_module_format;
3998
    struct line_range elt_container_body;
276✔
3999
};
2✔
4000

4001
std::shared_ptr<log_vtab_impl>
2✔
4002
external_log_format::get_vtab_impl() const
2✔
4003
{
2✔
4004
    return std::make_shared<external_log_table>(*this);
2✔
4005
}
4006

2✔
4007
std::shared_ptr<log_format>
2✔
4008
external_log_format::specialized(int fmt_lock)
2✔
4009
{
2✔
4010
    auto retval = std::make_shared<external_log_format>(*this);
2✔
4011

2✔
4012
    retval->lf_specialized = true;
2✔
4013
    this->lf_pattern_locks.clear();
274✔
4014
    if (fmt_lock != -1) {
274✔
4015
        retval->lf_pattern_locks.emplace_back(0, fmt_lock);
4016
    }
276✔
4017

4018
    if (this->elf_type == elf_type_t::ELF_TYPE_JSON) {
4019
        this->jlf_parse_context
4020
            = std::make_shared<yajlpp_parse_context>(this->elf_name);
4021
        this->jlf_yajl_handle.reset(
4022
            yajl_alloc(&this->jlf_parse_context->ypc_callbacks,
4023
                       nullptr,
4024
                       this->jlf_parse_context.get()),
28,015✔
4025
            yajl_handle_deleter());
4026
        yajl_config(this->jlf_yajl_handle.get(), yajl_dont_validate_strings, 1);
28,015✔
4027
        this->jlf_cached_line.reserve(16 * 1024);
4028
    }
4029

4030
    this->lf_value_stats.clear();
463✔
4031
    this->lf_value_stats.resize(this->elf_value_defs.size());
4032
    this->elf_specialized_value_defs_state = *this->elf_value_defs_state;
463✔
4033

4034
    return retval;
463✔
4035
}
463✔
4036

463✔
4037
log_format::match_name_result
38✔
4038
external_log_format::match_name(const std::string& filename)
4039
{
4040
    if (this->elf_filename_pcre.pp_value == nullptr) {
463✔
4041
        return name_matched{};
4042
    }
34✔
4043

34✔
4044
    if (this->elf_filename_pcre.pp_value->find_in(filename)
34✔
4045
            .ignore_error()
4046
            .has_value())
34✔
4047
    {
4048
        return name_matched{};
34✔
4049
    }
34✔
4050

4051
    return name_mismatched{
4052
        this->elf_filename_pcre.pp_value->match_partial(filename),
463✔
4053
        this->elf_filename_pcre.pp_value->get_pattern(),
463✔
4054
    };
463✔
4055
}
4056

926✔
4057
auto
463✔
4058
external_log_format::value_line_count(const intern_string_t ist,
4059
                                      bool top_level,
4060
                                      std::optional<double> val,
543,995✔
4061
                                      const unsigned char* str,
4062
                                      ssize_t len) -> value_line_count_result
543,995✔
4063
{
543,304✔
4064
    const auto iter = this->elf_value_defs.find(ist);
4065
    value_line_count_result retval;
4066

1,382✔
4067
    if (iter == this->elf_value_defs.end()) {
1,382✔
4068
        if (this->jlf_hide_extra || !top_level) {
691✔
4069
            retval.vlcr_count = 0;
4070
        }
224✔
4071

4072
        return retval;
4073
    }
934✔
4074

934✔
4075
    if (str != nullptr && !val) {
467✔
4076
        auto frag = string_fragment::from_bytes(str, len);
467✔
4077
        while (frag.endswith("\n")) {
4078
            frag.pop_back();
4079
        }
4080
        while (!frag.empty()) {
44,667✔
4081
            auto utf_res = is_utf8(frag, '\n');
4082
            if (!utf_res.is_valid()) {
4083
                retval.vlcr_valid_utf = false;
4084
            }
4085
            retval.vlcr_has_ansi |= utf_res.usr_has_ansi;
4086
            if (!utf_res.usr_remaining) {
44,667✔
4087
                break;
44,667✔
4088
            }
4089
            frag = utf_res.usr_remaining.value();
44,667✔
4090
            retval.vlcr_count += 1;
41,085✔
4091
        }
36,702✔
4092
    }
4093

4094
    if (iter->second->vd_meta.lvm_values_index) {
41,085✔
4095
        auto& lvs = this->lf_value_stats[iter->second->vd_meta.lvm_values_index
4096
                                             .value()];
4097
        if (len > lvs.lvs_width) {
3,582✔
4098
            lvs.lvs_width = len;
2,057✔
4099
        }
2,171✔
4100
        if (val) {
114✔
4101
            lvs.add_value(val.value());
4102
        }
2,207✔
4103
    }
2,176✔
4104

2,176✔
4105
    if (iter->second->vd_meta.is_hidden()) {
×
4106
        retval.vlcr_count = 0;
4107
        return retval;
2,176✔
4108
    }
2,176✔
4109

2,026✔
4110
    if (std::find_if(this->jlf_line_format.begin(),
4111
                     this->jlf_line_format.end(),
150✔
4112
                     json_field_cmp(json_log_field::VARIABLE, ist))
150✔
4113
        != this->jlf_line_format.end())
4114
    {
4115
        retval.vlcr_line_format_count += 1;
4116
        retval.vlcr_count -= 1;
3,582✔
4117
    }
2,771✔
4118

5,542✔
4119
    return retval;
2,771✔
4120
}
373✔
4121

4122
log_level_t
2,771✔
4123
external_log_format::convert_level(string_fragment sf,
211✔
4124
                                   scan_batch_context* sbc) const
4125
{
4126
    log_level_t retval = LEVEL_INFO;
4127

3,582✔
4128
    if (sf.is_valid()) {
2,030✔
4129
        if (sbc != nullptr && sbc->sbc_cached_level_count > 0) {
2,030✔
4130
            auto cached_level_iter
4131
                = std::find(std::begin(sbc->sbc_cached_level_strings),
4132
                            std::begin(sbc->sbc_cached_level_strings)
1,552✔
4133
                                + sbc->sbc_cached_level_count,
4134
                            sf);
4135
            if (cached_level_iter
3,104✔
4136
                != std::begin(sbc->sbc_cached_level_strings)
4137
                    + sbc->sbc_cached_level_count)
1,410✔
4138
            {
1,410✔
4139
                auto cache_index
4140
                    = std::distance(std::begin(sbc->sbc_cached_level_strings),
4141
                                    cached_level_iter);
1,552✔
4142
                if (cache_index != 0) {
4143
                    std::swap(sbc->sbc_cached_level_strings[cache_index],
4144
                              sbc->sbc_cached_level_strings[0]);
4145
                    std::swap(sbc->sbc_cached_level_values[cache_index],
122,648✔
4146
                              sbc->sbc_cached_level_values[0]);
4147
                }
4148
                return sbc->sbc_cached_level_values[0];
122,648✔
4149
            }
4150
        }
122,648✔
4151

96,628✔
4152
        if (this->elf_level_patterns.empty()) {
4153
            retval = string2level(sf.data(), sf.length());
969✔
4154
        } else {
969✔
4155
            for (const auto& elf_level_pattern : this->elf_level_patterns) {
969✔
4156
                if (elf_level_pattern.second.lp_pcre.pp_value
4157
                        ->find_in(sf, PCRE2_NO_UTF_CHECK)
969✔
4158
                        .ignore_error()
969✔
4159
                        .has_value())
969✔
4160
                {
4161
                    retval = elf_level_pattern.first;
4162
                    break;
500✔
4163
                }
4164
            }
500✔
4165
        }
35✔
4166

35✔
4167
        if (sbc != nullptr && sf.length() < 10) {
35✔
4168
            size_t cache_index;
35✔
4169

4170
            if (sbc->sbc_cached_level_count == 4) {
500✔
4171
                cache_index = sbc->sbc_cached_level_count - 1;
4172
            } else {
4173
                cache_index = sbc->sbc_cached_level_count;
4174
                sbc->sbc_cached_level_count += 1;
96,128✔
4175
            }
11,464✔
4176
            sbc->sbc_cached_level_strings[cache_index] = sf.to_string();
4177
            sbc->sbc_cached_level_values[cache_index] = retval;
227,246✔
4178
        }
414,852✔
4179
    }
414,852✔
4180

414,852✔
4181
    return retval;
207,426✔
4182
}
4183

64,844✔
4184
logline_value_meta
64,844✔
4185
external_log_format::get_value_meta(intern_string_t field_name,
4186
                                    value_kind_t kind)
4187
{
4188
    auto iter = this->elf_value_defs.find(field_name);
4189

96,128✔
4190
    if (iter == this->elf_value_defs.end()) {
4191
        auto retval = logline_value_meta(
4192
            field_name, kind, logline_value_meta::external_column{}, this);
963✔
4193

100✔
4194
        retval.lvm_hidden = this->jlf_hide_extra;
4195
        return retval;
863✔
4196
    }
863✔
4197

4198
    auto lvm = iter->second->vd_meta;
963✔
4199

963✔
4200
    lvm.lvm_kind = kind;
4201
    return lvm;
4202
}
4203

122,148✔
4204
void
4205
external_log_format::json_append(
4206
    const external_log_format::json_format_element& jfe,
4207
    const value_def* vd,
8,016✔
4208
    const char* value,
4209
    ssize_t len)
4210
{
8,016✔
4211
    if (len == -1) {
4212
        len = strlen(value);
8,016✔
4213
    }
4214
    if (jfe.jfe_align == json_format_element::align_t::RIGHT) {
692✔
4215
        if (len < jfe.jfe_min_width) {
4216
            this->json_append_to_cache(jfe.jfe_min_width - len);
692✔
4217
        } else if (jfe.jfe_auto_width && vd != nullptr
692✔
4218
                   && len < this->lf_value_stats[vd->vd_meta.lvm_values_index
692✔
4219
                                                     .value()]
4220
                                .lvs_width)
7,324✔
4221
        {
4222
            this->json_append_to_cache(
7,324✔
4223
                this->lf_value_stats[vd->vd_meta.lvm_values_index.value()]
7,324✔
4224
                    .lvs_width
7,324✔
4225
                - len);
4226
        }
4227
    }
5,656✔
4228
    this->json_append_to_cache(value, len);
4229
    if (jfe.jfe_align == json_format_element::align_t::LEFT
4230
        || jfe.jfe_align == json_format_element::align_t::NONE)
4231
    {
4232
        if (len < jfe.jfe_min_width) {
4233
            this->json_append_to_cache(jfe.jfe_min_width - len);
5,656✔
4234
        } else if (jfe.jfe_auto_width && vd != nullptr
×
4235
                   && len < this->lf_value_stats[vd->vd_meta.lvm_values_index
4236
                                                     .value()]
5,656✔
4237
                                .lvs_width)
400✔
4238
        {
×
4239
            this->json_append_to_cache(
32✔
4240
                this->lf_value_stats[vd->vd_meta.lvm_values_index.value()]
432✔
4241
                    .lvs_width
32✔
4242
                - len);
32✔
4243
        }
4244
    }
12✔
4245
}
12✔
4246

12✔
4247
intern_string_t
4248
external_log_format::get_pattern_name(uint64_t line_number) const
4249
{
4250
    if (this->elf_type != elf_type_t::ELF_TYPE_TEXT) {
5,656✔
4251
        static auto structured = intern_string::lookup("structured");
5,656✔
4252

5,656✔
4253
        return structured;
4254
    }
5,256✔
4255
    int pat_index = this->pattern_index_for_line(line_number);
254✔
4256
    return this->elf_pattern_order[pat_index]->p_name;
619✔
4257
}
5,621✔
4258

560✔
4259
int
560✔
4260
log_format::pattern_index_for_line(uint64_t line_number) const
4261
{
335✔
4262
    if (this->lf_pattern_locks.empty()) {
335✔
4263
        return -1;
335✔
4264
    }
4265

4266
    auto iter = lower_bound(this->lf_pattern_locks.cbegin(),
4267
                            this->lf_pattern_locks.cend(),
5,656✔
4268
                            line_number,
4269
                            [](const pattern_for_lines& pfl, uint32_t line) {
4270
                                return pfl.pfl_line < line;
34✔
4271
                            });
4272

34✔
4273
    if (iter == this->lf_pattern_locks.end() || iter->pfl_line != line_number) {
4274
        --iter;
4275
    }
×
4276

4277
    return iter->pfl_pat_index;
34✔
4278
}
34✔
4279

4280
std::string
4281
log_format::get_pattern_path(uint64_t line_number) const
4282
{
1,325✔
4283
    int pat_index = this->pattern_index_for_line(line_number);
4284
    return fmt::format(FMT_STRING("builtin ({})"), pat_index);
1,325✔
4285
}
×
4286

4287
intern_string_t
4288
log_format::get_pattern_name(uint64_t line_number) const
1,325✔
4289
{
4290
    char pat_str[128];
4291

1,375✔
4292
    int pat_index = this->pattern_index_for_line(line_number);
1,375✔
4293
    auto to_n_res = fmt::format_to_n(
4294
        pat_str, sizeof(pat_str) - 1, FMT_STRING("builtin ({})"), pat_index);
4295
    pat_str[to_n_res.size] = '\0';
1,325✔
4296
    return intern_string::lookup(pat_str);
831✔
4297
}
4298

4299
std::shared_ptr<log_format>
1,325✔
4300
log_format::find_root_format(const char* name)
4301
{
4302
    auto& fmts = get_root_formats();
4303
    for (auto& lf : fmts) {
×
4304
        if (lf->get_name() == name) {
4305
            return lf;
×
4306
        }
×
4307
    }
×
4308
    return nullptr;
×
4309
}
4310

4311
log_format::pattern_for_lines::
4312
pattern_for_lines(uint32_t pfl_line, uint32_t pfl_pat_index)
6✔
4313
    : pfl_line(pfl_line), pfl_pat_index(pfl_pat_index)
4314
{
4315
}
4316

6✔
4317
void
×
4318
logline_value_stats::merge(const logline_value_stats& other)
18✔
4319
{
6✔
4320
    if (other.lvs_count == 0) {
6✔
4321
        return;
6✔
4322
    }
6✔
4323

4324
    require(other.lvs_min_value <= other.lvs_max_value);
4325

4326
    if (other.lvs_width > this->lvs_width) {
1,042✔
4327
        this->lvs_width = other.lvs_width;
4328
    }
1,042✔
4329
    if (other.lvs_min_value < this->lvs_min_value) {
32,703✔
4330
        this->lvs_min_value = other.lvs_min_value;
32,703✔
4331
    }
1,042✔
4332
    if (other.lvs_max_value > this->lvs_max_value) {
4333
        this->lvs_max_value = other.lvs_max_value;
4334
    }
×
4335
    this->lvs_count += other.lvs_count;
4336
    this->lvs_total += other.lvs_total;
4337

1,436✔
4338
    ensure(this->lvs_count >= 0);
1,436✔
4339
    ensure(this->lvs_min_value <= this->lvs_max_value);
1,436✔
4340
}
4341

1,436✔
4342
void
4343
logline_value_stats::add_value(double value)
4344
{
32✔
4345
    if (value < this->lvs_min_value) {
4346
        this->lvs_min_value = value;
32✔
4347
    }
×
4348
    if (value > this->lvs_max_value) {
4349
        this->lvs_max_value = value;
4350
    }
32✔
4351
    this->lvs_count += 1;
4352
    this->lvs_total += value;
32✔
4353
}
×
4354

4355
std::vector<logline_value_meta>
32✔
4356
external_log_format::get_value_metadata() const
32✔
4357
{
4358
    std::vector<logline_value_meta> retval;
32✔
4359

32✔
4360
    for (const auto& vd : this->elf_value_def_order) {
4361
        retval.emplace_back(vd->vd_meta);
32✔
4362
    }
32✔
4363

4364
    return retval;
32✔
4365
}
32✔
4366

4367
const logline_value_stats*
4368
external_log_format::stats_for_value(const intern_string_t& name) const
4369
{
19,680✔
4370
    auto iter = this->elf_value_defs.find(name);
4371
    if (iter != this->elf_value_defs.end()
19,680✔
4372
        && iter->second->vd_meta.lvm_values_index)
659✔
4373
    {
4374
        return &this->lf_value_stats[iter->second->vd_meta.lvm_values_index
19,680✔
4375
                                         .value()];
1,264✔
4376
    }
4377

19,680✔
4378
    return nullptr;
19,680✔
4379
}
19,680✔
4380

4381
std::string
4382
external_log_format::get_pattern_regex(uint64_t line_number) const
5,709✔
4383
{
4384
    if (this->elf_type != elf_type_t::ELF_TYPE_TEXT) {
5,709✔
4385
        return "";
4386
    }
59,685✔
4387
    int pat_index = this->pattern_index_for_line(line_number);
53,976✔
4388
    return this->elf_pattern_order[pat_index]->p_pcre.pp_value->get_pattern();
4389
}
4390

5,709✔
4391
bool
×
4392
external_log_format::hide_field(const intern_string_t field_name, bool val)
4393
{
4394
    auto vd_iter = this->elf_value_defs.find(field_name);
×
4395

4396
    if (vd_iter == this->elf_value_defs.end()) {
×
4397
        return false;
×
4398
    }
×
4399

4400
    vd_iter->second->vd_meta.lvm_user_hidden = val;
×
4401
    if (this->elf_type == elf_type_t::ELF_TYPE_JSON) {
×
4402
        bool found = false;
4403

4404
        for (const auto& jfe : this->jlf_line_format) {
×
4405
            if (jfe.jfe_value.pp_value == field_name) {
4406
                found = true;
4407
            }
4408
        }
5✔
4409
        if (!found) {
4410
            log_info("format field %s.%s changed, rebuilding",
5✔
4411
                     this->elf_name.get(),
1✔
4412
                     field_name.get());
4413
            this->elf_value_defs_state->vds_generation += 1;
4✔
4414
        }
4✔
4415
    }
4416
    return true;
4417
}
4418

7✔
4419
bool
4420
external_log_format::format_changed()
7✔
4421
{
4422
    if (this->elf_specialized_value_defs_state.vds_generation
7✔
4423
        != this->elf_value_defs_state->vds_generation)
1✔
4424
    {
4425
        this->elf_specialized_value_defs_state = *this->elf_value_defs_state;
4426
        this->jlf_cached_offset = -1;
6✔
4427
        return true;
6✔
4428
    }
1✔
4429

4430
    return false;
18✔
4431
}
17✔
4432

×
4433
bool
4434
format_tag_def::path_restriction::matches(const char* fn) const
4435
{
1✔
4436
    return fnmatch(this->p_glob.c_str(), fn, 0) == 0;
1✔
4437
}
4438

4439
bool
1✔
4440
format_partition_def::path_restriction::matches(const char* fn) const
4441
{
4442
    return fnmatch(this->p_glob.c_str(), fn, 0) == 0;
6✔
4443
}
4444

4445
/* XXX */
4446
#include "log_format_impls.cc"
2,147✔
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