diff --git a/kernel/kernel-api/src/main/java/io/delta/kernel/internal/metrics/MetricsReportSerializers.java b/kernel/kernel-api/src/main/java/io/delta/kernel/internal/metrics/MetricsReportSerializer.java
similarity index 71%
rename from kernel/kernel-api/src/main/java/io/delta/kernel/internal/metrics/MetricsReportSerializers.java
rename to kernel/kernel-api/src/main/java/io/delta/kernel/internal/metrics/MetricsReportSerializer.java
index 12f6ac9f88b..1568a34b7f0 100644
--- a/kernel/kernel-api/src/main/java/io/delta/kernel/internal/metrics/MetricsReportSerializers.java
+++ b/kernel/kernel-api/src/main/java/io/delta/kernel/internal/metrics/MetricsReportSerializer.java
@@ -26,46 +26,34 @@
import io.delta.kernel.expressions.Column;
import io.delta.kernel.expressions.Predicate;
import io.delta.kernel.metrics.MetricsReport;
-import io.delta.kernel.metrics.ScanReport;
-import io.delta.kernel.metrics.SnapshotReport;
-import io.delta.kernel.metrics.TransactionReport;
import io.delta.kernel.types.StructType;
import java.io.IOException;
/** Defines JSON serializers for {@link MetricsReport} types */
-public final class MetricsReportSerializers {
+public final class MetricsReportSerializer {
/////////////////
// Public APIs //
/////////////////
/**
- * Serializes a {@link SnapshotReport} to a JSON string
+ * Serializes any {@link MetricsReport} to a JSON string using the configured ObjectMapper. This
+ * method works for any report type that has proper Jackson annotations.
*
- * @throws JsonProcessingException
- */
- public static String serializeSnapshotReport(SnapshotReport snapshotReport)
- throws JsonProcessingException {
- return OBJECT_MAPPER.writeValueAsString(snapshotReport);
- }
-
- /**
- * Serializes a {@link ScanReport} to a JSON string
+ *
For successful serialization, the report class should have:
*
- * @throws JsonProcessingException
- */
- public static String serializeScanReport(ScanReport scanReport) throws JsonProcessingException {
- return OBJECT_MAPPER.writeValueAsString(scanReport);
- }
-
- /**
- * Serializes a {@link TransactionReport} to a JSON string
+ *
+ * - Public fields or public getter methods
+ *
- No circular references in the object graph
+ *
- Fields with types supported by Jackson (primitives, collections, or types with registered
+ * serializers)
+ *
*
- * @throws JsonProcessingException
+ * @throws JsonProcessingException if the report cannot be serialized (e.g., circular references,
+ * unsupported types, or inaccessible fields)
*/
- public static String serializeTransactionReport(TransactionReport transactionReport)
- throws JsonProcessingException {
- return OBJECT_MAPPER.writeValueAsString(transactionReport);
+ public static String serializeMetricsReport(MetricsReport report) throws JsonProcessingException {
+ return OBJECT_MAPPER.writeValueAsString(report);
}
/////////////////////////////////
@@ -96,5 +84,5 @@ public void serialize(Column value, JsonGenerator gen, SerializerProvider serial
}
}
- private MetricsReportSerializers() {}
+ private MetricsReportSerializer() {}
}
diff --git a/kernel/kernel-api/src/test/scala/io/delta/kernel/internal/metrics/MetricsReportSerializerSuite.scala b/kernel/kernel-api/src/test/scala/io/delta/kernel/internal/metrics/MetricsReportSerializerSuite.scala
index 9b65bed67b8..db1dbf083c8 100644
--- a/kernel/kernel-api/src/test/scala/io/delta/kernel/internal/metrics/MetricsReportSerializerSuite.scala
+++ b/kernel/kernel-api/src/test/scala/io/delta/kernel/internal/metrics/MetricsReportSerializerSuite.scala
@@ -69,7 +69,7 @@ class MetricsReportSerializerSuite extends AnyFunSuite {
|}
|}
|""".stripMargin.replaceAll("\n", "")
- assert(expectedJson == MetricsReportSerializers.serializeSnapshotReport(snapshotReport))
+ assert(expectedJson == MetricsReportSerializer.serializeMetricsReport(snapshotReport))
}
test("SnapshotReport serializer") {
@@ -106,7 +106,7 @@ class MetricsReportSerializerSuite extends AnyFunSuite {
|}
|}
|""".stripMargin.replaceAll("\n", "")
- assert(expectedJson == MetricsReportSerializers.serializeSnapshotReport(snapshotReport1))
+ assert(expectedJson == MetricsReportSerializer.serializeMetricsReport(snapshotReport1))
// Check with test function
testSnapshotReport(snapshotReport1)
@@ -152,7 +152,7 @@ class MetricsReportSerializerSuite extends AnyFunSuite {
|}
|}
|""".stripMargin.replaceAll("\n", "")
- assert(expectedJson == MetricsReportSerializers.serializeTransactionReport(transactionReport))
+ assert(expectedJson == MetricsReportSerializer.serializeMetricsReport(transactionReport))
}
test("TransactionReport serializer") {
@@ -204,7 +204,7 @@ class MetricsReportSerializerSuite extends AnyFunSuite {
|}
|}
|""".stripMargin.replaceAll("\n", "")
- assert(expectedJson == MetricsReportSerializers.serializeTransactionReport(transactionReport1))
+ assert(expectedJson == MetricsReportSerializer.serializeMetricsReport(transactionReport1))
// Check with test function
testTransactionReport(transactionReport1)
@@ -257,7 +257,7 @@ class MetricsReportSerializerSuite extends AnyFunSuite {
|}
|}
|""".stripMargin.replaceAll("\n", "")
- assert(expectedJson == MetricsReportSerializers.serializeScanReport(scanReport))
+ assert(expectedJson == MetricsReportSerializer.serializeMetricsReport(scanReport))
}
test("ScanReport serializer") {
@@ -319,7 +319,7 @@ class MetricsReportSerializerSuite extends AnyFunSuite {
|}
|}
|""".stripMargin.replaceAll("\n", "")
- assert(expectedJson == MetricsReportSerializers.serializeScanReport(scanReport1))
+ assert(expectedJson == MetricsReportSerializer.serializeMetricsReport(scanReport1))
// Check with test function
testScanReport(scanReport1)
@@ -339,4 +339,28 @@ class MetricsReportSerializerSuite extends AnyFunSuite {
Optional.empty())
testScanReport(scanReport2)
}
+
+ test("Generic serializer handles custom/new report types") {
+ // This test demonstrates that any custom MetricsReport implementation with proper
+ // Jackson annotations can be serialized without modifying MetricsReportSerializer
+
+ val fooReport = new FooReport("bar-value", 42, "zap-value")
+
+ val actualJson = MetricsReportSerializer.serializeMetricsReport(fooReport)
+
+ val expectedJson = """{"bar":"bar-value","zip":42,"zap":"zap-value"}"""
+
+ assert(expectedJson == actualJson)
+ }
}
+
+/**
+ * Example custom report type demonstrating extensibility.
+ * Uses public fields (via @BeanProperty) to work with Jackson, like UcCommitTelemetry.Report.
+ */
+@com.fasterxml.jackson.annotation.JsonPropertyOrder(Array("bar", "zip", "zap"))
+class FooReport(
+ @scala.beans.BeanProperty val bar: String,
+ @scala.beans.BeanProperty val zip: Int,
+ @scala.beans.BeanProperty val zap: String)
+ extends io.delta.kernel.metrics.MetricsReport
diff --git a/kernel/kernel-defaults/src/main/java/io/delta/kernel/defaults/engine/LoggingMetricsReporter.java b/kernel/kernel-defaults/src/main/java/io/delta/kernel/defaults/engine/LoggingMetricsReporter.java
index a10f995ee14..6e202fd664c 100644
--- a/kernel/kernel-defaults/src/main/java/io/delta/kernel/defaults/engine/LoggingMetricsReporter.java
+++ b/kernel/kernel-defaults/src/main/java/io/delta/kernel/defaults/engine/LoggingMetricsReporter.java
@@ -17,11 +17,8 @@
import com.fasterxml.jackson.core.JsonProcessingException;
import io.delta.kernel.engine.MetricsReporter;
-import io.delta.kernel.internal.metrics.MetricsReportSerializers;
+import io.delta.kernel.internal.metrics.MetricsReportSerializer;
import io.delta.kernel.metrics.MetricsReport;
-import io.delta.kernel.metrics.ScanReport;
-import io.delta.kernel.metrics.SnapshotReport;
-import io.delta.kernel.metrics.TransactionReport;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -36,23 +33,8 @@ public class LoggingMetricsReporter implements MetricsReporter {
@Override
public void report(MetricsReport report) {
try {
- if (report instanceof SnapshotReport) {
- logger.info(
- "SnapshotReport = {}",
- MetricsReportSerializers.serializeSnapshotReport((SnapshotReport) report));
- } else if (report instanceof ScanReport) {
- logger.info(
- "ScanReport = {}", MetricsReportSerializers.serializeScanReport((ScanReport) report));
- } else if (report instanceof TransactionReport) {
- logger.info(
- "TransactionReport = {}",
- MetricsReportSerializers.serializeTransactionReport((TransactionReport) report));
- } else {
- logger.info(
- "{} = [{} does not support serializing this type of MetricReport]",
- report.getClass(),
- this.getClass());
- }
+ final String reportType = report.getClass().getSimpleName();
+ logger.info("{} = {}", reportType, MetricsReportSerializer.serializeMetricsReport(report));
} catch (JsonProcessingException e) {
logger.info("Encountered exception while serializing report {}: {}", report, e);
}