posix_logger.h raw

   1  // Copyright (c) 2011 The LevelDB Authors. All rights reserved.
   2  // Use of this source code is governed by a BSD-style license that can be
   3  // found in the LICENSE file. See the AUTHORS file for names of contributors.
   4  //
   5  // Logger implementation that can be shared by all environments
   6  // where enough posix functionality is available.
   7  
   8  #ifndef STORAGE_LEVELDB_UTIL_POSIX_LOGGER_H_
   9  #define STORAGE_LEVELDB_UTIL_POSIX_LOGGER_H_
  10  
  11  #include <sys/time.h>
  12  
  13  #include <cassert>
  14  #include <cstdarg>
  15  #include <cstdio>
  16  #include <ctime>
  17  #include <sstream>
  18  #include <thread>
  19  
  20  #include "leveldb/env.h"
  21  
  22  namespace leveldb {
  23  
  24  class PosixLogger final : public Logger {
  25   public:
  26    // Creates a logger that writes to the given file.
  27    //
  28    // The PosixLogger instance takes ownership of the file handle.
  29    explicit PosixLogger(std::FILE* fp) : fp_(fp) { assert(fp != nullptr); }
  30  
  31    ~PosixLogger() override { std::fclose(fp_); }
  32  
  33    void Logv(const char* format, va_list arguments) override {
  34      // Record the time as close to the Logv() call as possible.
  35      struct ::timeval now_timeval;
  36      ::gettimeofday(&now_timeval, nullptr);
  37      const std::time_t now_seconds = now_timeval.tv_sec;
  38      struct std::tm now_components;
  39      ::localtime_r(&now_seconds, &now_components);
  40  
  41      // Record the thread ID.
  42      constexpr const int kMaxThreadIdSize = 32;
  43      std::ostringstream thread_stream;
  44      thread_stream << std::this_thread::get_id();
  45      std::string thread_id = thread_stream.str();
  46      if (thread_id.size() > kMaxThreadIdSize) {
  47        thread_id.resize(kMaxThreadIdSize);
  48      }
  49  
  50      // We first attempt to print into a stack-allocated buffer. If this attempt
  51      // fails, we make a second attempt with a dynamically allocated buffer.
  52      constexpr const int kStackBufferSize = 512;
  53      char stack_buffer[kStackBufferSize];
  54      static_assert(sizeof(stack_buffer) == static_cast<size_t>(kStackBufferSize),
  55                    "sizeof(char) is expected to be 1 in C++");
  56  
  57      int dynamic_buffer_size = 0;  // Computed in the first iteration.
  58      for (int iteration = 0; iteration < 2; ++iteration) {
  59        const int buffer_size =
  60            (iteration == 0) ? kStackBufferSize : dynamic_buffer_size;
  61        char* const buffer =
  62            (iteration == 0) ? stack_buffer : new char[dynamic_buffer_size];
  63  
  64        // Print the header into the buffer.
  65        int buffer_offset = snprintf(
  66            buffer, buffer_size, "%04d/%02d/%02d-%02d:%02d:%02d.%06d %s ",
  67            now_components.tm_year + 1900, now_components.tm_mon + 1,
  68            now_components.tm_mday, now_components.tm_hour, now_components.tm_min,
  69            now_components.tm_sec, static_cast<int>(now_timeval.tv_usec),
  70            thread_id.c_str());
  71  
  72        // The header can be at most 28 characters (10 date + 15 time +
  73        // 3 delimiters) plus the thread ID, which should fit comfortably into the
  74        // static buffer.
  75        assert(buffer_offset <= 28 + kMaxThreadIdSize);
  76        static_assert(28 + kMaxThreadIdSize < kStackBufferSize,
  77                      "stack-allocated buffer may not fit the message header");
  78        assert(buffer_offset < buffer_size);
  79  
  80        // Print the message into the buffer.
  81        std::va_list arguments_copy;
  82        va_copy(arguments_copy, arguments);
  83        buffer_offset +=
  84            std::vsnprintf(buffer + buffer_offset, buffer_size - buffer_offset,
  85                           format, arguments_copy);
  86        va_end(arguments_copy);
  87  
  88        // The code below may append a newline at the end of the buffer, which
  89        // requires an extra character.
  90        if (buffer_offset >= buffer_size - 1) {
  91          // The message did not fit into the buffer.
  92          if (iteration == 0) {
  93            // Re-run the loop and use a dynamically-allocated buffer. The buffer
  94            // will be large enough for the log message, an extra newline and a
  95            // null terminator.
  96            dynamic_buffer_size = buffer_offset + 2;
  97            continue;
  98          }
  99  
 100          // The dynamically-allocated buffer was incorrectly sized. This should
 101          // not happen, assuming a correct implementation of (v)snprintf. Fail
 102          // in tests, recover by truncating the log message in production.
 103          assert(false);
 104          buffer_offset = buffer_size - 1;
 105        }
 106  
 107        // Add a newline if necessary.
 108        if (buffer[buffer_offset - 1] != '\n') {
 109          buffer[buffer_offset] = '\n';
 110          ++buffer_offset;
 111        }
 112  
 113        assert(buffer_offset <= buffer_size);
 114        std::fwrite(buffer, 1, buffer_offset, fp_);
 115        std::fflush(fp_);
 116  
 117        if (iteration != 0) {
 118          delete[] buffer;
 119        }
 120        break;
 121      }
 122    }
 123  
 124   private:
 125    std::FILE* const fp_;
 126  };
 127  
 128  }  // namespace leveldb
 129  
 130  #endif  // STORAGE_LEVELDB_UTIL_POSIX_LOGGER_H_
 131