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