open-telemetry / opentelemetry-cpp

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

Status Error 204, when sending logs. #2624

Closed Odinaka-git closed 3 months ago

Odinaka-git commented 3 months ago

This really isn't a bug report. I just wanted some help figuring out what might be missing from my code. I tried starting up discussions but I couldn't really find anyone who could give me an answer.

Describe your environment I running my application on a windows 11, and I'm using visual studio. The version of opentelemetry that I'm using is v1.12.0.

Steps to reproduce I have placed my Initlogger function here. This function is responsible for setting up my logger provider which will ship logs to grafana cloud.

   void InitLogger()
   {
       //Create struct to store options to initialize a otlp log record exporter with.
       opentelemetry::exporter::otlp::OtlpHttpLogRecordExporterOptions logger_opts;

       //provide the "OTEL_EXPORTER_OTLP_ENDPOINT" here. This is the endpoint your logs will be sent to.
       logger_opts.url = std::string(std::getenv("OTEL_EXPORTER_OTLP_ENDPOINT")) + "/v1/logs";

       //Set the content type you want. You can choose between KJson and KBinary.
       logger_opts.content_type = otlp::HttpRequestContentType::kBinary;

       //You can uncomment opts.console_debug if you need to do any debbuging. Doing this will print more details about the traces you are sending. make sure to uncomment line 24 in main.cpp
       logger_opts.console_debug = true;

       //provide the "OTEL_EXPORTER_OTLP_HEADERS" here. This is very important if you want to avoid  authorization errors.
       logger_opts.http_headers.insert(std::make_pair<const std::string, std::string>("Authorization", std::getenv("OTEL_EXPORTER_OTLP_HEADERS")));

       std::cout << "Using " << logger_opts.url << " to export log records." << std::endl;

       // Create OTLP HTTP LogRecord exporter instance. These Exporters are responsible for sending logs to a consumer.
       auto exporter = otlp::OtlpHttpLogRecordExporterFactory::Create(logger_opts);

       std::unique_ptr<logs_sdk::LogRecordProcessor> processor = 
           logs_sdk::SimpleLogRecordProcessorFactory::Create(std::move(exporter));
       std::vector<std::unique_ptr<logs_sdk::LogRecordProcessor>> processors; 
       processors.push_back(std::move(processor));

       // Default is an always-on sampler.
       // Context is an object that contains the information for the sending and receiving service to correlate one signal to another. We intialize it using the 
       // processors and merged_resources we created earlier.
       std::unique_ptr<logs_sdk::LoggerContext> context =
           logs_sdk::LoggerContextFactory::Create(std::move(processors),merged_resource);

       //// Basically a factory for logs. It's lifecycle matches the applications life cycle.
       std::shared_ptr<logs::LoggerProvider> provider =
           logs_sdk::LoggerProviderFactory::Create(std::move(context));

       // Set your LoggerProvider as the global Logger provider
      opentelemetry::logs::Provider::SetLoggerProvider(provider);
   }

Then this is the code that sends my logs in main

InitLogger();
internal_log::GlobalLogHandler::SetLogLevel(internal_log::LogLevel::Debug);
Logs = get_logger("App Logs");
Logs->Info("App on device with a MAC address of " + deviceMAC + " and IP address of " + deviceIP + " is running.);

What is the expected behavior? I expect to see a 200 status code not a 204 status code

What is the actual behavior? I get a 204 status code instead.

Additional context This is what the error looks like

[Debug] File: C:\.conan\e118c5\1\src\exporters\otlp\src\otlp_http_client.cc:175 [OTLP HTTP Client] Session state: session created
[Debug] File: C:\.conan\e118c5\1\src\exporters\otlp\src\otlp_http_client.cc:189 [OTLP HTTP Client] Session state: connecting to peer
[Debug] File: C:\.conan\e118c5\1\src\exporters\otlp\src\otlp_http_client.cc:750 [OTLP HTTP Client] Waiting for response from <grafana-otlp-endpoint>/otlp/v1/logs (timeout = 10000 milliseconds)
[Debug] File: C:\.conan\e118c5\1\src\exporters\otlp\src\otlp_http_client.cc:207 [OTLP HTTP Client] Session state: connected
[Debug] File: C:\.conan\e118c5\1\src\exporters\otlp\src\otlp_http_client.cc:214 [OTLP HTTP Client] Session state: sending request
[Debug] File: C:\.conan\e118c5\1\src\exporters\otlp\src\otlp_http_client.cc:232 [OTLP HTTP Client] Session state: response received
[Error] File: C:\.conan\e118c5\1\src\exporters\otlp\src\otlp_http_client.cc:103 [OTLP HTTP Client] Export failed, Status:204, Header:   Date: Tue, 02 Apr,Body:10:42:35 GMT
[Error] File: C:\.conan\e118c5\1\src\exporters\otlp\src\otlp_http_log_record_exporter.cc:124 [OTLP HTTP Client] ERROR: Export 1 log(s) error: 1
marcalff commented 3 months ago

A couple of things here.

1)

In opentelemetry-cpp 1.12.0, the following fix is not present:

This may affect which encoding is supported for the Authorization key/value pair.

2)

This code:

       //provide the "OTEL_EXPORTER_OTLP_ENDPOINT" here. This is the endpoint your logs will be sent to.
       logger_opts.url = std::string(std::getenv("OTEL_EXPORTER_OTLP_ENDPOINT")) + "/v1/logs";

is unnecessary, because opentelemetry-cpp does this by default, and even honor OTEL_EXPORTER_OTLP_LOGS_ENDPOINT if present.

Beside, the code assumes OTEL_EXPORTER_OTLP_ENDPOINT is provided, else it breaks with no fallback to a localhost default.

This is a side comments, that does not explain the failure.

3)

This code:

       //provide the "OTEL_EXPORTER_OTLP_HEADERS" here. This is very important if you want to avoid  authorization errors.
       logger_opts.http_headers.insert(std::make_pair<const std::string, std::string>("Authorization", std::getenv("OTEL_EXPORTER_OTLP_HEADERS")));

is ill-formed.

Assuming the backend is graphana, documented at:

There are two ways to provide key/value pairs.

a)

First way is to provide a fully encoded OTEL_EXPORTER_OTLP_HEADERS environment variable, as described in the graphana doc.

The variable should look like:

export  OTEL_EXPORTER_OTLP_HEADERS="Authorization=Basic XXXXXX"

or better (once using a release with the fix for #2579, not available yet)

export  OTEL_EXPORTER_OTLP_HEADERS="Authorization=Basic%20XXXXXX"

and then the logger_opts.http_headers member should be left unchanged, as opentelemetry-cpp will populate it from the env variable.

b)

Populate logger_opts.http_headers programatically.

In this case, the key, which depends on the vendor, is Authorization.

The key value is only Basic XXXXXX or Basic%20XXXXXX, not the full Authorization=Basic XXXXXX string.

The full content of OTEL_EXPORTER_OTLP_HEADERS was not disclosed in the issue report (and don't, it is sensitive data).

Assuming the value followed the vendor documentation, something like:

       //provide the "OTEL_EXPORTER_OTLP_HEADERS" here. This is very important if you want to avoid  authorization errors.
       logger_opts.http_headers.insert(std::make_pair<const std::string, std::string>("Authorization", std::getenv("OTEL_EXPORTER_OTLP_HEADERS")));

combined with:

export  OTEL_EXPORTER_OTLP_HEADERS="Authorization=Basic XXXXXX"

Will lead to key = Authorization and value = Authorization=Basic XXXXXX, which will fail in the HTTP OTLP backend.

Odinaka-git commented 3 months ago

oh, and thank you