gpt4 book ai didi

c++ - std::cerr 不等待 std::cout (运行 CTest 时)

转载 作者:IT老高 更新时间:2023-10-28 21:50:41 24 4
gpt4 key购买 nike

上下文

我为用户编写了一个记录器打印消息。级别为“debug”、“info”或“warning”的消息打印在 std::cout 中,级别为“error”或“system_error”的消息打印在 std::cerr。我的程序不是多线程的。我在 Linux openSUSE 12.3 下使用 gcc 4.7.2 和 CMake 3.1.0 工作。

我的问题

我发现有时,当一条错误消息(打印在 std::cerr 中)跟在一条长信息消息(打印在 std::cout 中)之后,并且当输出被 CTest 重定向到文件 LastTest.log,错误消息出现在信息消息中(看下面的例子)。我不太了解这种行为,但我想为 std::cout 启动了一个写入线程,然后代码继续,另一个写入线程为 std::cerr 启动> 不等待第一个被终止。

不使用 std::cout 是否可以避免这种情况?

我在终端中没有问题。仅当 CTest 将输出重定向到 LastTest.log 文件时才会发生这种情况。

请注意,我的缓冲区已刷新。这不是 std::endl 在调用 std::cerr 之后出现的问题!

示例

预期行为:

[ 12:06:51.497   TRACE ] Start test
[ 12:06:52.837 WARNING ] This
is
a
very
long
warning
message...
[ 12:06:52.837 ERROR ] AT LINE 49 : 7
[ 12:06:52.841 ERROR ] AT LINE 71 : 506
[ 12:06:52.841 TRACE ] End of test

会发生什么:

[ 12:06:51.497   TRACE ] Start test
[ 12:06:52.837 WARNING ] This
is
a
very
long
[ 12:06:52.837 ERROR ] AT LINE 49 : 7
warning
message...
[ 12:06:52.841 ERROR ] AT LINE 71 : 506
[ 12:06:52.841 TRACE ] End of test

我如何称呼我的记录器

这是我如何使用记录器调用 std::coutstd::cerr 的示例。我用这样的宏调用记录器:

#define LOG_DEBUG(X) {if(Log::debug_is_active()){std::ostringstream o;o<<X;Log::debug(o.str());}}
#define LOG_ERROR(X) {if(Log::error_is_active()){std::ostringstream o;o<<X;Log::error(o.str());}}
//...
LOG_DEBUG("This" << std::endl << "is" << std::endl << "a message");
LOG_ERROR("at line " << __LINE__ << " : " << err_id);

void Log::debug(const std::string& msg)
{
Log::write_if_active(Log::DEBUG, msg);
}
void Log::error(const std::string& msg)
{
Log::write_if_active(Log::ERROR, msg);
}
//...
void Log::write_if_active(unsigned short int state, const std::string& msg)
{
Instant now;
now.setCurrentTime();
std::vector<std::string> lines;
for(std::size_t k = 0; k < msg.size();)
{
std::size_t next_endl = msg.find('\n', k);
if(next_endl == std::string::npos)
next_endl = msg.size();
lines.push_back(msg.substr(k, next_endl - k));
k = next_endl + 1;
}
boost::mutex::scoped_lock lock(Log::mutex);
for(unsigned long int i = 0; i < Log::chanels.size(); ++i)
if(Log::chanels[i])
if(Log::chanels[i]->flags & state)
Log::chanels[i]->write(state, now, lines);
}

这里,log chanel是专门用于终端输出的对象,写函数是:

void Log::StdOut::write(unsigned short int state, const Instant& t, const std::vector<std::string>& lines)
{
assert(lines.size() > 0 && "PRE: empty lines");
std::string prefix = "[ ";
if(this->withDate || this->withTime)
{
std::string pattern = "";
if(this->withDate)
pattern += "%Y-%m-%d ";
if(this->withTime)
pattern += "%H:%M:%S.%Z ";
prefix += t.toString(pattern);
}
std::ostream* out = 0;
if(state == Log::TRACE)
{
prefix += " TRACE";
out = &std::cout;
}
else if(state == Log::DEBUG)
{
prefix += " DEBUG";
out = &std::cout;
}
else if(state == Log::INFO)
{
prefix += " INFO";
out = &std::cout;
}
else if(state == Log::WARNING)
{
prefix += "WARNING";
out = &std::cout;
}
else if(state == Log::ERROR)
{
prefix += " ERROR";
out = &std::cerr;
}
else if(state == Log::SYS_ERROR)
{
prefix += "SYERROR";
out = &std::cerr;
}
else
assert(false && "PRE: Invalid Log state");
prefix += " ] ";
(*out) << prefix << lines[0] << "\n";
prefix = std::string(prefix.size(), ' ');
for(unsigned long int i = 1; i < lines.size(); ++i)
(*out) << prefix << lines[i] << "\n";
out->flush();
}

你可以看到我的缓冲区在执行日志指令的时候被刷新了。

最佳答案

我以前曾以几种形式看到过这种行为。中心思想是要记住 std::coutstd::cerr 写入两个完全独立的流,所以任何时候你看到两者的输出在同一个地方,这是因为程序外部的某种机制合并了两个流。

有时,我看到这只是由于一个错误,例如

myprogram > logfile &
tail -f logfile

它正在观察日志文件的写入,但也忘记将 stderr 重定向到日志文件,因此写入 stdout 至少要经过两个额外的缓冲层tail 在显示之前,但写入 stderr 直接进入 tty,因此可以混入。

我见过的其他示例涉及合并流的外部进程。我对 CTest 一无所知,但也许它正在这样做。此类进程没有义务按照您最初将它们写入流的确切时间对行进行排序,并且即使它们愿意,也可能无法访问该信息!


你真的只有两个选择:

  • 将两个日志写入同一个流 - 例如使用 std::clog 代替 std::coutstd::cout 代替 std::cerr ;或使用 myprogram 2>&1 或类似方法启动程序
  • 确保合并是由一个真正知道要合并什么的进程完成的,并注意适本地进行。如果您通过传递包含日志事件的数据包而不是自己编写格式化的日志消息来进行通信,这会更好。

关于c++ - std::cerr 不等待 std::cout (运行 CTest 时),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30593312/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com