【发布时间】:2021-12-07 08:18:12
【问题描述】:
我需要创建一个记录器工具,根据用户提供的内容从不同的代码位置输出到相同或不同的文件。如果未打开,它应该重新创建一个用于记录的文件。但它必须附加到已打开的文件中。
这种幼稚的方式如
std::ofstream f1(“log”);
f1 << "1 from f1\n";
std::ofstream f2(“log”);
f2 << "1 from f2\n";
f1 << "2 from f1\n";
窃取流并重新创建文件。日志包含
1 from f2
使用追加,它将重用文件,但第二个打开窃取来自f1 的流。
日志包含
1 from f1
1 from f2
尝试猜测将使用哪些文件并在非常乞求时将它们全部打开,但可能会创建很多实际上没有使用的文件。
在每个日志记录调用上打开以追加和关闭将是一个几乎可行的解决方案,但由于大量系统调用和每个日志记录操作的刷新,它似乎是一个缓慢的解决方案。
我要创建一个打开文件的静态表,希望std::filesystem::canonical
将适用于我的所有情况。但据我了解,这样的表应该已经存在于流程中的某个地方。
我读到过,在 Fortran 中人们可以使用 inquire 检查文件是否被打开。
Check whether file has been opened already
但这个答案并没有让我了解如何使用 С/C++ 实现同样的目标。
更新
带有“静态”打开日志表的记录器的划痕可能看起来像
//hpp
class Logger {
static std::mutex _mutex;
static std::unordered_map<std::string, std::ofstream> _openFiles;
std::ostream& _appender;
std::ostream& _createAppender(const std::filesystem::path& logPath);
public:
Logger(const std::filesystem::path& logPath):
_appender(_createAppender(logPath)) {
}
template<class... Args>
void log(const Args&... args) const {
std::scoped_lock<std::mutex> lock(_mutex);
(_appender << ... << args);
}
};
//cpp
#include "Logger.hpp"
std::mutex Logger::_mutex;
std::unordered_map<std::string, std::ofstream> Logger::_openFiles;
std::ostream& Logger::_createAppender(const std::filesystem::path& logPath) {
if (logPath.empty()) return std::cout;
const auto truePath{std::filesystem::weakly_canonical(logPath).string()};
std::scoped_lock<std::mutex> lock(_mutex);
const auto entry{_openFiles.find(truePath)};
if (entry != _openFiles.end()) return entry->second;
_openFiles.emplace(truePath, logPath);
std::ostream& stream{_openFiles[truePath]};
stream.exceptions(std::ifstream::failbit|std::ifstream::badbit);
return stream;
}
也许它会对某人有所帮助。
然而,我仍然想知道是否可以从@yzt 提到的操作系统中获取表映射句柄/描述符,如果有人在程序中解释如何做到这一点,我会接受作为答案。
【问题讨论】:
-
最好的办法是使用一个记录器库来处理这个问题。或者编写自己的记录器模块或库,提供返回文件句柄的函数。
-
没有标准的方法可以做到这一点,但您可以自己轻松实现它(如您所写)。操作系统确实有一个表,将句柄/描述符映射到每个进程的其他事物(包括文件),因此标准 C/C++ 库不需要这样的工具。
-
我不确定“静态”表是否是您想要的(通常首选非静态表),但是,您可以通过自己跟踪所有打开的文件来做到这一点。这一点都不难,但它可能是最便携的方式。
-
在一个地方打开文件一次。使用简单的类是个好主意。对该类使用单例模式很常见。你的班级可以像你喜欢的那样愚蠢或聪明。对于多个日志,将其外推到一个日志管理器中,该管理器通过某种标识符按需创建它们。最后,根据需要快速、轻松地开始和扩展。如果您在兔子洞中走得太远,请考虑以前有许多其他人已经在那里,并开发了您可能可以使用的出色解决方案。
-
@freakish,我需要在多线程上下文中使用该日志记录。我的意思是“线程之间共享”的静态。因为只共享一个指针就足够了,你是对的,使表格的内容非静态是合理的。