Skip to content

Commit fcdef5f

Browse files
cwangg897artembilan
authored andcommitted
GH-4012: Fix ContainerProperties & ConsumerProperties toString() for StringBuilder
Fixes: #4012 * Improve toString() performance with StringBuilder This commit improves the performance of toString() methods in ContainerProperties and ConsumerProperties classes by replacing string concatenations with StringBuilder usage. Motivation: The ContainerProperties.toString() method is frequently used in Spring Boot applications for debugging Kafka container configuration issues. With 40+ properties being concatenated using the + operator, the original implementation created excessive temporary String objects, leading to: - Memory allocation overhead - Performance degradation in logging-heavy scenarios - Increased GC pressure Changes: ContainerProperties: - Replaced string concatenation with StringBuilder in toString() method - Added helper methods for consistent formatting: - appendProperty(): for regular non-null properties - appendEnabledProperty(): for properties with "enabled/not enabled" formatting - Improved organization with logical property grouping and descriptive comments - Enhanced maintainability through reduced code duplication ConsumerProperties: - Updated renderProperties() method to use StringBuilder instead of string concatenations - Modified renderTopics() to accept StringBuilder parameter for efficiency - Applied ContainerProperties improvements Performance Impact: - Reduced memory allocations during toString() operations - Improved performance in logging-heavy scenarios - Better scalability for applications with frequent container configuration logging Backward Compatibility: - No breaking changes - Output format unchanged - All existing functionality preserved * Skip null fields in toString() output Omit properties with null values from the toString() output by updating the appendProperty method. This improves readability and avoids clutter in logs by excluding unset configuration fields. * Apply Spring code style using spring-framework.xml Reformatted ContainerProperties.java to comply with the project's Spring-specific IntelliJ code style configuration. This ensures consistency and passes local style checks. * Remove trailing whitespace to fix checkstyle violations Removed trailing spaces from lines 517, 533, 535, 542, and 546 in ConsumerProperties.java. This change ensures that `./gradlew check` passes successfully, addressing the reviewer's request for code style compliance. Signed-off-by: Choi Wang Gyu <[email protected]> # Conflicts: # spring-kafka/src/main/java/org/springframework/kafka/listener/ContainerProperties.java
1 parent 3901231 commit fcdef5f

File tree

2 files changed

+136
-72
lines changed

2 files changed

+136
-72
lines changed

spring-kafka/src/main/java/org/springframework/kafka/listener/ConsumerProperties.java

Lines changed: 47 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
*
3737
* @author Gary Russell
3838
* @author Sagnhyeok An
39+
* @author Choi Wang Gyu
3940
* @since 2.3
4041
*
4142
*/
@@ -510,29 +511,52 @@ public String toString() {
510511
}
511512

512513
protected final String renderProperties() {
513-
return renderTopics()
514-
+ "\n pollTimeout=" + this.pollTimeout
515-
+ (this.groupId != null ? "\n groupId=" + this.groupId : "")
516-
+ (StringUtils.hasText(this.clientId) ? "\n clientId=" + this.clientId : "")
517-
+ (this.consumerRebalanceListener != null
518-
? "\n consumerRebalanceListener=" + this.consumerRebalanceListener
519-
: "")
520-
+ (this.commitCallback != null ? "\n commitCallback=" + this.commitCallback : "")
521-
+ (this.offsetAndMetadataProvider != null ? "\n offsetAndMetadataProvider=" + this.offsetAndMetadataProvider : "")
522-
+ "\n syncCommits=" + this.syncCommits
523-
+ (this.syncCommitTimeout != null ? "\n syncCommitTimeout=" + this.syncCommitTimeout : "")
524-
+ (!this.kafkaConsumerProperties.isEmpty() ? "\n properties=" + this.kafkaConsumerProperties : "")
525-
+ "\n authExceptionRetryInterval=" + this.authExceptionRetryInterval
526-
+ "\n commitRetries=" + this.commitRetries
527-
+ "\n fixTxOffsets" + this.fixTxOffsets;
528-
}
529-
530-
private String renderTopics() {
531-
return (this.topics != null ? "\n topics=" + Arrays.toString(this.topics) : "")
532-
+ (this.topicPattern != null ? "\n topicPattern=" + this.topicPattern : "")
533-
+ (this.topicPartitions != null
534-
? "\n topicPartitions=" + Arrays.toString(this.topicPartitions)
535-
: "");
514+
StringBuilder sb = new StringBuilder();
515+
renderTopics(sb);
516+
sb.append("\n pollTimeout=").append(this.pollTimeout);
517+
518+
if (this.groupId != null) {
519+
sb.append("\n groupId=").append(this.groupId);
520+
}
521+
if (StringUtils.hasText(this.clientId)) {
522+
sb.append("\n clientId=").append(this.clientId);
523+
}
524+
if (this.consumerRebalanceListener != null) {
525+
sb.append("\n consumerRebalanceListener=").append(this.consumerRebalanceListener);
526+
}
527+
if (this.commitCallback != null) {
528+
sb.append("\n commitCallback=").append(this.commitCallback);
529+
}
530+
if (this.offsetAndMetadataProvider != null) {
531+
sb.append("\n offsetAndMetadataProvider=").append(this.offsetAndMetadataProvider);
532+
}
533+
534+
sb.append("\n syncCommits=").append(this.syncCommits);
535+
536+
if (this.syncCommitTimeout != null) {
537+
sb.append("\n syncCommitTimeout=").append(this.syncCommitTimeout);
538+
}
539+
if (!this.kafkaConsumerProperties.isEmpty()) {
540+
sb.append("\n properties=").append(this.kafkaConsumerProperties);
541+
}
542+
543+
sb.append("\n authExceptionRetryInterval=").append(this.authExceptionRetryInterval);
544+
sb.append("\n commitRetries=").append(this.commitRetries);
545+
sb.append("\n fixTxOffsets=").append(this.fixTxOffsets);
546+
547+
return sb.toString();
548+
}
549+
550+
private void renderTopics(StringBuilder sb) {
551+
if (this.topics != null) {
552+
sb.append("\n topics=").append(Arrays.toString(this.topics));
553+
}
554+
if (this.topicPattern != null) {
555+
sb.append("\n topicPattern=").append(this.topicPattern);
556+
}
557+
if (this.topicPartitions != null) {
558+
sb.append("\n topicPartitions=").append(Arrays.toString(this.topicPartitions));
559+
}
536560
}
537561

538562
}

spring-kafka/src/main/java/org/springframework/kafka/listener/ContainerProperties.java

Lines changed: 89 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@
5454
* @author Lukasz Kaminski
5555
* @author Kyuhyeok Park
5656
* @author Wang Zhiyang
57+
* @author Choi Wang Gyu
5758
*/
5859
public class ContainerProperties extends ConsumerProperties {
5960

@@ -1091,55 +1092,94 @@ public void setRestartAfterAuthExceptions(boolean restartAfterAuthExceptions) {
10911092

10921093
@Override
10931094
public String toString() {
1094-
return "ContainerProperties ["
1095-
+ renderProperties()
1096-
+ "\n ackMode=" + this.ackMode
1097-
+ "\n ackCount=" + this.ackCount
1098-
+ "\n ackTime=" + this.ackTime
1099-
+ "\n consumerStartTimeout=" + this.consumerStartTimeout
1100-
+ "\n messageListener=" + this.messageListener
1101-
+ (this.listenerTaskExecutor != null
1102-
? "\n listenerTaskExecutor=" + this.listenerTaskExecutor
1103-
: "")
1104-
+ "\n shutdownTimeout=" + this.shutdownTimeout
1105-
+ "\n idleEventInterval="
1106-
+ (this.idleEventInterval == null ? "not enabled" : this.idleEventInterval)
1107-
+ "\n idlePartitionEventInterval="
1108-
+ (this.idlePartitionEventInterval == null ? "not enabled" : this.idlePartitionEventInterval)
1109-
+ (this.transactionManager != null
1110-
? "\n transactionManager=" + this.transactionManager
1111-
: "")
1112-
+ (this.kafkaAwareTransactionManager != null
1113-
? "\n kafkaAwareTransactionManager=" + this.kafkaAwareTransactionManager
1114-
: "")
1115-
+ "\n monitorInterval=" + this.monitorInterval
1116-
+ (this.scheduler != null ? "\n scheduler=" + this.scheduler : "")
1117-
+ "\n noPollThreshold=" + this.noPollThreshold
1118-
+ "\n pauseImmediate=" + this.pauseImmediate
1119-
+ "\n pollTimeoutWhilePaused=" + this.pollTimeoutWhilePaused
1120-
+ "\n subBatchPerPartition=" + this.subBatchPerPartition
1121-
+ "\n assignmentCommitOption=" + this.assignmentCommitOption
1122-
+ "\n deliveryAttemptHeader=" + this.deliveryAttemptHeader
1123-
+ "\n batchRecoverAfterRollback=" + this.batchRecoverAfterRollback
1124-
+ "\n eosMode=" + this.eosMode
1125-
+ "\n transactionDefinition=" + this.transactionDefinition
1126-
+ "\n stopContainerWhenFenced=" + this.stopContainerWhenFenced
1127-
+ "\n stopImmediate=" + this.stopImmediate
1128-
+ "\n asyncAcks=" + this.asyncAcks
1129-
+ "\n logContainerConfig=" + this.logContainerConfig
1130-
+ "\n missingTopicsFatal=" + this.missingTopicsFatal
1131-
+ "\n idleBeforeDataMultiplier=" + this.idleBeforeDataMultiplier
1132-
+ "\n idleBetweenPolls=" + this.idleBetweenPolls
1133-
+ "\n micrometerEnabled=" + this.micrometerEnabled
1134-
+ "\n observationEnabled=" + this.observationEnabled
1135-
+ (this.observationConvention != null
1136-
? "\n observationConvention=" + this.observationConvention
1137-
: "")
1138-
+ (this.observationRegistry != null
1139-
? "\n observationRegistry=" + this.observationRegistry
1140-
: "")
1141-
+ "\n restartAfterAuthExceptions=" + this.restartAfterAuthExceptions
1142-
+ "\n]";
1095+
StringBuilder sb = new StringBuilder("ContainerProperties [");
1096+
sb.append(renderProperties());
1097+
1098+
// Core acknowledgment properties
1099+
appendProperty(sb, "ackMode", this.ackMode);
1100+
appendProperty(sb, "ackCount", this.ackCount);
1101+
appendProperty(sb, "ackTime", this.ackTime);
1102+
1103+
// Timeout and startup properties
1104+
appendProperty(sb, "consumerStartTimeout", this.consumerStartTimeout);
1105+
appendProperty(sb, "shutdownTimeout", this.shutdownTimeout);
1106+
1107+
// Listener configuration
1108+
appendProperty(sb, "messageListener", this.messageListener);
1109+
appendProperty(sb, "listenerTaskExecutor", this.listenerTaskExecutor);
1110+
1111+
// Idle event configuration
1112+
appendEnabledProperty(sb, "idleEventInterval", this.idleEventInterval);
1113+
appendEnabledProperty(sb, "idlePartitionEventInterval", this.idlePartitionEventInterval);
1114+
1115+
// Transaction management
1116+
appendProperty(sb, "transactionManager", this.transactionManager);
1117+
appendProperty(sb, "kafkaAwareTransactionManager", this.kafkaAwareTransactionManager);
1118+
appendProperty(sb, "transactionDefinition", this.transactionDefinition);
1119+
1120+
// Monitoring and scheduling
1121+
appendProperty(sb, "monitorInterval", this.monitorInterval);
1122+
appendProperty(sb, "scheduler", this.scheduler);
1123+
appendProperty(sb, "noPollThreshold", this.noPollThreshold);
1124+
1125+
// Container behavior flags
1126+
appendProperty(sb, "pauseImmediate", this.pauseImmediate);
1127+
appendProperty(sb, "stopImmediate", this.stopImmediate);
1128+
appendProperty(sb, "stopContainerWhenFenced", this.stopContainerWhenFenced);
1129+
appendProperty(sb, "asyncAcks", this.asyncAcks);
1130+
1131+
// Polling and partition configuration
1132+
appendProperty(sb, "pollTimeoutWhilePaused", this.pollTimeoutWhilePaused);
1133+
appendProperty(sb, "subBatchPerPartition", this.subBatchPerPartition);
1134+
appendProperty(sb, "assignmentCommitOption", this.assignmentCommitOption);
1135+
appendProperty(sb, "idleBetweenPolls", this.idleBetweenPolls);
1136+
1137+
// Header and recovery configuration
1138+
appendProperty(sb, "deliveryAttemptHeader", this.deliveryAttemptHeader);
1139+
appendProperty(sb, "batchRecoverAfterRollback", this.batchRecoverAfterRollback);
1140+
1141+
// Exactly-once semantics
1142+
appendProperty(sb, "eosMode", this.eosMode);
1143+
1144+
// Logging and error handling
1145+
appendProperty(sb, "logContainerConfig", this.logContainerConfig);
1146+
appendProperty(sb, "missingTopicsFatal", this.missingTopicsFatal);
1147+
appendProperty(sb, "restartAfterAuthExceptions", this.restartAfterAuthExceptions);
1148+
1149+
// Metrics and observation
1150+
appendProperty(sb, "micrometerEnabled", this.micrometerEnabled);
1151+
appendProperty(sb, "observationEnabled", this.observationEnabled);
1152+
appendProperty(sb, "observationConvention", this.observationConvention);
1153+
appendProperty(sb, "observationRegistry", this.observationRegistry);
1154+
1155+
// Data multiplier
1156+
appendProperty(sb, "idleBeforeDataMultiplier", this.idleBeforeDataMultiplier);
1157+
1158+
sb.append("\n]");
1159+
return sb.toString();
1160+
}
1161+
1162+
/**
1163+
* Append a property to the StringBuilder with consistent formatting.
1164+
* @param sb the StringBuilder
1165+
* @param name the property name
1166+
* @param value the property value
1167+
*/
1168+
private void appendProperty(StringBuilder sb, String name, @Nullable Object value) {
1169+
if (value != null) {
1170+
sb.append("\n ").append(name).append("=").append(value);
1171+
}
1172+
}
1173+
1174+
/**
1175+
* Append a property with "enabled/not enabled" formatting for nullable values.
1176+
* @param sb the StringBuilder
1177+
* @param name the property name
1178+
* @param value the property value (nullable)
1179+
*/
1180+
private void appendEnabledProperty(StringBuilder sb, String name, @Nullable Object value) {
1181+
sb.append("\n ").append(name).append("=")
1182+
.append(value == null ? "not enabled" : value);
11431183
}
11441184

11451185
}

0 commit comments

Comments
 (0)