diff --git a/changelogs/current.yaml b/changelogs/current.yaml index 783cccd4139ac..799b11acae4c9 100644 --- a/changelogs/current.yaml +++ b/changelogs/current.yaml @@ -334,6 +334,11 @@ removed_config_or_runtime: and legacy code path. new_features: +- area: logging + change: | + Added ``%E`` as a custom spdlog pattern flag that emits the Envoy version string. It can be + used in the ``--log-format`` CLI flag or the bootstrap ``application_log_config.log_format`` + to include the running version in every log line, e.g. ``--log-format "[%E][%l] %v"``. - area: ratelimit change: | Added ``is_negative_hits`` boolean to the ``hits_addend`` diff --git a/docs/root/operations/cli.rst b/docs/root/operations/cli.rst index 7ecbf9fc48bbc..6412d10c7dca6 100644 --- a/docs/root/operations/cli.rst +++ b/docs/root/operations/cli.rst @@ -138,6 +138,7 @@ following are the command line options that Envoy supports. :%v: The actual message to log ("some user text") :%_: The actual message to log, but with escaped newlines (from (if using ``%v``) "some user text\nbelow", to "some user text\\nbelow") :%j: The actual message to log as JSON escaped string (https://tools.ietf.org/html/rfc7159#page-8). + :%E: The Envoy version string (e.g. "1.32.0/Clean/RELEASE/BoringSSL"). :%t: Thread id ("1232") :%P: Process id ("3456") :%n: Logger's name ("filter") diff --git a/source/common/common/BUILD b/source/common/common/BUILD index ebc894f407bab..502da80cecb99 100644 --- a/source/common/common/BUILD +++ b/source/common/common/BUILD @@ -222,6 +222,7 @@ envoy_cc_library( ":macros", ":non_copyable", "//source/common/protobuf", + "//source/common/version:version_string_lib", "@abseil-cpp//absl/container:flat_hash_map", "@abseil-cpp//absl/synchronization", ] + select({ diff --git a/source/common/common/logger.cc b/source/common/common/logger.cc index c2979d1072125..ac70c16582db5 100644 --- a/source/common/common/logger.cc +++ b/source/common/common/logger.cc @@ -9,6 +9,7 @@ #include "source/common/common/json_escape_string.h" #include "source/common/common/lock_guard.h" +#include "source/common/version/version_string.h" #include "absl/strings/ascii.h" #include "absl/strings/escaping.h" @@ -321,6 +322,10 @@ void setLogFormatForLogger(spdlog::logger& logger, const std::string& log_format CustomFlagFormatter::ExtractedMessage::Placeholder) .set_pattern(log_format); + formatter + ->add_flag(CustomFlagFormatter::EnvoyVersion::Placeholder) + .set_pattern(log_format); + logger.set_formatter(std::move(formatter)); } @@ -420,6 +425,12 @@ void ExtractedMessage::format(const spdlog::details::log_msg& msg, const std::tm Envoy::Logger::Utility::escapeMessageJsonString(original_message, dest); } +void EnvoyVersion::format(const spdlog::details::log_msg&, const std::tm&, + spdlog::memory_buf_t& dest) { + const std::string& version = envoyVersionString(); + dest.append(version.data(), version.data() + version.size()); +} + } // namespace CustomFlagFormatter } // namespace Logger } // namespace Envoy diff --git a/source/common/common/logger.h b/source/common/common/logger.h index cd298c4659ecc..9b41c636b1355 100644 --- a/source/common/common/logger.h +++ b/source/common/common/logger.h @@ -480,6 +480,24 @@ class ExtractedMessage : public spdlog::custom_flag_formatter { constexpr static char Placeholder = '+'; }; +/** + * When added to a formatter, this adds 'E' as a user defined flag in the log pattern that emits + * the Envoy version string set via setVersion(). Use %E in --log-format to include the running + * version in every log line. Call setVersion() once at server start-up (before any logs are + * written) with the result of VersionInfo::version(). + */ +class EnvoyVersion : public spdlog::custom_flag_formatter { +public: + void format(const spdlog::details::log_msg& msg, const std::tm& tm, + spdlog::memory_buf_t& dest) override; + + std::unique_ptr clone() const override { + return spdlog::details::make_unique(); + } + + constexpr static char Placeholder = 'E'; +}; + } // namespace CustomFlagFormatter } // namespace Logger diff --git a/source/common/version/BUILD b/source/common/version/BUILD index 7f588bc079c59..08aa359ca9f00 100644 --- a/source/common/version/BUILD +++ b/source/common/version/BUILD @@ -47,6 +47,26 @@ genrule( visibility = ["//visibility:private"], ) +envoy_cc_library( + name = "version_string_lib", + srcs = ["version_string.cc"], + hdrs = [ + "version_string.h", + ":generate_version_number", + ], + copts = select({ + "//bazel:using_boringssl_fips": ["-DENVOY_SSL_VERSION=\\\"BoringSSL-FIPS\\\""], + "//bazel:using_aws_lc": ["-DENVOY_SSL_VERSION=\\\"AWS-LC-FIPS\\\""], + "//bazel:using_openssl": ["-DENVOY_SSL_VERSION=\\\"OpenSSL\\\""], + "//conditions:default": ["-DENVOY_SSL_VERSION=\\\"BoringSSL\\\""], + }), + deps = [ + ":version_suffix_default", + "//source/common/common:fmt_lib", + "//source/common/common:macros", + ], +) + envoy_cc_library( name = "version_includes", hdrs = [ @@ -62,17 +82,11 @@ envoy_cc_library( envoy_cc_library( name = "version_lib", srcs = ["version.cc"], - copts = select({ - "//bazel:using_boringssl_fips": ["-DENVOY_SSL_VERSION=\\\"BoringSSL-FIPS\\\""], - "//bazel:using_aws_lc": ["-DENVOY_SSL_VERSION=\\\"AWS-LC-FIPS\\\""], - "//bazel:using_openssl": ["-DENVOY_SSL_VERSION=\\\"OpenSSL\\\""], - "//conditions:default": ["-DENVOY_SSL_VERSION=\\\"BoringSSL\\\""], - }), external_deps = ["ssl"], tags = ["notidy"], deps = [ ":version_includes", - ":version_suffix_default", + ":version_string_lib", "//source/common/common:macros", "//source/common/protobuf:utility_lib", ], diff --git a/source/common/version/version.cc b/source/common/version/version.cc index 2e429f3c12318..6a080506403c6 100644 --- a/source/common/version/version.cc +++ b/source/common/version/version.cc @@ -7,6 +7,7 @@ #include "source/common/common/fmt.h" #include "source/common/common/macros.h" #include "source/common/protobuf/utility.h" +#include "source/common/version/version_string.h" #include "absl/strings/numbers.h" #include "absl/strings/str_split.h" @@ -26,11 +27,7 @@ const std::string& VersionInfo::revisionStatus() { CONSTRUCT_ON_FIRST_USE(std::string, build_scm_status); } -const std::string& VersionInfo::version() { - CONSTRUCT_ON_FIRST_USE(std::string, fmt::format("{}/{}{}/{}/{}/{}", revision(), - BUILD_VERSION_NUMBER, build_version_suffix, - revisionStatus(), buildType(), sslVersion())); -} +const std::string& VersionInfo::version() { return envoyVersionString(); } const envoy::config::core::v3::BuildVersion& VersionInfo::buildVersion() { static const auto* result = new envoy::config::core::v3::BuildVersion( @@ -40,23 +37,9 @@ const envoy::config::core::v3::BuildVersion& VersionInfo::buildVersion() { bool VersionInfo::sslFipsCompliant() { return FIPS_mode() == 1; } -const std::string& VersionInfo::buildType() { -#ifdef NDEBUG - static const std::string release_type = "RELEASE"; -#else - static const std::string release_type = "DEBUG"; -#endif - return release_type; -} +const std::string& VersionInfo::buildType() { return envoyBuildType(); } -const std::string& VersionInfo::sslVersion() { -#ifdef ENVOY_SSL_VERSION - static const std::string ssl_version = ENVOY_SSL_VERSION; -#else - static const std::string ssl_version = "no-ssl"; -#endif - return ssl_version; -} +const std::string& VersionInfo::sslVersion() { return envoySSLVersion(); } envoy::config::core::v3::BuildVersion VersionInfo::makeBuildVersion(const char* version) { envoy::config::core::v3::BuildVersion result; diff --git a/source/common/version/version_string.cc b/source/common/version/version_string.cc new file mode 100644 index 0000000000000..24c1764187020 --- /dev/null +++ b/source/common/version/version_string.cc @@ -0,0 +1,39 @@ +#include "source/common/version/version_string.h" + +#include "source/common/common/fmt.h" +#include "source/common/common/macros.h" +#include "source/common/version/version_number.h" + +// Provided by the linkstamp (version_linkstamp.cc / test_version_linkstamp.cc). +extern const char build_scm_revision[]; +extern const char build_scm_status[]; +extern const char build_version_suffix[]; + +namespace Envoy { + +const std::string& envoyBuildType() { +#ifdef NDEBUG + static const std::string type = "RELEASE"; +#else + static const std::string type = "DEBUG"; +#endif + return type; +} + +const std::string& envoySSLVersion() { +#ifdef ENVOY_SSL_VERSION + static const std::string version = ENVOY_SSL_VERSION; +#else + static const std::string version = "no-ssl"; +#endif + return version; +} + +const std::string& envoyVersionString() { + CONSTRUCT_ON_FIRST_USE(std::string, + fmt::format("{}/{}{}/{}/{}/{}", build_scm_revision, BUILD_VERSION_NUMBER, + build_version_suffix, build_scm_status, envoyBuildType(), + envoySSLVersion())); +} + +} // namespace Envoy diff --git a/source/common/version/version_string.h b/source/common/version/version_string.h new file mode 100644 index 0000000000000..716af98cbdf28 --- /dev/null +++ b/source/common/version/version_string.h @@ -0,0 +1,19 @@ +#pragma once + +#include + +namespace Envoy { + +// Returns the Envoy version string (e.g. "1.32.0/abc123.Modified/RELEASE/BoringSSL"). +// This is intentionally a slim alternative to VersionInfo::version() for use by low-level +// libraries (such as the logger) that cannot depend on version_lib without creating a +// circular BUILD dependency through protobuf. +const std::string& envoyVersionString(); + +// Returns the build type string: "RELEASE" or "DEBUG". +const std::string& envoyBuildType(); + +// Returns the SSL library version string (e.g. "BoringSSL", "OpenSSL"). +const std::string& envoySSLVersion(); + +} // namespace Envoy diff --git a/test/common/common/BUILD b/test/common/common/BUILD index a6ceeec1952d1..890e94b4b2166 100644 --- a/test/common/common/BUILD +++ b/test/common/common/BUILD @@ -245,6 +245,7 @@ envoy_cc_test( rbe_pool = "6gig", deps = [ "//source/common/common:minimal_logger_lib", + "//source/common/version:version_string_lib", "//test/mocks/http:http_mocks", "//test/mocks/network:network_mocks", ], diff --git a/test/common/common/logger_test.cc b/test/common/common/logger_test.cc index c949b8bd43a70..3f6738c49a695 100644 --- a/test/common/common/logger_test.cc +++ b/test/common/common/logger_test.cc @@ -4,6 +4,7 @@ #include "source/common/common/json_escape_string.h" #include "source/common/common/logger.h" +#include "source/common/version/version_string.h" #include "test/mocks/common.h" #include "test/mocks/http/mocks.h" @@ -173,6 +174,10 @@ class LoggerCustomFlagsTest : public testing::TestWithParam { ->add_flag( CustomFlagFormatter::ExtractedMessage::Placeholder) .set_pattern(pattern); + formatter + ->add_flag( + CustomFlagFormatter::EnvoyVersion::Placeholder) + .set_pattern(pattern); logger_->set_formatter(std::move(formatter)); logger_->set_level(spdlog::level::info); @@ -238,6 +243,12 @@ TEST_P(LoggerCustomFlagsTest, LogMessageWithTagsAndExtractTags) { expectLogMessage("%*", "[Tags: \"key\":\"val\"] mes\"] sge4", ",\"key\":\"val\""); } +TEST_P(LoggerCustomFlagsTest, LogMessageWithEnvoyVersion) { + // %E emits the Envoy version string from envoyVersionString(). + expectLogMessage("%E %v", "hello", absl::StrCat(envoyVersionString(), " hello")); + expectLogMessage("%v", "hello", "hello"); +} + class NamedLogTest : public Loggable, public testing::Test {}; TEST_F(NamedLogTest, NamedLogsAreSentToSink) {