mirror of
https://github.com/google/leveldb.git
synced 2025-01-03 04:30:12 +08:00
c69d33b0ec
This change adds a native Windows port (port_windows.h) and a Windows Env (WindowsEnv). Note1: "small" is defined when including <Windows.h> so some parameters were renamed to avoid conflict. Note2: leveldb::Env defines the method: "DeleteFile" which is also a constant defined when including <Windows.h>. The solution was to ensure this macro is defined in env.h which forces the function, when compiled, to be either DeleteFileA or DeleteFileW when building for MBCS or UNICODE respectively. This resolves #519 on GitHub. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=236364778
125 lines
4.4 KiB
C++
125 lines
4.4 KiB
C++
// Copyright (c) 2018 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 for the Windows platform.
|
|
|
|
#ifndef STORAGE_LEVELDB_UTIL_WINDOWS_LOGGER_H_
|
|
#define STORAGE_LEVELDB_UTIL_WINDOWS_LOGGER_H_
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <cassert>
|
|
#include <cstdarg>
|
|
#include <ctime>
|
|
#include <sstream>
|
|
#include <thread>
|
|
|
|
#include "leveldb/env.h"
|
|
|
|
namespace leveldb {
|
|
|
|
class WindowsLogger final : public Logger {
|
|
public:
|
|
WindowsLogger(HANDLE handle) : handle_(handle) {
|
|
assert(handle != INVALID_HANDLE_VALUE);
|
|
}
|
|
|
|
~WindowsLogger() override { ::CloseHandle(handle_); }
|
|
|
|
void Logv(const char* format, va_list arguments) override {
|
|
// Record the time as close to the Logv() call as possible.
|
|
SYSTEMTIME now_components;
|
|
::GetLocalTime(&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.
|
|
// TODO(costan): Sync this logger with another logger.
|
|
int buffer_offset = snprintf(
|
|
buffer, buffer_size, "%04d/%02d/%02d-%02d:%02d:%02d.%06d %s ",
|
|
now_components.wYear, now_components.wMonth, now_components.wDay,
|
|
now_components.wHour, now_components.wMinute, now_components.wSecond,
|
|
static_cast<int>(now_components.wMilliseconds * 1000),
|
|
std::stoull(thread_id));
|
|
|
|
// The header can be at most 28 characters (10 date + 15 time +
|
|
// 3 spacing) 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);
|
|
::WriteFile(handle_, buffer, buffer_offset, nullptr, nullptr);
|
|
|
|
if (iteration != 0) {
|
|
delete[] buffer;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
private:
|
|
HANDLE handle_;
|
|
};
|
|
|
|
} // namespace leveldb
|
|
|
|
#endif // STORAGE_LEVELDB_UTIL_WINDOWS_LOGGER_H_
|