Repository: hpcc-systems/HPCC-Platform
Pull request #19783: HPCC-33885 Ensure persistent components do not restart, but pick up logging config changes
Author: login: streeterd
Created: 2025-04-24T07:41:59Z | Merged: 2025-05-22T15:55:59Z

<!-- Thank you for submitting a pull request to the HPCC project

 PLEASE READ the following before proceeding.

 This project only accepts pull requests related to open JIRA issues.
 If suggesting a new feature or change, please discuss it in a JIRA issue first.
 If fixing a bug, there should be an issue describing it with steps to reproduce.
 The title line of the pull request (and of each commit within it) should refer to the
 associated issue using the format:

 HPCC-nnnnn Short description of issue

 This will allow the Jira ticket to be automatically updated to refer to this pull request,
 and will ensure that the automatically-generated changelog is properly formatted.
 Where a pull request contains a single commit the pull request title will be set automatically,
 assuming that the commit has followed the proper guidelines.

 Please go over all the following points, and put an `x` in all the boxes that apply. You may find
 it easier to press the 'Create' button first then click on the checkboxes to edit the comment.
-->

## Type of change:
- [x] This change is a bug fix (non-breaking change which fixes an issue).
- [x] This change is a new feature (non-breaking change which adds functionality).
- [ ] This change improves the code (refactor or other change that does not change the functionality)
- [ ] This change fixes warnings (the fix does not alter the functionality or the generated code)
- [ ] This change is a breaking change (fix or feature that will cause existing behavior to change).
- [ ] This change alters the query API (existing queries will have to be recompiled)

## Checklist:
- [x] My code follows the code style of this project.
  - [ ] My code does not create any new warnings from compiler, build system, or lint.
- [ ] The commit message is properly formatted and free of typos.
  - [ ] The commit message title makes sense in a changelog, by itself.
  - [ ] The commit is signed.
- [ ] My change requires a change to the documentation.
  - [ ] I have updated the documentation accordingly, or...
  - [ ] I have created a JIRA ticket to update the documentation.
  - [ ] Any new interfaces or exported functions are appropriately commented.
- [ ] I have read the CONTRIBUTORS document.
- [ ] The change has been fully tested:
  - [ ] I have added tests to cover my changes.
  - [ ] All new and existing tests passed.
  - [ ] I have checked that this change does not introduce memory leaks.
  - [ ] I have used Valgrind or similar tools to check for potential issues.
- [ ] I have given due consideration to all of the following potential concerns:
  - [ ] Scalability
  - [ ] Performance
  - [ ] Security
  - [ ] Thread-safety
  - [ ] Cloud-compatibility
  - [ ] Premature optimization
  - [ ] Existing deployed queries will not be broken
  - [ ] This change fixes the problem, not just the symptom
  - [ ] The target branch of this pull request is appropriate for such a change.
- [ ] There are no similar instances of the same problem that should be addressed
  - [ ] I have addressed them here
  - [ ] I have raised JIRA issues to address them separately
- [ ] This is a user interface / front-end modification
  - [ ] I have tested my changes in multiple modern browsers
  - [ ] The component(s) render as expected

## Smoketest:
- [ ] Send notifications about my Pull Request position in Smoketest queue.
- [ ] Test my draft Pull Request.

## Testing:
<!-- Please describe how this change has been tested.-->

<!-- Thank you for taking the time to submit this pull request and to answer all of the above-->



Diff:
diff --git a/dali/base/sysinfologger.cpp b/dali/base/sysinfologger.cpp
index 64bca9f0146..d82e345b9fd 100644
--- a/dali/base/sysinfologger.cpp
+++ b/dali/base/sysinfologger.cpp
@@ -778,6 +778,10 @@ class DaliMsgLoggerHandler : public CSimpleInterfaceOf<ILogMsgHandler>
     {
         return 0;
     }
+    virtual LogHandlerFormat queryFormatType() const override
+    {
+         return LOGFORMAT_undefined;
+    };
 private:
     unsigned messageFields = MSGFIELD_all;
 };
diff --git a/helm/hpcc/templates/_helpers.tpl b/helm/hpcc/templates/_helpers.tpl
index 43b2ec80fa6..53cf09ca99d 100644
--- a/helm/hpcc/templates/_helpers.tpl
+++ b/helm/hpcc/templates/_helpers.tpl
@@ -2623,7 +2623,7 @@ globalExcludeList below is a hard-coded list of global keys to exclude.
 
 */}}
 {{- define "hpcc.getConfigSHA" }}
-{{- $globalExcludeList := list "~.*::replicas" "~.*::vaults" "~.*::warnings" -}}
+{{- $globalExcludeList := list "~.*::logging" "~.*::replicas" "~.*::vaults" "~.*::warnings" -}}
 {{- $globalExcludeSectionRegexList := list ".*-job.yaml$" -}}
 {{- $componentExcludeList := ternary (splitList "," (.excludeKeys | default "")) list (hasKey . "excludeKeys") -}}
 {{- $combinedExcludeKeyList := concat $globalExcludeList $componentExcludeList -}}
diff --git a/helm/hpcc/values.schema.json b/helm/hpcc/values.schema.json
index dc27a9dc09c..992473c9f42 100644
--- a/helm/hpcc/values.schema.json
+++ b/helm/hpcc/values.schema.json
@@ -1147,7 +1147,7 @@
           "description": "Number of log entries to drop from the log queue when it is full. Disabled if 0, or if queueLength is 0",
           "default": 0
         },
-        "dataFormat": {
+        "format": {
           "type": "string",
           "description": "The data format used to report logging: xml|json|table(space delimited values)",
           "enum": [ "xml", "json", "table"]
@@ -1320,7 +1320,7 @@
               "items": { "type": "object" }
             }
           }
-        }  
+        }
       ]
     },
     "globalEgress": {
@@ -1349,7 +1349,7 @@
         {
           "egress": {
             "$ref" : "#/definitions/egress"
-          },              
+          },
           "additionalProperties": false
         }
       }
@@ -1878,107 +1878,107 @@
           "type": "string",
           "description": "Name of the secret which contains the TLS certificate.  Custom configuration instead of using, or overriding cert-manager certificate."
         },
-        "allFilesDynamic": { 
+        "allFilesDynamic": {
           "type": "boolean",
           "default": false,
           "description": "If enabled, files will be resolved per-query and not locked between queries"
         },
-        "backgroundCopyClass": { 
+        "backgroundCopyClass": {
           "type": "string",
           "default": "none",
           "enum": [ "none", "best-effort", "idle"],
           "description": "Specify an IONICE class for the background copy thread."
         },
-        "backgroundCopyPrio": { 
+        "backgroundCopyPrio": {
           "type": "integer",
           "default": 0,
           "description": "Specify an IONICE value for the background copy thread, if backgroundCopyClass set to best-effort."
         },
-        "blockedLocalAgent": { 
+        "blockedLocalAgent": {
           "type": "boolean",
           "default": true,
           "description": "Used DataBuffer blocks to return agent data in localAgent mode."
         },
-        "callbackRetries": { 
+        "callbackRetries": {
           "type": "integer",
           "default": 3,
           "minimum": 1,
           "description": "Number of retries before callbacks from agents to server are aborted"
         },
-        "callbackTimeout": { 
+        "callbackTimeout": {
           "type": "integer",
           "default": 5000,
           "minimum": 0,
           "description": "Timeout (in ms) before callbacks from agents to server are resent"
         },
-        "checkFileDate": { 
+        "checkFileDate": {
           "type": "boolean",
           "default": true,
           "description": "Compare file dates of physical files with the information in DFS."
         },
-        "collectFactoryStatistics": { 
+        "collectFactoryStatistics": {
           "type": "boolean",
           "default": true,
           "description": "Accumulate summary statistics for all queries"
         },
-        "copyResources": { 
+        "copyResources": {
           "type": "boolean",
           "default": true,
           "description": "Copies any missing data files/keys from the position they were in when query was deployed."
         },
-        "defaultHighPriorityTimeLimit": { 
+        "defaultHighPriorityTimeLimit": {
           "type": "integer",
           "default": 0,
           "minimum": 0,
           "description": "Maximum run time (in ms) for any single active high-priority query (if not overridden)"
         },
-        "defaultHighPriorityTimeWarning": { 
+        "defaultHighPriorityTimeWarning": {
           "type": "integer",
           "default": 5000,
           "minimum": 0,
           "description": "Time (in ms) before generating SNMP warning for a high-priority query (if not overridden)"
         },
-        "defaultLowPriorityTimeLimit": { 
+        "defaultLowPriorityTimeLimit": {
           "type": "integer",
           "default": 0,
           "minimum": 0,
           "description": "Maximum run time (in ms) for any single active low-priority query (if not overridden)"
         },
-        "defaultLowPriorityTimeWarning": { 
+        "defaultLowPriorityTimeWarning": {
           "type": "integer",
           "default": 0,
           "minimum": 0,
           "description": "Time (in ms) before generating SNMP warning for a low-priority query (if not overridden)"
         },
-        "defaultMemoryLimit": { 
+        "defaultMemoryLimit": {
           "type": "integer",
           "default": 0,
           "minimum": 0,
           "description": "Maximum amount of memory available for row data in any single active query (if not overridden)"
         },
-        "defaultSLAPriorityTimeLimit": { 
+        "defaultSLAPriorityTimeLimit": {
           "type": "integer",
           "default": 0,
           "minimum": 0,
           "description": "Maximum run time (in ms) for any single active SLA-high-priority query (if not overridden)"
         },
-        "defaultSLAPriorityTimeWarning": { 
+        "defaultSLAPriorityTimeWarning": {
           "type": "integer",
           "default": 5000,
           "minimum": 0,
           "description": "Time (in ms) before generating SNMP warning for a SLA-high-priority query (if not overridden)"
         },
-        "defaultStripLeadingWhitespace": { 
+        "defaultStripLeadingWhitespace": {
           "type": "boolean",
           "default": true,
           "description": "Default value for stripping leading whitespace in input XML values"
         },
-        "encryptionInTransit": { 
+        "encryptionInTransit": {
           "type": "boolean",
           "default": false,
           "description": "Encrypt traffic between Roxie nodes."
         },
-        "flushJHtreeCacheOnOOM": { 
+        "flushJHtreeCacheOnOOM": {
           "type": "boolean",
           "default": true,
           "description": "Should the index node memory allocation flush the cache and retry if memory allocation fails"
@@ -1988,18 +1988,18 @@
           "enum": ["false", "true", "payload", "payloadRemoveOnly"],
           "description": "Enables translation (where possible) of mismatched file layouts on-the-fly. Specify 'payload' to attempt to translate payload fields only"
         },
-        "highTimeout": { 
+        "highTimeout": {
           "type": "integer",
           "default": 2000,
           "minimum": 0,
           "description": "Timeout (in ms) before high priority requests are resent to agents"
         },
-        "ignoreOrphans": { 
+        "ignoreOrphans": {
           "type": "boolean",
           "default": true,
           "description": "Treat out-of-date local files as if they were not present."
         },
-        "ignoreFileDateMismatches": { 
+        "ignoreFileDateMismatches": {
           "type": "boolean",
           "default": false,
           "description": "Ignore mismatched file dates on local files"
@@ -2009,7 +2009,7 @@
           "default": false,
           "description": "Ignore mismatched file sizes on local files"
         },
-        "fileTimeFuzzySeconds": { 
+        "fileTimeFuzzySeconds": {
           "type": "integer",
           "default": 0,
           "description": "Ignore mismatched file dates of up to this amount"
@@ -2019,454 +2019,454 @@
           "default": false,
           "description": "Delay opening files until first use."
         },
-        "localAgent": { 
+        "localAgent": {
           "type": "boolean",
           "default": false,
           "description": "Communication between server and agent uses a memory queue rather than network. Implies singleNode, and defaults on if singleNode set."
         },
-        "localFilesExpire": { 
+        "localFilesExpire": {
           "type": "integer",
           "default": -1,
           "description": "Period (in ms) of inactivity before a local datafile handle is closed"
         },
-        "lockSuperFiles": { 
+        "lockSuperFiles": {
           "type": "boolean",
           "default": false,
           "description": "If enabled, superfiles will be locked while queries that use them are loaded"
         },
-        "lowTimeout": { 
+        "lowTimeout": {
           "type": "integer",
           "default": 10000,
           "minimum": 0,
           "description": "Timeout (in ms) before low priority requests are resent to agents"
         },
-        "maxHttpConnectionRequests": { 
+        "maxHttpConnectionRequests": {
           "type": "integer",
           "default": 0,
           "minimum": 0,
           "description": "Max number of query requests per persistent http connection"
         },
-        "maxHttpKeepAliveWait": { 
+        "maxHttpKeepAliveWait": {
           "type": "integer",
           "default": 5000,
           "minimum": 0,
           "description": "Max number of miliseconds to wait for additional requests on a persistent http connection"
         },
-        "maxLocalFilesOpen": { 
+        "maxLocalFilesOpen": {
           "type": "integer",
           "default": 4000,
           "minimum": 0,
           "description": "Maximum number of local files to keep open"
         },
-        "maxRemoteFilesOpen": { 
+        "maxRemoteFilesOpen": {
           "type": "integer",
           "default": 1000,
           "minimum": 0,
           "description": "Maximum number of remote files to keep open"
         },
-        "minLocalFilesOpen": { 
+        "minLocalFilesOpen": {
           "type": "integer",
           "default": 2000,
           "minimum": 0,
           "description": "Minimum number of local files to keep open"
         },
-        "minRemoteFilesOpen": { 
+        "minRemoteFilesOpen": {
           "type": "integer",
           "default": 500,
           "minimum": 0,
           "description": "Minimum number of remote files to keep open"
         },
-        "packetAcknowledgeTimeout": { 
+        "packetAcknowledgeTimeout": {
           "type": "integer",
           "default": 100,
           "minimum": 10,
           "description": "Maximum time to wait for agent query packets to be acknowledged before resending"
         },
-        "parallelQueryLoadThreads": { 
+        "parallelQueryLoadThreads": {
           "type": "integer",
           "default": 0,
           "minimum": 0,
           "description": "Use up to n multiple threads for initial query loading"
         },
-        "preloadOnceData": { 
+        "preloadOnceData": {
           "type": "boolean",
           "default": true,
           "description": "Evaluate : ONCE sections of queries at query load time"
         },
-        "prestartAgentThreads": { 
+        "prestartAgentThreads": {
           "type": "boolean",
           "default": false,
           "description": "Prestart agent worker threads at startup"
         },
-        "reloadRetriesSuspended": { 
+        "reloadRetriesSuspended": {
           "type": "boolean",
           "default": true,
           "description": "Retry loading of suspended queries whenever QuerySet reloads"
         },
-        "remoteFilesExpire": { 
+        "remoteFilesExpire": {
           "type": "integer",
           "default": 3600000,
           "minimum": 0,
           "description": "Period (in ms) of inactivity before a remote datafile handle is closed"
         },
-        "serverThreads": { 
+        "serverThreads": {
           "type": "integer",
           "default": 30,
           "minimum": 0,
           "description": "Default number of threads processing Roxie server requests (if not specified on service)"
         },
-        "singleNode": { 
+        "singleNode": {
           "type": "boolean",
           "default": false,
           "description": "Roxie is deployed as an array of self-contained single nodes, with all agents part of the server process."
         },
-        "slaTimeout": { 
+        "slaTimeout": {
           "type": "integer",
           "default": 2000,
           "minimum": 0,
           "description": "Timeout (in ms) before SLA high priority requests are resent to agents"
         },
-        "agentQueryReleaseDelaySeconds": { 
+        "agentQueryReleaseDelaySeconds": {
           "type": "integer",
           "default": 60,
           "minimum": 0,
           "description": "Delay before unregistering slave queries to allow in-flight to complete. Files are locked until query is unregistered."
         },
-        "agentThreads": { 
+        "agentThreads": {
           "type": "integer",
           "default": 30,
           "minimum": 0,
           "description": "Number of threads processing agent requests"
         },
-        "statsExpiryTime": { 
+        "statsExpiryTime": {
           "type": "integer",
           "default": 3600,
           "minimum": 0,
           "description": "Time (in seconds) that detailed reporting stats are kept"
         },
-        "totalMemoryLimit": { 
+        "totalMemoryLimit": {
           "type": "string",
           "description": "Maximum amount of memory available for row data in all active queries"
         },
-        "heapUseHugePages": { 
+        "heapUseHugePages": {
           "type": "boolean",
           "default": false,
           "description": "Allow roxie to use memory from huge pages if they have been configured."
         },
-        "heapUseTransparentHugePages": { 
+        "heapUseTransparentHugePages": {
           "type": "boolean",
           "default": true,
           "description": "Allow roxie to use memory from transparent huge pages."
         },
-        "heapRetainMemory": { 
+        "heapRetainMemory": {
           "type": "boolean",
           "default": false,
           "description": "Retain and do not return unused memory to the operating system."
         },
-        "trapTooManyActiveQueries": { 
+        "trapTooManyActiveQueries": {
           "type": "boolean",
           "default": true,
           "description": "should an SNMP trap get sent when too many active query error occurs"
         },
-        "useMemoryMappedIndexes": { 
+        "useMemoryMappedIndexes": {
           "type": "boolean",
           "default": false,
           "description": "Using memory-mapped files when merging multiple result streams from row-compressed indexes."
         },
-        "useRemoteResources": { 
+        "useRemoteResources": {
           "type": "boolean",
           "default": false,
           "description": "Reads any missing data files/keys from the position they were in when deployed."
         },
-        "traceLevel": { 
+        "traceLevel": {
           "type": "integer",
           "default": 1,
           "minimum": 0,
           "description": "Level of detail in reporting (set to 0 for none, 1 for normal, > 1 or more for extended)"
         },
-        "logFullQueries": { 
+        "logFullQueries": {
           "type": "boolean",
           "default": false,
           "description": "Log full text (unless blindLogging) and resource usage of all queries received "
         },
-        "blindLogging": { 
+        "blindLogging": {
           "type": "boolean",
           "default": false,
           "description": "Suppress all logging of any data or query text"
         },
-        "memTraceLevel": { 
+        "memTraceLevel": {
           "type": "integer",
           "default": 1,
           "minimum": 0,
           "description": "Level of detail in reporting mem mgr information(set to 0 for none, 1 for normal, >1 or more for extended)"
         },
-        "miscDebugTraceLevel": { 
+        "miscDebugTraceLevel": {
           "type": "integer",
           "default": 0,
           "minimum": 0,
           "description": "Level of miscellaneous debug tracing unrelated to all other tracing(set to 0 for none, 1 for normal, >1 or more for extended)"
         },
-        "soapTraceLevel": { 
+        "soapTraceLevel": {
           "type": "integer",
           "default": 1,
           "minimum": 0,
           "description": "Level of detail in reporting SOAPCALL information(set to 0 for none, 1 for normal, >1 or more for extended)"
         },
-        "traceTranslations": { 
+        "traceTranslations": {
           "type": "boolean",
           "default": true,
           "description": "Trace record layout translations to log file"
         },
-        "traceEnabled": { 
+        "traceEnabled": {
           "type": "boolean",
           "default": false,
           "description": "TRACE activity output enabled by default (can be overridden in workunit or query)"
         },
-        "traceLimit": { 
+        "traceLimit": {
           "type": "integer",
           "default": 10,
           "minimum": 0,
           "description": "Number of rows output by TRACE activity"
         },
-        "udpTraceLevel": { 
+        "udpTraceLevel": {
           "type": "integer",
           "default": 1,
           "minimum": 0,
           "description": "Level of detail in reporting udp information(set to 0 for none, 1 for normal, >1 or more for extended)"
         },
-        "udpAdjustThreadPriorities": { 
+        "udpAdjustThreadPriorities": {
           "type": "boolean",
           "default": false,
           "description": "Should UDP socket reading threads operate at elevated priority"
         },
-        "udpFlowSocketsSize": { 
+        "udpFlowSocketsSize": {
           "type": "integer",
           "default": 131072,
           "minimum": 0,
           "description": "Controls the read socket buffer size of the UDP layer flow control sockets"
         },
-        "udpLocalWriteSocketSize": { 
+        "udpLocalWriteSocketSize": {
           "type": "integer",
           "default": 1024000,
           "minimum": 0,
           "description": "Controls the write socket buffer size of the local UDP sockets (Agent to Server on same node)"
         },
-        "udpAgentBufferSize": { 
+        "udpAgentBufferSize": {
           "type": "integer",
           "default": 262142,
           "minimum": 0,
           "description": "Controls the read socket buffer size of the UDP agent read sockets"
         },
-        "udpOutQsPriority": { 
+        "udpOutQsPriority": {
           "type": "integer",
           "default": 0,
           "minimum": 0,
           "description": "Turns on/off Priority weight-based for output queues (0 round-robin no priority - old logic, 1 round-robin new logic, 2 and higher is factor of priority)"
         },
-        "udpQueueSize": { 
+        "udpQueueSize": {
           "type": "integer",
           "default": 100,
           "minimum": 0,
           "description": "UDP transport layer receive queue size"
         },
-        "udpResendLostPackets": { 
+        "udpResendLostPackets": {
           "type": "boolean",
           "default": true,
           "description": "UDP transport layer resend lost packets"
         },
-        "udpSendQueueSize": { 
+        "udpSendQueueSize": {
           "type": "integer",
           "default": 50,
           "minimum": 0,
           "description": "UDP transport layer send queue size"
         },
-        "udpStatsReportInterval": { 
+        "udpStatsReportInterval": {
           "type": "integer",
           "default": 60000,
           "minimum": 0,
           "description": "UDP transport layer stats reporting interval"
         },
-        "blobCacheMem": { 
+        "blobCacheMem": {
           "type": "integer",
           "default": 0,
           "minimum": 0,
           "description": "Size (in Mb) of blob index page cache"
         },
-        "leafCacheMem": { 
+        "leafCacheMem": {
           "type": "integer",
           "default": 50,
           "minimum": 0,
           "description": "Size (in Mb) of leaf index page cache"
         },
-        "nodeCacheMem": { 
+        "nodeCacheMem": {
           "type": "integer",
           "default": 100,
           "minimum": 0,
           "description": "Size (in Mb) of non-leaf index page cache"
         },
-        "mysqlCacheCheckPeriod": { 
+        "mysqlCacheCheckPeriod": {
           "type": "integer",
           "default": 10000,
           "minimum": 0,
           "description": "Time to wait (ms) between checking if any cached MySQL connections can be closed"
         },
-        "mysqlCacheTimeoutPeriod": { 
+        "mysqlCacheTimeoutPeriod": {
           "type": "integer",
           "default": 60000,
           "minimum": 0,
           "description": "Time to wait (ms) before closing a cached MySQL connection"
         },
-        "mysqlConnectionCacheSize": { 
+        "mysqlConnectionCacheSize": {
           "type": "integer",
           "default": 10000,
           "minimum": 0,
           "description": "Number of MySQL connections to hold in cache"
         },
-        "checkCompleted": { 
+        "checkCompleted": {
           "type": "boolean",
           "default": true,
           "description": "Check pending replies when agent gets a retry request"
         },
-        "dafilesrvLookupTimeout": { 
+        "dafilesrvLookupTimeout": {
           "type": "integer",
           "default": 10000,
           "minimum": 0,
           "description": "Maximum time (in milliseconds) dafilesrv will wait before timing out the first time through the list"
         },
-        "defaultConcatPreload": { 
+        "defaultConcatPreload": {
           "type": "integer",
           "default": 0,
           "minimum": 0,
           "description": "Default concat preload"
         },
-        "defaultFetchPreload": { 
+        "defaultFetchPreload": {
           "type": "integer",
           "default": 0,
           "minimum": 0,
           "description": "Default fetch preload"
         },
-        "defaultFullKeyedJoinPreload": { 
+        "defaultFullKeyedJoinPreload": {
           "type": "integer",
           "default": 0,
           "minimum": 0,
           "description": "Default full keyed join preload"
         },
-        "defaultKeyedJoinPreload": { 
+        "defaultKeyedJoinPreload": {
           "type": "integer",
           "default": 0,
           "minimum": 0,
           "description": "Default keyed join preload"
         },
-        "defaultParallelJoinPreload": { 
+        "defaultParallelJoinPreload": {
           "type": "integer",
           "default": 0,
           "minimum": 0,
           "description": "Default parallel join preload"
         },
-        "defaultPrefetchProjectPreload": { 
+        "defaultPrefetchProjectPreload": {
           "type": "integer",
           "default": 10,
           "minimum": 0,
           "description": "Default prefetch value for PROJECT,PREFETCH activity"
         },
-        "doIbytiDelay": { 
+        "doIbytiDelay": {
           "type": "boolean",
           "default": true,
           "description": "Enables the IBYTI delay logic in the agents."
         },
-        "enableHeartBeat": { 
+        "enableHeartBeat": {
           "type": "boolean",
           "default": true,
           "description": "Enable HeartBeat messages to roxiepipe clients"
         },
-        "fastLaneQueue": { 
+        "fastLaneQueue": {
           "type": "boolean",
           "default": true,
           "description": "Enable special fast-lane queue for simple queries."
         },
-        "ignoreMissingFiles": { 
+        "ignoreMissingFiles": {
           "type": "boolean",
           "default": false,
           "description": "Ignore missing files"
         },
-        "indexReadChunkSize": { 
+        "indexReadChunkSize": {
           "type": "integer",
           "default": 60000,
           "minimum": 0,
           "description": "Break up results from indexRead (and other remote activities) every N bytes"
         },
-        "initIbytiDelay": { 
+        "initIbytiDelay": {
           "type": "integer",
           "default": 50,
           "minimum": 0,
           "description": "Initial time (in milliseconds) a secondary agent will wait for an IBYTI packet from a primary peer."
         },
-        "mtuPayload": { 
+        "mtuPayload": {
           "type": "integer",
           "default": 1400,
           "minimum": 1400,
           "maximum": 9000,
           "description": "Set higher than 1400 for networks with a larger MTU."
         },
-        "linuxYield": { 
+        "linuxYield": {
           "type": "boolean",
           "default": false,
           "description": "Yield to scheduler in some tight loops. May help latency on uniprocessor machines"
         },
-        "maxBlockSize": { 
+        "maxBlockSize": {
           "type": "integer",
           "default": 10000000,
           "minimum": 0,
           "description": "Max size of block read from client socket"
         },
-        "maxLockAttempts": { 
+        "maxLockAttempts": {
           "type": "integer",
           "default": 5,
           "minimum": 0,
           "description": "Number of retries to get lock for global queries"
         },
-        "memoryStatsInterval": { 
+        "memoryStatsInterval": {
           "type": "integer",
           "default": 60,
           "minimum": 0,
           "description": "Interval (in seconds) between reports on Roxie heap usage"
         },
-        "memTraceSizeLimit": { 
+        "memTraceSizeLimit": {
           "type": "integer",
           "default": 10,
           "minimum": 0,
           "description": "Generate stacktrace whenever a request is made for a row larger than this threshold (0 to disable)"
         },
-        "parallelAggregate": { 
+        "parallelAggregate": {
           "type": "integer",
           "default": 0,
           "minimum": 0,
           "description": "Number of parallel threads to use for in-memory aggregate processing. Set to 0 to use one per CPU, 1 to disable parallel processing of in-memory aggregates"
         },
-        "perChannelFlowLimit": { 
+        "perChannelFlowLimit": {
           "type": "integer",
           "default": 10,
           "minimum": 1,
           "description": "Number of pending queries permitted per channel (per active activity) before blocking"
         },
-        "preabortIndexReadsThreshold": { 
+        "preabortIndexReadsThreshold": {
           "type": "integer",
           "default": 100,
           "minimum": 0,
           "description": "Use seek to precheck keyed limits (i.e. assume ,COUNT) on index reads if limit greater than this value"
         },
-        "preabortKeyedJoinsThreshold": { 
+        "preabortKeyedJoinsThreshold": {
           "type": "integer",
           "minimum" : 0,
           "default": 100,
           "description": "Use seek to precheck limits on keyed joins if limit greater than this value"
         },
-        "simpleLocalKeyedJoins": { 
+        "simpleLocalKeyedJoins": {
           "type": "boolean",
           "default": true,
           "description": "Enable single-threaded local keyed joins"
         },
-        "socketCheckInterval": { 
+        "socketCheckInterval": {
           "type": "integer",
           "default": 5000,
           "minimum": 0,
@@ -2907,7 +2907,7 @@
         { "$ref": "#/definitions/sashacommon" },
         { "$ref": "#/definitions/sasha-limitcutoff" },
         {
-          "properties": 
+          "properties":
           {
             "backup": {
               "type": "integer",
@@ -2953,11 +2953,11 @@
     "sasha-dfuwu-archiver": {
       "type": "object",
       "required": [ "plane" ],
-      "allOf": [ 
+      "allOf": [
         { "$ref": "#/definitions/sashacommon" },
         { "$ref": "#/definitions/sasha-limitcutoff" },
         {
-          "properties": 
+          "properties":
           {
             "disabled": {},
             "interval": {},
diff --git a/system/jlib/jdebug.cpp b/system/jlib/jdebug.cpp
index c7142ae2526..d1fc58d766f 100644
--- a/system/jlib/jdebug.cpp
+++ b/system/jlib/jdebug.cpp
@@ -83,21 +83,21 @@ PU (%) is the percentage CPU in use (unchanged from previously).
 
 MU (%) is what percentage of total (all processes) memory is in use
 (ram + swap) or in 32 bit is the percentage of 3GB (address space)
-used (whichever larger). 
+used (whichever larger).
 
 MAL is total memory in use (i.e malloced and not freed ) by this
-process  (= MMP+SBK) 
+process  (= MMP+SBK)
 
 MMP is the sum of memory mapped (large) blocks in use by this process
 (which will be  returned to OS when freed).
 
 SBK is the sbrk'ed memory i.e. smaller blocks allocated from the
 arena. (note this memory is unlikely to be returned to OS while the
-process is still running). 
+process is still running).
 
 TOT (K) is an indication of the memory footprint of the process.
 This is the 'arena' size (which is how much reserved by sbrk) plus the
-mmap memory size (MMP). 
+mmap memory size (MMP).
 
 RAM (K) is how much real memory is in use by all processes - it is
 the same as what would be reported by the 'free' command after the
@@ -140,27 +140,27 @@ inline offset_t readHexNum(const char *&s)
     for (;;) {
         switch (*s) {
             case '0':
-            case '1': 
-            case '2': 
-            case '3': 
+            case '1':
+            case '2':
+            case '3':
             case '4':
-            case '5': 
-            case '6': 
-            case '7': 
+            case '5':
+            case '6':
+            case '7':
             case '8':
-            case '9': ret = ret*16+(*s-'0'); 
+            case '9': ret = ret*16+(*s-'0');
                  break;
-            case 'A': 
-            case 'B': 
-            case 'C': 
-            case 'D': 
+            case 'A':
+            case 'B':
+            case 'C':
+            case 'D':
             case 'E':
             case 'F': ret = ret*16+(*s-'A'+10);
                  break;
-            case 'a': 
-            case 'b': 
-            case 'c': 
-            case 'd': 
+            case 'a':
+            case 'b':
+            case 'c':
+            case 'd':
             case 'e':
             case 'f': ret = ret*16+(*s-'a'+10);
                  break;
@@ -178,15 +178,15 @@ inline offset_t readDecNum(const char *&s)
     for (;;) {
         switch (*s) {
             case '0':
-            case '1': 
-            case '2': 
-            case '3': 
+            case '1':
+            case '2':
+            case '3':
             case '4':
-            case '5': 
-            case '6': 
-            case '7': 
+            case '5':
+            case '6':
+            case '7':
             case '8':
-            case '9': ret = ret*10+(*s-'0'); 
+            case '9': ret = ret*10+(*s-'0');
                  break;
         default:
             return ret;
@@ -199,22 +199,22 @@ inline offset_t readDecNum(const char *&s)
 
 #if defined(_WIN32)
 
-static __int64 numCyclesNTicks; 
+static __int64 numCyclesNTicks;
 static __int64 ticksPerSec;
 static __int64 numScaleTicks;
 static bool useRDTSC = _USE_RDTSC;
-static double cycleToNanoScale; 
+static double cycleToNanoScale;
 
 static bool calibrate_timing()
 {
 #ifndef _AMD64_
-    if (useRDTSC) 
+    if (useRDTSC)
     {
         unsigned long r;
-        __asm { 
-             mov eax, 1 ;  
-             cpuid ; 
-             mov r, edx 
+        __asm {
+             mov eax, 1 ;
+             cpuid ;
+             mov r, edx
         }
         if ((r&0x10)==0)
             useRDTSC = false;
@@ -310,7 +310,7 @@ static bool calibrate_timing()
 #if defined(_ARCH_X86_) || defined(_ARCH_X86_64_)
     if (useRDTSC) {
         unsigned long eax;
-        unsigned long ebx; 
+        unsigned long ebx;
         unsigned long ecx;
         unsigned long edx;
 #if defined(_ARCH_X86_64_)
@@ -318,10 +318,10 @@ static bool calibrate_timing()
 
 #else
 // NB PIC code and ebx usage don't mix well
-        asm volatile("pushl %%ebx      \n\t" 
+        asm volatile("pushl %%ebx      \n\t"
                      "cpuid            \n\t"
-                     "movl %%ebx, %1   \n\t" 
-                     "popl %%ebx       \n\t" 
+                     "movl %%ebx, %1   \n\t"
+                     "popl %%ebx       \n\t"
                      : "=a"(eax), "=r"(ebx), "=c"(ecx), "=d"(edx)
                  : "a"(1)
                  : "cc");
@@ -385,14 +385,14 @@ cycle_t jlib_decl get_cycles_now()
     if (!use_gettimeofday) {
         timespec tm;
         if (clock_gettime(CLOCK_MONOTONIC, &tm)>=0)
-            return ((cycle_t)tm.tv_sec)*1000000000L+(tm.tv_nsec);   
+            return ((cycle_t)tm.tv_sec)*1000000000L+(tm.tv_nsec);
         use_gettimeofday = true;
         fprintf(stderr,"clock_gettime CLOCK_MONOTONIC returns %d",errno);   // don't use PROGLOG
     }
 #endif
     struct timeval tm;
     gettimeofday(&tm,NULL);
-    return ((cycle_t)tm.tv_sec)*1000000000L+(cycle_t)tm.tv_usec*1000L; 
+    return ((cycle_t)tm.tv_sec)*1000000000L+(cycle_t)tm.tv_usec*1000L;
 }
 #endif
 
@@ -491,7 +491,7 @@ MTimeSection::~MTimeSection()
         display_time(scope, end_time-start_time);
 }
 
-class TimeSectionInfo : public MappingBase 
+class TimeSectionInfo : public MappingBase
 {
 public:
     TimeSectionInfo(const char * _scope, __int64 _cycles) : scope(_scope), totalcycles(_cycles), maxcycles(_cycles), count(1) {};
@@ -526,11 +526,11 @@ class DefaultTimeReporter : implements ITimeReporter, public CInterface
     }
 public:
     IMPLEMENT_IINTERFACE
-    DefaultTimeReporter() 
+    DefaultTimeReporter()
     {
         sections = new StringMapOf<TimeSectionInfo>(true);
     }
-    ~DefaultTimeReporter() 
+    ~DefaultTimeReporter()
     {
 //      printTimings();                     // Must explicitly call printTimings - no automatic print (too late here!)
         delete sections;
@@ -836,7 +836,7 @@ struct KERNEL_USER_TIMES {
     __int64 ExitTime;
     __int64 KernelTime;
     __int64 UserTime;
-    //__int64 EllapsedTime; 
+    //__int64 EllapsedTime;
 };
 
 
@@ -1340,12 +1340,12 @@ void getSystemProcessInfo(unsigned &numCPUs, unsigned &CPUSpeed)
         DBGLOG("RegQueryValueEx() failed to get CPU speed - errorCode=%d", lRet);
         RegCloseKey(hKey);
     }
-    else 
+    else
     {
         CPUSpeed = keyValue;
         RegCloseKey(hKey);
     }
-    
+
 
     SYSTEM_INFO sysInfo;
     GetSystemInfo(&sysInfo);
@@ -1481,9 +1481,9 @@ void getSystemProcessInfo(unsigned &numCPUs, unsigned &CPUSpeed)
     const char *bufptr;
     while ((bufptr = fgets(line, 1000, cpufp)) != NULL)
     {
-        if (strncmp(cpuNumTag, bufptr, strlen(cpuNumTag))==0) 
+        if (strncmp(cpuNumTag, bufptr, strlen(cpuNumTag))==0)
             numCPUs++;
-        else if (strncmp(cpuSpeedTag, bufptr, strlen(cpuSpeedTag))==0) 
+        else if (strncmp(cpuSpeedTag, bufptr, strlen(cpuSpeedTag))==0)
             CPUSpeed = (unsigned)strtol(bufptr+strlen(cpuSpeedTag), &tail, 10);
     }
 
@@ -1538,7 +1538,7 @@ static unsigned evalAffinityCpus()
 
 // Note - values are returned in Kb
 
-static void getMemUsage(unsigned &inuse,unsigned &active,unsigned &total,unsigned &swaptotal,unsigned &swapinuse) 
+static void getMemUsage(unsigned &inuse,unsigned &active,unsigned &total,unsigned &swaptotal,unsigned &swapinuse)
 {
 #ifdef __APPLE__
     active = 0;
@@ -1586,24 +1586,24 @@ static void getMemUsage(unsigned &inuse,unsigned &active,unsigned &total,unsigne
     unsigned swapcached = 0;
     unsigned cached = 0;
     while (bufptr&&i) {
-        if (*bufptr =='\n') 
+        if (*bufptr =='\n')
             bufptr++;
         i--;
-        if (strncmp("MemTotal:", bufptr, 9)==0) 
+        if (strncmp("MemTotal:", bufptr, 9)==0)
             total = (unsigned)strtol(bufptr+9, &tail, 10);
-        else if (strncmp("SwapTotal:", bufptr, 10)==0) 
+        else if (strncmp("SwapTotal:", bufptr, 10)==0)
             swaptotal = (unsigned)strtol(bufptr+10, &tail, 10);
-        else if (strncmp("MemFree:", bufptr, 8)==0) 
+        else if (strncmp("MemFree:", bufptr, 8)==0)
             free = (unsigned)strtol(bufptr+8, &tail, 10);
-        else if (strncmp("Active:", bufptr, 7)==0) 
+        else if (strncmp("Active:", bufptr, 7)==0)
             active = (unsigned)strtol(bufptr+7, &tail, 10);
-        else if (strncmp("SwapFree:", bufptr, 9)==0) 
+        else if (strncmp("SwapFree:", bufptr, 9)==0)
             swapfree = (unsigned)strtol(bufptr+9, &tail, 10);
-        else if (strncmp("Cached:", bufptr, 7)==0) 
+        else if (strncmp("Cached:", bufptr, 7)==0)
             cached = (unsigned)strtol(bufptr+7, &tail, 10);
-        else if (strncmp("SwapCached:", bufptr, 11)==0) 
+        else if (strncmp("SwapCached:", bufptr, 11)==0)
             swapcached = (unsigned)strtol(bufptr+11, &tail, 10);
-        else 
+        else
             i++;
         bufptr = strchr(bufptr, '\n');
     }
@@ -1648,7 +1648,7 @@ static void getMemUsage(unsigned &inuse,unsigned &active,unsigned &total,unsigne
 #endif
 }
 
-class CInt64fix 
+class CInt64fix
 {
     __int64 val;
 public:
@@ -1656,10 +1656,10 @@ class CInt64fix
     {
         val = 0;
     }
-    void set(int v) 
+    void set(int v)
     {
         __int64 ret = (unsigned)v;
-        while (val-ret>0x80000000LL) 
+        while (val-ret>0x80000000LL)
             ret += 0x100000000LL;
         val = ret;
     }
@@ -1669,7 +1669,7 @@ class CInt64fix
         return val;
     }
 };
-    
+
 
 
 void getMemStats(StringBuffer &out, unsigned &memused, unsigned &memtot)
@@ -1699,7 +1699,7 @@ void getMemStats(StringBuffer &out, unsigned &memused, unsigned &memtot)
     out.appendf("MU=%3u%% MAL=%" I64F "d MMP=%" I64F "d SBK=%" I64F "d TOT=%uK RAM=%uK SWP=%uK FLT=%" I64F "u CTX=%" I64F "u",
         muval, total, mmapmem, sbrkmem, (unsigned)(virttot/1024), mu, su, processInfo.getMajorFaults(), processInfo.getNumContextSwitches());
 #ifdef _USE_MALLOC_HOOK
-    if (totalMem) 
+    if (totalMem)
         out.appendf(" TM=%" I64F "d",totalMem);
 #endif
     memused = mu+su;
@@ -1922,7 +1922,7 @@ struct CProcInfo: extends CInterface
             return false;
 
         active = true;
-        if (first) 
+        if (first)
             first = false;
         else {
             delta.system = info.time.system-prev.system;
@@ -1984,7 +1984,7 @@ class CProcessMonitor
         tot_time = 0;
         ForEachItemInRev(i3,processes) {
             CProcInfo &pi = processes.item(i3);
-            if (pi.active) 
+            if (pi.active)
                 tot_time += pi.delta.system+pi.delta.user;
             else
                 processes.remove(i3);
@@ -2015,7 +2015,7 @@ class CProcessMonitor
     {
         if (pc>90) {
             scan();
-            if (busy) 
+            if (busy)
                 print(3,str); // print top 3
             else
                 busy = true;
@@ -2551,7 +2551,7 @@ class CExtendedStats  // Disk network and cpu stats
     }
 
 
-    inline double perSec(double v,double deltams) 
+    inline double perSec(double v,double deltams)
     {
         return 1000.0*v/deltams;
     }
@@ -3274,7 +3274,7 @@ void getProcessTime(UserSystemTime_t & result)
 
 void getSystemTraceInfo(StringBuffer &str, PerfMonMode mode)
 {
-    if (!MemoryUsageReporter) 
+    if (!MemoryUsageReporter)
         MemoryUsageReporter = new CMemoryUsageReporter(1000, mode, 0, false);
     MemoryUsageReporter->getSystemTraceInfo(str, mode);
 }
@@ -3355,7 +3355,7 @@ void getHardwareInfo(HardwareInfo &hdwInfo, const char *primDiskPath, const char
     // MORE: Find win32 call for NIC speed
 
 #else  // linux
-    unsigned memUsed, memActive, memSwap, memSwapUsed; 
+    unsigned memUsed, memActive, memSwap, memSwapUsed;
     getMemUsage(memUsed, memActive, hdwInfo.totalMemory, memSwap, memSwapUsed);
     hdwInfo.totalMemory /= 1024; // in MB
 
@@ -3383,12 +3383,12 @@ void getHardwareInfo(HardwareInfo &hdwInfo, const char *primDiskPath, const char
 //===========================================================================
 
 
-enum SegTypes { 
+enum SegTypes {
         segtype_free,         //
         segtype_heap,         // rw-p named [heap]
         segtype_data,         // rw-p unnamed
         segtype_guard,        // ---p unnamed/named
-        segtype_stack,        // rwxp 
+        segtype_stack,        // rwxp
         segtype_qlibcode,     // r-xp  named */lib200
         segtype_qlibdata,     // rw-p  named */lib200
         segtype_libcode,      // r-xp  named *
@@ -3407,7 +3407,7 @@ struct SegTypeRec
 
 
 
-const char *PROCMAPHEADER = 
+const char *PROCMAPHEADER =
     "FREE,NFREE,MAXFREE,HEAP,STACK,NSTACKS,DATA,NDATA,MAXDATA,LIBDATA,QUERYDATA,MAXQUERYDATA,LIBCODE,QUERYCODE,MAXQLIBCODE";
 
 
@@ -3969,7 +3969,7 @@ void printAllocationSummary()
     {
         _CrtMemBlockHeader * display = locations[i2];
         //char tempBuffer[16];
-        //unsigned len = dumpMemory(sizeof(tempBuffer), tempBuffer, display->nDataSize, 
+        //unsigned len = dumpMemory(sizeof(tempBuffer), tempBuffer, display->nDataSize,
 
         PROGLOG("%s(%d) %d:%d {%ld} = %d", display->szFileName ? display->szFileName : "<unknown>", display->nLine, display->nDataSize, counts[i2], display->lRequest, display->nDataSize * counts[i2]);
     }
@@ -4308,7 +4308,7 @@ class UserMetricMsgHandler : public CInterface, implements ILogMsgHandler, imple
     Owned<ILogMsgFilter> regexFilter;
 public:
     virtual void Link(void) const { CInterface::Link(); }
-    virtual bool Release(void) const    
+    virtual bool Release(void) const
     {
         if (CInterface::Release())
             return true;
@@ -4326,22 +4326,23 @@ class UserMetricMsgHandler : public CInterface, implements ILogMsgHandler, imple
     }
 
     // interface ILogMsgHandler
-    virtual void handleMessage(const LogMsg & msg __attribute__((unused))) { counter++; }
-    virtual bool needsPrep() const { return false; }
-    virtual void prep() {}
-    virtual unsigned queryMessageFields() const { return MSGFIELD_detail; }
-    virtual void setMessageFields(unsigned _fields __attribute__((unused)) = MSGFIELD_all) {}
-    virtual void addToPTree(IPropertyTree * parent __attribute__((unused))) const {}
-    virtual int flush() { return 0; }
-    virtual bool getLogName(StringBuffer &name __attribute__((unused))) const { return false; }
-    virtual offset_t getLogPosition(StringBuffer &logFileName __attribute__((unused))) const { return 0; };
+    virtual void handleMessage(const LogMsg & msg __attribute__((unused))) override { counter++; }
+    virtual bool needsPrep() const override { return false; }
+    virtual void prep() override {}
+    virtual unsigned queryMessageFields() const override { return MSGFIELD_detail; }
+    virtual void setMessageFields(unsigned _fields __attribute__((unused)) = MSGFIELD_all) override {}
+    virtual void addToPTree(IPropertyTree * parent __attribute__((unused))) const override {}
+    virtual int flush() override { return 0; }
+    virtual bool getLogName(StringBuffer &name __attribute__((unused))) const override { return false; }
+    virtual offset_t getLogPosition(StringBuffer &logFileName __attribute__((unused))) const override { return 0; };
+    virtual LogHandlerFormat queryFormatType() const override { return LOGFORMAT_undefined; };
 
     // interface IUserMetric
-    virtual unsigned __int64 queryCount() const { return counter; }
-    virtual const char *queryName() const { return metricName; }
-    virtual const char *queryMatchString() const { return regex; }
-    virtual void inc() { counter++; }
-    virtual void reset() { counter = 0; }
+    virtual unsigned __int64 queryCount() const override { return counter; }
+    virtual const char *queryName() const override { return metricName; }
+    virtual const char *queryMatchString() const override { return regex; }
+    virtual void inc() override { counter++; }
+    virtual void reset() override { counter = 0; }
 };
 
 jlib_decl IUserMetric *createUserMetric(const char *name, const char *matchString)
diff --git a/system/jlib/jlog.cpp b/system/jlib/jlog.cpp
index 301067970a7..2df34f14ae0 100644
--- a/system/jlib/jlog.cpp
+++ b/system/jlib/jlog.cpp
@@ -814,9 +814,9 @@ void SessionLogMsgFilter::addToPTree(IPropertyTree * tree) const
     tree->addPropTree("filter", filterTree);
 }
 
-bool RegexLogMsgFilter::includeMessage(const LogMsg & msg) const 
-{ 
-    if(localFlag && msg.queryRemoteFlag()) return false; 
+bool RegexLogMsgFilter::includeMessage(const LogMsg & msg) const
+{
+    if(localFlag && msg.queryRemoteFlag()) return false;
     SpinBlock b(lock);
     return const_cast<RegExpr &>(regex).find(msg.queryText()) != NULL;
 }
@@ -1303,7 +1303,7 @@ void RollingFileLogMsgHandler::doRollover(bool daily, const char *forceName)
             }
         }
     }
-    if(!handle) 
+    if(!handle)
     {
         handle = getNullHandle();
         OWARNLOG("RollingFileLogMsgHandler::doRollover : could not open log file %s for output", filename.str());
@@ -1420,9 +1420,9 @@ void LogMsgMonitor::addToPTree(IPropertyTree * tree) const
 void CLogMsgManager::MsgProcessor::push(LogMsg * msg)
 {
     //assertex(more); an assertex will just recurse here
-    if (!more) // we are effective stopped so don't bother even dropping (and leak parameter) as drop will involve 
-               // interaction with the base class which is stopped and could easily crash (as this condition 
-               // is expected not to occur - typically occurs if the user has incorrectly called exit on one thread 
+    if (!more) // we are effective stopped so don't bother even dropping (and leak parameter) as drop will involve
+               // interaction with the base class which is stopped and could easily crash (as this condition
+               // is expected not to occur - typically occurs if the user has incorrectly called exit on one thread
                // while still in the process of logging on another)
                // cf Bug #53695 for more discussion of the issue
         return;
@@ -1578,14 +1578,14 @@ void CLogMsgManager::enterQueueingMode()
 
 void CLogMsgManager::setQueueBlockingLimit(unsigned lim)
 {
-    CriticalBlock crit(modeLock); 
+    CriticalBlock crit(modeLock);
     if(processor)
         processor->setBlockingLimit(lim);
 }
 
 void CLogMsgManager::setQueueDroppingLimit(unsigned lim, unsigned numToDrop)
 {
-    CriticalBlock crit(modeLock); 
+    CriticalBlock crit(modeLock);
     if(processor)
         processor->setDroppingLimit(lim, numToDrop);
 }
@@ -1925,8 +1925,8 @@ void CLogMsgManager::addAllMonitorsToPTree(IPropertyTree * tree) const
         monitors.item(i).addToPTree(tree);
 }
 
-bool CLogMsgManager::rejectsCategory(const LogMsgCategory & cat) const 
-{ 
+bool CLogMsgManager::rejectsCategory(const LogMsgCategory & cat) const
+{
     if (!prefilter.includeCategory(cat))
         return true;
 
@@ -2329,7 +2329,7 @@ class CNullManager : implements ILogMsgManager
 
 static CNullManager nullManager;
 static Singleton<IRemoteLogAccess> logAccessor;
-static CriticalSection logAccessCrit;
+
 
 MODULE_INIT(INIT_PRIORITY_JLOG)
 {
@@ -2377,70 +2377,114 @@ static constexpr unsigned queueLenDefault = 512;
 static constexpr unsigned queueDropDefault = 0; // disabled by default
 static constexpr bool useSysLogDefault = false;
 
-void setupContainerizedLogMsgHandler()
+// returns LOGFORMAT_undefined if format has not changed
+// NB: returns LOGFORMAT_table if no format specified (i.e. this is the default)
+LogHandlerFormat getConfigHandlerFormat(const IPropertyTree *logConfig)
 {
-    Owned<IPropertyTree> logConfig = getComponentConfigSP()->getPropTree("logging");
-    if (logConfig)
+    LogHandlerFormat currentFormat = theStderrHandler->queryFormatType();
+    LogHandlerFormat newFormat{LOGFORMAT_undefined};
+    const char *newFormatString = logConfig ? logConfig->queryProp(logFormatAtt) : nullptr;
+    if (!newFormatString) // absent, defaults to "table"
+        newFormat = LOGFORMAT_table;
+    else
     {
-        if (logConfig->getPropBool(logDisabledAtt, false))
-        {
-            removeLog();
-            return;
-        }
+        if (streq(newFormatString, "xml"))
+            newFormat = LOGFORMAT_xml;
+        else if (streq(newFormatString, "json"))
+            newFormat = LOGFORMAT_json;
+        else if (streq(newFormatString, "table"))
+            newFormat = LOGFORMAT_table;
+        else
+            OWARNLOG("JLog: Invalid log format configuration detected '%s'!", newFormatString);
+    }
+    return currentFormat != newFormat ? newFormat : LOGFORMAT_undefined;
+}
 
-        if (logConfig->hasProp(logFormatAtt))
-        {
-            const char *logFormat = logConfig->queryProp(logFormatAtt);
-            bool newFormatDetected = false;
-            if (streq(logFormat, "xml"))
-            {
-                theStderrHandler = new HandleLogMsgHandlerXML(stderr, MSGFIELD_STANDARD);
-                newFormatDetected = true;
-            }
-            else if (streq(logFormat, "json"))
-            {
-                theStderrHandler = new HandleLogMsgHandlerJSON(stderr, MSGFIELD_STANDARD);
-                newFormatDetected = true;
-            }
-            else if (!streq(logFormat, "table"))
-                LOG(MCoperatorWarning, "JLog: Invalid log format configuration detected '%s'!", logFormat);
+void updateStdErrLogHandler(const IPropertyTree *logConfig)
+{
+    if (logConfig->hasProp(logFieldsAtt))
+    {
+        // Supported logging fields: TRC,SPN,AUD,CLS,DET,MID,TIM,DAT,PID,TID,NOD,JOB,USE,SES,COD,MLT,MCT,NNT,COM,QUO,PFX,ALL,STD
+        const char *logFields = logConfig->queryProp(logFieldsAtt);
+        if (!isEmptyString(logFields))
+            theStderrHandler->setMessageFields(logMsgFieldsFromAbbrevs(logFields));
+    }
 
-            if (newFormatDetected)
-                theManager->resetMonitors();
-        }
+    // Only recreate filter if at least one filter attribute configured
+    if (logConfig->hasProp(logMsgDetailAtt) || logConfig->hasProp(logMsgAudiencesAtt) || logConfig->hasProp(logMsgClassesAtt))
+    {
+        LogMsgDetail logDetail = logConfig->getPropInt(logMsgDetailAtt, DefaultDetail);
 
-        if (logConfig->hasProp(logFieldsAtt))
-        {
-            //Supported logging fields: TRC,SPN,AUD,CLS,DET,MID,TIM,DAT,PID,TID,NOD,JOB,USE,SES,COD,MLT,MCT,NNT,COM,QUO,PFX,ALL,STD
-            const char *logFields = logConfig->queryProp(logFieldsAtt);
-            if (!isEmptyString(logFields))
-                theStderrHandler->setMessageFields(logMsgFieldsFromAbbrevs(logFields));
-        }
+        unsigned msgClasses = MSGCLS_all;
+        const char *logClasses = logConfig->queryProp(logMsgClassesAtt);
+        if (!isEmptyString(logClasses))
+            msgClasses = logMsgClassesFromAbbrevs(logClasses);
 
-        //Only recreate filter if at least one filter attribute configured
-        if (logConfig->hasProp(logMsgDetailAtt) || logConfig->hasProp(logMsgAudiencesAtt) || logConfig->hasProp(logMsgClassesAtt))
-        {
-            LogMsgDetail logDetail = logConfig->getPropInt(logMsgDetailAtt, DefaultDetail);
+        unsigned msgAudiences = MSGAUD_all;
+        const char *logAudiences = logConfig->queryProp(logMsgAudiencesAtt);
+        if (!isEmptyString(logAudiences))
+            msgAudiences = logMsgAudsFromAbbrevs(logAudiences);
 
-            unsigned msgClasses = MSGCLS_all;
-            const char *logClasses = logConfig->queryProp(logMsgClassesAtt);
-            if (!isEmptyString(logClasses))
-            {
-                msgClasses = logMsgClassesFromAbbrevs(logClasses);
-            }
+        const bool local = true; // Do not include remote messages from other components
+        Owned<ILogMsgFilter> filter = getCategoryLogMsgFilter(msgAudiences, msgClasses, logDetail, local);
+        theManager->changeMonitorFilter(theStderrHandler, filter);
+    }
+}
 
-            unsigned msgAudiences = MSGAUD_all;
-            const char *logAudiences = logConfig->queryProp(logMsgAudiencesAtt);
-            if (!isEmptyString(logAudiences))
-            {
-                msgAudiences = logMsgAudsFromAbbrevs(logAudiences);
-            }
+static void loggingSetupUpdate(const IPropertyTree *oldComponentConfiguration, const IPropertyTree *oldGlobalConfiguration)
+{
+    Owned<IPropertyTree> logConfig = getComponentConfigSP()->getPropTree("logging");
+    if (!logConfig)
+        return;
+    if (logConfig->getPropBool(logDisabledAtt, false))
+    {
+        OWARNLOG("JLog: Ignoring can't be disabled dynamically via an update");
+        return;
+    }
 
-            const bool local = true; // Do not include remote messages from other components
-            Owned<ILogMsgFilter> filter = getCategoryLogMsgFilter(msgAudiences, msgClasses, logDetail, local);
-            theManager->changeMonitorFilter(theStderrHandler, filter);
-        }
+    LogHandlerFormat newFormat = getConfigHandlerFormat(logConfig);
+    if (newFormat != LOGFORMAT_undefined)
+    {
+        OWARNLOG("JLog: Ignoring log format configuration change on the fly, as it is not supported in a containerized environment");
+    }
 
+    updateStdErrLogHandler(logConfig);
+}
+
+static CConfigUpdateHook configUpdateHook;
+
+// NB: it is not thread-safe to change the handler whilst other threads are logging.
+// This should only be called at startup.
+void setupContainerizedLogMsgHandler()
+{
+    Owned<IPropertyTree> logConfig = getComponentConfigSP()->getPropTree("logging");
+    if (logConfig && logConfig->getPropBool(logDisabledAtt, false))
+    {
+        removeLog();
+        return; // NB: can't be reenabled dynamically via an update at present
+    }
+    LogHandlerFormat newFormat = getConfigHandlerFormat(logConfig); // NB: theStderrHandler is initially setup in MODULE_INIT
+    if (newFormat != LOGFORMAT_undefined)
+    {
+        // NB: old theStderrHandler leaks, should be fixed by separate PR.
+        switch (newFormat)
+        {
+            case LOGFORMAT_xml:
+                theStderrHandler = new HandleLogMsgHandlerXML(stderr, MSGFIELD_STANDARD);
+                break;
+            case LOGFORMAT_json:
+                theStderrHandler = new HandleLogMsgHandlerJSON(stderr, MSGFIELD_STANDARD);
+                break;
+            case LOGFORMAT_table:
+                theStderrHandler = new HandleLogMsgHandlerTable(stderr, MSGFIELD_STANDARD);
+                break;
+            default:
+                throwUnexpected(); // should never reach here
+        }
+        theManager->resetMonitors();
+    }
+    if (logConfig)
+    {
         unsigned queueLen = logConfig->getPropInt(logQueueLenAtt, queueLenDefault);
         if (queueLen)
         {
@@ -2472,7 +2516,10 @@ void setupContainerizedLogMsgHandler()
             thePostMortemHandler = new PostMortemLogMsgHandler(portMortemFileBase, postMortemLines, MSGFIELD_STANDARD);
             queryLogMsgManager()->addMonitor(thePostMortemHandler, getCategoryLogMsgFilter(MSGAUD_all, MSGCLS_all, TopDetail));
         }
+
+        updateStdErrLogHandler(logConfig);
     }
+    configUpdateHook.installOnce(loggingSetupUpdate, false);
 }
 
 ILogMsgManager * queryLogMsgManager()
@@ -2504,7 +2551,7 @@ ISysLogEventLogger * querySysLogEventLogger()
 
 ILogMsgHandler * getSysLogMsgHandler(unsigned fields)
 {
-    return new SysLogMsgHandler(theSysLogEventLogger, fields); 
+    return new SysLogMsgHandler(theSysLogEventLogger, fields);
 }
 
 #ifdef _WIN32
@@ -2555,7 +2602,7 @@ bool CSysLogEventLogger::win32Report(unsigned eventtype, unsigned category, unsi
                 char src[_MAX_PATH+1];
                 LPTSTR tail;
                 DWORD res = SearchPath(NULL,"JELOG.DLL",NULL,sizeof(src),src,&tail);
-                if (res>0) 
+                if (res>0)
                     copyFile(path,src);
                 else
                     throw makeOsException(GetLastError());
@@ -2570,20 +2617,20 @@ bool CSysLogEventLogger::win32Report(unsigned eventtype, unsigned category, unsi
 
         }
         HKEY hk;
-        if (RegCreateKeyEx(HKEY_LOCAL_MACHINE,"SYSTEM\\CurrentControlSet\\Services\\EventLog\\Application\\Seisint", 
+        if (RegCreateKeyEx(HKEY_LOCAL_MACHINE,"SYSTEM\\CurrentControlSet\\Services\\EventLog\\Application\\Seisint",
                            NULL, NULL, 0, KEY_ALL_ACCESS, NULL, &hk, NULL)==0) {
             DWORD sizedata = 0;
             DWORD type = REG_EXPAND_SZ;
             if ((RegQueryValueEx(hk,"EventMessageFile",NULL, &type, NULL, &sizedata)!=0)||!sizedata) {
-                StringAttr str("%SystemRoot%\\System32\\JELOG.dll"); 
+                StringAttr str("%SystemRoot%\\System32\\JELOG.dll");
                 RegSetValueEx(hk,"EventMessageFile", 0, REG_EXPAND_SZ, (LPBYTE) str.get(), (DWORD)str.length() + 1);
                 RegSetValueEx(hk,"CategoryMessageFile", 0, REG_EXPAND_SZ, (LPBYTE) str.get(), (DWORD)str.length() + 1);
-                DWORD dwData = EVENTLOG_ERROR_TYPE | EVENTLOG_WARNING_TYPE | EVENTLOG_INFORMATION_TYPE | EVENTLOG_AUDIT_SUCCESS | EVENTLOG_AUDIT_FAILURE; 
+                DWORD dwData = EVENTLOG_ERROR_TYPE | EVENTLOG_WARNING_TYPE | EVENTLOG_INFORMATION_TYPE | EVENTLOG_AUDIT_SUCCESS | EVENTLOG_AUDIT_FAILURE;
                 RegSetValueEx(hk, "TypesSupported", 0, REG_DWORD,  (LPBYTE) &dwData,  sizeof(DWORD));
                 dwData = 16;
                 RegSetValueEx(hk, "CategoryCount", 0, REG_DWORD,  (LPBYTE) &dwData,  sizeof(DWORD));
             }
-            RegCloseKey(hk); 
+            RegCloseKey(hk);
         }
         hEventLog = RegisterEventSource(NULL,"Seisint");
         if (!hEventLog) {
@@ -2621,8 +2668,8 @@ bool CSysLogEventLogger::win32Report(unsigned eventtype, unsigned category, unsi
 
 CSysLogEventLogger::~CSysLogEventLogger()
 {
-    if (hEventLog!=0) 
-        DeregisterEventSource(hEventLog); 
+    if (hEventLog!=0)
+        DeregisterEventSource(hEventLog);
 }
 
 #else
@@ -2631,7 +2678,7 @@ CSysLogEventLogger::~CSysLogEventLogger()
 
 #define CATEGORY_AUDIT_FUNCTION_REQUIRED
 #define AUDIT_TYPES_BEGIN int auditTypeDataMap[NUM_AUDIT_TYPES+1] = {
-#define MAKE_AUDIT_TYPE(name, type, categoryid, eventid, level) level, 
+#define MAKE_AUDIT_TYPE(name, type, categoryid, eventid, level) level,
 #define AUDIT_TYPES_END 0 };
 #include "jelogtype.hpp"
 #undef CATEGORY_AUDIT_FUNCTION_REQUIRED
@@ -2932,7 +2979,7 @@ extern jlib_decl void UseSysLogForOperatorMessages(bool use)
         return;
     if (use) {
         msgHandler = getSysLogMsgHandler();
-        ILogMsgFilter * operatorFilter = getCategoryLogMsgFilter(MSGAUD_operator, MSGCLS_all, DefaultDetail, true);  
+        ILogMsgFilter * operatorFilter = getCategoryLogMsgFilter(MSGAUD_operator, MSGCLS_all, DefaultDetail, true);
         queryLogMsgManager()->addMonitorOwn(msgHandler, operatorFilter);
     }
     else {
@@ -3435,7 +3482,7 @@ TraceFlags loadTraceFlags(const IPropertyTree *ptree, const std::initializer_lis
     return dft;
 }
 
-void ctxlogReport(const LogMsgCategory & cat, const char * format, ...) 
+void ctxlogReport(const LogMsgCategory & cat, const char * format, ...)
 {
     va_list args;
     va_start(args, format);
@@ -3443,7 +3490,7 @@ void ctxlogReport(const LogMsgCategory & cat, const char * format, ...)
     va_end(args);
 }
 
-void ctxlogReportVA(const LogMsgCategory & cat, const char * format, va_list args) 
+void ctxlogReportVA(const LogMsgCategory & cat, const char * format, va_list args)
 {
     ctxlogReportVA(cat, NoLogMsgCode, format, args);
 }
diff --git a/system/jlib/jlog.hpp b/system/jlib/jlog.hpp
index fd452291063..e0611841d96 100644
--- a/system/jlib/jlog.hpp
+++ b/system/jlib/jlog.hpp
@@ -691,6 +691,7 @@ interface jlib_decl ILogMsgFilter : public IInterface
 
 typedef enum
 {
+    LOGFORMAT_undefined,
     LOGFORMAT_xml,
     LOGFORMAT_json,
     LOGFORMAT_table
@@ -708,6 +709,7 @@ interface jlib_decl ILogMsgHandler : public IInterface
     virtual int               flush() { return 0; }
     virtual bool              getLogName(StringBuffer &name) const = 0;
     virtual offset_t          getLogPosition(StringBuffer &logFileName) const = 0;
+    virtual LogHandlerFormat  queryFormatType() const = 0;
 };
 
 // Class on manager's list of children which sends new filters to children, and holds thread which receives log messages
@@ -755,7 +757,7 @@ interface jlib_decl ILogMsgListener : public IInterface
 
 interface jlib_decl ILogMsgManager : public ILogMsgListener
 {
- public:    
+ public:
     virtual void              enterQueueingMode() = 0;
     virtual void              setQueueBlockingLimit(unsigned lim) = 0;
     virtual void              setQueueDroppingLimit(unsigned lim, unsigned numToDrop) = 0;
diff --git a/system/jlib/jlog.ipp b/system/jlib/jlog.ipp
index a16269fcfdc..00709c79b4e 100644
--- a/system/jlib/jlog.ipp
+++ b/system/jlib/jlog.ipp
@@ -309,10 +309,10 @@ class AndLogMsgFilter : public CLogMsgFilter
 public:
     AndLogMsgFilter(ILogMsgFilter * _arg1, ILogMsgFilter * _arg2) : arg1(_arg1), arg2(_arg2) {}
     AndLogMsgFilter(MemoryBuffer & in) { arg1.setown(getDeserializedLogMsgFilter(in)); arg2.setown(getDeserializedLogMsgFilter(in)); }
-    AndLogMsgFilter(IPropertyTree * tree) 
-    { 
+    AndLogMsgFilter(IPropertyTree * tree)
+    {
         Owned <IPropertyTreeIterator> iter = tree->getElements("filter");
-        ForEach(*iter) 
+        ForEach(*iter)
         {
             ILogMsgFilter *filter = getLogMsgFilterFromPTree(&(iter->query()));
             if (!arg1.get())
@@ -342,10 +342,10 @@ class OrLogMsgFilter : public CLogMsgFilter
 public:
     OrLogMsgFilter(ILogMsgFilter * _arg1, ILogMsgFilter * _arg2) : arg1(_arg1), arg2(_arg2) {}
     OrLogMsgFilter(MemoryBuffer & in) { arg1.setown(getDeserializedLogMsgFilter(in)); arg2.setown(getDeserializedLogMsgFilter(in)); }
-    OrLogMsgFilter(IPropertyTree * tree) 
-    { 
+    OrLogMsgFilter(IPropertyTree * tree)
+    {
         Owned <IPropertyTreeIterator> iter = tree->getElements("filter");
-        ForEach(*iter) 
+        ForEach(*iter)
         {
             ILogMsgFilter *filter = getLogMsgFilterFromPTree(&(iter->query()));
             if (!arg1.get())
@@ -380,7 +380,7 @@ public:
         yes.setown(getDeserializedLogMsgFilter(in));
         no.setown(getDeserializedLogMsgFilter(in));
     }
-    SwitchLogMsgFilter(IPropertyTree * tree) 
+    SwitchLogMsgFilter(IPropertyTree * tree)
     {
         cond.setown(getLogMsgFilterFromPTree(tree->queryPropTree("filter[1]")));
         yes.setown(getLogMsgFilterFromPTree(tree->queryPropTree("filter[2]")));
@@ -433,11 +433,12 @@ public:
     HandleLogMsgHandler(FILE * _handle, unsigned _fields) : handle(_handle), messageFields(_fields) {}
     virtual ~HandleLogMsgHandler() { flush(); }
 
-    unsigned                  queryMessageFields() const { return messageFields; }
-    void                      setMessageFields(unsigned _fields) { messageFields = _fields; }
-    int                       flush() { CriticalBlock block(crit); return fflush(handle); }
-    bool                      getLogName(StringBuffer &name) const { return false; }
-    offset_t                  getLogPosition(StringBuffer &name) const { return 0; }
+    virtual unsigned          queryMessageFields() const override { return messageFields; }
+    virtual void              setMessageFields(unsigned _fields) override { messageFields = _fields; }
+    virtual int               flush() override { CriticalBlock block(crit); return fflush(handle); }
+    virtual bool              getLogName(StringBuffer &name) const override { return false; }
+    virtual offset_t          getLogPosition(StringBuffer &name) const override { return 0; }
+    virtual LogHandlerFormat  queryFormatType() const override = 0;
 protected:
     FILE *                    handle;
     unsigned                  messageFields;
@@ -450,10 +451,11 @@ class HandleLogMsgHandlerJSON : implements HandleLogMsgHandler, public CInterfac
 public:
     HandleLogMsgHandlerJSON(FILE * _handle, unsigned _fields) : HandleLogMsgHandler(_handle, _fields) {}
     IMPLEMENT_IINTERFACE;
-    void                      handleMessage(const LogMsg & msg);
-    bool                      needsPrep() const { return false; }
-    void                      prep() {}
-    void                      addToPTree(IPropertyTree * tree) const;
+    virtual void              handleMessage(const LogMsg & msg) override;
+    virtual bool              needsPrep() const override { return false; }
+    virtual void              prep() override {}
+    virtual void              addToPTree(IPropertyTree * tree) const override;
+    virtual LogHandlerFormat  queryFormatType() const override { return LOGFORMAT_json; };
 };
 
 class HandleLogMsgHandlerXML : implements HandleLogMsgHandler, public CInterface
@@ -461,10 +463,11 @@ class HandleLogMsgHandlerXML : implements HandleLogMsgHandler, public CInterface
 public:
     HandleLogMsgHandlerXML(FILE * _handle, unsigned _fields) : HandleLogMsgHandler(_handle, _fields) {}
     IMPLEMENT_IINTERFACE;
-    void                      handleMessage(const LogMsg & msg);
-    bool                      needsPrep() const { return false; }
-    void                      prep() {}
-    void                      addToPTree(IPropertyTree * tree) const;
+    virtual void              handleMessage(const LogMsg & msg) override;
+    virtual bool              needsPrep() const override { return false; }
+    virtual void              prep() override {}
+    virtual void              addToPTree(IPropertyTree * tree) const override;
+    virtual LogHandlerFormat  queryFormatType() const override { return LOGFORMAT_xml; };
 };
 
 class HandleLogMsgHandlerTable : implements HandleLogMsgHandler, public CInterface
@@ -472,10 +475,11 @@ class HandleLogMsgHandlerTable : implements HandleLogMsgHandler, public CInterfa
 public:
     HandleLogMsgHandlerTable(FILE * _handle, unsigned _fields) : HandleLogMsgHandler(_handle, _fields), prepped(false) {}
     IMPLEMENT_IINTERFACE;
-    void                      handleMessage(const LogMsg & msg);
-    bool                      needsPrep() const { return !prepped; }
-    void                      prep() { CriticalBlock block(crit); LogMsg::fprintTableHead(handle, messageFields); prepped = true; }
-    void                      addToPTree(IPropertyTree * tree) const;
+    virtual void              handleMessage(const LogMsg & msg) override;
+    virtual bool              needsPrep() const override { return !prepped; }
+    virtual void              prep() override { CriticalBlock block(crit); LogMsg::fprintTableHead(handle, messageFields); prepped = true; }
+    virtual void              addToPTree(IPropertyTree * tree) const override;
+    virtual LogHandlerFormat  queryFormatType() const override { return LOGFORMAT_table; };
 private:
     bool                      prepped;
 };
@@ -487,12 +491,12 @@ class FileLogMsgHandler : public ILogMsgHandler
 public:
     FileLogMsgHandler(const char * _filename, const char * _headerText = 0, unsigned _fields = MSGFIELD_all, bool _append = false, bool _flushes = true);
     virtual ~FileLogMsgHandler();
-    unsigned                  queryMessageFields() const { return messageFields; }
-    void                      setMessageFields(unsigned _fields) { messageFields = _fields; }
-    int                       flush() { CriticalBlock block(crit); return fflush(handle); }
-    bool                      getLogName(StringBuffer &name) const { name.append(filename); return true; }
-    offset_t                  getLogPosition(StringBuffer &name) const { CriticalBlock block(crit); fflush(handle); name.append(filename); return ftell(handle); }
-                
+    virtual unsigned          queryMessageFields() const override { return messageFields; }
+    virtual void              setMessageFields(unsigned _fields) override { messageFields = _fields; }
+    virtual int               flush() override { CriticalBlock block(crit); return fflush(handle); }
+    virtual bool              getLogName(StringBuffer &name) const override { name.append(filename); return true; }
+    virtual offset_t          getLogPosition(StringBuffer &name) const override { CriticalBlock block(crit); fflush(handle); name.append(filename); return ftell(handle); }
+
 protected:
     FILE *                    handle;
     unsigned                  messageFields;
@@ -509,10 +513,11 @@ class FileLogMsgHandlerJSON : implements FileLogMsgHandler, public CInterface
 public:
     FileLogMsgHandlerJSON(const char * _filename, const char * _headerText = 0, unsigned _fields = MSGFIELD_all, bool _append = false, bool _flushes = true) : FileLogMsgHandler(_filename, _headerText, _fields, _append, _flushes) {}
     IMPLEMENT_IINTERFACE;
-    void                      handleMessage(const LogMsg & msg);
-    bool                      needsPrep() const { return false; }
-    void                      prep() {}
-    void                      addToPTree(IPropertyTree * tree) const;
+    virtual void              handleMessage(const LogMsg & msg) override;
+    virtual bool              needsPrep() const override { return false; }
+    virtual void              prep() override {}
+    virtual void              addToPTree(IPropertyTree * tree) const override;
+    virtual LogHandlerFormat  queryFormatType() const override { return LOGFORMAT_json; };
 };
 
 class FileLogMsgHandlerXML : implements FileLogMsgHandler, public CInterface
@@ -520,10 +525,11 @@ class FileLogMsgHandlerXML : implements FileLogMsgHandler, public CInterface
 public:
     FileLogMsgHandlerXML(const char * _filename, const char * _headerText = 0, unsigned _fields = MSGFIELD_all, bool _append = false, bool _flushes = true) : FileLogMsgHandler(_filename, _headerText, _fields, _append, _flushes) {}
     IMPLEMENT_IINTERFACE;
-    void                      handleMessage(const LogMsg & msg);
-    bool                      needsPrep() const { return false; }
-    void                      prep() {}
-    void                      addToPTree(IPropertyTree * tree) const;
+    virtual void              handleMessage(const LogMsg & msg) override;
+    virtual bool              needsPrep() const override { return false; }
+    virtual void              prep() override {}
+    virtual void              addToPTree(IPropertyTree * tree) const override;
+    virtual LogHandlerFormat  queryFormatType() const override { return LOGFORMAT_xml; };
 };
 
 class FileLogMsgHandlerTable : implements FileLogMsgHandler, public CInterface
@@ -531,10 +537,11 @@ class FileLogMsgHandlerTable : implements FileLogMsgHandler, public CInterface
 public:
     FileLogMsgHandlerTable(const char * _filename, const char * _headerText = 0, unsigned _fields = MSGFIELD_all, bool _append = false, bool _flushes = true) : FileLogMsgHandler(_filename, _headerText, _fields, _append, _flushes), prepped(false) {}
     IMPLEMENT_IINTERFACE;
-    void                      handleMessage(const LogMsg & msg);
-    bool                      needsPrep() const { return !prepped; }
-    void                      prep() { CriticalBlock block(crit); LogMsg::fprintTableHead(handle, messageFields); prepped = true; }
-    void                      addToPTree(IPropertyTree * tree) const;
+    virtual void              handleMessage(const LogMsg & msg) override;
+    virtual bool              needsPrep() const override { return !prepped; }
+    virtual void              prep() override { CriticalBlock block(crit); LogMsg::fprintTableHead(handle, messageFields); prepped = true; }
+    virtual void              addToPTree(IPropertyTree * tree) const override;
+    virtual LogHandlerFormat  queryFormatType() const override { return LOGFORMAT_table; };
 private:
     bool                      prepped;
 };
@@ -555,6 +562,7 @@ public:
     virtual int flush() override { CriticalBlock block(crit); return fflush(handle); }
     virtual bool getLogName(StringBuffer &name) const override { CriticalBlock block(crit); name.append(filename); return true; }
     virtual offset_t getLogPosition(StringBuffer &name) const override { CriticalBlock block(crit); fflush(handle); name.append(filename); return ftell(handle); }
+    virtual LogHandlerFormat  queryFormatType() const override { return LOGFORMAT_table; };
 protected:
     void checkRollover();
     void doRollover();
@@ -579,7 +587,7 @@ public:
                              const char *initialName = NULL, const char *alias = NULL, bool daily = false, long _maxLogFileSize = 0);
     virtual ~RollingFileLogMsgHandler();
     IMPLEMENT_IINTERFACE;
-    void                      handleMessage(const LogMsg & msg)
+    virtual void              handleMessage(const LogMsg & msg) override
     {
         CriticalBlock block(crit);
         checkRollover();
@@ -596,14 +604,16 @@ public:
 
         if(flushes) fflush(handle);
     }
-    bool                      needsPrep() const { return false; }
-    void                      prep() {}
-    unsigned                  queryMessageFields() const { return messageFields; }
-    void                      setMessageFields(unsigned _fields) { messageFields = _fields; }
-    void                      addToPTree(IPropertyTree * tree) const;
-    int                       flush() { CriticalBlock block(crit); return fflush(handle); }
-    bool                      getLogName(StringBuffer &name) const { CriticalBlock block(crit); name.append(filename); return true; }
-    offset_t                  getLogPosition(StringBuffer &name) const { CriticalBlock block(crit); fflush(handle); name.append(filename); return ftell(handle); }
+    virtual bool              needsPrep() const override { return false; }
+    virtual void              prep() override {}
+    virtual unsigned          queryMessageFields() const override { return messageFields; }
+    virtual void              setMessageFields(unsigned _fields) override { messageFields = _fields; }
+    virtual void              addToPTree(IPropertyTree * tree) const override;
+    virtual int               flush() override { CriticalBlock block(crit); return fflush(handle); }
+    virtual bool              getLogName(StringBuffer &name) const override { CriticalBlock block(crit); name.append(filename); return true; }
+    virtual offset_t          getLogPosition(StringBuffer &name) const override { CriticalBlock block(crit); fflush(handle); name.append(filename); return ftell(handle); }
+    virtual LogHandlerFormat  queryFormatType() const override { return LOGFORMAT_table; };
+
 protected:
     void                      checkRollover();
     void                      doRollover(bool daily, const char *forceName = NULL);
@@ -634,15 +644,16 @@ public:
     BinLogMsgHandler(const char * _filename, bool _append = false);
     virtual ~BinLogMsgHandler();
     IMPLEMENT_IINTERFACE;
-    void                      handleMessage(const LogMsg & msg);
-    bool                      needsPrep() const { return false; }
-    void                      prep() {}
-    void                      addToPTree(IPropertyTree * tree) const;
-    unsigned                  queryMessageFields() const { return MSGFIELD_all; }
-    void                      setMessageFields(unsigned _fields) {}
-    int                       flush() { return 0; }
-    bool                      getLogName(StringBuffer &name) const { name.append(filename); return true; }
-    offset_t                  getLogPosition(StringBuffer &name) const { CriticalBlock block(crit); name.append(filename); return fstr->tell(); }
+    virtual void              handleMessage(const LogMsg & msg) override;
+    virtual bool              needsPrep() const override { return false; }
+    virtual void              prep() override {}
+    virtual void              addToPTree(IPropertyTree * tree) const override;
+    virtual unsigned          queryMessageFields() const override { return MSGFIELD_all; }
+    virtual void              setMessageFields(unsigned _fields) override {}
+    virtual int               flush() override { return 0; }
+    virtual bool              getLogName(StringBuffer &name) const override { name.append(filename); return true; }
+    virtual offset_t          getLogPosition(StringBuffer &name) const override { CriticalBlock block(crit); name.append(filename); return fstr->tell(); }
+    virtual LogHandlerFormat  queryFormatType() const override { return LOGFORMAT_undefined; };
 protected:
     StringAttr                filename;
     bool                      append;
@@ -660,15 +671,16 @@ class SysLogMsgHandler : implements ILogMsgHandler, public CInterface
 public:
     SysLogMsgHandler(ISysLogEventLogger * _logger, unsigned _fields) : logger(_logger), fields(_fields) {}
     IMPLEMENT_IINTERFACE;
-    void                      handleMessage(const LogMsg & msg);
-    bool                      needsPrep() const { return false; }
-    void                      prep() {}
-    void                      addToPTree(IPropertyTree * tree) const;
-    unsigned                  queryMessageFields() const { return fields; }
-    void                      setMessageFields(unsigned _fields) { fields = _fields; }
-    int                       flush() { return 0; }
-    bool                      getLogName(StringBuffer &name) const { return false; }
-    offset_t                  getLogPosition(StringBuffer &logFileName) const { return 0; }
+    virtual void              handleMessage(const LogMsg & msg) override;
+    virtual bool              needsPrep() const override { return false; }
+    virtual void              prep() override {}
+    virtual void              addToPTree(IPropertyTree * tree) const override;
+    virtual unsigned          queryMessageFields() const override { return fields; }
+    virtual void              setMessageFields(unsigned _fields) override { fields = _fields; }
+    virtual int               flush() override { return 0; }
+    virtual bool              getLogName(StringBuffer &name) const override { return false; }
+    virtual offset_t          getLogPosition(StringBuffer &logFileName) const override { return 0; }
+    virtual LogHandlerFormat  queryFormatType() const override { return LOGFORMAT_undefined; };
 protected:
     ISysLogEventLogger *      logger;
     unsigned                  fields;
@@ -926,7 +938,7 @@ public:
     BinaryLogAccessFilter(ILogAccessFilter * _arg1, ILogAccessFilter * _arg2, LogAccessFilterType _type) : arg1(_arg1), arg2(_arg2)
     {
         if (!arg1 || !arg2)
-            throw makeStringException(-1, "Binary Log Access Filter encountered empty operand"); 
+            throw makeStringException(-1, "Binary Log Access Filter encountered empty operand");
 
         setType(_type);
     }
diff --git a/system/mp/mplog.ipp b/system/mp/mplog.ipp
index 2f3e91c15cb..89ce9252242 100644
--- a/system/mp/mplog.ipp
+++ b/system/mp/mplog.ipp
@@ -135,20 +135,21 @@ public:
     LinkToParentLogMsgHandler(MPLogId _cid, MPLogId _pid, INode * _parentNode, bool _connected) : parentNode(_parentNode), cid(_cid), pid(_pid), receiverThread(new LogMsgFilterReceiverThread(_pid, _parentNode)), connected(_connected) { receiverThread->setHandler(this); }
     ~LinkToParentLogMsgHandler();
     IMPLEMENT_IINTERFACE;
-    void                      handleMessage(LogMsg const & msg);
-    bool                      needsPrep() const { return false; }
-    void                      prep() {}
-    void                      addToPTree(IPropertyTree * tree) const;
-    unsigned                  queryMessageFields() const { return MSGFIELD_all; }
-    void                      setMessageFields(unsigned _fields = MSGFIELD_all) {}
+    virtual void              handleMessage(LogMsg const & msg) override;
+    virtual bool              needsPrep() const override { return false; }
+    virtual void              prep() override {}
+    virtual void              addToPTree(IPropertyTree * tree) const override;
+    virtual unsigned          queryMessageFields() const override { return MSGFIELD_all; }
+    virtual void              setMessageFields(unsigned _fields = MSGFIELD_all) override {}
     ILogMsgFilter *           receiveFilter() const;
     void                      startReceiver() { receiverThread->start(false); }
     void                      connect();
     void                      disconnect();
     bool                      queryConnected() const { return connected; }
     void                      markDisconnected() { connected = false; }
-    bool                      getLogName(StringBuffer &name) const { return false; }
-    offset_t                  getLogPosition(StringBuffer &name) const { return 0; }
+    virtual bool              getLogName(StringBuffer &name) const override { return false; }
+    virtual offset_t          getLogPosition(StringBuffer &name) const override { return 0; }
+    virtual LogHandlerFormat  queryFormatType() const override { return LOGFORMAT_undefined; };
 private:
     Linked<INode>             parentNode;
     MPLogId                   cid;


Reviews:
user: login: copilot-pull-request-reviewer[bot]
body: ## Pull Request Overview

This PR addresses HPCC-33885 by enhancing the logging configuration to allow persistent components to update their logging configuration on the fly without restarting. Key changes include:
- Adding a new pure virtual method queryFormatType() to log handler classes in jlog.ipp.
- Introducing queryMaxLinesToKeep() and setMaxLinesToKeep() in the ILogMsgHandler interface in jlog.hpp and their concrete implementations.
- Updating the containerized logging setup in jlog.cpp to support on‑the‑fly configuration updates and dynamic postmortem log line adjustments.

### Reviewed Changes

Copilot reviewed 3 out of 4 changed files in this pull request and generated 1 comment.

| File                  | Description                                                                     |
| --------------------- | ------------------------------------------------------------------------------- |
| system/jlib/jlog.ipp  | Adds queryFormatType() method to log handler classes and adjusts maxLinesToKeep.  |
| system/jlib/jlog.hpp  | Extends ILogMsgHandler interface with new max-lines methods and updates function signature. |
| system/jlib/jlog.cpp  | Refactors setupContainerizedLogMsgHandler to support live updates and adjusts postmortem handler logic. |

<details>
<summary>Files not reviewed (1)</summary>

* **helm/hpcc/templates/_helpers.tpl**: Language not supported
</details>



state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-24T11:25:17Z
user: login: jakesmith
body: @streeterd - haven't looked beyond the unintended release of a change to plugins/CMakeLists.txt ..

Please self-review in github before tagging, to weed out issues like this.
state: CHANGES_REQUESTED
author_association: MEMBER
submitted_at: 2025-04-24T12:14:16Z
user: login: jakesmith
body: @streeterd - discussed offline. I think this needs to be simplified with setupContainerizedLogging split into 2 functions, with a configureHandlers() or similarly named, called from the setup function and installed as a hook.

Such that configureHandlers only changes properties of the already created handlers.
Should still complain if format detection spotted.

A postmortem maxlines change will need to be coded carefully. 
In fact, it may be best to do that as a follow on PR - smaller PR's are better PR's.
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-24T15:44:31Z
user: login: streeterd
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-28T13:48:11Z
user: login: jakesmith
body: @streeterd - tagging you back for now, after pushing commit.
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-30T15:31:15Z
user: login: streeterd
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-01T08:38:23Z
user: login: jakesmith
body: @streeterd - please see comments.
state: CHANGES_REQUESTED
author_association: MEMBER
submitted_at: 2025-05-01T10:45:52Z
user: login: streeterd
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-01T11:54:53Z
user: login: streeterd
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-01T11:55:00Z
user: login: streeterd
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-01T11:55:07Z
user: login: streeterd
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-01T11:55:10Z
user: login: streeterd
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-01T11:55:16Z
user: login: streeterd
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-01T11:55:19Z
user: login: streeterd
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-01T11:55:23Z
user: login: streeterd
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-01T11:55:27Z
user: login: streeterd
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-01T11:55:43Z
user: login: streeterd
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-01T11:56:27Z
user: login: jakesmith
body: @streeterd - 1 trivial issue remaining (see comment).
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-01T12:26:07Z
user: login: streeterd
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-01T13:00:34Z
user: login: jakesmith
body: @streeterd - a couple of minor comments.
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-12T10:04:31Z
user: login: streeterd
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-14T08:06:15Z
user: login: streeterd
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-14T08:06:41Z
user: login: jakesmith
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-15T13:02:49Z
user: login: jakesmith
body: @streeterd - 1 comment re. removal of format change warning 
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-15T13:03:48Z
user: login: streeterd
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-15T13:58:38Z
user: login: jakesmith
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-15T15:03:38Z
user: login: jakesmith
body: @streeterd - looks good.

Please squash.
state: APPROVED
author_association: MEMBER
submitted_at: 2025-05-15T15:03:52Z
user: login: ghalliday
body: Looks good.  Only a few trivial comments, not worth changing.
state: APPROVED
author_association: MEMBER
submitted_at: 2025-05-21T10:56:51Z

Review comments:
diff_hunk: @@ -2377,36 +2377,87 @@ static constexpr unsigned queueLenDefault = 512;
 static constexpr unsigned queueDropDefault = 0; // disabled by default
 static constexpr bool useSysLogDefault = false;
 
-void setupContainerizedLogMsgHandler()
+static Singleton<bool> firstUpdateConfigHookCall;
+static bool firstUpdateConfigHookCallValue{true};
+bool queryFirstUpdateConfigHookCall()
+{
+    return *firstUpdateConfigHookCall.query([] () { return &firstUpdateConfigHookCallValue; });
+}
+
+auto updateConfigFunc = [](const IPropertyTree *oldComponentConfiguration, const IPropertyTree *oldGlobalConfiguration)
+{
+    setupContainerizedLogMsgHandler(true);
+};
+static CConfigUpdateHook configUpdateHook;
+// NOTE:    The parameter updateConfigOnTheFly value of true signifies that the thread
+//          safe caller has completed setting up the logging configuration (normally
+//          from a main call).
+//          Any further configuration changes are from non-thread safe callers.
+//          EXCEPT: There are 2 calls to setupContainerizedLogMsgHandler that are
+//          thread-safe. In order to allow them to change the log format (if required)
+//          etc we need to allow the first config update hook call to update the config
+//          as if it was not called from the config update hook.
+    void setupContainerizedLogMsgHandler(bool updateConfigOnTheFly)
 {
+    // Alow the first config update hook call to update the config as if it was not
path: system/jlib/jlog.cpp
user: login: Copilot
body: Typo in comment: 'Alow' should be corrected to 'Allow'.
```suggestion
    // Allow the first config update hook call to update the config as if it was not
```
created_at: 2025-04-24T11:25:17Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1142,7 +1142,7 @@
           "description": "Number of log entries to drop from the log queue when it is full. Disabled if 0, or if queueLength is 0",
           "default": 0
         },
-        "dataFormat": {
+        "format": {
path: helm/hpcc/values.schema.json
user: login: streeterd
body: Correct the logging format property name to `format`.
created_at: 2025-04-28T13:48:11Z
author_association: CONTRIBUTOR
diff_hunk: @@ -438,6 +438,7 @@ public:
     int                       flush() { CriticalBlock block(crit); return fflush(handle); }
     bool                      getLogName(StringBuffer &name) const { return false; }
     offset_t                  getLogPosition(StringBuffer &name) const { return 0; }
+    virtual LogHandlerFormat  queryFormatType() const = 0;
path: system/jlib/jlog.ipp
user: login: jakesmith
body: virtual is good, but it jars that other existing virtuals methods are not decorated like this. In cases like this, it's worth updating the other methods, rather than sacrifice the new code for consistency.
created_at: 2025-04-30T12:27:49Z
author_association: MEMBER
diff_hunk: @@ -438,6 +438,7 @@ public:
     int                       flush() { CriticalBlock block(crit); return fflush(handle); }
     bool                      getLogName(StringBuffer &name) const { return false; }
     offset_t                  getLogPosition(StringBuffer &name) const { return 0; }
+    virtual LogHandlerFormat  queryFormatType() const = 0;
path: system/jlib/jlog.ipp
user: login: jakesmith
body: also, though not strictly needed for this change, I think it would be better to add to interface too (ILogMsgHandler), as it stands out as an anomaly otherwise.

And, given that, these should really all have 'override's on their definitions too
created_at: 2025-04-30T12:31:30Z
author_association: MEMBER
diff_hunk: @@ -438,6 +438,7 @@ public:
     int                       flush() { CriticalBlock block(crit); return fflush(handle); }
     bool                      getLogName(StringBuffer &name) const { return false; }
     offset_t                  getLogPosition(StringBuffer &name) const { return 0; }
+    virtual LogHandlerFormat  queryFormatType() const = 0;
path: system/jlib/jlog.ipp
user: login: streeterd
body: @jakesmith Fixed in 299171978b8eb2d3b671615436a1af702b246ea9
created_at: 2025-05-01T08:38:23Z
author_association: CONTRIBUTOR
diff_hunk: @@ -4320,6 +4320,7 @@ class UserMetricMsgHandler : public CInterface, implements ILogMsgHandler, imple
     virtual int flush() { return 0; }
     virtual bool getLogName(StringBuffer &name __attribute__((unused))) const { return false; }
     virtual offset_t getLogPosition(StringBuffer &logFileName __attribute__((unused))) const { return 0; };
+    virtual LogHandlerFormat queryFormatType() const override { return LOGFORMAT_undefined; };
path: system/jlib/jdebug.cpp
user: login: jakesmith
body: worth adding the missing 'override's to the other virtuals to make consistent.
created_at: 2025-05-01T10:09:42Z
author_association: MEMBER
diff_hunk: @@ -2377,70 +2377,114 @@ static constexpr unsigned queueLenDefault = 512;
 static constexpr unsigned queueDropDefault = 0; // disabled by default
 static constexpr bool useSysLogDefault = false;
 
-void setupContainerizedLogMsgHandler()
+// returns LOGFORMAT_undefined if format has not changed
+// NB: returns LOGFORMAT_table if no format specified (i.e. this is the default)
+LogHandlerFormat hasLogFormatChanged(const IPropertyTree *logConfig)
 {
-    Owned<IPropertyTree> logConfig = getComponentConfigSP()->getPropTree("logging");
-    if (logConfig)
+    LogHandlerFormat currentFormat = theStderrHandler->queryFormatType();
+    LogHandlerFormat newFormat{LOGFORMAT_undefined};
+    const char *newFormatString = logConfig ? logConfig->queryProp(logFormatAtt) : nullptr;
+    if (!newFormatString) // absent, defaults to "table"
+        newFormat = LOGFORMAT_table;
+    else
     {
-        if (logConfig->getPropBool(logDisabledAtt, false))
-        {
-            removeLog();
-            return;
-        }
+        if (streq(newFormatString, "xml"))
+            newFormat = LOGFORMAT_xml;
+        else if (streq(newFormatString, "json"))
+            newFormat = LOGFORMAT_json;
+        else if (streq(newFormatString, "table"))
+            newFormat = LOGFORMAT_table;
+        else
+            LOG(MCoperatorWarning, "JLog: Invalid log format configuration detected '%s'!", newFormatString);
path: system/jlib/jlog.cpp
user: login: jakesmith
body: OWARNLOG more concise
created_at: 2025-05-01T10:12:08Z
author_association: MEMBER
diff_hunk: @@ -2473,6 +2517,7 @@ void setupContainerizedLogMsgHandler()
             queryLogMsgManager()->addMonitor(thePostMortemHandler, getCategoryLogMsgFilter(MSGAUD_all, MSGCLS_all, TopDetail));
         }
path: system/jlib/jlog.cpp
user: login: jakesmith
body: there should be call after this line to updateStdErrLogHandler ..
as it stands, nothing is using the config properties at the outset..until there's an update.
created_at: 2025-05-01T10:23:40Z
author_association: MEMBER
diff_hunk: @@ -912,6 +914,7 @@ extern jlib_decl ILogMsgHandler * queryStderrLogMsgHandler();
 extern jlib_decl ILogMsgHandler * queryPostMortemLogMsgHandler();
 extern jlib_decl bool copyPostMortemLogging(const char *target, bool clear);
 extern jlib_decl void setupContainerizedLogMsgHandler();
+bool configureHandlers(bool rejectOnTheFlyChanges);
path: system/jlib/jlog.hpp
user: login: jakesmith
body: missing extern jlib_decl
created_at: 2025-05-01T10:23:55Z
author_association: MEMBER
diff_hunk: @@ -912,6 +914,7 @@ extern jlib_decl ILogMsgHandler * queryStderrLogMsgHandler();
 extern jlib_decl ILogMsgHandler * queryPostMortemLogMsgHandler();
 extern jlib_decl bool copyPostMortemLogging(const char *target, bool clear);
 extern jlib_decl void setupContainerizedLogMsgHandler();
+bool configureHandlers(bool rejectOnTheFlyChanges);
path: system/jlib/jlog.hpp
user: login: jakesmith
body: but actually this is old, should be removed.
created_at: 2025-05-01T10:24:19Z
author_association: MEMBER
diff_hunk: @@ -450,32 +451,35 @@ class HandleLogMsgHandlerJSON : implements HandleLogMsgHandler, public CInterfac
 public:
     HandleLogMsgHandlerJSON(FILE * _handle, unsigned _fields) : HandleLogMsgHandler(_handle, _fields) {}
     IMPLEMENT_IINTERFACE;
-    void                      handleMessage(const LogMsg & msg);
-    bool                      needsPrep() const { return false; }
-    void                      prep() {}
-    void                      addToPTree(IPropertyTree * tree) const;
+    void                      handleMessage(const LogMsg & msg) override;
+    bool                      needsPrep() const override { return false; }
+    void                      prep() override {}
+    void                      addToPTree(IPropertyTree * tree) const override;
+    virtual LogHandlerFormat  queryFormatType() const override { return LOGFORMAT_json; };
path: system/jlib/jlog.ipp
user: login: jakesmith
body: please decorate other methods with 'virtual' too to be consistent.
created_at: 2025-05-01T10:25:03Z
author_association: MEMBER
diff_hunk: @@ -450,32 +451,35 @@ class HandleLogMsgHandlerJSON : implements HandleLogMsgHandler, public CInterfac
 public:
     HandleLogMsgHandlerJSON(FILE * _handle, unsigned _fields) : HandleLogMsgHandler(_handle, _fields) {}
     IMPLEMENT_IINTERFACE;
-    void                      handleMessage(const LogMsg & msg);
-    bool                      needsPrep() const { return false; }
-    void                      prep() {}
-    void                      addToPTree(IPropertyTree * tree) const;
+    void                      handleMessage(const LogMsg & msg) override;
+    bool                      needsPrep() const override { return false; }
+    void                      prep() override {}
+    void                      addToPTree(IPropertyTree * tree) const override;
+    virtual LogHandlerFormat  queryFormatType() const override { return LOGFORMAT_json; };
 };
 
 class HandleLogMsgHandlerXML : implements HandleLogMsgHandler, public CInterface
 {
 public:
     HandleLogMsgHandlerXML(FILE * _handle, unsigned _fields) : HandleLogMsgHandler(_handle, _fields) {}
     IMPLEMENT_IINTERFACE;
-    void                      handleMessage(const LogMsg & msg);
-    bool                      needsPrep() const { return false; }
-    void                      prep() {}
-    void                      addToPTree(IPropertyTree * tree) const;
+    void                      handleMessage(const LogMsg & msg) override;
+    bool                      needsPrep() const override { return false; }
+    void                      prep() override {}
+    void                      addToPTree(IPropertyTree * tree) const override;
+    virtual LogHandlerFormat  queryFormatType() const override { return LOGFORMAT_xml; };
path: system/jlib/jlog.ipp
user: login: jakesmith
body: please decorate other methods with 'virtual' too to be consistent.
created_at: 2025-05-01T10:25:12Z
author_association: MEMBER
diff_hunk: @@ -450,32 +451,35 @@ class HandleLogMsgHandlerJSON : implements HandleLogMsgHandler, public CInterfac
 public:
     HandleLogMsgHandlerJSON(FILE * _handle, unsigned _fields) : HandleLogMsgHandler(_handle, _fields) {}
     IMPLEMENT_IINTERFACE;
-    void                      handleMessage(const LogMsg & msg);
-    bool                      needsPrep() const { return false; }
-    void                      prep() {}
-    void                      addToPTree(IPropertyTree * tree) const;
+    void                      handleMessage(const LogMsg & msg) override;
+    bool                      needsPrep() const override { return false; }
+    void                      prep() override {}
+    void                      addToPTree(IPropertyTree * tree) const override;
+    virtual LogHandlerFormat  queryFormatType() const override { return LOGFORMAT_json; };
 };
 
 class HandleLogMsgHandlerXML : implements HandleLogMsgHandler, public CInterface
 {
 public:
     HandleLogMsgHandlerXML(FILE * _handle, unsigned _fields) : HandleLogMsgHandler(_handle, _fields) {}
     IMPLEMENT_IINTERFACE;
-    void                      handleMessage(const LogMsg & msg);
-    bool                      needsPrep() const { return false; }
-    void                      prep() {}
-    void                      addToPTree(IPropertyTree * tree) const;
+    void                      handleMessage(const LogMsg & msg) override;
+    bool                      needsPrep() const override { return false; }
+    void                      prep() override {}
+    void                      addToPTree(IPropertyTree * tree) const override;
+    virtual LogHandlerFormat  queryFormatType() const override { return LOGFORMAT_xml; };
 };
 
 class HandleLogMsgHandlerTable : implements HandleLogMsgHandler, public CInterface
 {
 public:
     HandleLogMsgHandlerTable(FILE * _handle, unsigned _fields) : HandleLogMsgHandler(_handle, _fields), prepped(false) {}
     IMPLEMENT_IINTERFACE;
-    void                      handleMessage(const LogMsg & msg);
-    bool                      needsPrep() const { return !prepped; }
-    void                      prep() { CriticalBlock block(crit); LogMsg::fprintTableHead(handle, messageFields); prepped = true; }
-    void                      addToPTree(IPropertyTree * tree) const;
+    void                      handleMessage(const LogMsg & msg) override;
+    bool                      needsPrep() const override { return !prepped; }
+    void                      prep() override { CriticalBlock block(crit); LogMsg::fprintTableHead(handle, messageFields); prepped = true; }
+    void                      addToPTree(IPropertyTree * tree) const override;
+    virtual LogHandlerFormat  queryFormatType() const override { return LOGFORMAT_table; };
path: system/jlib/jlog.ipp
user: login: jakesmith
body: please decorate other methods with 'virtual' too to be consistent.
created_at: 2025-05-01T10:25:17Z
author_association: MEMBER
diff_hunk: @@ -487,12 +491,12 @@ class FileLogMsgHandler : public ILogMsgHandler
 public:
     FileLogMsgHandler(const char * _filename, const char * _headerText = 0, unsigned _fields = MSGFIELD_all, bool _append = false, bool _flushes = true);
     virtual ~FileLogMsgHandler();
-    unsigned                  queryMessageFields() const { return messageFields; }
-    void                      setMessageFields(unsigned _fields) { messageFields = _fields; }
-    int                       flush() { CriticalBlock block(crit); return fflush(handle); }
-    bool                      getLogName(StringBuffer &name) const { name.append(filename); return true; }
-    offset_t                  getLogPosition(StringBuffer &name) const { CriticalBlock block(crit); fflush(handle); name.append(filename); return ftell(handle); }
-                
+    unsigned                  queryMessageFields() const override { return messageFields; }
+    void                      setMessageFields(unsigned _fields) override { messageFields = _fields; }
+    int                       flush() override { CriticalBlock block(crit); return fflush(handle); }
+    bool                      getLogName(StringBuffer &name) const override { name.append(filename); return true; }
+    offset_t                  getLogPosition(StringBuffer &name) const override { CriticalBlock block(crit); fflush(handle); name.append(filename); return ftell(handle); }
path: system/jlib/jlog.ipp
user: login: jakesmith
body: please decorate methods with 'virtual' as well.
created_at: 2025-05-01T10:25:47Z
author_association: MEMBER
diff_hunk: @@ -509,32 +513,35 @@ class FileLogMsgHandlerJSON : implements FileLogMsgHandler, public CInterface
 public:
     FileLogMsgHandlerJSON(const char * _filename, const char * _headerText = 0, unsigned _fields = MSGFIELD_all, bool _append = false, bool _flushes = true) : FileLogMsgHandler(_filename, _headerText, _fields, _append, _flushes) {}
     IMPLEMENT_IINTERFACE;
-    void                      handleMessage(const LogMsg & msg);
-    bool                      needsPrep() const { return false; }
-    void                      prep() {}
-    void                      addToPTree(IPropertyTree * tree) const;
+    void                      handleMessage(const LogMsg & msg) override;
+    bool                      needsPrep() const override { return false; }
+    void                      prep() override {}
+    void                      addToPTree(IPropertyTree * tree) const override;
+    LogHandlerFormat          queryFormatType() const override { return LOGFORMAT_json; };
path: system/jlib/jlog.ipp
user: login: jakesmith
body: please decorate methods with 'virtual' as well.
created_at: 2025-05-01T10:25:51Z
author_association: MEMBER
diff_hunk: @@ -509,32 +513,35 @@ class FileLogMsgHandlerJSON : implements FileLogMsgHandler, public CInterface
 public:
     FileLogMsgHandlerJSON(const char * _filename, const char * _headerText = 0, unsigned _fields = MSGFIELD_all, bool _append = false, bool _flushes = true) : FileLogMsgHandler(_filename, _headerText, _fields, _append, _flushes) {}
     IMPLEMENT_IINTERFACE;
-    void                      handleMessage(const LogMsg & msg);
-    bool                      needsPrep() const { return false; }
-    void                      prep() {}
-    void                      addToPTree(IPropertyTree * tree) const;
+    void                      handleMessage(const LogMsg & msg) override;
+    bool                      needsPrep() const override { return false; }
+    void                      prep() override {}
+    void                      addToPTree(IPropertyTree * tree) const override;
+    LogHandlerFormat          queryFormatType() const override { return LOGFORMAT_json; };
 };
 
 class FileLogMsgHandlerXML : implements FileLogMsgHandler, public CInterface
 {
 public:
     FileLogMsgHandlerXML(const char * _filename, const char * _headerText = 0, unsigned _fields = MSGFIELD_all, bool _append = false, bool _flushes = true) : FileLogMsgHandler(_filename, _headerText, _fields, _append, _flushes) {}
     IMPLEMENT_IINTERFACE;
-    void                      handleMessage(const LogMsg & msg);
-    bool                      needsPrep() const { return false; }
-    void                      prep() {}
-    void                      addToPTree(IPropertyTree * tree) const;
+    void                      handleMessage(const LogMsg & msg) override;
+    bool                      needsPrep() const override { return false; }
+    void                      prep() override {}
+    void                      addToPTree(IPropertyTree * tree) const override;
+    LogHandlerFormat          queryFormatType() const override { return LOGFORMAT_xml; };
path: system/jlib/jlog.ipp
user: login: jakesmith
body: please decorate methods with 'virtual' as well.
created_at: 2025-05-01T10:25:56Z
author_association: MEMBER
diff_hunk: @@ -509,32 +513,35 @@ class FileLogMsgHandlerJSON : implements FileLogMsgHandler, public CInterface
 public:
     FileLogMsgHandlerJSON(const char * _filename, const char * _headerText = 0, unsigned _fields = MSGFIELD_all, bool _append = false, bool _flushes = true) : FileLogMsgHandler(_filename, _headerText, _fields, _append, _flushes) {}
     IMPLEMENT_IINTERFACE;
-    void                      handleMessage(const LogMsg & msg);
-    bool                      needsPrep() const { return false; }
-    void                      prep() {}
-    void                      addToPTree(IPropertyTree * tree) const;
+    void                      handleMessage(const LogMsg & msg) override;
+    bool                      needsPrep() const override { return false; }
+    void                      prep() override {}
+    void                      addToPTree(IPropertyTree * tree) const override;
+    LogHandlerFormat          queryFormatType() const override { return LOGFORMAT_json; };
 };
 
 class FileLogMsgHandlerXML : implements FileLogMsgHandler, public CInterface
 {
 public:
     FileLogMsgHandlerXML(const char * _filename, const char * _headerText = 0, unsigned _fields = MSGFIELD_all, bool _append = false, bool _flushes = true) : FileLogMsgHandler(_filename, _headerText, _fields, _append, _flushes) {}
     IMPLEMENT_IINTERFACE;
-    void                      handleMessage(const LogMsg & msg);
-    bool                      needsPrep() const { return false; }
-    void                      prep() {}
-    void                      addToPTree(IPropertyTree * tree) const;
+    void                      handleMessage(const LogMsg & msg) override;
+    bool                      needsPrep() const override { return false; }
+    void                      prep() override {}
+    void                      addToPTree(IPropertyTree * tree) const override;
+    LogHandlerFormat          queryFormatType() const override { return LOGFORMAT_xml; };
 };
 
 class FileLogMsgHandlerTable : implements FileLogMsgHandler, public CInterface
 {
 public:
     FileLogMsgHandlerTable(const char * _filename, const char * _headerText = 0, unsigned _fields = MSGFIELD_all, bool _append = false, bool _flushes = true) : FileLogMsgHandler(_filename, _headerText, _fields, _append, _flushes), prepped(false) {}
     IMPLEMENT_IINTERFACE;
-    void                      handleMessage(const LogMsg & msg);
-    bool                      needsPrep() const { return !prepped; }
-    void                      prep() { CriticalBlock block(crit); LogMsg::fprintTableHead(handle, messageFields); prepped = true; }
-    void                      addToPTree(IPropertyTree * tree) const;
+    void                      handleMessage(const LogMsg & msg) override;
+    bool                      needsPrep() const override { return !prepped; }
+    void                      prep() override { CriticalBlock block(crit); LogMsg::fprintTableHead(handle, messageFields); prepped = true; }
+    void                      addToPTree(IPropertyTree * tree) const override;
+    LogHandlerFormat          queryFormatType() const override { return LOGFORMAT_table; };
path: system/jlib/jlog.ipp
user: login: jakesmith
body: please decorate methods with 'virtual' as well.
created_at: 2025-05-01T10:26:01Z
author_association: MEMBER
diff_hunk: @@ -596,14 +604,16 @@ public:
 
         if(flushes) fflush(handle);
     }
-    bool                      needsPrep() const { return false; }
-    void                      prep() {}
-    unsigned                  queryMessageFields() const { return messageFields; }
-    void                      setMessageFields(unsigned _fields) { messageFields = _fields; }
-    void                      addToPTree(IPropertyTree * tree) const;
-    int                       flush() { CriticalBlock block(crit); return fflush(handle); }
-    bool                      getLogName(StringBuffer &name) const { CriticalBlock block(crit); name.append(filename); return true; }
-    offset_t                  getLogPosition(StringBuffer &name) const { CriticalBlock block(crit); fflush(handle); name.append(filename); return ftell(handle); }
+    bool                      needsPrep() const override { return false; }
+    void                      prep() override {}
+    unsigned                  queryMessageFields() const override { return messageFields; }
+    void                      setMessageFields(unsigned _fields) override { messageFields = _fields; }
+    void                      addToPTree(IPropertyTree * tree) const override;
+    int                       flush() override { CriticalBlock block(crit); return fflush(handle); }
+    bool                      getLogName(StringBuffer &name) const override { CriticalBlock block(crit); name.append(filename); return true; }
+    offset_t                  getLogPosition(StringBuffer &name) const override { CriticalBlock block(crit); fflush(handle); name.append(filename); return ftell(handle); }
+    LogHandlerFormat          queryFormatType() const override { return LOGFORMAT_undefined; };
path: system/jlib/jlog.ipp
user: login: jakesmith
body: please decorate methods with 'virtual' as well.
created_at: 2025-05-01T10:28:25Z
author_association: MEMBER
diff_hunk: @@ -555,6 +562,7 @@ public:
     virtual int flush() override { CriticalBlock block(crit); return fflush(handle); }
     virtual bool getLogName(StringBuffer &name) const override { CriticalBlock block(crit); name.append(filename); return true; }
     virtual offset_t getLogPosition(StringBuffer &name) const override { CriticalBlock block(crit); fflush(handle); name.append(filename); return ftell(handle); }
+    virtual LogHandlerFormat  queryFormatType() const override { return LOGFORMAT_undefined; };
path: system/jlib/jlog.ipp
user: login: jakesmith
body: this should be LOGFORMAT_table
created_at: 2025-05-01T10:33:07Z
author_association: MEMBER
diff_hunk: @@ -634,15 +644,16 @@ public:
     BinLogMsgHandler(const char * _filename, bool _append = false);
     virtual ~BinLogMsgHandler();
     IMPLEMENT_IINTERFACE;
-    void                      handleMessage(const LogMsg & msg);
-    bool                      needsPrep() const { return false; }
-    void                      prep() {}
-    void                      addToPTree(IPropertyTree * tree) const;
-    unsigned                  queryMessageFields() const { return MSGFIELD_all; }
-    void                      setMessageFields(unsigned _fields) {}
-    int                       flush() { return 0; }
-    bool                      getLogName(StringBuffer &name) const { name.append(filename); return true; }
-    offset_t                  getLogPosition(StringBuffer &name) const { CriticalBlock block(crit); name.append(filename); return fstr->tell(); }
+    void                      handleMessage(const LogMsg & msg) override;
+    bool                      needsPrep() const override { return false; }
+    void                      prep() override {}
+    void                      addToPTree(IPropertyTree * tree) const override;
+    unsigned                  queryMessageFields() const override { return MSGFIELD_all; }
+    void                      setMessageFields(unsigned _fields) override {}
+    int                       flush() override { return 0; }
+    bool                      getLogName(StringBuffer &name) const override { name.append(filename); return true; }
+    offset_t                  getLogPosition(StringBuffer &name) const override { CriticalBlock block(crit); name.append(filename); return fstr->tell(); }
+    LogHandlerFormat          queryFormatType() const override { return LOGFORMAT_undefined; };
path: system/jlib/jlog.ipp
user: login: jakesmith
body: please decorate methods with 'virtual' as well.
created_at: 2025-05-01T10:33:34Z
author_association: MEMBER
diff_hunk: @@ -596,14 +604,16 @@ public:
 
         if(flushes) fflush(handle);
     }
-    bool                      needsPrep() const { return false; }
-    void                      prep() {}
-    unsigned                  queryMessageFields() const { return messageFields; }
-    void                      setMessageFields(unsigned _fields) { messageFields = _fields; }
-    void                      addToPTree(IPropertyTree * tree) const;
-    int                       flush() { CriticalBlock block(crit); return fflush(handle); }
-    bool                      getLogName(StringBuffer &name) const { CriticalBlock block(crit); name.append(filename); return true; }
-    offset_t                  getLogPosition(StringBuffer &name) const { CriticalBlock block(crit); fflush(handle); name.append(filename); return ftell(handle); }
+    bool                      needsPrep() const override { return false; }
+    void                      prep() override {}
+    unsigned                  queryMessageFields() const override { return messageFields; }
+    void                      setMessageFields(unsigned _fields) override { messageFields = _fields; }
+    void                      addToPTree(IPropertyTree * tree) const override;
+    int                       flush() override { CriticalBlock block(crit); return fflush(handle); }
+    bool                      getLogName(StringBuffer &name) const override { CriticalBlock block(crit); name.append(filename); return true; }
+    offset_t                  getLogPosition(StringBuffer &name) const override { CriticalBlock block(crit); fflush(handle); name.append(filename); return ftell(handle); }
+    LogHandlerFormat          queryFormatType() const override { return LOGFORMAT_undefined; };
path: system/jlib/jlog.ipp
user: login: jakesmith
body: should be LOGFORMAT_table
created_at: 2025-05-01T10:34:10Z
author_association: MEMBER
diff_hunk: @@ -660,15 +671,16 @@ class SysLogMsgHandler : implements ILogMsgHandler, public CInterface
 public:
     SysLogMsgHandler(ISysLogEventLogger * _logger, unsigned _fields) : logger(_logger), fields(_fields) {}
     IMPLEMENT_IINTERFACE;
-    void                      handleMessage(const LogMsg & msg);
-    bool                      needsPrep() const { return false; }
-    void                      prep() {}
-    void                      addToPTree(IPropertyTree * tree) const;
-    unsigned                  queryMessageFields() const { return fields; }
-    void                      setMessageFields(unsigned _fields) { fields = _fields; }
-    int                       flush() { return 0; }
-    bool                      getLogName(StringBuffer &name) const { return false; }
-    offset_t                  getLogPosition(StringBuffer &logFileName) const { return 0; }
+    void                      handleMessage(const LogMsg & msg) override;
+    bool                      needsPrep() const override { return false; }
+    void                      prep() override {}
+    void                      addToPTree(IPropertyTree * tree) const override;
+    unsigned                  queryMessageFields() const override { return fields; }
+    void                      setMessageFields(unsigned _fields) override { fields = _fields; }
+    int                       flush() override { return 0; }
+    bool                      getLogName(StringBuffer &name) const override { return false; }
+    offset_t                  getLogPosition(StringBuffer &logFileName) const override { return 0; }
+    LogHandlerFormat          queryFormatType() const override { return LOGFORMAT_undefined; };
path: system/jlib/jlog.ipp
user: login: jakesmith
body: please decorate methods with 'virtual' as well.
created_at: 2025-05-01T10:35:32Z
author_association: MEMBER
diff_hunk: @@ -135,20 +135,21 @@ public:
     LinkToParentLogMsgHandler(MPLogId _cid, MPLogId _pid, INode * _parentNode, bool _connected) : parentNode(_parentNode), cid(_cid), pid(_pid), receiverThread(new LogMsgFilterReceiverThread(_pid, _parentNode)), connected(_connected) { receiverThread->setHandler(this); }
     ~LinkToParentLogMsgHandler();
     IMPLEMENT_IINTERFACE;
-    void                      handleMessage(LogMsg const & msg);
-    bool                      needsPrep() const { return false; }
-    void                      prep() {}
-    void                      addToPTree(IPropertyTree * tree) const;
-    unsigned                  queryMessageFields() const { return MSGFIELD_all; }
-    void                      setMessageFields(unsigned _fields = MSGFIELD_all) {}
+    void                      handleMessage(LogMsg const & msg) override;
+    bool                      needsPrep() const override { return false; }
+    void                      prep() override {}
+    void                      addToPTree(IPropertyTree * tree) const override;
+    unsigned                  queryMessageFields() const override { return MSGFIELD_all; }
+    void                      setMessageFields(unsigned _fields = MSGFIELD_all) override {}
path: system/mp/mplog.ipp
user: login: jakesmith
body: please decorate methods with 'virtual' as well.
created_at: 2025-05-01T10:35:40Z
author_association: MEMBER
diff_hunk: @@ -135,20 +135,21 @@ public:
     LinkToParentLogMsgHandler(MPLogId _cid, MPLogId _pid, INode * _parentNode, bool _connected) : parentNode(_parentNode), cid(_cid), pid(_pid), receiverThread(new LogMsgFilterReceiverThread(_pid, _parentNode)), connected(_connected) { receiverThread->setHandler(this); }
     ~LinkToParentLogMsgHandler();
     IMPLEMENT_IINTERFACE;
-    void                      handleMessage(LogMsg const & msg);
-    bool                      needsPrep() const { return false; }
-    void                      prep() {}
-    void                      addToPTree(IPropertyTree * tree) const;
-    unsigned                  queryMessageFields() const { return MSGFIELD_all; }
-    void                      setMessageFields(unsigned _fields = MSGFIELD_all) {}
+    void                      handleMessage(LogMsg const & msg) override;
+    bool                      needsPrep() const override { return false; }
+    void                      prep() override {}
+    void                      addToPTree(IPropertyTree * tree) const override;
+    unsigned                  queryMessageFields() const override { return MSGFIELD_all; }
+    void                      setMessageFields(unsigned _fields = MSGFIELD_all) override {}
     ILogMsgFilter *           receiveFilter() const;
     void                      startReceiver() { receiverThread->start(false); }
     void                      connect();
     void                      disconnect();
     bool                      queryConnected() const { return connected; }
     void                      markDisconnected() { connected = false; }
-    bool                      getLogName(StringBuffer &name) const { return false; }
-    offset_t                  getLogPosition(StringBuffer &name) const { return 0; }
+    bool                      getLogName(StringBuffer &name) const override { return false; }
+    offset_t                  getLogPosition(StringBuffer &name) const override { return 0; }
+    LogHandlerFormat          queryFormatType() const override { return LOGFORMAT_undefined; };
path: system/mp/mplog.ipp
user: login: jakesmith
body: please decorate methods with 'virtual' as well.
created_at: 2025-05-01T10:35:46Z
author_association: MEMBER
diff_hunk: @@ -2377,70 +2377,114 @@ static constexpr unsigned queueLenDefault = 512;
 static constexpr unsigned queueDropDefault = 0; // disabled by default
 static constexpr bool useSysLogDefault = false;
 
-void setupContainerizedLogMsgHandler()
+// returns LOGFORMAT_undefined if format has not changed
+// NB: returns LOGFORMAT_table if no format specified (i.e. this is the default)
+LogHandlerFormat hasLogFormatChanged(const IPropertyTree *logConfig)
path: system/jlib/jlog.cpp
user: login: jakesmith
body: hasXX doesn't really make sense anymore, I suggest renaming to 'getConfigHandlerFormat'
created_at: 2025-05-01T10:40:05Z
author_association: MEMBER
diff_hunk: @@ -2377,70 +2377,114 @@ static constexpr unsigned queueLenDefault = 512;
 static constexpr unsigned queueDropDefault = 0; // disabled by default
 static constexpr bool useSysLogDefault = false;
 
-void setupContainerizedLogMsgHandler()
+// returns LOGFORMAT_undefined if format has not changed
+// NB: returns LOGFORMAT_table if no format specified (i.e. this is the default)
+LogHandlerFormat hasLogFormatChanged(const IPropertyTree *logConfig)
path: system/jlib/jlog.cpp
user: login: streeterd
body: Fixed in 9ad9d660f5f2a264b57c2de13092f55f9993c059
created_at: 2025-05-01T11:54:52Z
author_association: CONTRIBUTOR
diff_hunk: @@ -135,20 +135,21 @@ public:
     LinkToParentLogMsgHandler(MPLogId _cid, MPLogId _pid, INode * _parentNode, bool _connected) : parentNode(_parentNode), cid(_cid), pid(_pid), receiverThread(new LogMsgFilterReceiverThread(_pid, _parentNode)), connected(_connected) { receiverThread->setHandler(this); }
     ~LinkToParentLogMsgHandler();
     IMPLEMENT_IINTERFACE;
-    void                      handleMessage(LogMsg const & msg);
-    bool                      needsPrep() const { return false; }
-    void                      prep() {}
-    void                      addToPTree(IPropertyTree * tree) const;
-    unsigned                  queryMessageFields() const { return MSGFIELD_all; }
-    void                      setMessageFields(unsigned _fields = MSGFIELD_all) {}
+    void                      handleMessage(LogMsg const & msg) override;
+    bool                      needsPrep() const override { return false; }
+    void                      prep() override {}
+    void                      addToPTree(IPropertyTree * tree) const override;
+    unsigned                  queryMessageFields() const override { return MSGFIELD_all; }
+    void                      setMessageFields(unsigned _fields = MSGFIELD_all) override {}
     ILogMsgFilter *           receiveFilter() const;
     void                      startReceiver() { receiverThread->start(false); }
     void                      connect();
     void                      disconnect();
     bool                      queryConnected() const { return connected; }
     void                      markDisconnected() { connected = false; }
-    bool                      getLogName(StringBuffer &name) const { return false; }
-    offset_t                  getLogPosition(StringBuffer &name) const { return 0; }
+    bool                      getLogName(StringBuffer &name) const override { return false; }
+    offset_t                  getLogPosition(StringBuffer &name) const override { return 0; }
+    LogHandlerFormat          queryFormatType() const override { return LOGFORMAT_undefined; };
path: system/mp/mplog.ipp
user: login: streeterd
body: Fixed in 9ad9d660f5f2a264b57c2de13092f55f9993c059
created_at: 2025-05-01T11:55:00Z
author_association: CONTRIBUTOR
diff_hunk: @@ -135,20 +135,21 @@ public:
     LinkToParentLogMsgHandler(MPLogId _cid, MPLogId _pid, INode * _parentNode, bool _connected) : parentNode(_parentNode), cid(_cid), pid(_pid), receiverThread(new LogMsgFilterReceiverThread(_pid, _parentNode)), connected(_connected) { receiverThread->setHandler(this); }
     ~LinkToParentLogMsgHandler();
     IMPLEMENT_IINTERFACE;
-    void                      handleMessage(LogMsg const & msg);
-    bool                      needsPrep() const { return false; }
-    void                      prep() {}
-    void                      addToPTree(IPropertyTree * tree) const;
-    unsigned                  queryMessageFields() const { return MSGFIELD_all; }
-    void                      setMessageFields(unsigned _fields = MSGFIELD_all) {}
+    void                      handleMessage(LogMsg const & msg) override;
+    bool                      needsPrep() const override { return false; }
+    void                      prep() override {}
+    void                      addToPTree(IPropertyTree * tree) const override;
+    unsigned                  queryMessageFields() const override { return MSGFIELD_all; }
+    void                      setMessageFields(unsigned _fields = MSGFIELD_all) override {}
path: system/mp/mplog.ipp
user: login: streeterd
body: Fixed in 9ad9d660f5f2a264b57c2de13092f55f9993c059
created_at: 2025-05-01T11:55:07Z
author_association: CONTRIBUTOR
diff_hunk: @@ -660,15 +671,16 @@ class SysLogMsgHandler : implements ILogMsgHandler, public CInterface
 public:
     SysLogMsgHandler(ISysLogEventLogger * _logger, unsigned _fields) : logger(_logger), fields(_fields) {}
     IMPLEMENT_IINTERFACE;
-    void                      handleMessage(const LogMsg & msg);
-    bool                      needsPrep() const { return false; }
-    void                      prep() {}
-    void                      addToPTree(IPropertyTree * tree) const;
-    unsigned                  queryMessageFields() const { return fields; }
-    void                      setMessageFields(unsigned _fields) { fields = _fields; }
-    int                       flush() { return 0; }
-    bool                      getLogName(StringBuffer &name) const { return false; }
-    offset_t                  getLogPosition(StringBuffer &logFileName) const { return 0; }
+    void                      handleMessage(const LogMsg & msg) override;
+    bool                      needsPrep() const override { return false; }
+    void                      prep() override {}
+    void                      addToPTree(IPropertyTree * tree) const override;
+    unsigned                  queryMessageFields() const override { return fields; }
+    void                      setMessageFields(unsigned _fields) override { fields = _fields; }
+    int                       flush() override { return 0; }
+    bool                      getLogName(StringBuffer &name) const override { return false; }
+    offset_t                  getLogPosition(StringBuffer &logFileName) const override { return 0; }
+    LogHandlerFormat          queryFormatType() const override { return LOGFORMAT_undefined; };
path: system/jlib/jlog.ipp
user: login: streeterd
body: Fixed in 9ad9d660f5f2a264b57c2de13092f55f9993c059
created_at: 2025-05-01T11:55:10Z
author_association: CONTRIBUTOR
diff_hunk: @@ -634,15 +644,16 @@ public:
     BinLogMsgHandler(const char * _filename, bool _append = false);
     virtual ~BinLogMsgHandler();
     IMPLEMENT_IINTERFACE;
-    void                      handleMessage(const LogMsg & msg);
-    bool                      needsPrep() const { return false; }
-    void                      prep() {}
-    void                      addToPTree(IPropertyTree * tree) const;
-    unsigned                  queryMessageFields() const { return MSGFIELD_all; }
-    void                      setMessageFields(unsigned _fields) {}
-    int                       flush() { return 0; }
-    bool                      getLogName(StringBuffer &name) const { name.append(filename); return true; }
-    offset_t                  getLogPosition(StringBuffer &name) const { CriticalBlock block(crit); name.append(filename); return fstr->tell(); }
+    void                      handleMessage(const LogMsg & msg) override;
+    bool                      needsPrep() const override { return false; }
+    void                      prep() override {}
+    void                      addToPTree(IPropertyTree * tree) const override;
+    unsigned                  queryMessageFields() const override { return MSGFIELD_all; }
+    void                      setMessageFields(unsigned _fields) override {}
+    int                       flush() override { return 0; }
+    bool                      getLogName(StringBuffer &name) const override { name.append(filename); return true; }
+    offset_t                  getLogPosition(StringBuffer &name) const override { CriticalBlock block(crit); name.append(filename); return fstr->tell(); }
+    LogHandlerFormat          queryFormatType() const override { return LOGFORMAT_undefined; };
path: system/jlib/jlog.ipp
user: login: streeterd
body: Fixed in 9ad9d660f5f2a264b57c2de13092f55f9993c059
created_at: 2025-05-01T11:55:16Z
author_association: CONTRIBUTOR
diff_hunk: @@ -450,32 +451,35 @@ class HandleLogMsgHandlerJSON : implements HandleLogMsgHandler, public CInterfac
 public:
     HandleLogMsgHandlerJSON(FILE * _handle, unsigned _fields) : HandleLogMsgHandler(_handle, _fields) {}
     IMPLEMENT_IINTERFACE;
-    void                      handleMessage(const LogMsg & msg);
-    bool                      needsPrep() const { return false; }
-    void                      prep() {}
-    void                      addToPTree(IPropertyTree * tree) const;
+    void                      handleMessage(const LogMsg & msg) override;
+    bool                      needsPrep() const override { return false; }
+    void                      prep() override {}
+    void                      addToPTree(IPropertyTree * tree) const override;
+    virtual LogHandlerFormat  queryFormatType() const override { return LOGFORMAT_json; };
path: system/jlib/jlog.ipp
user: login: streeterd
body: Fixed in 9ad9d660f5f2a264b57c2de13092f55f9993c059
created_at: 2025-05-01T11:55:19Z
author_association: CONTRIBUTOR
diff_hunk: @@ -912,6 +914,7 @@ extern jlib_decl ILogMsgHandler * queryStderrLogMsgHandler();
 extern jlib_decl ILogMsgHandler * queryPostMortemLogMsgHandler();
 extern jlib_decl bool copyPostMortemLogging(const char *target, bool clear);
 extern jlib_decl void setupContainerizedLogMsgHandler();
+bool configureHandlers(bool rejectOnTheFlyChanges);
path: system/jlib/jlog.hpp
user: login: streeterd
body: Fixed in 9ad9d660f5f2a264b57c2de13092f55f9993c059
created_at: 2025-05-01T11:55:22Z
author_association: CONTRIBUTOR
diff_hunk: @@ -2473,6 +2517,7 @@ void setupContainerizedLogMsgHandler()
             queryLogMsgManager()->addMonitor(thePostMortemHandler, getCategoryLogMsgFilter(MSGAUD_all, MSGCLS_all, TopDetail));
         }
path: system/jlib/jlog.cpp
user: login: streeterd
body: Fixed in 9ad9d660f5f2a264b57c2de13092f55f9993c059
created_at: 2025-05-01T11:55:27Z
author_association: CONTRIBUTOR
diff_hunk: @@ -4320,6 +4320,7 @@ class UserMetricMsgHandler : public CInterface, implements ILogMsgHandler, imple
     virtual int flush() { return 0; }
     virtual bool getLogName(StringBuffer &name __attribute__((unused))) const { return false; }
     virtual offset_t getLogPosition(StringBuffer &logFileName __attribute__((unused))) const { return 0; };
+    virtual LogHandlerFormat queryFormatType() const override { return LOGFORMAT_undefined; };
path: system/jlib/jdebug.cpp
user: login: streeterd
body: Fixed in 9ad9d660f5f2a264b57c2de13092f55f9993c059
created_at: 2025-05-01T11:55:43Z
author_association: CONTRIBUTOR
diff_hunk: @@ -2377,70 +2377,114 @@ static constexpr unsigned queueLenDefault = 512;
 static constexpr unsigned queueDropDefault = 0; // disabled by default
 static constexpr bool useSysLogDefault = false;
 
-void setupContainerizedLogMsgHandler()
+// returns LOGFORMAT_undefined if format has not changed
+// NB: returns LOGFORMAT_table if no format specified (i.e. this is the default)
+LogHandlerFormat hasLogFormatChanged(const IPropertyTree *logConfig)
 {
-    Owned<IPropertyTree> logConfig = getComponentConfigSP()->getPropTree("logging");
-    if (logConfig)
+    LogHandlerFormat currentFormat = theStderrHandler->queryFormatType();
+    LogHandlerFormat newFormat{LOGFORMAT_undefined};
+    const char *newFormatString = logConfig ? logConfig->queryProp(logFormatAtt) : nullptr;
+    if (!newFormatString) // absent, defaults to "table"
+        newFormat = LOGFORMAT_table;
+    else
     {
-        if (logConfig->getPropBool(logDisabledAtt, false))
-        {
-            removeLog();
-            return;
-        }
+        if (streq(newFormatString, "xml"))
+            newFormat = LOGFORMAT_xml;
+        else if (streq(newFormatString, "json"))
+            newFormat = LOGFORMAT_json;
+        else if (streq(newFormatString, "table"))
+            newFormat = LOGFORMAT_table;
+        else
+            LOG(MCoperatorWarning, "JLog: Invalid log format configuration detected '%s'!", newFormatString);
path: system/jlib/jlog.cpp
user: login: streeterd
body: Fixed in 28782b75b2e43271ae091d7a68eb283b83ad821d
created_at: 2025-05-01T11:56:27Z
author_association: CONTRIBUTOR
diff_hunk: @@ -596,14 +604,16 @@ public:
 
         if(flushes) fflush(handle);
     }
-    bool                      needsPrep() const { return false; }
-    void                      prep() {}
-    unsigned                  queryMessageFields() const { return messageFields; }
-    void                      setMessageFields(unsigned _fields) { messageFields = _fields; }
-    void                      addToPTree(IPropertyTree * tree) const;
-    int                       flush() { CriticalBlock block(crit); return fflush(handle); }
-    bool                      getLogName(StringBuffer &name) const { CriticalBlock block(crit); name.append(filename); return true; }
-    offset_t                  getLogPosition(StringBuffer &name) const { CriticalBlock block(crit); fflush(handle); name.append(filename); return ftell(handle); }
+    virtual bool              needsPrep() const override { return false; }
+    virtual void              prep() override {}
+    virtual unsigned          queryMessageFields() const override { return messageFields; }
+    virtual void              setMessageFields(unsigned _fields) override { messageFields = _fields; }
+    virtual void              addToPTree(IPropertyTree * tree) const override;
+    virtual int               flush() override { CriticalBlock block(crit); return fflush(handle); }
+    virtual bool              getLogName(StringBuffer &name) const override { CriticalBlock block(crit); name.append(filename); return true; }
+    virtual offset_t          getLogPosition(StringBuffer &name) const override { CriticalBlock block(crit); fflush(handle); name.append(filename); return ftell(handle); }
+    virtual LogHandlerFormat  queryFormatType() const override { return LOGFORMAT_undefined; };
path: system/jlib/jlog.ipp
user: login: jakesmith
body: I missed one.. RollingFileLogMsgHandler is implicitly LOGFORMAT_table (see its handleMessage impl).
created_at: 2025-05-01T12:24:34Z
author_association: MEMBER
diff_hunk: @@ -596,14 +604,16 @@ public:
 
         if(flushes) fflush(handle);
     }
-    bool                      needsPrep() const { return false; }
-    void                      prep() {}
-    unsigned                  queryMessageFields() const { return messageFields; }
-    void                      setMessageFields(unsigned _fields) { messageFields = _fields; }
-    void                      addToPTree(IPropertyTree * tree) const;
-    int                       flush() { CriticalBlock block(crit); return fflush(handle); }
-    bool                      getLogName(StringBuffer &name) const { CriticalBlock block(crit); name.append(filename); return true; }
-    offset_t                  getLogPosition(StringBuffer &name) const { CriticalBlock block(crit); fflush(handle); name.append(filename); return ftell(handle); }
+    virtual bool              needsPrep() const override { return false; }
+    virtual void              prep() override {}
+    virtual unsigned          queryMessageFields() const override { return messageFields; }
+    virtual void              setMessageFields(unsigned _fields) override { messageFields = _fields; }
+    virtual void              addToPTree(IPropertyTree * tree) const override;
+    virtual int               flush() override { CriticalBlock block(crit); return fflush(handle); }
+    virtual bool              getLogName(StringBuffer &name) const override { CriticalBlock block(crit); name.append(filename); return true; }
+    virtual offset_t          getLogPosition(StringBuffer &name) const override { CriticalBlock block(crit); fflush(handle); name.append(filename); return ftell(handle); }
+    virtual LogHandlerFormat  queryFormatType() const override { return LOGFORMAT_undefined; };
path: system/jlib/jlog.ipp
user: login: streeterd
body: Fixed in e647cc9736fddb95971da93f58636ba0a0258892
created_at: 2025-05-01T13:00:34Z
author_association: CONTRIBUTOR
diff_hunk: @@ -2377,70 +2377,114 @@ static constexpr unsigned queueLenDefault = 512;
 static constexpr unsigned queueDropDefault = 0; // disabled by default
 static constexpr bool useSysLogDefault = false;
 
-void setupContainerizedLogMsgHandler()
+// returns LOGFORMAT_undefined if format has not changed
+// NB: returns LOGFORMAT_table if no format specified (i.e. this is the default)
+LogHandlerFormat getConfigHandlerFormat(const IPropertyTree *logConfig)
 {
-    Owned<IPropertyTree> logConfig = getComponentConfigSP()->getPropTree("logging");
-    if (logConfig)
+    LogHandlerFormat currentFormat = theStderrHandler->queryFormatType();
+    LogHandlerFormat newFormat{LOGFORMAT_undefined};
+    const char *newFormatString = logConfig ? logConfig->queryProp(logFormatAtt) : nullptr;
+    if (!newFormatString) // absent, defaults to "table"
+        newFormat = LOGFORMAT_table;
+    else
     {
-        if (logConfig->getPropBool(logDisabledAtt, false))
-        {
-            removeLog();
-            return;
-        }
+        if (streq(newFormatString, "xml"))
+            newFormat = LOGFORMAT_xml;
+        else if (streq(newFormatString, "json"))
+            newFormat = LOGFORMAT_json;
+        else if (streq(newFormatString, "table"))
+            newFormat = LOGFORMAT_table;
+        else
+            OWARNLOG("JLog: Invalid log format configuration detected '%s'!", newFormatString);
+    }
+    return currentFormat != newFormat ? newFormat : LOGFORMAT_undefined;
+}
 
-        if (logConfig->hasProp(logFormatAtt))
-        {
-            const char *logFormat = logConfig->queryProp(logFormatAtt);
-            bool newFormatDetected = false;
-            if (streq(logFormat, "xml"))
-            {
-                theStderrHandler = new HandleLogMsgHandlerXML(stderr, MSGFIELD_STANDARD);
-                newFormatDetected = true;
-            }
-            else if (streq(logFormat, "json"))
-            {
-                theStderrHandler = new HandleLogMsgHandlerJSON(stderr, MSGFIELD_STANDARD);
-                newFormatDetected = true;
-            }
-            else if (!streq(logFormat, "table"))
-                LOG(MCoperatorWarning, "JLog: Invalid log format configuration detected '%s'!", logFormat);
+void updateStdErrLogHandler(const IPropertyTree *logConfig)
+{
+    if (logConfig->hasProp(logFieldsAtt))
+    {
+        // Supported logging fields: TRC,SPN,AUD,CLS,DET,MID,TIM,DAT,PID,TID,NOD,JOB,USE,SES,COD,MLT,MCT,NNT,COM,QUO,PFX,ALL,STD
+        const char *logFields = logConfig->queryProp(logFieldsAtt);
+        if (!isEmptyString(logFields))
+            theStderrHandler->setMessageFields(logMsgFieldsFromAbbrevs(logFields));
+    }
 
-            if (newFormatDetected)
-                theManager->resetMonitors();
-        }
+    // Only recreate filter if at least one filter attribute configured
+    if (logConfig->hasProp(logMsgDetailAtt) || logConfig->hasProp(logMsgAudiencesAtt) || logConfig->hasProp(logMsgClassesAtt))
+    {
+        LogMsgDetail logDetail = logConfig->getPropInt(logMsgDetailAtt, DefaultDetail);
 
-        if (logConfig->hasProp(logFieldsAtt))
-        {
-            //Supported logging fields: TRC,SPN,AUD,CLS,DET,MID,TIM,DAT,PID,TID,NOD,JOB,USE,SES,COD,MLT,MCT,NNT,COM,QUO,PFX,ALL,STD
-            const char *logFields = logConfig->queryProp(logFieldsAtt);
-            if (!isEmptyString(logFields))
-                theStderrHandler->setMessageFields(logMsgFieldsFromAbbrevs(logFields));
-        }
+        unsigned msgClasses = MSGCLS_all;
+        const char *logClasses = logConfig->queryProp(logMsgClassesAtt);
+        if (!isEmptyString(logClasses))
+            msgClasses = logMsgClassesFromAbbrevs(logClasses);
 
-        //Only recreate filter if at least one filter attribute configured
-        if (logConfig->hasProp(logMsgDetailAtt) || logConfig->hasProp(logMsgAudiencesAtt) || logConfig->hasProp(logMsgClassesAtt))
-        {
-            LogMsgDetail logDetail = logConfig->getPropInt(logMsgDetailAtt, DefaultDetail);
+        unsigned msgAudiences = MSGAUD_all;
+        const char *logAudiences = logConfig->queryProp(logMsgAudiencesAtt);
+        if (!isEmptyString(logAudiences))
+            msgAudiences = logMsgAudsFromAbbrevs(logAudiences);
 
-            unsigned msgClasses = MSGCLS_all;
-            const char *logClasses = logConfig->queryProp(logMsgClassesAtt);
-            if (!isEmptyString(logClasses))
-            {
-                msgClasses = logMsgClassesFromAbbrevs(logClasses);
-            }
+        const bool local = true; // Do not include remote messages from other components
+        Owned<ILogMsgFilter> filter = getCategoryLogMsgFilter(msgAudiences, msgClasses, logDetail, local);
+        theManager->changeMonitorFilter(theStderrHandler, filter);
+    }
+}
 
-            unsigned msgAudiences = MSGAUD_all;
-            const char *logAudiences = logConfig->queryProp(logMsgAudiencesAtt);
-            if (!isEmptyString(logAudiences))
-            {
-                msgAudiences = logMsgAudsFromAbbrevs(logAudiences);
-            }
+static void loggingSetupUpdate(const IPropertyTree *oldComponentConfiguration, const IPropertyTree *oldGlobalConfiguration)
+{
+    Owned<IPropertyTree> logConfig = getComponentConfigSP()->getPropTree("logging");
+    if (!logConfig)
+        return;
+    if (logConfig->getPropBool(logDisabledAtt, false))
+    {
+        OWARNLOG("JLog: Ignoring can't be disabled dynamically via an update");
+        return;
+    }
 
-            const bool local = true; // Do not include remote messages from other components
-            Owned<ILogMsgFilter> filter = getCategoryLogMsgFilter(msgAudiences, msgClasses, logDetail, local);
-            theManager->changeMonitorFilter(theStderrHandler, filter);
-        }
+    LogHandlerFormat newFormat = getConfigHandlerFormat(logConfig);
+    if (newFormat != LOGFORMAT_undefined)
+    {
+        OWARNLOG("JLog: Ignoring log format configuration change on the fly, as it is not supported in a containerized environment");
+        return;
path: system/jlib/jlog.cpp
user: login: jakesmith
body: the message implies only the format change will be ignored, but the return means all changes will be ignored.
Should either say [all] logging configure update ignore (not just format), or continue to call updateStdErrLogHandler.

I think probably should just ignore format and continue to call udpateStdErrLogHandler.
created_at: 2025-05-12T08:28:21Z
author_association: MEMBER
diff_hunk: @@ -2377,70 +2377,114 @@ static constexpr unsigned queueLenDefault = 512;
 static constexpr unsigned queueDropDefault = 0; // disabled by default
 static constexpr bool useSysLogDefault = false;
 
-void setupContainerizedLogMsgHandler()
+// returns LOGFORMAT_undefined if format has not changed
+// NB: returns LOGFORMAT_table if no format specified (i.e. this is the default)
+LogHandlerFormat getConfigHandlerFormat(const IPropertyTree *logConfig)
 {
-    Owned<IPropertyTree> logConfig = getComponentConfigSP()->getPropTree("logging");
-    if (logConfig)
+    LogHandlerFormat currentFormat = theStderrHandler->queryFormatType();
+    LogHandlerFormat newFormat{LOGFORMAT_undefined};
+    const char *newFormatString = logConfig ? logConfig->queryProp(logFormatAtt) : nullptr;
+    if (!newFormatString) // absent, defaults to "table"
+        newFormat = LOGFORMAT_table;
+    else
     {
-        if (logConfig->getPropBool(logDisabledAtt, false))
-        {
-            removeLog();
-            return;
-        }
+        if (streq(newFormatString, "xml"))
+            newFormat = LOGFORMAT_xml;
+        else if (streq(newFormatString, "json"))
+            newFormat = LOGFORMAT_json;
+        else if (streq(newFormatString, "table"))
+            newFormat = LOGFORMAT_table;
+        else
+            OWARNLOG("JLog: Invalid log format configuration detected '%s'!", newFormatString);
+    }
+    return currentFormat != newFormat ? newFormat : LOGFORMAT_undefined;
+}
 
-        if (logConfig->hasProp(logFormatAtt))
-        {
-            const char *logFormat = logConfig->queryProp(logFormatAtt);
-            bool newFormatDetected = false;
-            if (streq(logFormat, "xml"))
-            {
-                theStderrHandler = new HandleLogMsgHandlerXML(stderr, MSGFIELD_STANDARD);
-                newFormatDetected = true;
-            }
-            else if (streq(logFormat, "json"))
-            {
-                theStderrHandler = new HandleLogMsgHandlerJSON(stderr, MSGFIELD_STANDARD);
-                newFormatDetected = true;
-            }
-            else if (!streq(logFormat, "table"))
-                LOG(MCoperatorWarning, "JLog: Invalid log format configuration detected '%s'!", logFormat);
+void updateStdErrLogHandler(const IPropertyTree *logConfig)
+{
+    if (logConfig->hasProp(logFieldsAtt))
+    {
+        // Supported logging fields: TRC,SPN,AUD,CLS,DET,MID,TIM,DAT,PID,TID,NOD,JOB,USE,SES,COD,MLT,MCT,NNT,COM,QUO,PFX,ALL,STD
+        const char *logFields = logConfig->queryProp(logFieldsAtt);
+        if (!isEmptyString(logFields))
+            theStderrHandler->setMessageFields(logMsgFieldsFromAbbrevs(logFields));
+    }
 
-            if (newFormatDetected)
-                theManager->resetMonitors();
-        }
+    // Only recreate filter if at least one filter attribute configured
+    if (logConfig->hasProp(logMsgDetailAtt) || logConfig->hasProp(logMsgAudiencesAtt) || logConfig->hasProp(logMsgClassesAtt))
+    {
+        LogMsgDetail logDetail = logConfig->getPropInt(logMsgDetailAtt, DefaultDetail);
 
-        if (logConfig->hasProp(logFieldsAtt))
-        {
-            //Supported logging fields: TRC,SPN,AUD,CLS,DET,MID,TIM,DAT,PID,TID,NOD,JOB,USE,SES,COD,MLT,MCT,NNT,COM,QUO,PFX,ALL,STD
-            const char *logFields = logConfig->queryProp(logFieldsAtt);
-            if (!isEmptyString(logFields))
-                theStderrHandler->setMessageFields(logMsgFieldsFromAbbrevs(logFields));
-        }
+        unsigned msgClasses = MSGCLS_all;
+        const char *logClasses = logConfig->queryProp(logMsgClassesAtt);
+        if (!isEmptyString(logClasses))
+            msgClasses = logMsgClassesFromAbbrevs(logClasses);
 
-        //Only recreate filter if at least one filter attribute configured
-        if (logConfig->hasProp(logMsgDetailAtt) || logConfig->hasProp(logMsgAudiencesAtt) || logConfig->hasProp(logMsgClassesAtt))
-        {
-            LogMsgDetail logDetail = logConfig->getPropInt(logMsgDetailAtt, DefaultDetail);
+        unsigned msgAudiences = MSGAUD_all;
+        const char *logAudiences = logConfig->queryProp(logMsgAudiencesAtt);
+        if (!isEmptyString(logAudiences))
+            msgAudiences = logMsgAudsFromAbbrevs(logAudiences);
 
-            unsigned msgClasses = MSGCLS_all;
-            const char *logClasses = logConfig->queryProp(logMsgClassesAtt);
-            if (!isEmptyString(logClasses))
-            {
-                msgClasses = logMsgClassesFromAbbrevs(logClasses);
-            }
+        const bool local = true; // Do not include remote messages from other components
+        Owned<ILogMsgFilter> filter = getCategoryLogMsgFilter(msgAudiences, msgClasses, logDetail, local);
+        theManager->changeMonitorFilter(theStderrHandler, filter);
+    }
+}
 
-            unsigned msgAudiences = MSGAUD_all;
-            const char *logAudiences = logConfig->queryProp(logMsgAudiencesAtt);
-            if (!isEmptyString(logAudiences))
-            {
-                msgAudiences = logMsgAudsFromAbbrevs(logAudiences);
-            }
+static void loggingSetupUpdate(const IPropertyTree *oldComponentConfiguration, const IPropertyTree *oldGlobalConfiguration)
+{
+    Owned<IPropertyTree> logConfig = getComponentConfigSP()->getPropTree("logging");
+    if (!logConfig)
+        return;
+    if (logConfig->getPropBool(logDisabledAtt, false))
+    {
+        OWARNLOG("JLog: Ignoring can't be disabled dynamically via an update");
+        return;
+    }
 
-            const bool local = true; // Do not include remote messages from other components
-            Owned<ILogMsgFilter> filter = getCategoryLogMsgFilter(msgAudiences, msgClasses, logDetail, local);
-            theManager->changeMonitorFilter(theStderrHandler, filter);
-        }
+    LogHandlerFormat newFormat = getConfigHandlerFormat(logConfig);
+    if (newFormat != LOGFORMAT_undefined)
+    {
+        OWARNLOG("JLog: Ignoring log format configuration change on the fly, as it is not supported in a containerized environment");
+        return;
+    }
+    updateStdErrLogHandler(logConfig);
+}
 
+static CConfigUpdateHook configUpdateHook;
+
+// NB: it is not thread-safe to change the handler whilst other threads are logging.
+// This should only be called at startup.
+void setupContainerizedLogMsgHandler()
+{
+    Owned<IPropertyTree> logConfig = getComponentConfigSP()->getPropTree("logging");
+    if (logConfig && logConfig->getPropBool(logDisabledAtt, false))
+    {
+        removeLog();
+        return; // NB: can't be reenabled dynamically via an update at present
+    }
+    LogHandlerFormat newFormat = getConfigHandlerFormat(logConfig); // NB: theStderrHandler is initially setup in MODULE_INIT
+    if (newFormat != LOGFORMAT_undefined)
+    {
+        // NB: old theStderrHandler leaks, should be fixed by separate PR.
+        switch (newFormat)
+        {
+            case LOGFORMAT_xml:
+                theStderrHandler = new HandleLogMsgHandlerXML(stderr, MSGFIELD_STANDARD);
path: system/jlib/jlog.cpp
user: login: jakesmith
body: is there a separate existing JIRA to fix the pre-existing leak here?
(Can you link it to this JIRA if so?)
created_at: 2025-05-12T08:30:00Z
author_association: MEMBER
diff_hunk: @@ -2462,7 +2506,7 @@ void setupContainerizedLogMsgHandler()
             UseSysLogForOperatorMessages();
 
         unsigned postMortemLines = logConfig->getPropInt(capturePostMortemAtt, 0);
-        if (postMortemLines)
+        if (postMortemLines && !thePostMortemHandler)
path: system/jlib/jlog.cpp
user: login: jakesmith
body: adding && !thePostMortemHandler suggests, that it's valid to already be set.
If it were already set, then postMortemLines from config would be ignored.

I think best to remove this new check for now, but open a JIRA to support postMortemLines changing, and support it without recreating the handler, i.e. adding a method that sets it the existing handler, and manages increase/decrease/rollover carefully.

created_at: 2025-05-12T10:03:52Z
author_association: MEMBER
diff_hunk: @@ -2377,70 +2377,114 @@ static constexpr unsigned queueLenDefault = 512;
 static constexpr unsigned queueDropDefault = 0; // disabled by default
 static constexpr bool useSysLogDefault = false;
 
-void setupContainerizedLogMsgHandler()
+// returns LOGFORMAT_undefined if format has not changed
+// NB: returns LOGFORMAT_table if no format specified (i.e. this is the default)
+LogHandlerFormat getConfigHandlerFormat(const IPropertyTree *logConfig)
 {
-    Owned<IPropertyTree> logConfig = getComponentConfigSP()->getPropTree("logging");
-    if (logConfig)
+    LogHandlerFormat currentFormat = theStderrHandler->queryFormatType();
+    LogHandlerFormat newFormat{LOGFORMAT_undefined};
+    const char *newFormatString = logConfig ? logConfig->queryProp(logFormatAtt) : nullptr;
+    if (!newFormatString) // absent, defaults to "table"
+        newFormat = LOGFORMAT_table;
+    else
     {
-        if (logConfig->getPropBool(logDisabledAtt, false))
-        {
-            removeLog();
-            return;
-        }
+        if (streq(newFormatString, "xml"))
+            newFormat = LOGFORMAT_xml;
+        else if (streq(newFormatString, "json"))
+            newFormat = LOGFORMAT_json;
+        else if (streq(newFormatString, "table"))
+            newFormat = LOGFORMAT_table;
+        else
+            OWARNLOG("JLog: Invalid log format configuration detected '%s'!", newFormatString);
+    }
+    return currentFormat != newFormat ? newFormat : LOGFORMAT_undefined;
+}
 
-        if (logConfig->hasProp(logFormatAtt))
-        {
-            const char *logFormat = logConfig->queryProp(logFormatAtt);
-            bool newFormatDetected = false;
-            if (streq(logFormat, "xml"))
-            {
-                theStderrHandler = new HandleLogMsgHandlerXML(stderr, MSGFIELD_STANDARD);
-                newFormatDetected = true;
-            }
-            else if (streq(logFormat, "json"))
-            {
-                theStderrHandler = new HandleLogMsgHandlerJSON(stderr, MSGFIELD_STANDARD);
-                newFormatDetected = true;
-            }
-            else if (!streq(logFormat, "table"))
-                LOG(MCoperatorWarning, "JLog: Invalid log format configuration detected '%s'!", logFormat);
+void updateStdErrLogHandler(const IPropertyTree *logConfig)
+{
+    if (logConfig->hasProp(logFieldsAtt))
+    {
+        // Supported logging fields: TRC,SPN,AUD,CLS,DET,MID,TIM,DAT,PID,TID,NOD,JOB,USE,SES,COD,MLT,MCT,NNT,COM,QUO,PFX,ALL,STD
+        const char *logFields = logConfig->queryProp(logFieldsAtt);
+        if (!isEmptyString(logFields))
+            theStderrHandler->setMessageFields(logMsgFieldsFromAbbrevs(logFields));
+    }
 
-            if (newFormatDetected)
-                theManager->resetMonitors();
-        }
+    // Only recreate filter if at least one filter attribute configured
+    if (logConfig->hasProp(logMsgDetailAtt) || logConfig->hasProp(logMsgAudiencesAtt) || logConfig->hasProp(logMsgClassesAtt))
+    {
+        LogMsgDetail logDetail = logConfig->getPropInt(logMsgDetailAtt, DefaultDetail);
 
-        if (logConfig->hasProp(logFieldsAtt))
-        {
-            //Supported logging fields: TRC,SPN,AUD,CLS,DET,MID,TIM,DAT,PID,TID,NOD,JOB,USE,SES,COD,MLT,MCT,NNT,COM,QUO,PFX,ALL,STD
-            const char *logFields = logConfig->queryProp(logFieldsAtt);
-            if (!isEmptyString(logFields))
-                theStderrHandler->setMessageFields(logMsgFieldsFromAbbrevs(logFields));
-        }
+        unsigned msgClasses = MSGCLS_all;
+        const char *logClasses = logConfig->queryProp(logMsgClassesAtt);
+        if (!isEmptyString(logClasses))
+            msgClasses = logMsgClassesFromAbbrevs(logClasses);
 
-        //Only recreate filter if at least one filter attribute configured
-        if (logConfig->hasProp(logMsgDetailAtt) || logConfig->hasProp(logMsgAudiencesAtt) || logConfig->hasProp(logMsgClassesAtt))
-        {
-            LogMsgDetail logDetail = logConfig->getPropInt(logMsgDetailAtt, DefaultDetail);
+        unsigned msgAudiences = MSGAUD_all;
+        const char *logAudiences = logConfig->queryProp(logMsgAudiencesAtt);
+        if (!isEmptyString(logAudiences))
+            msgAudiences = logMsgAudsFromAbbrevs(logAudiences);
 
-            unsigned msgClasses = MSGCLS_all;
-            const char *logClasses = logConfig->queryProp(logMsgClassesAtt);
-            if (!isEmptyString(logClasses))
-            {
-                msgClasses = logMsgClassesFromAbbrevs(logClasses);
-            }
+        const bool local = true; // Do not include remote messages from other components
+        Owned<ILogMsgFilter> filter = getCategoryLogMsgFilter(msgAudiences, msgClasses, logDetail, local);
+        theManager->changeMonitorFilter(theStderrHandler, filter);
+    }
+}
 
-            unsigned msgAudiences = MSGAUD_all;
-            const char *logAudiences = logConfig->queryProp(logMsgAudiencesAtt);
-            if (!isEmptyString(logAudiences))
-            {
-                msgAudiences = logMsgAudsFromAbbrevs(logAudiences);
-            }
+static void loggingSetupUpdate(const IPropertyTree *oldComponentConfiguration, const IPropertyTree *oldGlobalConfiguration)
+{
+    Owned<IPropertyTree> logConfig = getComponentConfigSP()->getPropTree("logging");
+    if (!logConfig)
+        return;
+    if (logConfig->getPropBool(logDisabledAtt, false))
+    {
+        OWARNLOG("JLog: Ignoring can't be disabled dynamically via an update");
+        return;
+    }
 
-            const bool local = true; // Do not include remote messages from other components
-            Owned<ILogMsgFilter> filter = getCategoryLogMsgFilter(msgAudiences, msgClasses, logDetail, local);
-            theManager->changeMonitorFilter(theStderrHandler, filter);
-        }
+    LogHandlerFormat newFormat = getConfigHandlerFormat(logConfig);
+    if (newFormat != LOGFORMAT_undefined)
+    {
+        OWARNLOG("JLog: Ignoring log format configuration change on the fly, as it is not supported in a containerized environment");
+        return;
+    }
+    updateStdErrLogHandler(logConfig);
+}
 
+static CConfigUpdateHook configUpdateHook;
+
+// NB: it is not thread-safe to change the handler whilst other threads are logging.
+// This should only be called at startup.
+void setupContainerizedLogMsgHandler()
+{
+    Owned<IPropertyTree> logConfig = getComponentConfigSP()->getPropTree("logging");
+    if (logConfig && logConfig->getPropBool(logDisabledAtt, false))
+    {
+        removeLog();
+        return; // NB: can't be reenabled dynamically via an update at present
+    }
+    LogHandlerFormat newFormat = getConfigHandlerFormat(logConfig); // NB: theStderrHandler is initially setup in MODULE_INIT
+    if (newFormat != LOGFORMAT_undefined)
+    {
+        // NB: old theStderrHandler leaks, should be fixed by separate PR.
+        switch (newFormat)
+        {
+            case LOGFORMAT_xml:
+                theStderrHandler = new HandleLogMsgHandlerXML(stderr, MSGFIELD_STANDARD);
path: system/jlib/jlog.cpp
user: login: streeterd
body: Fixed in 9bf2dbe11a8d767b528ba26d7cb714b495ba5c13
New JIRA https://hpccsystems.atlassian.net/browse/HPCC-34101
created_at: 2025-05-14T08:06:15Z
author_association: CONTRIBUTOR
diff_hunk: @@ -2462,7 +2506,7 @@ void setupContainerizedLogMsgHandler()
             UseSysLogForOperatorMessages();
 
         unsigned postMortemLines = logConfig->getPropInt(capturePostMortemAtt, 0);
-        if (postMortemLines)
+        if (postMortemLines && !thePostMortemHandler)
path: system/jlib/jlog.cpp
user: login: streeterd
body: Fixed in 9bf2dbe11a8d767b528ba26d7cb714b495ba5c13
New JIRA https://hpccsystems.atlassian.net/browse/HPCC-34101
created_at: 2025-05-14T08:06:41Z
author_association: CONTRIBUTOR
diff_hunk: @@ -2377,70 +2377,114 @@ static constexpr unsigned queueLenDefault = 512;
 static constexpr unsigned queueDropDefault = 0; // disabled by default
 static constexpr bool useSysLogDefault = false;
 
-void setupContainerizedLogMsgHandler()
+// returns LOGFORMAT_undefined if format has not changed
+// NB: returns LOGFORMAT_table if no format specified (i.e. this is the default)
+LogHandlerFormat getConfigHandlerFormat(const IPropertyTree *logConfig)
 {
-    Owned<IPropertyTree> logConfig = getComponentConfigSP()->getPropTree("logging");
-    if (logConfig)
+    LogHandlerFormat currentFormat = theStderrHandler->queryFormatType();
+    LogHandlerFormat newFormat{LOGFORMAT_undefined};
+    const char *newFormatString = logConfig ? logConfig->queryProp(logFormatAtt) : nullptr;
+    if (!newFormatString) // absent, defaults to "table"
+        newFormat = LOGFORMAT_table;
+    else
     {
-        if (logConfig->getPropBool(logDisabledAtt, false))
-        {
-            removeLog();
-            return;
-        }
+        if (streq(newFormatString, "xml"))
+            newFormat = LOGFORMAT_xml;
+        else if (streq(newFormatString, "json"))
+            newFormat = LOGFORMAT_json;
+        else if (streq(newFormatString, "table"))
+            newFormat = LOGFORMAT_table;
+        else
+            OWARNLOG("JLog: Invalid log format configuration detected '%s'!", newFormatString);
+    }
+    return currentFormat != newFormat ? newFormat : LOGFORMAT_undefined;
+}
 
-        if (logConfig->hasProp(logFormatAtt))
-        {
-            const char *logFormat = logConfig->queryProp(logFormatAtt);
-            bool newFormatDetected = false;
-            if (streq(logFormat, "xml"))
-            {
-                theStderrHandler = new HandleLogMsgHandlerXML(stderr, MSGFIELD_STANDARD);
-                newFormatDetected = true;
-            }
-            else if (streq(logFormat, "json"))
-            {
-                theStderrHandler = new HandleLogMsgHandlerJSON(stderr, MSGFIELD_STANDARD);
-                newFormatDetected = true;
-            }
-            else if (!streq(logFormat, "table"))
-                LOG(MCoperatorWarning, "JLog: Invalid log format configuration detected '%s'!", logFormat);
+void updateStdErrLogHandler(const IPropertyTree *logConfig)
+{
+    if (logConfig->hasProp(logFieldsAtt))
+    {
+        // Supported logging fields: TRC,SPN,AUD,CLS,DET,MID,TIM,DAT,PID,TID,NOD,JOB,USE,SES,COD,MLT,MCT,NNT,COM,QUO,PFX,ALL,STD
+        const char *logFields = logConfig->queryProp(logFieldsAtt);
+        if (!isEmptyString(logFields))
+            theStderrHandler->setMessageFields(logMsgFieldsFromAbbrevs(logFields));
+    }
 
-            if (newFormatDetected)
-                theManager->resetMonitors();
-        }
+    // Only recreate filter if at least one filter attribute configured
+    if (logConfig->hasProp(logMsgDetailAtt) || logConfig->hasProp(logMsgAudiencesAtt) || logConfig->hasProp(logMsgClassesAtt))
+    {
+        LogMsgDetail logDetail = logConfig->getPropInt(logMsgDetailAtt, DefaultDetail);
 
-        if (logConfig->hasProp(logFieldsAtt))
-        {
-            //Supported logging fields: TRC,SPN,AUD,CLS,DET,MID,TIM,DAT,PID,TID,NOD,JOB,USE,SES,COD,MLT,MCT,NNT,COM,QUO,PFX,ALL,STD
-            const char *logFields = logConfig->queryProp(logFieldsAtt);
-            if (!isEmptyString(logFields))
-                theStderrHandler->setMessageFields(logMsgFieldsFromAbbrevs(logFields));
-        }
+        unsigned msgClasses = MSGCLS_all;
+        const char *logClasses = logConfig->queryProp(logMsgClassesAtt);
+        if (!isEmptyString(logClasses))
+            msgClasses = logMsgClassesFromAbbrevs(logClasses);
 
-        //Only recreate filter if at least one filter attribute configured
-        if (logConfig->hasProp(logMsgDetailAtt) || logConfig->hasProp(logMsgAudiencesAtt) || logConfig->hasProp(logMsgClassesAtt))
-        {
-            LogMsgDetail logDetail = logConfig->getPropInt(logMsgDetailAtt, DefaultDetail);
+        unsigned msgAudiences = MSGAUD_all;
+        const char *logAudiences = logConfig->queryProp(logMsgAudiencesAtt);
+        if (!isEmptyString(logAudiences))
+            msgAudiences = logMsgAudsFromAbbrevs(logAudiences);
 
-            unsigned msgClasses = MSGCLS_all;
-            const char *logClasses = logConfig->queryProp(logMsgClassesAtt);
-            if (!isEmptyString(logClasses))
-            {
-                msgClasses = logMsgClassesFromAbbrevs(logClasses);
-            }
+        const bool local = true; // Do not include remote messages from other components
+        Owned<ILogMsgFilter> filter = getCategoryLogMsgFilter(msgAudiences, msgClasses, logDetail, local);
+        theManager->changeMonitorFilter(theStderrHandler, filter);
+    }
+}
 
-            unsigned msgAudiences = MSGAUD_all;
-            const char *logAudiences = logConfig->queryProp(logMsgAudiencesAtt);
-            if (!isEmptyString(logAudiences))
-            {
-                msgAudiences = logMsgAudsFromAbbrevs(logAudiences);
-            }
+static void loggingSetupUpdate(const IPropertyTree *oldComponentConfiguration, const IPropertyTree *oldGlobalConfiguration)
+{
+    Owned<IPropertyTree> logConfig = getComponentConfigSP()->getPropTree("logging");
+    if (!logConfig)
+        return;
+    if (logConfig->getPropBool(logDisabledAtt, false))
+    {
+        OWARNLOG("JLog: Ignoring can't be disabled dynamically via an update");
+        return;
+    }
 
-            const bool local = true; // Do not include remote messages from other components
-            Owned<ILogMsgFilter> filter = getCategoryLogMsgFilter(msgAudiences, msgClasses, logDetail, local);
-            theManager->changeMonitorFilter(theStderrHandler, filter);
-        }
+    LogHandlerFormat newFormat = getConfigHandlerFormat(logConfig);
+    if (newFormat != LOGFORMAT_undefined)
+    {
+        OWARNLOG("JLog: Ignoring log format configuration change on the fly, as it is not supported in a containerized environment");
+        return;
path: system/jlib/jlog.cpp
user: login: jakesmith
body: I meant still issue the warning that ignoring format change, but continue...
created_at: 2025-05-15T13:02:49Z
author_association: MEMBER
diff_hunk: @@ -2377,70 +2377,114 @@ static constexpr unsigned queueLenDefault = 512;
 static constexpr unsigned queueDropDefault = 0; // disabled by default
 static constexpr bool useSysLogDefault = false;
 
-void setupContainerizedLogMsgHandler()
+// returns LOGFORMAT_undefined if format has not changed
+// NB: returns LOGFORMAT_table if no format specified (i.e. this is the default)
+LogHandlerFormat getConfigHandlerFormat(const IPropertyTree *logConfig)
 {
-    Owned<IPropertyTree> logConfig = getComponentConfigSP()->getPropTree("logging");
-    if (logConfig)
+    LogHandlerFormat currentFormat = theStderrHandler->queryFormatType();
+    LogHandlerFormat newFormat{LOGFORMAT_undefined};
+    const char *newFormatString = logConfig ? logConfig->queryProp(logFormatAtt) : nullptr;
+    if (!newFormatString) // absent, defaults to "table"
+        newFormat = LOGFORMAT_table;
+    else
     {
-        if (logConfig->getPropBool(logDisabledAtt, false))
-        {
-            removeLog();
-            return;
-        }
+        if (streq(newFormatString, "xml"))
+            newFormat = LOGFORMAT_xml;
+        else if (streq(newFormatString, "json"))
+            newFormat = LOGFORMAT_json;
+        else if (streq(newFormatString, "table"))
+            newFormat = LOGFORMAT_table;
+        else
+            OWARNLOG("JLog: Invalid log format configuration detected '%s'!", newFormatString);
+    }
+    return currentFormat != newFormat ? newFormat : LOGFORMAT_undefined;
+}
 
-        if (logConfig->hasProp(logFormatAtt))
-        {
-            const char *logFormat = logConfig->queryProp(logFormatAtt);
-            bool newFormatDetected = false;
-            if (streq(logFormat, "xml"))
-            {
-                theStderrHandler = new HandleLogMsgHandlerXML(stderr, MSGFIELD_STANDARD);
-                newFormatDetected = true;
-            }
-            else if (streq(logFormat, "json"))
-            {
-                theStderrHandler = new HandleLogMsgHandlerJSON(stderr, MSGFIELD_STANDARD);
-                newFormatDetected = true;
-            }
-            else if (!streq(logFormat, "table"))
-                LOG(MCoperatorWarning, "JLog: Invalid log format configuration detected '%s'!", logFormat);
+void updateStdErrLogHandler(const IPropertyTree *logConfig)
+{
+    if (logConfig->hasProp(logFieldsAtt))
+    {
+        // Supported logging fields: TRC,SPN,AUD,CLS,DET,MID,TIM,DAT,PID,TID,NOD,JOB,USE,SES,COD,MLT,MCT,NNT,COM,QUO,PFX,ALL,STD
+        const char *logFields = logConfig->queryProp(logFieldsAtt);
+        if (!isEmptyString(logFields))
+            theStderrHandler->setMessageFields(logMsgFieldsFromAbbrevs(logFields));
+    }
 
-            if (newFormatDetected)
-                theManager->resetMonitors();
-        }
+    // Only recreate filter if at least one filter attribute configured
+    if (logConfig->hasProp(logMsgDetailAtt) || logConfig->hasProp(logMsgAudiencesAtt) || logConfig->hasProp(logMsgClassesAtt))
+    {
+        LogMsgDetail logDetail = logConfig->getPropInt(logMsgDetailAtt, DefaultDetail);
 
-        if (logConfig->hasProp(logFieldsAtt))
-        {
-            //Supported logging fields: TRC,SPN,AUD,CLS,DET,MID,TIM,DAT,PID,TID,NOD,JOB,USE,SES,COD,MLT,MCT,NNT,COM,QUO,PFX,ALL,STD
-            const char *logFields = logConfig->queryProp(logFieldsAtt);
-            if (!isEmptyString(logFields))
-                theStderrHandler->setMessageFields(logMsgFieldsFromAbbrevs(logFields));
-        }
+        unsigned msgClasses = MSGCLS_all;
+        const char *logClasses = logConfig->queryProp(logMsgClassesAtt);
+        if (!isEmptyString(logClasses))
+            msgClasses = logMsgClassesFromAbbrevs(logClasses);
 
-        //Only recreate filter if at least one filter attribute configured
-        if (logConfig->hasProp(logMsgDetailAtt) || logConfig->hasProp(logMsgAudiencesAtt) || logConfig->hasProp(logMsgClassesAtt))
-        {
-            LogMsgDetail logDetail = logConfig->getPropInt(logMsgDetailAtt, DefaultDetail);
+        unsigned msgAudiences = MSGAUD_all;
+        const char *logAudiences = logConfig->queryProp(logMsgAudiencesAtt);
+        if (!isEmptyString(logAudiences))
+            msgAudiences = logMsgAudsFromAbbrevs(logAudiences);
 
-            unsigned msgClasses = MSGCLS_all;
-            const char *logClasses = logConfig->queryProp(logMsgClassesAtt);
-            if (!isEmptyString(logClasses))
-            {
-                msgClasses = logMsgClassesFromAbbrevs(logClasses);
-            }
+        const bool local = true; // Do not include remote messages from other components
+        Owned<ILogMsgFilter> filter = getCategoryLogMsgFilter(msgAudiences, msgClasses, logDetail, local);
+        theManager->changeMonitorFilter(theStderrHandler, filter);
+    }
+}
 
-            unsigned msgAudiences = MSGAUD_all;
-            const char *logAudiences = logConfig->queryProp(logMsgAudiencesAtt);
-            if (!isEmptyString(logAudiences))
-            {
-                msgAudiences = logMsgAudsFromAbbrevs(logAudiences);
-            }
+static void loggingSetupUpdate(const IPropertyTree *oldComponentConfiguration, const IPropertyTree *oldGlobalConfiguration)
+{
+    Owned<IPropertyTree> logConfig = getComponentConfigSP()->getPropTree("logging");
+    if (!logConfig)
+        return;
+    if (logConfig->getPropBool(logDisabledAtt, false))
+    {
+        OWARNLOG("JLog: Ignoring can't be disabled dynamically via an update");
+        return;
+    }
 
-            const bool local = true; // Do not include remote messages from other components
-            Owned<ILogMsgFilter> filter = getCategoryLogMsgFilter(msgAudiences, msgClasses, logDetail, local);
-            theManager->changeMonitorFilter(theStderrHandler, filter);
-        }
+    LogHandlerFormat newFormat = getConfigHandlerFormat(logConfig);
+    if (newFormat != LOGFORMAT_undefined)
+    {
+        OWARNLOG("JLog: Ignoring log format configuration change on the fly, as it is not supported in a containerized environment");
+        return;
path: system/jlib/jlog.cpp
user: login: streeterd
body: Added code back in to warn if log format changes and carry on in 80f0769260193977fa571f4a2d9018fd8606d63e
created_at: 2025-05-15T13:58:37Z
author_association: CONTRIBUTOR
diff_hunk: @@ -2442,6 +2442,12 @@ static void loggingSetupUpdate(const IPropertyTree *oldComponentConfiguration, c
         return;
     }
 
+    LogHandlerFormat newFormat = getConfigHandlerFormat(logConfig);
+    if (newFormat != LOGFORMAT_undefined)
+    {
path: system/jlib/jlog.cpp
user: login: jakesmith
body: trivial/style/don't change: normally we avoid { } for single line.
created_at: 2025-05-15T15:03:38Z
author_association: MEMBER
diff_hunk: @@ -778,6 +778,10 @@ class DaliMsgLoggerHandler : public CSimpleInterfaceOf<ILogMsgHandler>
     {
         return 0;
     }
+    virtual LogHandlerFormat queryFormatType() const override
+    {
+         return LOGFORMAT_undefined;
+    };
path: dali/base/sysinfologger.cpp
user: login: ghalliday
body: trivial: extraneous semicolon
created_at: 2025-05-21T10:45:23Z
author_association: MEMBER
diff_hunk: @@ -4311,22 +4311,23 @@ class UserMetricMsgHandler : public CInterface, implements ILogMsgHandler, imple
     }
 
     // interface ILogMsgHandler
-    virtual void handleMessage(const LogMsg & msg __attribute__((unused))) { counter++; }
-    virtual bool needsPrep() const { return false; }
-    virtual void prep() {}
-    virtual unsigned queryMessageFields() const { return MSGFIELD_detail; }
-    virtual void setMessageFields(unsigned _fields __attribute__((unused)) = MSGFIELD_all) {}
-    virtual void addToPTree(IPropertyTree * parent __attribute__((unused))) const {}
-    virtual int flush() { return 0; }
-    virtual bool getLogName(StringBuffer &name __attribute__((unused))) const { return false; }
-    virtual offset_t getLogPosition(StringBuffer &logFileName __attribute__((unused))) const { return 0; };
+    virtual void handleMessage(const LogMsg & msg __attribute__((unused))) override { counter++; }
+    virtual bool needsPrep() const override { return false; }
+    virtual void prep() override {}
+    virtual unsigned queryMessageFields() const override { return MSGFIELD_detail; }
+    virtual void setMessageFields(unsigned _fields __attribute__((unused)) = MSGFIELD_all) override {}
+    virtual void addToPTree(IPropertyTree * parent __attribute__((unused))) const override {}
+    virtual int flush() override { return 0; }
+    virtual bool getLogName(StringBuffer &name __attribute__((unused))) const override { return false; }
+    virtual offset_t getLogPosition(StringBuffer &logFileName __attribute__((unused))) const override { return 0; };
+    virtual LogHandlerFormat queryFormatType() const override { return LOGFORMAT_undefined; };
path: system/jlib/jdebug.cpp
user: login: ghalliday
body: similar extra ;, and on line above.
created_at: 2025-05-21T10:46:48Z
author_association: MEMBER
diff_hunk: @@ -691,6 +691,7 @@ interface jlib_decl ILogMsgFilter : public IInterface
 
 typedef enum
 {
+    LOGFORMAT_undefined,
path: system/jlib/jlog.hpp
user: login: ghalliday
body: I think this ok, but I had  to double check none of these values were serialized - because this changes all the values.

created_at: 2025-05-21T10:54:58Z
author_association: MEMBER

PR comments:
user: login: github-actions[bot]
created_at: 2025-04-24T07:42:20Z
body: Jira Issue: https://hpccsystems.atlassian.net//browse/HPCC-33885

Jirabot Action Result:
Workflow Transition To: Merge Pending
Additional PR: https://github.com/hpcc-systems/HPCC-Platform/pull/19783

author_association: NONE
user: login: streeterd
created_at: 2025-04-24T12:26:10Z
body: > @streeterd - haven't looked beyond the unintended release of a change to plugins/CMakeLists.txt ..
> 
> Please self-review in github before tagging, to weed out issues like this.

Removed in 6522db7214319b44196dfd1bc8cb63c506a6ff8e
author_association: CONTRIBUTOR
user: login: streeterd
created_at: 2025-04-29T08:13:53Z
body: The failure of the K8s Regression Suite is being resolved under PR: https://github.com/hpcc-systems/hpcc4j/pull/815 
author_association: CONTRIBUTOR
user: login: streeterd
created_at: 2025-05-15T15:35:00Z
body: > @streeterd - looks good.
> 
> Please squash.

@jakesmith squashed
author_association: CONTRIBUTOR
user: login: jakesmith
created_at: 2025-05-16T14:50:47Z
body: @ghalliday - please sanity check.
author_association: MEMBER
user: login: ghalliday
created_at: 2025-05-21T10:57:23Z
body: @streeterd needs rebasing before it can be merged.
author_association: MEMBER
user: login: streeterd
created_at: 2025-05-21T12:16:25Z
body: > @streeterd needs rebasing before it can be merged.

@ghalliday rebased on candidate-9.12.x
author_association: CONTRIBUTOR
user: login: github-actions[bot]
created_at: 2025-05-22T15:57:26Z
body: Jirabot Action Result:
Added fix version: 9.12.6
Workflow Transition: 'Resolve issue'

author_association: NONE