Skip to content

Add support for proxy_log_destination ABI #26364

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

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 14 additions & 1 deletion api/envoy/extensions/wasm/v3/wasm.proto
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ message SanitizationConfig {
}

// Configuration for a Wasm VM.
// [#next-free-field: 8]
// [#next-free-field: 9]
message VmConfig {
// An ID which will be used along with a hash of the wasm code (or the name of the registered Null
// VM plugin) to determine which VM will be used for the plugin. All plugins which use the same
Expand Down Expand Up @@ -106,6 +106,19 @@ message VmConfig {
// vars just like when you do on native platforms.
// Warning: Envoy rejects the configuration if there's conflict of key space.
EnvironmentVariables environment_variables = 7;

// Specifies the log destination for the plugin.
// If not specified, the plugin will log to the default Envoy log
// example: "audit-logs": { file_path: "/var/log/audit.log" }
map<string, LogDestination> log_destination = 8;
}

message LogDestination {
// Target destination for the log messages from the plugin.
oneof target {
string file_path = 1;
// TODO: Remote logging service
}
}

message EnvironmentVariables {
Expand Down
26 changes: 26 additions & 0 deletions source/extensions/common/wasm/context.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1196,6 +1196,32 @@ WasmResult Context::log(uint32_t level, std::string_view message) {
PANIC_DUE_TO_CORRUPT_ENUM;
}

WasmResult Context::logWithDestination(uint32_t level, std::string_view message,
std::string_view destination) {
const auto& log_destinations = wasm()->log_destinations();
// iterate over log_destinations map to check if dest
// destination requested by plugin exists
for (const auto& e : log_destinations) {
if (e.first == destination) {
// write message to the file which is the value of the key if it exists
std::ofstream log_file;
log_file.open(e.second, std::ios::out | std::ios_base::app);
if (!log_file) {
std::stringstream buf;
buf << "Failed to open log file: " << e.second;
log(level, buf.str());
return WasmResult::InvalidMemoryAccess;
}
log_file << message << std::endl;
log_file.close();
return WasmResult::Ok;
}
}
log(level, "logWithDestination: destination requested by plugin does not exist: " +
std::string(destination)); // log to default destination i.e proxy logs
return WasmResult::NotFound;
}

uint32_t Context::getLogLevel() {
// Like the "log" call above, assume that spdlog level as an int
// matches the enum in the SDK
Expand Down
2 changes: 2 additions & 0 deletions source/extensions/common/wasm/context.h
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,8 @@ class Context : public proxy_wasm::ContextBase,
uint64_t getMonotonicTimeNanoseconds() override;
std::string_view getConfiguration() override;
std::pair<uint32_t, std::string_view> getStatus() override;
WasmResult logWithDestination(uint32_t level, std::string_view message,
std::string_view destination) override;

// State accessors
WasmResult getProperty(std::string_view path, std::string* result) override;
Expand Down
27 changes: 27 additions & 0 deletions source/extensions/common/wasm/plugin.cc
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,33 @@ WasmConfig::WasmConfig(const envoy::extensions::wasm::v3::PluginConfig& config)
}
}
}

if (config.vm_config().runtime() == "envoy.wasm.runtime.null" &&
!config_.vm_config().log_destination().empty()) {
throw EnvoyException("envoy.extensions.wasm.v3.VmConfig.log_destination must "
"not be set for NullVm.");
}
// Check key duplication.
absl::flat_hash_set<std::string> keys;
for (const auto& ld : config_.vm_config().log_destination()) {
if (!keys.insert(ld.first).second) {
throw EnvoyException(fmt::format("Key {} is duplicated in "
"envoy.extensions.wasm.v3.VmConfig.log_destination for {}. "
"All the keys must be unique.",
ld.first, config_.name()));
}
}
// Construct merged key-value pairs. Also check for boundary conditions
// (e.g. empty file path).
for (const auto& ld : config_.vm_config().log_destination()) {
if (ld.second.file_path().empty()) {
throw EnvoyException(
fmt::format("Key {} value envoy.extensions.wasm.v3.VmConfig.LogDestination.file_path "
"must not be empty for {}.",
ld.first, config_.name()));
}
log_destinations_[ld.first] = ld.second.file_path();
}
}

} // namespace Wasm
Expand Down
3 changes: 3 additions & 0 deletions source/extensions/common/wasm/plugin.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ namespace Wasm {

// clang-format off
using EnvironmentVariableMap = std::unordered_map<std::string, std::string>;
using LogDestinationMap = std::unordered_map<std::string, std::string>;
// clang-format on

class WasmConfig {
Expand All @@ -26,11 +27,13 @@ class WasmConfig {
const envoy::extensions::wasm::v3::PluginConfig& config() { return config_; }
proxy_wasm::AllowedCapabilitiesMap& allowedCapabilities() { return allowed_capabilities_; }
EnvironmentVariableMap& environmentVariables() { return envs_; }
LogDestinationMap& logDestinations() { return log_destinations_; }

private:
const envoy::extensions::wasm::v3::PluginConfig config_;
proxy_wasm::AllowedCapabilitiesMap allowed_capabilities_{};
EnvironmentVariableMap envs_;
LogDestinationMap log_destinations_;
};

using WasmConfigPtr = std::unique_ptr<WasmConfig>;
Expand Down
16 changes: 9 additions & 7 deletions source/extensions/common/wasm/wasm.cc
Original file line number Diff line number Diff line change
Expand Up @@ -75,10 +75,11 @@ void Wasm::initializeLifecycle(Server::ServerLifecycleNotifier& lifecycle_notifi

Wasm::Wasm(WasmConfig& config, absl::string_view vm_key, const Stats::ScopeSharedPtr& scope,
Api::Api& api, Upstream::ClusterManager& cluster_manager, Event::Dispatcher& dispatcher)
: WasmBase(
createWasmVm(config.config().vm_config().runtime()), config.config().vm_config().vm_id(),
MessageUtil::anyToBytes(config.config().vm_config().configuration()),
toStdStringView(vm_key), config.environmentVariables(), config.allowedCapabilities()),
: WasmBase(createWasmVm(config.config().vm_config().runtime()),
config.config().vm_config().vm_id(),
MessageUtil::anyToBytes(config.config().vm_config().configuration()),
toStdStringView(vm_key), config.environmentVariables(), config.logDestinations(),
config.allowedCapabilities()),
scope_(scope), api_(api), stat_name_pool_(scope_->symbolTable()),
custom_stat_namespace_(stat_name_pool_.add(CustomStatNamespace)),
cluster_manager_(cluster_manager), dispatcher_(dispatcher),
Expand Down Expand Up @@ -330,7 +331,8 @@ bool createWasm(const PluginSharedPtr& plugin, const Stats::ScopeSharedPtr& scop
code_cache = new std::remove_reference<decltype(*code_cache)>::type;
}
Stats::ScopeSharedPtr create_wasm_stats_scope = stats_handler.lockAndCreateStats(scope);
// Remove entries older than CODE_CACHE_SECONDS_CACHING_TTL except for our target.
// Remove entries older than CODE_CACHE_SECONDS_CACHING_TTL except for our
// target.
for (auto it = code_cache->begin(); it != code_cache->end();) {
if (now - it->second.use_time > std::chrono::seconds(CODE_CACHE_SECONDS_CACHING_TTL) &&
it->first != vm_config.code().remote().sha256()) {
Expand Down Expand Up @@ -423,8 +425,8 @@ bool createWasm(const PluginSharedPtr& plugin, const Stats::ScopeSharedPtr& scop
}
stats_handler.onRemoteCacheEntriesChanged(code_cache->size());
}
// NB: xDS currently does not support failing asynchronously, so we fail immediately
// if remote Wasm code is not cached and do a background fill.
// NB: xDS currently does not support failing asynchronously, so we fail
// immediately if remote Wasm code is not cached and do a background fill.
if (!vm_config.nack_on_code_cache_miss()) {
if (code.empty()) {
ENVOY_LOG_TO_LOGGER(Envoy::Logger::Registry::getLog(Envoy::Logger::Id::wasm), trace,
Expand Down