X-Git-Url: https://git.argeo.org/?a=blobdiff_plain;f=org.argeo.cms%2Fsrc%2Forg%2Fargeo%2Fcms%2Finternal%2Fkernel%2FNodeLogger.java;h=0b410379402af36dfaa35fe21b75b3d7c5822be5;hb=549ff25baf9371d910065303e22daf49321b517a;hp=ac0c8469f9297f42329945e96ffa6188f0dc9969;hpb=0af549d05ec45b5e31df9026b6627de9038d39eb;p=lgpl%2Fargeo-commons.git diff --git a/org.argeo.cms/src/org/argeo/cms/internal/kernel/NodeLogger.java b/org.argeo.cms/src/org/argeo/cms/internal/kernel/NodeLogger.java index ac0c8469f..0b4103794 100644 --- a/org.argeo.cms/src/org/argeo/cms/internal/kernel/NodeLogger.java +++ b/org.argeo.cms/src/org/argeo/cms/internal/kernel/NodeLogger.java @@ -1,20 +1,14 @@ -/* - * Copyright (C) 2007-2012 Argeo GmbH - * - * 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.argeo.cms.internal.kernel; +import java.io.IOException; +import java.net.URI; +import java.nio.file.FileSystems; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.StandardWatchEventKinds; +import java.nio.file.WatchEvent; +import java.nio.file.WatchKey; +import java.nio.file.WatchService; import java.security.SignatureException; import java.util.ArrayList; import java.util.Collections; @@ -29,32 +23,32 @@ import java.util.Properties; import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.apache.log4j.AppenderSkeleton; -import org.apache.log4j.Level; -import org.apache.log4j.LogManager; -import org.apache.log4j.Logger; -import org.apache.log4j.PropertyConfigurator; -import org.apache.log4j.spi.LoggingEvent; -import org.argeo.ArgeoException; -import org.argeo.ArgeoLogListener; -import org.argeo.ArgeoLogger; +import org.argeo.api.cms.CmsConstants; +import org.argeo.api.cms.CmsLog; +import org.argeo.cms.ArgeoLogListener; +import org.argeo.cms.ArgeoLogger; +import org.argeo.cms.CmsException; import org.argeo.cms.auth.CurrentUser; +import org.argeo.osgi.useradmin.UserAdminConf; +import org.osgi.framework.Bundle; +import org.osgi.framework.Constants; +import org.osgi.framework.ServiceReference; +import org.osgi.service.cm.ConfigurationAdmin; import org.osgi.service.log.LogEntry; +import org.osgi.service.log.LogLevel; import org.osgi.service.log.LogListener; import org.osgi.service.log.LogReaderService; -import org.osgi.service.log.LogService; /** Not meant to be used directly in standard log4j config */ class NodeLogger implements ArgeoLogger, LogListener { + /** Internal debug for development purposes. */ + private static Boolean debug = false; private Boolean disabled = false; private String level = null; - private Level log4jLevel = null; - // private Layout layout; +// private Level log4jLevel = null; private Properties configuration; @@ -81,12 +75,35 @@ class NodeLogger implements ArgeoLogger, LogListener { } }; - @SuppressWarnings("unchecked") public NodeLogger(LogReaderService lrs) { - Enumeration logEntries = lrs.getLog(); - while (logEntries.hasMoreElements()) - logged(logEntries.nextElement()); - lrs.addLogListener(this); + if (lrs != null) { + Enumeration logEntries = lrs.getLog(); + while (logEntries.hasMoreElements()) + logged(logEntries.nextElement()); + lrs.addLogListener(this); + + // configure log4j watcher + String log4jConfiguration = KernelUtils.getFrameworkProp("log4j.configuration"); + if (log4jConfiguration != null && log4jConfiguration.startsWith("file:")) { + if (log4jConfiguration.contains("..")) { + if (log4jConfiguration.startsWith("file://")) + log4jConfiguration = log4jConfiguration.substring("file://".length()); + else if (log4jConfiguration.startsWith("file:")) + log4jConfiguration = log4jConfiguration.substring("file:".length()); + } + try { + Path log4jconfigPath; + if (log4jConfiguration.startsWith("file:")) + log4jconfigPath = Paths.get(new URI(log4jConfiguration)); + else + log4jconfigPath = Paths.get(log4jConfiguration); + Thread log4jConfWatcher = new Log4jConfWatcherThread(log4jconfigPath); + log4jConfWatcher.start(); + } catch (Exception e) { + stdErr("Badly formatted log4j configuration URI " + log4jConfiguration + ": " + e.getMessage()); + } + } + } } public void init() { @@ -99,17 +116,17 @@ class NodeLogger implements ArgeoLogger, LogListener { // setLayout(new PatternLayout(pattern)); appender = new AppenderImpl(); reloadConfiguration(); - Logger.getRootLogger().addAppender(appender); +// Logger.getRootLogger().addAppender(appender); logDispatcherThread = new LogDispatcherThread(); logDispatcherThread.start(); } catch (Exception e) { - throw new ArgeoException("Cannot initialize log4j"); + throw new CmsException("Cannot initialize log4j"); } } public void destroy() throws Exception { - Logger.getRootLogger().removeAppender(appender); +// Logger.getRootLogger().removeAppender(appender); allUsersListeners.clear(); for (List lst : userListeners.values()) lst.clear(); @@ -124,57 +141,136 @@ class NodeLogger implements ArgeoLogger, LogListener { // this.layout = layout; // } + public String toString() { + return "Node Logger"; + } + // // OSGi LOGGER // @Override public void logged(LogEntry status) { - Log pluginLog = LogFactory.getLog(status.getBundle().getSymbolicName()); - Integer severity = status.getLevel(); - if (severity == LogService.LOG_ERROR) { + CmsLog pluginLog = CmsLog.getLog(status.getBundle().getSymbolicName()); + LogLevel severity = status.getLogLevel(); + if (severity.equals(LogLevel.ERROR) && pluginLog.isErrorEnabled()) { // FIXME Fix Argeo TP if (status.getException() instanceof SignatureException) return; - pluginLog.error(status.getMessage(), status.getException()); - } else if (severity == LogService.LOG_WARNING) - pluginLog.warn(status.getMessage(), status.getException()); - else if (severity == LogService.LOG_INFO && pluginLog.isDebugEnabled()) - pluginLog.debug(status.getMessage(), status.getException()); - else if (severity == LogService.LOG_DEBUG && pluginLog.isTraceEnabled()) - pluginLog.trace(status.getMessage(), status.getException()); + pluginLog.error(msg(status), status.getException()); + } else if (severity.equals(LogLevel.WARN) && pluginLog.isWarnEnabled()) { + if (pluginLog.isTraceEnabled()) + pluginLog.warn(msg(status), status.getException()); + else + pluginLog.warn(msg(status)); + } else if (severity.equals(LogLevel.INFO) && pluginLog.isDebugEnabled()) + pluginLog.debug(msg(status), status.getException()); + else if (severity.equals(LogLevel.DEBUG) && pluginLog.isTraceEnabled()) + pluginLog.trace(msg(status), status.getException()); + else if (severity.equals(LogLevel.TRACE) && pluginLog.isTraceEnabled()) + pluginLog.trace(msg(status), status.getException()); + } + + private String msg(LogEntry status) { + StringBuilder sb = new StringBuilder(); + sb.append(status.getMessage()); + Bundle bundle = status.getBundle(); + if (bundle != null) { + sb.append(" '" + bundle.getSymbolicName() + "'"); + } + ServiceReference sr = status.getServiceReference(); + if (sr != null) { + sb.append(' '); + String[] objectClasses = (String[]) sr.getProperty(Constants.OBJECTCLASS); + if (isSpringApplicationContext(objectClasses)) { + sb.append("{org.springframework.context.ApplicationContext}"); + Object symbolicName = sr.getProperty(Constants.BUNDLE_SYMBOLICNAME); + if (symbolicName != null) + sb.append(" " + Constants.BUNDLE_SYMBOLICNAME + ": " + symbolicName); + } else { + sb.append(arrayToString(objectClasses)); + } + Object cn = sr.getProperty(CmsConstants.CN); + if (cn != null) + sb.append(" " + CmsConstants.CN + ": " + cn); + Object factoryPid = sr.getProperty(ConfigurationAdmin.SERVICE_FACTORYPID); + if (factoryPid != null) + sb.append(" " + ConfigurationAdmin.SERVICE_FACTORYPID + ": " + factoryPid); + // else { + // Object servicePid = sr.getProperty(Constants.SERVICE_PID); + // if (servicePid != null) + // sb.append(" " + Constants.SERVICE_PID + ": " + servicePid); + // } + // servlets + Object whiteBoardPattern = sr.getProperty(KernelConstants.WHITEBOARD_PATTERN_PROP); + if (whiteBoardPattern != null) { + if (whiteBoardPattern instanceof String) { + sb.append(" " + KernelConstants.WHITEBOARD_PATTERN_PROP + ": " + whiteBoardPattern); + } else { + sb.append(" " + KernelConstants.WHITEBOARD_PATTERN_PROP + ": " + + arrayToString((String[]) whiteBoardPattern)); + } + } + // RWT + Object contextName = sr.getProperty(KernelConstants.CONTEXT_NAME_PROP); + if (contextName != null) + sb.append(" " + KernelConstants.CONTEXT_NAME_PROP + ": " + contextName); + + // user directories + Object baseDn = sr.getProperty(UserAdminConf.baseDn.name()); + if (baseDn != null) + sb.append(" " + UserAdminConf.baseDn.name() + ": " + baseDn); + + } + return sb.toString(); + } + + private String arrayToString(Object[] arr) { + StringBuilder sb = new StringBuilder(); + sb.append('['); + for (int i = 0; i < arr.length; i++) { + if (i != 0) + sb.append(','); + sb.append(arr[i]); + } + sb.append(']'); + return sb.toString(); + } + + private boolean isSpringApplicationContext(String[] objectClasses) { + for (String clss : objectClasses) { + if (clss.equals("org.eclipse.gemini.blueprint.context.DelegatedExecutionOsgiBundleApplicationContext")) { + return true; + } + } + return false; } // // ARGEO LOGGER // - public synchronized void register(ArgeoLogListener listener, - Integer numberOfPreviousEvents) { + public synchronized void register(ArgeoLogListener listener, Integer numberOfPreviousEvents) { String username = CurrentUser.getUsername(); if (username == null) - throw new ArgeoException( - "Only authenticated users can register a log listener"); + throw new CmsException("Only authenticated users can register a log listener"); if (!userListeners.containsKey(username)) { - List lst = Collections - .synchronizedList(new ArrayList()); + List lst = Collections.synchronizedList(new ArrayList()); userListeners.put(username, lst); } userListeners.get(username).add(listener); - List lastEvents = logDispatcherThread.getLastEvents(username, - numberOfPreviousEvents); + List lastEvents = logDispatcherThread.getLastEvents(username, numberOfPreviousEvents); for (LogEvent evt : lastEvents) dispatchEvent(listener, evt); } - public synchronized void registerForAll(ArgeoLogListener listener, - Integer numberOfPreviousEvents, boolean everything) { + public synchronized void registerForAll(ArgeoLogListener listener, Integer numberOfPreviousEvents, + boolean everything) { if (everything) everythingListeners.add(listener); else allUsersListeners.add(listener); - List lastEvents = logDispatcherThread.getLastEvents(null, - numberOfPreviousEvents); + List lastEvents = logDispatcherThread.getLastEvents(null, numberOfPreviousEvents); for (LogEvent evt : lastEvents) if (everything || evt.getUsername() != null) dispatchEvent(listener, evt); @@ -185,11 +281,9 @@ class NodeLogger implements ArgeoLogger, LogListener { if (username == null)// FIXME return; if (!userListeners.containsKey(username)) - throw new ArgeoException("No user listeners " + listener - + " registered for user " + username); + throw new CmsException("No user listeners " + listener + " registered for user " + username); if (!userListeners.get(username).contains(listener)) - throw new ArgeoException("No user listeners " + listener - + " registered for user " + username); + throw new CmsException("No user listeners " + listener + " registered for user " + username); userListeners.get(username).remove(listener); if (userListeners.get(username).isEmpty()) userListeners.remove(username); @@ -202,10 +296,23 @@ class NodeLogger implements ArgeoLogger, LogListener { } /** For development purpose, since using regular logging is not easy here */ - static void stdOut(Object obj) { + private static void stdOut(Object obj) { System.out.println(obj); } + private static void stdErr(Object obj) { + System.err.println(obj); + } + + private static void debug(Object obj) { + if (debug) + System.out.println(obj); + } + + private static boolean isInternalDebugEnabled() { + return debug; + } + // public void setPattern(String pattern) { // this.pattern = pattern; // } @@ -231,11 +338,13 @@ class NodeLogger implements ArgeoLogger, LogListener { return configuration; } - /** Reloads configuration (if the configuration {@link Properties} is set) */ + /** + * Reloads configuration (if the configuration {@link Properties} is set) + */ protected void reloadConfiguration() { if (configuration != null) { - LogManager.resetConfiguration(); - PropertyConfigurator.configure(configuration); +// LogManager.resetConfiguration(); +// PropertyConfigurator.configure(configuration); } } @@ -247,40 +356,33 @@ class NodeLogger implements ArgeoLogger, LogListener { return; if (level != null && !level.trim().equals("")) { - if (log4jLevel == null || !log4jLevel.toString().equals(level)) - try { - log4jLevel = Level.toLevel(level); - } catch (Exception e) { - System.err - .println("Log4j level could not be set for level '" - + level + "', resetting it to null."); - e.printStackTrace(); - level = null; - } - - if (log4jLevel != null - && !event.getLoggingEvent().getLevel() - .isGreaterOrEqual(log4jLevel)) { - return; - } +// if (log4jLevel == null || !log4jLevel.toString().equals(level)) +// try { +// log4jLevel = Level.toLevel(level); +// } catch (Exception e) { +// System.err.println("Log4j level could not be set for level '" + level + "', resetting it to null."); +// e.printStackTrace(); +// level = null; +// } +// +// if (log4jLevel != null && !event.getLoggingEvent().getLevel().isGreaterOrEqual(log4jLevel)) { +// return; +// } } try { // admin listeners - Iterator everythingIt = everythingListeners - .iterator(); + Iterator everythingIt = everythingListeners.iterator(); while (everythingIt.hasNext()) dispatchEvent(everythingIt.next(), event); if (event.getUsername() != null) { - Iterator allUsersIt = allUsersListeners - .iterator(); + Iterator allUsersIt = allUsersListeners.iterator(); while (allUsersIt.hasNext()) dispatchEvent(allUsersIt.next(), event); if (userListeners.containsKey(event.getUsername())) { - Iterator userIt = userListeners.get( - event.getUsername()).iterator(); + Iterator userIt = userListeners.get(event.getUsername()).iterator(); while (userIt.hasNext()) dispatchEvent(userIt.next(), event); } @@ -292,14 +394,12 @@ class NodeLogger implements ArgeoLogger, LogListener { } protected void dispatchEvent(ArgeoLogListener logListener, LogEvent evt) { - LoggingEvent event = evt.getLoggingEvent(); - logListener.appendLog(evt.getUsername(), event.getTimeStamp(), event - .getLevel().toString(), event.getLoggerName(), event - .getThreadName(), event.getMessage(), event - .getThrowableStrRep()); +// LoggingEvent event = evt.getLoggingEvent(); +// logListener.appendLog(evt.getUsername(), event.getTimeStamp(), event.getLevel().toString(), +// event.getLoggerName(), event.getThreadName(), event.getMessage(), event.getThrowableStrRep()); } - private class AppenderImpl extends AppenderSkeleton { + private class AppenderImpl { //extends AppenderSkeleton { public boolean requiresLayout() { return false; } @@ -307,17 +407,17 @@ class NodeLogger implements ArgeoLogger, LogListener { public void close() { } - @Override - protected void append(LoggingEvent event) { - if (events != null) { - try { - String username = CurrentUser.getUsername(); - events.put(new LogEvent(username, event)); - } catch (InterruptedException e) { - // silent - } - } - } +// @Override +// protected void append(LoggingEvent event) { +// if (events != null) { +// try { +// String username = CurrentUser.getUsername(); +// events.put(new LogEvent(username, event)); +// } catch (InterruptedException e) { +// // silent +// } +// } +// } } @@ -348,11 +448,9 @@ class NodeLogger implements ArgeoLogger, LogListener { lastEvents.add(loggingEvent); } - public synchronized List getLastEvents(String username, - Integer maxCount) { + public synchronized List getLastEvents(String username, Integer maxCount) { LinkedList evts = new LinkedList(); - ListIterator it = lastEvents.listIterator(lastEvents - .size()); + ListIterator it = lastEvents.listIterator(lastEvents.size()); int count = 0; while (it.hasPrevious() && (count < maxCount)) { LogEvent evt = it.previous(); @@ -367,36 +465,75 @@ class NodeLogger implements ArgeoLogger, LogListener { private class LogEvent { private final String username; - private final LoggingEvent loggingEvent; +// private final LoggingEvent loggingEvent; - public LogEvent(String username, LoggingEvent loggingEvent) { + public LogEvent(String username) { super(); this.username = username; - this.loggingEvent = loggingEvent; +// this.loggingEvent = loggingEvent; } - @Override - public int hashCode() { - return loggingEvent.hashCode(); - } - - @Override - public boolean equals(Object obj) { - return loggingEvent.equals(obj); - } - - @Override - public String toString() { - return username + "@ " + loggingEvent.toString(); - } +// @Override +// public int hashCode() { +// return loggingEvent.hashCode(); +// } +// +// @Override +// public boolean equals(Object obj) { +// return loggingEvent.equals(obj); +// } +// +// @Override +// public String toString() { +// return username + "@ " + loggingEvent.toString(); +// } public String getUsername() { return username; } - public LoggingEvent getLoggingEvent() { - return loggingEvent; +// public LoggingEvent getLoggingEvent() { +// return loggingEvent; +// } + + } + + private class Log4jConfWatcherThread extends Thread { + private Path log4jConfigurationPath; + + public Log4jConfWatcherThread(Path log4jConfigurationPath) { + super("Log4j Configuration Watcher"); + try { + this.log4jConfigurationPath = log4jConfigurationPath.toRealPath(); + } catch (IOException e) { + this.log4jConfigurationPath = log4jConfigurationPath.toAbsolutePath(); + stdOut("Cannot determine real path for " + log4jConfigurationPath + ": " + e.getMessage()); + } } + public void run() { + Path parentDir = log4jConfigurationPath.getParent(); + try (final WatchService watchService = FileSystems.getDefault().newWatchService()) { + parentDir.register(watchService, StandardWatchEventKinds.ENTRY_MODIFY); + WatchKey wk; + watching: while ((wk = watchService.take()) != null) { + for (WatchEvent event : wk.pollEvents()) { + final Path changed = (Path) event.context(); + if (log4jConfigurationPath.equals(parentDir.resolve(changed))) { + if (isInternalDebugEnabled()) + debug(log4jConfigurationPath + " has changed, reloading."); +// PropertyConfigurator.configure(log4jConfigurationPath.toUri().toURL()); + } + } + // reset the key + boolean valid = wk.reset(); + if (!valid) { + break watching; + } + } + } catch (IOException | InterruptedException e) { + stdErr("Log4j configuration watcher failed: " + e.getMessage()); + } + } } }