diff --git a/exporters/otlp/src/otlp_environment.cc b/exporters/otlp/src/otlp_environment.cc index 3fcc34c6ff..f108555508 100644 --- a/exporters/otlp/src/otlp_environment.cc +++ b/exporters/otlp/src/otlp_environment.cc @@ -80,32 +80,36 @@ static bool GetStringDualEnvVar(const char *signal_name, return exists; } -static std::uint32_t GetUintEnvVarOrDefault(opentelemetry::nostd::string_view signal_env, - opentelemetry::nostd::string_view generic_env, - std::uint32_t default_value) +static bool GetUintDualEnvVar(const char *signal_name, + const char *generic_name, + std::uint32_t &value) { - std::string value; + bool exists; - if (GetStringDualEnvVar(signal_env.data(), generic_env.data(), value)) + exists = sdk_common::GetUintEnvironmentVariable(signal_name, value); + if (exists) { - return static_cast(std::strtoul(value.c_str(), nullptr, 10)); + return true; } - return default_value; + exists = sdk_common::GetUintEnvironmentVariable(generic_name, value); + + return exists; } -static float GetFloatEnvVarOrDefault(opentelemetry::nostd::string_view signal_env, - opentelemetry::nostd::string_view generic_env, - float default_value) +static float GetFloatDualEnvVar(const char *signal_name, const char *generic_name, float &value) { - std::string value; + bool exists; - if (GetStringDualEnvVar(signal_env.data(), generic_env.data(), value)) + exists = sdk_common::GetFloatEnvironmentVariable(signal_name, value); + if (exists) { - return std::strtof(value.c_str(), nullptr); + return true; } - return default_value; + exists = sdk_common::GetFloatEnvironmentVariable(generic_name, value); + + return exists; } std::string GetOtlpDefaultGrpcTracesEndpoint() @@ -1157,84 +1161,168 @@ std::uint32_t GetOtlpDefaultTracesRetryMaxAttempts() { constexpr char kSignalEnv[] = "OTEL_EXPORTER_OTLP_TRACES_RETRY_MAX_ATTEMPTS"; constexpr char kGenericEnv[] = "OTEL_EXPORTER_OTLP_RETRY_MAX_ATTEMPTS"; - return GetUintEnvVarOrDefault(kSignalEnv, kGenericEnv, 5U); + std::uint32_t value; + + if (!GetUintDualEnvVar(kSignalEnv, kGenericEnv, value)) + { + return 5U; + } + + return value; } std::uint32_t GetOtlpDefaultMetricsRetryMaxAttempts() { constexpr char kSignalEnv[] = "OTEL_EXPORTER_OTLP_METRICS_RETRY_MAX_ATTEMPTS"; constexpr char kGenericEnv[] = "OTEL_EXPORTER_OTLP_RETRY_MAX_ATTEMPTS"; - return GetUintEnvVarOrDefault(kSignalEnv, kGenericEnv, 5U); + std::uint32_t value; + + if (!GetUintDualEnvVar(kSignalEnv, kGenericEnv, value)) + { + return 5U; + } + + return value; } std::uint32_t GetOtlpDefaultLogsRetryMaxAttempts() { constexpr char kSignalEnv[] = "OTEL_EXPORTER_OTLP_LOGS_RETRY_MAX_ATTEMPTS"; constexpr char kGenericEnv[] = "OTEL_EXPORTER_OTLP_RETRY_MAX_ATTEMPTS"; - return GetUintEnvVarOrDefault(kSignalEnv, kGenericEnv, 5U); + std::uint32_t value; + + if (!GetUintDualEnvVar(kSignalEnv, kGenericEnv, value)) + { + return 5U; + } + + return value; } float GetOtlpDefaultTracesRetryInitialBackoff() { constexpr char kSignalEnv[] = "OTEL_EXPORTER_OTLP_TRACES_RETRY_INITIAL_BACKOFF"; constexpr char kGenericEnv[] = "OTEL_EXPORTER_OTLP_RETRY_INITIAL_BACKOFF"; - return GetFloatEnvVarOrDefault(kSignalEnv, kGenericEnv, 1.0); + float value; + + if (!GetFloatDualEnvVar(kSignalEnv, kGenericEnv, value)) + { + return 1.0f; + } + + return value; } float GetOtlpDefaultMetricsRetryInitialBackoff() { constexpr char kSignalEnv[] = "OTEL_EXPORTER_OTLP_METRICS_RETRY_INITIAL_BACKOFF"; constexpr char kGenericEnv[] = "OTEL_EXPORTER_OTLP_RETRY_INITIAL_BACKOFF"; - return GetFloatEnvVarOrDefault(kSignalEnv, kGenericEnv, 1.0); + float value; + + if (!GetFloatDualEnvVar(kSignalEnv, kGenericEnv, value)) + { + return 1.0f; + } + + return value; } float GetOtlpDefaultLogsRetryInitialBackoff() { constexpr char kSignalEnv[] = "OTEL_EXPORTER_OTLP_LOGS_RETRY_INITIAL_BACKOFF"; constexpr char kGenericEnv[] = "OTEL_EXPORTER_OTLP_RETRY_INITIAL_BACKOFF"; - return GetFloatEnvVarOrDefault(kSignalEnv, kGenericEnv, 1.0); + float value; + + if (!GetFloatDualEnvVar(kSignalEnv, kGenericEnv, value)) + { + return 1.0f; + } + + return value; } float GetOtlpDefaultTracesRetryMaxBackoff() { constexpr char kSignalEnv[] = "OTEL_EXPORTER_OTLP_TRACES_RETRY_MAX_BACKOFF"; constexpr char kGenericEnv[] = "OTEL_EXPORTER_OTLP_RETRY_MAX_BACKOFF"; - return GetFloatEnvVarOrDefault(kSignalEnv, kGenericEnv, 5.0); + float value; + + if (!GetFloatDualEnvVar(kSignalEnv, kGenericEnv, value)) + { + return 5.0f; + } + + return value; } float GetOtlpDefaultMetricsRetryMaxBackoff() { constexpr char kSignalEnv[] = "OTEL_EXPORTER_OTLP_METRICS_RETRY_MAX_BACKOFF"; constexpr char kGenericEnv[] = "OTEL_EXPORTER_OTLP_RETRY_MAX_BACKOFF"; - return GetFloatEnvVarOrDefault(kSignalEnv, kGenericEnv, 5.0); + float value; + + if (!GetFloatDualEnvVar(kSignalEnv, kGenericEnv, value)) + { + return 5.0f; + } + + return value; } float GetOtlpDefaultLogsRetryMaxBackoff() { constexpr char kSignalEnv[] = "OTEL_EXPORTER_OTLP_LOGS_RETRY_MAX_BACKOFF"; constexpr char kGenericEnv[] = "OTEL_EXPORTER_OTLP_RETRY_MAX_BACKOFF"; - return GetFloatEnvVarOrDefault(kSignalEnv, kGenericEnv, 5.0); + float value; + + if (!GetFloatDualEnvVar(kSignalEnv, kGenericEnv, value)) + { + return 5.0f; + } + + return value; } float GetOtlpDefaultTracesRetryBackoffMultiplier() { constexpr char kSignalEnv[] = "OTEL_EXPORTER_OTLP_TRACES_RETRY_BACKOFF_MULTIPLIER"; constexpr char kGenericEnv[] = "OTEL_EXPORTER_OTLP_RETRY_BACKOFF_MULTIPLIER"; - return GetFloatEnvVarOrDefault(kSignalEnv, kGenericEnv, 1.5f); + float value; + + if (!GetFloatDualEnvVar(kSignalEnv, kGenericEnv, value)) + { + return 1.5f; + } + + return value; } float GetOtlpDefaultMetricsRetryBackoffMultiplier() { constexpr char kSignalEnv[] = "OTEL_EXPORTER_OTLP_METRICS_RETRY_BACKOFF_MULTIPLIER"; constexpr char kGenericEnv[] = "OTEL_EXPORTER_OTLP_RETRY_BACKOFF_MULTIPLIER"; - return GetFloatEnvVarOrDefault(kSignalEnv, kGenericEnv, 1.5f); + float value; + + if (!GetFloatDualEnvVar(kSignalEnv, kGenericEnv, value)) + { + return 1.5f; + } + + return value; } float GetOtlpDefaultLogsRetryBackoffMultiplier() { constexpr char kSignalEnv[] = "OTEL_EXPORTER_OTLP_LOGS_RETRY_BACKOFF_MULTIPLIER"; constexpr char kGenericEnv[] = "OTEL_EXPORTER_OTLP_RETRY_BACKOFF_MULTIPLIER"; - return GetFloatEnvVarOrDefault(kSignalEnv, kGenericEnv, 1.5f); + float value; + + if (!GetFloatDualEnvVar(kSignalEnv, kGenericEnv, value)) + { + return 1.5f; + } + + return value; } } // namespace otlp diff --git a/exporters/otlp/test/otlp_grpc_exporter_test.cc b/exporters/otlp/test/otlp_grpc_exporter_test.cc index 144dae20ff..be1ddbb3cf 100644 --- a/exporters/otlp/test/otlp_grpc_exporter_test.cc +++ b/exporters/otlp/test/otlp_grpc_exporter_test.cc @@ -19,7 +19,6 @@ # include "opentelemetry/exporters/otlp/otlp_grpc_exporter.h" # include "opentelemetry/exporters/otlp/otlp_grpc_exporter_factory.h" # include "opentelemetry/exporters/otlp/protobuf_include_prefix.h" -# include "opentelemetry/nostd/shared_ptr.h" // Problematic code that pulls in Gmock and breaks with vs2019/c++latest : # include "opentelemetry/proto/collector/trace/v1/trace_service_mock.grpc.pb.h" @@ -28,6 +27,7 @@ # include "opentelemetry/exporters/otlp/protobuf_include_suffix.h" +# include "opentelemetry/nostd/shared_ptr.h" # include "opentelemetry/sdk/trace/simple_processor.h" # include "opentelemetry/sdk/trace/simple_processor_factory.h" # include "opentelemetry/sdk/trace/tracer_provider.h" diff --git a/sdk/include/opentelemetry/sdk/common/env_variables.h b/sdk/include/opentelemetry/sdk/common/env_variables.h index a02a66c29e..7bf28e2c1c 100644 --- a/sdk/include/opentelemetry/sdk/common/env_variables.h +++ b/sdk/include/opentelemetry/sdk/common/env_variables.h @@ -4,6 +4,7 @@ #pragma once #include +#include #include #include "opentelemetry/version.h" @@ -39,6 +40,22 @@ bool GetDurationEnvironmentVariable(const char *env_var_name, */ bool GetStringEnvironmentVariable(const char *env_var_name, std::string &value); +/** + Read a uint32_t environment variable. + @param env_var_name Environment variable name + @param [out] value Variable value, if it exists + @return true if the variable exists +*/ +bool GetUintEnvironmentVariable(const char *env_var_name, std::uint32_t &value); + +/** + Read a float environment variable. + @param env_var_name Environment variable name + @param [out] value Variable value, if it exists + @return true if the variable exists +*/ +bool GetFloatEnvironmentVariable(const char *env_var_name, float &value); + #if defined(_MSC_VER) inline int setenv(const char *name, const char *value, int) { diff --git a/sdk/src/common/env_variables.cc b/sdk/src/common/env_variables.cc index 586a8ed420..b8e37b7eaf 100644 --- a/sdk/src/common/env_variables.cc +++ b/sdk/src/common/env_variables.cc @@ -10,6 +10,7 @@ # include #endif +#include #include #include @@ -23,6 +24,117 @@ namespace sdk namespace common { +template ::value, bool> = true> +constexpr bool ParseNumber(T &&input, U &output) +{ + constexpr auto max_value = std::numeric_limits::max(); + + // Skip spaces + for (; *input && std::isspace(*input); ++input) + ; + + const auto is_negative = (*input && *input == '-'); + + if (is_negative) + { + ++input; + + // Reject negative when expecting unsigned + if (std::is_unsigned::value) + { + return false; + } + } + + const auto pos = input; + U result = 0; + U temp = 0; + + for (; *input && std::isdigit(*input); ++input) + { + temp = result * 10 + (*input - '0'); + + if (max_value - temp > max_value - result) + { + return false; // Overflow protection + } + + result = temp; + } + + result *= (is_negative) ? -1 : 1; + + // Reject if nothing parsed + if (pos != input) + { + output = result; + return true; + } + + return false; +} + +template ::value, bool> = true> +constexpr bool ParseNumber(T &&input, U &output) +{ + // Skip spaces + for (; *input && std::isspace(*input); ++input) + ; + + const auto is_negative = (*input && *input == '-'); + + if (is_negative) + { + ++input; + } + + const auto pos = input; + U result = 0; + U temp = 0; + U decimal_mul = 0.1f; + bool is_decimal = false; + + for (; *input && (std::isdigit(*input) || !is_decimal); ++input) + { + if (*input == '.' && !is_decimal) + { + is_decimal = true; + continue; + } + else if (*input == '.' && is_decimal) + { + break; + } + else if (is_decimal) + { + temp = result + decimal_mul * (*input - '0'); + decimal_mul *= 0.1; + } + else + { + temp = result * 10 + (*input - '0'); + } + + if (std::isinf(temp)) + { + return false; // Overflow protection + } + + result = temp; + } + + result *= (is_negative) ? -1.0 : 1.0; + + // Reject if nothing parsed + if (pos != input && !std::isnan(output) && !std::isinf(output)) + { + output = result; + return true; + } + + return false; +} + bool GetRawEnvironmentVariable(const char *env_var_name, std::string &value) { #if !defined(NO_GETENV) @@ -88,16 +200,7 @@ static bool GetTimeoutFromString(const char *input, std::chrono::system_clock::d { std::chrono::system_clock::duration::rep result = 0; - // Skip spaces - for (; *input && (' ' == *input || '\t' == *input || '\r' == *input || '\n' == *input); ++input) - ; - - for (; *input && (*input >= '0' && *input <= '9'); ++input) - { - result = result * 10 + (*input - '0'); - } - - if (result == 0) + if (!ParseNumber(input, result)) { // Rejecting duration 0 as invalid. return false; @@ -193,6 +296,52 @@ bool GetStringEnvironmentVariable(const char *env_var_name, std::string &value) 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; + } + + if (!ParseNumber(raw_value.c_str(), value)) + { + OTEL_INTERNAL_LOG_WARN("Environment variable <" << env_var_name << "> has an invalid value <" + << raw_value << ">, defaulting to " + << kDefaultValue); + value = kDefaultValue; + return false; + } + + return true; +} + +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; + } + + if (!ParseNumber(raw_value.c_str(), value)) + { + OTEL_INTERNAL_LOG_WARN("Environment variable <" << env_var_name << "> has an invalid value <" + << raw_value << ">, defaulting to " + << kDefaultValue); + value = kDefaultValue; + return false; + } + + return true; +} + } // namespace common } // namespace sdk OPENTELEMETRY_END_NAMESPACE diff --git a/sdk/test/common/env_var_test.cc b/sdk/test/common/env_var_test.cc index 8f64dc1f10..9066dbaae1 100644 --- a/sdk/test/common/env_var_test.cc +++ b/sdk/test/common/env_var_test.cc @@ -15,7 +15,9 @@ using opentelemetry::sdk::common::unsetenv; using opentelemetry::sdk::common::GetBoolEnvironmentVariable; using opentelemetry::sdk::common::GetDurationEnvironmentVariable; +using opentelemetry::sdk::common::GetFloatEnvironmentVariable; using opentelemetry::sdk::common::GetStringEnvironmentVariable; +using opentelemetry::sdk::common::GetUintEnvironmentVariable; #ifndef NO_GETENV TEST(EnvVarTest, BoolEnvVar) @@ -210,4 +212,125 @@ TEST(EnvVarTest, DurationEnvVar) unsetenv("STRING_ENV_VAR_BROKEN_2"); } -#endif +TEST(EnvVarTest, UintEnvVar) +{ + unsetenv("UINT_ENV_VAR_NONE"); + setenv("UINT_ENV_VAR_EMPTY", "", 1); + setenv("UINT_ENV_VAR_POSITIVE_INT", "42", 1); + setenv("UINT_ENV_VAR_NEGATIVE_INT", "-42", 1); + setenv("UINT_ENV_VAR_POSITIVE_DEC", "12.34", 1); + setenv("UINT_ENV_VAR_NEGATIVE_DEC", "-12.34", 1); + setenv("UINT_ENV_VAR_POSITIVE_INT_MAX", "4294967295", 1); + setenv("UINT_ENV_VAR_POSITIVE_OVERFLOW", "4294967296", 1); + setenv("UINT_ENV_VAR_NEGATIVE_INT_MIN", "-2147483648", 1); + setenv("UINT_ENV_VAR_NEGATIVE_OVERFLOW", "-4294967296", 1); + setenv("UINT_ENV_VAR_WITH_NOISE", " \t \n 9.12345678.9", 1); + setenv("UINT_ENV_VAR_ONLY_SPACES", " ", 1); + + std::uint32_t value; + + ASSERT_FALSE(GetUintEnvironmentVariable("UINT_ENV_VAR_NONE", value)); + + ASSERT_FALSE(GetUintEnvironmentVariable("UINT_ENV_VAR_EMPTY", value)); + + ASSERT_TRUE(GetUintEnvironmentVariable("UINT_ENV_VAR_POSITIVE_INT", value)); + ASSERT_EQ(42, value); + + ASSERT_FALSE(GetUintEnvironmentVariable("UINT_ENV_VAR_NEGATIVE_INT", value)); + + ASSERT_TRUE(GetUintEnvironmentVariable("UINT_ENV_VAR_POSITIVE_DEC", value)); + ASSERT_EQ(12, value); + + ASSERT_FALSE(GetUintEnvironmentVariable("UINT_ENV_VAR_NEGATIVE_DEC", value)); + + ASSERT_TRUE(GetUintEnvironmentVariable("UINT_ENV_VAR_POSITIVE_INT_MAX", value)); + ASSERT_EQ(4294967295, value); + + ASSERT_FALSE(GetUintEnvironmentVariable("UINT_ENV_VAR_POSITIVE_OVERFLOW", value)); + + ASSERT_FALSE(GetUintEnvironmentVariable("UINT_ENV_VAR_NEGATIVE_INT_MIN", value)); + + ASSERT_FALSE(GetUintEnvironmentVariable("UINT_ENV_VAR_NEGATIVE_OVERFLOW", value)); + + ASSERT_TRUE(GetUintEnvironmentVariable("UINT_ENV_VAR_WITH_NOISE", value)); + ASSERT_EQ(9, value); + + ASSERT_FALSE(GetUintEnvironmentVariable("UINT_ENV_VAR_ONLY_SPACES", value)); + + unsetenv("UINT_ENV_VAR_EMPTY"); + unsetenv("UINT_ENV_VAR_POSITIVE_INT"); + unsetenv("UINT_ENV_VAR_NEGATIVE_INT"); + unsetenv("UINT_ENV_VAR_POSITIVE_DEC"); + unsetenv("UINT_ENV_VAR_NEGATIVE_DEC"); + unsetenv("UINT_ENV_VAR_POSITIVE_INT_MAX"); + unsetenv("UINT_ENV_VAR_POSITIVE_OVERFLOW"); + unsetenv("UINT_ENV_VAR_NEGATIVE_INT_MIN"); + unsetenv("UINT_ENV_VAR_NEGATIVE_OVERFLOW"); + unsetenv("UINT_ENV_VAR_WITH_NOISE"); + unsetenv("UINT_ENV_VAR_ONLY_SPACES"); +} + +TEST(EnvVarTest, FloatEnvVar) +{ + unsetenv("FLOAT_ENV_VAR_NONE"); + setenv("FLOAT_ENV_VAR_EMPTY", "", 1); + setenv("FLOAT_ENV_VAR_POSITIVE_INT", "42", 1); + setenv("FLOAT_ENV_VAR_NEGATIVE_INT", "-42", 1); + setenv("FLOAT_ENV_VAR_POSITIVE_DEC", "12.34", 1); + setenv("FLOAT_ENV_VAR_NEGATIVE_DEC", "-12.34", 1); + setenv("FLOAT_ENV_VAR_POSITIVE_INT_MAX", "4294967295", 1); + setenv("FLOAT_ENV_VAR_POSITIVE_OVERFLOW", "4294967296", 1); + setenv("FLOAT_ENV_VAR_NEGATIVE_INT_MIN", "-2147483648", 1); + setenv("FLOAT_ENV_VAR_NEGATIVE_OVERFLOW", "-4294967296", 1); + setenv("FLOAT_ENV_VAR_WITH_NOISE", " \t \n 9.12345678.9", 1); + setenv("FLOAT_ENV_VAR_ONLY_SPACES", " ", 1); + + float value; + + ASSERT_FALSE(GetFloatEnvironmentVariable("FLOAT_ENV_VAR_NONE", value)); + + ASSERT_FALSE(GetFloatEnvironmentVariable("FLOAT_ENV_VAR_EMPTY", value)); + + ASSERT_TRUE(GetFloatEnvironmentVariable("FLOAT_ENV_VAR_POSITIVE_INT", value)); + ASSERT_FLOAT_EQ(42, value); + + ASSERT_TRUE(GetFloatEnvironmentVariable("FLOAT_ENV_VAR_NEGATIVE_INT", value)); + ASSERT_FLOAT_EQ(-42, value); + + ASSERT_TRUE(GetFloatEnvironmentVariable("FLOAT_ENV_VAR_POSITIVE_DEC", value)); + ASSERT_FLOAT_EQ(12.34, value); + + ASSERT_TRUE(GetFloatEnvironmentVariable("FLOAT_ENV_VAR_NEGATIVE_DEC", value)); + ASSERT_FLOAT_EQ(-12.34, value); + + ASSERT_TRUE(GetFloatEnvironmentVariable("FLOAT_ENV_VAR_POSITIVE_INT_MAX", value)); + ASSERT_FLOAT_EQ(4294967295, value); + + ASSERT_TRUE(GetFloatEnvironmentVariable("FLOAT_ENV_VAR_POSITIVE_OVERFLOW", value)); + ASSERT_FLOAT_EQ(4294967296, value); + + ASSERT_TRUE(GetFloatEnvironmentVariable("FLOAT_ENV_VAR_NEGATIVE_INT_MIN", value)); + ASSERT_FLOAT_EQ(-2147483648, value); + + ASSERT_TRUE(GetFloatEnvironmentVariable("FLOAT_ENV_VAR_NEGATIVE_OVERFLOW", value)); + ASSERT_FLOAT_EQ(-4294967296, value); + + ASSERT_TRUE(GetFloatEnvironmentVariable("FLOAT_ENV_VAR_WITH_NOISE", value)); + ASSERT_FLOAT_EQ(9.12345678, value); + + ASSERT_FALSE(GetFloatEnvironmentVariable("FLOAT_ENV_VAR_ONLY_SPACES", value)); + + unsetenv("FLOAT_ENV_VAR_EMPTY"); + unsetenv("FLOAT_ENV_VAR_POSITIVE_INT"); + unsetenv("FLOAT_ENV_VAR_NEGATIVE_INT"); + unsetenv("FLOAT_ENV_VAR_POSITIVE_DEC"); + unsetenv("FLOAT_ENV_VAR_NEGATIVE_DEC"); + unsetenv("FLOAT_ENV_VAR_POSITIVE_INT_MAX"); + unsetenv("FLOAT_ENV_VAR_POSITIVE_OVERFLOW"); + unsetenv("FLOAT_ENV_VAR_NEGATIVE_INT_MIN"); + unsetenv("FLOAT_ENV_VAR_NEGATIVE_OVERFLOW"); + unsetenv("FLOAT_ENV_VAR_WITH_NOISE"); + unsetenv("FLOAT_ENV_VAR_ONLY_SPACES"); +} + +#endif // NO_GETENV