Skip to content

Opentelemetry API singletons (TraceProvider etc.) do not play well in shared library uses that hide symbol visibility #1520

Closed
@astitcher

Description

@astitcher

Describe your environment
We are using the opentelemetry tracing API (and SDK) inside a Linux C++ shared library (.so file) built with the standard gcc toolchain. This library is provides messaging using the AMQP protocol (the library is Qpid Proton C++). We create tracing spans internal to the library to represent the lifecycle of messages sent and received by the library. The contexts associated with the spans are propagated with the messages so that distributed tracing is achieved.

The intention is that any application using our library should be able to create its own tracing spans that are naturally related to the library generated spans (by default using the active span as the parent span). This requires that the TracingProvider accessible to the library and to the application using it is the same TracingProvider.

Steps to reproduce
Our library is built with -fvisibility=hidden. This means that any symbol that should be exported from the library needs some extra annotation (__attribute__((visibility("default"))) for gcc/clang). We use this to carefully control the visible API/ABI from our library and to be sure that internal details aren't visible outside.

However the way that the singleton insideProvider is implemented in a header file means that it is defined as a static symbol inside the inline member function Provider::GetProvider. This means that there are duplicate symbols for every time the provider.h header file is included in for example our library and an application that uses it.

If the symbols are all visible to the linker at link and runtime then the symbol will be effectively deduplicated and there will be only one used - to my understanding the application symbol takes precedence, but as long as there is only one it will work correctly.

However when the library symbols are hidden by default as in our library there is no way for the runtime loader to know that there should only be one version of the singleton and what happens is that the library and the application end up with different TracerProviders.

This is a serious problem for us as we ship our library with the symbols exported explicitly for very good reason and so we can't just use Provider::GetTracerProvider in the library and application and have it work correctly together.

What is the expected behavior?
The way I would expect this to work is that the singletons are not defined in header files and so duplicated in every object file that includes them, but rather the header file only declares Provider::GetProvider and it is defined in one of the opentelemetry-cpp libraries (the common lib?). I understand that this moves the singleton out of what you think of as the 'API' and into the 'SDK'. But I really think that architecturally singletons are actuall implementation artifacts not API artifiacts.

Additional context
I note that this is not an issue at all if you statically link everything as in that case the final link phase takes care of the issue.

I also note that this should be an issue using DLLs on windows as by symbols are not exported by default (afair) and also need to be explicitly marked to be exported (using __declspec(dllexport)

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions