errlog std::ostream
This commit is contained in:
80
common/errlogstream.h
Normal file
80
common/errlogstream.h
Normal file
@ -0,0 +1,80 @@
|
||||
#ifndef ERRLOGSTREAM_H
|
||||
#define ERRLOGSTREAM_H
|
||||
|
||||
#include <streambuf>
|
||||
#include <ostream>
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
#include <errlog.h>
|
||||
|
||||
//! output only stream buffer which write to the epics errlog
|
||||
struct errlog_streambuf : public std::streambuf
|
||||
{
|
||||
typedef std::vector<char> buffer_t;
|
||||
|
||||
errlog_streambuf(bool block=false, size_t blen=126)
|
||||
:std::streambuf()
|
||||
,p_outbuf(std::max(blen, size_t(16u))+2)
|
||||
,p_block(block)
|
||||
{
|
||||
p_reset();
|
||||
}
|
||||
virtual ~errlog_streambuf() {sync();}
|
||||
virtual int_type overflow(int_type c)
|
||||
{
|
||||
size_t nwrite = pptr()-pbase();
|
||||
assert(nwrite<p_outbuf.size()-1);
|
||||
if(c!=traits_type::eof()) {
|
||||
p_outbuf[nwrite++] = traits_type::to_char_type(c);
|
||||
}
|
||||
p_flush(nwrite);
|
||||
p_reset();
|
||||
return traits_type::not_eof(c);
|
||||
}
|
||||
|
||||
//! flush local buffer to global errlog buffer.
|
||||
//! if block=true then also calls errlogFlush()
|
||||
virtual int sync()
|
||||
{
|
||||
size_t nwrite = pptr()-pbase();
|
||||
assert(nwrite<p_outbuf.size()-1);
|
||||
p_flush(nwrite);
|
||||
p_reset();
|
||||
return 0;
|
||||
}
|
||||
|
||||
private:
|
||||
void p_reset()
|
||||
{
|
||||
char *B = &p_outbuf[0];
|
||||
setp(B, B+p_outbuf.size()-2); // reserve one char for overflow()s arg and one for nil
|
||||
}
|
||||
void p_flush(size_t nwrite)
|
||||
{
|
||||
if(nwrite) {
|
||||
p_outbuf[nwrite++] = '\0';
|
||||
errlogMessage(&p_outbuf[0]);
|
||||
if(p_block)
|
||||
errlogFlush();
|
||||
}
|
||||
}
|
||||
|
||||
buffer_t p_outbuf;
|
||||
bool p_block;
|
||||
};
|
||||
|
||||
struct errlog_ostream : public std::ostream
|
||||
{
|
||||
errlog_ostream(bool block=false, size_t blen=126)
|
||||
:std::ostream(&p_strm)
|
||||
,p_strm(block, blen)
|
||||
{}
|
||||
virtual ~errlog_ostream() {}
|
||||
private:
|
||||
errlog_streambuf p_strm;
|
||||
};
|
||||
|
||||
#endif // ERRLOGSTREAM_H
|
@ -22,6 +22,11 @@ testweak_SRCS = testweak.cpp
|
||||
testweak_LIBS = Com
|
||||
TESTS += testweak
|
||||
|
||||
TESTPROD_HOST += teststream
|
||||
teststream_SRCS = teststream.cpp
|
||||
teststream_LIBS = Com
|
||||
TESTS += teststream
|
||||
|
||||
TESTPROD_HOST += testtest
|
||||
testtest_SRCS = testtest.cpp
|
||||
testtest_LIBS = testutils p2pcore pvAccess pvData Com
|
||||
|
89
testApp/teststream.cpp
Normal file
89
testApp/teststream.cpp
Normal file
@ -0,0 +1,89 @@
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include <testMain.h>
|
||||
#include <epicsUnitTest.h>
|
||||
#include <epicsEvent.h>
|
||||
#include <epicsThread.h>
|
||||
|
||||
#include "errlogstream.h"
|
||||
|
||||
namespace {
|
||||
|
||||
struct tester {
|
||||
std::string buf;
|
||||
epicsEvent evnt;
|
||||
bool wait;
|
||||
unsigned called;
|
||||
|
||||
tester() :wait(false), called(0) {
|
||||
errlogAddListener(&handler, (void*)this);
|
||||
}
|
||||
~tester() {
|
||||
errlogRemoveListeners(&handler, (void*)this);
|
||||
}
|
||||
|
||||
static void handler(void *raw, const char *msg)
|
||||
{
|
||||
tester *self = (tester*)raw;
|
||||
if(self->wait)
|
||||
self->evnt.wait();
|
||||
self->called++;
|
||||
self->buf += msg;
|
||||
}
|
||||
};
|
||||
|
||||
void testNoBlock(const char *msg)
|
||||
{
|
||||
testDiag("Test non blocking with message length %u", (unsigned)strlen(msg));
|
||||
tester T;
|
||||
T.wait = true;
|
||||
|
||||
{
|
||||
errlog_ostream strm(false, 16);
|
||||
|
||||
strm<<msg;
|
||||
}
|
||||
testDiag("sleep");
|
||||
epicsThreadSleep(0.1);
|
||||
// see that stream dtor doesn't call errlogFlush()
|
||||
testOk(T.called==0, "called %u times", T.called);
|
||||
T.wait = false;
|
||||
T.evnt.signal();
|
||||
testDiag("flush");
|
||||
errlogFlush();
|
||||
testOk(T.called!=0, "called %u times", T.called);
|
||||
testOk(T.buf==msg, "\"%s\"==\"%s\"", T.buf.c_str(), msg);
|
||||
}
|
||||
|
||||
void testBlock(const char *msg)
|
||||
{
|
||||
testDiag("Test blocking with message length %u", (unsigned)strlen(msg));
|
||||
tester T;
|
||||
|
||||
{
|
||||
errlog_ostream strm(true, 16);
|
||||
|
||||
strm<<msg;
|
||||
}
|
||||
|
||||
testOk(T.called!=0, "called %u times", T.called);
|
||||
testOk(T.buf==msg, "\"%s\"==\"%s\"", T.buf.c_str(), msg);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
static const char longmsg[] = "This message should be longer than the mimimum buffer length"
|
||||
" so that it results in a call to overflow()";
|
||||
|
||||
MAIN(teststream)
|
||||
{
|
||||
testPlan(0);
|
||||
eltc(0);
|
||||
testNoBlock("hello");
|
||||
testNoBlock(longmsg);
|
||||
testBlock("hello");
|
||||
testBlock(longmsg);
|
||||
eltc(1);
|
||||
return testDone();
|
||||
}
|
Reference in New Issue
Block a user