diff --git a/caPutLogApp/Makefile b/caPutLogApp/Makefile index 11f3ccf..2a6d5fa 100644 --- a/caPutLogApp/Makefile +++ b/caPutLogApp/Makefile @@ -8,6 +8,9 @@ LIBRARY_IOC = caPutLog USR_CPPFLAGS += -DUSE_TYPED_RSET USR_CPPFLAGS_WIN32 += -DNOMINMAX +# set time stamp format: +#USR_CPPFLAGS+=-DDEFAULT_TIME_FMT="%Y-%m-%d %H:%M:%S.%6f" + caPutLog_SRCS += caPutLogTask.c caPutLog_SRCS += caPutLogAs.c caPutLog_SRCS += caPutLogClient.c diff --git a/caPutLogApp/caPutJsonLogTask.cpp b/caPutLogApp/caPutJsonLogTask.cpp index 1cff4eb..31248a1 100644 --- a/caPutLogApp/caPutJsonLogTask.cpp +++ b/caPutLogApp/caPutJsonLogTask.cpp @@ -17,6 +17,10 @@ #include #include +#ifdef _WIN32 +#define strtok_r strtok_s +#endif + // Epics Base imports #include #include @@ -28,6 +32,7 @@ #include #include #include +#include #include // This module imports @@ -63,12 +68,23 @@ CaPutJsonLogTask::CaPutJsonLogTask() : caPutJsonLogQ(caPutLogJsonMsgQueueSize, sizeof(LOGDATA *)), threadId(NULL), taskStopper(false), - caPutJsonLogClient(NULL), + clients(NULL), + clientsMutex(), pCaPutJsonLogPV(NULL) { } CaPutJsonLogTask::~CaPutJsonLogTask() -{ } +{ + clientItem *client, *nextclient; + epicsMutex::guard_t G(clientsMutex); + nextclient = clients; + clients = NULL; + while (nextclient) { + client = nextclient; + nextclient = client->next; + free(client); + } +} caPutJsonLogStatus CaPutJsonLogTask::reconfigure(caPutJsonLogConfig config) { @@ -84,22 +100,26 @@ caPutJsonLogStatus CaPutJsonLogTask::reconfigure(caPutJsonLogConfig config) caPutJsonLogStatus CaPutJsonLogTask::report(int level) { - if (this->caPutJsonLogClient != NULL) { - logClientShow(this->caPutJsonLogClient, level); + + if (clients != NULL) { + clientItem *client; + epicsMutex::guard_t G(clientsMutex); + for (client = clients; client; client = client->next) { + logClientShow(client->caPutJsonLogClient, level); + } return caPutJsonLogSuccess; } else { - errlogSevPrintf(errlogMinor, "caPutJsonLog: log client not initialised\n"); + errlogSevPrintf(errlogMinor, "caPutJsonLog: no clients initialised\n"); return caPutJsonLogError; } } -caPutJsonLogStatus CaPutJsonLogTask::initialize(const char* address, caPutJsonLogConfig config) +caPutJsonLogStatus CaPutJsonLogTask::initialize(const char* addresslist, caPutJsonLogConfig config) { caPutJsonLogStatus status; // Store passed configuration parameters - this->address = epicsStrDup(address); this->reconfigure(config); // Check if user enabled the logger @@ -112,26 +132,44 @@ caPutJsonLogStatus CaPutJsonLogTask::initialize(const char* address, caPutJsonLo this->configurePvLogging(); // Initialize server logging - if (this->caPutJsonLogClient == NULL) { - status = configureServerLogging(address); - if (status != caPutJsonLogSuccess) return status; + if (!addresslist || !addresslist[0]) { + addresslist = envGetConfigParamPtr(&EPICS_CA_JSON_PUT_LOG_ADDR); } - - // Start logger - status = this->start(); - if (status != caPutJsonLogSuccess) { - return status; + if (addresslist == NULL) { + errlogSevPrintf(errlogMajor, "caPutJsonLog: server address not specified\n"); + return caPutJsonLogError; } - // Initialize caPutLogAs - status = static_cast(caPutLogAsInit(caddPutToQueue, NULL)); - if (status != caPutJsonLogSuccess) { - errlogSevPrintf(errlogMinor, "caPutJsonLog: failed to configure Access security\n"); - return caPutJsonLogError; + char *addresslistcopy1, *addresslistcopy2; + addresslistcopy2 = addresslistcopy1 = epicsStrDup(addresslist); + char *saveptr; + while (true) { + char *address = strtok_r(addresslistcopy1, " \t\n\r", &saveptr); + if (!address) break; + addresslistcopy1 = NULL; + configureServerLogging(address); } + free(addresslistcopy2); + if (!clients) + return caPutJsonLogError; + + // Start logger if not done already + if (!threadId) { + status = this->start(); + if (status != caPutJsonLogSuccess) { + return status; + } - // Register exit handler - epicsAtExit(caPutJsonLogExit, NULL); + // Initialize caPutLogAs + status = static_cast(caPutLogAsInit(caddPutToQueue, NULL)); + if (status != caPutJsonLogSuccess) { + errlogSevPrintf(errlogMinor, "caPutJsonLog: failed to configure Access security\n"); + return caPutJsonLogError; + } + + // Register exit handler + epicsAtExit(caPutJsonLogExit, NULL); + } return caPutJsonLogSuccess; } @@ -174,14 +212,15 @@ caPutJsonLogStatus CaPutJsonLogTask::configureServerLogging(const char* address) { int status; struct sockaddr_in saddr; + clientItem** pclient; // Parse the address - if (!address || !address[0]) { - address = envGetConfigParamPtr(&EPICS_CA_JSON_PUT_LOG_ADDR); - } - if (address == NULL) { - errlogSevPrintf(errlogMajor, "caPutJsonLog: server address not specified\n"); - return caPutJsonLogError; + epicsMutex::guard_t G(clientsMutex); + for (pclient = &clients; *pclient; pclient = &(*pclient)->next) { + if (strcmp(address, (*pclient)->address) == 0) { + fprintf (stderr, "caPutJsonLog: address %s already configured\n", address); + return caPutJsonLogSuccess; + } } status = aToIPAddr(address, this->default_port, &saddr); @@ -191,9 +230,15 @@ caPutJsonLogStatus CaPutJsonLogTask::configureServerLogging(const char* address) } // Create log client - this->caPutJsonLogClient = logClientCreate(saddr.sin_addr, ntohs(saddr.sin_port)); - if (this->caPutJsonLogClient == NULL) return caPutJsonLogError; - + *pclient = (clientItem*)callocMustSucceed(1,sizeof(clientItem)+strlen(address),"caPutJsonLog"); + strcpy((*pclient)->address, address); + (*pclient)->caPutJsonLogClient = logClientCreate(saddr.sin_addr, ntohs(saddr.sin_port)); + if (!(*pclient)->caPutJsonLogClient) { + fprintf (stderr, "caPutJsonLog: cannot create logClient %s\n", address); + free(*pclient); + *pclient = NULL; + return caPutJsonLogError; + } return caPutJsonLogSuccess; } @@ -558,8 +603,10 @@ caPutJsonLogStatus CaPutJsonLogTask::buildJsonMsg(const VALUE *pold_value, const void CaPutJsonLogTask::logToServer(std::string &msg) { - if (caPutJsonLogClient) { - logClientSend(caPutJsonLogClient, msg.c_str()); + clientItem* client; + epicsMutex::guard_t G(clientsMutex); + for (client = clients; client; client = client->next) { + logClientSend (client->caPutJsonLogClient, msg.c_str()); } } diff --git a/caPutLogApp/caPutJsonLogTask.h b/caPutLogApp/caPutJsonLogTask.h index 7e476f0..3df4889 100644 --- a/caPutLogApp/caPutJsonLogTask.h +++ b/caPutLogApp/caPutJsonLogTask.h @@ -148,7 +148,6 @@ class epicsShareClass CaPutJsonLogTask { static CaPutJsonLogTask *instance; // Logger configuration - const char * address; int config; // To modify or read this value only epicsAtomic methods should be used // Interthread communication @@ -158,8 +157,13 @@ class epicsShareClass CaPutJsonLogTask { epicsThreadId threadId; int taskStopper; // To modify or read this value only epicsAtomic methods should be used - //Logging to a server - logClientId caPutJsonLogClient; + //Logging to a list of servers + struct clientItem { + logClientId caPutJsonLogClient; + struct clientItem *next; + char address[1]; + } *clients; + epicsMutex clientsMutex; // Logging to a PV DBADDR caPutJsonLogPV; diff --git a/caPutLogApp/caPutLog.c b/caPutLogApp/caPutLog.c index e7e0c31..9683d2f 100644 --- a/caPutLogApp/caPutLog.c +++ b/caPutLogApp/caPutLog.c @@ -44,9 +44,7 @@ void caPutLogShow (int level) { if (level < 0) level = 0; if (level > 2) level = 2; -#if 0 - caPutLogAsShow(level); -#endif + caPutLogTaskShow(); caPutLogClientShow(level); } @@ -55,9 +53,10 @@ void caPutLogShow (int level) */ int caPutLogReconf (int config) { -#if 0 - caPutLogTaskReconf(config); -#endif + if (config < 0) + caPutLogTaskStop(); + else + caPutLogTaskStart(config); caPutLogClientFlush(); return caPutLogSuccess; } diff --git a/caPutLogApp/caPutLog.dbd b/caPutLogApp/caPutLog.dbd index 407a76b..e8b3256 100644 --- a/caPutLogApp/caPutLog.dbd +++ b/caPutLogApp/caPutLog.dbd @@ -1 +1,2 @@ +variable(caPutLogDebug,int) registrar(caPutLogRegister) diff --git a/caPutLogApp/caPutLog.h b/caPutLogApp/caPutLog.h index 246d887..cedb55a 100644 --- a/caPutLogApp/caPutLog.h +++ b/caPutLogApp/caPutLog.h @@ -20,6 +20,7 @@ extern "C" { epicsShareFunc int caPutLogInit (const char *addr_str, int config); epicsShareFunc int caPutLogReconf (int config); epicsShareFunc void caPutLogShow (int level); +epicsShareFunc void caPutLogSetTimeFmt (const char *format); #ifdef __cplusplus } diff --git a/caPutLogApp/caPutLogClient.c b/caPutLogApp/caPutLogClient.c index ec81ef2..b46a7c7 100644 --- a/caPutLogApp/caPutLogClient.c +++ b/caPutLogApp/caPutLogClient.c @@ -26,6 +26,10 @@ #include #include #include +#include +#include +#include +#include #define epicsExportSharedSymbols #include "caPutLog.h" @@ -35,18 +39,30 @@ #define LOCAL static #endif +#ifdef _WIN32 +#define strtok_r strtok_s +#endif + LOCAL READONLY ENV_PARAM EPICS_CA_PUT_LOG_ADDR = {"EPICS_CA_PUT_LOG_ADDR", ""}; -LOCAL logClientId caPutLogClient; +LOCAL struct clientItem { + logClientId caPutLogClient; + struct clientItem *next; + char addr[1]; +} *caPutLogClients = NULL; +static epicsMutexId caPutLogClientsMutex; /* * caPutLogClientFlush () */ void caPutLogClientFlush () { - if (caPutLogClient!=NULL) { - logClientFlush (caPutLogClient); + struct clientItem* c; + epicsMutexMustLock(caPutLogClientsMutex); + for (c = caPutLogClients; c; c = c->next) { + logClientFlush (c->caPutLogClient); } + epicsMutexUnlock(caPutLogClientsMutex); } /* @@ -54,9 +70,12 @@ void caPutLogClientFlush () */ void caPutLogClientShow (unsigned level) { - if (caPutLogClient!=NULL) { - logClientShow (caPutLogClient, level); + struct clientItem* c; + epicsMutexMustLock(caPutLogClientsMutex); + for (c = caPutLogClients; c; c = c->next) { + logClientShow (c->caPutLogClient, level); } + epicsMutexUnlock(caPutLogClientsMutex); } /* @@ -66,13 +85,17 @@ int caPutLogClientInit (const char *addr_str) { int status; struct sockaddr_in saddr; - long default_port = 7011; - - if (caPutLogClient!=NULL) { - return caPutLogSuccess; - } + unsigned short default_port = 7011; + struct clientItem** pclient; + char *clientaddr; + char *saveptr; + char *addr_str_copy1; + char *addr_str_copy2; + if (!caPutLogClientsMutex) + caPutLogClientsMutex = epicsMutexMustCreate(); if (!addr_str || !addr_str[0]) { + if (caPutLogClients) return caPutLogSuccess; addr_str = envGetConfigParamPtr(&EPICS_CA_PUT_LOG_ADDR); } if (addr_str == NULL) { @@ -80,20 +103,44 @@ int caPutLogClientInit (const char *addr_str) return caPutLogError; } - status = aToIPAddr (addr_str, default_port, &saddr); - if (status<0) { - fprintf (stderr, "caPutLog: bad address or host name\n"); - return caPutLogError; - } + addr_str_copy2 = addr_str_copy1 = epicsStrDup(addr_str); - caPutLogClient = logClientCreate (saddr.sin_addr, ntohs(saddr.sin_port)); + epicsMutexMustLock(caPutLogClientsMutex); + while(1) { + clientaddr = strtok_r(addr_str_copy1, " \t\n\r", &saveptr); + if (!clientaddr) break; + addr_str_copy1 = NULL; - if (!caPutLogClient) { - return caPutLogError; - } - else { - return caPutLogSuccess; + for (pclient = &caPutLogClients; *pclient; pclient = &(*pclient)->next) { + if (strcmp(clientaddr, (*pclient)->addr) == 0) { + fprintf (stderr, "caPutLog: address %s already configured\n", clientaddr); + break; + } + } + if (*pclient) continue; + + status = aToIPAddr (clientaddr, default_port, &saddr); + if (status<0) { + fprintf (stderr, "caPutLog: bad address or host name %s\n", clientaddr); + continue; + } + + *pclient = callocMustSucceed(1,sizeof(struct clientItem)+strlen(clientaddr),"caPutLog"); + strcpy((*pclient)->addr, clientaddr); + + (*pclient)->caPutLogClient = logClientCreate (saddr.sin_addr, ntohs(saddr.sin_port)); + if (!(*pclient)->caPutLogClient) { + fprintf (stderr, "caPutLog: cannot create logClient %s\n", clientaddr); + free(*pclient); + *pclient = NULL; + continue; + } + + (*pclient)->next = NULL; } + epicsMutexUnlock(caPutLogClientsMutex); + free(addr_str_copy2); + return caPutLogClients ? caPutLogSuccess : caPutLogError; } /* @@ -101,7 +148,10 @@ int caPutLogClientInit (const char *addr_str) */ void caPutLogClientSend (const char *message) { - if (caPutLogClient) { - logClientSend (caPutLogClient, message); + struct clientItem* c; + epicsMutexMustLock(caPutLogClientsMutex); + for (c = caPutLogClients; c; c = c->next) { + logClientSend (c->caPutLogClient, message); } + epicsMutexUnlock(caPutLogClientsMutex); } diff --git a/caPutLogApp/caPutLogShellCommands.c b/caPutLogApp/caPutLogShellCommands.c index f1098e0..010fd57 100644 --- a/caPutLogApp/caPutLogShellCommands.c +++ b/caPutLogApp/caPutLogShellCommands.c @@ -36,6 +36,16 @@ static void caPutLogShowCall(const iocshArgBuf *args) caPutLogShow(args[0].ival); } +static const iocshArg caPutLogSetTimeFmtArg0 = {"format", iocshArgPersistentString}; +static const iocshArg *const caPutLogSetTimeFmtArgs[] = { + &caPutLogSetTimeFmtArg0 +}; +static const iocshFuncDef caPutLogSetTimeFmtDef = {"caPutLogSetTimeFmt", 1, caPutLogSetTimeFmtArgs}; +static void caPutLogSetTimeFmtCall(const iocshArgBuf *args) +{ + caPutLogSetTimeFmt(args[0].sval); +} + static void caPutLogRegister(void) { static int done = FALSE; @@ -45,5 +55,6 @@ static void caPutLogRegister(void) iocshRegister(&caPutLogInitDef,caPutLogInitCall); iocshRegister(&caPutLogReconfDef,caPutLogReconfCall); iocshRegister(&caPutLogShowDef,caPutLogShowCall); + iocshRegister(&caPutLogSetTimeFmtDef,caPutLogSetTimeFmtCall); } epicsExportRegistrar(caPutLogRegister); diff --git a/caPutLogApp/caPutLogTask.c b/caPutLogApp/caPutLogTask.c index 20922d2..51a81d9 100644 --- a/caPutLogApp/caPutLogTask.c +++ b/caPutLogApp/caPutLogTask.c @@ -47,6 +47,7 @@ #include #include +#include #include #include #include @@ -55,7 +56,7 @@ #include #include -#define epicsExportSharedSymbols +#include #include "caPutLog.h" #include "caPutLogAs.h" #include "caPutLogClient.h" @@ -81,6 +82,15 @@ #define MAX_BUF_SIZE 256 /* Length of log string */ +#ifndef DEFAULT_TIME_FMT +#define DEFAULT_TIME_FMT %d-%b-%y %H:%M:%S +#endif +#define STR2(x) #x +#define STR(x) STR2(x) +#define DEFAULT_TIME_FMT_STR STR(DEFAULT_TIME_FMT) + +static const char *timeFormat = DEFAULT_TIME_FMT_STR; + static void caPutLogTask(void *arg); static void log_msg(const VALUE *pold_value, const LOGDATA *pLogData, int burst, const VALUE *pmin, const VALUE *pmax, int config); @@ -89,17 +99,19 @@ static void val_min(VALUE *pres, const VALUE *pa, const VALUE *pb, short type); static void val_max(VALUE *pres, const VALUE *pa, const VALUE *pb, short type); static int val_equal(const VALUE *pa, const VALUE *pb, short type); static void val_assign(VALUE *dst, const VALUE *src, short type); -#if 0 static void val_dump(LOGDATA *pdata); -#endif -static int shut_down = FALSE; /* Shut down flag */ static DBADDR caPutLogPV; /* Structure to keep address of Log PV */ static DBADDR *pcaPutLogPV; /* Pointer to PV address structure, also used as a flag whether this PV is defined or not */ static epicsMessageQueueId caPutLogQ; /* Mailbox for caPutLogTask */ +static volatile int caPutLogConfig; + +int caPutLogDebug = 0; +epicsExportAddress(int, caPutLogDebug); + #define MAX_MSGS 1000 /* The length of queue (in messages) */ #define MSG_SIZE sizeof(LOGDATA*) /* We store only pointers */ @@ -128,10 +140,9 @@ int caPutLogTaskStart(int config) if (!caPutLogPVEnv || !caPutLogPVEnv[0]) { pcaPutLogPV = NULL; /* If no -- clear pointer */ -#if 0 - errlogSevPrintf(errlogMinor, - "caPutLog: EPICS_AS_PUT_LOG_PV variable not defined. CA Put Logging to PV is disabled\n"); -#endif + if (caPutLogDebug) + errlogSevPrintf(errlogMinor, + "caPutLog: EPICS_AS_PUT_LOG_PV variable not defined. CA Put Logging to PV is disabled\n"); } else { long getpv_st; @@ -145,14 +156,16 @@ int caPutLogTaskStart(int config) } } + caPutLogConfig = config; + if (epicsThreadGetId("caPutLog")) { - errlogSevPrintf(errlogInfo, "caPutLog: task already running\n"); + if (caPutLogDebug) + errlogSevPrintf(errlogInfo, "caPutLog: task already running\n"); return caPutLogSuccess; } - shut_down = FALSE; threadId = epicsThreadCreate("caPutLog", epicsThreadPriorityLow, epicsThreadGetStackSize(epicsThreadStackSmall), - caPutLogTask, (void*)(size_t)config); + caPutLogTask, NULL); if (!threadId) { errlogSevPrintf(errlogFatal,"caPutLog: thread creation failed\n"); return caPutLogError; @@ -160,36 +173,71 @@ int caPutLogTaskStart(int config) return caPutLogSuccess; } +void caPutLogTaskShow(void) +{ + const char *state; + switch (caPutLogConfig) { + case caPutLogNone: state = "disabled"; break; + case caPutLogOnChange: state = "default (on change, squash bursts)"; break; + case caPutLogAll: state = "all (same value too, squash bursts)"; break; + case caPutLogAllNoFilter: state = "no filter (every single put)"; break; + default: state = "invalid"; + } + printf("caPutLog mode: %d = %s\n", caPutLogConfig, state); +} + void caPutLogTaskStop(void) { - shut_down = TRUE; + caPutLogConfig = caPutLogNone; + printf("waiting for caPutLogTask to terminate\n"); + while (epicsThreadGetId("caPutLog")) { + epicsThreadSleep(1); + } } void caPutLogTaskSend(LOGDATA *plogData) { + static int overflow = 0; if (caPutLogQ) { if (!epicsMessageQueueTrySend(caPutLogQ, &plogData, MSG_SIZE)) + { + overflow = 0; return; - errlogSevPrintf(errlogMinor, "caPutLog: message queue overflow\n"); + } + if (!overflow) { + errlogSevPrintf(errlogMinor, "caPutLog: message queue overflow\n"); + overflow = 1; + } } caPutLogDataFree(plogData); } +void caPutLogSetTimeFmt (const char *format) +{ + if (format) + timeFormat = format; +} + static void caPutLogTask(void *arg) { int sent = FALSE, burst = FALSE; - int config = (size_t)arg; + int config; + int msg_size; LOGDATA *pcurrent, *pnext; VALUE old_value, max_value, min_value; VALUE *pold=&old_value, *pmax=&max_value, *pmin=&min_value; /* Receive 1st message */ - epicsMessageQueueReceive(caPutLogQ, &pcurrent, MSG_SIZE); + while (caPutLogConfig != caPutLogNone) { + msg_size = epicsMessageQueueReceiveWithTimeout(caPutLogQ, &pcurrent, MSG_SIZE, 5.0); + if (msg_size != -1) break; + } + if (caPutLogConfig == caPutLogNone) return; -#if 0 - printf("caPutLog: received a message\n"); - val_dump(pcurrent); -#endif + if (caPutLogDebug) { + printf("caPutLog: received a message\n"); + val_dump(pcurrent); + } /* Store the initial old_value */ val_assign(pold, &pcurrent->old_value, pcurrent->type); @@ -198,11 +246,11 @@ static void caPutLogTask(void *arg) val_assign(pmax, &pcurrent->new_value.value, pcurrent->type); val_assign(pmin, &pcurrent->new_value.value, pcurrent->type); - while (!shut_down) { /* Main Server Loop */ - int msg_size; + while (caPutLogConfig != caPutLogNone) { /* Main Server Loop */ /* Receive next message */ msg_size = epicsMessageQueueReceiveWithTimeout(caPutLogQ, &pnext, MSG_SIZE, 5.0); + config = caPutLogConfig; if (msg_size == -1) { /* timeout */ if (!sent) { @@ -216,11 +264,11 @@ static void caPutLogTask(void *arg) errlogSevPrintf(errlogMinor, "caPutLog: discarding incomplete log data message\n"); } else if ((pnext->pfield == pcurrent->pfield) && (config != caPutLogAllNoFilter)) { + if (caPutLogDebug) { + printf("caPutLog: received a message, same pv\n"); + val_dump(pnext); + } -#if 0 - printf("caPutLog: received a message, same pv\n"); - val_dump(pnext); -#endif /* current and next are same pv */ caPutLogDataFree(pcurrent); @@ -243,11 +291,10 @@ static void caPutLogTask(void *arg) } } else { - -#if 0 - printf("caPutLog: received a message, different pv\n"); - val_dump(pnext); -#endif + if (caPutLogDebug) { + printf("caPutLog: received a message, different pv\n"); + val_dump(pnext); + } /* current and next are different pvs */ if (!sent) { @@ -316,9 +363,9 @@ static void log_msg(const VALUE *pold_value, const LOGDATA *pLogData, } /* first comes the time */ - len = epicsTimeToStrftime(msg, space, "%d-%b-%y %H:%M:%S", + len = epicsTimeToStrftime(msg, space, timeFormat, &pLogData->new_value.time); - /* this should always succeed (18 chars, last time i counted */ + /* this should always succeed */ assert(len); /* host, user, pv_name */ @@ -520,8 +567,8 @@ static int val_to_string(char *pbuf, size_t buflen, const VALUE *pval, short typ switch (type) { case DBR_CHAR: /* CHAR and UCHAR are typically used as SHORTSHORT, - * so avoid mounting NULL-bytes into the string - */ + * so avoid mounting NULL-bytes into the string + */ return epicsSnprintf(pbuf, buflen, "%d", (int)pval->v_uint8); case DBR_UCHAR: return epicsSnprintf(pbuf, buflen, "%d", (int)pval->v_uint8); @@ -545,11 +592,16 @@ static int val_to_string(char *pbuf, size_t buflen, const VALUE *pval, short typ return epicsSnprintf(pbuf, buflen, "%llu", pval->v_uint64); #endif default: - return epicsSnprintf(pbuf, buflen, "%s", pval->v_string); + if (buflen < 3) return 0; + pbuf[0] = '"'; + epicsStrnEscapedFromRaw(pbuf+1, buflen-2, pval->v_string, strlen(pval->v_string)); + buflen = strlen(pbuf); + pbuf[buflen++] = '"'; + pbuf[buflen] = 0; + return buflen; } } -#if 0 static void val_dump(LOGDATA *pdata) { char oldbuf[512], newbuf[512], timebuf[64]; @@ -575,4 +627,3 @@ static void val_dump(LOGDATA *pdata) printf("new_value.value = %s\n", newbuf); } } -#endif diff --git a/caPutLogApp/caPutLogTask.h b/caPutLogApp/caPutLogTask.h index 45b6c41..b0b90e6 100644 --- a/caPutLogApp/caPutLogTask.h +++ b/caPutLogApp/caPutLogTask.h @@ -69,6 +69,7 @@ typedef struct { epicsShareFunc int caPutLogTaskStart(int config); epicsShareFunc void caPutLogTaskStop(void); epicsShareFunc void caPutLogTaskSend(LOGDATA *plogData); +epicsShareFunc void caPutLogTaskShow(void); #ifdef __cplusplus } diff --git a/docs/index.rst b/docs/index.rst index 72295b1..d1dde20 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -100,6 +100,9 @@ or for JSON output format:: where ``host`` (mandatory argument) is the IP address or host name of the log server and ``port`` is optional (the default is 7011). +To log to multiple hosts, either call the funtion with a space separated list like +``"host1[:port] host2[:port]"`` or call the function multiple times with different +hosts. The environment variable ``EPICS_CA_PUT_LOG_ADDR`` / ``EPICS_CA_PUT_JSON_LOG_ADDR`` is used if the first parameter to ``caPutLogInit`` / ``caPutJsonLogInit`` is ``NULL`` @@ -135,7 +138,7 @@ Other shell commands for logger are: ``caPutLogInit`` / ``caPutJsonLogInit``. ``caPutLogShow level`` / ``caPutJsonLogShow level`` - Show information about a running caPutLog, + Show information about a running caPutLog, including config parameter. level is the usual interest level (0, 1, or 2). Server @@ -173,6 +176,10 @@ in rapid succession; in this case only the original and the final value as well as the minimum and maximum value are logged. This filtering can be disabled by specifying the ``caPutLogAllNoFilter`` (``2``) configuration option. +From release 4 on, string values are quoted and special characters are escaped. +The default date/time format ``%d-%b-%y %H:%M:%S`` may be changed at compile time +with the macro DEFAULT_TIME_FMT and/or modified at run time using the shell function +``caPutLogSetTimeFmt ""``. Json Log Format +++++++++++++++ @@ -256,6 +263,12 @@ too long for the record it will be truncated. .. note:: As of EPICS base 7.0.1 ``lso``/``lsi`` records will be truncate a message at 40 character. As workaround add ``.$`` or ``.VAL$`` to a PV name. +Debugging ++++++++++ + +To switch on debug messages, set ``var caPutLogDebug,1``. + + Acknowledgments ---------------- diff --git a/docs/releasenotes.rst b/docs/releasenotes.rst index 6c3c8eb..7b70658 100644 --- a/docs/releasenotes.rst +++ b/docs/releasenotes.rst @@ -7,6 +7,11 @@ Changes since R3-7 ------------------------- * add new JSON log format + * quote and escape strings in non-JSON logs + * allow to change time format + * allow multiple receivers + * fix caPutLogReconf which was non-functional + * allow to switch on debug messages at run time .. _R3-5: