Skip to content

Conversation

@NicoPiel
Copy link
Collaborator

@NicoPiel NicoPiel commented Nov 19, 2025

Changelog:

  1. Server:

    • Modified ServerLogItem to include channelName, updated constructor, getters/setters.
    • Modified ServerLogProvider to pass channelName to ServerLogItem.
    • Modified ArrayAppender to extract channelName and channelId from Log4j ThreadContext.
    • Modified JavaScriptTask to populate ThreadContext with channel info.
  2. Donkey Engine:

    • Modified Channel to populate ThreadContext in dispatch and source queue threads.
    • Modified DestinationChain to populate ThreadContext using channel info.
    • Modified DestinationConnector to populate ThreadContext in queue threads.
  3. Client:

    • Modified ServerLogPanel to add "Channel" column to the UI table.

@NicoPiel NicoPiel force-pushed the feature/logging-improvements branch from 9fce32c to 3c94136 Compare November 20, 2025 14:13
@NicoPiel
Copy link
Collaborator Author

@mgaffigan Thoughts on the map implementation? Did you have this in mind when you suggested a map?

Signed-off-by: Nico Piel <nicopiel@mailbox.org>
Signed-off-by: Nico Piel <nico.piel@hotmail.de>
Signed-off-by: Nico Piel <nicopiel@mailbox.org>
Signed-off-by: Nico Piel <nico.piel@hotmail.de>
Signed-off-by: Nico Piel <nicopiel@mailbox.org>
Signed-off-by: Nico Piel <nico.piel@hotmail.de>
Signed-off-by: Nico Piel <nicopiel@mailbox.org>
Signed-off-by: Nico Piel <nico.piel@hotmail.de>
Signed-off-by: Nico Piel <nicopiel@mailbox.org>
Signed-off-by: Nico Piel <nico.piel@hotmail.de>
Signed-off-by: Nico Piel <nico.piel@hotmail.de>
Signed-off-by: Nico Piel <nico.piel@hotmail.de>
Signed-off-by: Nico Piel <nico.piel@hotmail.de>
Signed-off-by: Nico Piel <nico.piel@hotmail.de>
Signed-off-by: Nico Piel <nico.piel@hotmail.de>
Changes the ArrayAppender to construct a ServerLogItem directly
instead of passing individual log properties.

The ServerLogProvider now accepts a ServerLogItem, extracts the log
properties, adds the server ID and log ID, and then passes the log
item to the log controller.

This change simplifies the code and makes it more efficient by
reducing the number of parameters passed and centralizing the
creation of the ServerLogItem.

Signed-off-by: Nico Piel <nico.piel@hotmail.de>
Ensures that the ServerLogItem properties are initialized with default values when retrieving them from the context.
This prevents null pointer exceptions or unexpected behavior when these properties are not explicitly set in the context.

Signed-off-by: Nico Piel <nico.piel@hotmail.de>
@mgaffigan mgaffigan force-pushed the feature/logging-improvements branch from 2482499 to 53a85d9 Compare November 24, 2025 19:19
Signed-off-by: Nico Piel <nico.piel@hotmail.de>
@NicoPiel NicoPiel force-pushed the feature/logging-improvements branch from a418b01 to 278d2b3 Compare November 24, 2025 20:08
Signed-off-by: Nico Piel <nico.piel@hotmail.de>
@NicoPiel NicoPiel force-pushed the feature/logging-improvements branch from 469b1c1 to b1623c9 Compare November 24, 2025 20:19
Signed-off-by: Nico Piel <nico.piel@hotmail.de>
Signed-off-by: Nico Piel <nico.piel@hotmail.de>
Signed-off-by: Nico Piel <nico.piel@hotmail.de>
Signed-off-by: Nico Piel <nico.piel@hotmail.de>
Signed-off-by: Nico Piel <nico.piel@hotmail.de>
Signed-off-by: Nico Piel <nico.piel@hotmail.de>
Signed-off-by: Nico Piel <nico.piel@hotmail.de>
Signed-off-by: Nico Piel <nico.piel@hotmail.de>
Signed-off-by: Nico Piel <nico.piel@hotmail.de>
@mgaffigan mgaffigan changed the title Logging improvements Include Channel Name in server log Nov 26, 2025
@pacmano1 pacmano1 requested a review from Copilot December 9, 2025 22:22
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds channel name information to server logs by propagating channel context through Log4j's ThreadContext mechanism. The implementation spans three main components: server-side logging infrastructure, the Donkey engine threading model, and the client UI.

Key changes:

  • Modified ServerLogItem to store channel information in an attributes map with convenient getter/setter methods
  • Updated all channel-related thread entry points to populate ThreadContext with channel ID and name using CloseableThreadContext
  • Enhanced the server log UI table to display a new "Channel" column

Reviewed changes

Copilot reviewed 9 out of 9 changed files in this pull request and generated 12 comments.

Show a summary per file
File Description
server/src/com/mirth/connect/server/util/javascript/JavaScriptTask.java Added ThreadContext population with channel info in JavaScript task execution
server/src/com/mirth/connect/plugins/serverlog/ServerLogProvider.java Updated method signature to accept attributes map for channel information
server/src/com/mirth/connect/plugins/serverlog/ServerLogItem.java Added attributes map to store channel data with dedicated getter/setter methods
server/src/com/mirth/connect/plugins/serverlog/ArrayAppender.java Modified to extract context data from Log4j events and pass to provider
donkey/src/main/java/com/mirth/connect/donkey/server/channel/DestinationConnector.java Wrapped queue thread execution with ThreadContext for channel info
donkey/src/main/java/com/mirth/connect/donkey/server/channel/DestinationChainProvider.java Added channelName field with getter/setter
donkey/src/main/java/com/mirth/connect/donkey/server/channel/DestinationChain.java Added ThreadContext population in chain execution threads
donkey/src/main/java/com/mirth/connect/donkey/server/channel/Channel.java Updated to set channel name on chain providers and populate ThreadContext in dispatch/queue threads
client/src/com/mirth/connect/plugins/serverlog/ServerLogPanel.java Added "Channel" column to server log table and adjusted column indices

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +1942 to +1943
.put("channelId", channelId)
.put("channelName", name)
Copy link

Copilot AI Dec 9, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When channelId or name are null (which can happen in tests or partially initialized channels), passing null values to CloseableThreadContext.put() may cause issues. Consider adding null checks or using StringUtils.defaultString() to provide empty strings instead of null values.

Example:

try (CloseableThreadContext.Instance ctc = CloseableThreadContext
        .put("channelId", StringUtils.defaultString(channelId))
        .put("channelName", StringUtils.defaultString(name))
) {
Suggested change
.put("channelId", channelId)
.put("channelName", name)
.put("channelId", StringUtils.defaultString(channelId))
.put("channelName", StringUtils.defaultString(name))

Copilot uses AI. Check for mistakes.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If null is unacceptable (refer to Log4j docs), then the call to put should be omitted. Empty string does not seem semantically better than null. "fear" of nulls is foolish - equivalent to adding try-catch all over the place. Either null is valid or it is not.

// Add channel info to ThreadContext
try (CloseableThreadContext.Instance ctc = CloseableThreadContext
.put("channelId", getChannelId())
.put("channelName", channel.getName())
Copy link

Copilot AI Dec 9, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When channel.getName() returns null (which can happen in tests or partially initialized channels), passing null values to CloseableThreadContext.put() may cause issues. Consider adding null checks or using StringUtils.defaultString() to provide empty strings instead of null values.

Example:

try (CloseableThreadContext.Instance ctc = CloseableThreadContext
        .put("channelId", StringUtils.defaultString(getChannelId()))
        .put("channelName", StringUtils.defaultString(channel.getName()))
) {
Suggested change
.put("channelName", channel.getName())
.put("channelName", StringUtils.defaultString(channel.getName()))

Copilot uses AI. Check for mistakes.
Comment on lines +151 to +153
public void setChannelId(String channelId) {
this.attributes.put("channelId", channelId);
}
Copy link

Copilot AI Dec 9, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The setChannelId method doesn't check if attributes is null before calling put(). If setAttributes(null) was called previously, or if the attributes map becomes null through some other means, this will throw a NullPointerException. The setAttributes method ensures the map is never null, but adding a null check here would make this method more defensive.

Example:

public void setChannelId(String channelId) {
    if (this.attributes != null) {
        this.attributes.put("channelId", channelId);
    }
}

Copilot uses AI. Check for mistakes.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Attributes should never be null. If there's no way for it to be null, then this is bad advice.


@JsonIgnore
public void setChannelName(String channelName) {
this.attributes.put("channelName", channelName);
Copy link

Copilot AI Dec 9, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The setChannelName method doesn't check if attributes is null before calling put(). If setAttributes(null) was called previously, or if the attributes map becomes null through some other means, this will throw a NullPointerException. The setAttributes method ensures the map is never null, but adding a null check here would make this method more defensive.

Example:

public void setChannelName(String channelName) {
    if (this.attributes != null) {
        this.attributes.put("channelName", channelName);
    }
}
Suggested change
this.attributes.put("channelName", channelName);
if (this.attributes != null) {
this.attributes.put("channelName", channelName);
}

Copilot uses AI. Check for mistakes.
.put("channelName", channelName)
) {
Thread.currentThread().setName(threadName + " < " + originalThreadName);

Copy link

Copilot AI Dec 9, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Trailing whitespace on this line should be removed.

Suggested change

Copilot uses AI. Check for mistakes.
Comment on lines +1263 to +1264
.put("channelId", channelId)
.put("channelName", name)
Copy link

Copilot AI Dec 9, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When channelId or name are null (which can happen in tests or partially initialized channels), passing null values to CloseableThreadContext.put() may cause issues. Consider adding null checks or using StringUtils.defaultString() to provide empty strings instead of null values.

Example:

try (CloseableThreadContext.Instance ctc = CloseableThreadContext
        .put("channelId", StringUtils.defaultString(channelId))
        .put("channelName", StringUtils.defaultString(name))
) {
Suggested change
.put("channelId", channelId)
.put("channelName", name)
.put("channelId", StringUtils.defaultString(channelId))
.put("channelName", StringUtils.defaultString(name))

Copilot uses AI. Check for mistakes.
} else {
connectorMessage.setStatus(Status.ERROR);
connectorMessage.setProcessingError("Queued message has not yet been transformed, and Include Filter/Transformer is currently disabled.");
connectorMessage.setProcessingError("Mismatched connector properties detected in queued message. The connector type may have changed since the message was queued.\nFOUND: " + serializedPropertiesClass.getSimpleName() + "\nEXPECTED: " + connectorPropertiesClass.getSimpleName());
Copy link

Copilot AI Dec 9, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Variable serializedPropertiesClass may be null at this access because of this assignment.

Suggested change
connectorMessage.setProcessingError("Mismatched connector properties detected in queued message. The connector type may have changed since the message was queued.\nFOUND: " + serializedPropertiesClass.getSimpleName() + "\nEXPECTED: " + connectorPropertiesClass.getSimpleName());
connectorMessage.setProcessingError("Mismatched connector properties detected in queued message. The connector type may have changed since the message was queued.\nFOUND: " + (serializedPropertiesClass != null ? serializedPropertiesClass.getSimpleName() : "null") + "\nEXPECTED: " + connectorPropertiesClass.getSimpleName());

Copilot uses AI. Check for mistakes.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No changes were made to this line of code. If there's an issue, it's pre-existing and should be fixed in an unrelated PR.

}
}
} catch (RuntimeException e) {
logger.error("Error processing queued " + (connectorMessage != null ? connectorMessage.toString() : "message (null)") + " for channel " + channel.getName() + " (" + channel.getChannelId() + ") on destination " + destinationName + ". This error is expected if the message was manually removed from the queue.", e);
Copy link

Copilot AI Dec 9, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This check is useless. connectorMessage cannot be null at this check, since it is guarded by ... != ....

Suggested change
logger.error("Error processing queued " + (connectorMessage != null ? connectorMessage.toString() : "message (null)") + " for channel " + channel.getName() + " (" + channel.getChannelId() + ") on destination " + destinationName + ". This error is expected if the message was manually removed from the queue.", e);
logger.error("Error processing queued " + connectorMessage.toString() + " for channel " + channel.getName() + " (" + channel.getChannelId() + ") on destination " + destinationName + ". This error is expected if the message was manually removed from the queue.", e);

Copilot uses AI. Check for mistakes.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not changed - preexisting.

return;
} catch (Throwable t) {
// Send a different error message to the server log, but still invalidate the queue buffer
logger.error("Error processing queued " + (connectorMessage != null ? connectorMessage.toString() : "message (null)") + " for channel " + channel.getName() + " (" + channel.getChannelId() + ") on destination " + destinationName + ".", t);
Copy link

Copilot AI Dec 9, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This check is useless. connectorMessage cannot be null at this check, since it is guarded by ... != ....

Suggested change
logger.error("Error processing queued " + (connectorMessage != null ? connectorMessage.toString() : "message (null)") + " for channel " + channel.getName() + " (" + channel.getChannelId() + ") on destination " + destinationName + ".", t);
logger.error("Error processing queued " + connectorMessage.toString() + " for channel " + channel.getName() + " (" + channel.getChannelId() + ") on destination " + destinationName + ".", t);

Copilot uses AI. Check for mistakes.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not changed. Preexisting.

@tonygermano tonygermano added the enhancement New feature or request label Dec 12, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants