summaryrefslogtreecommitdiff
path: root/src/mongo/logv2
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 /src/mongo/logv2
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
Diffstat (limited to 'src/mongo/logv2')
-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
6 files changed, 633 insertions, 43 deletions
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