LCOV - code coverage report
Current view: top level - source/common/access_log - access_log_manager_impl.h (source / functions) Hit Total Coverage
Test: coverage.dat Lines: 1 1 100.0 %
Date: 2024-01-05 06:35:25 Functions: 1 1 100.0 %

          Line data    Source code
       1             : #pragma once
       2             : 
       3             : #include <string>
       4             : 
       5             : #include "envoy/access_log/access_log.h"
       6             : #include "envoy/api/api.h"
       7             : #include "envoy/event/dispatcher.h"
       8             : #include "envoy/filesystem/filesystem.h"
       9             : #include "envoy/stats/stats_macros.h"
      10             : #include "envoy/stats/store.h"
      11             : 
      12             : #include "source/common/buffer/buffer_impl.h"
      13             : #include "source/common/common/logger.h"
      14             : #include "source/common/common/thread.h"
      15             : 
      16             : #include "absl/container/node_hash_map.h"
      17             : 
      18             : namespace Envoy {
      19             : 
      20             : #define ACCESS_LOG_FILE_STATS(COUNTER, GAUGE)                                                      \
      21             :   COUNTER(flushed_by_timer)                                                                        \
      22             :   COUNTER(reopen_failed)                                                                           \
      23             :   COUNTER(write_buffered)                                                                          \
      24             :   COUNTER(write_completed)                                                                         \
      25             :   COUNTER(write_failed)                                                                            \
      26             :   GAUGE(write_total_buffered, Accumulate)
      27             : 
      28             : struct AccessLogFileStats {
      29             :   ACCESS_LOG_FILE_STATS(GENERATE_COUNTER_STRUCT, GENERATE_GAUGE_STRUCT)
      30             : };
      31             : 
      32             : namespace AccessLog {
      33             : 
      34             : class AccessLogManagerImpl : public AccessLogManager, Logger::Loggable<Logger::Id::main> {
      35             : public:
      36             :   AccessLogManagerImpl(std::chrono::milliseconds file_flush_interval_msec, Api::Api& api,
      37             :                        Event::Dispatcher& dispatcher, Thread::BasicLockable& lock,
      38             :                        Stats::Store& stats_store)
      39             :       : file_flush_interval_msec_(file_flush_interval_msec), api_(api), dispatcher_(dispatcher),
      40             :         lock_(lock), file_stats_{
      41             :                          ACCESS_LOG_FILE_STATS(POOL_COUNTER_PREFIX(stats_store, "filesystem."),
      42         455 :                                                POOL_GAUGE_PREFIX(stats_store, "filesystem."))} {}
      43             :   ~AccessLogManagerImpl() override;
      44             : 
      45             :   // AccessLog::AccessLogManager
      46             :   void reopen() override;
      47             :   AccessLogFileSharedPtr createAccessLog(const Filesystem::FilePathAndType& file_info) override;
      48             : 
      49             : private:
      50             :   const std::chrono::milliseconds file_flush_interval_msec_;
      51             :   Api::Api& api_;
      52             :   Event::Dispatcher& dispatcher_;
      53             :   Thread::BasicLockable& lock_;
      54             :   AccessLogFileStats file_stats_;
      55             :   absl::node_hash_map<std::string, AccessLogFileSharedPtr> access_logs_;
      56             : };
      57             : 
      58             : /**
      59             :  * This is a file implementation geared for writing out access logs. It turn out that in certain
      60             :  * cases even if a standard file is opened with O_NONBLOCK, the kernel can still block when writing.
      61             :  * This implementation uses a flush thread per file, with the idea there aren't that many
      62             :  * files. If this turns out to be a good implementation we can potentially have a single flush
      63             :  * thread that flushes all files, but we will start with this.
      64             :  */
      65             : class AccessLogFileImpl : public AccessLogFile {
      66             : public:
      67             :   AccessLogFileImpl(Filesystem::FilePtr&& file, Event::Dispatcher& dispatcher,
      68             :                     Thread::BasicLockable& lock, AccessLogFileStats& stats,
      69             :                     std::chrono::milliseconds flush_interval_msec,
      70             :                     Thread::ThreadFactory& thread_factory);
      71             :   ~AccessLogFileImpl() override;
      72             : 
      73             :   // AccessLog::AccessLogFile
      74             :   void write(absl::string_view data) override;
      75             : 
      76             :   /**
      77             :    * Reopen file asynchronously.
      78             :    * This only sets reopen flag, actual reopen operation is delayed.
      79             :    * Reopen happens before the next write operation.
      80             :    */
      81             :   void reopen() override;
      82             :   void flush() override;
      83             : 
      84             : private:
      85             :   void doWrite(Buffer::Instance& buffer);
      86             :   void flushThreadFunc();
      87             :   Api::IoCallBoolResult open();
      88             :   void createFlushStructures();
      89             : 
      90             :   // return default flags set which used by open
      91             :   static Filesystem::FlagSet defaultFlags();
      92             : 
      93             :   // Minimum size before the flush thread will be told to flush.
      94             :   static const uint64_t MIN_FLUSH_SIZE = 1024 * 64;
      95             : 
      96             :   Filesystem::FilePtr file_;
      97             : 
      98             :   // These locks are always acquired in the following order if multiple locks are held:
      99             :   //    1) write_lock_
     100             :   //    2) flush_lock_
     101             :   //    3) file_lock_
     102             :   Thread::BasicLockable& file_lock_;      // This lock is used only by the flush thread when writing
     103             :                                           // to disk. This is used to make sure that file blocks do
     104             :                                           // not get interleaved by multiple processes writing to
     105             :                                           // the same file during hot-restart.
     106             :   Thread::MutexBasicLockable flush_lock_; // This lock is used to prevent simultaneous flushes from
     107             :                                           // the flush thread and a synchronous flush. This protects
     108             :                                           // concurrent access to the about_to_write_buffer_, fd_,
     109             :                                           // and all other data used during flushing and file
     110             :                                           // re-opening.
     111             :   Thread::MutexBasicLockable
     112             :       write_lock_; // The lock is used when filling the flush buffer. It allows
     113             :                    // multiple threads to write to the same file at relatively
     114             :                    // high performance. It is always local to the process.
     115             :   Thread::ThreadPtr flush_thread_;
     116             :   Thread::CondVar flush_event_;
     117             :   bool flush_thread_exit_ ABSL_GUARDED_BY(write_lock_){false};
     118             :   bool reopen_file_ ABSL_GUARDED_BY(write_lock_){false};
     119             :   Buffer::OwnedImpl
     120             :       flush_buffer_ ABSL_GUARDED_BY(write_lock_); // This buffer is used by multiple threads. It
     121             :                                                   // gets filled and then flushed either when max
     122             :                                                   // size is reached or when a timer fires.
     123             :   // TODO(jmarantz): this should be ABSL_GUARDED_BY(flush_lock_) but the analysis cannot poke
     124             :   // through the std::make_unique assignment. I do not believe it's possible to annotate this
     125             :   // properly now due to limitations in the clang thread annotation analysis.
     126             :   Buffer::OwnedImpl about_to_write_buffer_; // This buffer is used only by the flush thread. Data
     127             :                                             // is moved from flush_buffer_ under lock, and then
     128             :                                             // the lock is released so that flush_buffer_ can
     129             :                                             // continue to fill. This buffer is then used for the
     130             :                                             // final write to disk.
     131             :   Event::TimerPtr flush_timer_;
     132             :   Thread::ThreadFactory& thread_factory_;
     133             :   const std::chrono::milliseconds flush_interval_msec_; // Time interval buffer gets flushed no
     134             :                                                         // matter if it reached the MIN_FLUSH_SIZE
     135             :                                                         // or not.
     136             :   AccessLogFileStats& stats_;
     137             : };
     138             : 
     139             : } // namespace AccessLog
     140             : } // namespace Envoy

Generated by: LCOV version 1.15