attr - attribute key-value pairs (update form only)
-log-record <severity> [id <integer>] [event <name>] [span <span-name>] [attr <key> <value>] ... <sample> ...
+log-record <severity> [id <integer>] [event <name>] [span <span-name>] [attr <key> <sample>] ... <sample> ...
This keyword emits an OpenTelemetry log record within the scope. The first
argument is a required severity level. Optional keywords follow in any order
before the trailing sample expressions that form the log record body:
- id <integer> - numeric event identifier
- event <name> - event name string
- span <span-name> - associate the log record with an open span
- attr <key> <value> - add a key-value attribute (repeatable)
+ id <integer> - numeric event identifier
+ event <name> - event name string
+ span <span-name> - associate the log record with an open span
+ attr <key> <sample> - add an attribute evaluated at runtime (repeatable)
The remaining arguments at the end are sample fetch expressions. A single
sample preserves its native type; multiple samples are concatenated as a
For example:
log-record info str("heartbeat")
- log-record info id 1001 event "http-request" span "Frontend HTTP request" attr "http.method" "GET" method url
- log-record trace id 1000 event "session-start" span "Client session" attr "attr_1_key" "attr_1_value" src str(":") src_port
+ log-record info id 1001 event "http-request" span "Frontend HTTP request" attr "http.method" method method url
+ log-record trace id 1000 event "session-start" span "Client session" attr "src_ip" src src str(":") src_port
log-record warn event "server-unavailable" str("503 Service Unavailable")
Arguments :
| +-- flt_otel_conf_sample (samples list)
| +-- flt_otel_conf_sample_expr (exprs list)
+-- flt_otel_conf_log_record (log_records list)
+ +-- flt_otel_conf_sample (attributes list)
+ | +-- flt_otel_conf_sample_expr (exprs list)
+-- flt_otel_conf_sample (samples list)
+-- flt_otel_conf_sample_expr (exprs list)
event_id Optional event identifier.
event_name Optional event name.
span Optional span reference.
- attr Log record attributes.
- attr_len Number of log record attributes.
+ attributes Log record attributes (flt_otel_conf_sample list).
samples Sample expressions for the body.
Log records are emitted via the OTel logger at the configured severity. The
optional span reference associates the log record with an open span at runtime.
-Attributes are stored as key-value pairs added via the 'attr' keyword, which
-can be repeated.
+Attributes are stored as flt_otel_conf_sample entries added via the 'attr'
+keyword, which can be repeated. Attribute values are HAProxy sample expressions
+evaluated at runtime.
4.8 flt_otel_conf_context
instrument update "name_cnt_int" attr "attr_1_key" "attr_1_value"
- log-record <severity> [id <integer>] [event <name>] [span <span-name>] [attr <key> <value>] ... <sample> ...
+ log-record <severity> [id <integer>] [event <name>] [span <span-name>] [attr <key> <sample>] ... <sample> ...
Emit an OpenTelemetry log record. The first argument is a required
severity level. Optional keywords follow in any order:
- id <integer> - numeric event identifier
- event <name> - event name string
- span <span-name> - associate the log record with an open span
- attr <key> <value> - add a key-value attribute (repeatable)
+ id <integer> - numeric event identifier
+ event <name> - event name string
+ span <span-name> - associate the log record with an open span
+ attr <key> <sample> - add an attribute evaluated at runtime (repeatable)
+
+ The 'attr' keyword takes an attribute name and a single HAProxy sample
+ expression. The expression is evaluated at runtime, following the same
+ rules as span attributes: a bare sample fetch (e.g. src) or a log-format
+ string (e.g. "%[src]:%[src_port]").
The remaining arguments at the end are sample fetch expressions that form
the log record body. A single sample preserves its native type; multiple
Examples:
log-record info str("heartbeat")
- log-record info id 1001 event "http-request" span "Frontend HTTP request" attr "http.method" "GET" method url
- log-record trace id 1000 event "session-start" span "Client session" attr "attr_1_key" "attr_1_value" attr "attr_2_key" "attr_2_value" src str(":") src_port
+ log-record info id 1001 event "http-request" span "Frontend HTTP request" attr "http.method" method method url
+ log-record trace id 1000 event "session-start" span "Client session" attr "src_ip" src attr "src_port" src_port src str(":") src_port
log-record warn event "server-unavailable" str("503 Service Unavailable")
log-record info event "session-stop" str("stream stopped")
inject <name-prefix> [use-headers] [use-vars]
finish <name> ...
instrument <type> <name> ... / instrument update <name> ...
- log-record <severity> [id <int>] [event <name>] [span <ref>] [attr <k> <v>] ... <sample> ...
+ log-record <severity> [id <int>] [event <name>] [span <ref>] [attr <key> <sample>] ... <sample> ...
acl <name> <criterion> ...
Each scope ties to a single HAProxy analyzer event (or none, if used only
event_id Optional numeric event identifier (int64).
event_name Optional event name string.
span Optional span reference name (resolved at runtime).
- attr Key-value attribute array (from "attr" keywords).
- attr_len Number of attributes.
+ attributes List of flt_otel_conf_sample entries for attributes.
samples List of sample expressions for the body.
+The attributes list contains flt_otel_conf_sample entries, one per "attr"
+keyword. Each entry's key field holds the attribute name and its sample
+expressions are evaluated at runtime, following the same two-path model
+(bare sample or log-format) as span attributes.
+
The samples list contains exactly one flt_otel_conf_sample entry, which
in turn holds either a list of bare sample expressions or a single
log-format expression (when the value contains "%[").
skipped. The threshold is controlled by the "min_severity" option
in the YAML logs signal configuration.
- 2. Body evaluation: the single sample entry is evaluated using one of
+ 2. Attribute evaluation: each entry in the attributes list is evaluated via
+ flt_otel_sample_add() into a temporary flt_otel_scope_data structure.
+ The evaluated key-value array is passed to logger->log_span() and freed
+ after emission.
+
+ 3. Body evaluation: the single sample entry is evaluated using one of
two paths:
Log-format path (sample->lf_used is true):
flt_otel_sample_to_str(). Results are concatenated into a
single buffer.
- 3. Span resolution: if conf_log->span is non-NULL, the runtime
+ 4. Span resolution: if conf_log->span is non-NULL, the runtime
context's spans list is searched for a scope_span with a matching
name. If found, the OTel span pointer is captured for correlation.
A missing span is non-fatal -- a NOTICE warning is logged and the
record is emitted without span correlation.
- 4. Emission: logger->log_span() is called with the severity, event_id,
- event_name, resolved span (or NULL), wall-clock timestamp,
+ 5. Emission: logger->log_span() is called with the severity, event_id,
+ event_name, resolved span (or NULL), wall-clock timestamp, the evaluated
attributes and the evaluated body string.
18.6.3 Logger Lifecycle Summary
(p)->bounds_num, (p)->bounds)
#define FLT_OTEL_DBG_CONF_LOG_RECORD(h,p) \
- OTELC_DBG_STRUCT(DEBUG, h, h FLT_OTEL_CONF_HDR_FMT "%d %" PRId64 " '%s' '%s' %p %zu %p }", (p), \
+ OTELC_DBG_STRUCT(DEBUG, h, h FLT_OTEL_CONF_HDR_FMT "%d %" PRId64 " '%s' '%s' %s %s }", (p), \
FLT_OTEL_CONF_HDR_ARGS(p, id), (p)->severity, (p)->event_id, OTELC_STR_ARG((p)->event_name), \
- OTELC_STR_ARG((p)->span), (p)->attr, (p)->attr_len, flt_otel_list_dump(&((p)->samples)))
+ OTELC_STR_ARG((p)->span), flt_otel_list_dump(&((p)->attributes)), flt_otel_list_dump(&((p)->samples)))
#define FLT_OTEL_DBG_CONF(h,p) \
OTELC_DBG(DEBUG, h "%p:{ %p '%s' '%s' %p %s %s }", (p), \
int64_t event_id; /* Optional event identifier. */
char *event_name; /* Optional event name. */
char *span; /* Optional span reference. */
- struct otelc_kv *attr; /* Log record attributes. */
- size_t attr_len; /* Number of log record attributes. */
+ struct list attributes; /* Log record attributes (flt_otel_conf_sample). */
struct list samples; /* Sample expressions for the body. */
};
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( LOG_RECORD, 0, NONE, 3, 0, "log-record", " <severity> [<id>] [<event>] [<span>] [<attr>] <sample>") \
+ FLT_OTEL_PARSE_SCOPE_DEF( LOG_RECORD, 0, NONE, 3, 0, "log-record", " <severity> [<id>] [<event>] [<span>] [<attr>] <sample> ...") \
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>]")
*
* DESCRIPTION
* Allocates and initializes a conf_log_record structure. Initializes the
- * sample expressions list. The <id> string is required by the macro but is
- * not used directly; the severity level is stored separately. If <head> is
- * non-NULL, the structure is appended to the list.
+ * attributes and sample expressions lists. The <id> string is required by
+ * the macro but is not used directly; the severity level is stored
+ * separately. 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(log_record, id,
+ LIST_INIT(&(retptr->attributes));
LIST_INIT(&(retptr->samples));
)
OTELC_SFREE((*ptr)->event_name);
OTELC_SFREE((*ptr)->span);
- otelc_kv_destroy(&((*ptr)->attr), (*ptr)->attr_len);
+ FLT_OTEL_LIST_DESTROY(sample, &((*ptr)->attributes));
FLT_OTEL_LIST_DESTROY(sample, &((*ptr)->samples));
)
struct flt_otel_conf_sample_expr *expr;
struct sample smp;
struct otelc_span *otel_span = NULL;
+ struct flt_otel_scope_data_kv log_attr;
struct buffer buffer;
int rc;
if (OTELC_OPS(logger, enabled, conf_log->severity) == 0)
continue;
+ /* Evaluate log record attributes from sample expressions. */
+ (void)memset(&log_attr, 0, sizeof(log_attr));
+
+ list_for_each_entry(sample, &(conf_log->attributes), list) {
+ struct otelc_value attr_value;
+
+ OTELC_DBG(DEBUG, "adding log-record attribute '%s' -> '%s'", sample->key, sample->fmt_string);
+
+ if (flt_otel_sample_eval(s, dir, sample, true, &attr_value, err) == FLT_OTEL_RET_ERROR) {
+ retval = FLT_OTEL_RET_ERROR;
+
+ continue;
+ }
+
+ if (flt_otel_sample_add_kv(&log_attr, sample->key, &attr_value) == FLT_OTEL_RET_ERROR) {
+ if (attr_value.u_type == OTELC_VALUE_DATA)
+ OTELC_SFREE(attr_value.u.value_data);
+
+ retval = FLT_OTEL_RET_ERROR;
+ }
+ }
+
/* The samples list has exactly one entry. */
sample = LIST_NEXT(&(conf_log->samples), typeof(sample), list);
retval = FLT_OTEL_RET_ERROR;
+ otelc_kv_destroy(&(log_attr.attr), log_attr.cnt);
+
continue;
}
OTELC_DBG(NOTICE, "WARNING: cannot find span '%s' for log-record", conf_log->span);
}
- if (OTELC_OPS(logger, log_span, conf_log->severity, conf_log->event_id, conf_log->event_name, otel_span, ts, conf_log->attr, conf_log->attr_len, "%s", buffer.area) == OTELC_RET_ERROR)
+ if (OTELC_OPS(logger, log_span, conf_log->severity, conf_log->event_id, conf_log->event_name, otel_span, ts, log_attr.attr, log_attr.cnt, "%s", buffer.area) == OTELC_RET_ERROR)
retval = FLT_OTEL_RET_ERROR;
+ otelc_kv_destroy(&(log_attr.attr), log_attr.cnt);
OTELC_SFREE(buffer.area);
}
else if (FLT_OTEL_PARSE_KEYWORD(i, FLT_OTEL_PARSE_LOG_RECORD_ATTR)) {
if (!FLT_OTEL_ARG_ISVALID(i + 1) || !FLT_OTEL_ARG_ISVALID(i + 2))
FLT_OTEL_PARSE_ERR(err, "'%s' : too few arguments (use '%s%s')", args[i], pdata->name, pdata->usage);
- else if (otelc_kv_add(&(log->attr), &(log->attr_len), args[i + 1], args[i + 2], strlen(args[i + 2])) == OTELC_RET_ERROR)
- FLT_OTEL_PARSE_ERR(err, "'%s' : out of memory", args[0]);
- else
- i += 2;
+ else {
+ retval = flt_otel_parse_cfg_sample(file, line, args, i + 2, 1, NULL, &(log->attributes), err);
+ if (!(retval & ERR_CODE))
+ i += 2;
+ }
}
else {
/*
event "event_be" "be" be_id str(" ") be_name
event "event_ip" "dst" dst str(":") dst_port
event "event_fe" "fe" fe_id str(" ") fe_name
- log-record trace id 1000 event "session-start" span "HAProxy session" attr "attr_1_key" "attr_1_value" attr "attr_2_key" "attr_2_value" src str(":") src_port
+ log-record trace id 1000 event "session-start" span "HAProxy session" attr "attr_1_key" src attr "attr_2_key" src_port src str(":") src_port
acl acl-test-src-ip src 127.0.0.1
otel-event on-stream-start if acl-test-src-ip
attribute "http.url" url
attribute "http.version" str("HTTP/") req.ver
finish "HTTP body request"
- log-record info id 1002 event "http-request" span "Frontend HTTP request" attr "http.method" "GET" method url
+ log-record info id 1002 event "http-request" span "Frontend HTTP request" attr "http.method" method method url
otel-event on-frontend-http-request
otel-scope switching_rules_request
event "event_be" "be" be_id str(" ") be_name
event "event_ip" "dst" dst str(":") dst_port
event "event_fe" "fe" fe_id str(" ") fe_name
- log-record trace id 1000 event "session-start" span "Client session" attr "attr_1_key" "attr_1_value" attr "attr_2_key" "attr_2_value" src str(":") src_port
+ log-record trace id 1000 event "session-start" span "Client session" attr "attr_1_key" src attr "attr_2_key" src_port src str(":") src_port
acl acl-test-src-ip src 127.0.0.1
otel-event on-stream-start if acl-test-src-ip
attribute "http.url" url
attribute "http.version" str("HTTP/") req.ver
finish "HTTP body request"
- log-record info id 1001 event "http-request" span "Frontend HTTP request" attr "http.method" "GET" method url
+ log-record info id 1001 event "http-request" span "Frontend HTTP request" attr "http.method" method method url
otel-event on-frontend-http-request
otel-scope switching_rules_request