1
#include "source/extensions/access_loggers/dynamic_modules/access_log.h"
2

            
3
#include "envoy/common/exception.h"
4

            
5
namespace Envoy {
6
namespace Extensions {
7
namespace AccessLoggers {
8
namespace DynamicModules {
9

            
10
ThreadLocalLogger::ThreadLocalLogger(envoy_dynamic_module_type_access_logger_module_ptr logger,
11
                                     DynamicModuleAccessLogConfigSharedPtr config,
12
                                     uint32_t worker_index)
13
135
    : logger_(logger), config_(config), worker_index_(worker_index) {}
14

            
15
135
ThreadLocalLogger::~ThreadLocalLogger() {
16
135
  if (logger_ != nullptr) {
17
    // Flush any buffered logs before destroying the logger.
18
12
    if (config_->on_logger_flush_ != nullptr) {
19
11
      config_->on_logger_flush_(logger_);
20
11
    }
21
12
    config_->on_logger_destroy_(logger_);
22
12
  }
23
135
}
24

            
25
DynamicModuleAccessLog::DynamicModuleAccessLog(AccessLog::FilterPtr&& filter,
26
                                               DynamicModuleAccessLogConfigSharedPtr config,
27
                                               ThreadLocal::SlotAllocator& tls)
28
7
    : Common::ImplBase(std::move(filter)), config_(config), tls_slot_(tls.allocateSlot()) {
29

            
30
9
  tls_slot_->set([config](Event::Dispatcher& dispatcher) {
31
7
    uint32_t worker_index;
32
7
    if (Envoy::Thread::MainThread::isMainOrTestThread()) {
33
5
      auto context = Server::Configuration::ServerFactoryContextInstance::getExisting();
34
5
      auto concurrency = context->options().concurrency();
35
5
      worker_index = concurrency; // Set main/test thread on free index.
36
5
    } else {
37
2
      const std::string& worker_name = dispatcher.name();
38
2
      auto pos = worker_name.find_first_of('_');
39
2
      ENVOY_BUG(pos != std::string::npos, "worker name is not in expected format worker_{index}");
40
2
      if (!absl::SimpleAtoi(worker_name.substr(pos + 1), &worker_index)) {
41
        IS_ENVOY_BUG("failed to parse worker index from name");
42
      }
43
2
    }
44
    // Create a thread-local logger wrapper first, then pass it to the module.
45
7
    auto tl_logger = std::make_shared<ThreadLocalLogger>(nullptr, config, worker_index);
46
7
    auto logger = config->on_logger_new_(config->in_module_config_, tl_logger->thisAsVoidPtr());
47
7
    tl_logger->logger_ = logger;
48
7
    return tl_logger;
49
7
  });
50
7
}
51

            
52
void DynamicModuleAccessLog::emitLog(const Formatter::Context& context,
53
6
                                     const StreamInfo::StreamInfo& stream_info) {
54
6
  auto& tl_logger = tls_slot_->getTyped<ThreadLocalLogger>();
55
6
  if (tl_logger.logger_ == nullptr) {
56
1
    return;
57
1
  }
58

            
59
5
  tl_logger.log_context_ = &context;
60
5
  tl_logger.stream_info_ = &stream_info;
61

            
62
  // Convert AccessLogType to ABI enum. The cast is safe because enum values are aligned.
63
5
  const auto abi_log_type =
64
5
      static_cast<envoy_dynamic_module_type_access_log_type>(context.accessLogType());
65

            
66
  // Invoke the module's log callback with the context pointer.
67
5
  config_->on_logger_log_(tl_logger.thisAsVoidPtr(), tl_logger.logger_, abi_log_type);
68

            
69
5
  tl_logger.log_context_ = nullptr;
70
5
  tl_logger.stream_info_ = nullptr;
71
5
}
72

            
73
} // namespace DynamicModules
74
} // namespace AccessLoggers
75
} // namespace Extensions
76
} // namespace Envoy