Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Exporting spans from inside DLL on Windows does not work. #3176

Open
rsomla1 opened this issue Nov 28, 2024 · 3 comments
Open

Exporting spans from inside DLL on Windows does not work. #3176

rsomla1 opened this issue Nov 28, 2024 · 3 comments
Assignees
Labels
bug Something isn't working issue:blocking This issue is preventing other fixes priority:p1 Issues that are blocking triage/accepted Indicates an issue or PR is ready to be actively worked on.

Comments

@rsomla1
Copy link

rsomla1 commented Nov 28, 2024

This issue is specific to Windows environment and using DLL libraries.

I build a simple executable which sets span exporter and creates a span. It also calls a library function that crates another span. Here is the main executable

int main(int argc, const char* argv[])
try
{
  // Configure trace provider that exports to stdout

  using prv_factory = trace_sdk::TracerProviderFactory;
  using prc_factory = trace_sdk::SimpleSpanProcessorFactory;
  using exp_factory = exporter::trace::OStreamSpanExporterFactory;

  trace::Provider::SetTracerProvider(
    std::shared_ptr<trace::TracerProvider>{
      prv_factory::Create(prc_factory::Create(exp_factory::Create(std::cout)))
    }
  );

  // Get provider and create a span

  auto provider = trace::Provider::GetTracerProvider();
  auto tracer = provider->GetTracer("try", "0.0.1");
  auto span = tracer->StartSpan("main");
  trace::Scope scope{tracer->WithActiveSpan(span)};

  // Call library function that creates another span.
  // Expecting that it will use trace provider configured above.

  foo();

  cout << "Done!" << endl;
}
catch(...) {}

Function foo() defined in a DLL library does this

extern "C" EXPORT void foo()
{
  auto provider = trace::Provider::GetTracerProvider();
  auto tracer = provider->GetTracer("lib", "0.0.1");
  auto span = tracer->StartSpan("foo");

  cout << "foo DONE!" << endl;
}

When I build this code on Linux it produces the expected output with two spans, the "foo" span a child of "main" span:

~/cpp/otel-issue/build./try
foo DONE!
{
  name          : foo
  trace_id      : 79194137338009900a5afe676c2c2471
  span_id       : 6f26aecf124938e5
  tracestate    :   parent_span_id: 0d9ec5b22ac67a29
  start         : 1732718683944699646
  duration      : 24848
  description   :   span kind     : Internal
  status        : Unset
  attributes    :   events        :   links         :   resources     :         service.name: unknown_service
        telemetry.sdk.version: 1.17.0
        telemetry.sdk.language: cpp
        telemetry.sdk.name: opentelemetry
  instr-lib     : lib-0.0.1
}
Done!
{
  name          : main
  trace_id      : 79194137338009900a5afe676c2c2471
  span_id       : 0d9ec5b22ac67a29
  tracestate    :   parent_span_id: 0000000000000000
  start         : 1732718683944630175
  duration      : 216489
  description   :   span kind     : Internal
  status        : Unset
  attributes    :   events        :   links         :   resources     :         service.name: unknown_service
        telemetry.sdk.version: 1.17.0
        telemetry.sdk.language: cpp
        telemetry.sdk.name: opentelemetry
  instr-lib     : try-0.0.1
}

However, if I build and run the same code on Windows the output is as follows without the "foo" span:

C:\Work\MySQL\cpp\build-internals>Release\try.exe
foo DONE!
Done!
{
  name          : main
  trace_id      : c265485eac059ec46d9d170a4f004d81
  span_id       : e615d927b958fde0
  tracestate    :
  parent_span_id: 0000000000000000
  start         : 1732718442900722900
  duration      : 694400
  description   :
  span kind     : Internal
  status        : Unset
  attributes    :
  events        :
  links         :
  resources     :
        telemetry.sdk.language: cpp
        telemetry.sdk.name: opentelemetry
        telemetry.sdk.version: 1.17.0
        service.name: unknown_service
  instr-lib     : try-0.0.1
}

As you can see I am testing with otel 1.17.0. My guess is that in case of Windows the connection between tracer provider configured in the main application and the one obtained inside DLL is broken. Therefore the function inside DLL gets the phony provider which ignores all traces sent to it. This is why the span from foo() is not seen in the Windows output.

@rsomla1 rsomla1 added the bug Something isn't working label Nov 28, 2024
@github-actions github-actions bot added the needs-triage Indicates an issue or PR lacks a `triage/foo` label and requires one. label Nov 28, 2024
@rsomla1
Copy link
Author

rsomla1 commented Nov 28, 2024

CMakeLists.txt
I wanted to add a small cmake project that reproduces the issue but the interface does not allow me to attach .cc files. So I paste them here instead.

The try.cc source

#include <iostream>

#ifdef _WIN32
#define IMPORT __declspec(dllimport)
#else
#define IMPORT
#endif

#include <opentelemetry/exporters/ostream/span_exporter_factory.h>
#include <opentelemetry/sdk/trace/processor.h>
#include <opentelemetry/sdk/trace/simple_processor_factory.h>
#include <opentelemetry/sdk/trace/tracer_provider_factory.h>
#include <opentelemetry/trace/provider.h>


using namespace std;
namespace trace      = opentelemetry::trace;
namespace exporter   = opentelemetry::exporter;
namespace trace_sdk  = opentelemetry::sdk::trace;
namespace nostd      = opentelemetry::v1::nostd;


void setup_exporter();
extern "C" IMPORT void foo();


int main(int argc, const char* argv[])
try
{
  // Configure trace provider that exports to stdout

  using prv_factory = trace_sdk::TracerProviderFactory;
  using prc_factory = trace_sdk::SimpleSpanProcessorFactory;
  using exp_factory = exporter::trace::OStreamSpanExporterFactory;

  trace::Provider::SetTracerProvider(
    std::shared_ptr<trace::TracerProvider>{
      prv_factory::Create(prc_factory::Create(exp_factory::Create(std::cout)))
    }
  );

  // Get provider and create a span

  auto provider = trace::Provider::GetTracerProvider();
  auto tracer = provider->GetTracer("try", "0.0.1");
  auto span = tracer->StartSpan("main");
  trace::Scope scope{tracer->WithActiveSpan(span)};

  // Call library function that creates another span.
  // Expecting that it will use trace provider configured above.

  foo();

  cout << "Done!" << endl;
}
catch(const char *e)
{
  cout << "ERROR: " << e << endl;
  exit(1);
}
catch(std::exception &e)
{
  cout << "EXCEPTION: " << e.what() << endl;
  exit(1);
}

The lib.cc source

#include <iostream>

#ifdef _WIN32
#define EXPORT __declspec(dllexport)
#else
#define EXPORT
#endif


#include <opentelemetry/trace/provider.h>

namespace trace      = opentelemetry::trace;
using namespace std;

extern "C" EXPORT void foo()
{
  auto provider = trace::Provider::GetTracerProvider();
  auto tracer = provider->GetTracer("lib", "0.0.1");
  auto span = tracer->StartSpan("foo");

  cout << "foo DONE!" << endl;
}

@rsomla1
Copy link
Author

rsomla1 commented Nov 28, 2024

A related issue: #2534

@malkia
Copy link

malkia commented Dec 2, 2024

I support my own soft fork which I'm using at work, but being soft fork would make it also not acceptable solution by many.

My goal is to have single opinionated .dll and easy to reuse precompiled sdk suitable for our needs. I've tried some years ago the vcpkg/CMake and dll but was not satisifed, hence my soft fork. Also I'm relying on bazel, and almost completely ignoring cmake (I simply understand bazel better than cmake).

So long story short, the version I've got works with your example, but being soft fork also makes it not suitable for your needs - e.g. I won't be able to support it. Given that it works, it's possible that you may have some mis-configuration with CMake and should be something easy to fix.

Here is what I've got - malkia@1ef815f


Edit: I saw that the request was to make lib a .dll itself - in my example above that's not the case, but added it since. Also added some docs and running depends on it to show what gets imported

https://github.com/malkia/opentelemetry-cpp/blob/main/x/rsomla1/README.md

@marcalff marcalff added triage/accepted Indicates an issue or PR is ready to be actively worked on. issue:blocking This issue is preventing other fixes priority:p1 Issues that are blocking and removed needs-triage Indicates an issue or PR lacks a `triage/foo` label and requires one. labels Dec 2, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working issue:blocking This issue is preventing other fixes priority:p1 Issues that are blocking triage/accepted Indicates an issue or PR is ready to be actively worked on.
Projects
None yet
Development

No branches or pull requests

4 participants