Skip to content

Commit

Permalink
Implement QueryStackTraceLogger using StackWalker #659
Browse files Browse the repository at this point in the history
  • Loading branch information
marschall authored and vladmihalcea committed Oct 12, 2023
1 parent 4a3d394 commit f6673cd
Show file tree
Hide file tree
Showing 2 changed files with 80 additions and 86 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,7 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import java.util.concurrent.atomic.AtomicBoolean;

/**
* The {@link QueryStackTraceLogger} allows you to log the stack trace that
Expand All @@ -17,7 +15,7 @@
*/
public class QueryStackTraceLogger implements StatementInspector {

public static final String ORG_HIBERNATE ="org.hibernate";
public static final String ORG_HIBERNATE = "org.hibernate";

public static String TAB = "\t";
public static String NEW_LINE = System.getProperty("line.separator");
Expand All @@ -32,51 +30,50 @@ public QueryStackTraceLogger(String packageNamePrefix) {

@Override
public String inspect(String sql) {
LOGGER.debug(
"This SQL query: [\n\t{}\n]\nwas generated by Hibernate like this: [\n{}\n]",
sql,
String.join(
NEW_LINE,
stackTraceElementsUpTo(packageNamePrefix)
.stream()
.map(e -> TAB + e.toString())
.collect(Collectors.toList())
)
);
if (LOGGER.isDebugEnabled()) {
LOGGER.debug(stackTraceStringUpTo(packageNamePrefix, sql));
}
return null;
}

/**
* Filter the stack trace based up to the provided package name prefix
* Generate a formatted stack trace string based up to the provided package name prefix.
*
* @param endPackageNamePrefix package name to match the {@link StackTraceElement}
* @return the {@link StackTraceElement} up to the matching the provided package name
* @param query the query string to embed in the log message
* @return the stack trace {@link String} up to the matching the provided package name
*/
private List<StackTraceElement> stackTraceElementsUpTo(String endPackageNamePrefix) {
StackTraceElement[] stackTraceElements = Thread.currentThread().getStackTrace();
List<StackTraceElement> filteredStackTraceElements = new ArrayList<>();
boolean startPackageMatched = false;
boolean endPackageMatched = false;
for (StackTraceElement stackTraceElement : stackTraceElements) {
String className = stackTraceElement.getClassName();
if(!startPackageMatched) {
if(className.startsWith(ORG_HIBERNATE)) {
startPackageMatched = true;
} else {
continue;
}
}
if(!className.contains(endPackageNamePrefix)) {
if(!endPackageMatched) {
filteredStackTraceElements.add(stackTraceElement);
} else {
break;
}
} else if(!endPackageMatched) {
endPackageMatched = true;
filteredStackTraceElements.add(stackTraceElement);
}
}
return filteredStackTraceElements;
private static String stackTraceStringUpTo(String endPackageNamePrefix, String query) {
return StackWalker.getInstance().walk(stream -> {
StringBuilder buffer = new StringBuilder();
buffer.append("This SQL query: [\n\t");
buffer.append(query);
buffer.append("\n]\n");
buffer.append("was generated by Hibernate like this: [\n");
AtomicBoolean firstMatch = new AtomicBoolean(false);
stream
.skip(2L) // skip this and the calling method
.dropWhile(stackFrame -> // skip anything else until we end up in Hibernate
!stackFrame.getClassName().startsWith(ORG_HIBERNATE))
.takeWhile(stackFrame -> {
// take anything up to and including the first method in a class in endPackageNamePrefix
// would #startsWith be more appropriate?
if (stackFrame.getClassName().contains(endPackageNamePrefix)) {
if (firstMatch.get()) {
return false;
} else {
firstMatch.set(true);
return true;
}
} else {
return !firstMatch.get();
}
})
.forEach(stackFrame -> {
buffer.append(TAB).append(stackFrame.toStackTraceElement()).append(NEW_LINE);
});
buffer.append(']');
return buffer.toString();
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,7 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import java.util.concurrent.atomic.AtomicBoolean;

/**
* The {@link QueryStackTraceLogger} allows you to log the stack trace that
Expand All @@ -17,7 +15,7 @@
*/
public class QueryStackTraceLogger implements StatementInspector {

public static final String ORG_HIBERNATE ="org.hibernate";
public static final String ORG_HIBERNATE = "org.hibernate";

public static String TAB = "\t";
public static String NEW_LINE = System.getProperty("line.separator");
Expand All @@ -32,51 +30,50 @@ public QueryStackTraceLogger(String packageNamePrefix) {

@Override
public String inspect(String sql) {
LOGGER.debug(
"This SQL query: [\n\t{}\n]\nwas generated by Hibernate like this: [\n{}\n]",
sql,
String.join(
NEW_LINE,
stackTraceElementsUpTo(packageNamePrefix)
.stream()
.map(e -> TAB + e.toString())
.collect(Collectors.toList())
)
);
if (LOGGER.isDebugEnabled()) {
LOGGER.debug(stackTraceStringUpTo(packageNamePrefix, sql));
}
return null;
}

/**
* Filter the stack trace based up to the provided package name prefix
* Generate a formatted stack trace string based up to the provided package name prefix.
*
* @param endPackageNamePrefix package name to match the {@link StackTraceElement}
* @return the {@link StackTraceElement} up to the matching the provided package name
* @param query the query string to embed in the log message
* @return the stack trace {@link String} up to the matching the provided package name
*/
private List<StackTraceElement> stackTraceElementsUpTo(String endPackageNamePrefix) {
StackTraceElement[] stackTraceElements = Thread.currentThread().getStackTrace();
List<StackTraceElement> filteredStackTraceElements = new ArrayList<>();
boolean startPackageMatched = false;
boolean endPackageMatched = false;
for (StackTraceElement stackTraceElement : stackTraceElements) {
String className = stackTraceElement.getClassName();
if(!startPackageMatched) {
if(className.startsWith(ORG_HIBERNATE)) {
startPackageMatched = true;
} else {
continue;
}
}
if(!className.contains(endPackageNamePrefix)) {
if(!endPackageMatched) {
filteredStackTraceElements.add(stackTraceElement);
} else {
break;
}
} else if(!endPackageMatched) {
endPackageMatched = true;
filteredStackTraceElements.add(stackTraceElement);
}
}
return filteredStackTraceElements;
private static String stackTraceStringUpTo(String endPackageNamePrefix, String query) {
return StackWalker.getInstance().walk(stream -> {
StringBuilder buffer = new StringBuilder();
buffer.append("This SQL query: [\n\t");
buffer.append(query);
buffer.append("\n]\n");
buffer.append("was generated by Hibernate like this: [\n");
AtomicBoolean firstMatch = new AtomicBoolean(false);
stream
.skip(2L) // skip this and the calling method
.dropWhile(stackFrame -> // skip anything else until we end up in Hibernate
!stackFrame.getClassName().startsWith(ORG_HIBERNATE))
.takeWhile(stackFrame -> {
// take anything up to and including the first method in a class in endPackageNamePrefix
// would #startsWith be more appropriate?
if (stackFrame.getClassName().contains(endPackageNamePrefix)) {
if (firstMatch.get()) {
return false;
} else {
firstMatch.set(true);
return true;
}
} else {
return !firstMatch.get();
}
})
.forEach(stackFrame -> {
buffer.append(TAB).append(stackFrame.toStackTraceElement()).append(NEW_LINE);
});
buffer.append(']');
return buffer.toString();
});
}
}

0 comments on commit f6673cd

Please sign in to comment.