-
Notifications
You must be signed in to change notification settings - Fork 4.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
server: warn on admin mutations, GET requests, add mocking of messages #2912
Changes from 14 commits
10f225f
6e19a68
f985810
372363c
2696a11
0dc74f0
900fc3d
7e3c3f4
fde6a65
24975f0
2cdc010
14de253
d92bf08
7ea82f2
218da90
5fcc829
cd177b0
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -12,6 +12,7 @@ | |
#include "common/common/fmt.h" | ||
#include "common/common/macros.h" | ||
|
||
#include "absl/strings/string_view.h" | ||
#include "spdlog/spdlog.h" | ||
|
||
namespace Envoy { | ||
|
@@ -82,46 +83,96 @@ class Logger { | |
friend class Registry; | ||
}; | ||
|
||
class DelegatingLogSink; | ||
typedef std::shared_ptr<DelegatingLogSink> DelegatingLogSinkPtr; | ||
|
||
/** | ||
* An optionally locking stderr or file logging sink. | ||
* | ||
* This sink outputs to either stderr or to a file. It shares both implementations (instead of | ||
* being two separate classes) because we can't setup file logging until after the AccessLogManager | ||
* is available, but by that time some loggers have cached their logger from the registry already, | ||
* so we need to be able switch implementations without replacing the object. | ||
* Captures a logging sink that can be delegated to for a bounded amount of time. | ||
* On destruction, logging is reverted to its previous state. SinkDelegates must | ||
* be allocated/freed as a stack. | ||
*/ | ||
class LockingStderrOrFileSink : public spdlog::sinks::sink { | ||
class SinkDelegate { | ||
public: | ||
explicit SinkDelegate(DelegatingLogSinkPtr log_sink); | ||
virtual ~SinkDelegate(); | ||
|
||
virtual void log(absl::string_view msg) PURE; | ||
virtual void flush() PURE; | ||
|
||
private: | ||
SinkDelegate* previous_delegate_; | ||
DelegatingLogSinkPtr log_sink_; | ||
}; | ||
|
||
/** | ||
* SinkDelegate that writes log messages to a file. | ||
*/ | ||
class FileSinkDelegate : public SinkDelegate { | ||
public: | ||
FileSinkDelegate(const std::string& log_path, AccessLog::AccessLogManager& log_manager, | ||
DelegatingLogSinkPtr log_sink); | ||
|
||
// SinkDelegate | ||
void log(absl::string_view msg) override; | ||
void flush() override; | ||
|
||
private: | ||
Filesystem::FileSharedPtr log_file_; | ||
}; | ||
|
||
/** | ||
* SinkDelegate that writes log messages to stderr. | ||
*/ | ||
class StderrSinkDelegate : public SinkDelegate { | ||
public: | ||
explicit StderrSinkDelegate(DelegatingLogSinkPtr log_sink); | ||
|
||
// SinkDelegate | ||
void log(absl::string_view msg) override; | ||
void flush() override; | ||
|
||
bool hasLock() const { return lock_ != nullptr; } | ||
void setLock(Thread::BasicLockable& lock) { lock_ = &lock; } | ||
|
||
/** | ||
* Configure this object to log to stderr. | ||
* | ||
* @note This method is not thread-safe and can only be called when no other threads | ||
* are logging. | ||
*/ | ||
void logToStdErr(); | ||
private: | ||
Thread::BasicLockable* lock_{}; | ||
}; | ||
|
||
/** | ||
* Configure this object to log to a file. | ||
* | ||
* @note This method is not thread-safe and can only be called when no other threads | ||
* are logging. | ||
*/ | ||
void logToFile(const std::string& log_path, AccessLog::AccessLogManager& log_manager); | ||
/** | ||
* Stacks logging sinks, so you can temporarily override the logging mechanism, restoring | ||
* the prevoius state when the DelegatingSink is destructed. | ||
*/ | ||
class DelegatingLogSink : public spdlog::sinks::sink { | ||
public: | ||
void setLock(Thread::BasicLockable& lock) { stderr_sink_->setLock(lock); } | ||
|
||
// spdlog::sinks::sink | ||
void log(const spdlog::details::log_msg& msg) override; | ||
void flush() override; | ||
void flush() override { sink_->flush(); } | ||
|
||
/** | ||
* @return bool whether a lock has been established. | ||
*/ | ||
bool hasLock() const { return lock_ != nullptr; } | ||
bool hasLock() const { return stderr_sink_->hasLock(); } | ||
|
||
// Constructs a new DelegatingLogSink, sets up the default sink to stderr, | ||
// and returns a shared_ptr to it. A shared_ptr is required for sinks used | ||
// in spdlog::logger; it would not otherwise be required in Envoy. This method | ||
// must own the construction process because StderrSinkDelegate needs access to | ||
// the DelegatingLogSinkPtr, not just the DelegatingLogSink*, and that is only | ||
// available after construction. | ||
static DelegatingLogSinkPtr init(); | ||
|
||
private: | ||
Thread::BasicLockable* lock_{}; | ||
Filesystem::FileSharedPtr log_file_; | ||
friend class SinkDelegate; | ||
|
||
DelegatingLogSink() {} | ||
|
||
void setDelegate(SinkDelegate* sink) { sink_ = sink; } | ||
SinkDelegate* delegate() { return sink_; } | ||
|
||
SinkDelegate* sink_{nullptr}; | ||
std::unique_ptr<StderrSinkDelegate> stderr_sink_; // Builtin sink to use as a last resort. | ||
}; | ||
|
||
/** | ||
|
@@ -139,16 +190,33 @@ class Registry { | |
/** | ||
* @return the singleton sink to use for all loggers. | ||
*/ | ||
static std::shared_ptr<LockingStderrOrFileSink> getSink() { | ||
static std::shared_ptr<LockingStderrOrFileSink> sink(new LockingStderrOrFileSink()); | ||
static DelegatingLogSinkPtr getSink() { | ||
static DelegatingLogSinkPtr sink = DelegatingLogSink::init(); | ||
return sink; | ||
} | ||
|
||
/** | ||
* Initialize the logging system from server options. | ||
/* | ||
* Initialize the logging system with the specified lock and log level. | ||
*/ | ||
static void initialize(uint64_t log_level, const std::string& log_format, | ||
Thread::BasicLockable& lock); | ||
Thread::BasicLockable& lock) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I have a minor preference for putting this in the .cc file because nearly every file has to parse this header. |
||
// TODO(jmarantz): I think it would be more robust to push a separate lockable | ||
// SinkDelegate onto the stack for the lifetime of the lock, so we don't crash | ||
// if we try to log anything after the context owning the lock is destroyed. | ||
getSink()->setLock(lock); | ||
setLogLevel(log_level); | ||
setLogFormat(log_format); | ||
} | ||
|
||
/** | ||
* Sets the log level. | ||
*/ | ||
static void setLogLevel(uint64_t log_level); | ||
|
||
/** | ||
* Sets the log format. | ||
*/ | ||
static void setLogFormat(const std::string& log_format); | ||
|
||
/** | ||
* @return const std::vector<Logger>& the installed loggers. | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -56,7 +56,8 @@ InstanceImpl::InstanceImpl(Options& options, Network::Address::InstanceConstShar | |
try { | ||
if (!options.logPath().empty()) { | ||
try { | ||
Logger::Registry::getSink()->logToFile(options.logPath(), access_log_manager_); | ||
file_logger_ = std::make_unique<Logger::FileSinkDelegate>( | ||
options.logPath(), access_log_manager_, Logger::Registry::getSink()); | ||
} catch (const EnvoyException& e) { | ||
throw EnvoyException( | ||
fmt::format("Failed to open log-file '{}'. e.what(): {}", options.logPath(), e.what())); | ||
|
@@ -80,7 +81,7 @@ InstanceImpl::~InstanceImpl() { | |
|
||
// Stop logging to file before all the AccessLogManager and its dependencies are | ||
// destructed to avoid crashing at shutdown. | ||
Logger::Registry::getSink()->logToStdErr(); | ||
file_logger_.reset(nullptr); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nullptr is the default value; envoy convention is to omit it as an arg at the callsite. |
||
} | ||
|
||
Upstream::ClusterManager& InstanceImpl::clusterManager() { return config_->clusterManager(); } | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
DelegatingLogSink() = default;