summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHenrik Edin <henrik.edin@mongodb.com>2020-02-06 08:31:38 -0500
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2020-02-07 21:46:37 +0000
commited96ae9dedac361207c747299448f6b0631ff00f (patch)
tree8f013439c934e87f391f5c18c6f1dd8a95c45460
parent48e9e0edecd6a0359e809ba041c898244cb0cb01 (diff)
downloadmongo-ed96ae9dedac361207c747299448f6b0631ff00f.tar.gz
SERVER-45989 Log files are opened in shared mode on Windows.
Added fstream abstraction for Windows that does this. Log rotation is now behaving like the old log system create mode 100644 src/mongo/logv2/file_rotate_sink.cpp create mode 100644 src/mongo/logv2/file_rotate_sink.h create mode 100644 src/mongo/logv2/shared_access_fstream.cpp create mode 100644 src/mongo/logv2/shared_access_fstream.h
-rw-r--r--etc/evergreen.yml1
-rw-r--r--jstests/noPassthroughWithMongod/logpath.js1
-rw-r--r--src/mongo/SConscript2
-rw-r--r--src/mongo/db/commands/generic_servers.cpp2
-rw-r--r--src/mongo/logv2/file_rotate_sink.cpp128
-rw-r--r--src/mongo/logv2/file_rotate_sink.h57
-rw-r--r--src/mongo/logv2/log_domain_global.cpp56
-rw-r--r--src/mongo/logv2/log_domain_global.h2
-rw-r--r--src/mongo/logv2/shared_access_fstream.cpp296
-rw-r--r--src/mongo/logv2/shared_access_fstream.h137
-rw-r--r--src/mongo/util/log.cpp20
-rw-r--r--src/mongo/util/log.h2
-rw-r--r--src/mongo/util/signal_handlers.cpp2
-rw-r--r--src/mongo/util/text.cpp23
-rw-r--r--src/mongo/util/text.h1
15 files changed, 673 insertions, 57 deletions
diff --git a/etc/evergreen.yml b/etc/evergreen.yml
index b499595afd7..92f572029cf 100644
--- a/etc/evergreen.yml
+++ b/etc/evergreen.yml
@@ -9285,7 +9285,6 @@ buildvariants:
# spawning a large number of linker processes.
num_scons_link_jobs_available: $(( $(grep -c ^processor /proc/cpuinfo) / 4 ))
python: '/cygdrive/c/python/python37/python.exe'
- test_flags: --excludeWithAnyTags=SERVER-45989
ext: zip
scons_cache_scope: shared
multiversion_platform: windows
diff --git a/jstests/noPassthroughWithMongod/logpath.js b/jstests/noPassthroughWithMongod/logpath.js
index 7f7aecd0bf6..bb39282871f 100644
--- a/jstests/noPassthroughWithMongod/logpath.js
+++ b/jstests/noPassthroughWithMongod/logpath.js
@@ -1,5 +1,4 @@
// check replica set authentication
-// @tags: [SERVER-45989]
var name = "logpath";
var token = "logpath_token";
diff --git a/src/mongo/SConscript b/src/mongo/SConscript
index c94521e3886..f1d4f64e9e3 100644
--- a/src/mongo/SConscript
+++ b/src/mongo/SConscript
@@ -112,6 +112,7 @@ baseEnv.Library(
'logv2/attributes.cpp',
'logv2/bson_formatter.cpp',
'logv2/console.cpp',
+ 'logv2/file_rotate_sink.cpp',
'logv2/json_formatter.cpp',
'logv2/log_component.cpp',
'logv2/log_component_settings.cpp',
@@ -123,6 +124,7 @@ baseEnv.Library(
'logv2/log_severity.cpp',
'logv2/log_tag.cpp',
'logv2/plain_formatter.cpp',
+ 'logv2/shared_access_fstream.cpp',
'logv2/ramlog.cpp',
'logv2/text_formatter.cpp',
'platform/decimal128.cpp',
diff --git a/src/mongo/db/commands/generic_servers.cpp b/src/mongo/db/commands/generic_servers.cpp
index 5ddd102c366..b398e6d59f2 100644
--- a/src/mongo/db/commands/generic_servers.cpp
+++ b/src/mongo/db/commands/generic_servers.cpp
@@ -196,7 +196,7 @@ public:
const std::string& ns,
const BSONObj& cmdObj,
BSONObjBuilder& result) {
- bool didRotate = rotateLogs(serverGlobalParams.logRenameOnRotate, logV2Enabled());
+ bool didRotate = rotateLogs(serverGlobalParams.logRenameOnRotate);
if (didRotate)
logProcessDetailsForLogRotate(opCtx->getServiceContext());
return didRotate;
diff --git a/src/mongo/logv2/file_rotate_sink.cpp b/src/mongo/logv2/file_rotate_sink.cpp
new file mode 100644
index 00000000000..c0ca2ac91ee
--- /dev/null
+++ b/src/mongo/logv2/file_rotate_sink.cpp
@@ -0,0 +1,128 @@
+/**
+ * Copyright (C) 2020-present MongoDB, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the Server Side Public License, version 1,
+ * as published by MongoDB, Inc.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * Server Side Public License for more details.
+ *
+ * You should have received a copy of the Server Side Public License
+ * along with this program. If not, see
+ * <http://www.mongodb.com/licensing/server-side-public-license>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the Server Side Public License in all respects for
+ * all of the code used other than as permitted herein. If you modify file(s)
+ * with this exception, you may extend this exception to your version of the
+ * file(s), but you are not obligated to do so. If you do not wish to do so,
+ * delete this exception statement from your version. If you delete this
+ * exception statement from all source files in the program, then also delete
+ * it in the license file.
+ */
+
+#include "mongo/logv2/file_rotate_sink.h"
+
+#include <boost/filesystem/operations.hpp>
+#include <boost/make_shared.hpp>
+#include <fmt/format.h>
+#include <fstream>
+
+#include "mongo/logv2/shared_access_fstream.h"
+#include "mongo/util/string_map.h"
+
+
+namespace mongo::logv2 {
+namespace {
+#if _WIN32
+using stream_t = Win32SharedAccessOfstream;
+#else
+using stream_t = std::ofstream;
+#endif
+
+StatusWith<boost::shared_ptr<stream_t>> openFile(const std::string& filename, bool append) {
+ std::ios_base::openmode mode = std::ios_base::out;
+ if (append)
+ mode |= std::ios_base::app;
+ else
+ mode |= std::ios_base::trunc;
+ auto file = boost::make_shared<stream_t>(filename, mode);
+ if (file->fail())
+ return Status(ErrorCodes::FileNotOpen, fmt::format("Failed to open {}", filename));
+ return file;
+}
+} // namespace
+
+struct FileRotateSink::Impl {
+ StringMap<boost::shared_ptr<stream_t>> files;
+};
+
+FileRotateSink::FileRotateSink() : _impl(std::make_unique<Impl>()) {}
+FileRotateSink::~FileRotateSink() {}
+
+Status FileRotateSink::addFile(const std::string& filename, bool append) {
+ auto statusWithFile = openFile(filename, append);
+ if (statusWithFile.isOK()) {
+ add_stream(statusWithFile.getValue());
+ _impl->files[filename] = statusWithFile.getValue();
+ }
+
+ return statusWithFile.getStatus();
+}
+void FileRotateSink::removeFile(const std::string& filename) {
+ auto it = _impl->files.find(filename);
+ if (it != _impl->files.cend()) {
+ remove_stream(it->second);
+ _impl->files.erase(it);
+ }
+}
+
+Status FileRotateSink::rotate(bool rename, StringData renameSuffix) {
+ for (auto& file : _impl->files) {
+ const std::string& filename = file.first;
+ if (rename) {
+ std::string renameTarget = filename + renameSuffix;
+ try {
+ if (boost::filesystem::exists(renameTarget)) {
+ return Status(
+ ErrorCodes::FileRenameFailed,
+ fmt::format("Renaming file {} to {} failed; destination already exists",
+ filename,
+ renameTarget));
+ }
+ } catch (const std::exception& e) {
+ return Status(ErrorCodes::FileRenameFailed,
+ fmt::format("Renaming file {} to {} failed; Cannot verify whether "
+ "destination already exists: {}",
+ filename,
+ renameTarget,
+ e.what()));
+ }
+
+ boost::system::error_code ec;
+ boost::filesystem::rename(filename, renameTarget, ec);
+ if (ec) {
+ return Status(
+ ErrorCodes::FileRenameFailed,
+ fmt::format(
+ "Failed to rename {} to {}: {}", filename, renameTarget, ec.message()));
+ }
+ }
+
+ auto newFile = openFile(filename, false);
+ if (newFile.isOK()) {
+ file.second = newFile.getValue();
+ }
+ return newFile.getStatus();
+ }
+
+ return Status::OK();
+}
+
+} // namespace mongo::logv2
diff --git a/src/mongo/logv2/file_rotate_sink.h b/src/mongo/logv2/file_rotate_sink.h
new file mode 100644
index 00000000000..56f12215750
--- /dev/null
+++ b/src/mongo/logv2/file_rotate_sink.h
@@ -0,0 +1,57 @@
+/**
+ * Copyright (C) 2020-present MongoDB, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the Server Side Public License, version 1,
+ * as published by MongoDB, Inc.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * Server Side Public License for more details.
+ *
+ * You should have received a copy of the Server Side Public License
+ * along with this program. If not, see
+ * <http://www.mongodb.com/licensing/server-side-public-license>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the Server Side Public License in all respects for
+ * all of the code used other than as permitted herein. If you modify file(s)
+ * with this exception, you may extend this exception to your version of the
+ * file(s), but you are not obligated to do so. If you do not wish to do so,
+ * delete this exception statement from your version. If you delete this
+ * exception statement from all source files in the program, then also delete
+ * it in the license file.
+ */
+
+#pragma once
+
+#include <boost/log/sinks/text_ostream_backend.hpp>
+#include <memory>
+#include <string>
+
+#include "mongo/base/status.h"
+
+namespace mongo::logv2 {
+// boost::log backend sink to provide MongoDB style file rotation.
+// Uses custom stream type to open log files with shared access on Windows, somthing the built-in
+// boost file rotation sink does not do.
+class FileRotateSink : public boost::log::sinks::text_ostream_backend {
+public:
+ FileRotateSink();
+ ~FileRotateSink();
+
+ Status addFile(const std::string& filename, bool append);
+ void removeFile(const std::string& filename);
+
+ Status rotate(bool rename, StringData renameSuffix);
+
+private:
+ struct Impl;
+ std::unique_ptr<Impl> _impl;
+};
+
+} // namespace mongo::logv2
diff --git a/src/mongo/logv2/log_domain_global.cpp b/src/mongo/logv2/log_domain_global.cpp
index bc9d512f176..d80be9f65d7 100644
--- a/src/mongo/logv2/log_domain_global.cpp
+++ b/src/mongo/logv2/log_domain_global.cpp
@@ -32,9 +32,11 @@
#include "mongo/logv2/component_settings_filter.h"
#include "mongo/logv2/composite_backend.h"
#include "mongo/logv2/console.h"
+#include "mongo/logv2/file_rotate_sink.h"
#include "mongo/logv2/json_formatter.h"
#include "mongo/logv2/log_source.h"
#include "mongo/logv2/ramlog_sink.h"
+#include "mongo/logv2/shared_access_fstream.h"
#include "mongo/logv2/tagged_severity_filter.h"
#include "mongo/logv2/text_formatter.h"
@@ -45,35 +47,6 @@
namespace mongo {
namespace logv2 {
-namespace {
-
-class RotateCollector : public boost::log::sinks::file::collector {
-public:
- explicit RotateCollector(LogDomainGlobal::ConfigurationOptions const& options)
- : _mode{options._fileRotationMode} {}
-
- void store_file(boost::filesystem::path const& file) override {
- if (_mode == LogDomainGlobal::ConfigurationOptions::RotationMode::kRename) {
- auto renameTarget = file.string() + "." + terseCurrentTime(false);
- boost::system::error_code ec;
- boost::filesystem::rename(file, renameTarget, ec);
- if (ec) {
- // throw here or propagate this error in another way?
- }
- }
- }
-
- uintmax_t scan_for_files(boost::log::sinks::file::scan_method,
- boost::filesystem::path const&,
- unsigned int*) override {
- return 0;
- }
-
-private:
- LogDomainGlobal::ConfigurationOptions::RotationMode _mode;
-};
-
-} // namespace
void LogDomainGlobal::ConfigurationOptions::makeDisabled() {
_consoleEnabled = false;
@@ -86,12 +59,11 @@ struct LogDomainGlobal::Impl {
typedef CompositeBackend<boost::log::sinks::syslog_backend, RamLogSink, RamLogSink>
SyslogBackend;
#endif
- typedef CompositeBackend<boost::log::sinks::text_file_backend, RamLogSink, RamLogSink>
- RotatableFileBackend;
+ typedef CompositeBackend<FileRotateSink, RamLogSink, RamLogSink> RotatableFileBackend;
Impl(LogDomainGlobal& parent);
Status configure(LogDomainGlobal::ConfigurationOptions const& options);
- Status rotate();
+ Status rotate(bool rename, StringData renameSuffix);
LogDomainGlobal& _parent;
LogComponentSettings _settings;
@@ -171,16 +143,16 @@ Status LogDomainGlobal::Impl::configure(LogDomainGlobal::ConfigurationOptions co
if (options._fileEnabled) {
auto backend = boost::make_shared<RotatableFileBackend>(
- boost::make_shared<boost::log::sinks::text_file_backend>(
- boost::log::keywords::file_name = options._filePath),
+ boost::make_shared<FileRotateSink>(),
boost::make_shared<RamLogSink>(RamLog::get("global")),
boost::make_shared<RamLogSink>(RamLog::get("startupWarnings")));
-
+ Status ret = backend->lockedBackend<0>()->addFile(
+ options._filePath,
+ options._fileOpenMode == ConfigurationOptions::OpenMode::kAppend ? true : false);
+ if (!ret.isOK())
+ return ret;
backend->lockedBackend<0>()->auto_flush(true);
- backend->lockedBackend<0>()->set_file_collector(
- boost::make_shared<RotateCollector>(options));
-
_rotatableFileSink =
boost::make_shared<boost::log::sinks::unlocked_sink<RotatableFileBackend>>(backend);
_rotatableFileSink->set_filter(ComponentSettingsFilter(_parent, _settings));
@@ -214,10 +186,10 @@ Status LogDomainGlobal::Impl::configure(LogDomainGlobal::ConfigurationOptions co
return Status::OK();
}
-Status LogDomainGlobal::Impl::rotate() {
+Status LogDomainGlobal::Impl::rotate(bool rename, StringData renameSuffix) {
if (_rotatableFileSink) {
auto backend = _rotatableFileSink->locked_backend()->lockedBackend<0>();
- backend->rotate_file();
+ return backend->rotate(rename, renameSuffix);
}
return Status::OK();
}
@@ -239,8 +211,8 @@ Status LogDomainGlobal::configure(LogDomainGlobal::ConfigurationOptions const& o
return _impl->configure(options);
}
-Status LogDomainGlobal::rotate() {
- return _impl->rotate();
+Status LogDomainGlobal::rotate(bool rename, StringData renameSuffix) {
+ return _impl->rotate(rename, renameSuffix);
}
LogComponentSettings& LogDomainGlobal::settings() {
diff --git a/src/mongo/logv2/log_domain_global.h b/src/mongo/logv2/log_domain_global.h
index afc943db4d3..03a09562c07 100644
--- a/src/mongo/logv2/log_domain_global.h
+++ b/src/mongo/logv2/log_domain_global.h
@@ -58,7 +58,7 @@ public:
LogSource& source() override;
Status configure(ConfigurationOptions const& options);
- Status rotate();
+ Status rotate(bool rename, StringData renameSuffix);
LogComponentSettings& settings();
diff --git a/src/mongo/logv2/shared_access_fstream.cpp b/src/mongo/logv2/shared_access_fstream.cpp
new file mode 100644
index 00000000000..2179e27fe6c
--- /dev/null
+++ b/src/mongo/logv2/shared_access_fstream.cpp
@@ -0,0 +1,296 @@
+/**
+ * Copyright (C) 2020-present MongoDB, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the Server Side Public License, version 1,
+ * as published by MongoDB, Inc.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * Server Side Public License for more details.
+ *
+ * You should have received a copy of the Server Side Public License
+ * along with this program. If not, see
+ * <http://www.mongodb.com/licensing/server-side-public-license>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the Server Side Public License in all respects for
+ * all of the code used other than as permitted herein. If you modify file(s)
+ * with this exception, you may extend this exception to your version of the
+ * file(s), but you are not obligated to do so. If you do not wish to do so,
+ * delete this exception statement from your version. If you delete this
+ * exception statement from all source files in the program, then also delete
+ * it in the license file.
+ */
+
+#include "mongo/platform/basic.h"
+
+#include "mongo/logv2/shared_access_fstream.h"
+
+#if defined(_WIN32) && defined(_MSC_VER)
+
+#include <fcntl.h>
+#include <filesystem>
+#include <io.h>
+
+#include "mongo/util/text.h"
+
+namespace mongo {
+FILE* Win32SharedAccessFileDescriptor::_open(const wchar_t* filename,
+ std::ios_base::openmode mode,
+ bool sharedWriteAccess) {
+ const char* fdmode = nullptr;
+ int openflags = 0;
+ uint32_t desiredAccess = 0;
+ uint32_t creationDisposition = 0;
+ uint32_t shareMode = FILE_SHARE_DELETE | FILE_SHARE_READ;
+ if (sharedWriteAccess)
+ shareMode |= FILE_SHARE_WRITE;
+
+ // The combinations are taken from https://en.cppreference.com/w/cpp/io/basic_filebuf/open
+ auto modeWithoutAte = mode & ~std::ios_base::ate;
+ if (modeWithoutAte == std::ios_base::in) {
+ fdmode = "r";
+ creationDisposition = OPEN_EXISTING;
+ openflags |= _O_RDONLY;
+ } else if (modeWithoutAte == std::ios_base::out ||
+ modeWithoutAte == (std::ios_base::out | std::ios_base::trunc)) {
+ fdmode = "w";
+ creationDisposition = CREATE_ALWAYS;
+ openflags |= _O_WRONLY;
+ } else if (modeWithoutAte == std::ios_base::app ||
+ modeWithoutAte == (std::ios_base::out | std::ios_base::app)) {
+
+ fdmode = "a";
+ creationDisposition = OPEN_ALWAYS;
+ openflags |= _O_WRONLY | _O_APPEND;
+ } else if (modeWithoutAte == (std::ios_base::out | std::ios_base::in)) {
+
+ fdmode = "r+";
+ creationDisposition = OPEN_EXISTING;
+ openflags |= _O_RDWR;
+ } else if (modeWithoutAte == (std::ios_base::out | std::ios_base::in | std::ios_base::trunc)) {
+
+ fdmode = "w+";
+ creationDisposition = CREATE_ALWAYS;
+ openflags |= _O_RDWR;
+ } else if (modeWithoutAte == (std::ios_base::out | std::ios_base::in | std::ios_base::app) ||
+ modeWithoutAte == (std::ios_base::in | std::ios_base::app)) {
+ fdmode = "a+";
+ creationDisposition = OPEN_ALWAYS;
+ openflags |= _O_RDWR;
+ } else if (modeWithoutAte == (std::ios_base::binary | std::ios_base::in)) {
+
+ fdmode = "rb";
+ creationDisposition = OPEN_EXISTING;
+ openflags |= _O_RDONLY;
+ } else if (modeWithoutAte == (std::ios_base::binary | std::ios_base::out) ||
+ modeWithoutAte ==
+ (std::ios_base::binary | std::ios_base::out | std::ios_base::trunc)) {
+
+ fdmode = "wb";
+ creationDisposition = CREATE_ALWAYS;
+ openflags |= _O_WRONLY;
+ } else if (modeWithoutAte == (std::ios_base::binary | std::ios_base::app) ||
+ modeWithoutAte ==
+ (std::ios_base::binary | std::ios_base::out | std::ios_base::app)) {
+
+ fdmode = "ab";
+ creationDisposition = OPEN_ALWAYS;
+ openflags |= _O_WRONLY;
+ } else if (modeWithoutAte == (std::ios_base::binary | std::ios_base::out | std::ios_base::in)) {
+
+ fdmode = "r+b";
+ creationDisposition = OPEN_EXISTING;
+ openflags |= _O_RDWR;
+ } else if (modeWithoutAte ==
+ (std::ios_base::binary | std::ios_base::out | std::ios_base::in |
+ std::ios_base::trunc)) {
+
+ fdmode = "w+b";
+ creationDisposition = CREATE_ALWAYS;
+ openflags |= _O_RDWR;
+ } else if (modeWithoutAte ==
+ (std::ios_base::binary | std::ios_base::out | std::ios_base::in |
+ std::ios_base::app) ||
+ modeWithoutAte == (std::ios_base::binary | std::ios_base::in | std::ios_base::app)) {
+
+ fdmode = "a+b";
+ creationDisposition = OPEN_ALWAYS;
+ openflags |= _O_RDWR;
+ } else
+ return nullptr;
+
+ if (mode & std::ios_base::in)
+ desiredAccess |= GENERIC_READ;
+ if (mode & std::ios_base::out || mode & std::ios_base::app)
+ desiredAccess |= GENERIC_WRITE;
+
+ if (mode & std::ios_base::binary)
+ openflags |= _O_BINARY;
+ else
+ openflags |= _O_U8TEXT;
+
+ if (mode & std::ios_base::trunc)
+ openflags |= _O_TRUNC;
+
+ // Open file with share access to get a Windows HANDLE
+ HANDLE handle = CreateFileW(filename, // lpFileName
+ desiredAccess, // dwDesiredAccess
+ shareMode, // dwShareMode
+ nullptr, // lpSecurityAttributes
+ creationDisposition, // dwCreationDisposition
+ FILE_ATTRIBUTE_NORMAL, // dwFlagsAndAttributes
+ nullptr // hTemplateFile
+ );
+ if (handle == INVALID_HANDLE_VALUE) {
+ return nullptr;
+ }
+
+ LARGE_INTEGER zero;
+ zero.QuadPart = 0LL;
+ if (mode & std::ios_base::ate ||
+ ((mode & std::ios_base::app) && (openflags & _O_APPEND) == 0)) {
+ SetFilePointerEx(handle, zero, nullptr, FILE_END);
+ }
+
+ if (mode & std::ios_base::trunc) {
+ SetEndOfFile(handle);
+ }
+
+ // Convert the HANDLE to a file descriptor
+ int fd = _open_osfhandle(reinterpret_cast<intptr_t>(handle), openflags);
+ if (fd == -1) {
+ CloseHandle(handle);
+ return nullptr;
+ }
+
+ // Open the file descriptor to get a FILE*
+ return _fdopen(fd, fdmode);
+}
+
+FILE* Win32SharedAccessFileDescriptor::_open(StringData filename,
+ std::ios_base::openmode mode,
+ bool sharedWriteAccess) {
+ return _open(toWideStringFromStringData(filename).c_str(), mode, sharedWriteAccess);
+}
+
+Win32SharedAccessFileDescriptor::Win32SharedAccessFileDescriptor(const wchar_t* filename,
+ std::ios_base::openmode mode,
+ bool sharedWriteAccess)
+ : _file(_open(filename, mode, sharedWriteAccess)) {}
+Win32SharedAccessFileDescriptor::Win32SharedAccessFileDescriptor(const char* filename,
+ std::ios_base::openmode mode,
+ bool sharedWriteAccess)
+ : _file(_open(filename, mode, sharedWriteAccess)) {}
+Win32SharedAccessFileDescriptor::Win32SharedAccessFileDescriptor(const std::string& filename,
+ std::ios_base::openmode mode,
+ bool sharedWriteAccess)
+ : _file(_open(filename.c_str(), mode, sharedWriteAccess)) {}
+Win32SharedAccessFileDescriptor::Win32SharedAccessFileDescriptor(
+ const std::filesystem::path& filename, std::ios_base::openmode mode, bool sharedWriteAccess)
+ : _file(_open(filename.c_str(), mode, sharedWriteAccess)) {}
+
+Win32SharedAccessOfstream::Win32SharedAccessOfstream(const char* filename,
+ std::ios_base::openmode mode,
+ bool sharedWriteAccess)
+ : Win32SharedAccessFileDescriptor(filename, mode, sharedWriteAccess), std::ofstream(_file) {
+ if (!_file)
+ setstate(failbit);
+}
+
+Win32SharedAccessOfstream::Win32SharedAccessOfstream(const wchar_t* filename,
+ std::ios_base::openmode mode,
+ bool sharedWriteAccess)
+ : Win32SharedAccessFileDescriptor(filename, mode, sharedWriteAccess), std::ofstream(_file) {
+ if (!_file)
+ setstate(failbit);
+}
+
+Win32SharedAccessOfstream::Win32SharedAccessOfstream(const std::string& filename,
+ std::ios_base::openmode mode,
+ bool sharedWriteAccess)
+ : Win32SharedAccessFileDescriptor(filename, mode, sharedWriteAccess), std::ofstream(_file) {
+ if (!_file)
+ setstate(failbit);
+}
+
+Win32SharedAccessOfstream::Win32SharedAccessOfstream(const std::filesystem::path& filename,
+ std::ios_base::openmode mode,
+ bool sharedWriteAccess)
+ : Win32SharedAccessFileDescriptor(filename, mode, sharedWriteAccess), std::ofstream(_file) {
+ if (!_file)
+ setstate(failbit);
+}
+
+Win32SharedAccessIfstream::Win32SharedAccessIfstream(const char* filename,
+ std::ios_base::openmode mode,
+ bool sharedWriteAccess)
+ : Win32SharedAccessFileDescriptor(filename, mode, sharedWriteAccess), std::ifstream(_file) {
+ if (!_file)
+ setstate(failbit);
+}
+
+Win32SharedAccessIfstream::Win32SharedAccessIfstream(const wchar_t* filename,
+ std::ios_base::openmode mode,
+ bool sharedWriteAccess)
+ : Win32SharedAccessFileDescriptor(filename, mode, sharedWriteAccess), std::ifstream(_file) {
+ if (!_file)
+ setstate(failbit);
+}
+
+Win32SharedAccessIfstream::Win32SharedAccessIfstream(const std::string& filename,
+ std::ios_base::openmode mode,
+ bool sharedWriteAccess)
+ : Win32SharedAccessFileDescriptor(filename, mode, sharedWriteAccess), std::ifstream(_file) {
+ if (!_file)
+ setstate(failbit);
+}
+
+Win32SharedAccessIfstream::Win32SharedAccessIfstream(const std::filesystem::path& filename,
+ std::ios_base::openmode mode,
+ bool sharedWriteAccess)
+ : Win32SharedAccessFileDescriptor(filename, mode, sharedWriteAccess), std::ifstream(_file) {
+ if (!_file)
+ setstate(failbit);
+}
+
+Win32SharedAccessFstream::Win32SharedAccessFstream(const char* filename,
+ std::ios_base::openmode mode,
+ bool sharedWriteAccess)
+ : Win32SharedAccessFileDescriptor(filename, mode, sharedWriteAccess), std::fstream(_file) {
+ if (!_file)
+ setstate(failbit);
+}
+
+Win32SharedAccessFstream::Win32SharedAccessFstream(const wchar_t* filename,
+ std::ios_base::openmode mode,
+ bool sharedWriteAccess)
+ : Win32SharedAccessFileDescriptor(filename, mode, sharedWriteAccess), std::fstream(_file) {
+ if (!_file)
+ setstate(failbit);
+}
+
+Win32SharedAccessFstream::Win32SharedAccessFstream(const std::string& filename,
+ std::ios_base::openmode mode,
+ bool sharedWriteAccess)
+ : Win32SharedAccessFileDescriptor(filename, mode, sharedWriteAccess), std::fstream(_file) {
+ if (!_file)
+ setstate(failbit);
+}
+
+Win32SharedAccessFstream::Win32SharedAccessFstream(const std::filesystem::path& filename,
+ std::ios_base::openmode mode,
+ bool sharedWriteAccess)
+ : Win32SharedAccessFileDescriptor(filename, mode, sharedWriteAccess), std::fstream(_file) {
+ if (!_file)
+ setstate(failbit);
+}
+
+} // namespace mongo
+
+#endif // _WIN32 && _MSC_VER
diff --git a/src/mongo/logv2/shared_access_fstream.h b/src/mongo/logv2/shared_access_fstream.h
new file mode 100644
index 00000000000..6157705a5be
--- /dev/null
+++ b/src/mongo/logv2/shared_access_fstream.h
@@ -0,0 +1,137 @@
+/**
+ * Copyright (C) 2020-present MongoDB, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the Server Side Public License, version 1,
+ * as published by MongoDB, Inc.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * Server Side Public License for more details.
+ *
+ * You should have received a copy of the Server Side Public License
+ * along with this program. If not, see
+ * <http://www.mongodb.com/licensing/server-side-public-license>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the Server Side Public License in all respects for
+ * all of the code used other than as permitted herein. If you modify file(s)
+ * with this exception, you may extend this exception to your version of the
+ * file(s), but you are not obligated to do so. If you do not wish to do so,
+ * delete this exception statement from your version. If you delete this
+ * exception statement from all source files in the program, then also delete
+ * it in the license file.
+ */
+
+#pragma once
+
+#if defined(_WIN32) && defined(_MSC_VER)
+
+#include <fstream>
+
+#include "mongo/base/string_data.h"
+
+namespace mongo {
+// Helper class to open a FILE* with shared access. Used by the stream classes below.
+class Win32SharedAccessFileDescriptor {
+protected:
+ Win32SharedAccessFileDescriptor(const wchar_t* filename,
+ std::ios_base::openmode mode,
+ bool sharedWriteAccess);
+ Win32SharedAccessFileDescriptor(const char* filename,
+ std::ios_base::openmode mode,
+ bool sharedWriteAccess);
+ Win32SharedAccessFileDescriptor(const std::string& filename,
+ std::ios_base::openmode mode,
+ bool sharedWriteAccess);
+ Win32SharedAccessFileDescriptor(const std::filesystem::path& filename,
+ std::ios_base::openmode mode,
+ bool sharedWriteAccess);
+
+ static FILE* _open(const wchar_t* filename,
+ std::ios_base::openmode mode,
+ bool sharedWriteAccess);
+ static FILE* _open(StringData filename, std::ios_base::openmode mode, bool sharedWriteAccess);
+
+ FILE* _file = nullptr;
+};
+
+// File stream classes that extends the regular file streams with two capabilities:
+// (1) Opens files with shared delete and read access with optional shared write access.
+// (2) Uses UTF-8 as the encoding for the filename parameter
+class Win32SharedAccessOfstream : private Win32SharedAccessFileDescriptor, public std::ofstream {
+public:
+ explicit Win32SharedAccessOfstream(const char* filename,
+ ios_base::openmode mode = ios_base::out,
+ bool sharedWriteAccess = false);
+ explicit Win32SharedAccessOfstream(const wchar_t* filename,
+ ios_base::openmode mode = ios_base::out,
+ bool sharedWriteAccess = false);
+ explicit Win32SharedAccessOfstream(const std::string& filename,
+ ios_base::openmode mode = ios_base::out,
+ bool sharedWriteAccess = false);
+ explicit Win32SharedAccessOfstream(const std::filesystem::path& filename,
+ ios_base::openmode mode = ios_base::out,
+ bool sharedWriteAccess = false);
+
+ // The Visual Studio extension that operates on FILE* does not have an open overload. Needs to
+ // open with constructor.
+ void open(const char* filename, ios_base::openmode mode) = delete;
+ void open(const wchar_t* filename, ios_base::openmode mode) = delete;
+ void open(const std::string& filename, ios_base::openmode mode) = delete;
+ void open(const std::filesystem::path& filename, ios_base::openmode mode) = delete;
+};
+
+class Win32SharedAccessIfstream : private Win32SharedAccessFileDescriptor, public std::ifstream {
+public:
+ explicit Win32SharedAccessIfstream(const char* filename,
+ ios_base::openmode mode = ios_base::in,
+ bool sharedWriteAccess = false);
+ explicit Win32SharedAccessIfstream(const wchar_t* filename,
+ ios_base::openmode mode = ios_base::in,
+ bool sharedWriteAccess = false);
+ explicit Win32SharedAccessIfstream(const std::string& filename,
+ ios_base::openmode mode = ios_base::in,
+ bool sharedWriteAccess = false);
+ explicit Win32SharedAccessIfstream(const std::filesystem::path& filename,
+ ios_base::openmode mode = ios_base::in,
+ bool sharedWriteAccess = false);
+
+ // The Visual Studio extension that operates on FILE* does not have an open overload. Needs to
+ // open with constructor.
+ void open(const char* filename, ios_base::openmode mode) = delete;
+ void open(const wchar_t* filename, ios_base::openmode mode) = delete;
+ void open(const std::string& filename, ios_base::openmode mode) = delete;
+ void open(const std::filesystem::path& filename, ios_base::openmode mode) = delete;
+};
+
+class Win32SharedAccessFstream : private Win32SharedAccessFileDescriptor, public std::fstream {
+public:
+ explicit Win32SharedAccessFstream(const char* filename,
+ ios_base::openmode mode = ios_base::in | ios_base::out,
+ bool sharedWriteAccess = false);
+ explicit Win32SharedAccessFstream(const wchar_t* filename,
+ ios_base::openmode mode = ios_base::in | ios_base::out,
+ bool sharedWriteAccess = false);
+ explicit Win32SharedAccessFstream(const std::string& filename,
+ ios_base::openmode mode = ios_base::in | ios_base::out,
+ bool sharedWriteAccess = false);
+ explicit Win32SharedAccessFstream(const std::filesystem::path& filename,
+ ios_base::openmode mode = ios_base::in | ios_base::out,
+ bool sharedWriteAccess = false);
+
+ // The Visual Studio extension that operates on FILE* does not have an open overload. Needs to
+ // open with constructor.
+ void open(const char* filename, ios_base::openmode mode) = delete;
+ void open(const wchar_t* filename, ios_base::openmode mode) = delete;
+ void open(const std::string& filename, ios_base::openmode mode) = delete;
+ void open(const std::filesystem::path& filename, ios_base::openmode mode) = delete;
+};
+
+} // namespace mongo
+
+#endif // _WIN32
diff --git a/src/mongo/util/log.cpp b/src/mongo/util/log.cpp
index 837f80cafd8..9a9d7099e4a 100644
--- a/src/mongo/util/log.cpp
+++ b/src/mongo/util/log.cpp
@@ -82,22 +82,24 @@ Status logger::registerExtraLogContextFn(logger::ExtraLogContextFn contextFn) {
return Status::OK();
}
-bool rotateLogs(bool renameFiles, bool useLogV2) {
- if (useLogV2) {
- log() << "Logv2 rotation initiated";
- return logv2::LogManager::global().getGlobalDomainInternal().rotate().isOK();
- }
+bool rotateLogs(bool renameFiles) {
+ // Rotate on both logv1 and logv2 so all files that need rotation gets rotated
+ log() << "Log rotation initiated";
+ std::string suffix = "." + terseCurrentTime(false);
+ Status resultv2 =
+ logv2::LogManager::global().getGlobalDomainInternal().rotate(renameFiles, suffix);
+ if (!resultv2.isOK())
+ warning() << "Log rotation failed: " << resultv2;
+
using logger::RotatableFileManager;
RotatableFileManager* manager = logger::globalRotatableFileManager();
- log() << "Log rotation initiated";
- RotatableFileManager::FileNameStatusPairVector result(
- manager->rotateAll(renameFiles, "." + terseCurrentTime(false)));
+ RotatableFileManager::FileNameStatusPairVector result(manager->rotateAll(renameFiles, suffix));
for (RotatableFileManager::FileNameStatusPairVector::iterator it = result.begin();
it != result.end();
it++) {
warning() << "Rotating log file " << it->first << " failed: " << it->second.toString();
}
- return result.empty();
+ return resultv2.isOK() && result.empty();
}
void logContext(const char* errmsg) {
diff --git a/src/mongo/util/log.h b/src/mongo/util/log.h
index 145b41c5a6f..cfe42dd8695 100644
--- a/src/mongo/util/log.h
+++ b/src/mongo/util/log.h
@@ -214,7 +214,7 @@ inline bool shouldLog(logger::LogSeverity severity) {
* We expect logrotate to rename the existing file before we rotate, and so the next open
* we do should result in a file create.
*/
-bool rotateLogs(bool renameFiles, bool useLogV2);
+bool rotateLogs(bool renameFiles);
extern Tee* const warnings; // Things put here go in serverStatus
extern Tee* const startupWarningsLog; // Things put here get reported in MMS
diff --git a/src/mongo/util/signal_handlers.cpp b/src/mongo/util/signal_handlers.cpp
index 47582a9ae84..20e82cbc0ea 100644
--- a/src/mongo/util/signal_handlers.cpp
+++ b/src/mongo/util/signal_handlers.cpp
@@ -215,7 +215,7 @@ void handleOneSignal(const SignalWaitResult& waited, LogRotationState* rotation)
return;
rotation->previous = now;
}
- fassert(16782, rotateLogs(serverGlobalParams.logRenameOnRotate, logV2Enabled()));
+ fassert(16782, rotateLogs(serverGlobalParams.logRenameOnRotate));
if (rotation->logFileStatus == LogFileStatus::kNeedToRotateLogFile) {
logProcessDetailsForLogRotate(getGlobalServiceContext());
}
diff --git a/src/mongo/util/text.cpp b/src/mongo/util/text.cpp
index be0e18eea3a..dccd4d3fb49 100644
--- a/src/mongo/util/text.cpp
+++ b/src/mongo/util/text.cpp
@@ -177,6 +177,29 @@ std::string toUtf8String(const std::wstring& wide) {
return "";
}
+std::wstring toWideStringFromStringData(StringData utf8String) {
+ int bufferSize = MultiByteToWideChar(CP_UTF8, // Code page
+ 0, // Flags
+ utf8String.rawData(), // Input string
+ utf8String.size(), // Count, -1 for NUL-terminated
+ nullptr, // No output buffer
+ 0 // Zero means "compute required size"
+ );
+ if (bufferSize == 0) {
+ return std::wstring();
+ }
+ std::unique_ptr<wchar_t[]> tempBuffer(new wchar_t[bufferSize]);
+ tempBuffer[0] = L'0';
+ MultiByteToWideChar(CP_UTF8, // Code page
+ 0, // Flags
+ utf8String.rawData(), // Input string
+ utf8String.size(), // Count, -1 for NUL-terminated
+ tempBuffer.get(), // UTF-16 output buffer
+ bufferSize // Buffer size in wide characters
+ );
+ return std::wstring(tempBuffer.get(), bufferSize);
+}
+
std::wstring toWideString(const char* utf8String) {
int bufferSize = MultiByteToWideChar(CP_UTF8, // Code page
0, // Flags
diff --git a/src/mongo/util/text.h b/src/mongo/util/text.h
index 76309541d0c..40493fa9d4f 100644
--- a/src/mongo/util/text.h
+++ b/src/mongo/util/text.h
@@ -75,6 +75,7 @@ bool isValidUTF8(StringData s);
std::string toUtf8String(const std::wstring& wide);
+std::wstring toWideStringFromStringData(StringData s);
std::wstring toWideString(const char* s);
bool writeUtf8ToWindowsConsole(const char* utf8String, unsigned int utf8StringSize);