| /******************************************************************************* |
| * 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); |
| } |
| } |
| } |