// Copyright The OpenTelemetry Authors // SPDX-License-Identifier: Apache-2.0 #include "opentelemetry/sdk/common/env_variables.h" #ifdef _MSC_VER # include # define strcasecmp _stricmp #else # include #endif #include #include #include #include #include #include "opentelemetry/nostd/string_view.h" #include "opentelemetry/sdk/common/global_log_handler.h" #include "opentelemetry/version.h" OPENTELEMETRY_BEGIN_NAMESPACE namespace sdk { namespace common { static bool GetRawEnvironmentVariable(const char *env_var_name, std::string &value) { #if !defined(NO_GETENV) const char *endpoint_from_env = nullptr; # if defined(_MSC_VER) // avoid calling std::getenv which is deprecated in MSVC. size_t required_size = 0; getenv_s(&required_size, nullptr, 0, env_var_name); std::unique_ptr endpoint_buffer; if (required_size > 0) { endpoint_buffer = std::unique_ptr{new char[required_size]}; getenv_s(&required_size, endpoint_buffer.get(), required_size, env_var_name); endpoint_from_env = endpoint_buffer.get(); } # else endpoint_from_env = std::getenv(env_var_name); # endif // defined(_MSC_VER) if (endpoint_from_env != nullptr) { value = std::string{endpoint_from_env}; return true; } value = std::string{}; return false; #else value = std::string{}; return false; #endif // !defined(NO_GETENV) } bool GetBoolEnvironmentVariable(const char *env_var_name, bool &value) { std::string raw_value; bool exists = GetRawEnvironmentVariable(env_var_name, raw_value); if (!exists || raw_value.empty()) { value = false; return false; } if (strcasecmp(raw_value.c_str(), "true") == 0) { value = true; return true; } if (strcasecmp(raw_value.c_str(), "false") == 0) { value = false; return true; } OTEL_INTERNAL_LOG_WARN("Environment variable <" << env_var_name << "> has an invalid value <" << raw_value << ">, defaulting to false"); value = false; return true; } static bool GetTimeoutFromString(const char *input, std::chrono::system_clock::duration &value) { std::chrono::system_clock::duration::rep result = 0; // Skip spaces for (; *input && std::isspace(*input); ++input) ; for (; *input && std::isdigit(*input); ++input) { auto digit = (*input - '0'); if (result > (std::numeric_limits::max() - digit) / 10) { // Rejecting overflow as invalid. return false; } result = result * 10 + digit; } if (result == 0) { // Rejecting duration 0 as invalid. return false; } opentelemetry::nostd::string_view unit{input}; if (unit == "ns") { value = std::chrono::duration_cast( std::chrono::nanoseconds{result}); return true; } if (unit == "us") { value = std::chrono::duration_cast( std::chrono::microseconds{result}); return true; } if (unit == "ms") { value = std::chrono::duration_cast( std::chrono::milliseconds{result}); return true; } if (unit == "s") { value = std::chrono::duration_cast( std::chrono::seconds{result}); return true; } if (unit == "m") { value = std::chrono::duration_cast( std::chrono::minutes{result}); return true; } if (unit == "h") { value = std::chrono::duration_cast(std::chrono::hours{result}); return true; } if (unit == "") { // TODO: The spec says milliseconds, but opentelemetry-cpp implemented // seconds by default. Fixing this is a breaking change. value = std::chrono::duration_cast( std::chrono::seconds{result}); return true; } // Failed to parse the input string. return false; } bool GetDurationEnvironmentVariable(const char *env_var_name, std::chrono::system_clock::duration &value) { std::string raw_value; bool exists = GetRawEnvironmentVariable(env_var_name, raw_value); if (!exists || raw_value.empty()) { value = std::chrono::duration_cast(std::chrono::seconds{0}); return false; } exists = GetTimeoutFromString(raw_value.c_str(), value); if (!exists) { OTEL_INTERNAL_LOG_WARN("Environment variable <" << env_var_name << "> has an invalid value <" << raw_value << ">, ignoring"); } return exists; } bool GetStringEnvironmentVariable(const char *env_var_name, std::string &value) { bool exists = GetRawEnvironmentVariable(env_var_name, value); if (!exists || value.empty()) { return false; } return true; } bool GetUintEnvironmentVariable(const char *env_var_name, std::uint32_t &value) { static constexpr auto kDefaultValue = 0U; std::string raw_value; bool exists = GetRawEnvironmentVariable(env_var_name, raw_value); if (!exists || raw_value.empty()) { value = kDefaultValue; return false; } const char *end = raw_value.c_str() + raw_value.length(); char *actual_end = nullptr; const auto temp = std::strtoull(raw_value.c_str(), &actual_end, 10); if (errno == ERANGE) { errno = 0; OTEL_INTERNAL_LOG_WARN("Environment variable <" << env_var_name << "> is out of range <" << raw_value << ">, defaulting to " << kDefaultValue); } else if (actual_end != end || std::numeric_limits::max() < temp) { OTEL_INTERNAL_LOG_WARN("Environment variable <" << env_var_name << "> has an invalid value <" << raw_value << ">, defaulting to " << kDefaultValue); } else { value = static_cast(temp); return true; } value = kDefaultValue; return false; } bool GetFloatEnvironmentVariable(const char *env_var_name, float &value) { static constexpr auto kDefaultValue = 0.0f; std::string raw_value; bool exists = GetRawEnvironmentVariable(env_var_name, raw_value); if (!exists || raw_value.empty()) { value = kDefaultValue; return false; } const char *end = raw_value.c_str() + raw_value.length(); char *actual_end = nullptr; value = std::strtof(raw_value.c_str(), &actual_end); if (errno == ERANGE) { errno = 0; OTEL_INTERNAL_LOG_WARN("Environment variable <" << env_var_name << "> is out of range <" << raw_value << ">, defaulting to " << kDefaultValue); } else if (actual_end != end) { OTEL_INTERNAL_LOG_WARN("Environment variable <" << env_var_name << "> has an invalid value <" << raw_value << ">, defaulting to " << kDefaultValue); } else { return true; } value = kDefaultValue; return false; } } // namespace common } // namespace sdk OPENTELEMETRY_END_NAMESPACE