HHH-14217 Add syntax highlighting to the logged SQL
Using ANSI escape codes Must be explicitly enabled using hibernate.highlight_sql
This commit is contained in:
parent
9ac29ab4dc
commit
cf995a1571
|
@ -621,6 +621,9 @@ Write all SQL statements to the console. This is an alternative to setting the l
|
||||||
`*hibernate.format_sql*` (e.g. `true` or `false` (default value))::
|
`*hibernate.format_sql*` (e.g. `true` or `false` (default value))::
|
||||||
Pretty-print the SQL in the log and console.
|
Pretty-print the SQL in the log and console.
|
||||||
|
|
||||||
|
`*hibernate.highlight_sql*` (e.g. `true` or `false` (default value))::
|
||||||
|
Colorize the SQL in the console using ANSI escape codes.
|
||||||
|
|
||||||
`*hibernate.use_sql_comments*` (e.g. `true` or `false` (default value))::
|
`*hibernate.use_sql_comments*` (e.g. `true` or `false` (default value))::
|
||||||
If true, Hibernate generates comments inside the SQL, for easier debugging.
|
If true, Hibernate generates comments inside the SQL, for easier debugging.
|
||||||
|
|
||||||
|
|
|
@ -742,6 +742,11 @@ public interface AvailableSettings extends org.hibernate.jpa.AvailableSettings {
|
||||||
*/
|
*/
|
||||||
String FORMAT_SQL ="hibernate.format_sql";
|
String FORMAT_SQL ="hibernate.format_sql";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enable highlighting of SQL logged to the console using ANSI escape codes
|
||||||
|
*/
|
||||||
|
String HIGHLIGHT_SQL ="hibernate.highlight_sql";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add comments to the generated SQL
|
* Add comments to the generated SQL
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -20,6 +20,10 @@ public enum FormatStyle {
|
||||||
* Formatting for DDL (CREATE, ALTER, DROP, etc) statements
|
* Formatting for DDL (CREATE, ALTER, DROP, etc) statements
|
||||||
*/
|
*/
|
||||||
DDL( "ddl", DDLFormatterImpl.INSTANCE ),
|
DDL( "ddl", DDLFormatterImpl.INSTANCE ),
|
||||||
|
/**
|
||||||
|
* Syntax highlighting via ANSI escape codes
|
||||||
|
*/
|
||||||
|
HIGHLIGHT( "highlight", HighlightingFormatter.INSTANCE ),
|
||||||
/**
|
/**
|
||||||
* No formatting
|
* No formatting
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -0,0 +1,105 @@
|
||||||
|
/*
|
||||||
|
* Hibernate, Relational Persistence for Idiomatic Java
|
||||||
|
*
|
||||||
|
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
||||||
|
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
||||||
|
*/
|
||||||
|
package org.hibernate.engine.jdbc.internal;
|
||||||
|
|
||||||
|
import org.hibernate.engine.jdbc.env.spi.AnsiSqlKeywords;
|
||||||
|
import org.hibernate.internal.util.StringHelper;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.StringTokenizer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Performs basic syntax highlighting for SQL using ANSI escape codes.
|
||||||
|
*
|
||||||
|
* @author Gavin King
|
||||||
|
*/
|
||||||
|
public class HighlightingFormatter implements Formatter {
|
||||||
|
|
||||||
|
private static final Set<String> KEYWORDS = new HashSet<>( AnsiSqlKeywords.INSTANCE.sql2003() );
|
||||||
|
static {
|
||||||
|
// additional keywords not reserved by ANSI SQL 2003
|
||||||
|
KEYWORDS.addAll( Arrays.asList( "KEY", "SEQUENCE", "CASCADE", "INCREMENT" ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final Formatter INSTANCE =
|
||||||
|
new HighlightingFormatter(
|
||||||
|
"34", // blue
|
||||||
|
"36", // cyan
|
||||||
|
"32"
|
||||||
|
);
|
||||||
|
|
||||||
|
private static String escape(String code) {
|
||||||
|
return "\u001b[" + code + "m";
|
||||||
|
};
|
||||||
|
|
||||||
|
private final String keywordEscape;
|
||||||
|
private final String stringEscape;
|
||||||
|
private final String quotedEscape;
|
||||||
|
private final String normalEscape;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param keywordCode the ANSI escape code to use for highlighting SQL keywords
|
||||||
|
* @param stringCode the ANSI escape code to use for highlighting SQL strings
|
||||||
|
*/
|
||||||
|
public HighlightingFormatter(String keywordCode, String stringCode, String quotedCode) {
|
||||||
|
keywordEscape =escape(keywordCode);
|
||||||
|
stringEscape = escape(stringCode);
|
||||||
|
quotedEscape = escape(quotedCode);
|
||||||
|
normalEscape = escape("0");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String format(String sql) {
|
||||||
|
String symbolsAndWs = "=><!+-*/()',.|&`\"?" + StringHelper.WHITESPACE;
|
||||||
|
StringBuilder result = new StringBuilder();
|
||||||
|
boolean inString = false;
|
||||||
|
boolean inQuoted = false;
|
||||||
|
for (StringTokenizer tokenizer = new StringTokenizer( sql, symbolsAndWs, true );
|
||||||
|
tokenizer.hasMoreTokens(); ) {
|
||||||
|
String token = tokenizer.nextToken();
|
||||||
|
switch (token) {
|
||||||
|
case "\"":
|
||||||
|
case "`": // for MySQL
|
||||||
|
if (inString) {
|
||||||
|
result.append(token);
|
||||||
|
}
|
||||||
|
else if (inQuoted) {
|
||||||
|
inQuoted = false;
|
||||||
|
result.append(token).append(normalEscape);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
inQuoted = true;
|
||||||
|
result.append(quotedEscape).append(token);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "'":
|
||||||
|
if (inQuoted) {
|
||||||
|
result.append("'");
|
||||||
|
}
|
||||||
|
else if (inString) {
|
||||||
|
inString = false;
|
||||||
|
result.append("'").append(normalEscape);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
inString = true;
|
||||||
|
result.append(stringEscape).append("'");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
if ( KEYWORDS.contains( token.toUpperCase() ) ) {
|
||||||
|
result.append(keywordEscape).append(token).append(normalEscape);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
result.append(token);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result.toString();
|
||||||
|
}
|
||||||
|
}
|
|
@ -55,9 +55,10 @@ public class JdbcServicesImpl implements JdbcServices, ServiceRegistryAwareServi
|
||||||
|
|
||||||
final boolean showSQL = ConfigurationHelper.getBoolean( Environment.SHOW_SQL, configValues, false );
|
final boolean showSQL = ConfigurationHelper.getBoolean( Environment.SHOW_SQL, configValues, false );
|
||||||
final boolean formatSQL = ConfigurationHelper.getBoolean( Environment.FORMAT_SQL, configValues, false );
|
final boolean formatSQL = ConfigurationHelper.getBoolean( Environment.FORMAT_SQL, configValues, false );
|
||||||
|
final boolean highlightSQL = ConfigurationHelper.getBoolean( Environment.HIGHLIGHT_SQL, configValues, false );
|
||||||
final long logSlowQuery = ConfigurationHelper.getLong( Environment.LOG_SLOW_QUERY, configValues, 0 );
|
final long logSlowQuery = ConfigurationHelper.getLong( Environment.LOG_SLOW_QUERY, configValues, 0 );
|
||||||
|
|
||||||
this.sqlStatementLogger = new SqlStatementLogger( showSQL, formatSQL, logSlowQuery );
|
this.sqlStatementLogger = new SqlStatementLogger( showSQL, formatSQL, highlightSQL, logSlowQuery );
|
||||||
|
|
||||||
resultSetWrapper = new ResultSetWrapperImpl( serviceRegistry );
|
resultSetWrapper = new ResultSetWrapperImpl( serviceRegistry );
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,16 +6,15 @@
|
||||||
*/
|
*/
|
||||||
package org.hibernate.engine.jdbc.spi;
|
package org.hibernate.engine.jdbc.spi;
|
||||||
|
|
||||||
import java.sql.Statement;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
|
|
||||||
import org.hibernate.engine.jdbc.internal.FormatStyle;
|
import org.hibernate.engine.jdbc.internal.FormatStyle;
|
||||||
import org.hibernate.engine.jdbc.internal.Formatter;
|
import org.hibernate.engine.jdbc.internal.Formatter;
|
||||||
import org.hibernate.internal.CoreLogging;
|
import org.hibernate.internal.CoreLogging;
|
||||||
import org.hibernate.internal.build.AllowSysOut;
|
import org.hibernate.internal.build.AllowSysOut;
|
||||||
|
|
||||||
import org.jboss.logging.Logger;
|
import org.jboss.logging.Logger;
|
||||||
|
|
||||||
|
import java.sql.Statement;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Centralize logging for SQL statements.
|
* Centralize logging for SQL statements.
|
||||||
*
|
*
|
||||||
|
@ -27,6 +26,7 @@ public class SqlStatementLogger {
|
||||||
|
|
||||||
private boolean logToStdout;
|
private boolean logToStdout;
|
||||||
private boolean format;
|
private boolean format;
|
||||||
|
private final boolean highlight;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Configuration value that indicates slow query. (In milliseconds) 0 - disabled.
|
* Configuration value that indicates slow query. (In milliseconds) 0 - disabled.
|
||||||
|
@ -37,29 +37,42 @@ public class SqlStatementLogger {
|
||||||
* Constructs a new SqlStatementLogger instance.
|
* Constructs a new SqlStatementLogger instance.
|
||||||
*/
|
*/
|
||||||
public SqlStatementLogger() {
|
public SqlStatementLogger() {
|
||||||
this( false, false );
|
this( false, false, false );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs a new SqlStatementLogger instance.
|
* Constructs a new SqlStatementLogger instance.
|
||||||
*
|
*
|
||||||
* @param logToStdout Should we log to STDOUT in addition to our internal logger.
|
* @param logToStdout Should we log to STDOUT in addition to our internal logger.
|
||||||
* @param format Should we format the statements prior to logging
|
* @param format Should we format the statements in the console and log
|
||||||
*/
|
*/
|
||||||
public SqlStatementLogger(boolean logToStdout, boolean format) {
|
public SqlStatementLogger(boolean logToStdout, boolean format) {
|
||||||
this( logToStdout, format, 0 );
|
this( logToStdout, format, false );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs a new SqlStatementLogger instance.
|
* Constructs a new SqlStatementLogger instance.
|
||||||
*
|
*
|
||||||
* @param logToStdout Should we log to STDOUT in addition to our internal logger.
|
* @param logToStdout Should we log to STDOUT in addition to our internal logger.
|
||||||
* @param format Should we format the statements prior to logging
|
* @param format Should we format the statements in the console and log
|
||||||
|
* @param highlight Should we highlight the statements in the console
|
||||||
|
*/
|
||||||
|
public SqlStatementLogger(boolean logToStdout, boolean format, boolean highlight) {
|
||||||
|
this( logToStdout, format, highlight, 0 );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a new SqlStatementLogger instance.
|
||||||
|
*
|
||||||
|
* @param logToStdout Should we log to STDOUT in addition to our internal logger.
|
||||||
|
* @param format Should we format the statements in the console and log
|
||||||
|
* @param highlight Should we highlight the statements in the console
|
||||||
* @param logSlowQuery Should we logs query which executed slower than specified milliseconds. 0 - disabled.
|
* @param logSlowQuery Should we logs query which executed slower than specified milliseconds. 0 - disabled.
|
||||||
*/
|
*/
|
||||||
public SqlStatementLogger(boolean logToStdout, boolean format, long logSlowQuery) {
|
public SqlStatementLogger(boolean logToStdout, boolean format, boolean highlight, long logSlowQuery) {
|
||||||
this.logToStdout = logToStdout;
|
this.logToStdout = logToStdout;
|
||||||
this.format = format;
|
this.format = format;
|
||||||
|
this.highlight = highlight;
|
||||||
this.logSlowQuery = logSlowQuery;
|
this.logSlowQuery = logSlowQuery;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -120,14 +133,18 @@ public class SqlStatementLogger {
|
||||||
*/
|
*/
|
||||||
@AllowSysOut
|
@AllowSysOut
|
||||||
public void logStatement(String statement, Formatter formatter) {
|
public void logStatement(String statement, Formatter formatter) {
|
||||||
if ( format ) {
|
|
||||||
if ( logToStdout || LOG.isDebugEnabled() ) {
|
if ( logToStdout || LOG.isDebugEnabled() ) {
|
||||||
|
if ( format ) {
|
||||||
statement = formatter.format( statement );
|
statement = formatter.format( statement );
|
||||||
}
|
}
|
||||||
|
if ( highlight ) {
|
||||||
|
statement = FormatStyle.HIGHLIGHT.getFormatter().format( statement );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
LOG.debug( statement );
|
LOG.debug( statement );
|
||||||
if ( logToStdout ) {
|
if ( logToStdout ) {
|
||||||
System.out.println( "Hibernate: " + statement );
|
String prefix = highlight ? "\u001b[35m[Hibernate]\u001b[0m " : "Hibernate: ";
|
||||||
|
System.out.println( prefix + statement );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -57,7 +57,7 @@ public class BasicTestingJdbcServiceImpl implements JdbcServices, ServiceRegistr
|
||||||
public void prepare(boolean allowAggressiveRelease) throws SQLException {
|
public void prepare(boolean allowAggressiveRelease) throws SQLException {
|
||||||
dialect = ConnectionProviderBuilder.getCorrespondingDialect();
|
dialect = ConnectionProviderBuilder.getCorrespondingDialect();
|
||||||
connectionProvider = ConnectionProviderBuilder.buildConnectionProvider( allowAggressiveRelease );
|
connectionProvider = ConnectionProviderBuilder.buildConnectionProvider( allowAggressiveRelease );
|
||||||
sqlStatementLogger = new SqlStatementLogger( true, false );
|
sqlStatementLogger = new SqlStatementLogger( true, false, false );
|
||||||
|
|
||||||
Connection jdbcConnection = connectionProvider.getConnection();
|
Connection jdbcConnection = connectionProvider.getConnection();
|
||||||
try {
|
try {
|
||||||
|
|
Loading…
Reference in New Issue