mirror of https://github.com/tstack/lnav.git
[log-format] add min-width and align options to line-format
Also some fixes for validating the format definitions. Fixes #338
This commit is contained in:
parent
41854cf637
commit
9ee18c26d3
3
NEWS
3
NEWS
|
@ -3,6 +3,9 @@ lnav v0.8.2:
|
|||
Features:
|
||||
* The timestamp format for JSON log files can be specified with the
|
||||
"timestamp-format" option in the "line-format" array.
|
||||
* Added "min-width" and "align" options to the "line-format" in format
|
||||
definitions for JSON log files. These options give you more control
|
||||
over how the displayed line looks.
|
||||
|
||||
lnav v0.8.1:
|
||||
Features:
|
||||
|
|
|
@ -74,6 +74,12 @@ fields:
|
|||
|
||||
:field: The name of the message field that should be inserted at this
|
||||
point in the message.
|
||||
:min-width: The minimum width for the field. If the value for the field
|
||||
in a given log message is shorter, padding will be added as needed to
|
||||
meet the minimum-width requirement. (v0.8.2+)
|
||||
:align: Specifies the alignment for the field, either "left" or "right".
|
||||
If "left", padding to meet the minimum-width will be added on the right.
|
||||
If "right", padding will be added on the left. (v0.8.2+)
|
||||
:timestamp-format: The timestamp format to use when displaying the time
|
||||
for this log message. (v0.8.2+)
|
||||
:default-value: The default value to use if the field could not be found
|
||||
|
|
|
@ -66,6 +66,11 @@ string_attr_type logline::L_PARTITION;
|
|||
string_attr_type logline::L_MODULE;
|
||||
string_attr_type logline::L_OPID;
|
||||
|
||||
const intern_string_t external_log_format::json_format_element::ALIGN_LEFT =
|
||||
intern_string::lookup("left");
|
||||
const intern_string_t external_log_format::json_format_element::ALIGN_RIGHT =
|
||||
intern_string::lookup("right");
|
||||
|
||||
const char *logline::level_names[LEVEL__MAX + 1] = {
|
||||
"unknown",
|
||||
"trace",
|
||||
|
@ -1128,27 +1133,43 @@ void external_log_format::get_subline(const logline &ll, shared_buffer_ref &sbr,
|
|||
iter != this->jlf_line_format.end();
|
||||
++iter) {
|
||||
static const intern_string_t ts_field = intern_string::lookup("__timestamp__", -1);
|
||||
json_format_element &jfe = *iter;
|
||||
|
||||
switch (iter->jfe_type) {
|
||||
switch (jfe.jfe_type) {
|
||||
case JLF_CONSTANT:
|
||||
this->json_append_to_cache(iter->jfe_default_value.c_str(),
|
||||
iter->jfe_default_value.size());
|
||||
this->json_append_to_cache(jfe.jfe_default_value.c_str(),
|
||||
jfe.jfe_default_value.size());
|
||||
break;
|
||||
case JLF_VARIABLE:
|
||||
lv_iter = find_if(this->jlf_line_values.begin(),
|
||||
this->jlf_line_values.end(),
|
||||
logline_value_cmp(&iter->jfe_value));
|
||||
logline_value_cmp(&jfe.jfe_value));
|
||||
if (lv_iter != this->jlf_line_values.end()) {
|
||||
string str = lv_iter->to_string();
|
||||
size_t nl_pos = str.find('\n');
|
||||
|
||||
lr.lr_start = this->jlf_cached_line.size();
|
||||
this->json_append_to_cache(
|
||||
str.c_str(), str.size());
|
||||
if (nl_pos == string::npos)
|
||||
if (jfe.jfe_align == json_format_element::ALIGN_RIGHT) {
|
||||
if (str.size() < jfe.jfe_min_width) {
|
||||
this->json_append_to_cache(jfe.jfe_min_width -
|
||||
str.size());
|
||||
}
|
||||
}
|
||||
this->json_append_to_cache(str.c_str(), str.size());
|
||||
if (jfe.jfe_align == json_format_element::ALIGN_LEFT) {
|
||||
if (str.size() < jfe.jfe_min_width) {
|
||||
this->json_append_to_cache(jfe.jfe_min_width -
|
||||
str.size());
|
||||
}
|
||||
}
|
||||
|
||||
if (nl_pos == string::npos) {
|
||||
lr.lr_end = this->jlf_cached_line.size();
|
||||
else
|
||||
}
|
||||
else {
|
||||
lr.lr_end = lr.lr_start + nl_pos;
|
||||
}
|
||||
|
||||
if (lv_iter->lv_name == this->lf_timestamp_field) {
|
||||
this->jlf_line_attrs.push_back(
|
||||
string_attr(lr, &logline::L_TIMESTAMP));
|
||||
|
@ -1170,13 +1191,12 @@ void external_log_format::get_subline(const logline &ll, shared_buffer_ref &sbr,
|
|||
used_values[distance(this->jlf_line_values.begin(),
|
||||
lv_iter)] = true;
|
||||
}
|
||||
else if (iter->jfe_value == ts_field ||
|
||||
!iter->jfe_ts_format.empty()) {
|
||||
else if (jfe.jfe_value == ts_field) {
|
||||
struct line_range lr;
|
||||
ssize_t ts_len;
|
||||
char ts[64];
|
||||
|
||||
if (iter->jfe_ts_format.empty()) {
|
||||
if (jfe.jfe_ts_format.empty()) {
|
||||
ts_len = sql_strftime(ts, sizeof(ts),
|
||||
ll.get_timeval(), 'T');
|
||||
} else {
|
||||
|
@ -1184,7 +1204,7 @@ void external_log_format::get_subline(const logline &ll, shared_buffer_ref &sbr,
|
|||
|
||||
ll.to_exttm(et);
|
||||
ts_len = ftime_fmt(ts, sizeof(ts),
|
||||
iter->jfe_ts_format.c_str(),
|
||||
jfe.jfe_ts_format.c_str(),
|
||||
et);
|
||||
}
|
||||
lr.lr_start = this->jlf_cached_line.size();
|
||||
|
@ -1195,8 +1215,8 @@ void external_log_format::get_subline(const logline &ll, shared_buffer_ref &sbr,
|
|||
}
|
||||
else {
|
||||
this->json_append_to_cache(
|
||||
iter->jfe_default_value.c_str(),
|
||||
iter->jfe_default_value.size());
|
||||
jfe.jfe_default_value.c_str(),
|
||||
jfe.jfe_default_value.size());
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -1577,6 +1597,16 @@ void external_log_format::build(std::vector<std::string> &errors) {
|
|||
}
|
||||
|
||||
this->lf_value_stats.resize(this->elf_numeric_value_defs.size());
|
||||
|
||||
for (vector<json_format_element>::iterator iter = this->jlf_line_format.begin();
|
||||
iter != this->jlf_line_format.end();
|
||||
++iter) {
|
||||
json_format_element &jfe = *iter;
|
||||
|
||||
if (jfe.jfe_value.empty() && !jfe.jfe_ts_format.empty()) {
|
||||
jfe.jfe_value = intern_string::lookup("__timestamp__");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void external_log_format::register_vtabs(log_vtab_manager *vtab_manager,
|
||||
|
|
|
@ -1041,14 +1041,19 @@ public:
|
|||
};
|
||||
|
||||
struct json_format_element {
|
||||
static const intern_string_t ALIGN_LEFT;
|
||||
static const intern_string_t ALIGN_RIGHT;
|
||||
|
||||
json_format_element()
|
||||
: jfe_type(JLF_CONSTANT), jfe_default_value("-"), jfe_min_width(0)
|
||||
: jfe_type(JLF_CONSTANT), jfe_default_value("-"), jfe_min_width(0),
|
||||
jfe_align(ALIGN_LEFT)
|
||||
{ };
|
||||
|
||||
json_log_field jfe_type;
|
||||
intern_string_t jfe_value;
|
||||
std::string jfe_default_value;
|
||||
int jfe_min_width;
|
||||
long long jfe_min_width;
|
||||
intern_string_t jfe_align;
|
||||
std::string jfe_ts_format;
|
||||
};
|
||||
|
||||
|
@ -1058,6 +1063,12 @@ public:
|
|||
memcpy(&this->jlf_cached_line[old_size], value, len);
|
||||
};
|
||||
|
||||
void json_append_to_cache(size_t len) {
|
||||
size_t old_size = this->jlf_cached_line.size();
|
||||
this->jlf_cached_line.resize(old_size + len);
|
||||
memset(&this->jlf_cached_line[old_size], ' ', len);
|
||||
};
|
||||
|
||||
bool jlf_json;
|
||||
bool jlf_hide_extra;
|
||||
std::vector<json_format_element> jlf_line_format;
|
||||
|
|
|
@ -64,6 +64,7 @@ static map<intern_string_t, external_log_format *> LOG_FORMATS;
|
|||
struct userdata {
|
||||
std::string ud_format_path;
|
||||
vector<intern_string_t> *ud_format_names;
|
||||
std::vector<std::string> *ud_errors;
|
||||
};
|
||||
|
||||
static external_log_format *ensure_format(yajlpp_parse_context *ypc)
|
||||
|
@ -423,6 +424,13 @@ static struct json_path_handler pattern_handlers[] = {
|
|||
json_path_handler()
|
||||
};
|
||||
|
||||
static const intern_string_t ALIGN_ENUM[] = {
|
||||
external_log_format::json_format_element::ALIGN_LEFT,
|
||||
external_log_format::json_format_element::ALIGN_RIGHT,
|
||||
|
||||
intern_string_t()
|
||||
};
|
||||
|
||||
static struct json_path_handler line_format_handlers[] = {
|
||||
json_path_handler("field")
|
||||
.with_synopsis("<field-name>")
|
||||
|
@ -441,10 +449,22 @@ static struct json_path_handler line_format_handlers[] = {
|
|||
.with_description("The strftime(3) format for this field")
|
||||
.for_field(&nullobj<external_log_format::json_format_element>()->jfe_ts_format),
|
||||
|
||||
json_path_handler("min-width")
|
||||
.with_min_value(0)
|
||||
.with_synopsis("<size>")
|
||||
.with_description("The minimum width of the field")
|
||||
.for_field(&nullobj<external_log_format::json_format_element>()->jfe_min_width),
|
||||
|
||||
json_path_handler("align")
|
||||
.with_synopsis("left|right")
|
||||
.with_description("Align the text in the column to the left or right side")
|
||||
.with_enum_values(ALIGN_ENUM)
|
||||
.for_field(&nullobj<external_log_format::json_format_element>()->jfe_align),
|
||||
|
||||
json_path_handler()
|
||||
};
|
||||
|
||||
static struct json_path_handler format_handlers[] = {
|
||||
struct json_path_handler format_handlers[] = {
|
||||
json_path_handler("/\\w+/regex/[^/]+/")
|
||||
.with_obj_provider(pattern_provider)
|
||||
.with_children(pattern_handlers),
|
||||
|
@ -537,6 +557,13 @@ static void write_sample_file(void)
|
|||
}
|
||||
}
|
||||
|
||||
static void format_error_reporter(const yajlpp_parse_context &ypc, const char *msg)
|
||||
{
|
||||
struct userdata *ud = (userdata *) ypc.ypc_userdata;
|
||||
|
||||
ud->ud_errors->push_back(msg);
|
||||
}
|
||||
|
||||
std::vector<intern_string_t> load_format_file(const string &filename, std::vector<string> &errors)
|
||||
{
|
||||
std::vector<intern_string_t> retval;
|
||||
|
@ -546,6 +573,7 @@ std::vector<intern_string_t> load_format_file(const string &filename, std::vecto
|
|||
log_info("loading formats from file: %s", filename.c_str());
|
||||
ud.ud_format_path = filename;
|
||||
ud.ud_format_names = &retval;
|
||||
ud.ud_errors = &errors;
|
||||
yajlpp_parse_context ypc(filename, format_handlers);
|
||||
ypc.ypc_userdata = &ud;
|
||||
if ((fd = open(filename.c_str(), O_RDONLY)) == -1) {
|
||||
|
@ -563,6 +591,8 @@ std::vector<intern_string_t> load_format_file(const string &filename, std::vecto
|
|||
ssize_t rc = -1;
|
||||
|
||||
handle = yajl_alloc(&ypc.ypc_callbacks, NULL, &ypc);
|
||||
ypc.with_handle(handle)
|
||||
.with_error_reporter(format_error_reporter);
|
||||
yajl_config(handle, yajl_allow_comments, 1);
|
||||
while (true) {
|
||||
rc = read(fd, buffer, sizeof(buffer));
|
||||
|
@ -580,7 +610,7 @@ std::vector<intern_string_t> load_format_file(const string &filename, std::vecto
|
|||
// Turn it into a JavaScript comment.
|
||||
buffer[0] = buffer[1] = '/';
|
||||
}
|
||||
if (yajl_parse(handle, (const unsigned char *)buffer, rc) != yajl_status_ok) {
|
||||
if (ypc.parse((const unsigned char *)buffer, rc) != yajl_status_ok) {
|
||||
errors.push_back(filename +
|
||||
": invalid json -- " +
|
||||
string((char *)yajl_get_error(handle, 1, (unsigned char *)buffer, rc)));
|
||||
|
@ -589,7 +619,7 @@ std::vector<intern_string_t> load_format_file(const string &filename, std::vecto
|
|||
offset += rc;
|
||||
}
|
||||
if (rc == 0) {
|
||||
if (yajl_complete_parse(handle) != yajl_status_ok) {
|
||||
if (ypc.complete_parse() != yajl_status_ok) {
|
||||
errors.push_back(filename +
|
||||
": invalid json -- " +
|
||||
string((char *)yajl_get_error(handle, 0, NULL, 0)));
|
||||
|
@ -640,6 +670,7 @@ void load_formats(const std::vector<std::string> &extra_paths,
|
|||
log_debug("Loading default formats");
|
||||
handle = yajl_alloc(&ypc_builtin.ypc_callbacks, NULL, &ypc_builtin);
|
||||
ud.ud_format_names = &retval;
|
||||
ud.ud_errors = &errors;
|
||||
ypc_builtin
|
||||
.with_handle(handle)
|
||||
.ypc_userdata = &ud;
|
||||
|
|
|
@ -64,4 +64,6 @@ void extract_metadata_from_file(struct script_metadata &meta_inout);
|
|||
void find_format_scripts(const std::vector<std::string> &extra_paths,
|
||||
std::map<std::string, std::vector<script_metadata> > &scripts);
|
||||
|
||||
extern struct json_path_handler format_handlers[];
|
||||
|
||||
#endif
|
||||
|
|
139
src/yajlpp.cc
139
src/yajlpp.cc
|
@ -32,6 +32,7 @@
|
|||
#include "config.h"
|
||||
|
||||
#include "yajlpp.hh"
|
||||
#include "yajl/api/yajl_parse.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
|
@ -56,6 +57,8 @@ int yajlpp_static_string(yajlpp_parse_context *ypc, const unsigned char *str, si
|
|||
|
||||
(*field_ptr) = string((const char *) str, len);
|
||||
|
||||
yajlpp_validator_for_string(*ypc, *ypc->ypc_current_handler);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -66,6 +69,8 @@ int yajlpp_static_intern_string(yajlpp_parse_context *ypc, const unsigned char *
|
|||
|
||||
(*field_ptr) = intern_string::lookup((const char *) str, len);
|
||||
|
||||
yajlpp_validator_for_intern_string(*ypc, *ypc->ypc_current_handler);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -86,31 +91,85 @@ yajl_gen_status yajlpp_static_gen_string(yajlpp_gen_context &ygc,
|
|||
return yajl_gen_string(handle, *field_ptr);
|
||||
}
|
||||
|
||||
void yajlpp_validator_for_string(json_schema_validator &validator,
|
||||
const std::string &path,
|
||||
void yajlpp_validator_for_string(yajlpp_parse_context &ypc,
|
||||
const json_path_handler_base &jph)
|
||||
{
|
||||
string *field_ptr = resolve_simple_object<string>(validator.jsv_simple_data, jph.jph_simple_offset);
|
||||
string *field_ptr = (string *) resolve_root(&ypc);
|
||||
char buffer[1024];
|
||||
|
||||
if (field_ptr->empty() && jph.jph_min_length > 0) {
|
||||
validator.jsv_errors[path].push_back("value must not be empty");
|
||||
ypc.report_error("value must not be empty");
|
||||
}
|
||||
else if (field_ptr->size() < jph.jph_min_length) {
|
||||
snprintf(buffer, sizeof(buffer),
|
||||
"value must be at least %lu characters long",
|
||||
jph.jph_min_length);
|
||||
validator.jsv_errors[path].push_back(buffer);
|
||||
ypc.report_error(buffer);
|
||||
}
|
||||
}
|
||||
|
||||
void yajlpp_validator_for_intern_string(yajlpp_parse_context &ypc,
|
||||
const json_path_handler_base &jph)
|
||||
{
|
||||
intern_string_t *field_ptr = (intern_string_t *) resolve_root(&ypc);
|
||||
char buffer[1024];
|
||||
|
||||
if (field_ptr->empty() && jph.jph_min_length > 0) {
|
||||
ypc.report_error("value must not be empty");
|
||||
}
|
||||
else if (field_ptr->size() < jph.jph_min_length) {
|
||||
snprintf(buffer, sizeof(buffer),
|
||||
"value must be at least %lu characters long",
|
||||
jph.jph_min_length);
|
||||
ypc.report_error(buffer);
|
||||
}
|
||||
|
||||
if (jph.jph_enum_values != NULL) {
|
||||
bool matched = false;
|
||||
|
||||
for (int lpc = 0; !jph.jph_enum_values[lpc].empty(); lpc++) {
|
||||
intern_string_t enum_value = jph.jph_enum_values[lpc];
|
||||
|
||||
if (enum_value == *field_ptr) {
|
||||
matched = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!matched) {
|
||||
ypc.report_error("error:%s:line %d\n "
|
||||
"Unexpected value for path %s -- %s",
|
||||
ypc.ypc_source.c_str(),
|
||||
ypc.get_line_number(),
|
||||
ypc.get_path().get(),
|
||||
(*field_ptr).get());
|
||||
ypc.report_error(" Allowed values:\n");
|
||||
for (int lpc = 0; !jph.jph_enum_values[lpc].empty(); lpc++) {
|
||||
intern_string_t enum_value = jph.jph_enum_values[lpc];
|
||||
|
||||
ypc.report_error(" %s\n", enum_value.get());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void yajlpp_validator_for_int(yajlpp_parse_context &ypc,
|
||||
const json_path_handler_base &jph)
|
||||
{
|
||||
long long *field_ptr = (long long int *) resolve_root(&ypc);
|
||||
char buffer[1024];
|
||||
|
||||
if (*field_ptr < jph.jph_min_value) {
|
||||
snprintf(buffer, sizeof(buffer),
|
||||
"value must be greater than %lld",
|
||||
jph.jph_min_value);
|
||||
ypc.report_error(buffer);
|
||||
}
|
||||
}
|
||||
|
||||
int yajlpp_static_number(yajlpp_parse_context *ypc, long long num)
|
||||
{
|
||||
const json_path_handler_base *jph = ypc->ypc_current_handler;
|
||||
|
||||
ptrdiff_t offset = (char *) jph->jph_simple_offset - (char *) NULL;
|
||||
char *root_ptr = (char *) ypc->ypc_simple_data;
|
||||
long long *field_ptr = (long long *) (root_ptr + offset);
|
||||
char *root_ptr = resolve_root(ypc);
|
||||
long long *field_ptr = (long long *) root_ptr;
|
||||
|
||||
*field_ptr = num;
|
||||
|
||||
|
@ -231,6 +290,8 @@ void yajlpp_parse_context::update_callbacks(const json_path_handler_base *orig_h
|
|||
{
|
||||
const json_path_handler_base *handlers = orig_handlers;
|
||||
|
||||
this->ypc_current_handler = NULL;
|
||||
|
||||
if (this->ypc_handlers == NULL) {
|
||||
return;
|
||||
}
|
||||
|
@ -355,55 +416,57 @@ int yajlpp_parse_context::handle_unused(void *ctx)
|
|||
|
||||
const json_path_handler_base *handler = ypc->ypc_current_handler;
|
||||
|
||||
int line_number = ypc->get_line_number();
|
||||
|
||||
if (handler != NULL && strlen(handler->jph_synopsis) > 0 &&
|
||||
strlen(handler->jph_description) > 0) {
|
||||
|
||||
fprintf(stderr, "warning:%s:%s %s -- %s\n",
|
||||
fprintf(stderr,
|
||||
"warning:%s:line %d\n unexpected data for path -- \n",
|
||||
ypc->ypc_source.c_str(),
|
||||
line_number);
|
||||
|
||||
fprintf(stderr, " %s %s -- %s\n",
|
||||
&ypc->ypc_path[0],
|
||||
handler->jph_synopsis,
|
||||
handler->jph_description
|
||||
);
|
||||
}
|
||||
else {
|
||||
fprintf(stderr,
|
||||
"warning:%s:line %d\n unexpected path -- \n",
|
||||
ypc->ypc_source.c_str(),
|
||||
line_number);
|
||||
|
||||
int line_number = 0;
|
||||
if (ypc->ypc_handle != NULL && ypc->ypc_json_text != NULL) {
|
||||
size_t consumed = yajl_get_bytes_consumed(ypc->ypc_handle);
|
||||
int current_count = count(&ypc->ypc_json_text[0],
|
||||
&ypc->ypc_json_text[consumed],
|
||||
'\n');
|
||||
line_number = ypc->ypc_line_number + current_count;
|
||||
fprintf(stderr, " %s\n", &ypc->ypc_path[0]);
|
||||
}
|
||||
|
||||
if (ypc->ypc_callbacks.yajl_boolean != (int (*)(void *, int))yajlpp_parse_context::handle_unused ||
|
||||
ypc->ypc_callbacks.yajl_integer != (int (*)(void *, long long))yajlpp_parse_context::handle_unused ||
|
||||
ypc->ypc_callbacks.yajl_double != (int (*)(void *, double))yajlpp_parse_context::handle_unused ||
|
||||
ypc->ypc_callbacks.yajl_string != (int (*)(void *, const unsigned char *, size_t))yajlpp_parse_context::handle_unused) {
|
||||
fprintf(stderr, " expecting one of the following data types --\n");
|
||||
}
|
||||
|
||||
fprintf(stderr,
|
||||
"warning:%s:%s:line %d:unexpected data, expecting one of the following data types --\n",
|
||||
ypc->ypc_source.c_str(),
|
||||
&ypc->ypc_path[0],
|
||||
line_number);
|
||||
if (ypc->ypc_callbacks.yajl_boolean != (int (*)(void *, int))yajlpp_parse_context::handle_unused) {
|
||||
fprintf(stderr, "warning:%s:%s: boolean\n",
|
||||
ypc->ypc_source.c_str(), &ypc->ypc_path[0]);
|
||||
fprintf(stderr, " boolean\n");
|
||||
}
|
||||
if (ypc->ypc_callbacks.yajl_integer != (int (*)(void *, long long))yajlpp_parse_context::handle_unused) {
|
||||
fprintf(stderr, "warning:%s:%s: integer\n",
|
||||
ypc->ypc_source.c_str(), &ypc->ypc_path[0]);
|
||||
fprintf(stderr, " integer\n");
|
||||
}
|
||||
if (ypc->ypc_callbacks.yajl_double != (int (*)(void *, double))yajlpp_parse_context::handle_unused) {
|
||||
fprintf(stderr, "warning:%s:%s: float\n",
|
||||
ypc->ypc_source.c_str(), &ypc->ypc_path[0]);
|
||||
fprintf(stderr, " float\n");
|
||||
}
|
||||
if (ypc->ypc_callbacks.yajl_string != (int (*)(void *, const unsigned char *, size_t))yajlpp_parse_context::handle_unused) {
|
||||
fprintf(stderr, "warning:%s:%s: string\n",
|
||||
ypc->ypc_source.c_str(), &ypc->ypc_path[0]);
|
||||
fprintf(stderr, " string\n");
|
||||
}
|
||||
|
||||
fprintf(stderr, "warning:%s:%s:accepted paths --\n",
|
||||
ypc->ypc_source.c_str(), &ypc->ypc_path[0]);
|
||||
for (int lpc = 0; ypc->ypc_handlers[lpc].jph_path[0]; lpc++) {
|
||||
fprintf(stderr, "warning:%s:%s: %s\n",
|
||||
ypc->ypc_source.c_str(),
|
||||
&ypc->ypc_path[0],
|
||||
ypc->ypc_handlers[lpc].jph_path);
|
||||
if (handler == NULL) {
|
||||
fprintf(stderr, " accepted paths --\n");
|
||||
for (int lpc = 0; ypc->ypc_handlers[lpc].jph_path[0]; lpc++) {
|
||||
fprintf(stderr, " %s\n",
|
||||
ypc->ypc_handlers[lpc].jph_path);
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
|
|
|
@ -98,7 +98,9 @@ struct json_path_handler_base {
|
|||
jph_simple_offset(NULL),
|
||||
jph_children(NULL),
|
||||
jph_min_length(0),
|
||||
jph_max_length(INT_MAX)
|
||||
jph_max_length(INT_MAX),
|
||||
jph_enum_values(NULL),
|
||||
jph_min_value(LLONG_MIN)
|
||||
{
|
||||
memset(&this->jph_callbacks, 0, sizeof(this->jph_callbacks));
|
||||
};
|
||||
|
@ -109,8 +111,7 @@ struct json_path_handler_base {
|
|||
pcrepp jph_regex;
|
||||
yajl_callbacks jph_callbacks;
|
||||
yajl_gen_status (*jph_gen_callback)(yajlpp_gen_context &, const json_path_handler_base &, yajl_gen);
|
||||
void (*jph_validator)(json_schema_validator &validator,
|
||||
const std::string &path,
|
||||
void (*jph_validator)(yajlpp_parse_context &ypc,
|
||||
const json_path_handler_base &jph);
|
||||
void *(*jph_obj_provider)(yajlpp_parse_context &ypc, void *root);
|
||||
const char * jph_synopsis;
|
||||
|
@ -119,6 +120,8 @@ struct json_path_handler_base {
|
|||
json_path_handler_base *jph_children;
|
||||
size_t jph_min_length;
|
||||
size_t jph_max_length;
|
||||
const intern_string_t *jph_enum_values;
|
||||
long long jph_min_value;
|
||||
};
|
||||
|
||||
int yajlpp_static_string(yajlpp_parse_context *, const unsigned char *, size_t);
|
||||
|
@ -126,9 +129,12 @@ int yajlpp_static_intern_string(yajlpp_parse_context *, const unsigned char *, s
|
|||
yajl_gen_status yajlpp_static_gen_string(yajlpp_gen_context &ygc,
|
||||
const json_path_handler_base &,
|
||||
yajl_gen);
|
||||
void yajlpp_validator_for_string(json_schema_validator &validator,
|
||||
const std::string &path,
|
||||
void yajlpp_validator_for_string(yajlpp_parse_context &ypc,
|
||||
const json_path_handler_base &jph);
|
||||
void yajlpp_validator_for_intern_string(yajlpp_parse_context &ypc,
|
||||
const json_path_handler_base &jph);
|
||||
void yajlpp_validator_for_int(yajlpp_parse_context &ypc,
|
||||
const json_path_handler_base &jph);
|
||||
|
||||
int yajlpp_static_number(yajlpp_parse_context *, long long);
|
||||
|
||||
|
@ -222,6 +228,16 @@ struct json_path_handler : public json_path_handler_base {
|
|||
return *this;
|
||||
}
|
||||
|
||||
json_path_handler &with_enum_values(const intern_string_t values[]) {
|
||||
this->jph_enum_values = values;
|
||||
return *this;
|
||||
}
|
||||
|
||||
json_path_handler &with_min_value(long long val) {
|
||||
this->jph_min_value = val;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename R, typename T>
|
||||
json_path_handler &with_obj_provider(R *(*provider)(yajlpp_parse_context &ypc, T *root)) {
|
||||
this->jph_obj_provider = (void *(*)(yajlpp_parse_context &, void *)) provider;
|
||||
|
@ -240,13 +256,14 @@ struct json_path_handler : public json_path_handler_base {
|
|||
this->add_cb(yajlpp_static_intern_string);
|
||||
this->jph_simple_offset = field;
|
||||
this->jph_gen_callback = yajlpp_static_gen_string;
|
||||
this->jph_validator = yajlpp_validator_for_string;
|
||||
this->jph_validator = yajlpp_validator_for_intern_string;
|
||||
return *this;
|
||||
};
|
||||
|
||||
json_path_handler &for_field(long long *field) {
|
||||
this->add_cb(yajlpp_static_number);
|
||||
this->jph_simple_offset = field;
|
||||
this->jph_validator = yajlpp_validator_for_int;
|
||||
return *this;
|
||||
};
|
||||
|
||||
|
@ -272,6 +289,9 @@ struct json_path_handler : public json_path_handler_base {
|
|||
|
||||
class yajlpp_parse_context {
|
||||
public:
|
||||
typedef void (*error_reporter_t)(const yajlpp_parse_context &ypc,
|
||||
const char *msg);
|
||||
|
||||
yajlpp_parse_context(const std::string &source,
|
||||
struct json_path_handler *handlers = NULL)
|
||||
: ypc_source(source),
|
||||
|
@ -378,6 +398,11 @@ public:
|
|||
return *this;
|
||||
};
|
||||
|
||||
yajlpp_parse_context &with_error_reporter(error_reporter_t err) {
|
||||
this->ypc_error_reporter = err;
|
||||
return *this;
|
||||
}
|
||||
|
||||
yajl_status parse(const unsigned char *jsonText, size_t jsonTextLen) {
|
||||
this->ypc_json_text = jsonText;
|
||||
|
||||
|
@ -393,10 +418,38 @@ public:
|
|||
return retval;
|
||||
};
|
||||
|
||||
int get_line_number() const {
|
||||
if (this->ypc_handle != NULL && this->ypc_json_text) {
|
||||
size_t consumed = yajl_get_bytes_consumed(this->ypc_handle);
|
||||
long current_count = std::count(&this->ypc_json_text[0],
|
||||
&this->ypc_json_text[consumed],
|
||||
'\n');
|
||||
|
||||
return this->ypc_line_number + current_count;
|
||||
}
|
||||
else {
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
yajl_status complete_parse() {
|
||||
return yajl_complete_parse(this->ypc_handle);
|
||||
};
|
||||
|
||||
void report_error(const char *format, ...) {
|
||||
va_list args;
|
||||
|
||||
va_start(args, format);
|
||||
if (this->ypc_error_reporter != NULL) {
|
||||
char buffer[1024];
|
||||
|
||||
vsnprintf(buffer, sizeof(buffer), format, args);
|
||||
|
||||
this->ypc_error_reporter(*this, buffer);
|
||||
}
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
const std::string ypc_source;
|
||||
int ypc_line_number;
|
||||
struct json_path_handler *ypc_handlers;
|
||||
|
@ -413,6 +466,7 @@ public:
|
|||
bool ypc_ignore_unused;
|
||||
const struct json_path_handler_base *ypc_current_handler;
|
||||
std::set<std::string> ypc_active_paths;
|
||||
error_reporter_t ypc_error_reporter;
|
||||
|
||||
void update_callbacks(const json_path_handler_base *handlers = NULL,
|
||||
int child_start = 0);
|
||||
|
@ -581,7 +635,7 @@ public:
|
|||
|
||||
iter = this->jsv_schema.js_path_to_handler.find(path);
|
||||
if (iter->second->jph_validator) {
|
||||
iter->second->jph_validator(*this, path, *iter->second);
|
||||
// iter->second->jph_validator(*this, path, *iter->second);
|
||||
}
|
||||
|
||||
return *this;
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
" ",
|
||||
{ "timestamp-format" : "abc %S def" },
|
||||
" ",
|
||||
{ "field" : "lvl" },
|
||||
{ "field" : "lvl", "min-width": 5 },
|
||||
" ",
|
||||
{ "field" : "msg" }
|
||||
],
|
||||
|
|
|
@ -54,10 +54,10 @@ run_test ${lnav_test} -n \
|
|||
${test_dir}/logfile_json2.json
|
||||
|
||||
check_output "timestamp-format not working" <<EOF
|
||||
2013-09-06T20:00:49.124 abc 49 def 0 Starting up service
|
||||
2013-09-06T22:00:49.124 abc 49 def 0 Shutting down service
|
||||
2013-09-06T20:00:49.124 abc 49 def 0 Starting up service
|
||||
2013-09-06T22:00:49.124 abc 49 def 0 Shutting down service
|
||||
user: steve@example.com
|
||||
2013-09-06T22:01:49.124 abc 49 def 10 looking bad
|
||||
2013-09-06T22:01:49.124 abc 49 def 10 looking bad
|
||||
EOF
|
||||
|
||||
run_test ${lnav_test} -n -d /tmp/lnav.err \
|
||||
|
|
Loading…
Reference in New Issue