libCom: Added errlogSetConsole() functionality.
This commit is contained in:
@@ -15,6 +15,21 @@ EPICS Base 3.15.0.x releases are not intended for use in production systems.</p>
|
||||
<h2 align="center">Changes between 3.15.0.1 and 3.15.0.2</h2>
|
||||
<!-- Insert new items immediately below here ... -->
|
||||
|
||||
<h3>Redirection of the errlog console stream</h3>
|
||||
|
||||
<p>A new routine has been added to the errlog facility which allows the console
|
||||
error message stream to be redirected from stderr to some other already open
|
||||
file stream:</p>
|
||||
|
||||
<blockquote><pre>int errlogSetConsole(FILE *stream);
|
||||
</pre></blockquote>
|
||||
|
||||
<p>The stream argument must be a FILE* pointer as returned by fopen() that is
|
||||
open for output. If NULL is passed in, the errlog thread's stderr output stream
|
||||
will be used instead. Note that messages to the console can be disabled and
|
||||
re-enabled using the eltc routine which is also an iocsh command, but there is
|
||||
no iocsh command currently provided for calling errlogSetConsole.</p>
|
||||
|
||||
<h3>Add cleanup subroutine to aSub record</h3>
|
||||
|
||||
<p>An aSub routine may set the CADR field with a function pointer which will be
|
||||
|
||||
@@ -81,11 +81,12 @@ static struct {
|
||||
int msgNeeded;
|
||||
int sevToLog;
|
||||
int toConsole;
|
||||
FILE *console;
|
||||
int missedMessages;
|
||||
char *pbuffer;
|
||||
} pvtData;
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* vsnprintf with truncation message
|
||||
*/
|
||||
@@ -114,17 +115,24 @@ epicsShareFunc int errlogPrintf(const char *pFormat, ...)
|
||||
("errlogPrintf called from interrupt level\n");
|
||||
return 0;
|
||||
}
|
||||
isOkToBlock = epicsThreadIsOkToBlock();
|
||||
|
||||
errlogInit(0);
|
||||
isOkToBlock = epicsThreadIsOkToBlock();
|
||||
|
||||
if (pvtData.atExit || (isOkToBlock && pvtData.toConsole)) {
|
||||
va_start(pvar, pFormat);
|
||||
nchar = vfprintf(stderr, pFormat, pvar);
|
||||
nchar = vfprintf(pvtData.console, pFormat, pvar);
|
||||
va_end (pvar);
|
||||
fflush(stderr);
|
||||
fflush(pvtData.console);
|
||||
}
|
||||
if (pvtData.atExit) return nchar;
|
||||
|
||||
if (pvtData.atExit)
|
||||
return nchar;
|
||||
|
||||
pbuffer = msgbufGetFree(isOkToBlock);
|
||||
if (!pbuffer) return 0;
|
||||
if (!pbuffer)
|
||||
return 0;
|
||||
|
||||
va_start(pvar, pFormat);
|
||||
nchar = tvsnPrint(pbuffer, pvtData.maxMsgSize, pFormat?pFormat:"", pvar);
|
||||
va_end(pvar);
|
||||
@@ -144,19 +152,23 @@ epicsShareFunc int errlogVprintf(
|
||||
("errlogVprintf called from interrupt level\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
errlogInit(0);
|
||||
if (pvtData.atExit) return 0;
|
||||
if (pvtData.atExit)
|
||||
return 0;
|
||||
isOkToBlock = epicsThreadIsOkToBlock();
|
||||
|
||||
pbuffer = msgbufGetFree(isOkToBlock);
|
||||
if (!pbuffer) {
|
||||
vfprintf(stderr, pFormat, pvar);
|
||||
fflush(stderr);
|
||||
vfprintf(pvtData.console, pFormat, pvar);
|
||||
fflush(pvtData.console);
|
||||
return 0;
|
||||
}
|
||||
|
||||
nchar = tvsnPrint(pbuffer, pvtData.maxMsgSize, pFormat?pFormat:"", pvar);
|
||||
if (pvtData.atExit || (isOkToBlock && pvtData.toConsole)) {
|
||||
fprintf(stderr, "%s", pbuffer);
|
||||
fflush(stderr);
|
||||
fprintf(pvtData.console, "%s", pbuffer);
|
||||
fflush(pvtData.console);
|
||||
}
|
||||
msgbufSetSize(nchar);
|
||||
return nchar;
|
||||
@@ -178,6 +190,7 @@ epicsShareFunc int errlogPrintfNoConsole( const char *pFormat, ...)
|
||||
("errlogPrintfNoConsole called from interrupt level\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
errlogInit(0);
|
||||
va_start(pvar, pFormat);
|
||||
nchar = errlogVprintfNoConsole(pFormat, pvar);
|
||||
@@ -196,15 +209,21 @@ epicsShareFunc int errlogVprintfNoConsole(
|
||||
("errlogVprintfNoConsole called from interrupt level\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
errlogInit(0);
|
||||
if (pvtData.atExit) return 0;
|
||||
if (pvtData.atExit)
|
||||
return 0;
|
||||
|
||||
pbuffer = msgbufGetFree(1);
|
||||
if (!pbuffer) return 0;
|
||||
if (!pbuffer)
|
||||
return 0;
|
||||
|
||||
nchar = tvsnPrint(pbuffer, pvtData.maxMsgSize, pFormat?pFormat:"", pvar);
|
||||
msgbufSetSize(nchar);
|
||||
return nchar;
|
||||
}
|
||||
|
||||
|
||||
|
||||
epicsShareFunc int errlogSevPrintf(
|
||||
const errlogSevEnum severity,const char *pFormat, ...)
|
||||
{
|
||||
@@ -217,16 +236,20 @@ epicsShareFunc int errlogSevPrintf(
|
||||
("errlogSevPrintf called from interrupt level\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
errlogInit(0);
|
||||
if (pvtData.sevToLog>severity) return 0;
|
||||
if (pvtData.sevToLog > severity)
|
||||
return 0;
|
||||
|
||||
isOkToBlock = epicsThreadIsOkToBlock();
|
||||
if (pvtData.atExit || (isOkToBlock && pvtData.toConsole)) {
|
||||
fprintf(stderr, "sevr=%s ", errlogGetSevEnumString(severity));
|
||||
fprintf(pvtData.console, "sevr=%s ", errlogGetSevEnumString(severity));
|
||||
va_start(pvar, pFormat);
|
||||
vfprintf(stderr, pFormat, pvar);
|
||||
vfprintf(pvtData.console, pFormat, pvar);
|
||||
va_end(pvar);
|
||||
fflush(stderr);
|
||||
fflush(pvtData.console);
|
||||
}
|
||||
|
||||
va_start(pvar, pFormat);
|
||||
nchar = errlogSevVprintf(severity, pFormat, pvar);
|
||||
va_end(pvar);
|
||||
@@ -246,11 +269,16 @@ epicsShareFunc int errlogSevVprintf(
|
||||
("errlogSevVprintf called from interrupt level\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
errlogInit(0);
|
||||
if (pvtData.atExit) return 0;
|
||||
if (pvtData.atExit)
|
||||
return 0;
|
||||
|
||||
isOkToBlock = epicsThreadIsOkToBlock();
|
||||
pnext = msgbufGetFree(isOkToBlock);
|
||||
if (!pnext) return 0;
|
||||
if (!pnext)
|
||||
return 0;
|
||||
|
||||
nchar = sprintf(pnext, "sevr=%s ", errlogGetSevEnumString(severity));
|
||||
pnext += nchar; totalChar += nchar;
|
||||
nchar = tvsnPrint(pnext, pvtData.maxMsgSize - totalChar - 1, pFormat, pvar);
|
||||
@@ -262,12 +290,14 @@ epicsShareFunc int errlogSevVprintf(
|
||||
msgbufSetSize(totalChar);
|
||||
return nchar;
|
||||
}
|
||||
|
||||
|
||||
|
||||
epicsShareFunc char * epicsShareAPI errlogGetSevEnumString(
|
||||
const errlogSevEnum severity)
|
||||
{
|
||||
errlogInit(0);
|
||||
if (severity > 3) return "unknown";
|
||||
if (severity > 3)
|
||||
return "unknown";
|
||||
return errlogSevEnumString[severity];
|
||||
}
|
||||
|
||||
@@ -290,7 +320,9 @@ epicsShareFunc void epicsShareAPI errlogAddListener(
|
||||
listenerNode *plistenerNode;
|
||||
|
||||
errlogInit(0);
|
||||
if (pvtData.atExit) return;
|
||||
if (pvtData.atExit)
|
||||
return;
|
||||
|
||||
plistenerNode = callocMustSucceed(1,sizeof(listenerNode),
|
||||
"errlogAddListener");
|
||||
epicsMutexMustLock(pvtData.listenerLock);
|
||||
@@ -306,7 +338,9 @@ epicsShareFunc void epicsShareAPI errlogRemoveListener(
|
||||
listenerNode *plistenerNode;
|
||||
|
||||
errlogInit(0);
|
||||
if (!pvtData.atExit) epicsMutexMustLock(pvtData.listenerLock);
|
||||
if (!pvtData.atExit)
|
||||
epicsMutexMustLock(pvtData.listenerLock);
|
||||
|
||||
plistenerNode = (listenerNode *)ellFirst(&pvtData.listenerList);
|
||||
while (plistenerNode) {
|
||||
if (plistenerNode->listener==listener) {
|
||||
@@ -316,9 +350,13 @@ epicsShareFunc void epicsShareAPI errlogRemoveListener(
|
||||
}
|
||||
plistenerNode = (listenerNode *)ellNext(&plistenerNode->node);
|
||||
}
|
||||
if (!pvtData.atExit) epicsMutexUnlock(pvtData.listenerLock);
|
||||
|
||||
if (!pvtData.atExit)
|
||||
epicsMutexUnlock(pvtData.listenerLock);
|
||||
|
||||
if (!plistenerNode) {
|
||||
fprintf(stderr, "errlogRemoveListener did not find listener\n");
|
||||
fprintf(pvtData.console,
|
||||
"errlogRemoveListener did not find listener\n");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -328,9 +366,16 @@ epicsShareFunc int epicsShareAPI eltc(int yesno)
|
||||
pvtData.toConsole = yesno;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
epicsShareFunc int errlogSetConsole(FILE *stream)
|
||||
{
|
||||
errlogInit(0);
|
||||
pvtData.console = stream ? stream : stderr;
|
||||
return 0;
|
||||
}
|
||||
|
||||
epicsShareFunc void errPrintf(long status, const char *pFileName,
|
||||
int lineno, const char *pformat, ...)
|
||||
int lineno, const char *pformat, ...)
|
||||
{
|
||||
va_list pvar;
|
||||
char *pnext;
|
||||
@@ -343,30 +388,43 @@ epicsShareFunc void errPrintf(long status, const char *pFileName,
|
||||
epicsInterruptContextMessage("errPrintf called from interrupt level\n");
|
||||
return;
|
||||
}
|
||||
|
||||
errlogInit(0);
|
||||
isOkToBlock = epicsThreadIsOkToBlock();
|
||||
if (status == 0) status = errno;
|
||||
if (status == 0)
|
||||
status = errno;
|
||||
|
||||
if (status > 0) {
|
||||
errSymLookup(status, name, sizeof(name));
|
||||
}
|
||||
|
||||
if (pvtData.atExit || (isOkToBlock && pvtData.toConsole)) {
|
||||
if (pFileName) fprintf(stderr, "filename=\"%s\" line number=%d\n",
|
||||
pFileName, lineno);
|
||||
if (status>0) fprintf(stderr, "%s ", name);
|
||||
if (pFileName)
|
||||
fprintf(pvtData.console, "filename=\"%s\" line number=%d\n",
|
||||
pFileName, lineno);
|
||||
if (status > 0)
|
||||
fprintf(pvtData.console, "%s ", name);
|
||||
|
||||
va_start(pvar, pformat);
|
||||
vfprintf(stderr, pformat, pvar);
|
||||
vfprintf(pvtData.console, pformat, pvar);
|
||||
va_end(pvar);
|
||||
fputc('\n', stderr);
|
||||
fflush(stderr);
|
||||
fputc('\n', pvtData.console);
|
||||
fflush(pvtData.console);
|
||||
}
|
||||
if (pvtData.atExit) return;
|
||||
|
||||
if (pvtData.atExit)
|
||||
return;
|
||||
|
||||
pnext = msgbufGetFree(isOkToBlock);
|
||||
if (!pnext) return;
|
||||
if (!pnext)
|
||||
return;
|
||||
|
||||
if (pFileName) {
|
||||
nchar = sprintf(pnext,"filename=\"%s\" line number=%d\n",
|
||||
pFileName, lineno);
|
||||
pnext += nchar; totalChar += nchar;
|
||||
}
|
||||
|
||||
if (status > 0) {
|
||||
nchar = sprintf(pnext,"%s ",name);
|
||||
pnext += nchar; totalChar += nchar;
|
||||
@@ -382,7 +440,8 @@ epicsShareFunc void errPrintf(long status, const char *pFileName,
|
||||
totalChar++ ; /*include the \n */
|
||||
msgbufSetSize(totalChar);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void exitHandler(void *pvt)
|
||||
{
|
||||
pvtData.atExit = 1;
|
||||
@@ -417,6 +476,7 @@ static void errlogInitPvt(void *arg)
|
||||
ellInit(&pvtData.listenerList);
|
||||
ellInit(&pvtData.msgQueue);
|
||||
pvtData.toConsole = TRUE;
|
||||
pvtData.console = stderr;
|
||||
pvtData.waitForWork = epicsEventMustCreate(epicsEventEmpty);
|
||||
pvtData.listenerLock = epicsMutexMustCreate();
|
||||
pvtData.msgQueueLock = epicsMutexMustCreate();
|
||||
@@ -436,7 +496,8 @@ static void errlogInitPvt(void *arg)
|
||||
pvtData.errlogInitFailed = FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
epicsShareFunc int epicsShareAPI errlogInit2(int bufsize, int maxMsgSize)
|
||||
{
|
||||
static epicsThreadOnceId errlogOnceFlag = EPICS_THREAD_ONCE_INIT;
|
||||
@@ -445,31 +506,42 @@ epicsShareFunc int epicsShareAPI errlogInit2(int bufsize, int maxMsgSize)
|
||||
if (pvtData.atExit)
|
||||
return 0;
|
||||
|
||||
if (bufsize < BUFFER_SIZE) bufsize = BUFFER_SIZE;
|
||||
if (bufsize < BUFFER_SIZE)
|
||||
bufsize = BUFFER_SIZE;
|
||||
config.bufsize = bufsize;
|
||||
if (maxMsgSize < MAX_MESSAGE_SIZE) maxMsgSize = MAX_MESSAGE_SIZE;
|
||||
|
||||
if (maxMsgSize < MAX_MESSAGE_SIZE)
|
||||
maxMsgSize = MAX_MESSAGE_SIZE;
|
||||
config.maxMsgSize = maxMsgSize;
|
||||
epicsThreadOnce(&errlogOnceFlag, errlogInitPvt, (void *)&config);
|
||||
|
||||
epicsThreadOnce(&errlogOnceFlag, errlogInitPvt, &config);
|
||||
if (pvtData.errlogInitFailed) {
|
||||
fprintf(stderr,"errlogInit failed\n");
|
||||
exit(1);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
epicsShareFunc int epicsShareAPI errlogInit(int bufsize)
|
||||
{
|
||||
return errlogInit2(bufsize, MAX_MESSAGE_SIZE);
|
||||
}
|
||||
|
||||
epicsShareFunc void epicsShareAPI errlogFlush(void)
|
||||
{
|
||||
int count;
|
||||
|
||||
errlogInit(0);
|
||||
if (pvtData.atExit) return;
|
||||
if (pvtData.atExit)
|
||||
return;
|
||||
|
||||
/*If nothing in queue dont wake up errlogThread*/
|
||||
epicsMutexMustLock(pvtData.msgQueueLock);
|
||||
count = ellCount(&pvtData.msgQueue);
|
||||
epicsMutexUnlock(pvtData.msgQueueLock);
|
||||
if (count <= 0) return;
|
||||
if (count <= 0)
|
||||
return;
|
||||
|
||||
/*must let errlogThread empty queue*/
|
||||
epicsMutexMustLock(pvtData.flushLock);
|
||||
epicsEventSignal(pvtData.flush);
|
||||
@@ -490,25 +562,32 @@ static void errlogThread(void)
|
||||
while ((pmessage = msgbufGetSend(&noConsoleMessage))) {
|
||||
epicsMutexMustLock(pvtData.listenerLock);
|
||||
if (pvtData.toConsole && !noConsoleMessage) {
|
||||
fprintf(stderr,"%s",pmessage);
|
||||
fflush(stderr);
|
||||
fprintf(pvtData.console,"%s",pmessage);
|
||||
fflush(pvtData.console);
|
||||
}
|
||||
|
||||
plistenerNode = (listenerNode *)ellFirst(&pvtData.listenerList);
|
||||
while (plistenerNode) {
|
||||
(*plistenerNode->listener)(plistenerNode->pPrivate, pmessage);
|
||||
plistenerNode = (listenerNode *)ellNext(&plistenerNode->node);
|
||||
}
|
||||
|
||||
epicsMutexUnlock(pvtData.listenerLock);
|
||||
msgbufFreeSend();
|
||||
}
|
||||
if (pvtData.atExit) break;
|
||||
if (epicsEventTryWait(pvtData.flush) != epicsEventWaitOK) continue;
|
||||
|
||||
if (pvtData.atExit)
|
||||
break;
|
||||
if (epicsEventTryWait(pvtData.flush) != epicsEventWaitOK)
|
||||
continue;
|
||||
|
||||
epicsThreadSleep(.2); /*just wait an extra .2 seconds*/
|
||||
epicsEventSignal(pvtData.waitForFlush);
|
||||
}
|
||||
epicsEventSignal(pvtData.waitForExit);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static msgNode *msgbufGetNode(void)
|
||||
{
|
||||
char *pbuffer = pvtData.pbuffer;
|
||||
@@ -517,7 +596,8 @@ static msgNode *msgbufGetNode(void)
|
||||
|
||||
if (ellCount(&pvtData.msgQueue) == 0 ) {
|
||||
pnextFree = pbuffer; /* Reset if empty */
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
msgNode *pfirst = (msgNode *)ellFirst(&pvtData.msgQueue);
|
||||
msgNode *plast = (msgNode *)ellLast(&pvtData.msgQueue);
|
||||
char *plimit = pbuffer + pvtData.buffersize;
|
||||
@@ -534,6 +614,7 @@ static msgNode *msgbufGetNode(void)
|
||||
return 0; /* No room */
|
||||
}
|
||||
}
|
||||
|
||||
pnextSend = (msgNode *)pnextFree;
|
||||
pnextSend->message = pnextFree + sizeof(msgNode);
|
||||
pnextSend->length = 0;
|
||||
@@ -557,17 +638,19 @@ static char *msgbufGetFree(int noConsoleMessage)
|
||||
pvtData.missedMessages = 0;
|
||||
ellAdd(&pvtData.msgQueue, &pnextSend->node);
|
||||
}
|
||||
|
||||
pvtData.pnextSend = pnextSend = msgbufGetNode();
|
||||
if (pnextSend) {
|
||||
pnextSend->noConsoleMessage = noConsoleMessage;
|
||||
pnextSend->length = 0;
|
||||
return pnextSend->message; /* NB: msgQueueLock is still locked */
|
||||
}
|
||||
|
||||
++pvtData.missedMessages;
|
||||
epicsMutexUnlock(pvtData.msgQueueLock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static void msgbufSetSize(int size)
|
||||
{
|
||||
msgNode *pnextSend = pvtData.pnextSend;
|
||||
@@ -578,10 +661,7 @@ static void msgbufSetSize(int size)
|
||||
epicsEventSignal(pvtData.waitForWork);
|
||||
}
|
||||
|
||||
/*errlogThread is the only task that calls msgbufGetSend and msgbufFreeSend*/
|
||||
/*Thus errlogThread is the ONLY task that removes messages from msgQueue */
|
||||
/*This is why each can lock and unlock msgQueue */
|
||||
/*This is necessary to prevent other tasks from waiting for errlogThread */
|
||||
|
||||
static char * msgbufGetSend(int *noConsoleMessage)
|
||||
{
|
||||
msgNode *pnextSend;
|
||||
@@ -589,9 +669,11 @@ static char * msgbufGetSend(int *noConsoleMessage)
|
||||
epicsMutexMustLock(pvtData.msgQueueLock);
|
||||
pnextSend = (msgNode *)ellFirst(&pvtData.msgQueue);
|
||||
epicsMutexUnlock(pvtData.msgQueueLock);
|
||||
if (!pnextSend) return(0);
|
||||
if (!pnextSend)
|
||||
return 0;
|
||||
|
||||
*noConsoleMessage = pnextSend->noConsoleMessage;
|
||||
return(pnextSend->message);
|
||||
return pnextSend->message;
|
||||
}
|
||||
|
||||
static void msgbufFreeSend(void)
|
||||
@@ -601,7 +683,7 @@ static void msgbufFreeSend(void)
|
||||
epicsMutexMustLock(pvtData.msgQueueLock);
|
||||
pnextSend = (msgNode *)ellFirst(&pvtData.msgQueue);
|
||||
if (!pnextSend) {
|
||||
fprintf(stderr, "errlog: msgbufFreeSend logic error\n");
|
||||
fprintf(pvtData.console, "errlog: msgbufFreeSend logic error\n");
|
||||
epicsThreadSuspendSelf();
|
||||
}
|
||||
ellDelete(&pvtData.msgQueue, &pnextSend->node);
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#define INCerrlogh
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "shareLib.h"
|
||||
#include "compilerDependencies.h"
|
||||
@@ -46,7 +47,7 @@ epicsShareFunc int errlogSevPrintf(
|
||||
epicsShareFunc int errlogSevVprintf(
|
||||
const errlogSevEnum severity,const char *pformat,va_list pvar);
|
||||
epicsShareFunc int epicsShareAPI errlogMessage(
|
||||
const char *message);
|
||||
const char *message);
|
||||
|
||||
epicsShareFunc char * epicsShareAPI errlogGetSevEnumString(
|
||||
const errlogSevEnum severity);
|
||||
@@ -60,6 +61,8 @@ epicsShareFunc void epicsShareAPI errlogRemoveListener(
|
||||
errlogListener listener);
|
||||
|
||||
epicsShareFunc int epicsShareAPI eltc(int yesno);
|
||||
epicsShareFunc int errlogSetConsole(FILE *stream);
|
||||
|
||||
epicsShareFunc int epicsShareAPI errlogInit(int bufsize);
|
||||
epicsShareFunc int epicsShareAPI errlogInit2(int bufsize, int maxMsgSize);
|
||||
epicsShareFunc void epicsShareAPI errlogFlush(void);
|
||||
|
||||
Reference in New Issue
Block a user