diff --git a/src/envoy/http/mixer/control_factory.h b/src/envoy/http/mixer/control_factory.h index 2dc21057b98..0d5a840e620 100644 --- a/src/envoy/http/mixer/control_factory.h +++ b/src/envoy/http/mixer/control_factory.h @@ -19,6 +19,7 @@ #include "envoy/local_info/local_info.h" #include "src/envoy/http/mixer/control.h" #include "src/envoy/utils/stats.h" +#include "src/istio/utils/logger.h" namespace Envoy { namespace Http { @@ -51,6 +52,10 @@ class ControlFactory : public Logger::Loggable { return std::make_shared(control_data, cm, dispatcher, random, scope, local_info); }); + + // All MIXER_DEBUG(), MIXER_WARN(), etc log messages will be passed to + // ENVOY_LOG(). + istio::utils::setLogger(std::make_unique()); } Control& control() { return tls_->getTyped(); } @@ -62,6 +67,44 @@ class ControlFactory : public Logger::Loggable { return {ALL_MIXER_FILTER_STATS(POOL_COUNTER_PREFIX(scope, name))}; } + class LoggerAdaptor : public istio::utils::Logger, + Envoy::Logger::Loggable { + virtual bool isLoggable(istio::utils::Logger::Level level) override { + switch (level) { + case istio::utils::Logger::Level::DEBUG_: + return ENVOY_LOG_CHECK_LEVEL(debug); + case istio::utils::Logger::Level::TRACE_: + return ENVOY_LOG_CHECK_LEVEL(trace); + case istio::utils::Logger::Level::INFO_: + return ENVOY_LOG_CHECK_LEVEL(info); + case istio::utils::Logger::Level::WARN_: + return ENVOY_LOG_CHECK_LEVEL(warn); + case istio::utils::Logger::Level::ERROR_: + return ENVOY_LOG_CHECK_LEVEL(error); + } + } + + virtual void writeBuffer(istio::utils::Logger::Level level, + const char* buffer) override { + switch (level) { + case istio::utils::Logger::Level::DEBUG_: + ENVOY_LOGGER().debug(buffer); + break; + case istio::utils::Logger::Level::TRACE_: + ENVOY_LOGGER().trace(buffer); + break; + case istio::utils::Logger::Level::INFO_: + ENVOY_LOGGER().info(buffer); + break; + case istio::utils::Logger::Level::WARN_: + ENVOY_LOGGER().warn(buffer); + break; + case istio::utils::Logger::Level::ERROR_: + ENVOY_LOGGER().error(buffer); + } + } + }; + // The control data object ControlDataSharedPtr control_data_; // Thread local slot. diff --git a/src/istio/control/client_context_base.cc b/src/istio/control/client_context_base.cc index c2ff0ef551e..2c9690d2564 100644 --- a/src/istio/control/client_context_base.cc +++ b/src/istio/control/client_context_base.cc @@ -17,6 +17,7 @@ #include "include/istio/mixerclient/check_response.h" #include "include/istio/utils/attribute_names.h" #include "include/istio/utils/attributes_builder.h" +#include "src/istio/utils/logger.h" using ::google::protobuf::util::Status; using ::istio::mixer::v1::config::client::NetworkFailPolicy; @@ -98,17 +99,17 @@ CancelFunc ClientContextBase::SendCheck(TransportCheckFunc transport, on_done(check_response_info); }; - // TODO: add debug message - // GOOGLE_LOG(INFO) << "Check attributes: " << - // request->attributes->DebugString(); + MIXER_DEBUG("Check attributes: %s", + request->attributes->DebugString().c_str()); + return mixer_client_->Check(*request->attributes, request->quotas, transport, local_on_done); } void ClientContextBase::SendReport(const RequestContext& request) { - // TODO: add debug message - // GOOGLE_LOG(INFO) << "Report attributes: " << - // request.attributes->DebugString(); + MIXER_DEBUG("Report attributes: %s", + request.attributes->DebugString().c_str()); + mixer_client_->Report(*request.attributes); } diff --git a/src/istio/mixerclient/check_cache.cc b/src/istio/mixerclient/check_cache.cc index 2f10557ce2a..18a2cad2879 100644 --- a/src/istio/mixerclient/check_cache.cc +++ b/src/istio/mixerclient/check_cache.cc @@ -15,6 +15,7 @@ #include "src/istio/mixerclient/check_cache.h" #include "include/istio/utils/protobuf.h" +#include "src/istio/utils/logger.h" using namespace std::chrono; using ::google::protobuf::util::Status; @@ -147,9 +148,10 @@ Status CheckCache::CacheResponse(const Attributes &attributes, } utils::HashType signature; if (!referenced.Signature(attributes, "", &signature)) { - GOOGLE_LOG(ERROR) << "Response referenced mismatchs with request"; - GOOGLE_LOG(ERROR) << "Request attributes: " << attributes.DebugString(); - GOOGLE_LOG(ERROR) << "Referenced attributes: " << referenced.DebugString(); + MIXER_WARN( + "Response referenced does not match request. Request attributes: " + "%s. Referenced attributes: %s", + attributes.DebugString().c_str(), referenced.DebugString().c_str()); return ConvertRpcStatus(response.precondition().status()); } @@ -157,8 +159,8 @@ Status CheckCache::CacheResponse(const Attributes &attributes, utils::HashType hash = referenced.Hash(); if (referenced_map_.find(hash) == referenced_map_.end()) { referenced_map_[hash] = referenced; - GOOGLE_LOG(INFO) << "Add a new Referenced for check cache: " - << referenced.DebugString(); + MIXER_DEBUG("Add a new Referenced for check cache: %s", + referenced.DebugString().c_str()); } CheckLRUCache::ScopedLookup lookup(cache_.get(), signature); diff --git a/src/istio/mixerclient/quota_cache.cc b/src/istio/mixerclient/quota_cache.cc index 4d77a038fff..4cb6f904494 100644 --- a/src/istio/mixerclient/quota_cache.cc +++ b/src/istio/mixerclient/quota_cache.cc @@ -15,6 +15,7 @@ #include "src/istio/mixerclient/quota_cache.h" #include "include/istio/utils/protobuf.h" +#include "src/istio/utils/logger.h" using namespace std::chrono; using ::google::protobuf::util::Status; @@ -96,6 +97,7 @@ bool QuotaCache::CheckResult::BuildRequest(CheckRequest* request) { } } if (!rejected_quota_names.empty()) { + MIXER_DEBUG("Quota is exhausted for: %s", rejected_quota_names.c_str()); status_ = Status(Code::RESOURCE_EXHAUSTED, std::string("Quota is exhausted for: ") + rejected_quota_names); @@ -118,8 +120,8 @@ void QuotaCache::CheckResult::SetResponse(const Status& status, if (it != quotas.end()) { result = &it->second; } else { - GOOGLE_LOG(ERROR) - << "Quota response did not have quota for: " << quota.name; + MIXER_WARN("Quota response did not have quota for: %s", + quota.name.c_str()); } } if (!quota.response_func(attributes, result)) { @@ -131,6 +133,7 @@ void QuotaCache::CheckResult::SetResponse(const Status& status, } } if (!rejected_quota_names.empty()) { + MIXER_DEBUG("Quota is exhausted for: %s", rejected_quota_names.c_str()); status_ = Status(Code::RESOURCE_EXHAUSTED, std::string("Quota is exhausted for: ") + rejected_quota_names); @@ -218,9 +221,10 @@ void QuotaCache::SetResponse(const Attributes& attributes, utils::HashType signature; if (!referenced.Signature(attributes, quota_name, &signature)) { - GOOGLE_LOG(ERROR) << "Quota response referenced mismatchs with request"; - GOOGLE_LOG(ERROR) << "Request attributes: " << attributes.DebugString(); - GOOGLE_LOG(ERROR) << "Referenced attributes: " << referenced.DebugString(); + MIXER_WARN( + "Quota response referenced does not match request. Request " + "attributes: %s, Referenced attributes: %s", + attributes.DebugString().c_str(), referenced.DebugString().c_str()); return; } @@ -235,8 +239,8 @@ void QuotaCache::SetResponse(const Attributes& attributes, utils::HashType hash = referenced.Hash(); if (quota_ref.referenced_map.find(hash) == quota_ref.referenced_map.end()) { quota_ref.referenced_map[hash] = referenced; - GOOGLE_LOG(INFO) << "Add a new Referenced for quota cache: " << quota_name - << ", reference: " << referenced.DebugString(); + MIXER_DEBUG("Add a new Referenced for quota cache: %s, reference: %s", + quota_name.c_str(), referenced.DebugString().c_str()); } cache_->Insert(signature, quota_ref.pending_item.release(), 1); diff --git a/src/istio/mixerclient/report_batch.cc b/src/istio/mixerclient/report_batch.cc index 5bb07ef436f..9906eba3883 100644 --- a/src/istio/mixerclient/report_batch.cc +++ b/src/istio/mixerclient/report_batch.cc @@ -15,6 +15,7 @@ #include "src/istio/mixerclient/report_batch.h" #include "include/istio/utils/protobuf.h" +#include "src/istio/utils/logger.h" using ::google::protobuf::util::Status; using ::google::protobuf::util::error::Code; @@ -25,6 +26,9 @@ using ::istio::mixer::v1::ReportResponse; namespace istio { namespace mixerclient { +static std::atomic REPORT_FAIL_LOG_MESSAGES{0}; +static constexpr uint32_t REPORT_FAIL_LOG_MODULUS{100}; + ReportBatch::ReportBatch(const ReportOptions& options, TransportReportFunc transport, TimerCreateFunc timer_create, @@ -70,7 +74,12 @@ void ReportBatch::FlushWithLock() { transport_(request, response, [this, response](const Status& status) { delete response; if (!status.ok()) { - GOOGLE_LOG(ERROR) << "Mixer Report failed with: " << status.ToString(); + if (MIXER_WARN_ENABLED && + 0 == REPORT_FAIL_LOG_MESSAGES++ % REPORT_FAIL_LOG_MODULUS) { + MIXER_WARN("Mixer Report failed with: %s", status.ToString().c_str()); + } else { + MIXER_DEBUG("Mixer Report failed with: %s", status.ToString().c_str()); + } if (utils::InvalidDictionaryStatus(status)) { compressor_.ShrinkGlobalDictionary(); } diff --git a/src/istio/prefetch/BUILD b/src/istio/prefetch/BUILD index ded66da7c2e..2695206733c 100644 --- a/src/istio/prefetch/BUILD +++ b/src/istio/prefetch/BUILD @@ -25,6 +25,7 @@ cc_library( visibility = ["//visibility:public"], deps = [ "//include/istio/prefetch:headers_lib", + "//src/istio/utils:utils_lib", ], ) diff --git a/src/istio/prefetch/quota_prefetch.cc b/src/istio/prefetch/quota_prefetch.cc index ec244d2bc72..62ae5642658 100644 --- a/src/istio/prefetch/quota_prefetch.cc +++ b/src/istio/prefetch/quota_prefetch.cc @@ -16,28 +16,12 @@ #include "include/istio/prefetch/quota_prefetch.h" #include "src/istio/prefetch/circular_queue.h" #include "src/istio/prefetch/time_based_counter.h" +#include "src/istio/utils/logger.h" #include using namespace std::chrono; -// Turn this on to debug for quota_prefetch_test.cc -// Not for debugging in production. -#if 0 -#include -#define LOG(t) \ - std::cerr << "(" \ - << duration_cast(t.time_since_epoch()).count() \ - << "):" -#else -// Pipe to stringstream to disable logging. -#include -std::ostringstream os; -#define LOG(t) \ - os.clear(); \ - os -#endif - namespace istio { namespace prefetch { namespace { @@ -168,6 +152,10 @@ void QuotaPrefetchImpl::AttemptPrefetch(int amount, Tick t) { int avail = CountAvailable(t); int pass_count = counter_.Count(t); int desired = std::max(pass_count, options_.min_prefetch_amount); + MIXER_TRACE( + "Prefetch decision: available=%d, desired=%d, inflight_count=%d, " + "requested=%d", + avail, desired, inflight_count_, amount); if ((avail < desired / 2 && inflight_count_ == 0) || avail < amount) { bool use_not_granted = (avail == 0 && mode_ == OPEN); Prefetch(std::max(amount, desired), use_not_granted, t); @@ -181,7 +169,7 @@ void QuotaPrefetchImpl::Prefetch(int req_amount, bool use_not_granted, Tick t) { slot_id = Add(req_amount, t + milliseconds(kMaxExpirationInMs)); } - LOG(t) << "Prefetch: " << req_amount << ", id: " << slot_id << std::endl; + MIXER_DEBUG("Prefetch amount %d for slotid: %lu", req_amount, slot_id); last_prefetch_time_ = t; ++inflight_count_; @@ -225,7 +213,7 @@ int QuotaPrefetchImpl::Substract(int delta, Tick t) { } } else { if (n->available > 0) { - LOG(t) << "Expired:" << n->available << std::endl; + MIXER_DEBUG("Expired: %d", n->available); } } queue_.Pop(); @@ -240,9 +228,8 @@ void QuotaPrefetchImpl::OnResponse(SlotId slot_id, int req_amount, std::lock_guard lock(mutex_); --inflight_count_; - LOG(t) << "OnResponse: req:" << req_amount << ", resp: " << resp_amount - << ", expire: " << expiration.count() << ", id: " << slot_id - << std::endl; + MIXER_DEBUG("OnResponse: req: %d, resp: %d, expire: %ld, id: %lu", req_amount, + resp_amount, expiration.count(), slot_id); // resp_amount of -1 indicates any network failures. // Use fail open policy to handle any netowrk failures. @@ -301,7 +288,7 @@ bool QuotaPrefetchImpl::Check(int amount, Tick t) { } } if (!ret) { - LOG(t) << "Rejected amount: " << amount << std::endl; + MIXER_DEBUG("Rejected amount: %d", amount); } return ret; } diff --git a/src/istio/utils/BUILD b/src/istio/utils/BUILD index 4cc7ff72413..b6bced64571 100644 --- a/src/istio/utils/BUILD +++ b/src/istio/utils/BUILD @@ -18,11 +18,13 @@ cc_library( name = "utils_lib", srcs = [ "local_attributes.cc", + "logger.cc", "protobuf.cc", "status.cc", "utils.cc" ], hdrs = [ + "logger.h", "utils.h", ], visibility = ["//visibility:public"], @@ -60,6 +62,16 @@ cc_test( ], ) +cc_test( + name = "logger_test", + size = "small", + srcs = ["logger_test.cc"], + deps = [ + ":utils_lib", + "//external:googletest_main", + ], +) + cc_library( name = "attribute_names_lib", srcs = [ diff --git a/src/istio/utils/logger.cc b/src/istio/utils/logger.cc new file mode 100644 index 00000000000..f5b32c6981e --- /dev/null +++ b/src/istio/utils/logger.cc @@ -0,0 +1,87 @@ +/* Copyright 2019 Istio Authors. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "src/istio/utils/logger.h" +#include + +namespace istio { +namespace utils { + +Logger::~Logger() {} + +void Logger::log(Level level, const char *format, ...) { + if (!isLoggable(level)) { + return; + } + + va_list args; + va_start(args, format); + char buffer[256]; + ::vsnprintf(buffer, sizeof(buffer), format, args); + buffer[sizeof(buffer) - 1] = 0; + va_end(args); + + writeBuffer(level, buffer); +} + +// This is equivalent to the original mixer client logger, but is not used when +// mixer client is used inside Envoy. This preserves mixer client's +// independence of the Envoy source code without forcing it to log (infrequenty) +// to stdout. +class DefaultLogger : public Logger { + protected: + virtual bool isLoggable(Level level) override { + switch (level) { + case Level::TRACE_: + case Level::DEBUG_: + return false; + case Level::INFO_: + case Level::WARN_: + case Level::ERROR_: + return true; + } + } + + virtual void writeBuffer(Level level, const char *buffer) override { + fprintf(stderr, "%s %s\n", levelString(level), buffer); + } + + private: + const char *levelString(Level level) { + switch (level) { + case Level::TRACE_: + return "TRACE"; + case Level::DEBUG_: + return "DEBUG"; + case Level::INFO_: + return "INFO"; + case Level::WARN_: + return "WARN"; + case Level::ERROR_: + return "ERROR"; + } + } +}; + +static std::unique_ptr active_logger{new DefaultLogger()}; + +void setLogger(std::unique_ptr logger) { + active_logger = std::move(logger); + MIXER_INFO("Logger active"); +} +Logger &getLogger() { return *active_logger; } + +} // namespace utils +} // namespace istio diff --git a/src/istio/utils/logger.h b/src/istio/utils/logger.h new file mode 100644 index 00000000000..b06baa30622 --- /dev/null +++ b/src/istio/utils/logger.h @@ -0,0 +1,107 @@ +/* Copyright 2019 Istio Authors. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include + +namespace istio { +namespace utils { + +class Logger { + public: + virtual ~Logger(); + + enum class Level { TRACE_, DEBUG_, INFO_, WARN_, ERROR_ }; + + void log(Level level, const char *format, ...); + + virtual bool isLoggable(Level level) = 0; + + protected: + virtual void writeBuffer(Level level, const char *buffer) = 0; +}; + +extern void setLogger(std::unique_ptr logger); +extern Logger &getLogger(); + +} // namespace utils +} // namespace istio + +#define STRINGLIT2(x) #x +#define STRINGLIT(x) STRINGLIT2(x) +#define FILE_LINE "[" __FILE__ ":" STRINGLIT(__LINE__) "] " + +#define MIXER_TRACE_ENABLED \ + (istio::utils::getLogger().isLoggable(istio::utils::Logger::Level::TRACE_)) +#define MIXER_DEBUG_ENABLED \ + (istio::utils::getLogger().isLoggable(istio::utils::Logger::Level::DEBUG_)) +#define MIXER_INFO_ENABLED \ + (istio::utils::getLogger().isLoggable(istio::utils::Logger::Level::INFO_)) +#define MIXER_WARN_ENABLED \ + (istio::utils::getLogger().isLoggable(istio::utils::Logger::Level::WARN_)) +#define MIXER_ERROR_ENABLED \ + (istio::utils::getLogger().isLoggable(istio::utils::Logger::Level::ERROR_)) + +#define MIXER_TRACE_INT(FORMAT, ...) \ + istio::utils::getLogger().log(istio::utils::Logger::Level::TRACE_, \ + FILE_LINE FORMAT, ##__VA_ARGS__) +#define MIXER_DEBUG_INT(FORMAT, ...) \ + istio::utils::getLogger().log(istio::utils::Logger::Level::DEBUG_, \ + FILE_LINE FORMAT, ##__VA_ARGS__) +#define MIXER_INFO_INT(FORMAT, ...) \ + istio::utils::getLogger().log(istio::utils::Logger::Level::INFO_, \ + FILE_LINE FORMAT, ##__VA_ARGS__) +#define MIXER_WARN_INT(FORMAT, ...) \ + istio::utils::getLogger().log(istio::utils::Logger::Level::WARN_, \ + FILE_LINE FORMAT, ##__VA_ARGS__) +#define MIXER_ERROR_INT(FORMAT, ...) \ + istio::utils::getLogger().log(istio::utils::Logger::Level::ERROR_, \ + FILE_LINE FORMAT, ##__VA_ARGS__) + +#define MIXER_TRACE(FORMAT, ...) \ + do { \ + if (MIXER_TRACE_ENABLED) { \ + MIXER_TRACE_INT(FORMAT, ##__VA_ARGS__); \ + } \ + } while (0) + +#define MIXER_DEBUG(FORMAT, ...) \ + do { \ + if (MIXER_DEBUG_ENABLED) { \ + MIXER_DEBUG_INT(FORMAT, ##__VA_ARGS__); \ + } \ + } while (0) + +#define MIXER_INFO(FORMAT, ...) \ + do { \ + if (MIXER_INFO_ENABLED) { \ + MIXER_INFO_INT(FORMAT, ##__VA_ARGS__); \ + } \ + } while (0) + +#define MIXER_WARN(FORMAT, ...) \ + do { \ + if (MIXER_WARN_ENABLED) { \ + MIXER_WARN_INT(FORMAT, ##__VA_ARGS__); \ + } \ + } while (0) + +#define MIXER_ERROR(FORMAT, ...) \ + do { \ + if (MIXER_ERROR_ENABLED) { \ + MIXER_ERROR_INT(FORMAT, ##__VA_ARGS__); \ + } \ + } while (0) diff --git a/src/istio/utils/logger_test.cc b/src/istio/utils/logger_test.cc new file mode 100644 index 00000000000..a03522e578b --- /dev/null +++ b/src/istio/utils/logger_test.cc @@ -0,0 +1,133 @@ +/* Copyright 2019 Istio Authors. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "src/istio/utils/logger.h" +#include "gtest/gtest.h" + +#include + +namespace istio { +namespace utils { + +class CountingArgument { + public: + const char* c_str() { + ++to_string_calls; + return "logged entity"; + } + + int to_string_calls{0}; +}; + +class CountingLogger : public Logger { + public: + CountingLogger(int& is_loggable_calls, int& write_buffer_calls) + : is_loggable_calls_(is_loggable_calls), + write_buffer_calls_(write_buffer_calls) {} + + virtual bool isLoggable(Level level) override { + ++is_loggable_calls_; + + switch (level) { + case Level::TRACE_: + case Level::DEBUG_: + return false; + case Level::INFO_: + case Level::WARN_: + case Level::ERROR_: + return true; + } + } + + virtual void writeBuffer(Level level, const char* buffer) override { + ++write_buffer_calls_; + } + + private: + int& is_loggable_calls_; + int& write_buffer_calls_; +}; + +class LoggerTest : public ::testing::Test { + protected: + virtual void SetUp() override { + std::unique_ptr logger{ + new CountingLogger(is_loggable_calls_, write_buffer_calls_)}; + setLogger(std::move(logger)); + // Set logger itself logs something, so clear the counters + is_loggable_calls_ = 0; + write_buffer_calls_ = 0; + } + + int is_loggable_calls_{0}; + int write_buffer_calls_{0}; +}; + +TEST_F(LoggerTest, CallArgsOnlyIfLoggable) { + CountingArgument entity; + int expected_to_string_calls = 0; + int expected_is_loggable_calls = 0; + int expected_write_buffer_calls = 0; + + // TRACE and DEBUG shouldn't be logged and shouldn't have any affect on the + // arguments to be logged. + + MIXER_TRACE("%s", entity.c_str()); + ++expected_is_loggable_calls; + + EXPECT_EQ(expected_to_string_calls, entity.to_string_calls); + EXPECT_EQ(expected_is_loggable_calls, is_loggable_calls_); + EXPECT_EQ(expected_write_buffer_calls, write_buffer_calls_); + + MIXER_DEBUG("%s", entity.c_str()); + ++expected_is_loggable_calls; + + EXPECT_EQ(expected_to_string_calls, entity.to_string_calls); + EXPECT_EQ(expected_is_loggable_calls, is_loggable_calls_); + EXPECT_EQ(expected_write_buffer_calls, write_buffer_calls_); + + // INFO+ will invoke their arguments once, be logged, and call isLoggable + // twice due to a redundant/defensive isLoggable check. + + MIXER_INFO("%s", entity.c_str()); + expected_is_loggable_calls += 2; + ++expected_to_string_calls; + ++expected_write_buffer_calls; + + EXPECT_EQ(expected_to_string_calls, entity.to_string_calls); + EXPECT_EQ(expected_is_loggable_calls, is_loggable_calls_); + EXPECT_EQ(expected_write_buffer_calls, write_buffer_calls_); + + MIXER_WARN("%s", entity.c_str()); + expected_is_loggable_calls += 2; + ++expected_to_string_calls; + ++expected_write_buffer_calls; + + EXPECT_EQ(expected_to_string_calls, entity.to_string_calls); + EXPECT_EQ(expected_is_loggable_calls, is_loggable_calls_); + EXPECT_EQ(expected_write_buffer_calls, write_buffer_calls_); + + MIXER_ERROR("%s", entity.c_str()); + expected_is_loggable_calls += 2; + ++expected_to_string_calls; + ++expected_write_buffer_calls; + + EXPECT_EQ(expected_to_string_calls, entity.to_string_calls); + EXPECT_EQ(expected_is_loggable_calls, is_loggable_calls_); + EXPECT_EQ(expected_write_buffer_calls, write_buffer_calls_); +} + +} // namespace utils +} // namespace istio