open-telemetry / opentelemetry-cpp

The OpenTelemetry C++ Client
https://opentelemetry.io/
Apache License 2.0
862 stars 412 forks source link

[Trace] reopen - Missing example instrumenting asynchronous operation #1463 #1464

Open keminming opened 2 years ago

keminming commented 2 years ago

Open this issue since I don’t think #1463 Is well resolved, as we don’t find a good example how to instrument function call with async callback, in most condition, I/O operation will be made in an asynchrous fashion, but it doesn’t seem to be well supported by the cpp lib.

lalitb commented 2 years ago

Ok fair enough. Please let us know which HTTP async library you are going to use - the example would depend on that.

keminming commented 2 years ago

It’s an in-house http client, the usage is described in #1463, where the “get” method takes a callback to handle the http response.

Think you can make an example based on the async http client you shared here: https://github.com/open-telemetry/opentelemetry-cpp/blob/main/ext/include/opentelemetry/ext/http/client/http_client.h

lalitb commented 2 years ago

The rough code (not tested) should be something like this, do you see any issue in this -

auto span = ... ;
// not creating any scope, as it won't work for async mode.
http_client->Get(url, carrier.headers_,[&span](http_client::Result result){
if (result)
{
  span->SetAttribute(..);
  span->SetStatus(...);
  span->End(); // span exported here
});

scope object won't work in the async case. scope object is helpful for creating nested spans (i.e, the inner span is a child of the outer span). So the nested span scenario would now look like this -

auto outer_span =  tracer->StartSpan(name1, attributes);
// not creating any scope, as it won't work for async mode.
http_client->Get(url, carrier.headers_,[&outer_span, &tracer](http_client::Result result){
if (result)
{
  outer_span->SetAttribute(..);
  outer_span->SetStatus(...);
  StartSpanOptions option;
  option.parent = outer_span.GetSpanContext();
  auto innner_span =  tracer->StartSpan(name2, attributes, option);
  inner_span->SetAttribute(...);
  inner_span->SetStatus(...);
  inner_span->End(); //inner span exported here
  outer_span->End(); // outer span exporter here...
});
lalitb commented 2 years ago

@keminming - Let me know if the above example brings clarity. You can try this, and update here if it doesn't work. And if it works, feel free to contribute async example.

keminming commented 2 years ago

@lalitb Thanks for sharing the example. I've run debugging on it and the result looks to be as expected. It ended up with 2 spans, outer and inner, and the parent span of inner is outer.

So "scope" seems only helps for nested sync calls, that don't require passing parent span explicitly to the callee to set up the parent/child relationship. Also noticed "span link" is designed to express a certain relationship between spans, is there guidance on when to use parent and when to use span link? Does it have any impact on how the backend visualizes spans like in Jaeger or Zipkin?

lalitb commented 2 years ago

. Also noticed "span link" is designed to express a certain relationship between spans, is there guidance on when to use parent and when to use span link?

You can find few of the examples of links here - https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/overview.md#links-between-spans

Does it have any impact on how the backend visualizes spans like in Jaeger or Zipkin?

lalitb commented 2 years ago

@keminming - Let me know if it is good to close this issue unless you have plans to add the async example?

keminming commented 2 years ago

@lalitb This is the sample code I wrote for testing, based on the default http_client. For some reason, the onResponse handler won't trigger, so I add the code in the onEvent handler. Feel it's definitely helpful to include an async example (not have to be this one), which has 2-3 layers of the nested HTTP call.

` auto httpClient = http_client::HttpClientFactory::Create();

class CustomEventHandler : public http_client::EventHandler { public: void setSpan(nostd::shared_ptr span, std::shared_ptr session){ _span = span; _session = session; }

virtual void OnEvent(http_client::SessionState state, nostd::string_view reason) noexcept override
{
  _span->End();
  StartSpanOptions option;
  option.parent = _span->GetContext();
  auto inner_span =  get_tracer("http-client")->StartSpan("name2", option);
  /*
  /auto request = _session->CreateRequest();
  CustomEventHandler res_handler;
  res_handler.setSpan(inner_span, _session);
  _session->SendRequest(res_handler);
   */
  inner_span->End();
}
virtual void OnConnecting(const http_client::SSLCertificate &) noexcept {}
virtual ~CustomEventHandler() = default;
nostd::shared_ptr<Span> _span;
std::shared_ptr<http_client::Session> _session;

};

auto session = httpClient->CreateSession("localhost" + 8000); auto request = session->CreateRequest(); //request->AddHeader(..); CustomEventHandler res_handler;

auto outer_span = get_tracer("http-client")->StartSpan("name1"); res_handler.setSpan(outer_span, session); session->SendRequest(res_handler);`

github-actions[bot] commented 2 years ago

This issue was marked as stale due to lack of activity.