Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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
* <p>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
* <ul>
* <li>Public fields or public getter methods
* <li>No circular references in the object graph
* <li>Fields with types supported by Jackson (primitives, collections, or types with registered
* serializers)
* </ul>
*
* @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);
}

/////////////////////////////////
Expand Down Expand Up @@ -96,5 +84,5 @@ public void serialize(Column value, JsonGenerator gen, SerializerProvider serial
}
}

private MetricsReportSerializers() {}
private MetricsReportSerializer() {}
}
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ class MetricsReportSerializerSuite extends AnyFunSuite {
|}
|}
|""".stripMargin.replaceAll("\n", "")
assert(expectedJson == MetricsReportSerializers.serializeSnapshotReport(snapshotReport))
assert(expectedJson == MetricsReportSerializer.serializeMetricsReport(snapshotReport))
}

test("SnapshotReport serializer") {
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -152,7 +152,7 @@ class MetricsReportSerializerSuite extends AnyFunSuite {
|}
|}
|""".stripMargin.replaceAll("\n", "")
assert(expectedJson == MetricsReportSerializers.serializeTransactionReport(transactionReport))
assert(expectedJson == MetricsReportSerializer.serializeMetricsReport(transactionReport))
}

test("TransactionReport serializer") {
Expand Down Expand Up @@ -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)

Expand Down Expand Up @@ -257,7 +257,7 @@ class MetricsReportSerializerSuite extends AnyFunSuite {
|}
|}
|""".stripMargin.replaceAll("\n", "")
assert(expectedJson == MetricsReportSerializers.serializeScanReport(scanReport))
assert(expectedJson == MetricsReportSerializer.serializeMetricsReport(scanReport))
}

test("ScanReport serializer") {
Expand Down Expand Up @@ -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)
Expand All @@ -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
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -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);
}
Expand Down
Loading