001/* 002 * Copyright (c) 2006-2011 Nuxeo SA (http://nuxeo.com/) and others. 003 * 004 * All rights reserved. This program and the accompanying materials 005 * are made available under the terms of the Eclipse Public License v1.0 006 * which accompanies this distribution, and is available at 007 * http://www.eclipse.org/legal/epl-v10.html 008 * 009 * Contributors: 010 * Florent Guillaume 011 */ 012package org.nuxeo.ecm.core.storage.sql.jdbc; 013 014import java.io.Serializable; 015import java.sql.ResultSet; 016import java.sql.SQLException; 017import java.util.ArrayList; 018import java.util.Calendar; 019import java.util.Collection; 020import java.util.Collections; 021import java.util.LinkedList; 022import java.util.List; 023import java.util.Map; 024import java.util.Map.Entry; 025import java.util.Set; 026 027import org.apache.commons.logging.Log; 028import org.apache.commons.logging.LogFactory; 029import org.nuxeo.common.utils.StringUtils; 030import org.nuxeo.ecm.core.api.model.Delta; 031import org.nuxeo.ecm.core.storage.sql.Row; 032import org.nuxeo.ecm.core.storage.sql.jdbc.db.Column; 033 034/** 035 * Logger used for debugging. 036 */ 037public class JDBCLogger { 038 039 public static final Log log = LogFactory.getLog(JDBCLogger.class); 040 041 public static final int DEBUG_MAX_STRING = 100; 042 043 public static final int DEBUG_MAX_ARRAY = 10; 044 045 public final String instance; 046 047 public JDBCLogger(String instance) { 048 this.instance = instance; 049 } 050 051 public boolean isLogEnabled() { 052 return log.isTraceEnabled(); 053 } 054 055 public String formatMessage(String message) { 056 return "(" + instance + ") SQL: " + message; 057 } 058 059 public void error(String message) { 060 log.error(formatMessage(message)); 061 } 062 063 public void error(String message, Throwable t) { 064 log.error(formatMessage(message), t); 065 } 066 067 public void warn(String message) { 068 log.warn(formatMessage(message)); 069 } 070 071 public void info(String message) { 072 log.info(formatMessage(message)); 073 } 074 075 public void log(String message) { 076 log.trace(formatMessage(message)); 077 } 078 079 public void logCount(int count) { 080 if (count > 0 && isLogEnabled()) { 081 log(" -> " + count + " row" + (count > 1 ? "s" : "")); 082 } 083 } 084 085 public void logCounts(int[] counts) { 086 if (!isLogEnabled()) { 087 return; 088 } 089 int count = 0; 090 for (int c : counts) { 091 count += c; 092 } 093 logCount(count); 094 } 095 096 public void logResultSet(ResultSet rs, List<Column> columns) throws SQLException { 097 List<String> res = new LinkedList<String>(); 098 int i = 0; 099 for (Column column : columns) { 100 i++; 101 Serializable v = column.getFromResultSet(rs, i); 102 res.add(column.getKey() + "=" + loggedValue(v)); 103 } 104 log(" -> " + StringUtils.join(res, ", ")); 105 } 106 107 public void logMap(Map<String, Serializable> map) throws SQLException { 108 List<String> res = new LinkedList<String>(); 109 for (Entry<String, Serializable> en : map.entrySet()) { 110 res.add(en.getKey() + "=" + loggedValue(en.getValue())); 111 } 112 log(" -> " + StringUtils.join(res, ", ")); 113 } 114 115 public void logIds(List<Serializable> ids, boolean countTotal, long totalSize) { 116 List<Serializable> debugIds = ids; 117 String end = ""; 118 if (ids.size() > DEBUG_MAX_ARRAY) { 119 debugIds = new ArrayList<Serializable>(DEBUG_MAX_ARRAY); 120 int i = 0; 121 for (Serializable id : ids) { 122 debugIds.add(id); 123 i++; 124 if (i == DEBUG_MAX_ARRAY) { 125 break; 126 } 127 } 128 end = "...(" + ids.size() + " ids)..."; 129 } 130 if (countTotal) { 131 end += " (total " + totalSize + ')'; 132 } 133 log(" -> " + debugIds + end); 134 } 135 136 public void logSQL(String sql, List<Column> columns, Row row) { 137 logSQL(sql, columns, row, Collections.<String> emptySet()); 138 } 139 140 public void logSQL(String sql, List<Column> columns, Row row, Set<String> deltas) { 141 List<Serializable> values = new ArrayList<Serializable>(columns.size()); 142 for (Column column : columns) { 143 String key = column.getKey(); 144 Serializable value = row.get(key); 145 if (deltas.contains(key)) { 146 value = ((Delta) value).getDeltaValue(); 147 } 148 values.add(value); 149 } 150 logSQL(sql, values); 151 } 152 153 // callable statement with one return value 154 private static final String CALLABLE_START = "{?="; 155 156 public void logSQL(String sql, Collection<Serializable> values) { 157 StringBuilder buf = new StringBuilder(); 158 int start = 0; 159 if (sql.startsWith(CALLABLE_START)) { 160 buf.append(CALLABLE_START); 161 start = CALLABLE_START.length(); 162 } 163 for (Serializable v : values) { 164 int index = sql.indexOf('?', start); 165 if (index == -1) { 166 // mismatch between number of ? and number of values 167 break; 168 } 169 buf.append(sql, start, index); 170 buf.append(loggedValue(v)); 171 start = index + 1; 172 } 173 buf.append(sql, start, sql.length()); 174 log(buf.toString()); 175 } 176 177 /** 178 * Returns a loggable value using pseudo-SQL syntax. 179 */ 180 @SuppressWarnings("boxing") 181 public static String loggedValue(Object value) { 182 if (value == null) { 183 return "NULL"; 184 } 185 if (value instanceof String) { 186 String v = (String) value; 187 if (v.length() > DEBUG_MAX_STRING) { 188 v = v.substring(0, DEBUG_MAX_STRING) + "...(" + v.length() + " chars)..."; 189 } 190 return "'" + v.replace("'", "''") + "'"; 191 } 192 if (value instanceof Calendar) { 193 Calendar cal = (Calendar) value; 194 char sign; 195 int offset = cal.getTimeZone().getOffset(cal.getTimeInMillis()) / 60000; 196 if (offset < 0) { 197 offset = -offset; 198 sign = '-'; 199 } else { 200 sign = '+'; 201 } 202 return String.format("TIMESTAMP '%04d-%02d-%02dT%02d:%02d:%02d.%03d%c%02d:%02d'", cal.get(Calendar.YEAR), // 203 cal.get(Calendar.MONTH) + 1, // 204 cal.get(Calendar.DAY_OF_MONTH), // 205 cal.get(Calendar.HOUR_OF_DAY), // 206 cal.get(Calendar.MINUTE), // 207 cal.get(Calendar.SECOND), // 208 cal.get(Calendar.MILLISECOND), // 209 sign, offset / 60, offset % 60); 210 } 211 if (value instanceof java.sql.Date) { 212 return "DATE '" + value.toString() + "'"; 213 } 214 if (value instanceof Object[]) { 215 Object[] v = (Object[]) value; 216 StringBuilder b = new StringBuilder(); 217 b.append('['); 218 for (int i = 0; i < v.length; i++) { 219 if (i > 0) { 220 b.append(','); 221 if (i > DEBUG_MAX_ARRAY) { 222 b.append("...(" + v.length + " items)..."); 223 break; 224 } 225 } 226 b.append(loggedValue(v[i])); 227 } 228 b.append(']'); 229 return b.toString(); 230 } 231 return value.toString(); 232 } 233}