Branch data Line data Source code
# 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 : 0 : explicit PosixLogger(std::FILE* fp) : fp_(fp) { assert(fp != nullptr); }
# 30 : :
# 31 : 0 : ~PosixLogger() override { std::fclose(fp_); }
# 32 : :
# 33 : 0 : void Logv(const char* format, va_list arguments) override {
# 34 : : // Record the time as close to the Logv() call as possible.
# 35 : 0 : struct ::timeval now_timeval;
# 36 : 0 : ::gettimeofday(&now_timeval, nullptr);
# 37 : 0 : const std::time_t now_seconds = now_timeval.tv_sec;
# 38 : 0 : struct std::tm now_components;
# 39 : 0 : ::localtime_r(&now_seconds, &now_components);
# 40 : :
# 41 : : // Record the thread ID.
# 42 : 0 : constexpr const int kMaxThreadIdSize = 32;
# 43 : 0 : std::ostringstream thread_stream;
# 44 : 0 : thread_stream << std::this_thread::get_id();
# 45 : 0 : std::string thread_id = thread_stream.str();
# 46 [ # # ]: 0 : if (thread_id.size() > kMaxThreadIdSize) {
# 47 : 0 : thread_id.resize(kMaxThreadIdSize);
# 48 : 0 : }
# 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 : 0 : constexpr const int kStackBufferSize = 512;
# 53 : 0 : char stack_buffer[kStackBufferSize];
# 54 : 0 : static_assert(sizeof(stack_buffer) == static_cast<size_t>(kStackBufferSize),
# 55 : 0 : "sizeof(char) is expected to be 1 in C++");
# 56 : :
# 57 : 0 : int dynamic_buffer_size = 0; // Computed in the first iteration.
# 58 [ # # ]: 0 : for (int iteration = 0; iteration < 2; ++iteration) {
# 59 : 0 : const int buffer_size =
# 60 [ # # ]: 0 : (iteration == 0) ? kStackBufferSize : dynamic_buffer_size;
# 61 : 0 : char* const buffer =
# 62 [ # # ]: 0 : (iteration == 0) ? stack_buffer : new char[dynamic_buffer_size];
# 63 : :
# 64 : : // Print the header into the buffer.
# 65 : 0 : int buffer_offset = snprintf(
# 66 : 0 : buffer, buffer_size, "%04d/%02d/%02d-%02d:%02d:%02d.%06d %s ",
# 67 : 0 : now_components.tm_year + 1900, now_components.tm_mon + 1,
# 68 : 0 : now_components.tm_mday, now_components.tm_hour, now_components.tm_min,
# 69 : 0 : now_components.tm_sec, static_cast<int>(now_timeval.tv_usec),
# 70 : 0 : 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 : 0 : assert(buffer_offset <= 28 + kMaxThreadIdSize);
# 76 : 0 : static_assert(28 + kMaxThreadIdSize < kStackBufferSize,
# 77 : 0 : "stack-allocated buffer may not fit the message header");
# 78 : 0 : assert(buffer_offset < buffer_size);
# 79 : :
# 80 : : // Print the message into the buffer.
# 81 : 0 : std::va_list arguments_copy;
# 82 : 0 : va_copy(arguments_copy, arguments);
# 83 : 0 : buffer_offset +=
# 84 : 0 : std::vsnprintf(buffer + buffer_offset, buffer_size - buffer_offset,
# 85 : 0 : format, arguments_copy);
# 86 : 0 : 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 [ # # ]: 0 : if (buffer_offset >= buffer_size - 1) {
# 91 : : // The message did not fit into the buffer.
# 92 [ # # ]: 0 : 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 : 0 : dynamic_buffer_size = buffer_offset + 2;
# 97 : 0 : continue;
# 98 : 0 : }
# 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 : 0 : assert(false);
# 104 : 0 : buffer_offset = buffer_size - 1;
# 105 : 0 : }
# 106 : :
# 107 : : // Add a newline if necessary.
# 108 [ # # ]: 0 : if (buffer[buffer_offset - 1] != '\n') {
# 109 : 0 : buffer[buffer_offset] = '\n';
# 110 : 0 : ++buffer_offset;
# 111 : 0 : }
# 112 : :
# 113 : 0 : assert(buffer_offset <= buffer_size);
# 114 : 0 : std::fwrite(buffer, 1, buffer_offset, fp_);
# 115 : 0 : std::fflush(fp_);
# 116 : :
# 117 [ # # ]: 0 : if (iteration != 0) {
# 118 : 0 : delete[] buffer;
# 119 : 0 : }
# 120 : 0 : break;
# 121 : 0 : }
# 122 : 0 : }
# 123 : :
# 124 : : private:
# 125 : : std::FILE* const fp_;
# 126 : : };
# 127 : :
# 128 : : } // namespace leveldb
# 129 : :
# 130 : : #endif // STORAGE_LEVELDB_UTIL_POSIX_LOGGER_H_
|