/**
   * Determine if the given sql should be logged or not based on the various DumpSqlXXXXXX flags.
   *
   * @param sql SQL to test.
   * @return true if the SQL should be logged, false if not.
   */
  private boolean shouldSqlBeLogged(String sql) {
    if (sql == null) {
      return false;
    }
    sql = sql.trim();

    if (sql.length() < 6) {
      return false;
    }
    sql = sql.substring(0, 6).toLowerCase();
    return (Properties.isDumpSqlSelect() && "select".equals(sql))
        || (Properties.isDumpSqlInsert() && "insert".equals(sql))
        || (Properties.isDumpSqlUpdate() && "update".equals(sql))
        || (Properties.isDumpSqlDelete() && "delete".equals(sql))
        || (Properties.isDumpSqlCreate() && "create".equals(sql));
  }
 public void sqlOccurred(Spy spy, String methodCall, String sql) {
   if (!Properties.isDumpSqlFilteringOn() || shouldSqlBeLogged(sql)) {
     if (sqlOnlyLogger.isDebugEnabled()) {
       sqlOnlyLogger.debug(
           getDebugInfo() + nl + spy.getConnectionNumber() + ". " + processSql(sql));
     } else if (sqlOnlyLogger.isInfoEnabled()) {
       sqlOnlyLogger.info(processSql(sql));
     }
   }
 }
 /**
  * Special call that is called only for JDBC method calls that contain SQL.
  *
  * @param spy the Spy wrapping the class where the SQL occurred.
  * @param execTime how long it took the SQL to run, in milliseconds.
  * @param methodCall a description of the name and call parameters of the method that generated
  *     the SQL.
  * @param sql SQL that occurred.
  */
 public void sqlTimingOccurred(Spy spy, long execTime, String methodCall, String sql) {
   if (sqlTimingLogger.isErrorEnabled()
       && (!Properties.isDumpSqlFilteringOn() || shouldSqlBeLogged(sql))) {
     if (Properties.isSqlTimingErrorThresholdEnabled()
         && execTime >= Properties.getSqlTimingErrorThresholdMsec()) {
       sqlTimingLogger.error(
           buildSqlTimingDump(spy, execTime, methodCall, sql, sqlTimingLogger.isDebugEnabled()));
     } else if (sqlTimingLogger.isWarnEnabled()) {
       if (Properties.isSqlTimingWarnThresholdEnabled()
           && execTime >= Properties.getSqlTimingWarnThresholdMsec()) {
         sqlTimingLogger.warn(
             buildSqlTimingDump(spy, execTime, methodCall, sql, sqlTimingLogger.isDebugEnabled()));
       } else if (sqlTimingLogger.isDebugEnabled()) {
         sqlTimingLogger.debug(buildSqlTimingDump(spy, execTime, methodCall, sql, true));
       } else if (sqlTimingLogger.isInfoEnabled()) {
         sqlTimingLogger.info(buildSqlTimingDump(spy, execTime, methodCall, sql, false));
       }
     }
   }
 }
  /**
   * 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)
   *
   * <p>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.
   */
  private 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.
        {
          j = 1 + 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;
  }
  /**
   * 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
   */
  private 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;
      try {
        while (true) {
          String line = lineReader.readLine();
          if (line == null) {
            break;
          }

          // is this line blank?
          if (line.trim().length() == 0) {
            contiguousBlankLines++;
            // skip contiguous blank lines
            if (contiguousBlankLines > 1) {
              continue;
            }
          } else {
            contiguousBlankLines = 0;
            output.append(line);
          }
          output.append(nl);
        }
      } 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;
  }