diff --git a/src/main/java/io/ebean/DtoQuery.java b/src/main/java/io/ebean/DtoQuery.java index 1603e1d10..61f7f3d86 100644 --- a/src/main/java/io/ebean/DtoQuery.java +++ b/src/main/java/io/ebean/DtoQuery.java @@ -109,6 +109,12 @@ public interface DtoQuery { */ DtoQuery setLabel(String label); + /** + * Set the profile location of this query. This is used to relate query execution metrics + * back to a location like a specific line of code. + */ + DtoQuery setProfileLocation(ProfileLocation profileLocation); + /** * Set a timeout on this query. *

diff --git a/src/main/java/io/ebean/meta/MetaOrmQueryMetric.java b/src/main/java/io/ebean/meta/MetaOrmQueryMetric.java index 7a74409f2..23525261d 100644 --- a/src/main/java/io/ebean/meta/MetaOrmQueryMetric.java +++ b/src/main/java/io/ebean/meta/MetaOrmQueryMetric.java @@ -1,7 +1,5 @@ package io.ebean.meta; -import io.ebean.ProfileLocation; - import java.util.List; /** @@ -9,11 +7,6 @@ import java.util.List; */ public interface MetaOrmQueryMetric extends MetaQueryMetric { - /** - * Return the profile location. - */ - ProfileLocation getProfileLocation(); - /** * Return true if this query plan was tuned by AutoTune. */ diff --git a/src/main/java/io/ebean/meta/MetaQueryMetric.java b/src/main/java/io/ebean/meta/MetaQueryMetric.java index c59142dc6..6816128db 100644 --- a/src/main/java/io/ebean/meta/MetaQueryMetric.java +++ b/src/main/java/io/ebean/meta/MetaQueryMetric.java @@ -1,5 +1,7 @@ package io.ebean.meta; +import io.ebean.ProfileLocation; + /** * Query execution metrics. */ @@ -15,6 +17,11 @@ public interface MetaQueryMetric extends MetaTimedMetric { */ String getLabel(); + /** + * Return the profile location. + */ + ProfileLocation getProfileLocation(); + /** * The actual SQL of the query. */ diff --git a/src/main/java/io/ebean/metric/MetricFactory.java b/src/main/java/io/ebean/metric/MetricFactory.java index f068089d4..241ca461c 100644 --- a/src/main/java/io/ebean/metric/MetricFactory.java +++ b/src/main/java/io/ebean/metric/MetricFactory.java @@ -1,5 +1,6 @@ package io.ebean.metric; +import io.ebean.ProfileLocation; import io.ebean.meta.MetricType; /** @@ -27,6 +28,6 @@ public interface MetricFactory { /** * Create a Timed metric. */ - QueryPlanMetric createQueryPlanMetric(MetricType metricType, Class type, String label, String sql); + QueryPlanMetric createQueryPlanMetric(MetricType metricType, Class type, String label, ProfileLocation profileLocation, String sql); } diff --git a/src/main/java/io/ebeaninternal/api/SpiDtoQuery.java b/src/main/java/io/ebeaninternal/api/SpiDtoQuery.java index 3ba088c58..dba0169a2 100644 --- a/src/main/java/io/ebeaninternal/api/SpiDtoQuery.java +++ b/src/main/java/io/ebeaninternal/api/SpiDtoQuery.java @@ -1,6 +1,7 @@ package io.ebeaninternal.api; import io.ebean.DtoQuery; +import io.ebean.ProfileLocation; import io.ebeaninternal.server.dto.DtoMappingRequest; import io.ebeaninternal.server.dto.DtoQueryPlan; @@ -37,9 +38,23 @@ public interface SpiDtoQuery extends DtoQuery, SpiSqlBinding { /** * Return the label for the query. */ - @Override String getLabel(); + /** + * Return the label with fallback to profile location label. + */ + String getPlanLabel(); + + /** + * Obtain the location if necessary. + */ + void obtainLocation(); + + /** + * Return the profile location. + */ + ProfileLocation getProfileLocation(); + /** * Return the associated DTO bean type. */ diff --git a/src/main/java/io/ebeaninternal/server/core/DtoQueryRequest.java b/src/main/java/io/ebeaninternal/server/core/DtoQueryRequest.java index d5ea92e5c..6fd5218df 100644 --- a/src/main/java/io/ebeaninternal/server/core/DtoQueryRequest.java +++ b/src/main/java/io/ebeaninternal/server/core/DtoQueryRequest.java @@ -37,6 +37,7 @@ public final class DtoQueryRequest extends AbstractSqlQueryRequest { super(server, query, null); this.queryEngine = engine; this.query = query; + query.obtainLocation(); } /** diff --git a/src/main/java/io/ebeaninternal/server/core/DumpMetrics.java b/src/main/java/io/ebeaninternal/server/core/DumpMetrics.java index f60f7657c..6857275f9 100644 --- a/src/main/java/io/ebeaninternal/server/core/DumpMetrics.java +++ b/src/main/java/io/ebeaninternal/server/core/DumpMetrics.java @@ -110,22 +110,12 @@ class DumpMetrics { StringBuilder sb = new StringBuilder(); - sb.append("query:").append(padName(metric.getName())).append(" "); - addCounters(metric, sb); - + appendQueryName(metric, sb); + appendCounters(metric, sb); if (dumpHash) { sb.append("\n hash:").append(metric.getQueryPlanHash()); } - - ProfileLocation profileLocation = metric.getProfileLocation(); - if (dumpLoc && profileLocation != null) { - sb.append("\n loc:").append(profileLocation.shortDescription()); - } - - if (dumpSql) { - sb.append("\n\n sql:").append(metric.getSql()).append("\n\n"); - } - + appendProfileAndSql(metric, sb); out(sb.toString()); } @@ -134,24 +124,36 @@ class DumpMetrics { StringBuilder sb = new StringBuilder(); + appendQueryName(metric, sb); + appendCounters(metric, sb); + appendProfileAndSql(metric, sb); + out(sb.toString()); + } + + private void appendQueryName(MetaQueryMetric metric, StringBuilder sb) { sb.append("query:").append(padName(metric.getName())).append(" "); - addCounters(metric, sb); + } + + private void appendProfileAndSql(MetaQueryMetric metric, StringBuilder sb) { + ProfileLocation profileLocation = metric.getProfileLocation(); + if (dumpLoc && profileLocation != null) { + sb.append("\n loc:").append(profileLocation.shortDescription()); + } if (dumpSql) { sb.append(" \n\n sql:").append(metric.getSql()).append("\n\n"); } - out(sb.toString()); } private void log(MetaTimedMetric metric) { StringBuilder sb = new StringBuilder(); sb.append(padNameTimed(metric.getName())).append(" "); - addCounters(metric, sb); + appendCounters(metric, sb); out(sb.toString()); } - private void addCounters(MetaTimedMetric timedMetric, StringBuilder sb) { + private void appendCounters(MetaTimedMetric timedMetric, StringBuilder sb) { sb.append(" count:").append(pad(timedMetric.getCount())) .append(" total:").append(pad(timedMetric.getTotal())) .append(" mean:").append(pad(timedMetric.getMean())) diff --git a/src/main/java/io/ebeaninternal/server/dto/DtoMappingRequest.java b/src/main/java/io/ebeaninternal/server/dto/DtoMappingRequest.java index de21fd1a9..588d7450c 100644 --- a/src/main/java/io/ebeaninternal/server/dto/DtoMappingRequest.java +++ b/src/main/java/io/ebeaninternal/server/dto/DtoMappingRequest.java @@ -1,5 +1,6 @@ package io.ebeaninternal.server.dto; +import io.ebean.ProfileLocation; import io.ebean.meta.MetricType; import io.ebean.metric.MetricFactory; import io.ebean.metric.QueryPlanMetric; @@ -14,6 +15,8 @@ public class DtoMappingRequest { private final String label; + private final ProfileLocation profileLocation; + private final String sql; private final boolean relaxedMode; @@ -22,7 +25,8 @@ public class DtoMappingRequest { public DtoMappingRequest(SpiDtoQuery query, String sql, DtoColumn[] columnMeta) { this.type = query.getType(); - this.label = query.getLabel(); + this.label = query.getPlanLabel(); + this.profileLocation = query.getProfileLocation(); this.sql = sql; this.relaxedMode = query.isRelaxedMode(); this.columnMeta = columnMeta; @@ -45,6 +49,6 @@ public class DtoMappingRequest { } public QueryPlanMetric createMetric() { - return MetricFactory.get().createQueryPlanMetric(MetricType.DTO, type, label, sql); + return MetricFactory.get().createQueryPlanMetric(MetricType.DTO, type, label, profileLocation, sql); } } diff --git a/src/main/java/io/ebeaninternal/server/profile/DMetricFactory.java b/src/main/java/io/ebeaninternal/server/profile/DMetricFactory.java index 404dc65c6..b333efd63 100644 --- a/src/main/java/io/ebeaninternal/server/profile/DMetricFactory.java +++ b/src/main/java/io/ebeaninternal/server/profile/DMetricFactory.java @@ -1,5 +1,6 @@ package io.ebeaninternal.server.profile; +import io.ebean.ProfileLocation; import io.ebean.meta.MetricType; import io.ebean.metric.MetricFactory; import io.ebean.metric.QueryPlanMetric; @@ -22,8 +23,8 @@ public class DMetricFactory implements MetricFactory { } @Override - public QueryPlanMetric createQueryPlanMetric(MetricType metricType, Class type, String label, String sql) { - return new DQueryPlanMetric(new DQueryPlanMeta(type, label, sql), new DTimedMetric(metricType, label)); + public QueryPlanMetric createQueryPlanMetric(MetricType metricType, Class type, String label, ProfileLocation profileLocation, String sql) { + return new DQueryPlanMetric(new DQueryPlanMeta(type, label, profileLocation, sql), new DTimedMetric(metricType, label)); } } diff --git a/src/main/java/io/ebeaninternal/server/profile/DProfileLocation.java b/src/main/java/io/ebeaninternal/server/profile/DProfileLocation.java index 03cf36070..c8156f581 100644 --- a/src/main/java/io/ebeaninternal/server/profile/DProfileLocation.java +++ b/src/main/java/io/ebeaninternal/server/profile/DProfileLocation.java @@ -42,11 +42,12 @@ class DProfileLocation implements ProfileLocation { @Override public String obtain() { - // atomic assignment so happy with this + // atomic assignments so happy enough with this (racing but atomic) if (location == null) { final String loc = create(); - shortDescription = shortDesc(loc); - label = UtilLocation.label(shortDescription); + final String shortDesc = shortDesc(loc); + label = UtilLocation.label(shortDesc); + shortDescription = shortDesc; location = loc; } return location; @@ -63,6 +64,7 @@ class DProfileLocation implements ProfileLocation { } private String create() { + // relatively expensive but we only do it once per profile location StackTraceElement[] trace = Thread.currentThread().getStackTrace(); for (int i = 3; i < trace.length; i++) { if (!trace[i].getClassName().startsWith(IO_EBEAN)) { diff --git a/src/main/java/io/ebeaninternal/server/profile/DQueryPlanMeta.java b/src/main/java/io/ebeaninternal/server/profile/DQueryPlanMeta.java index f6d1f46fd..bceed88c4 100644 --- a/src/main/java/io/ebeaninternal/server/profile/DQueryPlanMeta.java +++ b/src/main/java/io/ebeaninternal/server/profile/DQueryPlanMeta.java @@ -1,15 +1,19 @@ package io.ebeaninternal.server.profile; +import io.ebean.ProfileLocation; + class DQueryPlanMeta { private final Class type; private final String label; + private final ProfileLocation profileLocation; private final String name; private final String sql; - DQueryPlanMeta(Class type, String label, String sql) { + DQueryPlanMeta(Class type, String label, ProfileLocation profileLocation, String sql) { this.type = type; this.label = label; + this.profileLocation = profileLocation; this.sql = sql; String name = type.getSimpleName(); if (label != null) { @@ -30,6 +34,10 @@ class DQueryPlanMeta { return label; } + public ProfileLocation getProfileLocation() { + return profileLocation; + } + public String getSql() { return sql; } diff --git a/src/main/java/io/ebeaninternal/server/profile/DQueryPlanMetric.java b/src/main/java/io/ebeaninternal/server/profile/DQueryPlanMetric.java index 2490a1b5c..695fdcef2 100644 --- a/src/main/java/io/ebeaninternal/server/profile/DQueryPlanMetric.java +++ b/src/main/java/io/ebeaninternal/server/profile/DQueryPlanMetric.java @@ -1,5 +1,6 @@ package io.ebeaninternal.server.profile; +import io.ebean.ProfileLocation; import io.ebean.meta.MetaQueryMetric; import io.ebean.meta.MetricType; import io.ebean.meta.MetricVisitor; @@ -60,6 +61,11 @@ class DQueryPlanMetric implements QueryPlanMetric { return meta.getLabel(); } + @Override + public ProfileLocation getProfileLocation() { + return meta.getProfileLocation(); + } + @Override public String getSql() { return meta.getSql(); diff --git a/src/main/java/io/ebeaninternal/server/querydefn/DefaultDtoQuery.java b/src/main/java/io/ebeaninternal/server/querydefn/DefaultDtoQuery.java index 1f97fdb12..6433f83fb 100644 --- a/src/main/java/io/ebeaninternal/server/querydefn/DefaultDtoQuery.java +++ b/src/main/java/io/ebeaninternal/server/querydefn/DefaultDtoQuery.java @@ -1,6 +1,7 @@ package io.ebeaninternal.server.querydefn; import io.ebean.DtoQuery; +import io.ebean.ProfileLocation; import io.ebeaninternal.api.BindParams; import io.ebeaninternal.api.SpiDtoQuery; import io.ebeaninternal.api.SpiEbeanServer; @@ -39,6 +40,8 @@ public class DefaultDtoQuery implements SpiDtoQuery { private String label; + private ProfileLocation profileLocation; + /** * Bind parameters when using the query language. */ @@ -52,6 +55,7 @@ public class DefaultDtoQuery implements SpiDtoQuery { this.descriptor = descriptor; this.ormQuery = ormQuery; this.label = ormQuery.getLabel(); + this.profileLocation = ormQuery.getProfileLocation(); } /** @@ -166,6 +170,35 @@ public class DefaultDtoQuery implements SpiDtoQuery { return label; } + @Override + public String getPlanLabel() { + if (label != null) { + return label; + } + if (profileLocation != null) { + return profileLocation.label(); + } + return null; + } + + @Override + public void obtainLocation() { + if (profileLocation != null) { + profileLocation.obtain(); + } + } + + @Override + public DtoQuery setProfileLocation(ProfileLocation profileLocation) { + this.profileLocation = profileLocation; + return this; + } + + @Override + public ProfileLocation getProfileLocation() { + return profileLocation; + } + @Override public int getFirstRow() { return firstRow; diff --git a/src/test/java/io/ebean/DtoQueryFromOrmTest.java b/src/test/java/io/ebean/DtoQueryFromOrmTest.java index 793d69720..95e3830db 100644 --- a/src/test/java/io/ebean/DtoQueryFromOrmTest.java +++ b/src/test/java/io/ebean/DtoQueryFromOrmTest.java @@ -36,6 +36,8 @@ public class DtoQueryFromOrmTest extends BaseTestCase { } } + private static final ProfileLocation loc0 = ProfileLocation.create(); + @ForPlatform(Platform.H2) @Test public void testPlanHits() { @@ -48,10 +50,10 @@ public class DtoQueryFromOrmTest extends BaseTestCase { for (String val : prefix) { DB.find(Contact.class) + .setProfileLocation(loc0) .select("email, " + concat("lastName", ", ", "firstName") + " as fullName").where() .istartsWith(concat("lastName", ", ", "firstName"), val).orderBy().asc("lastName").setMaxRows(10) .asDto(ContactDto.class).setLabel("prefixLoop").findList(); - } ServerMetrics metrics = collectMetrics(); @@ -60,6 +62,7 @@ public class DtoQueryFromOrmTest extends BaseTestCase { for (MetaQueryMetric stat : stats) { long meanMicros = stat.getMean(); assertThat(meanMicros).isLessThan(900_000); + assertThat(stat.getProfileLocation()).isSameAs(loc0); } assertThat(stats).hasSize(1);