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. PosixLogger(std::FILE * fp)29 explicit PosixLogger(std::FILE* fp) : fp_(fp) { assert(fp != nullptr); } 30 ~PosixLogger()31 ~PosixLogger() override { std::fclose(fp_); } 32 Logv(const char * format,std::va_list arguments)33 void Logv(const char* format, std::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 = std::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 std::(v)snprintf. 102 // Fail 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