/** * * @author greg (at) myrobotlab.org * * This file is part of MyRobotLab (http://myrobotlab.org). * * MyRobotLab is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version (subject to the "Classpath" exception * as provided in the LICENSE.txt file that accompanied this code). * * MyRobotLab is distributed in the hope that it will be useful or fun, * 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. * * All libraries in thirdParty bundle are subject to their own license * requirements - please refer to http://myrobotlab.org/libraries for * details. * * Enjoy ! * * */ package org.myrobotlab.service; import java.lang.reflect.Method; import java.net.URI; import java.util.ArrayList; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import org.myrobotlab.framework.MRLListener; import org.myrobotlab.framework.Message; import org.myrobotlab.framework.Service; import org.myrobotlab.framework.ServiceType; import org.myrobotlab.logging.Level; import org.myrobotlab.logging.LoggerFactory; import org.myrobotlab.logging.Logging; import org.myrobotlab.logging.LoggingFactory; import org.myrobotlab.service.interfaces.CommunicationInterface; import org.myrobotlab.service.interfaces.NameProvider; import org.myrobotlab.service.interfaces.ServiceInterface; import org.slf4j.Logger; import ch.qos.logback.classic.spi.ILoggingEvent; import ch.qos.logback.core.Appender; import ch.qos.logback.core.Context; import ch.qos.logback.core.LogbackException; import ch.qos.logback.core.spi.FilterReply; import ch.qos.logback.core.status.Status; /** * * Log - This service should allow you to record and play back messages. for * testing purposes only. * */ // TODO - add non Root log level changing ability - Service.setLogLevel public class Log extends Service implements Appender<ILoggingEvent>, NameProvider, CommunicationInterface { private static final long serialVersionUID = 1L; public final static Logger log = LoggerFactory.getLogger(Log.class); /** * onLogEvent subscriber private queue :) */ public HashMap<String, ArrayList<MRLListener>> publishLogEventNotifyList = new HashMap<String, ArrayList<MRLListener>>(); boolean isLogging = false; String logLevel = "info"; /* * TODO - allow options to record and playback message log - serialize to * disk etc */ // TODO - do in Service public Log(String n) { super(n); } public void addListener(MRLListener listener) { addListener(listener.topicMethod, listener.callbackName, listener.callbackMethod); } public void addListener(String topicMethod, String callbackName, String callbackMethod) { if ("publishLogEvent".equals(topicMethod)) { log.info("private subscription {} {} {}", topicMethod, callbackName, callbackMethod); MRLListener listener = new MRLListener(topicMethod, callbackName, callbackMethod); if (publishLogEventNotifyList.containsKey(listener.topicMethod.toString())) { // iterate through all looking for duplicate boolean found = false; ArrayList<MRLListener> nes = publishLogEventNotifyList.get(listener.topicMethod.toString()); for (int i = 0; i < nes.size(); ++i) { MRLListener entry = nes.get(i); if (entry.equals(listener)) { log.warn(String.format("attempting to add duplicate MRLListener %s", listener)); found = true; break; } } if (!found) { log.info(String.format("adding addListener from %s.%s to %s.%s", this.getName(), listener.topicMethod, listener.callbackName, listener.callbackMethod)); nes.add(listener); } } else { ArrayList<MRLListener> notifyList = new ArrayList<MRLListener>(); notifyList.add(listener); log.info(String.format("adding addListener from %s.%s to %s.%s", this.getName(), listener.topicMethod, listener.callbackName, listener.callbackMethod)); publishLogEventNotifyList.put(listener.topicMethod.toString(), notifyList); } } else { super.addListener(topicMethod, callbackName, callbackMethod); } } public String publishLogEvent(String entry) { return entry; } public Message log(Message m) { log.info("log message from " + m.sender + "." + m.data); return m; } @Override public boolean preProcessHook(Message m) { if (m.method.equals("log")) { invoke("log", m); return false; } return true; } public void startService() { super.startService(); // ch.qos.logback.classic.Logger logger = // (ch.qos.logback.classic.Logger)LoggerFactory.getLogger(this.getClass()); // logger.addAppender(this); startLogging(); // LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory(); // lc.addListener(listener); /* * FileAppender<LoggingEvent> fileAppender = * (FileAppender<LoggingEvent>) logger..getAppender("file"); * if(fileAppender != null) { fileAppender.stop(); * fileAppender.setFile("new.log"); PatternLayout pl = new * PatternLayout(); pl.setPattern("%d %5p %t [%c:%L] %m%n)"); * pl.setContext(lc); pl.start(); fileAppender.setLayout(pl); * fileAppender.setContext(lc); fileAppender.start(); } */ /* * LoggerContext loggerContext = (LoggerContext) * LogManager.getContext(); Configuration configuration = * loggerContext.getConfiguration(); * * ((Log4jLoggerAdapter)log)..addAppender(this); * ((LoggingLog4J)log).addAppender(this); */ } @Override public boolean isStarted() { return true; } @Override public void start() { } @Override public void stop() { } @Override public void addError(String msg) { } @Override public void addError(String arg0, Throwable arg1) { } @Override public void addInfo(String info) { invoke("publishLogEvent", info); } @Override public void addInfo(String info, Throwable arg1) { invoke("publishLogEvent", info); } @Override public void addStatus(Status arg0) { // TODO Auto-generated method stub } @Override public void addWarn(String arg0) { // TODO Auto-generated method stub } @Override public void addWarn(String arg0, Throwable arg1) { // TODO Auto-generated method stub } @Override public Context getContext() { // TODO Auto-generated method stub return null; } @Override public void setContext(Context arg0) { // TODO Auto-generated method stub } @Override public void addFilter(ch.qos.logback.core.filter.Filter arg0) { // TODO Auto-generated method stub } @Override public void clearAllFilters() { // TODO Auto-generated method stub } @Override public List getCopyOfAttachedFiltersList() { // TODO Auto-generated method stub return null; } @Override public FilterReply getFilterChainDecision(ILoggingEvent arg0) { // TODO Auto-generated method stub return null; } /** * Main interface through which slf4j sends logging. * This method in turn publishes the events to a MRL publishLogEvent topic. */ @Override public void doAppend(ILoggingEvent event) throws LogbackException { // event.getFormattedMessage(); Message msg = createMessage(null, "onLogEvent", new Object[] { String.format("[%s] %s", event.getThreadName(), event.toString()) }); msg.sendingMethod = "publishLogEvent"; msg.sender = getName(); Object[] param = new Object[] { msg }; // Object[] param = new Object[] { String.format("[%s] %s", // arg0.getThreadName(), arg0.toString()) }; if (publishLogEventNotifyList.size() != 0) { // get the value for the source method ArrayList<MRLListener> subList = publishLogEventNotifyList.get("publishLogEvent"); for (int i = 0; i < subList.size(); ++i) { MRLListener listener = subList.get(i); ServiceInterface si = Runtime.getService(listener.callbackName); Class<?> c = si.getClass(); try { Method meth = c.getMethod(listener.callbackMethod, new Class<?>[] { Message.class }); // TODO: what to do with this returned object? // Object retobj = meth.invoke(si, param); meth.invoke(si, param); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } // send(msg); // must make new for internal queues // otherwise you'll change the name on // existing enqueued messages // msg = new Message(msg); } } } public void add(Message msg) throws InterruptedException { LinkedList<Message> msgBox = getOutbox().getMsgBox(); synchronized (msgBox) { while (msgBox.size() > getOutbox().getMaxQueueSize()) { msgBox.wait(); // Limit the size } msgBox.addFirst(msg); msgBox.notifyAll(); // must own the lock } } public void setRootLogLevel(String level) { ch.qos.logback.classic.Logger root = (ch.qos.logback.classic.Logger) LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME); if (level == null || level.length() == 0) { stopLogging(); return; } else if ("debug".equalsIgnoreCase(level)) { root.setLevel(ch.qos.logback.classic.Level.DEBUG); logLevel = "debug"; } else if ("info".equalsIgnoreCase(level)) { root.setLevel(ch.qos.logback.classic.Level.INFO); logLevel = "info"; } else if ("warn".equalsIgnoreCase(level)) { root.setLevel(ch.qos.logback.classic.Level.WARN); logLevel = "warn"; } else if ("error".equalsIgnoreCase(level)) { root.setLevel(ch.qos.logback.classic.Level.ERROR); logLevel = "error"; } else { log.error("unknown logging level {}", level); } if (!isLogging) { root.addAppender(this); } broadcastState(); } public void startLogging() { // LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory(); ch.qos.logback.classic.Logger root = (ch.qos.logback.classic.Logger) LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME); root.setLevel(ch.qos.logback.classic.Level.INFO); root.addAppender(this); isLogging = true; } public void stopLogging() { ch.qos.logback.classic.Logger root = (ch.qos.logback.classic.Logger) LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME); root.detachAppender(this); isLogging = false; } public static void main(String[] args) { LoggingFactory.getInstance().configure(); LoggingFactory.getInstance().setLevel(Level.DEBUG); try { // Log4jLoggerAdapter blah; Runtime.start("log", "Log"); Runtime.start("python", "Python"); Runtime.start("webgui", "WebGui"); log.info("this is an info test"); log.warn("this is an warn test"); log.error("this is an error test"); // Runtime.start("gui", "GUIService"); } catch (Exception e) { Logging.logError(e); } } @Override public void addRemote(URI mrlHost, URI protocolKey) { // not a gateway - don't need to worry about this } @Override public void send(Message msg) { ServiceInterface sw = Runtime.getService(msg.getName()); if (sw == null) { ServiceInterface sender = Runtime.getService(msg.sender); if (sender != null) { sender.removeListener(msg.sendingMethod, msg.getName(), msg.method); } return; } URI host = sw.getInstanceId(); if (host == null) { sw.in(msg); } } @Override public void send(URI uri, Message msg) { // no remote sending enabled } static public String[] getCategories() { return new String[] { "testing" }; } /** * This static method returns all the details of the class without it having * to be constructed. It has description, categories, dependencies, and peer * definitions. * * @return ServiceType - returns all the data * */ static public ServiceType getMetaData() { ServiceType meta = new ServiceType(Log.class.getCanonicalName()); meta.addDescription("Logging Service helpful in diagnostics"); meta.addCategory("famework"); return meta; } }