/******************************************************************************
* Product: Adempiere ERP & CRM Smart Business Solution *
* Copyright (C) 1999-2006 ComPiere, Inc. All Rights Reserved. *
* This program is free software; you can redistribute it and/or modify it *
* under the terms version 2 of the GNU General Public License as published *
* by the Free Software Foundation. This program is distributed in the hope *
* that it will be useful, but WITHOUT ANY WARRANTY; without even the implied *
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. *
* See the GNU General Public License for more details. *
* You should have received a copy of the GNU General Public License along *
* with this program; if not, write to the Free Software Foundation, Inc., *
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. *
* For the text or an alternative of this public license, you may reach us *
* ComPiere, Inc., 2620 Augustine Dr. #245, Santa Clara, CA 95054, USA *
* or via info@compiere.org or http://www.compiere.org/license.html *
*****************************************************************************/
package org.compiere.util;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.Properties;
import java.util.Vector;
import java.util.logging.Handler;
import java.util.logging.Level;
import java.util.logging.LogRecord;
import org.compiere.model.MIssue;
/**
* Handler that publish log record to the system error output stream
*
* @author Jorg Janke
* @version $Id: CLogErrorBuffer.java,v 1.3 2006/07/30 00:54:36 jjanke Exp $
*
* @author Teo Sarca, teo.sarca@gmail.com
*
BF [ 2973298 ] NPE on CLogErrorBuffer
*/
public class CLogErrorBuffer extends Handler
{
private static final String ISSUE_ERROR_KEY = "org.compiere.util.CLogErrorBuffer.issueError";
private static final String HISTORY_KEY = "org.compiere.util.CLogErrorBuffer.history";
private static final String ERRORS_KEY = "org.compiere.util.CLogErrorBuffer.errors";
private static final String LOGS_KEY = "org.compiere.util.CLogErrorBuffer.logs";
/**************************************************************************
* Constructor
*/
public CLogErrorBuffer ()
{
initialize();
} // CLogErrorBuffer
/** Error Buffer Size */
private static final int ERROR_SIZE = 20;
/** Log Size */
private static final int LOG_SIZE = 100;
/**
* Initialize
*/
private void initialize()
{
// Formatting
setFormatter(CLogFormatter.get());
// Default Level
super.setLevel(Level.INFO);
// Filter
setFilter(CLogFilter.get());
} // initialize
private boolean isAddLogRecordToContext()
{
return CLogMgt.getLevelAsInt() <= Level.INFO.intValue();
}
/**
* Issue Error
* @return true if issue error
*/
public boolean isIssueError()
{
Boolean b = (Boolean) Env.getCtx().get(ISSUE_ERROR_KEY);
if (b == null)
{
b = Boolean.TRUE;
setIssueError(b);
}
return b;
} // isIssueError
/**
* Set Issue Error
* @param issueError issue error
*/
public void setIssueError(boolean issueError)
{
Env.getCtx().put(ISSUE_ERROR_KEY, issueError);
} // setIssueError
/**
* Set Level.
* Ignore OFF - and higher then FINE
* @see java.util.logging.Handler#setLevel(java.util.logging.Level)
* @param newLevel ignored
* @throws java.lang.SecurityException
*/
@Override
public synchronized void setLevel (Level newLevel)
throws SecurityException
{
if (newLevel == null)
return;
if (newLevel == Level.OFF)
super.setLevel(Level.SEVERE);
else if (newLevel == Level.ALL || newLevel == Level.FINEST || newLevel == Level.FINER)
super.setLevel(Level.FINE);
else
super.setLevel(newLevel);
} // SetLevel
/**
* Publish
* @see java.util.logging.Handler#publish(java.util.logging.LogRecord)
* @param record log record
*/
@Override
public void publish (LogRecord record)
{
if (!isLoggable (record))
return;
checkContext();
@SuppressWarnings("unchecked")
LinkedList m_logs = (LinkedList) Env.getCtx().get(LOGS_KEY);
if (m_logs == null)
return;
// Output
if (isAddLogRecordToContext())
{
synchronized (m_logs)
{
if (m_logs.size() >= LOG_SIZE)
m_logs.removeFirst();
m_logs.add(record);
}
}
// We have an error
if (record.getLevel() == Level.SEVERE)
{
if (isAddLogRecordToContext())
{
@SuppressWarnings("unchecked")
LinkedList m_errors = (LinkedList) Env.getCtx().get(ERRORS_KEY);
synchronized (m_errors)
{
if (m_errors.size() >= ERROR_SIZE)
{
m_errors.removeFirst();
}
// Add Error
m_errors.add(record);
}
}
record.getSourceClassName(); // forces Class Name eval
// Create History
if (isAddLogRecordToContext())
{
@SuppressWarnings("unchecked")
LinkedList m_history = (LinkedList) Env.getCtx().get(HISTORY_KEY);
ArrayList history = new ArrayList();
synchronized (m_history)
{
if (m_history.size() >= ERROR_SIZE)
{
m_history.removeFirst();
}
for (int i = m_logs.size()-1; i >= 0; i--)
{
LogRecord rec = (LogRecord)m_logs.get(i);
if (rec.getLevel() == Level.SEVERE)
{
if (history.size() == 0)
history.add(rec);
else
break; // don't include previous error
}
else
{
history.add(rec);
if (history.size() > 10)
break; // no more then 10 history records
}
}
LogRecord[] historyArray = new LogRecord[history.size()];
int no = 0;
for (int i = history.size()-1; i >= 0; i--)
historyArray[no++] = (LogRecord)history.get(i);
m_history.add(historyArray);
}
}
// Issue Reporting
if (isIssueError())
{
String loggerName = record.getLoggerName(); // class name
if (loggerName == null)
loggerName = "";
//String className = record.getSourceClassName(); // physical class
String methodName = record.getSourceMethodName(); //
if (methodName == null)
methodName = "";
if (methodName != null
&& !methodName.equals("saveError")
&& !methodName.equals("get_Value")
&& !methodName.equals("dataSave")
&& loggerName.indexOf("Issue") == -1
&& loggerName.indexOf("CConnection") == -1
&& !loggerName.startsWith("com.zaxxer.hikari")
&& DB.isConnected()
)
{
// create issue on a separate thread in order to eventually
// wait until all model factories are initialized
new Thread(() -> {
try {
MIssue.create(record);
} catch (Throwable e) {
// failed to save exception to db, print to console
System.err.println(getFormatter().format(record));
setIssueError(false);
}
}).start();
}
else
{
//display to user if database connection not available
if (methodName != null
&& !methodName.equals("saveError")
&& !methodName.equals("get_Value")
&& !methodName.equals("dataSave")
&& loggerName.indexOf("Issue") == -1
&& loggerName.indexOf("CConnection") == -1
&& !loggerName.startsWith("com.zaxxer.hikari"))
{
System.err.println(getFormatter().format(record));
}
}
}
}
} // publish
/**
* Flush (NOP)
* @see java.util.logging.Handler#flush()
*/
@Override
public void flush ()
{
} // flush
/**
* Close
* @see java.util.logging.Handler#close()
* @throws SecurityException
*/
@Override
public void close () throws SecurityException
{
Env.getCtx().remove(LOGS_KEY);
Env.getCtx().remove(ERRORS_KEY);
Env.getCtx().remove(HISTORY_KEY);
} // close
/**
* Get ColumnNames of Log Entries
* @param ctx context (not used)
* @return string vector
*/
public Vector getColumnNames(Properties ctx)
{
Vector cn = new Vector();
cn.add(Msg.getMsg(ctx, "DateTime"));
cn.add(Msg.getMsg(ctx, "Level"));
//
cn.add(Msg.getMsg(ctx, "Class.Method"));
cn.add(Msg.getMsg(ctx, "Message"));
//2
cn.add(Msg.getMsg(ctx, "Parameter"));
cn.add(Msg.getMsg(ctx, "Trace"));
//
return cn;
} // getColumnNames
/**
* Get Log Data
* @param errorsOnly if true errors otherwise log
* @return data array
*/
public Vector> getLogData (boolean errorsOnly)
{
LogRecord[] records = getRecords(errorsOnly);
Vector> rows = new Vector>(records.length);
for (int i = 0; i < records.length; i++)
{
LogRecord record = records[i];
Vector