Skip to content

Commit 4898890

Browse files
authored
Merge pull request #30 from sy-c/master
stdout/stderr process capture
2 parents 29c124f + 2ec8211 commit 4898890

File tree

6 files changed

+131
-2
lines changed

6 files changed

+131
-2
lines changed

CMakeLists.txt

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,7 @@ set(INFOLOGGER_LIB_OBJECTS
118118
$<TARGET_OBJECTS:objInfoLoggerClient>
119119
$<TARGET_OBJECTS:objCommonConfiguration>
120120
$<TARGET_OBJECTS:objCommonSimpleLog>
121+
$<TARGET_OBJECTS:objCommonLineBuffer>
121122
)
122123

123124
# shared library
@@ -137,6 +138,7 @@ target_link_libraries(InfoLogger
137138
PUBLIC
138139
# AliceO2::Common
139140
Boost::boost
141+
pthread
140142
)
141143
install(TARGETS InfoLogger
142144
EXPORT InfoLoggerTargets
@@ -188,13 +190,13 @@ target_include_directories(libInfoLogger-static
188190
${INFOLOGGER_INCLUDE_DIRS_PUBLIC}
189191
)
190192
add_dependencies(libInfoLogger-static Common-standalone)
193+
target_link_libraries(libInfoLogger-static pthread)
191194

192195

193196
# executable: log (command line tool to inject log messages)
194197
add_executable(
195198
log
196-
src/log.cxx
197-
$<TARGET_OBJECTS:objCommonLineBuffer>
199+
src/log.cxx
198200
)
199201
target_link_libraries(
200202
log

doc/README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,10 @@ The InfoLogger library allows to inject messages directly from programs, as show
178178
179179
* Some example calls are available in [the source code](/test/testInfoLogger.cxx)
180180
181+
* There is the possibility to easily redirect FairLogger messages (see InfoLoggerFMQ.hxx) and process stdout/stderr to infologger (see InfoLogger.hxx setStandardRedirection())
182+
without changing the code from where they are issued. Although practical to get all output in InfoLogger, the messages captured this way are injected with a reduced number of tags,
183+
so it is recommended to use the native InfoLogger API to inject messages whenever possible.
184+
181185
* The tags associated to a message consist of the following fields (which may be left undefined):
182186
* Severity: the kind of message, one of: Info (default), Error, Fatal, Warning, Debug
183187
* Level: the relative visibility of the message and associated target audience to whom is addressed the message: from 1 (important, visible to all) to 99 (low-level debug message, for experts only).

doc/releaseNotes.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,6 @@ This file describes the main feature changes for each InfoLogger released versio
1919
- node.js binding updated for Mac.
2020
- Compatibility with mysql v8.
2121
- Code cleanup (Warnings, clang-format, copyright).
22+
23+
## next version
24+
- added process stdout/stderr capture and redirection to infologger

include/InfoLogger/InfoLogger.hxx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,12 @@ class InfoLogger
196196
// all context fields will be set to default unless an explicit context argument is given for each message.
197197
int unsetContext();
198198

199+
/// Turn on/off stdout/stderr redirection
200+
/// true -> turn ON. Stdout/Stderr are redirected to internal pipes and a dedicated thread captures the stream and redirects to this instance of infologger (with corresponding severity).
201+
/// false -> turn OFF (if it was ON). Stdout/Stderr are redirected to previous outputs, and capture thread is stopped.
202+
/// Return 0 on success, or an error code otherwise
203+
int setStandardRedirection(bool state);
204+
199205
// todo: common handling of error codes in O2?
200206
// 0-9999: "shared" -> can be reused accross modules
201207
// 99.9999: 2-digit 'error namespace' + local error id

src/InfoLogger.cxx

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,12 @@
2727
#include <sys/types.h>
2828
#include <pwd.h>
2929
#include <ctype.h>
30+
#include <thread>
31+
#include <memory>
32+
#include <functional>
3033

3134
#include "InfoLoggerMessageHelper.h"
35+
#include "Common/LineBuffer.h"
3236

3337
#define InfoLoggerMagicNumber (int)0xABABAC00
3438

@@ -261,6 +265,9 @@ class InfoLogger::Impl
261265
/// \return 0 on success, an error code otherwise (but never throw exceptions)..
262266
int logV(const InfoLoggerMessageOption& options, const InfoLoggerContext& context, const char* message, va_list ap) __attribute__((format(printf, 4, 0)));
263267

268+
// main loop of collecting thread, reading incoming messages from a pipe and redirecting to infoLogger
269+
void redirectThreadLoop();
270+
264271
protected:
265272
int magicTag; //< A static tag used for handle validity cross-check
266273
int numberOfMessages; //< number of messages received by this object
@@ -275,6 +282,14 @@ class InfoLogger::Impl
275282

276283
InfoLoggerClient* client; //< entity to communicate with local infoLoggerD
277284
SimpleLog stdLog; //< object to output messages to stdout/file
285+
286+
bool isRedirecting = false; // state of stdout/stderr redirection
287+
int fdStdout = -1; // initial stdout file descriptor, if redirection active
288+
int fdStderr = -1; // initial stderr file descriptor, if redirection active
289+
int pipeStdout[2]; // a pipe to redirect stdout to collecting thread
290+
int pipeStderr[2]; // a pipe to redirect stderr to collecting thread
291+
std::unique_ptr<std::thread> redirectThread; // the thread handling the redirection
292+
bool redirectThreadShutdown; // flag to ask the thread to stop
278293
};
279294

280295
void InfoLogger::Impl::refreshDefaultMsg()
@@ -726,6 +741,96 @@ int InfoLogger::setMessageOption(const char* fieldName, const char* fieldValue,
726741
// required with some compilers to avoid linking errors
727742
constexpr InfoLogger::InfoLoggerMessageOption InfoLogger::undefinedMessageOption;
728743

744+
void InfoLogger::Impl::redirectThreadLoop()
745+
{
746+
LineBuffer lbStdout; // buffer for input lines
747+
LineBuffer lbStderr; // buffer for input lines
748+
std::string msg;
749+
bool isActive = 0;
750+
while (!redirectThreadShutdown) {
751+
isActive = 0;
752+
lbStdout.appendFromFileDescriptor(pipeStdout[0], 0);
753+
for (;;) {
754+
if (lbStdout.getNextLine(msg)) {
755+
// no line left
756+
break;
757+
}
758+
isActive = 1;
759+
pushMessage(InfoLogger::Severity::Info, msg.c_str());
760+
}
761+
lbStderr.appendFromFileDescriptor(pipeStderr[0], 0);
762+
for (;;) {
763+
if (lbStderr.getNextLine(msg)) {
764+
// no line left
765+
break;
766+
}
767+
isActive = 1;
768+
pushMessage(InfoLogger::Severity::Error, msg.c_str());
769+
}
770+
771+
if (!isActive) {
772+
usleep(50000);
773+
}
774+
}
775+
// fprintf(fp,"thread stopped\n");
776+
// fclose(fp);
777+
}
778+
779+
int InfoLogger::setStandardRedirection(bool state)
780+
{
781+
if (state == mPimpl->isRedirecting) {
782+
// we are already in the requested redirection state
783+
return -1;
784+
}
785+
786+
if (state) {
787+
// turn ON redirection
788+
789+
// create a pipe
790+
if (pipe(mPimpl->pipeStdout) != 0) {
791+
return -1;
792+
}
793+
if (pipe(mPimpl->pipeStderr) != 0) {
794+
return -1;
795+
}
796+
797+
// save current stdout/stderr
798+
mPimpl->fdStdout = dup(STDOUT_FILENO);
799+
dup2(mPimpl->pipeStdout[1], STDOUT_FILENO);
800+
mPimpl->fdStderr = dup(STDERR_FILENO);
801+
dup2(mPimpl->pipeStderr[1], STDERR_FILENO);
802+
803+
mPimpl->stdLog.setFileDescriptors(mPimpl->fdStdout, mPimpl->fdStderr);
804+
805+
// create collecting thread
806+
mPimpl->redirectThreadShutdown = 0;
807+
std::function<void(void)> l = std::bind(&InfoLogger::Impl::redirectThreadLoop, mPimpl.get());
808+
mPimpl->redirectThread = std::make_unique<std::thread>(l);
809+
810+
} else {
811+
// turn OFF redirection
812+
813+
// stop collecting thread
814+
mPimpl->redirectThreadShutdown = 1;
815+
mPimpl->redirectThread->join();
816+
mPimpl->redirectThread = nullptr;
817+
818+
mPimpl->stdLog.setFileDescriptors(STDOUT_FILENO, STDERR_FILENO);
819+
820+
dup2(mPimpl->fdStdout, STDOUT_FILENO);
821+
close(mPimpl->fdStdout);
822+
close(mPimpl->pipeStdout[0]);
823+
close(mPimpl->pipeStdout[1]);
824+
dup2(mPimpl->fdStderr, STDERR_FILENO);
825+
close(mPimpl->fdStderr);
826+
close(mPimpl->pipeStderr[0]);
827+
close(mPimpl->pipeStderr[1]);
828+
}
829+
830+
mPimpl->isRedirecting = state;
831+
return 0;
832+
}
833+
729834
// end of namespace
730835
} // namespace InfoLogger
731836
} // namespace AliceO2

test/testInfoLogger.cxx

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,15 @@ int main()
2222
{
2323
InfoLogger theLog;
2424

25+
theLog.log("infoLogger message test");
26+
printf("Message on stdout (initial stdout)\n");
27+
theLog.setStandardRedirection(1);
28+
printf("Message on stdout (redirection enabled)\n");
29+
fprintf(stderr, "Message on stderr (redirection enabled)\n");
30+
usleep(100000);
31+
theLog.setStandardRedirection(0);
32+
printf("Message on stdout (redirection stopped)\n");
33+
2534
theLog.log("infoLogger message test");
2635
theLog.log(InfoLogger::Severity::Info, "infoLogger INFO message test");
2736
theLog.log(InfoLogger::Severity::Warning, "infoLogger WARNING message test");

0 commit comments

Comments
 (0)