diff --git a/src/main/java/io/ebean/meta/MetaInfoManager.java b/src/main/java/io/ebean/meta/MetaInfoManager.java index b2eaab614..8b38e9786 100644 --- a/src/main/java/io/ebean/meta/MetaInfoManager.java +++ b/src/main/java/io/ebean/meta/MetaInfoManager.java @@ -16,6 +16,18 @@ public interface MetaInfoManager { */ ServerMetrics collectMetrics(); + /** + * Collect the metrics in raw JSON form. + *
{@code
+   *
+   *   String metricsJson = database.getMetaInfoManager()
+   *       .collectMetricsAsJson()
+   *       .json();
+   *
+   * }
+ */ + ServerMetricsAsJson collectMetricsAsJson(); + /** * Collect query plans. */ diff --git a/src/main/java/io/ebean/meta/ServerMetricsAsJson.java b/src/main/java/io/ebean/meta/ServerMetricsAsJson.java new file mode 100644 index 000000000..a85c072cb --- /dev/null +++ b/src/main/java/io/ebean/meta/ServerMetricsAsJson.java @@ -0,0 +1,40 @@ +package io.ebean.meta; + +import java.util.Comparator; + +/** + * Collect the metrics in raw JSON form. + */ +public interface ServerMetricsAsJson { + + /** + * Set to false to exclude profile location. + */ + ServerMetricsAsJson withLocation(boolean withLocation); + + /** + * Set to false to exclude SQL for query metrics. + */ + ServerMetricsAsJson withSql(boolean withSql); + + /** + * Set to false to exclude SQL hash. + */ + ServerMetricsAsJson withHash(boolean withHash); + + /** + * Set the sort property - see SortMetric + * @see SortMetric + */ + ServerMetricsAsJson withSort(Comparator sortBy); + + /** + * Set the new line character to use. + */ + ServerMetricsAsJson withNewLine(boolean withNewLine); + + /** + * Return the metrics in raw JSON. + */ + String json(); +} diff --git a/src/main/java/io/ebeaninternal/server/core/DefaultMetaInfoManager.java b/src/main/java/io/ebeaninternal/server/core/DefaultMetaInfoManager.java index 0c7fe65fa..cc7f101e0 100644 --- a/src/main/java/io/ebeaninternal/server/core/DefaultMetaInfoManager.java +++ b/src/main/java/io/ebeaninternal/server/core/DefaultMetaInfoManager.java @@ -12,6 +12,7 @@ import io.ebean.meta.MetaTimedMetric; import io.ebean.meta.MetricVisitor; import io.ebean.meta.QueryPlanRequest; import io.ebean.meta.ServerMetrics; +import io.ebean.meta.ServerMetricsAsJson; import java.util.ArrayList; import java.util.List; @@ -42,6 +43,11 @@ public class DefaultMetaInfoManager implements MetaInfoManager { return visitBasic(); } + @Override + public ServerMetricsAsJson collectMetricsAsJson() { + return new DumpMetricsJson(server); + } + @Override public BasicMetricVisitor visitBasic() { BasicMetricVisitor basic = new BasicMetricVisitor(); diff --git a/src/main/java/io/ebeaninternal/server/core/DumpMetricsJson.java b/src/main/java/io/ebeaninternal/server/core/DumpMetricsJson.java new file mode 100644 index 000000000..11c01149a --- /dev/null +++ b/src/main/java/io/ebeaninternal/server/core/DumpMetricsJson.java @@ -0,0 +1,232 @@ +package io.ebeaninternal.server.core; + +import io.ebean.Database; +import io.ebean.ProfileLocation; +import io.ebean.meta.MetaCountMetric; +import io.ebean.meta.MetaOrmQueryMetric; +import io.ebean.meta.MetaQueryMetric; +import io.ebean.meta.MetaTimedMetric; +import io.ebean.meta.ServerMetrics; +import io.ebean.meta.ServerMetricsAsJson; +import io.ebean.meta.SortMetric; + +import java.io.StringWriter; +import java.util.Comparator; +import java.util.List; + +class DumpMetricsJson implements ServerMetricsAsJson { + + private final Database database; + + private final StringWriter writer = new StringWriter(); + + private boolean dumpHash = true; + private boolean dumpSql = true; + private boolean dumpLoc = true; + + private Comparator sortBy = SortMetric.NAME; + + private int listCounter; + private int objKeyCounter; + + private String newLine = "\n"; + + DumpMetricsJson(Database database) { + this.database = database; + } + + @Override + public ServerMetricsAsJson withLocation(boolean withLocation) { + this.dumpLoc = withLocation; + return this; + } + + @Override + public ServerMetricsAsJson withSql(boolean withSql) { + this.dumpSql = withSql; + return this; + } + + @Override + public ServerMetricsAsJson withHash(boolean withHash) { + this.dumpHash = withHash; + return this; + } + + @Override + public ServerMetricsAsJson withSort(Comparator sortBy) { + this.sortBy = sortBy; + return this; + } + + @Override + public ServerMetricsAsJson withNewLine(boolean withNewLine) { + this.newLine = withNewLine ? "\n" : ""; + return this; + } + + @Override + public String json() { + collect(database.getMetaInfoManager().collectMetrics()); + return writer.toString(); + } + + + private void collect(ServerMetrics serverMetrics) { + + start(); + for (MetaTimedMetric metric : serverMetrics.getTimedMetrics()) { + log(metric); + } + + List countMetrics = serverMetrics.getCountMetrics(); + if (!countMetrics.isEmpty()) { + if (sortBy != null) { + countMetrics.sort(SortMetric.COUNT_NAME); + } + for (MetaCountMetric metric : countMetrics) { + logCount(metric); + } + } + + List ormQueryMetrics = serverMetrics.getOrmQueryMetrics(); + if (!ormQueryMetrics.isEmpty()) { + if (sortBy != null) { + ormQueryMetrics.sort(sortBy); + } + for (MetaOrmQueryMetric metric : ormQueryMetrics) { + logQuery(metric); + } + } + + List dtoQueryMetrics = serverMetrics.getDtoQueryMetrics(); + if (!dtoQueryMetrics.isEmpty()) { + if (sortBy != null) { + dtoQueryMetrics.sort(sortBy); + } + for (MetaQueryMetric metric : dtoQueryMetrics) { + logDtoQuery(metric); + } + } + end(); + } + + private void start() { + objStart(); + key("db"); + val(database.getName()); + key("metrics"); + listStart(); + } + + private void end() { + listEnd(); + objEnd(); + } + + private void objStart() { + objKeyCounter = 0; + writer.append("{"); + } + + private void objEnd() { + writer.append("}"); + } + + private void listStart() { + listCounter = 0; + writer.append("["); + writer.append(newLine); + } + + private void listEnd() { + writer.append("]"); + } + + private void key(String key) { + if (objKeyCounter++ > 0) { + writer.append(", "); + } + writer.append("\"").append(key).append("\":"); + } + + private void val(long count) { + writer.append(Long.toString(count)); + } + + private void val(String val) { + writer.append("\"").append(val).append("\""); + } + + private void log(MetaTimedMetric metric) { + + metricStart(); + key("name"); + val(metric.getName()); + appendCounters(metric); + objEnd(); + } + + private void metricStart() { + if (listCounter++ > 0) { + writer.append(",").append(newLine); + } + objStart(); + } + + private void logCount(MetaCountMetric metric) { + metricStart(); + key("name"); + val(metric.getName()); + key("count"); + val(metric.getCount()); + objEnd(); + } + + private void logQuery(MetaOrmQueryMetric metric) { + + metricStart(); + key("name"); + val(metric.getName()); + appendCounters(metric); + if (dumpHash) { + key("hash"); + val(metric.getSqlHash()); + } + appendProfileAndSql(metric); + objEnd(); + } + + private void logDtoQuery(MetaQueryMetric metric) { + + metricStart(); + key("name"); + val(metric.getName()); + appendCounters(metric); + appendProfileAndSql(metric); + objEnd(); + } + + private void appendProfileAndSql(MetaQueryMetric metric) { + ProfileLocation profileLocation = metric.getProfileLocation(); + if (dumpLoc && profileLocation != null) { + key("loc"); + val(profileLocation.shortDescription()); + } + if (dumpSql) { + key("sql"); + val(metric.getSql()); + } + } + + private void appendCounters(MetaTimedMetric timedMetric) { + key("count"); + val(timedMetric.getCount()); + key("total"); + val(timedMetric.getTotal()); + key("mean"); + val(timedMetric.getMean()); + key("max"); + val(timedMetric.getMax()); + } +} diff --git a/src/test/java/org/tests/query/finder/TestCustomerFinder.java b/src/test/java/org/tests/query/finder/TestCustomerFinder.java index b06bd8e69..7ab664a09 100644 --- a/src/test/java/org/tests/query/finder/TestCustomerFinder.java +++ b/src/test/java/org/tests/query/finder/TestCustomerFinder.java @@ -8,6 +8,7 @@ import io.ebean.meta.MetaQueryPlan; import io.ebean.meta.MetaTimedMetric; import io.ebean.meta.QueryPlanRequest; import io.ebean.meta.ServerMetrics; +import io.ebean.meta.SortMetric; import org.ebeantest.LoggedSqlCollector; import org.junit.Test; import org.tests.model.basic.Customer; @@ -138,20 +139,7 @@ public class TestCustomerFinder extends BaseTestCase { ResetBasicData.reset(); - resetAllMetrics(); - - List customers = Customer.find.all(); - assertThat(customers).isNotEmpty(); - - Customer customer = Customer.find.byId(1); - assertThat(customer).isNotNull(); - Customer.find.byId(2); - - Customer.find.namesStartingWith("F"); - Customer.find.byNameStatus("Rob", Customer.Status.ACTIVE); - Customer.find.totalCount(); - Customer.find.updateNames("Junk", 2000); - Customer.find.byId(3); + runQueries(); ServerMetrics metrics = server().getMetaInfoManager().collectMetrics(); @@ -172,7 +160,71 @@ public class TestCustomerFinder extends BaseTestCase { for (MetaQueryPlan plan : plans) { System.out.println(plan); } + } + @Test + public void test_metricsAsJson_withAll() { + + ResetBasicData.reset(); + + runQueries(); + + String metricsJson = server().getMetaInfoManager() + .collectMetricsAsJson() + .withHash(true) + .withLocation(true) + .withNewLine(true) + .withSql(true) + .withSort(SortMetric.TOTAL) + .json(); + + System.out.println(metricsJson); + assertThat(metricsJson).contains("\"name\":\"txn.main\""); + assertThat(metricsJson).contains("\"name\":\"Customer.findList\""); + assertThat(metricsJson).contains("\"loc\":\"CustomerFinder.byNameStatus(CustomerFinder.java:44)\""); + assertThat(metricsJson).contains("\"hash\":\"4d648ce0542aedfb042ad68746342730\""); + assertThat(metricsJson).contains("\"sql\":\"select t0.id, t0.status,"); + } + + @Test + public void test_metricsAsJson_minimal() { + + ResetBasicData.reset(); + + runQueries(); + + String metricsJson = server().getMetaInfoManager() + .collectMetricsAsJson() + .withHash(false) + .withLocation(false) + .withNewLine(false) + .withSql(false) + .withSort(null) + .json(); + + System.out.println(metricsJson); + assertThat(metricsJson).contains("\"name\":\"txn.main\""); + assertThat(metricsJson).contains("\"name\":\"Customer.findList\""); + assertThat(metricsJson).doesNotContain("\"loc\":"); + assertThat(metricsJson).doesNotContain("\"hash\":"); + assertThat(metricsJson).doesNotContain("\"sql\":"); + } + + private void runQueries() { + resetAllMetrics(); + + List customers = Customer.find.all(); + assertThat(customers).isNotEmpty(); + + Customer customer = Customer.find.byId(1); + assertThat(customer).isNotNull(); + Customer.find.byId(2); + + Customer.find.namesStartingWith("F"); + Customer.find.byNameStatus("Rob", Customer.Status.ACTIVE); + Customer.find.totalCount(); + Customer.find.updateNames("Junk", 2000); + Customer.find.byId(3); } }