res_cdrel_custom: Resolve several formatting issues.

Several issues are resolved:

* Internally, floats were used for timestamp values but this could result
in wrapping so they've been changed to doubles.

* Historically, the default CEL eventtime format is `<seconds>.<microseconds>`
with `<microseconds>` always being 6 digits.  This should have continued to be
the case but res_cdrel_custom wasn't checking the `dateformat` setting in
cel.conf and was defaulting to `%F %T`.  res_cdrel_custom now gets the default
date format from cel.conf, which will be whatever the `dateformat` parameter
is set to or `<seconds>.<microseconds>` if not set.

* The timeval field formatter for both CDR and CEL wasn't handling custom
strftime format strings correctly.  This is now fixed so you should be able
to specifiy custom strftime format strings for the CEL `eventtime` and CDR
`start`, `answer` and `end` fields.  For example: `eventtime(%FT%T%z)`.

Resolves: #1844
Resolves: #1845
This commit is contained in:
George Joseph
2026-03-31 10:16:48 -06:00
parent d29c79eeea
commit cc5542e753
8 changed files with 45 additions and 18 deletions

View File

@@ -106,7 +106,8 @@
;
; The default output format for the "start", "answer" and "end" timestamp fields
; is the "%Y-%m-%d %T" strftime string format however you can also format those
; fields as an int64 or a float: `start(int64),answer(float),end`.
; fields as an int64 or a double: `start(int64)` or `start(double)` or
; provide your own strftime format string: `start(%FT%T%z)`.
;
; The "disposition" and "amaflags" are formatted as their string names like
; "ANSWERED" and "DOCUMENTATION" by default but if you just want the numbers and

View File

@@ -111,9 +111,12 @@
; you can force the uniqueid field to not be quoted with `uniqueid(noquote)`. The
; example in fields above shows this.
;
; The default output format for the "EventTime" timestamp field is the "%Y-%m-%d %T"
; strftime string format however you can also format the field as an int64 or a
; float: `eventtime(int64)` or `eventtime(float)`.
; The default output format for the "EventTime" timestamp field is taken from the
; "dateformat" parameter in cel.conf. If that's not set, the default is
; "<seconds>.<microseconds>" where "<microseconds>" is always 6 digits with
; leading zeros. You can also format the field as an int64 or a double:
; `eventtime(int64)` or `eventtime(double)` or provide your own strftime
; format string: `eventtime(%FT%T%z)`.
;
; Unlike CDRs, the "amaflags" field is output as its numerical value by default
; for historical reasons. You can output it as its friendly string with

View File

@@ -78,12 +78,13 @@ enum cdrel_data_type {
cdrel_type_uservar,
cdrel_type_event_type,
cdrel_type_event_enum,
cdrel_type_cel_timefmt,
cdrel_data_type_strings_end,
cdrel_type_int32,
cdrel_type_uint32,
cdrel_type_int64,
cdrel_type_uint64,
cdrel_type_float,
cdrel_type_double,
cdrel_data_type_end
};
@@ -180,7 +181,7 @@ struct cdrel_value {
int64_t int64;
uint64_t uint64;
struct timeval tv;
float floater;
double doubler;
} values;
};

View File

@@ -432,6 +432,7 @@ static struct cdrel_field *field_alloc(struct cdrel_config *config, const char *
if (strchr(qualifier, '%') != NULL) {
data_swap = ast_strdupa(qualifier);
ast_set_flag(&field_flags, cdrel_flag_format_spec);
forced_output_data_type = cdrel_type_string;
ast_debug(3, " Using qualifier '%s' for field '%s' flags: %s\n", qualifier,
field_name, ast_str_tmp(128, cdrel_get_field_flags(&field_flags, &STR_TMP)));
}
@@ -466,6 +467,17 @@ static struct cdrel_field *field_alloc(struct cdrel_config *config, const char *
return NULL;
}
if (ast_test_flag(&field_flags, cdrel_flag_format_spec)
&& registered_field->input_data_type != cdrel_type_timeval) {
ast_log(LOG_WARNING, "%s->%s: Custom format '%s' ignored for field '%s'."
" Only timeval types can use custom format strings.\n",
cdrel_basename(config->config_filename), cdrel_basename(config->output_filename),
data, field_name);
forced_output_data_type = cdrel_data_type_end;
ast_clear_flag(&field_flags, cdrel_flag_format_spec);
data = NULL;
}
field = ast_calloc(1, sizeof(*registered_field) + strlen(input_field_template) + 1);
if (!field) {
return NULL;
@@ -1127,7 +1139,7 @@ static struct cdrel_config *load_text_file_legacy_config(enum cdrel_record_type
return NULL;
}
ast_log(LOG_NOTICE, "%s->%s: Logging %s records\n",
ast_log(LOG_NOTICE, "%s->%s: Logging legacy %s records as advanced\n",
cdrel_basename(config->config_filename), cdrel_basename(config->output_filename),
RECORD_TYPE_STR(config->record_type));

View File

@@ -118,7 +118,7 @@ DEFINE_FORMATTER(uint32, uint32, uint32_t, "%u")
DEFINE_FORMATTER(int32, int32, int32_t, "%d")
DEFINE_FORMATTER(uint64, uint64, uint64_t, "%lu")
DEFINE_FORMATTER(int64, int64, int64_t, "%ld")
DEFINE_FORMATTER(float, floater, float, "%.1f")
DEFINE_FORMATTER(double, doubler, double, "%.6f")
static int format_timeval(struct cdrel_config *config,
struct cdrel_field *field, struct cdrel_value *input_value, struct cdrel_value *output_value)
@@ -134,11 +134,20 @@ static int format_timeval(struct cdrel_config *config,
output_value->data_type = cdrel_type_int64;
output_value->values.int64 = input_value->values.tv.tv_sec;
return format_int64(config, field, output_value, output_value);
} else if (field->output_data_type == cdrel_type_float) {
output_value->data_type = cdrel_type_float;
output_value->values.floater = ((float)input_value->values.tv.tv_sec) + ((float)input_value->values.tv.tv_usec) / 1000000.0;
return format_float(config, field, output_value, output_value);
} else if (!ast_strlen_zero(field->data)) {
} else if (field->output_data_type == cdrel_type_double) {
output_value->data_type = cdrel_type_double;
output_value->values.doubler = ((double)input_value->values.tv.tv_sec) + ((double)input_value->values.tv.tv_usec) / 1000000.0;
return format_double(config, field, output_value, output_value);
} else if (field->output_data_type == cdrel_type_cel_timefmt) {
res = ast_cel_format_eventtime(input_value->values.tv, tempbuf, 64);
if (res != 0) {
return res;
}
input_value->values.string = tempbuf;
input_value->data_type = cdrel_type_string;
output_value->data_type = cdrel_type_string;
return format_string(config, field, input_value, output_value);
} else if (!ast_strlen_zero(field->data)) {
format = field->data;
}
@@ -186,7 +195,7 @@ int load_formatters(void)
cdrel_field_formatters[cdrel_type_int64] = format_int64;
cdrel_field_formatters[cdrel_type_uint64] = format_uint64;
cdrel_field_formatters[cdrel_type_timeval] = format_timeval;
cdrel_field_formatters[cdrel_type_float] = format_float;
cdrel_field_formatters[cdrel_type_double] = format_double;
cdrel_field_formatters[cdrel_type_amaflags] = format_amaflags;
cdrel_field_formatters[cdrel_type_disposition] = format_disposition;

View File

@@ -53,7 +53,7 @@ DEFINE_CDR_GETTER(uint32, uint32, uint32_t)
DEFINE_CDR_GETTER(int64, int64, int64_t)
DEFINE_CDR_GETTER(uint64, uint64, uint64_t)
DEFINE_CDR_GETTER(tv, timeval, struct timeval)
DEFINE_CDR_GETTER(floater, float, float)
DEFINE_CDR_GETTER(doubler, double, double)
static int cdr_get_literal(void *record, struct cdrel_config *config,
struct cdrel_field *field, struct cdrel_value *value)
@@ -108,7 +108,7 @@ int load_cdr(void)
cdrel_field_getters[cdrel_record_cdr][cdrel_type_int64] = cdr_get_int64;
cdrel_field_getters[cdrel_record_cdr][cdrel_type_uint64] = cdr_get_uint64;
cdrel_field_getters[cdrel_record_cdr][cdrel_type_timeval] = cdr_get_timeval;
cdrel_field_getters[cdrel_record_cdr][cdrel_type_float] = cdr_get_float;
cdrel_field_getters[cdrel_record_cdr][cdrel_type_double] = cdr_get_double;
cdrel_field_getters[cdrel_record_cdr][cdrel_type_uservar] = cdr_get_uservar;
cdrel_dummy_channel_allocators[cdrel_record_cdr] = dummy_chan_alloc_cdr;

View File

@@ -47,7 +47,7 @@
static const struct cdrel_field cdrel_field_registry[] = {
REGISTER_FIELD(cdrel_record_cel, AST_EVENT_IE_CEL_EVENT_ENUM, "eventenum", cdrel_type_event_enum, cdrel_type_string),
REGISTER_FIELD(cdrel_record_cel, AST_EVENT_IE_CEL_EVENT_TYPE, "eventtype", cdrel_type_event_type, cdrel_type_string),
REGISTER_FIELD(cdrel_record_cel, AST_EVENT_IE_CEL_EVENT_TIME, "eventtime", cdrel_type_timeval, cdrel_type_string),
REGISTER_FIELD(cdrel_record_cel, AST_EVENT_IE_CEL_EVENT_TIME, "eventtime", cdrel_type_timeval, cdrel_type_cel_timefmt),
REGISTER_FIELD(cdrel_record_cel, AST_EVENT_IE_CEL_EVENT_TIME_USEC, "eventtimeusec", cdrel_type_uint32, cdrel_type_uint32),
REGISTER_FIELD(cdrel_record_cel, AST_EVENT_IE_CEL_USEREVENT_NAME, "usereventname", cdrel_type_string, cdrel_type_string),
REGISTER_FIELD(cdrel_record_cel, AST_EVENT_IE_CEL_USEREVENT_NAME, "userdeftype", cdrel_type_string, cdrel_type_string),

View File

@@ -166,12 +166,13 @@ const char *cdrel_data_type_map[] = {
[cdrel_type_uservar] = "uservar",
[cdrel_type_event_type] = "event_type",
[cdrel_type_event_enum] = "event_enum",
[cdrel_type_cel_timefmt] = "cel_timefmt",
[cdrel_data_type_strings_end] = "!!STRINGS END!!",
[cdrel_type_int32] = "int32",
[cdrel_type_uint32] = "uint32",
[cdrel_type_int64] = "int64",
[cdrel_type_uint64] = "uint64",
[cdrel_type_float] = "float",
[cdrel_type_double] = "double",
[cdrel_data_type_end] = "!!END!!",
};