[jOOQ/jOOQ#14652] Add a SQLExceptionLoggerListener that logs additional information to help debug constraint violations

This commit is contained in:
Lukas Eder 2023-02-16 10:01:53 +01:00
parent f9012935f5
commit b109886ec1
6 changed files with 288 additions and 7 deletions

View File

@ -342,6 +342,8 @@ public class Settings
@XmlElement(defaultValue = "true")
protected Boolean executeLogging = true;
@XmlElement(defaultValue = "true")
protected Boolean executeLoggingSQLExceptions = true;
@XmlElement(defaultValue = "true")
protected Boolean diagnosticsLogging = true;
@XmlElement(defaultValue = "DEFAULT")
@XmlSchemaType(name = "string")
@ -4171,7 +4173,7 @@ public class Settings
}
/**
* When set to true, this will add jOOQ's default logging ExecuteListeners.
* When set to true, this will add jOOQ's default {@link org.jooq.tools.LoggerListener} for debug logging. This is meant for use in development only.
*
* @return
* possible object is
@ -4194,6 +4196,30 @@ public class Settings
this.executeLogging = value;
}
/**
* [#14420] Whether constraint violations and other {@link java.sql.SQLException} should produce additional log information about the column name and data causing the problem. Unlike {@link #executeLogging}, this is meant for use in production as well as development. This feature is available only in commercial distributions.
*
* @return
* possible object is
* {@link Boolean }
*
*/
public Boolean isExecuteLoggingSQLExceptions() {
return executeLoggingSQLExceptions;
}
/**
* Sets the value of the executeLoggingSQLExceptions property.
*
* @param value
* allowed object is
* {@link Boolean }
*
*/
public void setExecuteLoggingSQLExceptions(Boolean value) {
this.executeLoggingSQLExceptions = value;
}
/**
* When set to true, this will add jOOQ's default logging DiagnosticsListeners.
*
@ -6777,6 +6803,11 @@ public class Settings
return this;
}
public Settings withExecuteLoggingSQLExceptions(Boolean value) {
setExecuteLoggingSQLExceptions(value);
return this;
}
public Settings withDiagnosticsLogging(Boolean value) {
setDiagnosticsLogging(value);
return this;
@ -7516,6 +7547,7 @@ public class Settings
builder.append("executeListenerStartInvocationOrder", executeListenerStartInvocationOrder);
builder.append("executeListenerEndInvocationOrder", executeListenerEndInvocationOrder);
builder.append("executeLogging", executeLogging);
builder.append("executeLoggingSQLExceptions", executeLoggingSQLExceptions);
builder.append("diagnosticsLogging", diagnosticsLogging);
builder.append("diagnosticsConnection", diagnosticsConnection);
builder.append("updateRecordVersion", updateRecordVersion);
@ -8831,6 +8863,15 @@ public class Settings
return false;
}
}
if (executeLoggingSQLExceptions == null) {
if (other.executeLoggingSQLExceptions!= null) {
return false;
}
} else {
if (!executeLoggingSQLExceptions.equals(other.executeLoggingSQLExceptions)) {
return false;
}
}
if (diagnosticsLogging == null) {
if (other.diagnosticsLogging!= null) {
return false;
@ -9684,6 +9725,7 @@ public class Settings
result = ((prime*result)+((executeListenerStartInvocationOrder == null)? 0 :executeListenerStartInvocationOrder.hashCode()));
result = ((prime*result)+((executeListenerEndInvocationOrder == null)? 0 :executeListenerEndInvocationOrder.hashCode()));
result = ((prime*result)+((executeLogging == null)? 0 :executeLogging.hashCode()));
result = ((prime*result)+((executeLoggingSQLExceptions == null)? 0 :executeLoggingSQLExceptions.hashCode()));
result = ((prime*result)+((diagnosticsLogging == null)? 0 :diagnosticsLogging.hashCode()));
result = ((prime*result)+((diagnosticsConnection == null)? 0 :diagnosticsConnection.hashCode()));
result = ((prime*result)+((updateRecordVersion == null)? 0 :updateRecordVersion.hashCode()));

View File

@ -59,9 +59,13 @@ import org.jooq.tools.LoggerListener;
* @author Lukas Eder
*/
final class ExecuteListeners implements ExecuteListener {
private static final ExecuteListener EMPTY_LISTENER = new DefaultExecuteListener();
private static final JooqLogger LOGGER_LISTENER_LOGGER = JooqLogger.getLogger(LoggerListener.class);
private final ExecuteListener[][] listeners;
// In some setups, these two events may get mixed up chronologically by the
@ -116,6 +120,14 @@ final class ExecuteListeners implements ExecuteListener {
(list = init(list)).add(new LoggerListener());
}
for (ExecuteListenerProvider provider : ctx.configuration().executeListenerProviders())
// Could be null after deserialisation

View File

@ -54,12 +54,14 @@ import static org.jooq.impl.DSL.select;
import static org.jooq.impl.Keywords.K_DEFAULT_VALUES;
import static org.jooq.impl.Keywords.K_VALUES;
import static org.jooq.impl.QueryPartCollectionView.wrap;
import static org.jooq.impl.Tools.EMPTY_FIELD;
import static org.jooq.impl.Tools.anyMatch;
import static org.jooq.impl.Tools.filter;
import static org.jooq.impl.Tools.flatten;
import static org.jooq.impl.Tools.flattenCollection;
import static org.jooq.impl.Tools.flattenFieldOrRows;
import static org.jooq.impl.Tools.lazy;
import static org.jooq.impl.Tools.row0;
import java.util.AbstractList;
import java.util.AbstractMap;
@ -86,6 +88,7 @@ import org.jooq.Param;
// ...
import org.jooq.Record;
import org.jooq.RenderContext.CastMode;
import org.jooq.Row;
import org.jooq.SQLDialect;
import org.jooq.Select;
import org.jooq.Table;
@ -531,9 +534,23 @@ final class FieldMapsForInsert extends AbstractQueryPart implements UNotYetImple
nextRow++;
}
final List<Map<Field<?>, Field<?>>> maps() {
initNextRow();
final List<Row> rows() {
List<Map<Field<?>, Field<?>>> maps = maps();
return new AbstractList<Row>() {
@Override
public Row get(int index) {
return row0(maps.get(index).values().toArray(EMPTY_FIELD));
}
@Override
public int size() {
return rows;
}
};
}
final List<Map<Field<?>, Field<?>>> maps() {
return new AbstractList<Map<Field<?>, Field<?>>>() {
@Override
public Map<Field<?>, Field<?>> get(int index) {
@ -548,8 +565,6 @@ final class FieldMapsForInsert extends AbstractQueryPart implements UNotYetImple
}
final Map<Field<?>, Field<?>> map(final int index) {
initNextRow();
return new AbstractMap<Field<?>, Field<?>>() {
transient Set<Entry<Field<?>, Field<?>>> entrySet;

View File

@ -1215,7 +1215,7 @@ implements
@Override
public final UnmodifiableList<? extends Row> $values() {
return QOM.unmodifiable(new ArrayList<>());
return QOM.unmodifiable(insertMaps.rows());
}
@Override

View File

@ -0,0 +1,208 @@
/*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* Other licenses:
* -----------------------------------------------------------------------------
* Commercial licenses for this work are available. These replace the above
* ASL 2.0 and offer limited warranties, support, maintenance, and commercial
* database integrations.
*
* For more information, please visit: https://www.jooq.org/legal/licensing
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*/
package org.jooq.impl;

View File

@ -1220,7 +1220,11 @@ case of which, this defaults to INLINED]]></jxb:javadoc></jxb:property></appinfo
</element>
<element name="executeLogging" type="boolean" minOccurs="0" maxOccurs="1" default="true">
<annotation><appinfo><jxb:property><jxb:javadoc><![CDATA[When set to true, this will add jOOQ's default logging ExecuteListeners.]]></jxb:javadoc></jxb:property></appinfo></annotation>
<annotation><appinfo><jxb:property><jxb:javadoc><![CDATA[When set to true, this will add jOOQ's default {@link org.jooq.tools.LoggerListener} for debug logging. This is meant for use in development only.]]></jxb:javadoc></jxb:property></appinfo></annotation>
</element>
<element name="executeLoggingSQLExceptions" type="boolean" minOccurs="0" maxOccurs="1" default="true">
<annotation><appinfo><jxb:property><jxb:javadoc><![CDATA[[#14420] Whether constraint violations and other {@link java.sql.SQLException} should produce additional log information about the column name and data causing the problem. Unlike {@link #executeLogging}, this is meant for use in production as well as development. This feature is available only in commercial distributions.]]></jxb:javadoc></jxb:property></appinfo></annotation>
</element>
<element name="diagnosticsLogging" type="boolean" minOccurs="0" maxOccurs="1" default="true">