/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.apache.openjpa.trader.service;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import org.apache.openjpa.conf.OpenJPAConfiguration;
import org.apache.openjpa.jdbc.conf.JDBCConfiguration;
import org.apache.openjpa.lib.conf.Configurable;
import org.apache.openjpa.lib.conf.Configuration;
import org.apache.openjpa.lib.log.Log;
import org.apache.openjpa.lib.log.LogFactory;
import org.apache.openjpa.trader.domain.LogStatement;
/**
* Specialized log to consolidate multiple logs into a single one.
* Selects only query related messages.
* <br>
* Designed to capture multiple logs used by different slices. It would have
* been more useful to capture the slice thread that executed the query, but
* that is not possible in all cases as the log statement is emitted from the
* kernel's main thread.
*
* @author Pinaki Poddar
*
*/
public class BufferedLog implements LogFactory, Configurable {
private int _history = 100;
private String _diagCtx;
private Configuration _conf;
static String[] SQL_MARKERS = {"INSERT INTO", "SELECT", "UPDATE", "DELETE"};
static String[] JPQL_MARKERS = {"Executing query: ["};
static List<String> CHANNELS = Arrays.asList(OpenJPAConfiguration.LOG_QUERY, JDBCConfiguration.LOG_SQL);
private static LinkedList<LogStatement> _messageModel;
static {
_messageModel = new LinkedList<LogStatement>();
}
public void setConfiguration(Configuration conf) {
_conf = conf;
}
public void endConfiguration() {
}
public void startConfiguration() {
}
public BufferedLog() {
super();
}
public void setDiagnosticContext(String ctx) {
System.err.println(ctx);
_diagCtx = ctx;
}
public Log getLog(String channel) {
return new ChannelLog(channel);
}
public void setHistory(int i) {
_history = Math.max(i, 1);
}
public int getHistory() {
return _history;
}
String getContext() {
if (_diagCtx != null)
return _diagCtx;
if (_conf == null || isEmpty(_conf.getId()))
return "";
else {
_diagCtx = _conf.getId();
return _conf.getId();
}
}
void addStatement(LogStatement stmt) {
_messageModel.addLast(stmt);
if (_messageModel.size() > _history) {
_messageModel.removeFirst();
}
}
boolean isEmpty(String s) {
return s == null || s.trim().length() == 0;
}
public List<LogStatement> get() {
List<LogStatement> result = new ArrayList<LogStatement>(_messageModel);
_messageModel.clear();
return result;
}
public class ChannelLog implements Log {
final String _channel;
final String _thread;
public ChannelLog(String channel) {
_channel = channel;
_thread = Thread.currentThread().getName();
}
public void error(Object o) {
createLogStatement("ERROR", o, null);
}
public void error(Object o, Throwable t) {
createLogStatement("ERROR", o, t);
}
public void fatal(Object o) {
createLogStatement("FATAL", o, null);
}
public void fatal(Object o, Throwable t) {
createLogStatement("FATAL", o, t);
}
public void info(Object o) {
createLogStatement("INFO", o, null);
}
public void info(Object o, Throwable t) {
createLogStatement("INFO", o, t);
}
public boolean isErrorEnabled() {
return true;
}
public boolean isFatalEnabled() {
return true;
}
public boolean isInfoEnabled() {
return CHANNELS.contains(_channel);
}
public boolean isTraceEnabled() {
return CHANNELS.contains(_channel);
}
public boolean isWarnEnabled() {
return true;
}
public void trace(Object o) {
createLogStatement("TRACE", o, null);
}
public void trace(Object o, Throwable t) {
createLogStatement("TRACE", o, t);
}
public void warn(Object o) {
createLogStatement("WARN", o, null);
}
public void warn(Object o, Throwable t) {
createLogStatement("WARN", o, t);
}
protected void createLogStatement(String level, Object message, Throwable t) {
String msg = message == null ? null : message.toString();
msg = extractQuery(msg);
if (msg == null) {
return;
}
addStatement(new LogStatement(level, getContext(),
_thread, _channel, msg));
if (t != null) {
StringWriter buffer = new StringWriter();
t.printStackTrace(new PrintWriter(buffer));
addStatement(new LogStatement(
level, getContext(),
Thread.currentThread().getName(), _channel,
buffer.toString()));
}
}
public String extractQuery(String msg) {
if (msg == null)
return null;
if ("openjpa.jdbc.SQL".equals(_channel)) {
return getQuery(msg, SQL_MARKERS, true);
} else if ("openjpa.Query".equals(_channel)) {
return getQuery(msg, JPQL_MARKERS, false);
}
return null;
}
private String getQuery(String message, String[] markers, boolean sql) {
int k = -1;
for (int i = 0; i < markers.length; i++) {
k = message.indexOf(markers[i]);
if (k != -1) {
int m = sql ? 0 : markers[i].length();
return message.substring(k+m).trim();
}
}
return null;
}
}
}