From da22e34915e4873c39e7ab73823f37e88c0c652b Mon Sep 17 00:00:00 2001 From: Dirk Zimoch Date: Mon, 25 Nov 2019 14:31:41 +0100 Subject: [PATCH 1/9] do not flood terminal with message queue overflow messages Conflicts: caPutLogApp/caPutLogTask.c --- caPutLogApp/caPutLogTask.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/caPutLogApp/caPutLogTask.c b/caPutLogApp/caPutLogTask.c index 20922d2..daf7890 100644 --- a/caPutLogApp/caPutLogTask.c +++ b/caPutLogApp/caPutLogTask.c @@ -167,10 +167,17 @@ void caPutLogTaskStop(void) 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); } From 7f79ea101a3f2c4f9ffd50bfebbbe3eacb5a1ecb Mon Sep 17 00:00:00 2001 From: Dirk Zimoch Date: Thu, 29 Jun 2017 16:09:57 +0200 Subject: [PATCH 2/9] quote strings in log --- caPutLogApp/caPutLogTask.c | 13 ++++++++++--- docs/index.rst | 1 + docs/releasenotes.rst | 1 + 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/caPutLogApp/caPutLogTask.c b/caPutLogApp/caPutLogTask.c index daf7890..aa32ca8 100644 --- a/caPutLogApp/caPutLogTask.c +++ b/caPutLogApp/caPutLogTask.c @@ -47,6 +47,7 @@ #include #include +#include #include #include #include @@ -527,8 +528,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); @@ -552,7 +553,13 @@ 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; } } diff --git a/docs/index.rst b/docs/index.rst index 72295b1..4164150 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -173,6 +173,7 @@ 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. Json Log Format +++++++++++++++ diff --git a/docs/releasenotes.rst b/docs/releasenotes.rst index 6c3c8eb..f41320e 100644 --- a/docs/releasenotes.rst +++ b/docs/releasenotes.rst @@ -7,6 +7,7 @@ Changes since R3-7 ------------------------- * add new JSON log format + * quote and escape strings in non-JSON logs .. _R3-5: From 4f3e755d7270a2157efd05a58448d4ddcfc8f32d Mon Sep 17 00:00:00 2001 From: Dirk Zimoch Date: Mon, 16 Dec 2019 17:52:12 +0100 Subject: [PATCH 3/9] allow to change time stamp format Conflicts: caPutLogApp/caPutLogTask.c --- caPutLogApp/Makefile | 3 +++ caPutLogApp/caPutLog.h | 1 + caPutLogApp/caPutLogShellCommands.c | 11 +++++++++++ caPutLogApp/caPutLogTask.c | 19 +++++++++++++++++-- docs/index.rst | 3 +++ docs/releasenotes.rst | 1 + 6 files changed, 36 insertions(+), 2 deletions(-) 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/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/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 aa32ca8..156d334 100644 --- a/caPutLogApp/caPutLogTask.c +++ b/caPutLogApp/caPutLogTask.c @@ -82,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); @@ -183,6 +192,12 @@ void caPutLogTaskSend(LOGDATA *plogData) caPutLogDataFree(plogData); } +void caPutLogSetTimeFmt (const char *format) +{ + if (format) + timeFormat = format; +} + static void caPutLogTask(void *arg) { int sent = FALSE, burst = FALSE; @@ -324,9 +339,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 */ diff --git a/docs/index.rst b/docs/index.rst index 4164150..6f68254 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -174,6 +174,9 @@ 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 +++++++++++++++ diff --git a/docs/releasenotes.rst b/docs/releasenotes.rst index f41320e..e55b582 100644 --- a/docs/releasenotes.rst +++ b/docs/releasenotes.rst @@ -8,6 +8,7 @@ Changes since R3-7 * add new JSON log format * quote and escape strings in non-JSON logs + * allow to change time format .. _R3-5: From ec616c3fea5da03d44165ca0b640d5e138207273 Mon Sep 17 00:00:00 2001 From: Dirk Zimoch Date: Tue, 6 Aug 2019 17:58:50 +0200 Subject: [PATCH 4/9] allow multiple log receivers --- caPutLogApp/caPutLogClient.c | 87 ++++++++++++++++++++++++++---------- docs/index.rst | 3 ++ docs/releasenotes.rst | 1 + 3 files changed, 68 insertions(+), 23 deletions(-) diff --git a/caPutLogApp/caPutLogClient.c b/caPutLogApp/caPutLogClient.c index ec81ef2..b0af849 100644 --- a/caPutLogApp/caPutLogClient.c +++ b/caPutLogApp/caPutLogClient.c @@ -26,6 +26,8 @@ #include #include #include +#include +#include #define epicsExportSharedSymbols #include "caPutLog.h" @@ -35,17 +37,26 @@ #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 clientlist { + logClientId caPutLogClient; + struct clientlist *next; + char addr[1]; +} *caPutLogClients = NULL; /* * caPutLogClientFlush () */ void caPutLogClientFlush () { - if (caPutLogClient!=NULL) { - logClientFlush (caPutLogClient); + struct clientlist* c; + for (c = caPutLogClients; c; c = c->next) { + logClientFlush (c->caPutLogClient); } } @@ -54,8 +65,9 @@ void caPutLogClientFlush () */ void caPutLogClientShow (unsigned level) { - if (caPutLogClient!=NULL) { - logClientShow (caPutLogClient, level); + struct clientlist* c; + for (c = caPutLogClients; c; c = c->next) { + logClientShow (c->caPutLogClient, level); } } @@ -66,13 +78,15 @@ 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 clientlist** pclient; + char *clientaddr; + char *saveptr; + char *addr_str_copy1; + char *addr_str_copy2; if (!addr_str || !addr_str[0]) { + if (caPutLogClients) return caPutLogSuccess; addr_str = envGetConfigParamPtr(&EPICS_CA_PUT_LOG_ADDR); } if (addr_str == NULL) { @@ -80,20 +94,46 @@ 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)); + 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 = malloc(sizeof(struct clientlist)+strlen(clientaddr)); + if (!*pclient) { + fprintf (stderr, "caPutLog: out of memory\n"); + return caPutLogError; + } + 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; } + free(addr_str_copy2); + return caPutLogClients ? caPutLogSuccess : caPutLogError; } /* @@ -101,7 +141,8 @@ int caPutLogClientInit (const char *addr_str) */ void caPutLogClientSend (const char *message) { - if (caPutLogClient) { - logClientSend (caPutLogClient, message); + struct clientlist* c; + for (c = caPutLogClients; c; c = c->next) { + logClientSend (c->caPutLogClient, message); } } diff --git a/docs/index.rst b/docs/index.rst index 6f68254..909e06f 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`` diff --git a/docs/releasenotes.rst b/docs/releasenotes.rst index e55b582..799426c 100644 --- a/docs/releasenotes.rst +++ b/docs/releasenotes.rst @@ -9,6 +9,7 @@ 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 .. _R3-5: From 20fa487559f75a423a6c99bacfe6a3e982b3dec0 Mon Sep 17 00:00:00 2001 From: Dirk Zimoch Date: Thu, 10 Dec 2020 17:18:01 +0100 Subject: [PATCH 5/9] make caPutLogReconf functional --- caPutLogApp/caPutLog.c | 8 +++++--- caPutLogApp/caPutLogTask.c | 42 ++++++++++++++++++++++++++++++-------- caPutLogApp/caPutLogTask.h | 1 + docs/index.rst | 2 +- docs/releasenotes.rst | 1 + 5 files changed, 42 insertions(+), 12 deletions(-) diff --git a/caPutLogApp/caPutLog.c b/caPutLogApp/caPutLog.c index e7e0c31..a5a3b7e 100644 --- a/caPutLogApp/caPutLog.c +++ b/caPutLogApp/caPutLog.c @@ -44,6 +44,7 @@ void caPutLogShow (int level) { if (level < 0) level = 0; if (level > 2) level = 2; + caPutLogTaskShow(); #if 0 caPutLogAsShow(level); #endif @@ -55,9 +56,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/caPutLogTask.c b/caPutLogApp/caPutLogTask.c index 156d334..488eb0e 100644 --- a/caPutLogApp/caPutLogTask.c +++ b/caPutLogApp/caPutLogTask.c @@ -103,13 +103,14 @@ static void val_assign(VALUE *dst, const VALUE *src, short type); 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; + #define MAX_MSGS 1000 /* The length of queue (in messages) */ #define MSG_SIZE sizeof(LOGDATA*) /* We store only pointers */ @@ -155,14 +156,17 @@ int caPutLogTaskStart(int config) } } + caPutLogConfig = config; + if (epicsThreadGetId("caPutLog")) { +#if 0 errlogSevPrintf(errlogInfo, "caPutLog: task already running\n"); +#endif 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; @@ -170,9 +174,26 @@ 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) @@ -201,13 +222,18 @@ void caPutLogSetTimeFmt (const char *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"); @@ -221,11 +247,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) { 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 909e06f..7eeca9f 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -138,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 diff --git a/docs/releasenotes.rst b/docs/releasenotes.rst index 799426c..6e437ad 100644 --- a/docs/releasenotes.rst +++ b/docs/releasenotes.rst @@ -10,6 +10,7 @@ Changes since R3-7 * quote and escape strings in non-JSON logs * allow to change time format * allow multiple receivers + * fix caPutLogReconf which was non-functional .. _R3-5: From a5b6bf7949c415d62bf4b4a078c5a10be3721c5d Mon Sep 17 00:00:00 2001 From: Dirk Zimoch Date: Mon, 13 Sep 2021 14:41:00 +0200 Subject: [PATCH 6/9] allow to switch on debug messages at run time --- caPutLogApp/caPutLog.dbd | 1 + caPutLogApp/caPutLogTask.c | 46 +++++++++++++++++--------------------- docs/index.rst | 6 +++++ docs/releasenotes.rst | 1 + 4 files changed, 29 insertions(+), 25 deletions(-) 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/caPutLogTask.c b/caPutLogApp/caPutLogTask.c index 488eb0e..51a81d9 100644 --- a/caPutLogApp/caPutLogTask.c +++ b/caPutLogApp/caPutLogTask.c @@ -56,7 +56,7 @@ #include #include -#define epicsExportSharedSymbols +#include #include "caPutLog.h" #include "caPutLogAs.h" #include "caPutLogClient.h" @@ -99,9 +99,7 @@ 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 DBADDR caPutLogPV; /* Structure to keep address of Log PV */ static DBADDR *pcaPutLogPV; /* Pointer to PV address structure, @@ -111,6 +109,9 @@ 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 */ @@ -139,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; @@ -159,9 +159,8 @@ int caPutLogTaskStart(int config) caPutLogConfig = config; if (epicsThreadGetId("caPutLog")) { -#if 0 - errlogSevPrintf(errlogInfo, "caPutLog: task already running\n"); -#endif + if (caPutLogDebug) + errlogSevPrintf(errlogInfo, "caPutLog: task already running\n"); return caPutLogSuccess; } threadId = epicsThreadCreate("caPutLog", epicsThreadPriorityLow, @@ -235,10 +234,10 @@ static void caPutLogTask(void *arg) } 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); @@ -265,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); @@ -292,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) { @@ -604,7 +602,6 @@ static int val_to_string(char *pbuf, size_t buflen, const VALUE *pval, short typ } } -#if 0 static void val_dump(LOGDATA *pdata) { char oldbuf[512], newbuf[512], timebuf[64]; @@ -630,4 +627,3 @@ static void val_dump(LOGDATA *pdata) printf("new_value.value = %s\n", newbuf); } } -#endif diff --git a/docs/index.rst b/docs/index.rst index 7eeca9f..d1dde20 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -263,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 6e437ad..7b70658 100644 --- a/docs/releasenotes.rst +++ b/docs/releasenotes.rst @@ -11,6 +11,7 @@ Changes since R3-7 * 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: From f65035afbb64661a4b8b6c0b473a841075d3e188 Mon Sep 17 00:00:00 2001 From: Dirk Zimoch Date: Mon, 13 Sep 2021 14:44:11 +0200 Subject: [PATCH 7/9] cleanup dead code --- caPutLogApp/caPutLog.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/caPutLogApp/caPutLog.c b/caPutLogApp/caPutLog.c index a5a3b7e..9683d2f 100644 --- a/caPutLogApp/caPutLog.c +++ b/caPutLogApp/caPutLog.c @@ -45,9 +45,6 @@ void caPutLogShow (int level) if (level < 0) level = 0; if (level > 2) level = 2; caPutLogTaskShow(); -#if 0 - caPutLogAsShow(level); -#endif caPutLogClientShow(level); } From f1eb5ed8cdfaf15a67de403fe8400dc96b374ccf Mon Sep 17 00:00:00 2001 From: Dirk Zimoch Date: Thu, 16 Sep 2021 10:13:19 +0200 Subject: [PATCH 8/9] protect client list with mutex --- caPutLogApp/caPutLogClient.c | 31 ++++++++++++++++++++----------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/caPutLogApp/caPutLogClient.c b/caPutLogApp/caPutLogClient.c index b0af849..b46a7c7 100644 --- a/caPutLogApp/caPutLogClient.c +++ b/caPutLogApp/caPutLogClient.c @@ -28,6 +28,8 @@ #include #include #include +#include +#include #define epicsExportSharedSymbols #include "caPutLog.h" @@ -43,21 +45,24 @@ LOCAL READONLY ENV_PARAM EPICS_CA_PUT_LOG_ADDR = {"EPICS_CA_PUT_LOG_ADDR", ""}; -LOCAL struct clientlist { +LOCAL struct clientItem { logClientId caPutLogClient; - struct clientlist *next; + struct clientItem *next; char addr[1]; } *caPutLogClients = NULL; +static epicsMutexId caPutLogClientsMutex; /* * caPutLogClientFlush () */ void caPutLogClientFlush () { - struct clientlist* c; + struct clientItem* c; + epicsMutexMustLock(caPutLogClientsMutex); for (c = caPutLogClients; c; c = c->next) { logClientFlush (c->caPutLogClient); } + epicsMutexUnlock(caPutLogClientsMutex); } /* @@ -65,10 +70,12 @@ void caPutLogClientFlush () */ void caPutLogClientShow (unsigned level) { - struct clientlist* c; + struct clientItem* c; + epicsMutexMustLock(caPutLogClientsMutex); for (c = caPutLogClients; c; c = c->next) { logClientShow (c->caPutLogClient, level); } + epicsMutexUnlock(caPutLogClientsMutex); } /* @@ -79,12 +86,14 @@ int caPutLogClientInit (const char *addr_str) int status; struct sockaddr_in saddr; unsigned short default_port = 7011; - struct clientlist** pclient; + 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); @@ -96,6 +105,7 @@ int caPutLogClientInit (const char *addr_str) addr_str_copy2 = addr_str_copy1 = epicsStrDup(addr_str); + epicsMutexMustLock(caPutLogClientsMutex); while(1) { clientaddr = strtok_r(addr_str_copy1, " \t\n\r", &saveptr); if (!clientaddr) break; @@ -115,11 +125,7 @@ int caPutLogClientInit (const char *addr_str) continue; } - *pclient = malloc(sizeof(struct clientlist)+strlen(clientaddr)); - if (!*pclient) { - fprintf (stderr, "caPutLog: out of memory\n"); - return caPutLogError; - } + *pclient = callocMustSucceed(1,sizeof(struct clientItem)+strlen(clientaddr),"caPutLog"); strcpy((*pclient)->addr, clientaddr); (*pclient)->caPutLogClient = logClientCreate (saddr.sin_addr, ntohs(saddr.sin_port)); @@ -132,6 +138,7 @@ int caPutLogClientInit (const char *addr_str) (*pclient)->next = NULL; } + epicsMutexUnlock(caPutLogClientsMutex); free(addr_str_copy2); return caPutLogClients ? caPutLogSuccess : caPutLogError; } @@ -141,8 +148,10 @@ int caPutLogClientInit (const char *addr_str) */ void caPutLogClientSend (const char *message) { - struct clientlist* c; + struct clientItem* c; + epicsMutexMustLock(caPutLogClientsMutex); for (c = caPutLogClients; c; c = c->next) { logClientSend (c->caPutLogClient, message); } + epicsMutexUnlock(caPutLogClientsMutex); } From d14203a0a43165af51c4026d76e2f88192f79625 Mon Sep 17 00:00:00 2001 From: Dirk Zimoch Date: Thu, 16 Sep 2021 10:16:12 +0200 Subject: [PATCH 9/9] allow multiple log receivers for caPutJsonLog too --- caPutLogApp/caPutJsonLogTask.cpp | 113 ++++++++++++++++++++++--------- caPutLogApp/caPutJsonLogTask.h | 10 ++- 2 files changed, 87 insertions(+), 36 deletions(-) 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;