windows_logger.h raw

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