Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions ci/docker/python-wheel-manylinux.dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ RUN --mount=type=secret,id=github_repository_owner \
--x-feature=flight \
--x-feature=gcs \
--x-feature=json \
--x-feature=opentelemetry \
--x-feature=orc \
--x-feature=parquet \
--x-feature=s3 && \
Expand Down
10 changes: 9 additions & 1 deletion ci/scripts/python_wheel_xlinux_build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,13 @@ function check_arrow_visibility {
# Filter out Arrow symbols and see if anything remains.
# '_init' and '_fini' symbols may or not be present, we don't care.
# (note we must ignore the grep exit status when no match is found)
grep ' T ' nm_arrow.log | grep -v -E '(arrow|\b_init\b|\b_fini\b)' | cat - > visible_symbols.log
local allowed_symbols='(arrow|\b_init\b|\b_fini\b)'
# OpenTelemetry symbols are intentionally exported for features like
# automatic span linking. See cpp/src/arrow/symbols.map for more details.
if [[ "${ARROW_WITH_OPENTELEMETRY:-OFF}" == "ON" ]]; then
allowed_symbols="${allowed_symbols}|(opentelemetry)"
fi
grep ' T ' nm_arrow.log | grep -v -E "${allowed_symbols}" | cat - > visible_symbols.log

if [[ -f visible_symbols.log && `cat visible_symbols.log | wc -l` -eq 0 ]]; then
return 0
Expand Down Expand Up @@ -65,6 +71,7 @@ echo "=== (${PYTHON_VERSION}) Building Arrow C++ libraries ==="
: ${ARROW_WITH_BROTLI:=ON}
: ${ARROW_WITH_BZ2:=ON}
: ${ARROW_WITH_LZ4:=ON}
: ${ARROW_WITH_OPENTELEMETRY:=ON}
: ${ARROW_WITH_SNAPPY:=ON}
: ${ARROW_WITH_ZLIB:=ON}
: ${ARROW_WITH_ZSTD:=ON}
Expand Down Expand Up @@ -124,6 +131,7 @@ cmake \
-DARROW_WITH_BROTLI=${ARROW_WITH_BROTLI} \
-DARROW_WITH_BZ2=${ARROW_WITH_BZ2} \
-DARROW_WITH_LZ4=${ARROW_WITH_LZ4} \
-DARROW_WITH_OPENTELEMETRY=${ARROW_WITH_OPENTELEMETRY} \
-DARROW_WITH_SNAPPY=${ARROW_WITH_SNAPPY} \
-DARROW_WITH_ZLIB=${ARROW_WITH_ZLIB} \
-DARROW_WITH_ZSTD=${ARROW_WITH_ZSTD} \
Expand Down
11 changes: 11 additions & 0 deletions ci/vcpkg/vcpkg.json
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,17 @@
]
}
]
},
"opentelemetry": {
"description": "OpenTelemetry support",
"dependencies": [
{
"name": "opentelemetry-cpp",
"features": [
"otlp-http"
]
}
]
}
}
}
2 changes: 1 addition & 1 deletion cpp/src/arrow/flight/server_tracing_middleware.cc
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ class TracingServerMiddlewareFactory : public ServerMiddlewareFactory {
options.kind = otel::trace::SpanKind::kServer;
options.parent = otel::trace::GetSpan(new_otel_context)->GetContext();

auto tracer = otel::trace::Provider::GetTracerProvider()->GetTracer("arrow");
auto tracer = arrow::internal::tracing::GetTracer();

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@lidavidm I am not entirely sure why this is necessary but using the example middleware.py enabling otel as updated below with a running server:

$ export OTEL_SERVICE_NAME=arrow-raul
$ ARROW_TRACING_BACKEND=otlp_http python examples/flight/middleware.py server --listen grpc://localhost:5050 --otel

and client:

python examples/flight/middleware.py  client grpc://localhost:5050

I wasn't able to get traces unless I update this to use arrow::internal::tracing::GetTracer. Example of local traces with the built wheel and this changes:

Image

Initially I thought it might be due to the addition of CMAKE_INTERPROCEDURAL_OPTIMIZATION to the wheels but I've validated is unrelated.

Without this change I haven't been able to get opentelemetry traces using the flight.TracingServerMiddlewareFactory

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, it's been a while since I've worked with OTel...possibly the OTel trace provider isn't being configured (vs the hardcoded Arrow one)? The Arrow util is about equivalent, but does some static initialization first

@raulcd raulcd Feb 25, 2026

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah, that was my feeling too, either not being initialized or something to do with libarrow.so and libarrow_flight.so both linking opentelementry statically and getting their own copy of the static opentelemetry provider that's why I thought CMAKE_INTERPROCEDUREAL_OPTIMIZATION might be related (but proved it wasn't).
The Arrow util one should cross the shared object boundary and use the already initialized one (or initialize it) which I think is correct.
I just wanted to validate the change looks ok to you or whether do you think it would be better to keep investigating and try to configure when calling MakeTracingServerMiddlewareFactory instead of using the one initialized via libarrow.so?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is OK.

auto method_name = ToString(info.method);
auto span = tracer->StartSpan(
method_name,
Expand Down
10 changes: 9 additions & 1 deletion python/examples/flight/middleware.py
Original file line number Diff line number Diff line change
Expand Up @@ -138,17 +138,25 @@ def main():
"simply call the given server for the response. Demonstrates "
"propagation of the trace ID between servers."),
)
server.add_argument(
"--otel",
action="store_true",
help="Use OpenTelemetry instrumentation."
)

args = parser.parse_args()
if not getattr(args, "command"):
parser.print_help()
return 1

if args.command == "server":
middleware = {"trace": TracingServerMiddlewareFactory()}
if args.otel:
middleware["otel"] = flight.TracingServerMiddlewareFactory()
server = FlightServer(
args.delegate,
location=args.listen,
middleware={"trace": TracingServerMiddlewareFactory()})
middleware=middleware)
server.serve()
elif args.command == "client":
client = flight.connect(
Expand Down
Loading