windows_logger.h 4.4 KB

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