#1237 - ENH: Add ProfileLocation - support for relating performance metrics for queries and transactions back to source line of code

This commit is contained in:
Rob Bygrave
2018-01-18 18:10:03 +13:00
parent 82f5c21d26
commit b9ae76d465
23 changed files with 359 additions and 11 deletions
@@ -5,6 +5,7 @@ import io.ebean.EbeanServer;
import io.ebean.ExpressionList;
import io.ebean.OrderBy;
import io.ebean.PersistenceContextScope;
import io.ebean.ProfileLocation;
import io.ebean.Query;
import io.ebean.bean.CallStack;
import io.ebean.bean.ObjectGraphNode;
@@ -173,6 +174,11 @@ public interface SpiQuery<T> extends Query<T>, TxnProfileEventCodes {
*/
short getProfileId();
/**
* Return the profile location for this query.
*/
ProfileLocation getProfileLocation();
/**
* Check for a single "equal to" expression for the Id.
*/
@@ -1,5 +1,6 @@
package io.ebeaninternal.api;
import io.ebean.ProfileLocation;
import io.ebean.Transaction;
import io.ebean.annotation.DocStoreMode;
import io.ebean.bean.PersistenceContext;
@@ -298,4 +299,14 @@ public interface SpiTransaction extends Transaction {
* Return the stream that profiling events are written to.
*/
ProfileStream profileStream();
/**
* Set the profile location for this transaction.
*/
void setProfileLocation(ProfileLocation profileLocation);
/**
* Return the profile location for this transaction.
*/
ProfileLocation getProfileLocation();
}
@@ -1,5 +1,6 @@
package io.ebeaninternal.api;
import io.ebean.ProfileLocation;
import io.ebean.TransactionCallback;
import io.ebean.annotation.DocStoreMode;
import io.ebean.annotation.PersistBatch;
@@ -54,6 +55,16 @@ abstract class SpiTransactionProxy implements SpiTransaction {
return transaction.profileStream();
}
@Override
public void setProfileLocation(ProfileLocation profileLocation) {
transaction.setProfileLocation(profileLocation);
}
@Override
public ProfileLocation getProfileLocation() {
return transaction.getProfileLocation();
}
@Override
public void setTenantId(Object tenantId) {
transaction.setTenantId(tenantId);
@@ -12,6 +12,7 @@ import io.ebean.FutureList;
import io.ebean.FutureRowCount;
import io.ebean.PagedList;
import io.ebean.PersistenceContextScope;
import io.ebean.ProfileLocation;
import io.ebean.Query;
import io.ebean.QueryIterator;
import io.ebean.SqlQuery;
@@ -834,6 +835,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
break;
default:
transaction = transactionManager.createTransaction(txScope.getProfileId(), true, txScope.getIsolationLevel());
initNewTransaction(transaction, txScope);
}
}
@@ -844,6 +846,14 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
return txnContainer;
}
private void initNewTransaction(SpiTransaction transaction, TxScope txScope) {
ProfileLocation profileLocation = txScope.getProfileLocation();
if (profileLocation != null) {
transaction.setProfileLocation(profileLocation);
}
}
private TxScope initTxScope(TxScope txScope) {
if (txScope == null) {
return new TxScope();
@@ -1,5 +1,6 @@
package io.ebeaninternal.server.core;
import io.ebean.ProfileLocation;
import io.ebean.TransactionCallback;
import io.ebean.annotation.DocStoreMode;
import io.ebean.annotation.PersistBatch;
@@ -416,4 +417,14 @@ class NoTransaction implements SpiTransaction {
public ProfileStream profileStream() {
return null;
}
@Override
public void setProfileLocation(ProfileLocation profileLocation) {
}
@Override
public ProfileLocation getProfileLocation() {
return null;
}
}
@@ -0,0 +1,24 @@
package io.ebeaninternal.server.profile;
import io.ebean.ProfileLocation;
/**
* Fixed / given location. Used internally for find by id and find all.
*/
class BasicProfileLocation implements ProfileLocation {
private final String location;
BasicProfileLocation(String location) {
this.location = location;
}
public String toString() {
return "location: " + location;
}
public String obtain() {
return location;
}
}
@@ -0,0 +1,55 @@
package io.ebeaninternal.server.profile;
import io.ebean.ProfileLocation;
/**
* Default profile location that uses stack trace.
*/
class DProfileLocation implements ProfileLocation {
private static final String IO_EBEAN = "io.ebean";
private static final String UNKNOWN = "unknown";
private String location;
private final int lineNumber;
DProfileLocation() {
this.lineNumber = 0;
}
DProfileLocation(int lineNumber) {
this.lineNumber = lineNumber;
}
public String toString() {
return "location: " + location;
}
public String obtain() {
// atomic assignment so happy with this
if (location == null) {
location = create();
}
return location;
}
private String create() {
StackTraceElement[] trace = Thread.currentThread().getStackTrace();
for (int i = 3; i < trace.length; i++) {
if (!trace[i].getClassName().startsWith(IO_EBEAN)) {
return withLineNumber(trace[i].toString());
}
}
return UNKNOWN;
}
private String withLineNumber(String traceLine) {
if (lineNumber == 0 || traceLine.contains(":")) {
return traceLine;
} else {
return traceLine.substring(0, traceLine.length() - 1) + ":" + lineNumber + ")";
}
}
}
@@ -0,0 +1,25 @@
package io.ebeaninternal.server.profile;
import io.ebean.ProfileLocation;
import io.ebean.service.SpiProfileLocationFactory;
/**
* Default implementation of the profile location factory.
*/
public class DProfileLocationFactory implements SpiProfileLocationFactory {
@Override
public ProfileLocation create() {
return new DProfileLocation();
}
@Override
public ProfileLocation create(int lineNumber) {
return new DProfileLocation(lineNumber);
}
@Override
public ProfileLocation create(String location) {
return new BasicProfileLocation(location);
}
}
@@ -14,6 +14,7 @@ import io.ebean.OrderBy;
import io.ebean.OrderBy.Property;
import io.ebean.PagedList;
import io.ebean.PersistenceContextScope;
import io.ebean.ProfileLocation;
import io.ebean.Query;
import io.ebean.QueryIterator;
import io.ebean.RawSql;
@@ -257,6 +258,8 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
*/
private short profileId;
private ProfileLocation profileLocation;
public DefaultOrmQuery(BeanDescriptor<T> desc, EbeanServer server, ExpressionFactory expressionFactory) {
this.beanDescriptor = desc;
this.beanType = desc.getBeanType();
@@ -304,6 +307,12 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
return this;
}
@Override
public Query<T> setProfileLocation(ProfileLocation profileLocation) {
this.profileLocation = profileLocation;
return this;
}
@Override
public boolean isAutoTunable() {
return nativeSql == null && beanDescriptor.isAutoTunable();
@@ -1732,4 +1741,9 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
public OrmUpdateProperties getUpdateProperties() {
return updateProperties;
}
@Override
public ProfileLocation getProfileLocation() {
return profileLocation;
}
}
@@ -1,6 +1,7 @@
package io.ebeaninternal.server.querydefn;
import io.ebean.ExpressionList;
import io.ebean.ProfileLocation;
import io.ebean.UpdateQuery;
/**
@@ -41,6 +42,12 @@ public class DefaultUpdateQuery<T> implements UpdateQuery<T> {
return this;
}
@Override
public UpdateQuery<T> setProfileLocation(ProfileLocation profileLocation) {
query.setProfileLocation(profileLocation);
return this;
}
@Override
public ExpressionList<T> where() {
return query.where();
@@ -1,5 +1,6 @@
package io.ebeaninternal.server.transaction;
import io.ebean.ProfileLocation;
import io.ebean.TransactionCallback;
import io.ebean.annotation.DocStoreMode;
import io.ebean.annotation.PersistBatch;
@@ -92,6 +93,16 @@ class ImplicitReadOnlyTransaction implements SpiTransaction, TxnProfileEventCode
return null;
}
@Override
public void setProfileLocation(ProfileLocation profileLocation) {
throw new IllegalStateException(notExpectedMessage);
}
@Override
public ProfileLocation getProfileLocation() {
return null;
}
@Override
public boolean isSkipCache() {
return false;
@@ -1,15 +1,16 @@
package io.ebeaninternal.server.transaction;
import io.ebean.ProfileLocation;
import io.ebean.TransactionCallback;
import io.ebean.annotation.DocStoreMode;
import io.ebean.bean.PersistenceContext;
import io.ebean.annotation.PersistBatch;
import io.ebean.bean.PersistenceContext;
import io.ebean.config.ServerConfig;
import io.ebean.config.dbplatform.DatabasePlatform.OnQueryOnly;
import io.ebean.event.changelog.BeanChange;
import io.ebean.event.changelog.ChangeSet;
import io.ebeaninternal.api.SpiTransaction;
import io.ebeaninternal.api.SpiProfileTransactionEvent;
import io.ebeaninternal.api.SpiTransaction;
import io.ebeaninternal.api.TransactionEvent;
import io.ebeaninternal.api.TxnProfileEventCodes;
import io.ebeaninternal.server.core.PersistDeferredRelationship;
@@ -173,6 +174,8 @@ public class JdbcTransaction implements SpiTransaction, TxnProfileEventCodes {
private final ProfileStream profileStream;
protected ProfileLocation profileLocation;
/**
* Create without ProfileStream option (no profiling).
*/
@@ -230,6 +233,16 @@ public class JdbcTransaction implements SpiTransaction, TxnProfileEventCodes {
return profileStream;
}
@Override
public void setProfileLocation(ProfileLocation profileLocation) {
this.profileLocation = profileLocation;
}
@Override
public ProfileLocation getProfileLocation() {
return profileLocation;
}
/**
* Overridden in AutoCommitJdbcTransaction as that expects to run/operate with autocommit true.
*/