2222#include " opentelemetry/ext/http/client/curl/http_client_curl.h"
2323#include " opentelemetry/ext/http/client/curl/http_operation_curl.h"
2424#include " opentelemetry/ext/http/client/http_client.h"
25+ #include " opentelemetry/nostd/string_view.h"
2526#include " opentelemetry/sdk/common/global_log_handler.h"
2627#include " opentelemetry/version.h"
2728
@@ -261,7 +262,8 @@ HttpOperation::HttpOperation(opentelemetry::ext::http::client::Method method,
261262 // Default connectivity and response size options
262263 bool is_raw_response,
263264 std::chrono::milliseconds http_conn_timeout,
264- bool reuse_connection)
265+ bool reuse_connection,
266+ bool is_log_enabled)
265267 : is_aborted_(false ),
266268 is_finished_ (false ),
267269 is_cleaned_(false ),
@@ -281,6 +283,7 @@ HttpOperation::HttpOperation(opentelemetry::ext::http::client::Method method,
281283 request_nwrite_(0 ),
282284 session_state_(opentelemetry::ext::http::client::SessionState::Created),
283285 compression_(compression),
286+ is_log_enabled_(is_log_enabled),
284287 response_code_(0 )
285288{
286289 /* get a curl handle */
@@ -569,8 +572,77 @@ CURLcode HttpOperation::SetCurlOffOption(CURLoption option, curl_off_t value)
569572 return rc;
570573}
571574
575+ int HttpOperation::CurlLoggerCallback (const CURL * /* handle */ ,
576+ curl_infotype type,
577+ const char *data,
578+ size_t size,
579+ void * /* clientp */ ) noexcept
580+ {
581+ nostd::string_view text_to_log{data, size};
582+
583+ if (!text_to_log.empty () && text_to_log[size - 1 ] == ' \n ' )
584+ {
585+ text_to_log = text_to_log.substr (0 , size - 1 );
586+ }
587+
588+ if (type == CURLINFO_TEXT)
589+ {
590+ static const auto kTlsInfo = nostd::string_view (" SSL connection using" );
591+ static const auto kFailureMsg = nostd::string_view (" Recv failure:" );
592+
593+ if (text_to_log.substr (0 , kTlsInfo .size ()) == kTlsInfo )
594+ {
595+ OTEL_INTERNAL_LOG_INFO (text_to_log);
596+ }
597+ else if (text_to_log.substr (0 , kFailureMsg .size ()) == kFailureMsg )
598+ {
599+ OTEL_INTERNAL_LOG_ERROR (text_to_log);
600+ }
601+ // This guard serves as a catch-all block for all other less interesting output that should
602+ // remain available for maintainer internal use and for debugging purposes only.
603+ #ifdef OTEL_CURL_DEBUG
604+ else
605+ {
606+ OTEL_INTERNAL_LOG_DEBUG (text_to_log);
607+ }
608+ #endif // OTEL_CURL_DEBUG
609+ }
610+ // Same as above, this guard is meant only for internal use by maintainers, and should not be used
611+ // in production (information leak).
612+ #ifdef OTEL_CURL_DEBUG
613+ else if (type == CURLINFO_HEADER_OUT)
614+ {
615+ static const auto kHeaderSent = nostd::string_view (" Send header => " );
616+
617+ while (!text_to_log.empty () && !std::iscntrl (text_to_log[0 ]))
618+ {
619+ const auto pos = text_to_log.find (' \n ' );
620+
621+ if (pos != nostd::string_view::npos)
622+ {
623+ OTEL_INTERNAL_LOG_DEBUG (kHeaderSent << text_to_log.substr (0 , pos - 1 ));
624+ text_to_log = text_to_log.substr (pos + 1 );
625+ }
626+ }
627+ }
628+ else if (type == CURLINFO_HEADER_IN)
629+ {
630+ static const auto kHeaderRecv = nostd::string_view (" Recv header => " );
631+ OTEL_INTERNAL_LOG_DEBUG (kHeaderRecv << text_to_log);
632+ }
633+ #endif // OTEL_CURL_DEBUG
634+
635+ return 0 ;
636+ }
637+
572638CURLcode HttpOperation::Setup ()
573639{
640+ #ifdef ENABLE_CURL_LOGGING
641+ static constexpr auto kEnableCurlLogging = true ;
642+ #else
643+ static constexpr auto kEnableCurlLogging = false ;
644+ #endif // ENABLE_CURL_LOGGING
645+
574646 if (!curl_resource_.easy_handle )
575647 {
576648 return CURLE_FAILED_INIT;
@@ -581,11 +653,28 @@ CURLcode HttpOperation::Setup()
581653 curl_error_message_[0 ] = ' \0 ' ;
582654 curl_easy_setopt (curl_resource_.easy_handle , CURLOPT_ERRORBUFFER, curl_error_message_);
583655
656+ // Support for custom debug function callback was added in version 7.9.6 so we guard against
657+ // exposing the default CURL output by keeping verbosity always disabled in lower versions.
658+ #if LIBCURL_VERSION_NUM < CURL_VERSION_BITS(7, 9, 6)
584659 rc = SetCurlLongOption (CURLOPT_VERBOSE, 0L );
585660 if (rc != CURLE_OK)
586661 {
587662 return rc;
588663 }
664+ #else
665+ rc = SetCurlLongOption (CURLOPT_VERBOSE, (is_log_enabled_ && kEnableCurlLogging ) ? 1L : 0L );
666+ if (rc != CURLE_OK)
667+ {
668+ return rc;
669+ }
670+
671+ rc = SetCurlPtrOption (CURLOPT_DEBUGFUNCTION,
672+ reinterpret_cast <void *>(&HttpOperation::CurlLoggerCallback));
673+ if (rc != CURLE_OK)
674+ {
675+ return rc;
676+ }
677+ #endif
589678
590679 // Specify target URL
591680 rc = SetCurlStrOption (CURLOPT_URL, url_.c_str ());
0 commit comments