flt_otel_list_dump(&((p)->statuses)))
#define FLT_OTEL_DBG_CONF_SCOPE(h,p) \
- OTELC_DBG_STRUCT(DEBUG, h, h FLT_OTEL_CONF_HDR_FMT "%hhu %d %u %s %p %s %s %s }", (p), \
+ OTELC_DBG_STRUCT(DEBUG, h, h FLT_OTEL_CONF_HDR_FMT "%hhu %d %u %s %p %s %s %s %s }", (p), \
FLT_OTEL_CONF_HDR_ARGS(p, id), (p)->flag_used, (p)->event, (p)->idle_timeout, \
flt_otel_list_dump(&((p)->acls)), (p)->cond, flt_otel_list_dump(&((p)->contexts)), \
- flt_otel_list_dump(&((p)->spans)), flt_otel_list_dump(&((p)->spans_to_finish)))
+ flt_otel_list_dump(&((p)->spans)), flt_otel_list_dump(&((p)->spans_to_finish)), \
+ flt_otel_list_dump(&((p)->instruments)))
#define FLT_OTEL_DBG_CONF_GROUP(h,p) \
OTELC_DBG_STRUCT(DEBUG, h, h FLT_OTEL_CONF_HDR_FMT "%hhu %s }", (p), \
OTELC_DBG_STRUCT(DEBUG, h, h FLT_OTEL_CONF_HDR_FMT "%p }", (p), FLT_OTEL_CONF_HDR_ARGS(p, id), (p)->ptr)
#define FLT_OTEL_DBG_CONF_INSTR(h,p) \
- OTELC_DBG_STRUCT(DEBUG, h, h FLT_OTEL_CONF_HDR_FMT "'%s' %p %u %hhu %hhu 0x%02hhx %p:%s 0x%08x %u %s %s %s }", (p), \
- FLT_OTEL_CONF_HDR_ARGS(p, id), (p)->config, (p)->tracer, (p)->rate_limit, (p)->flag_harderr, \
+ OTELC_DBG_STRUCT(DEBUG, h, h FLT_OTEL_CONF_HDR_FMT "'%s' %p %p %u %hhu %hhu 0x%02hhx %p:%s 0x%08x %u %s %s %s }", (p), \
+ FLT_OTEL_CONF_HDR_ARGS(p, id), (p)->config, (p)->tracer, (p)->meter, (p)->rate_limit, (p)->flag_harderr, \
(p)->flag_disabled, (p)->logging, &((p)->proxy_log), flt_otel_list_dump(&((p)->proxy_log.loggers)), \
(p)->analyzers, (p)->idle_timeout, flt_otel_list_dump(&((p)->acls)), flt_otel_list_dump(&((p)->ph_groups)), \
flt_otel_list_dump(&((p)->ph_scopes)))
+#define FLT_OTEL_DBG_CONF_INSTRUMENT(h,p) \
+ OTELC_DBG_STRUCT(DEBUG, h, h FLT_OTEL_CONF_HDR_FMT "%" PRId64 " %d %d '%s' '%s' %s %p %zu %p %zu %p }", (p), \
+ FLT_OTEL_CONF_HDR_ARGS(p, id), (p)->idx, (p)->type, (p)->aggr_type, OTELC_STR_ARG((p)->description), \
+ OTELC_STR_ARG((p)->unit), flt_otel_list_dump(&((p)->samples)), (p)->attr, (p)->attr_len, (p)->ref, \
+ (p)->bounds_num, (p)->bounds)
+
#define FLT_OTEL_DBG_CONF(h,p) \
OTELC_DBG(DEBUG, h "%p:{ %p '%s' '%s' %p %s %s }", (p), \
(p)->proxy, (p)->id, (p)->cfg_file, (p)->instr, \
* flt_otel_conf_span->events (event_name -> OTELC_VALUE_STR(&extra))
* flt_otel_conf_span->baggages
* flt_otel_conf_span->statuses (status_code -> extra.u.value_int32)
+ * flt_otel_conf_instrument->samples
*/
struct flt_otel_conf_sample {
FLT_OTEL_CONF_HDR(key); /* The list containing sample names. */
struct list statuses; /* Span status code and description (only one per list). */
};
+/*
+ * Metric instrument configuration within a scope.
+ * flt_otel_conf_scope->instruments
+ */
+struct flt_otel_conf_instrument {
+ FLT_OTEL_CONF_HDR(id); /* The name of the instrument. */
+ int64_t idx; /* Meter instrument index (-1 if not yet created). */
+ otelc_metric_instrument_t type; /* Instrument type (or UPDATE). */
+ otelc_metric_aggregation_type_t aggr_type; /* Aggregation type for the view (create only). */
+ char *description; /* Instrument description (create only). */
+ char *unit; /* Instrument unit (create only). */
+ struct list samples; /* Sample expressions for the value. */
+ double *bounds; /* Histogram bucket boundaries (create only). */
+ size_t bounds_num; /* Number of histogram bucket boundaries. */
+ struct otelc_kv *attr; /* Instrument attributes (update only). */
+ size_t attr_len; /* Number of instrument attributes. */
+ struct flt_otel_conf_instrument *ref; /* Resolved create-form instrument (update only). */
+};
+
/* Configuration for a single event scope. */
struct flt_otel_conf_scope {
FLT_OTEL_CONF_HDR(id); /* The scope name. */
struct list contexts; /* Declared contexts. */
struct list spans; /* Declared spans. */
struct list spans_to_finish; /* The list of spans scheduled for finishing. */
+ struct list instruments; /* The list of metric instruments. */
};
/* Configuration for a named group of scopes. */
#define flt_otel_conf_ph_group flt_otel_conf_ph
#define flt_otel_conf_ph_scope flt_otel_conf_ph
-/* Top-level OTel instrumentation settings (tracer, options). */
+/* Top-level OTel instrumentation settings (tracer, meter, options). */
struct flt_otel_conf_instr {
FLT_OTEL_CONF_HDR(id); /* The OpenTelemetry instrumentation name. */
char *config; /* The OpenTelemetry configuration file name. */
struct otelc_tracer *tracer; /* The OpenTelemetry tracer handle. */
+ struct otelc_meter *meter; /* The OpenTelemetry meter handle. */
uint32_t rate_limit; /* [0 2^32-1] <-> [0.0 100.0] */
bool flag_harderr; /* [0 1] */
bool flag_disabled; /* [0 1] */
FLT_OTEL_CONF_FUNC_DECL(context)
FLT_OTEL_CONF_FUNC_DECL(span)
FLT_OTEL_CONF_FUNC_DECL(scope)
+FLT_OTEL_CONF_FUNC_DECL(instrument)
FLT_OTEL_CONF_FUNC_DECL(group)
FLT_OTEL_CONF_FUNC_DECL(instr)
/* Compare a runtime string against a compile-time string literal. */
#define FLT_OTEL_STR_CMP(S,s) ((s##_len == FLT_OTEL_STR_SIZE(S)) && (memcmp((s), FLT_OTEL_STR_ADDRSIZE(S)) == 0))
+/* Tolerance for double comparison in flt_otel_qsort_compar_double(). */
+#define FLT_OTEL_DBL_EPSILON 1e-9
+
/* Execute a statement exactly once across all invocations. */
#define FLT_OTEL_RUN_ONCE(f) do { static bool _f = 1; if (_f) { _f = 0; { f; } } } while (0)
#define FLT_OTEL_PARSE_SPAN_ROOT "root"
#define FLT_OTEL_PARSE_SPAN_PARENT "parent"
#define FLT_OTEL_PARSE_SPAN_LINK "link"
+#define FLT_OTEL_PARSE_INSTRUMENT_DESC "desc"
+#define FLT_OTEL_PARSE_INSTRUMENT_VALUE "value"
+#define FLT_OTEL_PARSE_INSTRUMENT_ATTR "attr"
+#define FLT_OTEL_PARSE_INSTRUMENT_UNIT "unit"
+#define FLT_OTEL_PARSE_INSTRUMENT_BOUNDS "bounds"
+#define FLT_OTEL_PARSE_INSTRUMENT_AGGR "aggr"
#define FLT_OTEL_PARSE_CTX_AUTONAME "-"
#define FLT_OTEL_PARSE_CTX_IGNORE_NAME '-'
#define FLT_OTEL_PARSE_CTX_USE_HEADERS "use-headers"
FLT_OTEL_PARSE_SCOPE_STATUS_DEF( OK, "ok" ) \
FLT_OTEL_PARSE_SCOPE_STATUS_DEF( ERROR, "error" )
+/* Sentinel: instrument has not been created yet. */
+#define OTELC_METRIC_INSTRUMENT_UNSET -1
+
+/* Sentinel: instrument creation is in progress by another thread. */
+#define OTELC_METRIC_INSTRUMENT_PENDING -2
+
+/* Sentinel: update-form instrument (re-evaluates an existing one). */
+#define OTELC_METRIC_INSTRUMENT_UPDATE 0xff
+
+#define OTELC_METRIC_AGGREGATION_UNSET -1
+
+/*
+ * Observable (asynchronous) instruments are not supported. The OTel SDK
+ * invokes their callbacks from an external background thread that is not a
+ * HAProxy thread. HAProxy sample fetches rely on internal per-thread-group
+ * state and return incorrect results when called from a non-HAProxy thread.
+ *
+ * Double-precision instruments are not supported because HAProxy sample fetches
+ * do not return double values.
+ */
+#define FLT_OTEL_PARSE_SCOPE_INSTRUMENT_DEFINES \
+ FLT_OTEL_PARSE_SCOPE_INSTRUMENT_DEF(UPDATE, "update" ) \
+ FLT_OTEL_PARSE_SCOPE_INSTRUMENT_DEF(COUNTER_UINT64, "cnt_int" ) \
+ FLT_OTEL_PARSE_SCOPE_INSTRUMENT_DEF(HISTOGRAM_UINT64, "hist_int" ) \
+ FLT_OTEL_PARSE_SCOPE_INSTRUMENT_DEF(UDCOUNTER_INT64, "udcnt_int") \
+ FLT_OTEL_PARSE_SCOPE_INSTRUMENT_DEF(GAUGE_INT64, "gauge_int")
+
/*
* In case the possibility of working with OpenTelemetry context via HAProxy
* variables is not used, args_max member of the structure flt_otel_parse_data
* because in this case the 'use-vars' argument cannot be entered anyway,
* so I will not complicate it here with additional definitions.
*/
-#define FLT_OTEL_PARSE_SCOPE_DEFINES \
- FLT_OTEL_PARSE_SCOPE_DEF( ID, 0, CHAR, 2, 2, "otel-scope", " <name>") \
- FLT_OTEL_PARSE_SCOPE_DEF( SPAN, 0, NONE, 2, 7, "span", " <name> [<reference>] [<link>] [root]") \
- FLT_OTEL_PARSE_SCOPE_DEF( LINK, 1, NONE, 2, 0, "link", " <span> ...") \
- FLT_OTEL_PARSE_SCOPE_DEF( ATTRIBUTE, 1, NONE, 3, 0, "attribute", " <key> <sample> ...") \
- FLT_OTEL_PARSE_SCOPE_DEF( EVENT, 1, NONE, 4, 0, "event", " <name> <key> <sample> ...") \
- FLT_OTEL_PARSE_SCOPE_DEF( BAGGAGE, 1, VAR, 3, 0, "baggage", " <key> <sample> ...") \
- FLT_OTEL_PARSE_SCOPE_DEF( INJECT, 1, CTX, 2, 4, "inject", FLT_OTEL_PARSE_SCOPE_INJECT_HELP) \
- FLT_OTEL_PARSE_SCOPE_DEF( EXTRACT, 0, CTX, 2, 3, "extract", FLT_OTEL_PARSE_SCOPE_EXTRACT_HELP) \
- FLT_OTEL_PARSE_SCOPE_DEF( STATUS, 1, NONE, 2, 0, "status", " <code> [<sample> ...]") \
- FLT_OTEL_PARSE_SCOPE_DEF( FINISH, 0, NONE, 2, 0, "finish", " <name> ...") \
- FLT_OTEL_PARSE_SCOPE_DEF(IDLE_TIMEOUT, 0, NONE, 2, 2, "idle-timeout", " <time>") \
- FLT_OTEL_PARSE_SCOPE_DEF( ACL, 0, CHAR, 3, 0, "acl", " <name> <criterion> [flags] [operator] <value> ...") \
+#define FLT_OTEL_PARSE_SCOPE_DEFINES \
+ FLT_OTEL_PARSE_SCOPE_DEF( ID, 0, CHAR, 2, 2, "otel-scope", " <name>") \
+ FLT_OTEL_PARSE_SCOPE_DEF( SPAN, 0, NONE, 2, 7, "span", " <name> [<reference>] [<link>] [root]") \
+ FLT_OTEL_PARSE_SCOPE_DEF( LINK, 1, NONE, 2, 0, "link", " <span> ...") \
+ FLT_OTEL_PARSE_SCOPE_DEF( ATTRIBUTE, 1, NONE, 3, 0, "attribute", " <key> <sample> ...") \
+ FLT_OTEL_PARSE_SCOPE_DEF( EVENT, 1, NONE, 4, 0, "event", " <name> <key> <sample> ...") \
+ FLT_OTEL_PARSE_SCOPE_DEF( BAGGAGE, 1, VAR, 3, 0, "baggage", " <key> <sample> ...") \
+ FLT_OTEL_PARSE_SCOPE_DEF( INJECT, 1, CTX, 2, 4, "inject", FLT_OTEL_PARSE_SCOPE_INJECT_HELP) \
+ FLT_OTEL_PARSE_SCOPE_DEF( EXTRACT, 0, CTX, 2, 3, "extract", FLT_OTEL_PARSE_SCOPE_EXTRACT_HELP) \
+ FLT_OTEL_PARSE_SCOPE_DEF( STATUS, 1, NONE, 2, 0, "status", " <code> [<sample> ...]") \
+ FLT_OTEL_PARSE_SCOPE_DEF( FINISH, 0, NONE, 2, 0, "finish", " <name> ...") \
+ FLT_OTEL_PARSE_SCOPE_DEF( INSTRUMENT, 0, NONE, 3, 0, "instrument", " { update <name> [<attr> ...] | <type> <name> [<aggr>] [<desc>] [<unit>] <value> [<bounds>] }") \
+ FLT_OTEL_PARSE_SCOPE_DEF(IDLE_TIMEOUT, 0, NONE, 2, 2, "idle-timeout", " <time>") \
+ FLT_OTEL_PARSE_SCOPE_DEF( ACL, 0, CHAR, 3, 0, "acl", " <name> <criterion> [flags] [operator] <value> ...") \
FLT_OTEL_PARSE_SCOPE_DEF( ON_EVENT, 0, NONE, 2, 0, "otel-event", " <name> [{ if | unless } <condition>]")
/* Invalid character check modes for identifier validation. */
/* Concatenate argument array elements into a single string. */
int flt_otel_args_concat(const char **args, int idx, int n, char **str);
+/* Comparator for qsort: ascending order of doubles with epsilon tolerance. */
+int flt_otel_qsort_compar_double(const void *a, const void *b);
+
/* Parse a string to double with range validation. */
bool flt_otel_strtod(const char *nptr, double *value, double limit_min, double limit_max, char **err);
* Handles the "otel status" CLI command. Builds a formatted status report
* for all OTel filter instances across all proxies. The report includes
* the library version, proxy name, configuration file path, group and scope
- * counts, disable counts, instrumentation ID, tracer state, rate limit, error
- * mode, disabled state, logging state, and analyzer bits. When DEBUG_OTEL is
- * enabled, the current debug level is also included.
+ * counts, disable counts, instrumentation ID, tracer and meter state, rate
+ * limit, error mode, disabled state, logging state, and analyzer bits. When
+ * DEBUG_OTEL is enabled, the current debug level is also included.
*
* RETURN VALUE
* Returns 1, or 0 on memory allocation failure.
(void)memprintf(&msg, "%s instrumentation %s\n", msg, conf->instr->id);
(void)memprintf(&msg, "%s configuration: %s\n", msg, conf->instr->config);
(void)memprintf(&msg, "%s tracer: %s\n", msg, (conf->instr->tracer != NULL) ? "active" : "not initialized");
+ (void)memprintf(&msg, "%s meter: %s\n", msg, (conf->instr->meter != NULL) ? "active" : "not initialized");
(void)memprintf(&msg, "%s rate limit: %.2f %%\n", msg, FLT_OTEL_U32_FLOAT(_HA_ATOMIC_LOAD(&(conf->instr->rate_limit))));
(void)memprintf(&msg, "%s hard errors: %s\n", msg, FLT_OTEL_STR_FLAG_YN(_HA_ATOMIC_LOAD(&(conf->instr->flag_harderr))));
(void)memprintf(&msg, "%s disabled: %s\n", msg, FLT_OTEL_STR_FLAG_YN(_HA_ATOMIC_LOAD(&(conf->instr->flag_disabled))));
)
+/***
+ * NAME
+ * flt_otel_conf_instrument_init - conf_instrument structure allocation
+ *
+ * SYNOPSIS
+ * struct flt_otel_conf_instrument *flt_otel_conf_instrument_init(const char *id, int line, struct list *head, char **err)
+ *
+ * ARGUMENTS
+ * id - identifier string to duplicate
+ * line - configuration file line number
+ * head - list to append to (or NULL)
+ * err - indirect pointer to error message string
+ *
+ * DESCRIPTION
+ * Allocates and initializes a conf_instrument structure. Sets the instrument
+ * type and meter index to OTELC_METRIC_INSTRUMENT_UNSET and initializes the
+ * samples list. The <id> string is duplicated and stored as the instrument
+ * name. If <head> is non-NULL, the structure is appended to the list.
+ *
+ * RETURN VALUE
+ * Returns a pointer to the initialized structure, or NULL on failure.
+ */
+FLT_OTEL_CONF_FUNC_INIT(instrument, id,
+ retptr->idx = OTELC_METRIC_INSTRUMENT_UNSET;
+ retptr->type = OTELC_METRIC_INSTRUMENT_UNSET;
+ retptr->aggr_type = OTELC_METRIC_AGGREGATION_UNSET;
+ LIST_INIT(&(retptr->samples));
+)
+
+
+/***
+ * NAME
+ * flt_otel_conf_instrument_free - conf_instrument structure deallocation
+ *
+ * SYNOPSIS
+ * void flt_otel_conf_instrument_free(struct flt_otel_conf_instrument **ptr)
+ *
+ * ARGUMENTS
+ * ptr - a pointer to the address of a structure
+ *
+ * DESCRIPTION
+ * Deallocates memory used by the flt_otel_conf_instrument structure and its
+ * contents, then removes it from the list of structures of that type.
+ *
+ * RETURN VALUE
+ * This function does not return a value.
+ */
+FLT_OTEL_CONF_FUNC_FREE(instrument, id,
+ FLT_OTEL_DBG_CONF_INSTRUMENT("- conf_instrument free ", *ptr);
+
+ OTELC_SFREE((*ptr)->description);
+ OTELC_SFREE((*ptr)->unit);
+ FLT_OTEL_LIST_DESTROY(sample, &((*ptr)->samples));
+ OTELC_SFREE((*ptr)->bounds);
+ otelc_kv_destroy(&((*ptr)->attr), (*ptr)->attr_len);
+)
+
+
/***
* NAME
* flt_otel_conf_scope_init - conf_scope structure allocation
LIST_INIT(&(retptr->contexts));
LIST_INIT(&(retptr->spans));
LIST_INIT(&(retptr->spans_to_finish));
+ LIST_INIT(&(retptr->instruments));
)
FLT_OTEL_LIST_DESTROY(context, &((*ptr)->contexts));
FLT_OTEL_LIST_DESTROY(span, &((*ptr)->spans));
FLT_OTEL_LIST_DESTROY(str, &((*ptr)->spans_to_finish));
+ FLT_OTEL_LIST_DESTROY(instrument, &((*ptr)->instruments));
)
#undef FLT_OTEL_EVENT_DEF
+/***
+ * NAME
+ * flt_otel_scope_run_instrument_record - metric instrument value recorder
+ *
+ * SYNOPSIS
+ * static int flt_otel_scope_run_instrument_record(struct stream *s, uint dir, struct otelc_meter *meter, struct flt_otel_conf_instrument *instr_ref, struct flt_otel_conf_instrument *instr, char **err)
+ *
+ * ARGUMENTS
+ * s - the stream providing the sample context
+ * dir - the sample fetch direction (SMP_OPT_DIR_REQ/RES)
+ * meter - the OTel meter instance
+ * instr_ref - the create-form instrument providing samples and meter index
+ * instr - the update-form instrument providing per-scope attributes
+ * err - indirect pointer to error message string
+ *
+ * DESCRIPTION
+ * Evaluates sample expressions from a create-form instrument and records
+ * the resulting value via the <meter> API. Each expression is evaluated
+ * with sample_process(), converted to an otelc_value via
+ * flt_otel_sample_to_value(), and recorded via
+ * <meter>->update_instrument_kv_n().
+ *
+ * RETURN VALUE
+ * Returns FLT_OTEL_RET_OK on success, FLT_OTEL_RET_ERROR on failure.
+ */
+static int flt_otel_scope_run_instrument_record(struct stream *s, uint dir, struct otelc_meter *meter, struct flt_otel_conf_instrument *instr_ref, struct flt_otel_conf_instrument *instr, char **err)
+{
+ struct flt_otel_conf_sample *sample;
+ struct flt_otel_conf_sample_expr *expr;
+ struct sample smp;
+ struct otelc_value value;
+ int retval = FLT_OTEL_RET_OK;
+
+ OTELC_FUNC("%p, %u, %p, %p, %p, %p:%p", s, dir, meter, instr_ref, instr, OTELC_DPTR_ARGS(err));
+
+ /* The samples list always contains exactly one entry. */
+ sample = LIST_NEXT(&(instr_ref->samples), struct flt_otel_conf_sample *, list);
+
+ (void)memset(&smp, 0, sizeof(smp));
+
+ if (sample->lf_used) {
+ /*
+ * Log-format path: evaluate into a temporary buffer and present
+ * the result as a string sample.
+ */
+ smp.data.u.str.area = OTELC_CALLOC(1, global.tune.bufsize);
+ if (smp.data.u.str.area == NULL) {
+ FLT_OTEL_ERR("out of memory");
+
+ OTELC_RETURN_INT(FLT_OTEL_RET_ERROR);
+ }
+
+ smp.data.type = SMP_T_STR;
+ smp.data.u.str.data = build_logline(s, smp.data.u.str.area, global.tune.bufsize, &(sample->lf_expr));
+ } else {
+ /* The expressions list always contains exactly one entry. */
+ expr = LIST_NEXT(&(sample->exprs), struct flt_otel_conf_sample_expr *, list);
+
+ FLT_OTEL_DBG_CONF_SAMPLE_EXPR("sample expression ", expr);
+
+ if (sample_process(s->be, s->sess, s, dir | SMP_OPT_FINAL, expr->expr, &smp) == NULL) {
+ OTELC_DBG(NOTICE, "WARNING: failed to fetch '%s'", expr->fmt_expr);
+
+ retval = FLT_OTEL_RET_ERROR;
+ }
+ }
+
+ if (retval == FLT_OTEL_RET_ERROR) {
+ /* Do nothing. */
+ }
+ else if (flt_otel_sample_to_value(sample->key, &(smp.data), &value, err) == FLT_OTEL_RET_ERROR) {
+ if (value.u_type == OTELC_VALUE_DATA)
+ OTELC_SFREE(value.u.value_data);
+
+ retval = FLT_OTEL_RET_ERROR;
+ }
+ else {
+ OTELC_DBG_VALUE(DEBUG, "value ", &value);
+
+ /*
+ * Metric instruments expect numeric values (INT64 or DOUBLE).
+ * Reject OTELC_VALUE_DATA since the meter cannot interpret
+ * arbitrary string data as a numeric measurement.
+ */
+ if (value.u_type == OTELC_VALUE_DATA) {
+ OTELC_DBG(NOTICE, "WARNING: non-numeric value type for instrument '%s'", instr_ref->id);
+
+ if (otelc_value_strtonum(&value, OTELC_VALUE_INT64) == OTELC_RET_ERROR) {
+ OTELC_SFREE(value.u.value_data);
+
+ retval = FLT_OTEL_RET_ERROR;
+ }
+ }
+
+ if (retval != FLT_OTEL_RET_ERROR)
+ if (OTELC_OPS(meter, update_instrument_kv_n, HA_ATOMIC_LOAD(&(instr_ref->idx)), &value, instr->attr, instr->attr_len) == OTELC_RET_ERROR)
+ retval = FLT_OTEL_RET_ERROR;
+ }
+
+ if (sample->lf_used)
+ OTELC_SFREE(smp.data.u.str.area);
+
+ OTELC_RETURN_INT(retval);
+}
+
+
+/***
+ * NAME
+ * flt_otel_scope_run_instrument - metric instrument processor
+ *
+ * SYNOPSIS
+ * static int flt_otel_scope_run_instrument(struct stream *s, uint dir, struct flt_otel_conf_scope *scope, struct otelc_meter *meter, char **err)
+ *
+ * ARGUMENTS
+ * s - the stream providing the sample context
+ * dir - the sample fetch direction (SMP_OPT_DIR_REQ/RES)
+ * scope - the scope configuration containing the instrument list
+ * meter - the OTel meter instance
+ * err - indirect pointer to error message string
+ *
+ * DESCRIPTION
+ * Processes all metric instruments configured in <scope>. Runs in two
+ * passes: the first pass lazily creates create-form instruments via <meter>
+ * on first use, using HA_ATOMIC_CAS on the instrument index to guarantee
+ * thread-safe one-time initialization. The second pass iterates over
+ * update-form instruments and records measurements via
+ * flt_otel_scope_run_instrument_record(). Instruments whose index is still
+ * negative (UNUSED or PENDING) are skipped, so that a concurrent creation by
+ * another thread does not cause an invalid <meter> access.
+ *
+ * RETURN VALUE
+ * Returns FLT_OTEL_RET_OK on success, FLT_OTEL_RET_ERROR on failure.
+ */
+static int flt_otel_scope_run_instrument(struct stream *s, uint dir, struct flt_otel_conf_scope *scope, struct otelc_meter *meter, char **err)
+{
+ struct flt_otel_conf_instrument *conf_instr;
+ int retval = FLT_OTEL_RET_OK;
+
+ OTELC_FUNC("%p, %u, %p, %p, %p:%p", s, dir, scope, meter, OTELC_DPTR_ARGS(err));
+
+ list_for_each_entry(conf_instr, &(scope->instruments), list) {
+ if (conf_instr->type == OTELC_METRIC_INSTRUMENT_UPDATE) {
+ /* Do nothing. */
+ }
+ else if (HA_ATOMIC_LOAD(&(conf_instr->idx)) == OTELC_METRIC_INSTRUMENT_UNSET) {
+ int64_t expected = OTELC_METRIC_INSTRUMENT_UNSET;
+ int rc;
+
+ OTELC_DBG(DEBUG, "run instrument '%s' -> '%s'", scope->id, conf_instr->id);
+ FLT_OTEL_DBG_CONF_INSTRUMENT("", conf_instr);
+
+ /*
+ * Create form: use this instrument directly. Lazily
+ * create the instrument on first use. Use CAS to
+ * ensure only one thread performs the creation in a
+ * multi-threaded environment.
+ */
+ if (!HA_ATOMIC_CAS(&(conf_instr->idx), &expected, OTELC_METRIC_INSTRUMENT_PENDING))
+ continue;
+
+ /*
+ * The view must be created before the instrument,
+ * otherwise bucket boundaries cannot be set.
+ */
+ if ((conf_instr->bounds != NULL) && (conf_instr->bounds_num > 0))
+ if (OTELC_OPS(meter, add_view, conf_instr->id, conf_instr->description, conf_instr->id, conf_instr->unit, conf_instr->type, conf_instr->aggr_type, conf_instr->bounds, conf_instr->bounds_num) == OTELC_RET_ERROR)
+ OTELC_DBG(NOTICE, "WARNING: failed to add view for instrument '%s'", conf_instr->id);
+
+ rc = OTELC_OPS(meter, create_instrument, conf_instr->id, conf_instr->description, conf_instr->unit, conf_instr->type, NULL);
+ if (rc == OTELC_RET_ERROR) {
+ OTELC_DBG(NOTICE, "WARNING: failed to create instrument '%s'", conf_instr->id);
+
+ HA_ATOMIC_STORE(&(conf_instr->idx), OTELC_METRIC_INSTRUMENT_UNSET);
+
+ retval = FLT_OTEL_RET_ERROR;
+
+ continue;
+ } else {
+ HA_ATOMIC_STORE(&(conf_instr->idx), rc);
+ }
+ }
+ }
+
+ list_for_each_entry(conf_instr, &(scope->instruments), list)
+ if (conf_instr->type == OTELC_METRIC_INSTRUMENT_UPDATE) {
+ struct flt_otel_conf_instrument *instr = conf_instr->ref;
+
+ OTELC_DBG(DEBUG, "run instrument '%s' -> '%s'", scope->id, conf_instr->id);
+ FLT_OTEL_DBG_CONF_INSTRUMENT("", conf_instr);
+
+ /*
+ * Update form: record a measurement using an existing
+ * create-form instrument.
+ */
+ if (instr == NULL) {
+ OTELC_DBG(NOTICE, "WARNING: invalid reference instrument '%s'", conf_instr->id);
+
+ retval = FLT_OTEL_RET_ERROR;
+ }
+ else if (HA_ATOMIC_LOAD(&(instr->idx)) < 0) {
+ OTELC_DBG(NOTICE, "WARNING: instrument '%s' not yet created, skipping", instr->id);
+ }
+ else if (flt_otel_scope_run_instrument_record(s, dir, meter, instr, conf_instr, err) == FLT_OTEL_RET_ERROR) {
+ retval = FLT_OTEL_RET_ERROR;
+ }
+ }
+
+ OTELC_RETURN_INT(retval);
+}
+
+
/***
* NAME
* flt_otel_scope_run_span - single span execution
flt_otel_scope_data_free(&data);
}
+ /* Process metric instruments. */
+ if (!LIST_ISEMPTY(&(conf_scope->instruments)))
+ if (flt_otel_scope_run_instrument(s, dir, conf_scope, conf->instr->meter, err) == FLT_OTEL_RET_ERROR)
+ retval = FLT_OTEL_RET_ERROR;
+
/* Mark the configured spans for finishing and clean up. */
list_for_each_entry(span_to_finish, &(conf_scope->spans_to_finish), list)
if (flt_otel_scope_finish_mark(f->ctx, span_to_finish->str, span_to_finish->str_len) == FLT_OTEL_RET_ERROR)
FLT_OTEL_ERR("%s", "failed to initialize OpenTelemetry tracer");
OTELC_RETURN_INT(retval);
+ }
+
+ instr->meter = otelc_meter_create(err);
+ if (instr->meter == NULL) {
+ if (*err == NULL)
+ FLT_OTEL_ERR("%s", "failed to initialize OpenTelemetry meter");
} else {
otelc_ext_init(flt_otel_mem_malloc, flt_otel_mem_free, flt_otel_thread_id);
otelc_log_set_handler(flt_otel_log_handler_cb, NULL, false);
{
struct flt_otel_conf **conf = (fconf == NULL) ? NULL : (typeof(conf))&(fconf->conf);
struct otelc_tracer *otel_tracer = NULL;
+ struct otelc_meter *otel_meter = NULL;
#ifdef DEBUG_OTEL
char buffer[BUFSIZ];
int i;
* still point to the HAProxy pool allocator; otelc_deinit() resets
* those callbacks, so it runs last.
*/
- if ((*conf)->instr != NULL)
+ if ((*conf)->instr != NULL) {
otel_tracer = (*conf)->instr->tracer;
+ otel_meter = (*conf)->instr->meter;
+ }
flt_otel_conf_free(conf);
OTELC_MEMINFO();
flt_otel_pool_destroy();
- otelc_deinit(&otel_tracer, NULL, NULL);
+ otelc_deinit(&otel_tracer, &otel_meter, NULL);
OTELC_RETURN();
}
else if (span_root_cnt > 1)
FLT_OTEL_ALERT("''%s' : multiple spans are marked as the root span'", conf->id);
+ OTELC_DBG(DEBUG, "- defined instruments ----------");
+
+ /*
+ * Validate update-form instruments: for each one, resolve its reference
+ * to the matching create-form instrument definition.
+ *
+ * Validate create-form instruments: check that names are unique across
+ * all scopes.
+ */
+ list_for_each_entry(conf_scope, &(conf->scopes), list) {
+ struct flt_otel_conf_instrument *conf_instr, *instr;
+ struct flt_otel_conf_scope *scope;
+
+ list_for_each_entry(conf_instr, &(conf_scope->instruments), list) {
+ if (conf_instr->type == OTELC_METRIC_INSTRUMENT_UPDATE) {
+ FLT_OTEL_DBG_CONF_INSTRUMENT(" update ", conf_instr);
+
+ /*
+ * Search all scopes for a create-form instrument
+ * whose name matches this update-form instrument.
+ */
+ list_for_each_entry(scope, &(conf->scopes), list) {
+ list_for_each_entry(instr, &(scope->instruments), list) {
+ if ((instr->type != OTELC_METRIC_INSTRUMENT_UPDATE) && (strcmp(instr->id, conf_instr->id) == 0))
+ conf_instr->ref = instr;
+
+ if (conf_instr->ref != NULL)
+ break;
+ }
+
+ if (conf_instr->ref != NULL)
+ break;
+ }
+
+ if (conf_instr->ref == NULL) {
+ FLT_OTEL_ALERT("''%s' : update-form instrument has no matching create-form definition'", conf_instr->id);
+
+ retval++;
+ }
+ } else {
+ bool flag_past = false, flag_dup = false;
+
+ FLT_OTEL_DBG_CONF_INSTRUMENT(" create ", conf_instr);
+
+ if (LIST_ISEMPTY(&(conf_instr->samples))) {
+ FLT_OTEL_ALERT("''%s' : create-form instrument '%s' has no value expression'", conf->id, conf_instr->id);
+
+ retval++;
+ }
+
+ if ((conf_instr->aggr_type == OTELC_METRIC_AGGREGATION_UNSET) && (conf_instr->type == OTELC_METRIC_INSTRUMENT_HISTOGRAM_UINT64))
+ conf_instr->aggr_type = OTELC_METRIC_AGGREGATION_HISTOGRAM;
+
+ /*
+ * Checking that create-form instrument names
+ * are unique across all scopes. Only compare
+ * forward to avoid reporting the same pair
+ * twice.
+ */
+ list_for_each_entry(scope, &(conf->scopes), list) {
+ list_for_each_entry(instr, &(scope->instruments), list)
+ if (instr == conf_instr) {
+ flag_past = true;
+
+ continue;
+ }
+ else if (!flag_past || (instr->type == OTELC_METRIC_INSTRUMENT_UPDATE)) {
+ continue;
+ }
+ else if (strcmp(instr->id, conf_instr->id) == 0) {
+ FLT_OTEL_ALERT("''%s' : duplicated create-form instrument '%s''", conf->id, conf_instr->id);
+
+ retval++;
+
+ flag_dup = true;
+ break;
+ }
+
+ if (flag_dup)
+ break;
+ }
+ }
+ }
+ }
+
FLT_OTEL_DBG_LIST(conf, group, "", "defined", _group,
FLT_OTEL_DBG_CONF_GROUP(" ", _group);
FLT_OTEL_DBG_LIST(_group, ph_scope, " ", "used", _scope, FLT_OTEL_DBG_CONF_PH(" ", _scope)));
if (retval == OTELC_RET_ERROR)
FLT_OTEL_ALERT("%s", conf->instr->tracer->err);
+ if (retval != OTELC_RET_ERROR) {
+ retval = OTELC_OPS(conf->instr->meter, start);
+ if (retval == OTELC_RET_ERROR)
+ FLT_OTEL_ALERT("%s", conf->instr->meter->err);
+ }
+
if (retval != FLT_OTEL_RET_ERROR)
fconf->flags |= FLT_CFG_FL_HTX;
} else {
}
+/***
+ * NAME
+ * flt_otel_parse_bounds - histogram boundary string parser
+ *
+ * SYNOPSIS
+ * static int flt_otel_parse_bounds(const char *str, double **bounds, size_t *bounds_num, char **err, const char *err_msg)
+ *
+ * ARGUMENTS
+ * str - space-separated numeric boundary string
+ * bounds - pointer to the destination boundary array
+ * bounds_num - pointer to store the number of boundaries
+ * err - indirect pointer to error message string
+ * err_msg - context label used in error messages
+ *
+ * DESCRIPTION
+ * Parses a space-separated string of numbers into a dynamically allocated
+ * array of doubles suitable for the meter add_view API. The string is
+ * duplicated internally and tokenized with strtok(). Each token is
+ * converted with flt_otel_strtod(). The values are sorted internally.
+ *
+ * RETURN VALUE
+ * Returns ERR_NONE (== 0) in case of success,
+ * or a combination of ERR_* flags if an error is encountered.
+ */
+static int flt_otel_parse_bounds(const char *str, double **bounds, size_t *bounds_num, char **err, const char *err_msg)
+{
+ char *buffer, *token, *lasts;
+ size_t bounds_len = 0, bounds_size = 8;
+ double value, *ptr;
+ int retval = ERR_NONE;
+
+ OTELC_FUNC("\"%s\", %p, %p, %p:%p, \"%s\"", OTELC_STR_ARG(str), bounds, bounds_num, OTELC_DPTR_ARGS(err), OTELC_STR_ARG(err_msg));
+
+ buffer = OTELC_STRDUP(str);
+ *bounds = OTELC_CALLOC(bounds_size, sizeof(**bounds));
+ if ((buffer == NULL) || (*bounds == NULL)) {
+ OTELC_SFREE(buffer);
+ OTELC_SFREE(*bounds);
+
+ FLT_OTEL_PARSE_ERR(err, "'%s' : out of memory", err_msg);
+
+ OTELC_RETURN_INT(retval);
+ }
+
+ /* Tokenize and parse space-separated boundary values. */
+ for (token = strtok_r(buffer, " \t", &lasts); token != NULL; token = strtok_r(NULL, " \t", &lasts)) {
+ if (!flt_otel_strtod(token, &value, 0.0, DBL_MAX, err)) {
+ retval |= ERR_ABORT | ERR_ALERT;
+
+ break;
+ }
+ else if (bounds_len >= bounds_size) {
+ ptr = OTELC_REALLOC(*bounds, (bounds_size + 8) * sizeof(*ptr));
+ if (ptr == NULL) {
+ FLT_OTEL_PARSE_ERR(err, "'%s' : out of memory", err_msg);
+
+ OTELC_SFREE_CLEAR(*bounds);
+
+ break;
+ }
+
+ *bounds = ptr;
+ bounds_size += 8;
+ }
+
+ (*bounds)[bounds_len++] = value;
+ }
+
+ /* Sort the bounds and reject duplicates. */
+ if ((*bounds != NULL) && (bounds_len > 1)) {
+ size_t i;
+
+ qsort(*bounds, bounds_len, sizeof(**bounds), flt_otel_qsort_compar_double);
+
+ for (i = 1; i < bounds_len; i++)
+ if (flt_otel_qsort_compar_double(*bounds + i - 1, *bounds + i) == 0) {
+ FLT_OTEL_PARSE_ERR(err, "'%s' : duplicate boundary value '%.2f'", err_msg, (*bounds)[i]);
+
+ OTELC_SFREE_CLEAR(*bounds);
+
+ break;
+ }
+ }
+
+ OTELC_SFREE(buffer);
+
+ if (*bounds == NULL) {
+ *bounds_num = 0;
+ }
+ else if (bounds_len == 0) {
+ FLT_OTEL_PARSE_ERR(err, "'%s' : empty bounds", err_msg);
+
+ OTELC_SFREE_CLEAR(*bounds);
+ *bounds_num = 0;
+ }
+ else {
+ *bounds_num = bounds_len;
+ }
+
+ OTELC_RETURN_INT(retval);
+}
+
+
+/***
+ * NAME
+ * flt_otel_parse_cfg_instrument - instrument keyword parser
+ *
+ * SYNOPSIS
+ * static int flt_otel_parse_cfg_instrument(const char *file, int line, char **args, const struct flt_otel_parse_data *pdata, char **err)
+ *
+ * ARGUMENTS
+ * file - configuration file path
+ * line - configuration file line number
+ * args - configuration line arguments array
+ * pdata - keyword metadata (name, usage, argument limits)
+ * err - indirect pointer to error message string
+ *
+ * DESCRIPTION
+ * Parses the "instrument" keyword inside an otel-scope section. Two forms
+ * are supported: the "update" form that references an existing instrument by
+ * name and adds attributes to it, and the "create" form that defines a new
+ * metric instrument with a type, name, optional aggregation type (preceded by
+ * the 'aggr' keyword), optional description, optional unit, a single sample
+ * expression for the value, and optional histogram bucket boundaries
+ * (preceded by the 'bounds' keyword). The 'bounds' keyword is only valid for
+ * histogram instrument types.
+ *
+ * RETURN VALUE
+ * Returns ERR_NONE (== 0) in case of success,
+ * or a combination of ERR_* flags if an error is encountered.
+ */
+static int flt_otel_parse_cfg_instrument(const char *file, int line, char **args, const struct flt_otel_parse_data *pdata, char **err)
+{
+#define FLT_OTEL_PARSE_SCOPE_INSTRUMENT_DEF(a,b) { OTELC_METRIC_INSTRUMENT_##a, b },
+ static const struct {
+ otelc_metric_instrument_t type;
+ const char *keyword;
+ } instr_type[] = { FLT_OTEL_PARSE_SCOPE_INSTRUMENT_DEFINES };
+#undef FLT_OTEL_PARSE_SCOPE_INSTRUMENT_DEF
+ struct flt_otel_conf_instrument *instr;
+ int i, retval = ERR_NONE;
+
+ OTELC_FUNC("\"%s\", %d, %p, %p, %p:%p", OTELC_STR_ARG(file), line, args, pdata, OTELC_DPTR_ARGS(err));
+
+ /* Look up the instrument type from args[1]. */
+ for (i = 0; i < OTELC_TABLESIZE(instr_type); i++)
+ if (FLT_OTEL_PARSE_KEYWORD(1, instr_type[i].keyword)) {
+ OTELC_DBG(DEBUG, "instrument type: %d '%s'", instr_type[i].type, instr_type[i].keyword);
+
+ break;
+ }
+
+ if (i >= OTELC_TABLESIZE(instr_type)) {
+ FLT_OTEL_PARSE_ERR(err, "'%s' : invalid instrument type", args[1]);
+
+ OTELC_RETURN_INT(retval);
+ }
+
+ /*
+ * Only one create and one update instrument per name are allowed.
+ * Pass NULL as head for update instruments to bypass the generic
+ * duplicate check (which would reject the shared name), check for
+ * update duplicates separately, and append to the list manually.
+ */
+ if (instr_type[i].type == OTELC_METRIC_INSTRUMENT_UPDATE) {
+ list_for_each_entry(instr, &(flt_otel_current_scope->instruments), list)
+ if ((instr->type == OTELC_METRIC_INSTRUMENT_UPDATE) && FLT_OTEL_PARSE_KEYWORD(2, instr->id)) {
+ FLT_OTEL_ERR("'%s' : already defined", args[2]);
+
+ OTELC_RETURN_INT(retval);
+ }
+
+ instr = flt_otel_conf_instrument_init(args[2], line, NULL, err);
+ if (instr != NULL)
+ LIST_APPEND(&(flt_otel_current_scope->instruments), &(instr->list));
+ } else {
+ instr = flt_otel_conf_instrument_init(args[2], line, &(flt_otel_current_scope->instruments), err);
+ }
+
+ if (instr == NULL) {
+ retval |= ERR_ABORT | ERR_ALERT;
+ }
+ else if (instr_type[i].type == OTELC_METRIC_INSTRUMENT_UPDATE) {
+ bool flag_add_attr = false;
+
+ instr->type = instr_type[i].type;
+
+ /* Update instruments only accept additional attributes. */
+ for (i = 3; !(retval & ERR_CODE) && FLT_OTEL_ARG_ISVALID(i); i++) {
+ if (flag_add_attr) {
+ if (!FLT_OTEL_ARG_ISVALID(i) || !FLT_OTEL_ARG_ISVALID(i + 1))
+ FLT_OTEL_PARSE_ERR(err, "'%s' : too few arguments (use '%s%s')", args[i], pdata->name, pdata->usage);
+ else if (otelc_kv_add(&(instr->attr), &(instr->attr_len), args[i], args[i + 1], strlen(args[i + 1])) == OTELC_RET_ERROR)
+ FLT_OTEL_PARSE_ERR(err, "'%s' : out of memory", args[0]);
+ else
+ i++;
+ }
+ else if (FLT_OTEL_PARSE_KEYWORD(i, FLT_OTEL_PARSE_INSTRUMENT_ATTR)) {
+ flag_add_attr = true;
+ }
+ else {
+ FLT_OTEL_PARSE_ERR(err, "'%s' : unknown keyword (use '%s%s')", args[i], pdata->name, pdata->usage);
+ }
+ }
+
+ if (flag_add_attr && (instr->attr_len == 0))
+ FLT_OTEL_PARSE_ERR(err, "'%s' : too few arguments (use '%s%s')", args[i], pdata->name, pdata->usage);
+ }
+ else {
+ instr->type = instr_type[i].type;
+
+ /*
+ * Create instruments accept aggr, description, unit, value,
+ * and bounds.
+ */
+ for (i = 3; !(retval & ERR_CODE) && FLT_OTEL_ARG_ISVALID(i); i++) {
+ if (FLT_OTEL_PARSE_KEYWORD(i, FLT_OTEL_PARSE_INSTRUMENT_AGGR)) {
+ if (!FLT_OTEL_ARG_ISVALID(i + 1))
+ FLT_OTEL_PARSE_ERR(err, "'%s' : too few arguments (use '%s%s')", args[i], pdata->name, pdata->usage);
+ else if (instr->aggr_type != OTELC_METRIC_AGGREGATION_UNSET)
+ FLT_OTEL_PARSE_ERR(err, "'%s' : already set (use '%s%s')", args[i], pdata->name, pdata->usage);
+ else {
+ otelc_metric_aggregation_type_t type = otelc_meter_aggr_parse(args[++i]);
+
+ if (type == OTELC_RET_ERROR)
+ FLT_OTEL_PARSE_ERR(err, "'%s' : invalid aggregation type", args[i]);
+ else
+ instr->aggr_type = type;
+ }
+ }
+ else if (FLT_OTEL_PARSE_KEYWORD(i, FLT_OTEL_PARSE_INSTRUMENT_DESC)) {
+ if (!FLT_OTEL_ARG_ISVALID(i + 1))
+ FLT_OTEL_PARSE_ERR(err, "'%s' : too few arguments (use '%s%s')", args[i], pdata->name, pdata->usage);
+ else if (instr->description == NULL)
+ retval = flt_otel_parse_strdup(&(instr->description), NULL, args[++i], err, args[0]);
+ else
+ FLT_OTEL_PARSE_ERR(err, "'%s' : already set (use '%s%s')", args[i], pdata->name, pdata->usage);
+ }
+ else if (FLT_OTEL_PARSE_KEYWORD(i, FLT_OTEL_PARSE_INSTRUMENT_UNIT)) {
+ if (!FLT_OTEL_ARG_ISVALID(i + 1))
+ FLT_OTEL_PARSE_ERR(err, "'%s' : too few arguments (use '%s%s')", args[i], pdata->name, pdata->usage);
+ else if (instr->unit == NULL)
+ retval = flt_otel_parse_strdup(&(instr->unit), NULL, args[++i], err, args[0]);
+ else
+ FLT_OTEL_PARSE_ERR(err, "'%s' : already set (use '%s%s')", args[i], pdata->name, pdata->usage);
+ }
+ else if (FLT_OTEL_PARSE_KEYWORD(i, FLT_OTEL_PARSE_INSTRUMENT_VALUE)) {
+ if (!FLT_OTEL_ARG_ISVALID(i + 1))
+ FLT_OTEL_PARSE_ERR(err, "'%s' : too few arguments (use '%s%s')", args[i], pdata->name, pdata->usage);
+ else if (!LIST_ISEMPTY(&(instr->samples)))
+ FLT_OTEL_PARSE_ERR(err, "'%s' : already set (use '%s%s')", args[i], pdata->name, pdata->usage);
+ else {
+ retval = flt_otel_parse_cfg_sample(file, line, args, ++i, 1, NULL, &(instr->samples), err);
+
+ if (!(retval & ERR_CODE) && FLT_OTEL_ARG_ISVALID(i + 1) && !FLT_OTEL_PARSE_KEYWORD(i + 1, FLT_OTEL_PARSE_INSTRUMENT_AGGR) && !FLT_OTEL_PARSE_KEYWORD(i + 1, FLT_OTEL_PARSE_INSTRUMENT_DESC) && !FLT_OTEL_PARSE_KEYWORD(i + 1, FLT_OTEL_PARSE_INSTRUMENT_UNIT) && !FLT_OTEL_PARSE_KEYWORD(i + 1, FLT_OTEL_PARSE_INSTRUMENT_VALUE) && !FLT_OTEL_PARSE_KEYWORD(i + 1, FLT_OTEL_PARSE_INSTRUMENT_BOUNDS))
+ FLT_OTEL_PARSE_ERR(err, "'%s' : only one sample expression allowed per instrument", args[0]);
+ }
+ }
+ else if (FLT_OTEL_PARSE_KEYWORD(i, FLT_OTEL_PARSE_INSTRUMENT_BOUNDS)) {
+ if (!FLT_OTEL_ARG_ISVALID(i + 1))
+ FLT_OTEL_PARSE_ERR(err, "'%s' : too few arguments (use '%s%s')", args[i], pdata->name, pdata->usage);
+ else if (instr->type != OTELC_METRIC_INSTRUMENT_HISTOGRAM_UINT64)
+ FLT_OTEL_PARSE_ERR(err, "'%s' : bounds only valid for hist_int instruments", args[i]);
+ else if (instr->bounds != NULL)
+ FLT_OTEL_PARSE_ERR(err, "'%s' : already set (use '%s%s')", args[i], pdata->name, pdata->usage);
+ else
+ retval = flt_otel_parse_bounds(args[++i], &(instr->bounds), &(instr->bounds_num), err, args[0]);
+ }
+ else {
+ FLT_OTEL_PARSE_ERR(err, "'%s' : invalid argument (use '%s%s')", args[i], pdata->name, pdata->usage);
+ }
+ }
+ }
+
+ OTELC_RETURN_INT(retval);
+}
+
+
/***
* NAME
* flt_otel_parse_cfg_scope - otel-scope section parser
else if (pdata->keyword == FLT_OTEL_PARSE_SCOPE_FINISH) {
retval = flt_otel_parse_cfg_str(file, line, args, &(flt_otel_current_scope->spans_to_finish), &err);
}
+ else if (pdata->keyword == FLT_OTEL_PARSE_SCOPE_INSTRUMENT) {
+ retval = flt_otel_parse_cfg_instrument(file, line, args, pdata, &err);
+ }
else if (pdata->keyword == FLT_OTEL_PARSE_SCOPE_ACL) {
if (FLT_OTEL_PARSE_KEYWORD(1, "or"))
FLT_OTEL_PARSE_ERR(&err, "'%s %s ...' : invalid ACL name", args[0], args[1]);
}
+/*
+ * Comparator for qsort: ascending order of doubles. Values within
+ * FLT_OTEL_DBL_EPSILON of each other are treated as equal.
+ */
+int flt_otel_qsort_compar_double(const void *p1, const void *p2)
+{
+ double a = *(const double *)p1;
+ double b = *(const double *)p2;
+
+ return (fabs(a - b) < FLT_OTEL_DBL_EPSILON) ? 0 : ((a < b) ? -1 : 1);
+}
+
+
/***
* NAME
* flt_otel_strtod - string to double conversion with range check
status "error" str("http.status_code: ") status
otel-scope on_stream_start
+ instrument udcnt_int "haproxy.sessions.active" desc "Active sessions" value int(1) unit "{session}"
+ instrument gauge_int "haproxy.fe.connections" desc "Frontend connections" value fe_conn unit "{connection}"
span "HAProxy session" root
baggage "haproxy_id" var(sess.otel.uuid)
event "event_ip" "src" src str(":") src_port
idle-timeout 1s
span "heartbeat" parent "HAProxy session"
attribute "idle.elapsed" str("idle-check")
+ instrument cnt_int "idle.count" value int(1)
+ instrument update "idle.count"
otel-event on-idle-timeout
otel-scope client_session_start
otel-event on-http-body-request
otel-scope frontend_http_request
+ instrument cnt_int "haproxy.http.requests" desc "HTTP request count" value int(1) unit "{request}"
+ instrument hist_int "haproxy.http.latency" desc "HTTP request latency" value lat_ns_tot unit "ns"
+ instrument update "haproxy.http.latency" attr "phase" "request"
span "Frontend HTTP request" parent "HTTP body request" link "HAProxy session"
attribute "http.method" method
attribute "http.url" url
otel-event on-process-sticking-rules-request
otel-scope client_session_end
+ instrument update "haproxy.sessions.active"
finish "*req*"
otel-event on-client-session-end
otel-event on-process-store-rules-response
otel-scope http_response
+ instrument update "haproxy.http.requests" attr "phase" "response"
+ instrument update "haproxy.http.latency" attr "phase" "response"
+ instrument update "haproxy.fe.connections"
span "HTTP response" parent "Process store rules response"
attribute "http.status_code" status
finish "Process store rules response"