| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192 |
- // Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
- // This source code is licensed under both the GPLv2 (found in the
- // COPYING file in the root directory) and Apache 2.0 License
- // (found in the LICENSE.Apache file in the root directory).
- //
- // 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.
- #include "port/win/win_logger.h"
- #include "port/win/io_win.h"
- #include <algorithm>
- #include <stdio.h>
- #include <time.h>
- #include <fcntl.h>
- #include <atomic>
- #include "rocksdb/env.h"
- #include "monitoring/iostats_context_imp.h"
- #include "port/sys_time.h"
- namespace ROCKSDB_NAMESPACE {
- namespace port {
- WinLogger::WinLogger(uint64_t (*gettid)(), Env* env, HANDLE file,
- const InfoLogLevel log_level)
- : Logger(log_level),
- file_(file),
- gettid_(gettid),
- log_size_(0),
- last_flush_micros_(0),
- env_(env),
- flush_pending_(false) {
- assert(file_ != NULL);
- assert(file_ != INVALID_HANDLE_VALUE);
- }
- void WinLogger::DebugWriter(const char* str, int len) {
- assert(file_ != INVALID_HANDLE_VALUE);
- DWORD bytesWritten = 0;
- BOOL ret = WriteFile(file_, str, len, &bytesWritten, NULL);
- if (ret == FALSE) {
- std::string errSz = GetWindowsErrSz(GetLastError());
- fprintf(stderr, errSz.c_str());
- }
- }
- WinLogger::~WinLogger() {
- CloseInternal();
- }
- Status WinLogger::CloseImpl() {
- return CloseInternal();
- }
- Status WinLogger::CloseInternal() {
- Status s;
- if (INVALID_HANDLE_VALUE != file_) {
- BOOL ret = FlushFileBuffers(file_);
- if (ret == 0) {
- auto lastError = GetLastError();
- s = IOErrorFromWindowsError("Failed to flush LOG on Close() ",
- lastError);
- }
- ret = CloseHandle(file_);
- // On error the return value is zero
- if (ret == 0 && s.ok()) {
- auto lastError = GetLastError();
- s = IOErrorFromWindowsError("Failed to flush LOG on Close() ",
- lastError);
- }
- file_ = INVALID_HANDLE_VALUE;
- closed_ = true;
- }
- return s;
- }
- void WinLogger::Flush() {
- assert(file_ != INVALID_HANDLE_VALUE);
- if (flush_pending_) {
- flush_pending_ = false;
- // With Windows API writes go to OS buffers directly so no fflush needed
- // unlike with C runtime API. We don't flush all the way to disk
- // for perf reasons.
- }
- last_flush_micros_ = env_->NowMicros();
- }
- void WinLogger::Logv(const char* format, va_list ap) {
- IOSTATS_TIMER_GUARD(logger_nanos);
- assert(file_ != INVALID_HANDLE_VALUE);
- const uint64_t thread_id = (*gettid_)();
- // We try twice: the first time with a fixed-size stack allocated buffer,
- // and the second time with a much larger dynamically allocated buffer.
- char buffer[500];
- std::unique_ptr<char[]> largeBuffer;
- for (int iter = 0; iter < 2; ++iter) {
- char* base;
- int bufsize;
- if (iter == 0) {
- bufsize = sizeof(buffer);
- base = buffer;
- } else {
- bufsize = 30000;
- largeBuffer.reset(new char[bufsize]);
- base = largeBuffer.get();
- }
- char* p = base;
- char* limit = base + bufsize;
- struct timeval now_tv;
- gettimeofday(&now_tv, nullptr);
- const time_t seconds = now_tv.tv_sec;
- struct tm t;
- localtime_s(&t, &seconds);
- p += snprintf(p, limit - p, "%04d/%02d/%02d-%02d:%02d:%02d.%06d %llx ",
- t.tm_year + 1900, t.tm_mon + 1, t.tm_mday, t.tm_hour,
- t.tm_min, t.tm_sec, static_cast<int>(now_tv.tv_usec),
- static_cast<long long unsigned int>(thread_id));
- // Print the message
- if (p < limit) {
- va_list backup_ap;
- va_copy(backup_ap, ap);
- int done = vsnprintf(p, limit - p, format, backup_ap);
- if (done > 0) {
- p += done;
- } else {
- continue;
- }
- va_end(backup_ap);
- }
- // Truncate to available space if necessary
- if (p >= limit) {
- if (iter == 0) {
- continue; // Try again with larger buffer
- } else {
- p = limit - 1;
- }
- }
- // Add newline if necessary
- if (p == base || p[-1] != '\n') {
- *p++ = '\n';
- }
- assert(p <= limit);
- const size_t write_size = p - base;
- DWORD bytesWritten = 0;
- BOOL ret = WriteFile(file_, base, static_cast<DWORD>(write_size),
- &bytesWritten, NULL);
- if (ret == FALSE) {
- std::string errSz = GetWindowsErrSz(GetLastError());
- fprintf(stderr, errSz.c_str());
- }
- flush_pending_ = true;
- assert((bytesWritten == write_size) || (ret == FALSE));
- if (bytesWritten > 0) {
- log_size_ += write_size;
- }
- uint64_t now_micros =
- static_cast<uint64_t>(now_tv.tv_sec) * 1000000 + now_tv.tv_usec;
- if (now_micros - last_flush_micros_ >= flush_every_seconds_ * 1000000) {
- flush_pending_ = false;
- // With Windows API writes go to OS buffers directly so no fflush needed
- // unlike with C runtime API. We don't flush all the way to disk
- // for perf reasons.
- last_flush_micros_ = now_micros;
- }
- break;
- }
- }
- size_t WinLogger::GetLogFileSize() const { return log_size_; }
- }
- } // namespace ROCKSDB_NAMESPACE
|