diff --git a/Makefile b/Makefile index 41aab3e37..ff09a1219 100644 --- a/Makefile +++ b/Makefile @@ -60,6 +60,7 @@ # USE_OBSOLETE_LINKER : use when the linker fails to emit __start_init/__stop_init # USE_THREAD_DUMP : use the more advanced thread state dump system. Automatic. # USE_OT : enable the OpenTracing filter +# USE_OTEL : enable the OpenTelemetry filter # USE_MEMORY_PROFILING : enable the memory profiler. Linux-glibc only. # USE_LIBATOMIC : force to link with/without libatomic. Automatic. # USE_PTHREAD_EMULATION : replace pthread's rwlocks with ours @@ -128,6 +129,10 @@ # OT_LIB : force the lib path to libopentracing-c-wrapper # OT_RUNPATH : add RUNPATH for libopentracing-c-wrapper to haproxy executable # OT_USE_VARS : allows the use of variables for the OpenTracing context +# OTEL_DEBUG : compile the OpenTelemetry filter in debug mode +# OTEL_INC : force the include path to libopentelemetry-c-wrapper +# OTEL_LIB : force the lib path to libopentelemetry-c-wrapper +# OTEL_RUNPATH : add RUNPATH for libopentelemetry-c-wrapper to haproxy executable # IGNOREGIT : ignore GIT commit versions if set. # VERSION : force haproxy version reporting. # SUBVERS : add a sub-version (eg: platform, model, ...). @@ -347,7 +352,7 @@ use_opts = USE_EPOLL USE_KQUEUE USE_NETFILTER USE_POLL \ USE_CPU_AFFINITY USE_TFO USE_NS USE_DL USE_RT USE_LIBATOMIC \ USE_MATH USE_DEVICEATLAS USE_51DEGREES \ USE_WURFL USE_OBSOLETE_LINKER USE_PRCTL USE_PROCCTL \ - USE_THREAD_DUMP USE_EVPORTS USE_OT USE_QUIC USE_PROMEX \ + USE_THREAD_DUMP USE_EVPORTS USE_OT USE_OTEL USE_QUIC USE_PROMEX \ USE_MEMORY_PROFILING USE_SHM_OPEN \ USE_STATIC_PCRE USE_STATIC_PCRE2 \ USE_PCRE USE_PCRE_JIT USE_PCRE2 USE_PCRE2_JIT \ @@ -862,6 +867,10 @@ ifneq ($(USE_OT:0=),) include addons/ot/Makefile endif +ifneq ($(USE_OTEL:0=),) + include addons/otel/Makefile +endif + # better keep this one close to the end, as several libs above may need it ifneq ($(USE_DL:0=),) DL_LDFLAGS = -ldl @@ -1170,6 +1179,7 @@ clean: $(Q)rm -f addons/51degrees/*.[oas] addons/51degrees/dummy/*.[oas] addons/51degrees/dummy/*/*.[oas] $(Q)rm -f addons/deviceatlas/*.[oas] addons/deviceatlas/dummy/*.[oas] addons/deviceatlas/dummy/*.o $(Q)rm -f addons/deviceatlas/dummy/Os/*.o + $(Q)rm -f addons/otel/src/*.[oas] $(Q)rm -f addons/ot/src/*.[oas] $(Q)rm -f addons/wurfl/*.[oas] addons/wurfl/dummy/*.[oas] $(Q)rm -f admin/*/*.[oas] admin/*/*/*.[oas] diff --git a/addons/otel/AUTHORS b/addons/otel/AUTHORS new file mode 100644 index 000000000..92b2831b2 --- /dev/null +++ b/addons/otel/AUTHORS @@ -0,0 +1 @@ +Miroslav Zagorac diff --git a/addons/otel/MAINTAINERS b/addons/otel/MAINTAINERS new file mode 100644 index 000000000..92b2831b2 --- /dev/null +++ b/addons/otel/MAINTAINERS @@ -0,0 +1 @@ +Miroslav Zagorac diff --git a/addons/otel/Makefile b/addons/otel/Makefile new file mode 100644 index 000000000..bfcfd744e --- /dev/null +++ b/addons/otel/Makefile @@ -0,0 +1,57 @@ +# USE_OTEL : enable the OpenTelemetry filter +# OTEL_DEBUG : compile the OpenTelemetry filter in debug mode +# OTEL_INC : force the include path to libopentelemetry-c-wrapper +# OTEL_LIB : force the lib path to libopentelemetry-c-wrapper +# OTEL_RUNPATH : add libopentelemetry-c-wrapper RUNPATH to haproxy executable + +OTEL_DEFINE = +OTEL_CFLAGS = +OTEL_LDFLAGS = +OTEL_DEBUG_EXT = +OTEL_PKGSTAT = +OTELC_WRAPPER = opentelemetry-c-wrapper + +ifneq ($(OTEL_DEBUG:0=),) +OTEL_DEBUG_EXT = _dbg +OTEL_DEFINE = -DDEBUG_OTEL +endif + +ifeq ($(OTEL_INC),) +OTEL_PKGSTAT = $(shell pkg-config --exists $(OTELC_WRAPPER)$(OTEL_DEBUG_EXT); echo $$?) +OTEL_CFLAGS = $(shell pkg-config --silence-errors --cflags $(OTELC_WRAPPER)$(OTEL_DEBUG_EXT)) +else +ifneq ($(wildcard $(OTEL_INC)/$(OTELC_WRAPPER)/.*),) +OTEL_CFLAGS = -I$(OTEL_INC) $(if $(OTEL_DEBUG),-DOTELC_DBG_MEM) +endif +endif + +ifeq ($(OTEL_PKGSTAT),) +ifeq ($(OTEL_CFLAGS),) +$(error OpenTelemetry C wrapper : can't find headers) +endif +else +ifneq ($(OTEL_PKGSTAT),0) +$(error OpenTelemetry C wrapper : can't find package) +endif +endif + +ifeq ($(OTEL_LIB),) +OTEL_LDFLAGS = $(shell pkg-config --silence-errors --libs $(OTELC_WRAPPER)$(OTEL_DEBUG_EXT)) +else +ifneq ($(wildcard $(OTEL_LIB)/lib$(OTELC_WRAPPER).*),) +OTEL_LDFLAGS = -L$(OTEL_LIB) -l$(OTELC_WRAPPER)$(OTEL_DEBUG_EXT) +ifneq ($(OTEL_RUNPATH),) +OTEL_LDFLAGS += -Wl,--rpath,$(OTEL_LIB) +endif +endif +endif + +ifeq ($(OTEL_LDFLAGS),) +$(error OpenTelemetry C wrapper : can't find library) +endif + +OPTIONS_OBJS += \ + addons/otel/src/filter.o \ + addons/otel/src/parser.o + +OTEL_CFLAGS := $(OTEL_CFLAGS) -Iaddons/otel/include $(OTEL_DEFINE) diff --git a/addons/otel/include/config.h b/addons/otel/include/config.h new file mode 100644 index 000000000..75ac5d175 --- /dev/null +++ b/addons/otel/include/config.h @@ -0,0 +1,17 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#ifndef _OTEL_CONFIG_H_ +#define _OTEL_CONFIG_H_ + +#define FLT_OTEL_DEBUG_LEVEL 0b11101111111 /* Default debug bitmask. */ + +#endif /* _OTEL_CONFIG_H_ */ + +/* + * Local variables: + * c-indent-level: 8 + * c-basic-offset: 8 + * End: + * + * vi: noexpandtab shiftwidth=8 tabstop=8 + */ diff --git a/addons/otel/include/define.h b/addons/otel/include/define.h new file mode 100644 index 000000000..92b9eb732 --- /dev/null +++ b/addons/otel/include/define.h @@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#ifndef _OTEL_DEFINE_H_ +#define _OTEL_DEFINE_H_ + +/* 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) + +#endif /* _OTEL_DEFINE_H_ */ + +/* + * Local variables: + * c-indent-level: 8 + * c-basic-offset: 8 + * End: + * + * vi: noexpandtab shiftwidth=8 tabstop=8 + */ diff --git a/addons/otel/include/filter.h b/addons/otel/include/filter.h new file mode 100644 index 000000000..2dbca001c --- /dev/null +++ b/addons/otel/include/filter.h @@ -0,0 +1,26 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#ifndef _OTEL_FILTER_H_ +#define _OTEL_FILTER_H_ + +/* Return codes for OTel filter operations. */ +enum FLT_OTEL_RET_enum { + FLT_OTEL_RET_ERROR = -1, + FLT_OTEL_RET_WAIT = 0, + FLT_OTEL_RET_IGNORE = 0, + FLT_OTEL_RET_OK = 1, +}; + + +extern const char *otel_flt_id; + +#endif /* _OTEL_FILTER_H_ */ + +/* + * Local variables: + * c-indent-level: 8 + * c-basic-offset: 8 + * End: + * + * vi: noexpandtab shiftwidth=8 tabstop=8 + */ diff --git a/addons/otel/include/include.h b/addons/otel/include/include.h new file mode 100644 index 000000000..64339a55f --- /dev/null +++ b/addons/otel/include/include.h @@ -0,0 +1,42 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#ifndef _OTEL_INCLUDE_H_ +#define _OTEL_INCLUDE_H_ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "config.h" +#include "define.h" +#include "filter.h" +#include "parser.h" + +#endif /* _OTEL_INCLUDE_H_ */ + +/* + * Local variables: + * c-indent-level: 8 + * c-basic-offset: 8 + * End: + * + * vi: noexpandtab shiftwidth=8 tabstop=8 + */ diff --git a/addons/otel/include/parser.h b/addons/otel/include/parser.h new file mode 100644 index 000000000..d0c42c2dd --- /dev/null +++ b/addons/otel/include/parser.h @@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#ifndef _OTEL_PARSER_H_ +#define _OTEL_PARSER_H_ + +#define FLT_OTEL_SCOPE "OTEL" +#define FLT_OTEL_OPT_NAME "opentelemetry" + +#endif /* _OTEL_PARSER_H_ */ + +/* + * Local variables: + * c-indent-level: 8 + * c-basic-offset: 8 + * End: + * + * vi: noexpandtab shiftwidth=8 tabstop=8 + */ diff --git a/addons/otel/src/filter.c b/addons/otel/src/filter.c new file mode 100644 index 000000000..b012f27c9 --- /dev/null +++ b/addons/otel/src/filter.c @@ -0,0 +1,666 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "../include/include.h" + + +/* + * OpenTelemetry filter id, used to identify OpenTelemetry filters. The name + * of this variable is consistent with the other filter names declared in + * include/haproxy/filters.h . + */ +const char *otel_flt_id = "the OpenTelemetry filter"; + + +/*** + * NAME + * flt_otel_ops_init - filter init callback (flt_ops.init) + * + * SYNOPSIS + * static int flt_otel_ops_init(struct proxy *p, struct flt_conf *fconf) + * + * ARGUMENTS + * p - the proxy to which the filter is attached + * fconf - the filter configuration + * + * DESCRIPTION + * It initializes the filter for a proxy. You may define this callback if you + * need to complete your filter configuration. + * + * RETURN VALUE + * Returns a negative value if an error occurs, any other value otherwise. + */ +static int flt_otel_ops_init(struct proxy *p, struct flt_conf *fconf) +{ + OTELC_FUNC("%p, %p", p, fconf); + + OTELC_RETURN_INT(0); +} + + +/*** + * NAME + * flt_otel_ops_deinit - filter deinit callback (flt_ops.deinit) + * + * SYNOPSIS + * static void flt_otel_ops_deinit(struct proxy *p, struct flt_conf *fconf) + * + * ARGUMENTS + * p - the proxy to which the filter is attached + * fconf - the filter configuration + * + * DESCRIPTION + * It cleans up what the parsing function and the init callback have done. + * This callback is useful to release memory allocated for the filter + * configuration. + * + * RETURN VALUE + * This function does not return a value. + */ +static void flt_otel_ops_deinit(struct proxy *p, struct flt_conf *fconf) +{ + OTELC_FUNC("%p, %p", p, fconf); + + OTELC_RETURN(); +} + + +/*** + * NAME + * flt_otel_ops_check - filter check callback (flt_ops.check) + * + * SYNOPSIS + * static int flt_otel_ops_check(struct proxy *p, struct flt_conf *fconf) + * + * ARGUMENTS + * p - the proxy to which the filter is attached + * fconf - the filter configuration + * + * DESCRIPTION + * Validates the internal configuration of the OTel filter after the parsing + * phase, when the HAProxy configuration is fully defined. The following + * checks are performed: duplicate filter IDs across all proxies, presence of + * an instrumentation section and its configuration file, duplicate group and + * scope names, empty groups, group-to-scope and instrumentation-to-group/scope + * cross-references, unused scopes, root span count, analyzer bits, and + * create-form instrument name uniqueness and update-form instrument + * resolution. + * + * RETURN VALUE + * Returns the number of encountered errors. + */ +static int flt_otel_ops_check(struct proxy *p, struct flt_conf *fconf) +{ + OTELC_FUNC("%p, %p", p, fconf); + + OTELC_RETURN_INT(0); +} + + +/*** + * NAME + * flt_otel_ops_init_per_thread - per-thread init callback (flt_ops.init_per_thread) + * + * SYNOPSIS + * static int flt_otel_ops_init_per_thread(struct proxy *p, struct flt_conf *fconf) + * + * ARGUMENTS + * p - the proxy to which the filter is attached + * fconf - the filter configuration + * + * DESCRIPTION + * Per-thread filter initialization called after thread creation. Starts + * the OTel tracer and meter threads via their start operations and enables + * HTX stream filtering. Subsequent calls on the same filter are no-ops. + * + * RETURN VALUE + * Returns a negative value if an error occurs, any other value otherwise. + */ +static int flt_otel_ops_init_per_thread(struct proxy *p, struct flt_conf *fconf) +{ + OTELC_FUNC("%p, %p", p, fconf); + + OTELC_RETURN_INT(FLT_OTEL_RET_OK); +} + + +#ifdef DEBUG_OTEL + +/*** + * NAME + * flt_otel_ops_deinit_per_thread - per-thread deinit callback (flt_ops.deinit_per_thread) + * + * SYNOPSIS + * static void flt_otel_ops_deinit_per_thread(struct proxy *p, struct flt_conf *fconf) + * + * ARGUMENTS + * p - the proxy to which the filter is attached + * fconf - the filter configuration + * + * DESCRIPTION + * It cleans up what the init_per_thread callback have done. It is called + * in the context of a thread, before exiting it. + * + * RETURN VALUE + * This function does not return a value. + */ +static void flt_otel_ops_deinit_per_thread(struct proxy *p, struct flt_conf *fconf) +{ + OTELC_FUNC("%p, %p", p, fconf); + + OTELC_RETURN(); +} + +#endif /* DEBUG_OTEL */ + + +/*** + * NAME + * flt_otel_ops_attach - filter attach callback (flt_ops.attach) + * + * SYNOPSIS + * static int flt_otel_ops_attach(struct stream *s, struct filter *f) + * + * ARGUMENTS + * s - the stream to which the filter is being attached + * f - the filter instance + * + * DESCRIPTION + * It is called after a filter instance creation, when it is attached to a + * stream. This happens when the stream is started for filters defined on + * the stream's frontend and when the backend is set for filters declared + * on the stream's backend. It is possible to ignore the filter, if needed, + * by returning 0. This could be useful to have conditional filtering. + * + * RETURN VALUE + * Returns a negative value if an error occurs, 0 to ignore the filter, + * any other value otherwise. + */ +static int flt_otel_ops_attach(struct stream *s, struct filter *f) +{ + OTELC_FUNC("%p, %p", s, f); + + OTELC_RETURN_INT(FLT_OTEL_RET_OK); +} + + +/*** + * NAME + * flt_otel_ops_stream_start - stream start callback (flt_ops.stream_start) + * + * SYNOPSIS + * static int flt_otel_ops_stream_start(struct stream *s, struct filter *f) + * + * ARGUMENTS + * s - the stream that is being started + * f - the filter instance + * + * DESCRIPTION + * It is called when a stream is started. This callback can fail by returning + * a negative value. It will be considered as a critical error by HAProxy + * which disabled the listener for a short time. After the stream-start + * event, it initializes the idle timer in the runtime context from the + * precomputed minimum idle_timeout in the instrumentation configuration. + * + * RETURN VALUE + * Returns a negative value if an error occurs, any other value otherwise. + */ +static int flt_otel_ops_stream_start(struct stream *s, struct filter *f) +{ + OTELC_FUNC("%p, %p", s, f); + + OTELC_RETURN_INT(FLT_OTEL_RET_OK); +} + + +/*** + * NAME + * flt_otel_ops_stream_set_backend - stream set-backend callback (flt_ops.stream_set_backend) + * + * SYNOPSIS + * static int flt_otel_ops_stream_set_backend(struct stream *s, struct filter *f, struct proxy *be) + * + * ARGUMENTS + * s - the stream being processed + * f - the filter instance + * be - the backend proxy being assigned + * + * DESCRIPTION + * It is called when a backend is set for a stream. This callback will be + * called for all filters attached to a stream (frontend and backend). Note + * this callback is not called if the frontend and the backend are the same. + * It fires the on-backend-set event. + * + * RETURN VALUE + * Returns a negative value if an error occurs, any other value otherwise. + */ +static int flt_otel_ops_stream_set_backend(struct stream *s, struct filter *f, struct proxy *be) +{ + OTELC_FUNC("%p, %p, %p", s, f, be); + + OTELC_RETURN_INT(FLT_OTEL_RET_OK); +} + + +/*** + * NAME + * flt_otel_ops_stream_stop - stream stop callback (flt_ops.stream_stop) + * + * SYNOPSIS + * static void flt_otel_ops_stream_stop(struct stream *s, struct filter *f) + * + * ARGUMENTS + * s - the stream being stopped + * f - the filter instance + * + * DESCRIPTION + * It is called when a stream is stopped. This callback always succeed. + * Anyway, it is too late to return an error. + * + * RETURN VALUE + * This function does not return a value. + */ +static void flt_otel_ops_stream_stop(struct stream *s, struct filter *f) +{ + OTELC_FUNC("%p, %p", s, f); + + OTELC_RETURN(); +} + + +/*** + * NAME + * flt_otel_ops_detach - filter detach callback (flt_ops.detach) + * + * SYNOPSIS + * static void flt_otel_ops_detach(struct stream *s, struct filter *f) + * + * ARGUMENTS + * s - the stream from which the filter is being detached + * f - the filter instance + * + * DESCRIPTION + * It is called when a filter instance is detached from a stream, before its + * destruction. This happens when the stream is stopped for filters defined + * on the stream's frontend and when the analyze ends for filters defined on + * the stream's backend. + * + * RETURN VALUE + * This function does not return a value. + */ +static void flt_otel_ops_detach(struct stream *s, struct filter *f) +{ + OTELC_FUNC("%p, %p", s, f); + + OTELC_RETURN(); +} + + +/*** + * NAME + * flt_otel_ops_check_timeouts - timeout callback (flt_ops.check_timeouts) + * + * SYNOPSIS + * static void flt_otel_ops_check_timeouts(struct stream *s, struct filter *f) + * + * ARGUMENTS + * s - the stream whose timer has expired + * f - the filter instance + * + * DESCRIPTION + * Timeout callback for the filter. When the idle-timeout timer has expired, + * it fires the on-idle-timeout event via flt_otel_event_run() and reschedules + * the timer for the next interval. It also sets the STRM_EVT_MSG pending + * event flag on the stream so that the stream processing loop + * re-evaluates the message state after the timeout. + * + * RETURN VALUE + * This function does not return a value. + */ +static void flt_otel_ops_check_timeouts(struct stream *s, struct filter *f) +{ + OTELC_FUNC("%p, %p", s, f); + + OTELC_RETURN(); +} + + +/*** + * NAME + * flt_otel_ops_channel_start_analyze - channel start-analyze callback + * + * SYNOPSIS + * static int flt_otel_ops_channel_start_analyze(struct stream *s, struct filter *f, struct channel *chn) + * + * ARGUMENTS + * s - the stream being analyzed + * f - the filter instance + * chn - the channel on which the analyzing starts + * + * DESCRIPTION + * Channel start-analyze callback. It registers the configured analyzers + * on the channel and runs the client or server session-start event + * depending on the channel direction. + * + * RETURN VALUE + * Returns a negative value if an error occurs, 0 if it needs to wait, + * any other value otherwise. + */ +static int flt_otel_ops_channel_start_analyze(struct stream *s, struct filter *f, struct channel *chn) +{ + OTELC_FUNC("%p, %p, %p", s, f, chn); + + OTELC_RETURN_INT(FLT_OTEL_RET_OK); +} + + +/*** + * NAME + * flt_otel_ops_channel_pre_analyze - channel pre-analyze callback + * + * SYNOPSIS + * static int flt_otel_ops_channel_pre_analyze(struct stream *s, struct filter *f, struct channel *chn, uint an_bit) + * + * ARGUMENTS + * s - the stream being analyzed + * f - the filter instance + * chn - the channel on which the analyzing is done + * an_bit - the analyzer identifier bit + * + * DESCRIPTION + * Channel pre-analyze callback. It maps the analyzer bit to an + * event index and runs the corresponding event via flt_otel_event_run(). + * + * RETURN VALUE + * Returns a negative value if an error occurs, 0 if it needs to wait, + * any other value otherwise. + */ +static int flt_otel_ops_channel_pre_analyze(struct stream *s, struct filter *f, struct channel *chn, uint an_bit) +{ + OTELC_FUNC("%p, %p, %p, 0x%08x", s, f, chn, an_bit); + + OTELC_RETURN_INT(FLT_OTEL_RET_OK); +} + + +/*** + * NAME + * flt_otel_ops_channel_post_analyze - channel post-analyze callback + * + * SYNOPSIS + * static int flt_otel_ops_channel_post_analyze(struct stream *s, struct filter *f, struct channel *chn, uint an_bit) + * + * ARGUMENTS + * s - the stream being analyzed + * f - the filter instance + * chn - the channel on which the analyzing is done + * an_bit - the analyzer identifier bit + * + * DESCRIPTION + * This function, for its part, is not resumable. It is called when a + * filterable analyzer finishes its processing. So it is called once for + * the same analyzer. + * + * RETURN VALUE + * Returns a negative value if an error occurs, 0 if it needs to wait, + * any other value otherwise. + */ +static int flt_otel_ops_channel_post_analyze(struct stream *s, struct filter *f, struct channel *chn, uint an_bit) +{ + OTELC_FUNC("%p, %p, %p, 0x%08x", s, f, chn, an_bit); + + OTELC_RETURN_INT(FLT_OTEL_RET_OK); +} + + +/*** + * NAME + * flt_otel_ops_channel_end_analyze - channel end-analyze callback + * + * SYNOPSIS + * static int flt_otel_ops_channel_end_analyze(struct stream *s, struct filter *f, struct channel *chn) + * + * ARGUMENTS + * s - the stream being analyzed + * f - the filter instance + * chn - the channel on which the analyzing ends + * + * DESCRIPTION + * Channel end-analyze callback. It runs the client or server session-end + * event depending on the channel direction. For the request channel, + * it also fires the server-unavailable event if response analyzers were + * configured but never executed. + * + * RETURN VALUE + * Returns a negative value if an error occurs, 0 if it needs to wait, + * any other value otherwise. + */ +static int flt_otel_ops_channel_end_analyze(struct stream *s, struct filter *f, struct channel *chn) +{ + OTELC_FUNC("%p, %p, %p", s, f, chn); + + OTELC_RETURN_INT(FLT_OTEL_RET_OK); +} + + +/*** + * NAME + * flt_otel_ops_http_headers - HTTP headers callback (flt_ops.http_headers) + * + * SYNOPSIS + * static int flt_otel_ops_http_headers(struct stream *s, struct filter *f, struct http_msg *msg) + * + * ARGUMENTS + * s - the stream being processed + * f - the filter instance + * msg - the HTTP message whose headers are ready + * + * DESCRIPTION + * HTTP headers callback. It fires the on-http-headers-request or + * on-http-headers-response event depending on the channel direction. + * + * RETURN VALUE + * Returns a negative value if an error occurs, 0 if it needs to wait, + * any other value otherwise. + */ +static int flt_otel_ops_http_headers(struct stream *s, struct filter *f, struct http_msg *msg) +{ + OTELC_FUNC("%p, %p, %p", s, f, msg); + + OTELC_RETURN_INT(FLT_OTEL_RET_OK); +} + + +#ifdef DEBUG_OTEL + +/*** + * NAME + * flt_otel_ops_http_payload - HTTP payload callback (flt_ops.http_payload) + * + * SYNOPSIS + * static int flt_otel_ops_http_payload(struct stream *s, struct filter *f, struct http_msg *msg, uint offset, uint len) + * + * ARGUMENTS + * s - the stream being processed + * f - the filter instance + * msg - the HTTP message containing the payload + * offset - the offset in the HTX message where data starts + * len - the maximum number of bytes to forward + * + * DESCRIPTION + * Debug-only HTTP payload callback. It logs the channel direction, proxy + * mode, offset and data length. No actual data processing is performed. + * + * RETURN VALUE + * Returns the number of bytes to forward, or a negative value on error. + */ +static int flt_otel_ops_http_payload(struct stream *s, struct filter *f, struct http_msg *msg, uint offset, uint len) +{ + OTELC_FUNC("%p, %p, %p, %u, %u", s, f, msg, offset, len); + + OTELC_RETURN_INT(len); +} + +#endif /* DEBUG_OTEL */ + + +/*** + * NAME + * flt_otel_ops_http_end - HTTP end callback (flt_ops.http_end) + * + * SYNOPSIS + * static int flt_otel_ops_http_end(struct stream *s, struct filter *f, struct http_msg *msg) + * + * ARGUMENTS + * s - the stream being processed + * f - the filter instance + * msg - the HTTP message that has ended + * + * DESCRIPTION + * HTTP end callback. It fires the on-http-end-request or + * on-http-end-response event depending on the channel direction. + * + * RETURN VALUE + * Returns a negative value if an error occurs, 0 if it needs to wait, + * any other value otherwise. + */ +static int flt_otel_ops_http_end(struct stream *s, struct filter *f, struct http_msg *msg) +{ + OTELC_FUNC("%p, %p, %p", s, f, msg); + + OTELC_RETURN_INT(FLT_OTEL_RET_OK); +} + + +/*** + * NAME + * flt_otel_ops_http_reply - HTTP reply callback (flt_ops.http_reply) + * + * SYNOPSIS + * static void flt_otel_ops_http_reply(struct stream *s, struct filter *f, short status, const struct buffer *msg) + * + * ARGUMENTS + * s - the stream being processed + * f - the filter instance + * status - the HTTP status code of the reply + * msg - the reply message buffer, or NULL + * + * DESCRIPTION + * HTTP reply callback. It fires the on-http-reply event when HAProxy + * generates an internal reply (e.g. error page or deny response). + * + * RETURN VALUE + * This function does not return a value. + */ +static void flt_otel_ops_http_reply(struct stream *s, struct filter *f, short status, const struct buffer *msg) +{ + OTELC_FUNC("%p, %p, %hd, %p", s, f, status, msg); + + OTELC_RETURN(); +} + + +#ifdef DEBUG_OTEL + +/*** + * NAME + * flt_otel_ops_http_reset - HTTP reset callback (flt_ops.http_reset) + * + * SYNOPSIS + * static void flt_otel_ops_http_reset(struct stream *s, struct filter *f, struct http_msg *msg) + * + * ARGUMENTS + * s - the stream being processed + * f - the filter instance + * msg - the HTTP message being reset + * + * DESCRIPTION + * Debug-only HTTP reset callback. It logs the channel direction and proxy + * mode when an HTTP message is reset (e.g. due to a redirect or retry). + * + * RETURN VALUE + * This function does not return a value. + */ +static void flt_otel_ops_http_reset(struct stream *s, struct filter *f, struct http_msg *msg) +{ + OTELC_FUNC("%p, %p, %p", s, f, msg); + + OTELC_RETURN(); +} + + +/*** + * NAME + * flt_otel_ops_tcp_payload - TCP payload callback (flt_ops.tcp_payload) + * + * SYNOPSIS + * static int flt_otel_ops_tcp_payload(struct stream *s, struct filter *f, struct channel *chn, uint offset, uint len) + * + * ARGUMENTS + * s - the stream being processed + * f - the filter instance + * chn - the channel containing the payload data + * offset - the offset in the buffer where data starts + * len - the maximum number of bytes to forward + * + * DESCRIPTION + * Debug-only TCP payload callback. It logs the channel direction, proxy + * mode, offset and data length. No actual data processing is performed. + * + * RETURN VALUE + * Returns the number of bytes to forward, or a negative value on error. + */ +static int flt_otel_ops_tcp_payload(struct stream *s, struct filter *f, struct channel *chn, uint offset, uint len) +{ + OTELC_FUNC("%p, %p, %p, %u, %u", s, f, chn, offset, len); + + OTELC_RETURN_INT(len); +} + +#endif /* DEBUG_OTEL */ + + +struct flt_ops flt_otel_ops = { + /* Callbacks to manage the filter lifecycle. */ + .init = flt_otel_ops_init, + .deinit = flt_otel_ops_deinit, + .check = flt_otel_ops_check, + .init_per_thread = flt_otel_ops_init_per_thread, + .deinit_per_thread = OTELC_DBG_IFDEF(flt_otel_ops_deinit_per_thread, NULL), + + /* Stream callbacks. */ + .attach = flt_otel_ops_attach, + .stream_start = flt_otel_ops_stream_start, + .stream_set_backend = flt_otel_ops_stream_set_backend, + .stream_stop = flt_otel_ops_stream_stop, + .detach = flt_otel_ops_detach, + .check_timeouts = flt_otel_ops_check_timeouts, + + /* Channel callbacks. */ + .channel_start_analyze = flt_otel_ops_channel_start_analyze, + .channel_pre_analyze = flt_otel_ops_channel_pre_analyze, + .channel_post_analyze = flt_otel_ops_channel_post_analyze, + .channel_end_analyze = flt_otel_ops_channel_end_analyze, + + /* HTTP callbacks. */ + .http_headers = flt_otel_ops_http_headers, + .http_payload = OTELC_DBG_IFDEF(flt_otel_ops_http_payload, NULL), + .http_end = flt_otel_ops_http_end, + .http_reset = OTELC_DBG_IFDEF(flt_otel_ops_http_reset, NULL), + .http_reply = flt_otel_ops_http_reply, + + /* TCP callbacks. */ + .tcp_payload = OTELC_DBG_IFDEF(flt_otel_ops_tcp_payload, NULL) +}; + + +/* Advertise OTel support in haproxy -vv output. */ +REGISTER_BUILD_OPTS("Built with OpenTelemetry support (C++ version " OTELCPP_VERSION ", C Wrapper version " OTELC_VERSION ")."); + +/* + * Local variables: + * c-indent-level: 8 + * c-basic-offset: 8 + * End: + * + * vi: noexpandtab shiftwidth=8 tabstop=8 + */ diff --git a/addons/otel/src/parser.c b/addons/otel/src/parser.c new file mode 100644 index 000000000..4d9172598 --- /dev/null +++ b/addons/otel/src/parser.c @@ -0,0 +1,73 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "../include/include.h" + + +#ifdef OTELC_DBG_MEM +static struct otelc_dbg_mem_data dbg_mem_data[1000000]; +static struct otelc_dbg_mem dbg_mem; +#endif + + +/*** + * NAME + * flt_otel_parse - main filter parser entry point + * + * SYNOPSIS + * static int flt_otel_parse(char **args, int *cur_arg, struct proxy *px, struct flt_conf *fconf, char **err, void *private) + * + * ARGUMENTS + * args - configuration line arguments array + * cur_arg - pointer to the current argument index + * px - proxy instance owning the filter + * fconf - filter configuration structure to populate + * err - indirect pointer to error message string + * private - unused private data pointer + * + * DESCRIPTION + * Main filter parser entry point, registered for the "otel" filter keyword. + * Verifies that insecure-fork-wanted is enabled, then parses the filter ID + * and configuration file path from the HAProxy configuration line. If no + * filter ID is specified, the default ID is used. + * + * 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(char **args, int *cur_arg, struct proxy *px, struct flt_conf *fconf, char **err, void *private) +{ + int retval = ERR_NONE; + + OTELC_FUNC("%p, %p, %p, %p, %p:%p, %p", args, cur_arg, px, fconf, OTELC_DPTR_ARGS(err), private); + + OTELC_DBG_IFDEF(otelc_dbg_level = FLT_OTEL_DEBUG_LEVEL, ); + +#ifdef OTELC_DBG_MEM + /* Initialize the debug memory tracker before the first allocation. */ + FLT_OTEL_RUN_ONCE( + if (otelc_dbg_mem_init(&dbg_mem, dbg_mem_data, OTELC_TABLESIZE(dbg_mem_data)) == -1) + OTELC_RETURN_INT(retval); + ); +#endif + + OTELC_RETURN_INT(retval); +} + + +/* Declare the filter parser for FLT_OTEL_OPT_NAME keyword. */ +static struct flt_kw_list flt_kws = { FLT_OTEL_SCOPE, { }, { + { FLT_OTEL_OPT_NAME, flt_otel_parse, NULL }, + { NULL, NULL, NULL }, + } +}; + +INITCALL1(STG_REGISTER, flt_register_keywords, &flt_kws); + +/* + * Local variables: + * c-indent-level: 8 + * c-basic-offset: 8 + * End: + * + * vi: noexpandtab shiftwidth=8 tabstop=8 + */ diff --git a/include/haproxy/arg-t.h b/include/haproxy/arg-t.h index 708a90ede..fc3de3cf6 100644 --- a/include/haproxy/arg-t.h +++ b/include/haproxy/arg-t.h @@ -92,6 +92,7 @@ enum { ARGC_TCK, /* tcp-check expression */ ARGC_CFG, /* configuration expression */ ARGC_CLI, /* CLI expression*/ + ARGC_OTEL, /* opentelemetry scope args */ }; /* flags used when compiling and executing regex */ diff --git a/src/sample.c b/src/sample.c index d741716f8..6765cbc0f 100644 --- a/src/sample.c +++ b/src/sample.c @@ -1471,6 +1471,7 @@ int smp_resolve_args(struct proxy *p, char **err) case ARGC_TCK: where = "in tcp-check expression in"; break; case ARGC_CFG: where = "in configuration expression in"; break; case ARGC_CLI: where = "in CLI expression in"; break; + case ARGC_OTEL: where = "in otel-scope directive in"; break; } /* set a few default settings */