]> git.proxmox.com Git - ceph.git/blob - ceph/src/rocksdb/port/win/win_logger.cc
import quincy beta 17.1.0
[ceph.git] / ceph / src / rocksdb / port / win / win_logger.cc
1 // Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
2 // This source code is licensed under both the GPLv2 (found in the
3 // COPYING file in the root directory) and Apache 2.0 License
4 // (found in the LICENSE.Apache file in the root directory).
5 //
6 // Copyright (c) 2011 The LevelDB Authors. All rights reserved.
7 // Use of this source code is governed by a BSD-style license that can be
8 // found in the LICENSE file. See the AUTHORS file for names of contributors.
9 //
10 // Logger implementation that can be shared by all environments
11 // where enough posix functionality is available.
12
13 #if defined(OS_WIN)
14
15 #include "port/win/win_logger.h"
16 #include "port/win/io_win.h"
17
18 #include <algorithm>
19 #include <stdio.h>
20 #include <time.h>
21 #include <fcntl.h>
22 #include <atomic>
23
24 #include "rocksdb/env.h"
25
26 #include "monitoring/iostats_context_imp.h"
27 #include "port/sys_time.h"
28
29 namespace ROCKSDB_NAMESPACE {
30
31 namespace port {
32
33 WinLogger::WinLogger(uint64_t (*gettid)(), Env* env, HANDLE file,
34 const InfoLogLevel log_level)
35 : Logger(log_level),
36 file_(file),
37 gettid_(gettid),
38 log_size_(0),
39 last_flush_micros_(0),
40 env_(env),
41 flush_pending_(false) {
42 assert(file_ != NULL);
43 assert(file_ != INVALID_HANDLE_VALUE);
44 }
45
46 void WinLogger::DebugWriter(const char* str, int len) {
47 assert(file_ != INVALID_HANDLE_VALUE);
48 DWORD bytesWritten = 0;
49 BOOL ret = WriteFile(file_, str, len, &bytesWritten, NULL);
50 if (ret == FALSE) {
51 std::string errSz = GetWindowsErrSz(GetLastError());
52 fprintf(stderr, "%s", errSz.c_str());
53 }
54 }
55
56 WinLogger::~WinLogger() { CloseInternal(); }
57
58 Status WinLogger::CloseImpl() {
59 return CloseInternal();
60 }
61
62 Status WinLogger::CloseInternal() {
63 Status s;
64 if (INVALID_HANDLE_VALUE != file_) {
65 BOOL ret = FlushFileBuffers(file_);
66 if (ret == 0) {
67 auto lastError = GetLastError();
68 s = IOErrorFromWindowsError("Failed to flush LOG on Close() ", lastError);
69 }
70 ret = CloseHandle(file_);
71 // On error the return value is zero
72 if (ret == 0 && s.ok()) {
73 auto lastError = GetLastError();
74 s = IOErrorFromWindowsError("Failed to flush LOG on Close() ", lastError);
75 }
76 file_ = INVALID_HANDLE_VALUE;
77 closed_ = true;
78 }
79 return s;
80 }
81
82 void WinLogger::Flush() {
83 assert(file_ != INVALID_HANDLE_VALUE);
84 if (flush_pending_) {
85 flush_pending_ = false;
86 // With Windows API writes go to OS buffers directly so no fflush needed
87 // unlike with C runtime API. We don't flush all the way to disk
88 // for perf reasons.
89 }
90
91 last_flush_micros_ = env_->NowMicros();
92 }
93
94 void WinLogger::Logv(const char* format, va_list ap) {
95 IOSTATS_TIMER_GUARD(logger_nanos);
96 assert(file_ != INVALID_HANDLE_VALUE);
97
98 const uint64_t thread_id = (*gettid_)();
99
100 // We try twice: the first time with a fixed-size stack allocated buffer,
101 // and the second time with a much larger dynamically allocated buffer.
102 char buffer[500];
103 std::unique_ptr<char[]> largeBuffer;
104 for (int iter = 0; iter < 2; ++iter) {
105 char* base;
106 int bufsize;
107 if (iter == 0) {
108 bufsize = sizeof(buffer);
109 base = buffer;
110 } else {
111 bufsize = 30000;
112 largeBuffer.reset(new char[bufsize]);
113 base = largeBuffer.get();
114 }
115
116 char* p = base;
117 char* limit = base + bufsize;
118
119 struct timeval now_tv;
120 gettimeofday(&now_tv, nullptr);
121 const time_t seconds = now_tv.tv_sec;
122 struct tm t;
123 localtime_s(&t, &seconds);
124 p += snprintf(p, limit - p, "%04d/%02d/%02d-%02d:%02d:%02d.%06d %llx ",
125 t.tm_year + 1900, t.tm_mon + 1, t.tm_mday, t.tm_hour,
126 t.tm_min, t.tm_sec, static_cast<int>(now_tv.tv_usec),
127 static_cast<long long unsigned int>(thread_id));
128
129 // Print the message
130 if (p < limit) {
131 va_list backup_ap;
132 va_copy(backup_ap, ap);
133 int done = vsnprintf(p, limit - p, format, backup_ap);
134 if (done > 0) {
135 p += done;
136 } else {
137 continue;
138 }
139 va_end(backup_ap);
140 }
141
142 // Truncate to available space if necessary
143 if (p >= limit) {
144 if (iter == 0) {
145 continue; // Try again with larger buffer
146 } else {
147 p = limit - 1;
148 }
149 }
150
151 // Add newline if necessary
152 if (p == base || p[-1] != '\n') {
153 *p++ = '\n';
154 }
155
156 assert(p <= limit);
157 const size_t write_size = p - base;
158
159 DWORD bytesWritten = 0;
160 BOOL ret = WriteFile(file_, base, static_cast<DWORD>(write_size),
161 &bytesWritten, NULL);
162 if (ret == FALSE) {
163 std::string errSz = GetWindowsErrSz(GetLastError());
164 fprintf(stderr, "%s", errSz.c_str());
165 }
166
167 flush_pending_ = true;
168 assert((bytesWritten == write_size) || (ret == FALSE));
169 if (bytesWritten > 0) {
170 log_size_ += write_size;
171 }
172
173 uint64_t now_micros =
174 static_cast<uint64_t>(now_tv.tv_sec) * 1000000 + now_tv.tv_usec;
175 if (now_micros - last_flush_micros_ >= flush_every_seconds_ * 1000000) {
176 flush_pending_ = false;
177 // With Windows API writes go to OS buffers directly so no fflush needed
178 // unlike with C runtime API. We don't flush all the way to disk
179 // for perf reasons.
180 last_flush_micros_ = now_micros;
181 }
182 break;
183 }
184 }
185
186 size_t WinLogger::GetLogFileSize() const { return log_size_; }
187
188 }
189
190 } // namespace ROCKSDB_NAMESPACE
191
192 #endif