blob: 0e73cb6203c78203178c7ad3d859d92f82788eda [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 1997, 2018 by ProSyst Software GmbH
* http://www.prosyst.com
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* ProSyst Software GmbH - initial API and implementation
*******************************************************************************/
package org.eclipse.equinox.internal.util.ref;
import java.io.*;
import java.util.Vector;
import org.eclipse.equinox.internal.util.UtilActivator;
import org.eclipse.equinox.internal.util.event.Queue;
import org.eclipse.equinox.internal.util.hash.HashIntObjNS;
import org.eclipse.equinox.internal.util.security.PrivilegedRunner;
import org.eclipse.equinox.internal.util.security.SecurityUtil;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceReference;
import org.osgi.service.log.LogService;
import org.osgi.util.tracker.ServiceTracker;
import org.osgi.util.tracker.ServiceTrackerCustomizer;
/**
* Log class is responsible for forwarding bundles's messages to the LogService
* or console output.
*
* @author Pavlin Dobrev
* @version 1.0
*/
public class Log implements LogInterface, ServiceTrackerCustomizer<LogService, LogService>, PrivilegedRunner.PrivilegedDispatcher {
/**
* Flag, pointing if printingOnConsole is allowed
*
* @deprecated since osgilib verion 1.3.9 use set/get PrintOnConsole
*/
@Deprecated
public boolean printOnConsole = false;
/**
* Flag, pointing whether printing on console should be done if log service
* is not available
*/
public boolean autoPrintOnConsole = false;
/**
* Flag, pointing if logging debuging info is allowed
*
* @deprecated since osgilib verion 1.3.9 use set/get Debug
*/
@Deprecated
public boolean debug = false;
/**
* Flag, pointing whether printing on console should be done for errors
* (exceptions) and warnings
*/
private boolean logErrorLevel = false;
private ServiceTracker<LogService, LogService> logTracker;
protected static final SecurityUtil securityUtil = new SecurityUtil();
private boolean isClosed = false;
private long bundleId;
/** BundleContext to get LogService and service owner of Log object BundleId */
protected BundleContext bc;
private static Vector<Log> logs = new Vector<>();
private static Log listener;
public Log(BundleContext bc) {
this(bc, true);
}
/**
* Constructs a log object, used for logging information in the LogService.
* If the LogService is unavailble, the information is printed on the
* server's console.
*
* @param bc
* BundleContext, necessary to get LogService, in case it is
* started.
*/
public Log(BundleContext bc, boolean initDebug) {
if (initDebug) {
debug = securityUtil.getBooleanProperty("equinox.util.ref.log.debug");
logErrorLevel = securityUtil.getBooleanProperty("equinox.log.errorlevel");
autoPrintOnConsole = securityUtil.getBooleanProperty("equinox.util.ref.log.autoPrintOnConsole");
printOnConsole = securityUtil.getBooleanProperty("equinox.util.ref.log.printOnConsole");
}
if (bc != null) {
this.bc = bc;
bundleId = bc.getBundle().getBundleId();
if (UtilActivator.startup && UtilActivator.points != null)
UtilActivator.points[0] = System.currentTimeMillis();
initSysServices();
if (UtilActivator.startup && UtilActivator.points != null)
UtilActivator.points[1] = System.currentTimeMillis();
synchronized (logs) {
if (listener == null)
initListener();
logs.addElement(this);
}
if (UtilActivator.startup && UtilActivator.points != null)
UtilActivator.points[2] = System.currentTimeMillis();
} else
printOnConsole = true;
}
private void initListener() {
try {
securityUtil.doPrivileged(this, OPEN_TYPE, null);
} catch (IllegalStateException ise) {
/* must be rethrown */
throw ise;
} catch (Throwable ignore) {
ignore.printStackTrace();
}
}
void initListener0() {
synchronized (logs) {
initListenerNS();
}
}
void initListenerNS() {
logTracker = new ServiceTracker<>(bc, org.osgi.service.log.LogService.class, this);
logTracker.open();
listener = this;
}
/**
* Logs error messages. If <code>printOnConsole</code> is true, or if the
* <code>LogService</code> is unavailable, log info is printed on console.
*
* @param str
* Message description of the error.
* @param ex
* Throwable object, containing the stack trace; may be null.
*/
@Override
public void error(String str, Throwable ex) {
if (isClosed)
return;
boolean logResult = logMessage(LogService.LOG_ERROR, str, ex);
if (printOnConsole || (!logResult && autoPrintOnConsole) || logErrorLevel) {
dumpOnConsole("ERROR ", str, bundleId, ex);
}
}
/**
* Logs error messages. If <code>printOnConsole</code> is true, or if the
* <code>LogService</code> is unavailable, log info is printed on console.
*
* @param moduleID
* @param msgID
* @param msg
* Message description of the error.
* @param t
* Throwable object, containing the stack trace; may be null.
* @param synch
* Weather tracer to work synchronious or not
*/
public void error(int moduleID, int msgID, String msg, Throwable t, boolean synch) {
if (isClosed)
return;
boolean logResult = true;
if (msg != null || t != null) {
logResult = logMessage(LogService.LOG_ERROR, msg, t);
}
if (printOnConsole || (!logResult && autoPrintOnConsole) || logErrorLevel) {
dumpOnConsole(buildDebugString(moduleID, msgID, msg, "ERROR " + bundleId + " "), t);
}
}
/**
* Logs warning messages. If <code>printOnConsole</code> is true, or if
* the <code>LogService</code> is unavailable, log info is printed on
* console.
*
* @param str
* Message description of the error.
* @param ex
* Throwable object, containing the stack trace; may be null.
*/
@Override
public void warning(String str, Throwable ex) {
if (isClosed)
return;
boolean logResult = logMessage(LogService.LOG_WARNING, str, ex);
if (printOnConsole || (!logResult && autoPrintOnConsole) || logErrorLevel) {
dumpOnConsole("WARNING ", str, bundleId, ex);
}
}
/**
* Logs warning messages. If <code>printOnConsole</code> is true, or if
* the <code>LogService</code> is unavailable, log info is printed on
* console.
*
* @param moduleID
* @param msgID
* @param msg
* Message description of the error.
* @param t
* Throwable object, containing the stack trace; may be null.
* @param synch
* Weather tracer to work synchronious or not
*/
public void warning(int moduleID, int msgID, String msg, Throwable t, boolean synch) {
if (isClosed)
return;
boolean logResult = true;
if (msg != null || t != null) {
logResult = logMessage(LogService.LOG_WARNING, msg, t);
}
if (printOnConsole || (!logResult && autoPrintOnConsole) || logErrorLevel) {
dumpOnConsole(buildDebugString(moduleID, msgID, msg, "WARNING " + bundleId + " "), t);
}
}
/**
* Logs info messages. If <code>printOnConsole</code> is true, or if the
* <code>LogService</code> is unavailable, message is printed on console.
*
* @param str
* Message to be logged.
*/
@Override
public void info(String str) {
if (isClosed)
return;
boolean logResult = logMessage(LogService.LOG_INFO, str, null);
if (printOnConsole || (!logResult && autoPrintOnConsole)) {
dumpOnConsole("INFO ", str, bundleId, null);
}
}
/**
* Logs info messages. If <code>printOnConsole</code> is true, or if the
* <code>LogService</code> is unavailable, log info is printed on console.
*
* @param moduleID
* @param msgID
* @param msg
* Message description of the error.
* @param synch
* Weather tracer to work synchronious or not
*/
public void info(int moduleID, int msgID, String msg, boolean synch) {
if (isClosed)
return;
boolean logResult = true;
if (msg != null) {
logResult = logMessage(LogService.LOG_INFO, msg, null);
}
if (printOnConsole || (!logResult && autoPrintOnConsole)) {
dumpOnConsole(buildDebugString(moduleID, msgID, msg, "INFO " + bundleId + " "), null);
}
}
/**
* Logs debug information if <code>debug</code> flag is true. If
* LogService is unaccessible or printOnConsole flag is true, log info is
* printed on console.
*
* @param str
* Message description.
* @param ex
* Throwable object, containing the stack trace; may be null.
*/
@Override
public void debug(String str, Throwable ex) {
if (!debug || isClosed)
return;
boolean logResult = logMessage(LogService.LOG_DEBUG, str, ex);
if (printOnConsole || (!logResult && autoPrintOnConsole)) {
dumpOnConsole("DEBUG ", str, bundleId, ex);
}
}
/**
* Logs debug messages. If <code>printOnConsole</code> is true, or if the
* <code>LogService</code> is unavailable, log info is printed on console.
*
* @param moduleID
* @param msgID
* @param msg
* Message description of the error.
* @param t
* Throwable object, containing the stack trace; may be null.
* @param synch
* Indicates whether tracer should log the message synchronously
* or not
*/
public void debug(int moduleID, int msgID, String msg, Throwable t, boolean synch) {
debug(moduleID, msgID, msg, t, synch, false, false, true);
}
/**
* Logs debug messages. If <code>printOnConsole</code> is true, or if the
* <code>LogService</code> is unavailable, log info is printed on console.
*
* @param moduleID
* @param msgID
* @param msg
* Message description of the error.
* @param t
* Throwable object, containing the stack trace; may be null.
* @param synch
* Indicates whether tracer should log the message synchronously
* or not
* @param measurement
* Indicates whether the message is a measurement or not
*/
public void debug(int moduleID, int msgID, String msg, Throwable t, boolean synch, boolean measurement) {
debug(moduleID, msgID, msg, t, synch, measurement, false, true);
}
/**
* Logs debug messages. If <code>printOnConsole</code> is true, or if the
* <code>LogService</code> is unavailable, log info is printed on console.
*
* @param moduleID
* @param msgID
* @param msg
* Message description of the error.
* @param t
* Throwable object, containing the stack trace; may be null.
* @param synch
* Indicates whether tracer should log the message synchronously
* or not
* @param measurement
* Indicates whether the message is a measurement or not
* @param display
* Indicates whether the message should be displayed in native
* GUI
*/
public void debug(int moduleID, int msgID, String msg, Throwable t, boolean synch, boolean measurement, boolean display) {
debug(moduleID, msgID, msg, t, synch, measurement, display, true);
}
/**
* Logs debug messages. If <code>printOnConsole</code> is true, or if the
* <code>LogService</code> is unavailable, log info is printed on console.
*
* @param moduleID
* @param msgID
* @param msg
* Message description of the error.
* @param t
* Throwable object, containing the stack trace; may be null.
* @param synch
* Indicates whether tracer should log the message synchronously
* or not
* @param measurement
* Indicates whether the message is a measurement or not
* @param display
* Indicates whether the message should be displayed in native
* GUI
* @param logInFile
* Indicates whether the message should be logged into the log
* file or not. Used for measurements' logs.
*/
public void debug(int moduleID, int msgID, String msg, Throwable t, boolean synch, boolean measurement, boolean display, boolean logInFile) {
if (!debug && !measurement || isClosed)
return;
String message = msg;
if (measurement) {
message = buildDebugString(moduleID, msgID, msg, "DEBUG " + bundleId + " ");
}
boolean logResult = logInFile ? true : (message == null && t == null);
if (logInFile && (message != null || t != null)) {
logResult = logMessage(LogService.LOG_DEBUG, message, t);
}
/* Checks are added for different framework implementations. */
if (printOnConsole || (!logResult && autoPrintOnConsole)) {
message = buildDebugString(moduleID, msgID, msg, "DEBUG " + bundleId + " ");
dumpOnConsole(message, t);
}
}
private void initSysServices() {
if (security) {
try {
securityUtil.doPrivileged(this, GET_SYS_SERVICES_TYPE, null);
} catch (Throwable ignore) {
ignore.printStackTrace();
}
}
}
LogService getService0() throws IllegalArgumentException {
synchronized (logs) {
ServiceReference<LogService> logRef = listener.logTracker.getServiceReference();
LogService ls = null;
if (logRef != null) {
ls = bc.getService(logRef);
}
return ls;
}
}
private LogService getService() throws IllegalArgumentException {
if (bc == null)
return null; // standalone hack
try {
return (LogService) securityUtil.doPrivileged(this, GET_SERVICE_TYPE, null);
} catch (IllegalArgumentException e) {
throw e;
} catch (Exception e) {
/* this will not happen */
e.printStackTrace();
throw new IllegalArgumentException(e.toString());
}
}
private boolean logMessage(int messageType, String messageText, Throwable t) {
LogService ls = null;
try {
ls = getService();
} catch (IllegalStateException ise) { // invalid bundle context
synchronized (logs) {
close0();
}
return false;
}
boolean result = true;
if (ls == null) {
result = false;
testClose(); // test the listener
} else {
try {
ls.log(messageType, messageText, t);
} catch (IllegalStateException ise) { // the log service instance
// is not valid
result = false;
testClose(); // test the listener
}
}
return result;
}
private void dumpOnConsole(String prefix, String msg, long bundleId, Throwable t) {
System.out.println(prefix + bundleId + " " + msg);
if (t != null) {
t.printStackTrace();
}
}
/**
* Prints a message to the console or a log dispatcher
*
* @param msg -
* the message to print, which contains the prefix and the
* bundleID in itself
* @param t
* throwable object, which stack trace should be printed
*/
private void dumpOnConsole(String msg, Throwable t) {
System.out.println(msg);
if (t != null) {
t.printStackTrace();
}
}
private void testClose() {
synchronized (logs) {
if (listener != null)
try {
listener.bc.getBundle();
return;
} catch (IllegalStateException ise) {
listener.close0();
}
}
}
/**
* Releases the Log's resources: ungets LogService, removes the
* ServiceListener from the framework and nulls references. After invocation
* of this method, this Log object can be used no longer.
*/
@Override
public void close() {
if (bc != null) {
synchronized (logs) {
close0();
}
}
}
private boolean close0() {
logs.removeElement(this);
isClosed = true;
if (listener == this) {
try {
logTracker.close();
logTracker = null;
} catch (IllegalStateException ise) {
}
Log ls = null;
while (logs.size() > 0) {
ls = logs.elementAt(0);
try {
ls.initListener();
break;
} catch (IllegalStateException ise) {
logs.removeElementAt(0);
ls = null;
}
}
listener = ls;
}
return (listener != null);
}
/**
* enable/disable print on console
*
* @param value
* boolean if true enables print on console else disables it
*/
@Override
public void setPrintOnConsole(boolean value) {
printOnConsole = value;
}
/**
* enable/disable loging of debug info
*
* @param value
* boolean if true enables loging of debug info else disables it
*/
@Override
public void setDebug(boolean value) {
debug = value;
}
/**
* Gets the flag, which enables logging debug messages.
*
* @return true if debugging is enabled
*/
@Override
public boolean getDebug() {
return debug;
}
/**
* Gets the flag, which enables printing log messages on the console.
*
* @return true if printingon console is enabled
*/
@Override
public boolean getPrintOnConsole() {
return printOnConsole;
}
@Override
public LogService addingService(ServiceReference<LogService> reference) {
return bc.getService(reference);
}
@Override
public void modifiedService(ServiceReference<LogService> reference, LogService service) {
}
@Override
public void removedService(ServiceReference<LogService> reference, LogService service) {
}
private HashIntObjNS map = null;
private HashIntObjNS starts = null;
public void setMaps(HashIntObjNS map0, HashIntObjNS starts0) {
map = map0;
starts = starts0;
}
private String getModuleName(int moduleID) {
return (String) map.get(-moduleID);
}
private String getMsgValue(int msgID) {
String res = (String) map.get(msgID);
if (res == null && starts != null) {
try {
int startID = ((Integer) starts.get(msgID)).intValue();
res = "END OF " + (String) map.get(startID);
} catch (Exception e) {
}
}
return res;
}
private static final char[] chars = {']', ' ', ':', ' '};
private String buildDebugString(int moduleID, int msgID, String message, String prefix) {
if (map == null) {
return prefix + " [" + moduleID + "] " + msgID + " : " + (message == null ? "" : message);
}
StringBuilder sBuf = new StringBuilder(prefix).append("[");
String module = getModuleName(moduleID);
sBuf.append(module != null ? module : String.valueOf(moduleID));
sBuf.append(chars, 0, 2);
if (msgID != 0) {
// map msgID to String
String msg = getMsgValue(msgID);
sBuf.append(msg != null ? msg : String.valueOf(msgID));
}
if (message != null)
sBuf.append(chars, 2, 2).append(message);
return sBuf.toString();
}
// public String toString() {
// return bc.toString();
// }
private static boolean security = false;
public static boolean security() {
if (UtilActivator.bc != null) {
try {
Object ssecurity = UtilActivator.bc.getProperty("equinox.security");
security = ssecurity != null && !"none".equals(ssecurity);
} catch (Throwable t) {
// no security implementation.
}
} else {
try {
Object ssecurity = System.getProperty("equinox.security");
security = ssecurity != null && !"none".equals(ssecurity);
} catch (Throwable t) {
// no security implementation.
}
}
return security;
}
String baseName;
boolean synch;
FileOutputStream fos = null;
static String logsdir = null;
static Queue queue;
static boolean running = false;
static Saver saver;
// boolean date = false;
long lastTime;
// byte[] bdate;
public Log(String baseName, boolean synch, BundleContext bc) {
this(bc);
this.baseName = baseName;
this.synch = synch;
if (!synch)
synchronized (Log.class) {
if (queue == null) {
queue = new Queue(20);
saver = new Saver();
}
}
}
public void trace(byte[] bytes) {
if (!synch)
synchronized (queue) {
queue.put(this);
queue.put(bytes);
if (!running) {
running = true;
UtilActivator.thMan.execute(saver, "File Log Thread");
}
}
else
trace(bytes, 0, bytes.length);
}
public void trace(byte[] bytes, int off, int len) {
if (logsdir == null)
synchronized (Log.class) {
if (logsdir == null) {
try {
logsdir = UtilActivator.bc.getProperty("equinox.logsDir");
if (logsdir == null)
logsdir = "./logs";
File logs = new File(logsdir);
logs.mkdirs();
} catch (Exception exc) {
exc.printStackTrace();
}
}
}
if (fos == null)
synchronized (this) {
if (fos == null) {
StringBuilder fname = new StringBuilder(logsdir.length() + baseName.length() + 1);
fname.append(logsdir).append(File.separatorChar).append(baseName);
try {
fos = new FileOutputStream(fname.toString(), true);
} catch (IOException ioExc) {
ioExc.printStackTrace();
}
}
}
try {
fos.write(bytes, off, len);
fos.write(10);
} catch (IOException ioExc) {
ioExc.printStackTrace();
}
}
@Override
public void finalize() {
if (fos != null)
try {
fos.close();
} catch (IOException ioExc) {
ioExc.printStackTrace();
}
}
/**
* Checks the auto print on console flag
*
* @return true, if autoPrintOnConsole is enabled
*/
public boolean isAutoPrintOnConsole() {
return autoPrintOnConsole;
}
/**
* Enables/disables auto printing on console. This flag points whether
* printing on console should be done if log service is not available
*
* @param autoPrintOnConsole
* if true enables auto print on console else disables it.
*/
public void setAutoPrintOnConsole(boolean autoPrintOnConsole) {
this.autoPrintOnConsole = autoPrintOnConsole;
}
/**
* Returns whether printing on console should be done for errors
* (exceptions) and warnings
*
* @return Returns the error log level flag - if true the error and warnings
* will be print on console
*/
public boolean isLogErrorLevel() {
return logErrorLevel;
}
private static final byte OPEN_TYPE = 0;
private static final byte GET_SERVICE_TYPE = 1;
private static final byte GET_SYS_SERVICES_TYPE = 2;
/**
* @see org.eclipse.equinox.internal.util.security.PrivilegedRunner.PrivilegedDispatcher#dispatchPrivileged(int,
* java.lang.Object, java.lang.Object, java.lang.Object,
* java.lang.Object)
*/
@Override
public Object dispatchPrivileged(int type, Object arg1, Object arg2, Object arg3, Object arg4) throws Exception {
switch (type) {
case OPEN_TYPE :
initListener0();
break;
case GET_SERVICE_TYPE :
return getService0();
case GET_SYS_SERVICES_TYPE :
break;
}
return null;
}
}// Log class
class Saver implements Runnable {
@Override
public void run() {
byte[] bytes = null;
Log log = null;
while (true) {
synchronized (Log.queue) {
log = (Log) Log.queue.get();
if (log == null) {
Log.running = false;
bytes = null;
return;
}
bytes = (byte[]) Log.queue.get();
}
log.trace(bytes, 0, bytes.length);
}
}
}