git / moonbite-core / src/leveldb/util/posix_logger.h · raw

// Copyright (c) 2011 The LevelDB Authors. All rights reserved.// Use of this source code is governed by a BSD-style license that can be// found in the LICENSE file. See the AUTHORS file for names of contributors.//// Logger implementation that can be shared by all environments// where enough posix functionality is available.#ifndef STORAGE_LEVELDB_UTIL_POSIX_LOGGER_H_#define STORAGE_LEVELDB_UTIL_POSIX_LOGGER_H_#include <sys/time.h>#include <cassert>#include <cstdarg>#include <cstdio>#include <ctime>#include <sstream>#include <thread>#include "leveldb/env.h"namespace leveldb {class PosixLogger final : public Logger { public:  // Creates a logger that writes to the given file.  //  // The PosixLogger instance takes ownership of the file handle.  explicit PosixLogger(std::FILE* fp) : fp_(fp) { assert(fp != nullptr); }  ~PosixLogger() override { std::fclose(fp_); }  void Logv(const char* format, va_list arguments) override {    // Record the time as close to the Logv() call as possible.    struct ::timeval now_timeval;    ::gettimeofday(&now_timeval, nullptr);    const std::time_t now_seconds = now_timeval.tv_sec;    struct std::tm now_components;    ::localtime_r(&now_seconds, &now_components);    // Record the thread ID.    constexpr const int kMaxThreadIdSize = 32;    std::ostringstream thread_stream;    thread_stream << std::this_thread::get_id();    std::string thread_id = thread_stream.str();    if (thread_id.size() > kMaxThreadIdSize) {      thread_id.resize(kMaxThreadIdSize);    }    // We first attempt to print into a stack-allocated buffer. If this attempt    // fails, we make a second attempt with a dynamically allocated buffer.    constexpr const int kStackBufferSize = 512;    char stack_buffer[kStackBufferSize];    static_assert(sizeof(stack_buffer) == static_cast<size_t>(kStackBufferSize),                  "sizeof(char) is expected to be 1 in C++");    int dynamic_buffer_size = 0;  // Computed in the first iteration.    for (int iteration = 0; iteration < 2; ++iteration) {      const int buffer_size =          (iteration == 0) ? kStackBufferSize : dynamic_buffer_size;      char* const buffer =          (iteration == 0) ? stack_buffer : new char[dynamic_buffer_size];      // Print the header into the buffer.      int buffer_offset = snprintf(          buffer, buffer_size, "%04d/%02d/%02d-%02d:%02d:%02d.%06d %s ",          now_components.tm_year + 1900, now_components.tm_mon + 1,          now_components.tm_mday, now_components.tm_hour, now_components.tm_min,          now_components.tm_sec, static_cast<int>(now_timeval.tv_usec),          thread_id.c_str());      // The header can be at most 28 characters (10 date + 15 time +      // 3 delimiters) plus the thread ID, which should fit comfortably into the      // static buffer.      assert(buffer_offset <= 28 + kMaxThreadIdSize);      static_assert(28 + kMaxThreadIdSize < kStackBufferSize,                    "stack-allocated buffer may not fit the message header");      assert(buffer_offset < buffer_size);      // Print the message into the buffer.      std::va_list arguments_copy;      va_copy(arguments_copy, arguments);      buffer_offset +=          std::vsnprintf(buffer + buffer_offset, buffer_size - buffer_offset,                         format, arguments_copy);      va_end(arguments_copy);      // The code below may append a newline at the end of the buffer, which      // requires an extra character.      if (buffer_offset >= buffer_size - 1) {        // The message did not fit into the buffer.        if (iteration == 0) {          // Re-run the loop and use a dynamically-allocated buffer. The buffer          // will be large enough for the log message, an extra newline and a          // null terminator.          dynamic_buffer_size = buffer_offset + 2;          continue;        }        // The dynamically-allocated buffer was incorrectly sized. This should        // not happen, assuming a correct implementation of (v)snprintf. Fail        // in tests, recover by truncating the log message in production.        assert(false);        buffer_offset = buffer_size - 1;      }      // Add a newline if necessary.      if (buffer[buffer_offset - 1] != '\n') {        buffer[buffer_offset] = '\n';        ++buffer_offset;      }      assert(buffer_offset <= buffer_size);      std::fwrite(buffer, 1, buffer_offset, fp_);      std::fflush(fp_);      if (iteration != 0) {        delete[] buffer;      }      break;    }  } private:  std::FILE* const fp_;};}  // namespace leveldb#endif  // STORAGE_LEVELDB_UTIL_POSIX_LOGGER_H_