net.sf.log4jdbc.log.log4j2.message.SqlMessage Maven / Gradle / Ivy
package net.sf.log4jdbc.log.log4j2.message;
import java.io.IOException;
import java.io.LineNumberReader;
import java.io.StringReader;
import java.util.StringTokenizer;
import java.util.regex.Pattern;
import net.sf.log4jdbc.Properties;
/**
* Parent class of all Message
s associated with log4jdbc log events,
* to perform common operations such as sql formatting.
*
* Subclasses must implement the abstract buildMessage()
method,
* that will then be called by the method getFormattedMessage()
of this class,
* to populate the message
attribute, only once.
* This way, messages are generated only once, and only when needed
* (avoid useless strings concatenations for instance).
*
* @author Frederic Bastian
* @version 1.0
* @since 1.0
*/
public abstract class SqlMessage
{
/**
* System dependent line separator.
*/
protected static String nl = System.getProperty("line.separator");
/**
* A boolean
to define whether debugInfo should be displayed.
* @see #getDebugInfo()
*/
private boolean isDebugEnabled;
/**
* A String
representing the final message
* built when needed using the attributes of this class.
* @see #buildMessage()
*/
private String message;
/**
* Default constructor.
*/
public SqlMessage()
{
this(false);
}
/**
* Constructor
* @param isDebugEnabled A boolean
used to set the isDebugEnabled
attribute
* @see #isDebugEnabled
*/
public SqlMessage(boolean isDebugEnabled)
{
this.setDebugEnabled(isDebugEnabled);
this.setMessage(null);
}
/**
* Populate the message
attribute.
* All subclasses can implement this method as they want,
* but the outcome must be to assign a String
not null
* to the message
attribute.
* This method is called only when this Message
is actually logged,
* avoiding useless concatenation costs, etc.
*
* @see #message
*/
protected abstract void buildMessage();
public String getFormattedMessage() {
if (this.getMessage() == null) {
this.buildMessage();
}
return this.getMessage();
}
public String getFormat() {
return this.getFormattedMessage();
}
public Object[] getParameters() {
return null;
}
/**
* Always return null
, no messages store a Throwable
in this project.
* @return always null
*/
public Throwable getThrowable() {
return null;
}
/**
* Break an SQL statement up into multiple lines in an attempt to make it
* more readable.
*
* @param sql SQL to break up.
* @return SQL broken up into multiple lines
* @author Arthur Blake
*/
protected String processSql(String sql)
{
if (sql==null) {
return null;
}
if (Properties.isSqlTrim()) {
sql = sql.trim();
}
StringBuilder output = new StringBuilder();
if (Properties.getDumpSqlMaxLineLength() <= 0) {
output.append(sql);
} else {
// insert line breaks into sql to make it more readable
StringTokenizer st = new StringTokenizer(sql);
String token;
int linelength = 0;
while (st.hasMoreElements()) {
token = (String) st.nextElement();
output.append(token);
linelength += token.length();
output.append(" ");
linelength++;
if (linelength > Properties.getDumpSqlMaxLineLength()) {
output.append(nl);
linelength = 0;
}
}
}
if (Properties.isDumpSqlAddSemicolon()) {
output.append(";");
}
String stringOutput = output.toString();
if (Properties.isTrimExtraBlankLinesInSql()) {
LineNumberReader lineReader = new LineNumberReader(new StringReader(stringOutput));
output = new StringBuilder();
int contiguousBlankLines = 0;
int lineCount = 0;
try {
while (true) {
String line = lineReader.readLine();
if (line==null) {
break;
}
//add a line return only if several lines;
//it is the responsibility of the caller to add a final line return if needed
if (lineCount > 0) {
output.append(nl);
}
// is this line blank?
if (line.trim().length() == 0) {
contiguousBlankLines ++;
// skip contiguous blank lines
if (contiguousBlankLines > 1) {
continue;
}
} else {
contiguousBlankLines = 0;
output.append(line);
}
lineCount++;
}
}
catch (IOException e)
{
// since we are reading from a buffer, this isn't likely to happen,
// but if it does we just ignore it and treat it like its the end of the stream
}
stringOutput = output.toString();
}
return stringOutput;
}
/**
* Get debugging info - the module and line number that called the logger
* version that prints the stack trace information from the point just before
* we got it (net.sf.log4jdbc)
*
* if the optional log4jdbc.debug.stack.prefix system property is defined then
* the last call point from an application is shown in the debug
* trace output, instead of the last direct caller into log4jdbc
*
* @return debugging info for whoever called into JDBC from within the application.
* @author Arthur Blake
*/
protected static String getDebugInfo()
{
Throwable t = new Throwable();
t.fillInStackTrace();
StackTraceElement[] stackTrace = t.getStackTrace();
if (stackTrace != null) {
String className;
StringBuffer dump = new StringBuffer();
/**
* The DumpFullDebugStackTrace option is useful in some situations when
* we want to see the full stack trace in the debug info- watch out
* though as this will make the logs HUGE!
*/
if (Properties.isDumpFullDebugStackTrace()) {
boolean first=true;
for (int i = 0; i < stackTrace.length; i++) {
className = stackTrace[i].getClassName();
if (!className.startsWith("net.sf.log4jdbc")) {
if (first) {
first = false;
} else {
dump.append(" ");
}
dump.append("at ");
dump.append(stackTrace[i]);
dump.append(nl);
}
}
} else {
dump.append(" ");
int firstLog4jdbcCall = 0;
int lastApplicationCall = 0;
for (int i = 0; i < stackTrace.length; i++) {
className = stackTrace[i].getClassName();
if (className.startsWith("net.sf.log4jdbc")) {
firstLog4jdbcCall = i;
} else if (Properties.isTraceFromApplication() &&
Pattern.matches(Properties.getDebugStackPrefix(), className)) {
lastApplicationCall = i;
break;
}
}
int j = lastApplicationCall;
if (j == 0) { // if app not found, then use whoever was the last guy that called a log4jdbc class.
if (stackTrace.length > 1 + firstLog4jdbcCall) {
j = 1 + firstLog4jdbcCall;
} else {
j = firstLog4jdbcCall;
}
}
dump.append(stackTrace[j].getClassName()).append(".").append(stackTrace[j].getMethodName()).append("(").
append(stackTrace[j].getFileName()).append(":").append(stackTrace[j].getLineNumber()).append(")");
}
return dump.toString();
}
return null;
}
/**
* @return the isDebugEnabled
*/
protected boolean isDebugEnabled() {
return this.isDebugEnabled;
}
/**
* @param isDebugEnabled the isDebugEnabled to set
*/
protected void setDebugEnabled(boolean isDebugEnabled) {
this.isDebugEnabled = isDebugEnabled;
}
/**
* @return the message
*/
protected String getMessage() {
return message;
}
/**
* @param message the message to set
*/
protected void setMessage(String message) {
this.message = message;
}
}