Skip to content

[ISSUE#173]Connect Exporter for Prometheus - #200

Open
yhx-coder wants to merge 6 commits into
apache:masterfrom
yhx-coder:master
Open

[ISSUE#173]Connect Exporter for Prometheus #200
yhx-coder wants to merge 6 commits into
apache:masterfrom
yhx-coder:master

Conversation

@yhx-coder

Copy link
Copy Markdown
Contributor

ISSUE#173 What is the purpose of the change
Some simple interfaces designed for metric exposure.

Brief changelog

Design some interfaces to ensure exposure behavior of metrics provided by dynamic loading.Thus,we can export the metrics to anywhere we want, e.g. prometheus.

@odbozhou odbozhou added the enhancement New feature or request label Jul 16, 2022


public class MetricsExportSinkTask extends SinkTask {
private List<MetricsExporter> metricsExporters;

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.

Has the implementation of MetricsExporter been submitted yet?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Sorry not yet :( . But I am trying my best to finish a exporter for Prometheus which gets the metrics from the log files, and some preliminary knowledge is got. And I hear the sftp source connector (my upstream task) is designing in the last weekly meeting. So I'm waiting for the implementation ConnectRecord in it in order to complete my parsing.

@RockteMQ-AI RockteMQ-AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Summary

This PR modifies 6 file(s) with 198 lines of diff. No test changes detected — consider adding test coverage.


Automated review by github-manager-bot

@@ -0,0 +1,41 @@
package org.apache.rocket.connect.metrics.export.sink.connector;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

No test changes detected alongside source modifications. Consider adding tests to cover the changes.

@RockteMQ-AI RockteMQ-AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Summary

This PR introduces the interface scaffolding for a Prometheus metrics exporter connector for RocketMQ Connect. It defines the MetricsExporter SPI interface, a SinkConnector/SinkTask pair that delegates to discovered exporters via ServiceLoader, and a utility class for service discovery.

The design is sound (SPI-based extensibility), but the implementation has several issues that need attention before this can be merged:

  • Critical: The SPI service file is empty and has a truncated filename, so no implementations will be discovered at runtime
  • Warnings: Class name typo, instance initializer ordering, and missing null safety

Additionally, this PR only adds interfaces — there is no actual Prometheus exporter implementation included. Is this intentional (to be followed by another PR), or should the implementation be part of this PR?

Note: This PR has been open since July 2022. If it is still actively being worked on, please address the above issues. If it has been abandoned, consider closing it.


Automated review by "github-manager-bot"

Additional notes (not anchored to a changed line)

  • [CRITICAL] connectors/rocketmq-connect-metrics-exporter/src/main/resources/META-INF/service/org.apache.rocket.connect.metrics.export.sink.connector.MetricsExport:1 — This SPI service file is empty and has a truncated filename (should end with MetricsExporter, not MetricsExport). Without a valid SPI entry, ServiceLoader will find no implementations. (line outside diff)


/**
* @author: ming
*/

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Typo in class name: ServiceProvicerUtil should be ServiceProviderUtil (missing d in Provider).


private List<MetricsExporter> metricsExporters;
{
metricsExporters = ServiceProvicerUtil.getMetricsExporterServices();

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Instance initializer block runs at construction time, before start(). Consider moving ServiceLoader initialization to start() to avoid potential classpath issues.

private List<MetricsExporter> metricsExporters;

@Override public void put(List<ConnectRecord> sinkRecords) throws ConnectException {
for (MetricsExporter exporter : metricsExporters) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

metricsExporters is initialized in init() but used in put() without null check. If put() is somehow called before init(), this will NPE. Consider adding a null guard or lazy initialization.

@RockteMQ-AI RockteMQ-AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Summary

PR received and logged for review. This PR requires detailed code review by a maintainer.

Diff size: 198 lines
Author: yhx-coder (CONTRIBUTOR)


Automated review by RockteMQ-AI

@RockteMQ-AI RockteMQ-AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Summary

Review of PR #200: [ISSUE#173]Connect Exporter for Prometheus

Findings: 8 issue(s) identified (1 critical).
CLA: unknown

Please address the inline comments above.


Automated review by github-manager-bot

Additional notes (not anchored to a changed line)

  • [CRITICAL] connectors/rocketmq-connect-metrics-exporter/src/main/resources/META-INF/service/org.apache.rocket.connect.metrics.export.sink.connector.MetricsExport — SPI service file has two fatal errors: (1) directory must be META-INF/services/ (plural), not META-INF/service/; (2) filename must be the fully-qualified interface name 'org.apache.rocket.connect.metrics.export.sink.connector.MetricsExporter' (missing trailing 'r'). Additionally the file is empty — no implementation classes are registered. ServiceLoader.load(MetricsExporter.class) will find zero implementations, making the entire connector a silent no-op. (missing file/line/body)

private List<MetricsExporter> metricsExporters;
{
metricsExporters = ServiceProvicerUtil.getMetricsExporterServices();
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

metricsExporters is loaded in an instance initializer block but never closed/stopped in stop(). If exporters hold resources (e.g., HTTP clients, thread pools for Prometheus scraping), they will leak because the connector's stop() only nulls out config. Consider iterating metricsExporters in stop() and calling stop() on each, or documenting that lifecycle is fully managed by the task.

private List<MetricsExporter> metricsExporters;

@Override public void put(List<ConnectRecord> sinkRecords) throws ConnectException {
for (MetricsExporter exporter : metricsExporters) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

In put(), if one exporter throws an exception, all subsequent exporters in the loop are skipped and the entire batch is lost. For a multi-exporter setup this means one faulty exporter blocks all others. Consider catching per-exporter, logging, and continuing — or aggregating exceptions before rethrowing.

private List<MetricsExporter> metricsExporters;

@Override public void put(List<ConnectRecord> sinkRecords) throws ConnectException {
for (MetricsExporter exporter : metricsExporters) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

If metricsExporters is empty (which it will be given the broken SPI file), put() silently discards all incoming ConnectRecords with no log or error. A warning or exception when the list is empty during start() or init() would prevent silent data loss.

* @author: ming
*/
public class ServiceProvicerUtil {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Class name 'ServiceProvicerUtil' contains a typo — should be 'ServiceProviderUtil'. This is a public API surface; fixing it later would be a breaking change.

List<KeyValue> configs = new ArrayList<>();
configs.add(config);
return configs;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

taskConfigs(int maxTasks) always returns a single-element list regardless of maxTasks. If the framework distributes work across multiple tasks for parallelism, this silently caps it at one. Either honor maxTasks or explicitly document that this connector supports only a single task.

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

pom.xml has no parent POM and uses a standalone hardcoded version '1.0-SNAPSHOT' with a standalone groupId. Other connector modules in this repo likely inherit from a parent POM for consistent dependency management. This may cause version drift and build inconsistency.

@@ -0,0 +1,47 @@
<?xml version="1.0" encoding="UTF-8"?>

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

No test files are included in this PR. There is no unit or integration test coverage for the connector, task, or SPI loading logic — critical paths like put(), start()/stop() lifecycle, and validate() are completely untested.

@RockteMQ-AI RockteMQ-AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Summary

8 finding(s) to address.

Findings

  • [CRITICAL] connectors/rocketmq-connect-metrics-exporter/src/main/resources/META-INF/service/org.apache.rocket.connect.metrics.export.sink.connector.MetricsExport:0 — SPI service file is in the wrong directory and has the wrong filename. Java's ServiceLoader requires the path META-INF/services/ (plural) and the filename must exactly match the fully-qualified interface name: org.apache.rocket.connect.metrics.export.sink.connector.MetricsExporter. The current path uses service/ (singular) and the filename is MetricsExport (missing the trailing 'r'). This means ServiceLoader.load(MetricsExporter.class) will silently return zero implementations at runtime, making the entire connector non-functional.
  • [CRITICAL] connectors/rocketmq-connect-metrics-exporter/src/main/java/org/apache/rocket/connect/metrics/export/sink/connector/MetricsExportSinkTask.java:13metricsExporters is not initialized at declaration — it is only assigned inside init(). If the framework ever calls start() or put() before init(), this will throw a NullPointerException. In MetricsExportSinkConnector, the equivalent field is initialized via an instance-initializer block. The task should do the same, or at minimum initialize at declaration: private List<MetricsExporter> metricsExporters = ServiceProvicerUtil.getMetricsExporterServices();
  • [WARNING] connectors/rocketmq-connect-metrics-exporter/src/main/java/org/apache/rocket/connect/metrics/export/sink/util/ServiceProvicerUtil.java:12 — Class name contains a typo: ServiceProvicerUtil should be ServiceProviderUtil. This typo propagates to every call site (connector and task classes) and will be a permanent API blemish if not fixed before merge.
  • [WARNING] connectors/rocketmq-connect-metrics-exporter/src/main/java/org/apache/rocket/connect/metrics/export/sink/connector/MetricsExportSinkConnector.java:21taskConfigs(int maxTasks) always returns a single-element list regardless of maxTasks. This caps the connector to exactly one task even when the runtime requests more. For a metrics exporter this may be intentional, but it should be documented or explicitly guarded — silently ignoring maxTasks can confuse operators who configure a higher parallelism.
  • [WARNING] connectors/rocketmq-connect-metrics-exporter/src/main/java/org/apache/rocket/connect/metrics/export/sink/connector/MetricsExportSinkTask.java:15 — No error handling in put(). If any single MetricsExporter.export() throws an exception, the remaining exporters in the list are skipped and the entire batch is lost. Consider catching per-exporter exceptions so one faulty exporter does not block the others, and log or route failures appropriately.
  • [INFO] connectors/rocketmq-connect-metrics-exporter/src/main/java/org/apache/rocket/connect/metrics/export/sink/connector/MetricsExportSinkConnector.java:15 — The metricsExporters field is populated in an instance-initializer block, meaning ServiceLoader runs at construction time — before start() or validate(). If the connector is constructed but never started (e.g., validation fails), the SPI scan is wasted work. Consider lazy initialization in start() for consistency with the connector lifecycle.
  • [INFO] connectors/rocketmq-connect-metrics-exporter/pom.xml:8 — This module defines its own <groupId> and <version> instead of inheriting from the parent POM (<parent> block is absent). This means it won't be built as part of the reactor, won't inherit dependency management, and won't receive the project-wide license/enforcer plugins. It will be an orphaned module that must be built and released independently.
  • [INFO] connectors/rocketmq-connect-metrics-exporter/src/main/java/org/apache/rocket/connect/metrics/export/sink/connector/MetricsExportSinkTask.java:21start() does not call super.start(config). While the current SinkTask base may not require it, omitting the super call can break lifecycle contracts in future framework versions. Same applies to stop().

Automated review by github-manager-bot

Additional notes (not anchored to a changed line)

  • [CRITICAL] connectors/rocketmq-connect-metrics-exporter/src/main/resources/META-INF/service/org.apache.rocket.connect.metrics.export.sink.connector.MetricsExport — SPI service file is in the wrong directory and has the wrong filename. Java's ServiceLoader requires the path META-INF/services/ (plural) and the filename must exactly match the fully-qualified interface name: org.apache.rocket.connect.metrics.export.sink.connector.MetricsExporter. The current path uses service/ (singular) and the filename is MetricsExport (missing the trailing 'r'). This means ServiceLoader.load(MetricsExporter.class) will silently return zero implementations at runtime, making the entire connector non-functional. (missing file/line/body)



public class MetricsExportSinkTask extends SinkTask {
private List<MetricsExporter> metricsExporters;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

metricsExporters is not initialized at declaration — it is only assigned inside init(). If the framework ever calls start() or put() before init(), this will throw a NullPointerException. In MetricsExportSinkConnector, the equivalent field is initialized via an instance-initializer block. The task should do the same, or at minimum initialize at declaration: private List<MetricsExporter> metricsExporters = ServiceProvicerUtil.getMetricsExporterServices();

/**
* @author: ming
*/
public class ServiceProvicerUtil {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Class name contains a typo: ServiceProvicerUtil should be ServiceProviderUtil. This typo propagates to every call site (connector and task classes) and will be a permanent API blemish if not fixed before merge.

@Override public List<KeyValue> taskConfigs(int maxTasks) {
List<KeyValue> configs = new ArrayList<>();
configs.add(config);
return configs;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

taskConfigs(int maxTasks) always returns a single-element list regardless of maxTasks. This caps the connector to exactly one task even when the runtime requests more. For a metrics exporter this may be intentional, but it should be documented or explicitly guarded — silently ignoring maxTasks can confuse operators who configure a higher parallelism.

public class MetricsExportSinkTask extends SinkTask {
private List<MetricsExporter> metricsExporters;

@Override public void put(List<ConnectRecord> sinkRecords) throws ConnectException {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

No error handling in put(). If any single MetricsExporter.export() throws an exception, the remaining exporters in the list are skipped and the entire batch is lost. Consider catching per-exporter exceptions so one faulty exporter does not block the others, and log or route failures appropriately.


private List<MetricsExporter> metricsExporters;
{
metricsExporters = ServiceProvicerUtil.getMetricsExporterServices();

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

The metricsExporters field is populated in an instance-initializer block, meaning ServiceLoader runs at construction time — before start() or validate(). If the connector is constructed but never started (e.g., validation fails), the SPI scan is wasted work. Consider lazy initialization in start() for consistency with the connector lifecycle.

<modelVersion>4.0.0</modelVersion>

<groupId>org.apache.rocketmq</groupId>
<artifactId>rocketmq-connect-metrics-exporter</artifactId>

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

This module defines its own <groupId> and <version> instead of inheriting from the parent POM (<parent> block is absent). This means it won't be built as part of the reactor, won't inherit dependency management, and won't receive the project-wide license/enforcer plugins. It will be an orphaned module that must be built and released independently.

}
}

@Override public void start(KeyValue config) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

start() does not call super.start(config). While the current SinkTask base may not require it, omitting the super call can break lifecycle contracts in future framework versions. Same applies to stop().

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