command line: use both stdout and stderr
Traditionally, the "syncevolution" command line tool mixed its INFO/ERROR/DEBUG messages into the normal stdout. This has the major drawback that error messages get lost during operations like syncevolution --export - @default addressbook | grep "John Doe" Now anything which not the expected result of the operation is always sent to stderr. Obviously this includes ERROR messages. INFO and DEBUG are harder to decide. Because they usually convey meta information about the running operation, they are also sent to stderr. This changes the behavior of syncevolution --run foo eds_event | less "less" will capture only the following output: vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv Local data changes to be applied during synchronization: *** eds_event *** no changes Synchronization successful. Changes applied during synchronization: +---------------|-----------------------|-----------------------|-CON-+ | | LOCAL | REMOTE | FLI | | Source | NEW | MOD | DEL | ERR | NEW | MOD | DEL | ERR | CTS | +---------------+-----+-----+-----+-----+-----+-----+-----+-----+-----+ | eds_event | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | | two-way, 0 KB sent by client, 0 KB received | | item(s) in database backup: 2 before sync, 2 after it | +---------------+-----+-----+-----+-----+-----+-----+-----+-----+-----+ | start Wed Apr 11 14:34:11 2012, duration 0:03min | | synchronization completed successfully | +---------------+-----+-----+-----+-----+-----+-----+-----+-----+-----+ Data modified locally during synchronization: *** eds_event *** no changes ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ To get the traditional behavior with bash or sh as shell, use syncevolution --run foo eds_event 2>&1 | less Some "normal" output in error messages was changed to INFO and thus now appears on stderr after the corresponding ERROR message. Empty lines which were used to separate different parts now also have a tag, for the sake of consistency with the surrounding output. The main implementation change is in LogRedirect, which now decides whether LogStdout shall send the message to stdout or stderr. This has the desired effect in the "syncevolution" binary, which uses LogRedirect. Other binaries inherit the same change, although there it usually doesn't matter. The Cmdline unit testing mirrors this change in the way how it stores and checks Cmdline output and also was adapted to the prefix changes.
This commit is contained in:
parent
b1f7f781e8
commit
1fe3100c34
|
@ -986,11 +986,13 @@ bool Cmdline::run() {
|
|||
} else if (missing.empty()) {
|
||||
SE_LOG_INFO(NULL, NULL, "All relevant properties seem to be set, omit the --template parameter to proceed.");
|
||||
}
|
||||
SE_LOG_SHOW(NULL, NULL, "");
|
||||
SE_LOG_INFO(NULL, NULL, "");
|
||||
SyncConfig::DeviceList devices;
|
||||
devices.push_back(SyncConfig::DeviceDescription("", "", SyncConfig::MATCH_ALL));
|
||||
dumpConfigTemplates("Available configuration templates (clients and servers):",
|
||||
SyncConfig::getPeerTemplates(devices));
|
||||
SyncConfig::getPeerTemplates(devices),
|
||||
false,
|
||||
Logger::INFO);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -1940,8 +1942,9 @@ void Cmdline::dumpConfigs(const string &preamble,
|
|||
}
|
||||
|
||||
void Cmdline::dumpConfigTemplates(const string &preamble,
|
||||
const SyncConfig::TemplateList &templates,
|
||||
bool printRank)
|
||||
const SyncConfig::TemplateList &templates,
|
||||
bool printRank,
|
||||
Logger::Level level)
|
||||
{
|
||||
ostringstream out;
|
||||
out << preamble << endl;
|
||||
|
@ -1961,7 +1964,7 @@ void Cmdline::dumpConfigTemplates(const string &preamble,
|
|||
if (!templates.size()) {
|
||||
out << " none" << endl;
|
||||
}
|
||||
SE_LOG_SHOW(NULL, NULL, "%s", out.str().c_str());
|
||||
SE_LOG(level, NULL, NULL, "%s", out.str().c_str());
|
||||
}
|
||||
|
||||
void Cmdline::dumpProperties(const ConfigNode &configuredProps,
|
||||
|
@ -3339,19 +3342,17 @@ protected:
|
|||
NULL);
|
||||
CPPUNIT_ASSERT(cmdline.m_cmdline->parse());
|
||||
CPPUNIT_ASSERT(!cmdline.m_cmdline->run());
|
||||
static const char error[] = "[ERROR] No configuration template for 'yahooxyz' available.\n";
|
||||
static const char hint[] = "\nAvailable configuration templates (clients and servers):\n";
|
||||
static const char error[] = "[ERROR] No configuration template for 'yahooxyz' available.\n"
|
||||
"[INFO] \n"
|
||||
"[INFO] Available configuration templates (clients and servers):\n";
|
||||
std::string out = cmdline.m_out.str();
|
||||
std::string err = cmdline.m_err.str();
|
||||
std::string all = cmdline.m_all.str();
|
||||
CPPUNIT_ASSERT(boost::starts_with(out, hint));
|
||||
CPPUNIT_ASSERT(boost::ends_with(out, "\n"));
|
||||
CPPUNIT_ASSERT(!boost::ends_with(out, "\n\n"));
|
||||
CPPUNIT_ASSERT_EQUAL(string(error),
|
||||
err);
|
||||
CPPUNIT_ASSERT(boost::starts_with(all, string(error) + hint));
|
||||
CPPUNIT_ASSERT(boost::ends_with(all, "\n"));
|
||||
CPPUNIT_ASSERT(!boost::ends_with(all, "\n\n"));
|
||||
CPPUNIT_ASSERT(boost::starts_with(err, error));
|
||||
CPPUNIT_ASSERT(boost::ends_with(err, "\n"));
|
||||
CPPUNIT_ASSERT(!boost::ends_with(err, "\n\n"));
|
||||
CPPUNIT_ASSERT_EQUAL(string(""), out);
|
||||
CPPUNIT_ASSERT_EQUAL(all, err);
|
||||
}
|
||||
{
|
||||
TestCmdline cmdline("--configure",
|
||||
|
@ -3359,19 +3360,18 @@ protected:
|
|||
NULL);
|
||||
CPPUNIT_ASSERT(cmdline.m_cmdline->parse());
|
||||
CPPUNIT_ASSERT(!cmdline.m_cmdline->run());
|
||||
static const char error[] = "[ERROR] No configuration template for 'foobar' available.\n";
|
||||
static const char hint[] = "[INFO] Use '--template none' and/or specify relevant properties on the command line to create a configuration without a template. Need values for: syncURL\n\nAvailable configuration templates (clients and servers):\n";
|
||||
static const char error[] = "[ERROR] No configuration template for 'foobar' available.\n"
|
||||
"[INFO] Use '--template none' and/or specify relevant properties on the command line to create a configuration without a template. Need values for: syncURL\n"
|
||||
"[INFO] \n"
|
||||
"[INFO] Available configuration templates (clients and servers):\n";
|
||||
std::string out = cmdline.m_out.str();
|
||||
std::string err = cmdline.m_err.str();
|
||||
std::string all = cmdline.m_all.str();
|
||||
CPPUNIT_ASSERT(boost::starts_with(out, hint));
|
||||
CPPUNIT_ASSERT(boost::ends_with(out, "\n"));
|
||||
CPPUNIT_ASSERT(!boost::ends_with(out, "\n\n"));
|
||||
CPPUNIT_ASSERT_EQUAL(string(error),
|
||||
err);
|
||||
CPPUNIT_ASSERT(boost::starts_with(all, string(error) + hint));
|
||||
CPPUNIT_ASSERT(boost::ends_with(all, "\n"));
|
||||
CPPUNIT_ASSERT(!boost::ends_with(all, "\n\n"));
|
||||
CPPUNIT_ASSERT(boost::starts_with(err, error));
|
||||
CPPUNIT_ASSERT(boost::ends_with(err, "\n"));
|
||||
CPPUNIT_ASSERT(!boost::ends_with(err, "\n\n"));
|
||||
CPPUNIT_ASSERT_EQUAL(string(""), out);
|
||||
CPPUNIT_ASSERT_EQUAL(err, all);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
@ -3596,19 +3596,18 @@ protected:
|
|||
TestCmdline failure("--configure", "foo", NULL);
|
||||
CPPUNIT_ASSERT(failure.m_cmdline->parse());
|
||||
CPPUNIT_ASSERT(!failure.m_cmdline->run());
|
||||
static const char error[] = "[ERROR] No configuration template for 'foo@default' available.\n";
|
||||
static const char hint[] = "[INFO] Use '--template none' and/or specify relevant properties on the command line to create a configuration without a template. Need values for: syncURL\n\nAvailable configuration templates (clients and servers):\n";
|
||||
static const char error[] = "[ERROR] No configuration template for 'foo@default' available.\n"
|
||||
"[INFO] Use '--template none' and/or specify relevant properties on the command line to create a configuration without a template. Need values for: syncURL\n"
|
||||
"[INFO] \n"
|
||||
"[INFO] Available configuration templates (clients and servers):\n";
|
||||
std::string out = failure.m_out.str();
|
||||
std::string err = failure.m_err.str();
|
||||
std::string all = failure.m_all.str();
|
||||
CPPUNIT_ASSERT(boost::starts_with(out, hint));
|
||||
CPPUNIT_ASSERT(boost::ends_with(out, "\n"));
|
||||
CPPUNIT_ASSERT(!boost::ends_with(out, "\n\n"));
|
||||
CPPUNIT_ASSERT_EQUAL(string(error),
|
||||
err);
|
||||
CPPUNIT_ASSERT(boost::starts_with(all, string(error) + hint));
|
||||
CPPUNIT_ASSERT(boost::ends_with(all, "\n"));
|
||||
CPPUNIT_ASSERT(!boost::ends_with(all, "\n\n"));
|
||||
CPPUNIT_ASSERT(boost::starts_with(err, error));
|
||||
CPPUNIT_ASSERT(boost::ends_with(err, "\n"));
|
||||
CPPUNIT_ASSERT(!boost::ends_with(err, "\n\n"));
|
||||
CPPUNIT_ASSERT_EQUAL(string(""), out);
|
||||
CPPUNIT_ASSERT_EQUAL(all, err);
|
||||
}
|
||||
|
||||
rm_r(m_testDir);
|
||||
|
@ -3620,19 +3619,18 @@ protected:
|
|||
CPPUNIT_ASSERT(failure.m_cmdline->parse());
|
||||
CPPUNIT_ASSERT(!failure.m_cmdline->run());
|
||||
|
||||
static const char error[] = "[ERROR] No configuration template for 'foo' available.\n";
|
||||
static const char hint[] = "[INFO] All relevant properties seem to be set, omit the --template parameter to proceed.\n\nAvailable configuration templates (clients and servers):\n";
|
||||
static const char error[] = "[ERROR] No configuration template for 'foo' available.\n"
|
||||
"[INFO] All relevant properties seem to be set, omit the --template parameter to proceed.\n"
|
||||
"[INFO] \n"
|
||||
"[INFO] Available configuration templates (clients and servers):\n";
|
||||
std::string out = failure.m_out.str();
|
||||
std::string err = failure.m_err.str();
|
||||
std::string all = failure.m_all.str();
|
||||
CPPUNIT_ASSERT(boost::starts_with(out, hint));
|
||||
CPPUNIT_ASSERT(boost::ends_with(out, "\n"));
|
||||
CPPUNIT_ASSERT(!boost::ends_with(out, "\n\n"));
|
||||
CPPUNIT_ASSERT_EQUAL(string(error),
|
||||
err);
|
||||
CPPUNIT_ASSERT(boost::starts_with(all, string(error) + hint));
|
||||
CPPUNIT_ASSERT(boost::ends_with(all, "\n"));
|
||||
CPPUNIT_ASSERT(!boost::ends_with(all, "\n\n"));
|
||||
CPPUNIT_ASSERT(boost::starts_with(err, error));
|
||||
CPPUNIT_ASSERT(boost::ends_with(err, "\n"));
|
||||
CPPUNIT_ASSERT(!boost::ends_with(err, "\n\n"));
|
||||
CPPUNIT_ASSERT_EQUAL(string(""), out);
|
||||
CPPUNIT_ASSERT_EQUAL(all, err);
|
||||
}
|
||||
|
||||
string fooconfig =
|
||||
|
@ -4796,7 +4794,7 @@ private:
|
|||
va_list args)
|
||||
{
|
||||
if (level <= INFO) {
|
||||
ostringstream &out = level <= ERROR ? m_err : m_out;
|
||||
ostringstream &out = level != SHOW ? m_err : m_out;
|
||||
std::string str = StringPrintfV(format, args);
|
||||
if (level != SHOW) {
|
||||
out << "[" << levelToStr(level) << "] ";
|
||||
|
|
|
@ -235,8 +235,9 @@ protected:
|
|||
const SyncConfig::ConfigList &servers);
|
||||
|
||||
void dumpConfigTemplates(const std::string &preamble,
|
||||
const SyncConfig::TemplateList &templates,
|
||||
bool printRank = false);
|
||||
const SyncConfig::TemplateList &templates,
|
||||
bool printRank = false,
|
||||
Logger::Level level = Logger::SHOW);
|
||||
|
||||
enum DumpPropertiesFlags {
|
||||
DUMP_PROPS_NORMAL = 0,
|
||||
|
|
|
@ -72,6 +72,7 @@ void LogRedirect::init()
|
|||
m_buffer = NULL;
|
||||
m_len = 0;
|
||||
m_out = NULL;
|
||||
m_err = NULL;
|
||||
m_streams = false;
|
||||
m_stderr.m_original =
|
||||
m_stderr.m_read =
|
||||
|
@ -116,6 +117,12 @@ LogRedirect::LogRedirect(bool both, const char *filename) throw()
|
|||
perror(filename);
|
||||
}
|
||||
}
|
||||
// Separate FILE, will write into same file as normal output
|
||||
// if a filename was given (for testing), otherwise to original
|
||||
// stderr.
|
||||
m_err = fdopen(dup((filename && m_out) ?
|
||||
fileno(m_out) :
|
||||
m_stderr.m_copy), "w");
|
||||
}
|
||||
LoggerBase::pushLogger(this);
|
||||
m_redirect = this;
|
||||
|
@ -165,6 +172,9 @@ LogRedirect::~LogRedirect() throw()
|
|||
if (m_out) {
|
||||
fclose(m_out);
|
||||
}
|
||||
if (m_err) {
|
||||
fclose(m_err);
|
||||
}
|
||||
if (m_buffer) {
|
||||
free(m_buffer);
|
||||
}
|
||||
|
@ -211,7 +221,11 @@ void LogRedirect::messagev(Level level,
|
|||
{
|
||||
// check for other output first
|
||||
process();
|
||||
LoggerStdout::messagev(m_out ? m_out : stdout,
|
||||
// Choose output channel: SHOW goes to original stdout,
|
||||
// everything else to stderr.
|
||||
LoggerStdout::messagev(level == SHOW ?
|
||||
(m_out ? m_out : stdout) :
|
||||
(m_err ? m_err : stderr),
|
||||
level, getLevel(),
|
||||
prefix,
|
||||
file, line, function,
|
||||
|
|
|
@ -104,7 +104,8 @@ class LogRedirect : public LoggerStdout
|
|||
private:
|
||||
FDs m_stdout, m_stderr;
|
||||
bool m_streams; /**< using reliable streams instead of UDP */
|
||||
FILE *m_out; /** a stream for the normal LogStdout which isn't redirected */
|
||||
FILE *m_out; /** a stream for Logger::SHOW output which isn't redirected */
|
||||
FILE *m_err; /** corresponding stream for any other output */
|
||||
char *m_buffer; /** typically fairly small buffer for reading */
|
||||
std::string m_stdoutData; /**< incomplete stdout line */
|
||||
size_t m_len; /** total length of buffer */
|
||||
|
|
|
@ -139,8 +139,9 @@ void LoggerStdout::messagev(FILE *file,
|
|||
break;
|
||||
}
|
||||
}
|
||||
if (pos < output.size()) {
|
||||
// handle dangling last line
|
||||
if (pos < output.size() || output.empty()) {
|
||||
// handle dangling last line or empty chunk (don't
|
||||
// want empty line for that, print at least the tag)
|
||||
buffer.append(tag);
|
||||
buffer.append(output, pos, output.size() - pos);
|
||||
haveNewline = false;
|
||||
|
|
Loading…
Reference in a new issue