Skip to content

Add start/stop count metrics to listener container#4383

Open
Yelagandula wants to merge 1 commit into
spring-projects:mainfrom
Yelagandula:feature/container-lifecycle-metrics
Open

Add start/stop count metrics to listener container#4383
Yelagandula wants to merge 1 commit into
spring-projects:mainfrom
Yelagandula:feature/container-lifecycle-metrics

Conversation

@Yelagandula
Copy link
Copy Markdown

Adds two Micrometer counters to AbstractMessageListenerContainer to track listener container lifecycle events:

  • spring.kafka.container.start.count: incremented each time a container is successfully started via doStart()
  • spring.kafka.container.stop.count: incremented each time a container is stopped, covering both wait=true and wait=false paths

Closes #4277

import java.util.stream.Collectors;

import io.micrometer.core.instrument.Counter;
import io.micrometer.core.instrument.Metrics;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I think it is better to avoid tight Micrometer coupling in this abstract base class, since it is shared by all container types. Putting Micrometer types here means anything that loads AbstractMessageListenerContainer pulls in those classes even when the application does not use metrics. I would rather follow the same pattern the listener consumer and KafkaTemplate use: obtain the Micrometer context only when it is needed (take a look at obtainMicrometerHolder()). A cleaner approach might be to introduce two protected lifecycle methods to record start and stop. Subclasses such as KafkaMessageListenerContainer can override them, and a dedicated class similar to MicrometerHolder can handle the core Micrometer work. That will take a bit more design work. Let us know what you think about this idea.

@Yelagandula
Copy link
Copy Markdown
Author

Yelagandula commented Apr 17, 2026

Thank you for the detailed feedback @sobychacko! I've refactored the
implementation following your suggestion:

  • Added protected recordContainerStarted() and recordContainerStopped()
    lifecycle hooks in AbstractMessageListenerContainer (no-op by default)
  • Created ContainerLifecycleMicrometerHolder to handle counter
    registration and increment logic
  • Overridden the lifecycle methods in KafkaMessageListenerContainer
    following the obtainMicrometerHolder() pattern to avoid tight
    Micrometer coupling in the abstract base class

Please let me know if this looks good or if anything needs adjustment.

@Yelagandula Yelagandula force-pushed the feature/container-lifecycle-metrics branch from 56a41f3 to c30d632 Compare April 17, 2026 05:39
Copy link
Copy Markdown
Contributor

@sobychacko sobychacko left a comment

Choose a reason for hiding this comment

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

Latest changes in the PR are in the right direction. Thanks for the updates. I added some review comments. Please take a look.

In addition, please address these concerns also.

  1. Add tests to verify the expected behavior.
  2. Reference docs - since these are new metrics, we should add reference docs with detailed information.
  3. Then add an entry in whats-new doc and link it to the ref docs section.
  4. You need to add your name as an author to all the classes you added or modified.
  5. Need to sign your commit using DCO - https://spring.io/blog/2025/01/06/hello-dco-goodbye-cla-simplifying-contributions-to-spring

Thanks.

* Micrometer holder for container lifecycle metrics (start/stop counts).
*
* @author Vineeth Yelagandula
* @since 3.4
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This needs to be 4.1.0.

* @author Vineeth Yelagandula
* @since 3.4
*/
public class ContainerLifecycleMicrometerHolder {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This class needs to be final.

@Nullable
private ContainerLifecycleMicrometerHolder obtainLifecycleMicrometerHolder() {
try {
if (KafkaUtils.MICROMETER_PRESENT) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

We also should add the this.containerProperties.isMicrometerEnabled() check (similar to the other obtain method for the listener). That way, if the user explicitly disabled micrometer via the property, then this method will (correctly so) return null.


private final Counter stopCounter;

public ContainerLifecycleMicrometerHolder(MeterRegistry registry, String containerName) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Need javadoc for the constructor.

}

protected void doStop(Runnable callback) {
recordContainerStopped();
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This call should also go into the stopAbnormally method above.

/**
* Called when the container is started. Subclasses may override to record metrics.
*/
protected void recordContainerStarted() {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Need since tag.

/**
* Called when the container is stopped. Subclasses may override to record metrics.
*/
protected void recordContainerStopped() {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Need since tag.

private ContainerLifecycleMicrometerHolder obtainLifecycleMicrometerHolder() {
try {
if (KafkaUtils.MICROMETER_PRESENT) {
org.springframework.context.ApplicationContext ctx = getApplicationContext();
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Why are we using FQCN here? Can we import it normally?

org.springframework.context.ApplicationContext ctx = getApplicationContext();
if (ctx != null) {
io.micrometer.core.instrument.MeterRegistry registry =
ctx.getBeanProvider(io.micrometer.core.instrument.MeterRegistry.class).getIfUnique();
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Why are we using FQCN here? Can we import it normally?

}
}
}
catch (Exception ex) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Maybe exception could be more specific? Also, add a logging statement?

@sobychacko
Copy link
Copy Markdown
Contributor

@Yelagandula Could you take a look at these review comments? #4383 (review)

@Yelagandula
Copy link
Copy Markdown
Author

Hi @sobychacko, addressed all the review comments:

  • Fixed @SInCE to 4.1.0
  • Made ContainerLifecycleMicrometerHolder final
  • Added javadoc for the constructor
  • Added isMicrometerEnabled() check in obtainLifecycleMicrometerHolder()
  • Replaced FQCN with proper imports for ApplicationContext and MeterRegistry
  • Changed catch to IllegalStateException with a debug log statement
  • Added recordContainerStopped() call in stopAbnormally() as well
  • Added @SInCE 4.1.0 to both recordContainerStarted() and recordContainerStopped()

Still working on tests, reference docs, whats-new entry, and author
tags will push those shortly.

@Yelagandula
Copy link
Copy Markdown
Author

Hi @sobychacko, addressed all remaining review comments:

  • Added tests verifying start/stop counter behavior
  • Added reference docs in micrometer.adoc under "Monitoring Listener Container Lifecycle"
  • Added whats-new entry linking to the ref docs section
  • Added @author Vineeth Yelagandula to all modified classes

Please take a look when you get a chance!

- Add protected recordContainerStarted() and recordContainerStopped()
  lifecycle hooks in AbstractMessageListenerContainer
- Create ContainerLifecycleMicrometerHolder following obtainMicrometerHolder() pattern
- Override lifecycle methods in KafkaMessageListenerContainer
- Add isMicrometerEnabled() check, proper imports, IllegalStateException logging
- Add tests for ContainerLifecycleMicrometerHolder
- Add reference docs in micrometer.adoc
- Add whats-new entry
- Add @author Vineeth Yelagandula to modified classes

Closes spring-projects#4277

Signed-off-by: Vineeth Yelagandula <111960524+Yelagandula@users.noreply.github.com>
@Yelagandula Yelagandula force-pushed the feature/container-lifecycle-metrics branch from a461f23 to 6812db7 Compare April 30, 2026 19:15
@Yelagandula
Copy link
Copy Markdown
Author

Also squashed all commits into one clean commit with DCO sign-off.
Should be good now!

@sobychacko
Copy link
Copy Markdown
Contributor

@Yelagandula Build failed on the CI. Can you take a look? Also, there are some test failures. You need to use a proper email address when signing the commit, the DCO check failed on the PR because of that.

@sobychacko
Copy link
Copy Markdown
Contributor

@Yelagandula Any updates on this PR? Just a reminder that we have a release coming up in a few days and if you want to include these changes as part of the 4.1.0 release, the review concerns I raised above must be addressed before then. Otherwise, it has to wait until the next release cycle. Thanks!

@sobychacko
Copy link
Copy Markdown
Contributor

@Yelagandula Any updates on this?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add Container start, stop count metric

2 participants