diff --git a/include/scope.h b/include/scope.h index 5307e903..4b701ac2 100644 --- a/include/scope.h +++ b/include/scope.h @@ -122,6 +122,8 @@ struct flt_otel_runtime_context { bool flag_harderr; /* [0 1] */ bool flag_disabled; /* [0 1] */ bool flag_ctx_valid; /* [0 1] */ + bool flag_norec; /* Root span is not recording (sampled out): fast path. */ + struct otelc_span *root_span; /* The stream's root span (parent fallback in fast path). */ uint8_t logging; /* [0 1 3] */ uint analyzers; /* Executed channel analyzers. */ uint idle_timeout; /* Idle timeout interval in milliseconds (0 = off). */ diff --git a/src/event.c b/src/event.c index 45b39559..b5b4486b 100644 --- a/src/event.c +++ b/src/event.c @@ -630,6 +630,26 @@ static int flt_otel_scope_run_span(struct stream *s, struct filter *f, struct ch span->span = OTELC_OPS(conf->instr->tracer, start_span_with_options, span->id, span->ref_span, span->ref_ctx, ts_steady, ts_system, conf_span->kind, NULL, 0); if (span->span == NULL) OTELC_RETURN_INT(FLT_OTEL_RET_ERROR); + + /* + * Non-recording fast path. The sampler decides at the root: a + * root that is not recording (sampled out) means every span of + * this stream will be a non-recording span whose attributes, + * events and status the SDK silently discards. Remember that + * on the runtime context so the remaining scopes can skip the + * sample evaluation and the creation of spans that do not take + * part in context propagation (see flt_otel_scope_run()). + */ + if (conf_span->flag_root) { + struct flt_otel_runtime_context *rt_ctx = FLT_OTEL_RT_CTX(f->ctx); + + rt_ctx->root_span = span->span; + if (OTELC_OPS(span->span, is_recording) == 0) { + OTELC_DBG(INFO, "root span '%s' is not recording: fast path enabled", span->id); + + rt_ctx->flag_norec = 1; + } + } } /* Add all resolved span links to the current span. */ @@ -669,7 +689,7 @@ static int flt_otel_scope_run_span(struct stream *s, struct filter *f, struct ch retval = FLT_OTEL_RET_ERROR; /* Record exceptions on the span via the wrapper's record_exception(). */ - if (!LIST_ISEMPTY(&(conf_span->exceptions))) { + if (!FLT_OTEL_RT_CTX(f->ctx)->flag_norec && !LIST_ISEMPTY(&(conf_span->exceptions))) { struct flt_otel_conf_exception *conf_exc; list_for_each_entry(conf_exc, &(conf_span->exceptions), list) { @@ -933,7 +953,7 @@ int flt_otel_scope_run(struct stream *s, struct filter *f, struct channel *chn, struct flt_otel_conf_unset_var *unset_var; struct timespec ts_now_steady, ts_now_system; int retval = FLT_OTEL_RET_OK; - bool flag_stop = 0; + bool flag_stop = 0, norec = 0; OTELC_FUNC("%p, %p, %p, %p, %p, %p, %u, %p:%p", s, f, chn, conf_scope, ts_steady, ts_system, dir, OTELC_DPTR_ARGS(err)); @@ -1078,6 +1098,21 @@ int flt_otel_scope_run(struct stream *s, struct filter *f, struct channel *chn, OTELC_DBG(INFO, "run span '%s' -> '%s'", conf_scope->id, conf_span->id); FLT_OTEL_DBG_CONF_SPAN("run span ", conf_span); + /* + * Non-recording fast path: the root span was sampled out, so + * nothing produced here will ever be exported. Spans that do + * not inject a context are not created at all, and for the ones + * that are (propagation must stay intact so that downstream + * follows the sampling decision) no attribute, event, baggage, + * link or status sample is evaluated. + */ + norec = FLT_OTEL_RT_CTX(f->ctx)->flag_norec; + if (norec && !conf_span->flag_root && (conf_span->ctx_id == NULL)) { + OTELC_DBG(DEBUG, "fast path: span '%s' skipped", conf_span->id); + + continue; + } + flt_otel_scope_data_init(&data); span = flt_otel_scope_span_init(f->ctx, conf_span->id, conf_span->id_len, conf_span->ref_id, conf_span->ref_id_len, dir, err); @@ -1092,7 +1127,7 @@ int flt_otel_scope_run(struct stream *s, struct filter *f, struct channel *chn, * Each link name is looked up first in the active spans, then * in the extracted contexts. */ - if (!LIST_ISEMPTY(&(conf_span->links))) { + if (!norec && !LIST_ISEMPTY(&(conf_span->links))) { struct flt_otel_runtime_context *rt_ctx = FLT_OTEL_RT_CTX(f->ctx); struct flt_otel_conf_link *conf_link; @@ -1148,7 +1183,7 @@ int flt_otel_scope_run(struct stream *s, struct filter *f, struct channel *chn, } } - list_for_each_entry(sample, &(conf_span->attributes), list) { + if (!norec) list_for_each_entry(sample, &(conf_span->attributes), list) { if (flt_otel_cond_pass(sample->cond, s, dir) == 0) continue; @@ -1158,7 +1193,7 @@ int flt_otel_scope_run(struct stream *s, struct filter *f, struct channel *chn, retval = FLT_OTEL_RET_ERROR; } - list_for_each_entry(sample, &(conf_span->events), list) { + if (!norec) list_for_each_entry(sample, &(conf_span->events), list) { if (flt_otel_cond_pass(sample->cond, s, dir) == 0) continue; @@ -1168,7 +1203,7 @@ int flt_otel_scope_run(struct stream *s, struct filter *f, struct channel *chn, retval = FLT_OTEL_RET_ERROR; } - list_for_each_entry(sample, &(conf_span->baggages), list) { + if (!norec) list_for_each_entry(sample, &(conf_span->baggages), list) { if (flt_otel_cond_pass(sample->cond, s, dir) == 0) continue; @@ -1183,7 +1218,7 @@ int flt_otel_scope_run(struct stream *s, struct filter *f, struct channel *chn, * defined, each gated by a condition; the first whose condition * holds is applied and the remaining lines are skipped. */ - list_for_each_entry(sample, &(conf_span->statuses), list) { + if (!norec) list_for_each_entry(sample, &(conf_span->statuses), list) { if (flt_otel_cond_pass(sample->cond, s, dir) == 0) continue; diff --git a/src/scope.c b/src/scope.c index 73c74b86..aac8bde3 100644 --- a/src/scope.c +++ b/src/scope.c @@ -204,9 +204,21 @@ struct flt_otel_scope_span *flt_otel_scope_span_init(struct flt_otel_runtime_con * regardless would silently turn it into a trace * root, which is meaningless. */ - FLT_OTEL_ERR("cannot find referenced span/context '%s'", ref_id); - - OTELC_RETURN_PTR(retptr); + if (rt_ctx->flag_norec && (rt_ctx->root_span != NULL)) { + /* + * Non-recording fast path: the parent was + * skipped on purpose; hang this span off + * the root so that the trace/flags it + * propagates stay those of the stream. + */ + OTELC_DBG(DEBUG, "fast path: parent '%s' skipped, using root span", ref_id); + + ref_span = rt_ctx->root_span; + } else { + FLT_OTEL_ERR("cannot find referenced span/context '%s'", ref_id); + + OTELC_RETURN_PTR(retptr); + } } } }