/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2017 Serge Rider (serge@jkiss.org)
*
* 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
*
* http://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.
*/
package org.jkiss.dbeaver.runtime.qm;
import org.eclipse.core.runtime.IStatus;
import org.jkiss.code.NotNull;
import org.jkiss.dbeaver.Log;
import org.jkiss.dbeaver.core.DBeaverCore;
import org.jkiss.dbeaver.model.preferences.DBPPreferenceListener;
import org.jkiss.dbeaver.model.qm.QMConstants;
import org.jkiss.dbeaver.model.qm.QMEventFilter;
import org.jkiss.dbeaver.model.qm.QMMetaEvent;
import org.jkiss.dbeaver.model.qm.QMMetaListener;
import org.jkiss.dbeaver.model.qm.meta.*;
import org.jkiss.dbeaver.utils.ContentUtils;
import org.jkiss.dbeaver.utils.GeneralUtils;
import org.jkiss.dbeaver.utils.RuntimeUtils;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
import java.util.Calendar;
import java.util.List;
/**
* Query manager log writer
*/
public class QMLogFileWriter implements QMMetaListener, DBPPreferenceListener {
private static final Log log = Log.getLog(QMLogFileWriter.class);
private File logFile;
private boolean enabled;
private Writer logWriter;
private QMEventFilter eventFilter;
private final String lineSeparator;
public QMLogFileWriter()
{
lineSeparator = GeneralUtils.getDefaultLineSeparator();
DBeaverCore.getGlobalPreferenceStore().addPropertyChangeListener(this);
initLogFile();
}
public void dispose()
{
DBeaverCore.getGlobalPreferenceStore().removePropertyChangeListener(this);
}
private synchronized void initLogFile()
{
enabled = DBeaverCore.getGlobalPreferenceStore().getBoolean(QMConstants.PROP_STORE_LOG_FILE);
if (enabled) {
String logFolder = DBeaverCore.getGlobalPreferenceStore().getString(QMConstants.PROP_LOG_DIRECTORY);
String logFileName = "dbeaver_sql_" + RuntimeUtils.getCurrentDate() + ".log";
logFile = new File(logFolder, logFileName);
try {
logWriter = new FileWriter(logFile, true);
} catch (IOException e) {
log.error("Can't open log writer", e);
}
} else {
if (logWriter != null) {
ContentUtils.close(logWriter);
logWriter = null;
}
}
eventFilter = new DefaultEventFilter();
}
@Override
public synchronized void metaInfoChanged(@NotNull List<QMMetaEvent> events)
{
if (!enabled || logWriter == null) {
return;
}
StringBuilder logBuffer = new StringBuilder(4000);
for (QMMetaEvent event : events) {
if (eventFilter.accept(event)) {
writeEvent(logBuffer, event);
}
}
try {
logWriter.write(logBuffer.toString());
logWriter.flush();
} catch (IOException e) {
log.warn("IO error writing QM log. Disable log file writer", e);
ContentUtils.close(logWriter);
logWriter = null;
}
}
@Override
public void preferenceChange(PreferenceChangeEvent event)
{
if (event.getProperty().startsWith(QMConstants.PROP_PREFIX)) {
initLogFile();
}
}
private void writeEvent(StringBuilder buffer, QMMetaEvent event)
{
QMMObject object = event.getObject();
QMMetaEvent.Action action = event.getAction();
// Filter
if (object instanceof QMMStatementInfo || object instanceof QMMTransactionSavepointInfo ||
(object instanceof QMMStatementExecuteInfo && action != QMMetaEvent.Action.END)) {
return;
}
// Entry
int severity = object instanceof QMMStatementExecuteInfo ? IStatus.INFO : IStatus.OK;
buffer.append("!ENTRY ");
appendEntryInfo(buffer, severity, object.getObjectId(), object.getOpenTime());
// Message
buffer.append("!MESSAGE ");
if (object instanceof QMMStatementExecuteInfo) {
QMMStatementExecuteInfo executeInfo = (QMMStatementExecuteInfo)object;
buffer.append(executeInfo.getQueryString());
buffer.append(lineSeparator);
buffer.append("!SUBENTRY 1 ");
int subSeverity = executeInfo.hasError() ? IStatus.ERROR : severity;
appendEntryInfo(buffer, subSeverity, executeInfo.getErrorCode(), object.getCloseTime());
buffer.append("!MESSAGE ");
if (executeInfo.hasError()) {
buffer.append(executeInfo.getErrorMessage());
} else {
buffer.append("SUCCESS [").append(executeInfo.getRowCount()).append("]");
}
} else if (object instanceof QMMTransactionInfo) {
QMMTransactionInfo transactionInfo = (QMMTransactionInfo)object;
if (transactionInfo.isCommitted()) {
buffer.append("COMMIT");
} else {
buffer.append("ROLLBACK");
}
} else if (object instanceof QMMSessionInfo) {
QMMSessionInfo sessionInfo = (QMMSessionInfo)object;
buffer.append(action).append(" SESSION [").append(sessionInfo.getContainerName()).append("]");
}
buffer.append(lineSeparator);
buffer.append(lineSeparator);
}
private void appendEntryInfo(StringBuilder buffer, int severity, long code, long time)
{
buffer.append(DBeaverCore.PLUGIN_ID).append(" ").append(severity).append(" ").append(code).append(" ");
appendDate(buffer, time);
buffer.append(lineSeparator);
}
private void appendDate(StringBuilder sb, long timestamp) {
Calendar c = Calendar.getInstance();
c.setTimeInMillis(timestamp);
appendPaddedInt(c.get(Calendar.YEAR), 4, sb).append('-');
appendPaddedInt(c.get(Calendar.MONTH) + 1, 2, sb).append('-');
appendPaddedInt(c.get(Calendar.DAY_OF_MONTH), 2, sb).append(' ');
appendPaddedInt(c.get(Calendar.HOUR_OF_DAY), 2, sb).append(':');
appendPaddedInt(c.get(Calendar.MINUTE), 2, sb).append(':');
appendPaddedInt(c.get(Calendar.SECOND), 2, sb).append('.');
appendPaddedInt(c.get(Calendar.MILLISECOND), 3, sb);
}
private StringBuilder appendPaddedInt(int value, int pad, StringBuilder buffer) {
pad = pad - 1;
if (pad == 0)
return buffer.append(Integer.toString(value));
int padding = (int) Math.pow(10, pad);
if (value >= padding)
return buffer.append(Integer.toString(value));
while (padding > value && padding > 1) {
buffer.append('0');
padding = padding / 10;
}
buffer.append(value);
return buffer;
}
}