All Downloads are FREE. Search and download functionalities are using the official Maven repository.

de.mhus.karaf.commands.shell.CmdLogTail Maven / Gradle / Ivy

There is a newer version: 6.4.0
Show newest version
/**
 * Copyright 2018 Mike Hummel
 *
 * 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 de.mhus.karaf.commands.shell;


import java.io.PrintStream;

import org.apache.karaf.log.core.LogEventFormatter;
import org.apache.karaf.log.core.LogService;
import org.apache.karaf.shell.api.action.Action;
import org.apache.karaf.shell.api.action.Argument;
import org.apache.karaf.shell.api.action.Command;
import org.apache.karaf.shell.api.action.Completion;
import org.apache.karaf.shell.api.action.Option;
import org.apache.karaf.shell.api.action.lifecycle.Reference;
import org.apache.karaf.shell.api.action.lifecycle.Service;
import org.apache.karaf.shell.api.console.Session;
import org.apache.karaf.shell.support.completers.StringsCompleter;
import org.ops4j.pax.logging.spi.PaxAppender;
import org.ops4j.pax.logging.spi.PaxLoggingEvent;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceReference;
import org.osgi.util.tracker.ServiceTracker;
import org.osgi.util.tracker.ServiceTrackerCustomizer;

// From https://github.com/apache/karaf/blob/678177241a3b03181490ba4a942e99b7745ba055/log/src/main/java/org/apache/karaf/log/command/LogTail.java

@Command(scope = "mhus", name = "logtail", description = "Continuously display log entries. Use ctrl-c to quit this command")
@Service
public class CmdLogTail implements Action {
	
    public final static int ERROR_INT = 3;
    public final static int WARN_INT  = 4;
    public final static int INFO_INT  = 6;
    public final static int DEBUG_INT = 7;

    @Option(name = "-n", aliases = {}, description="Number of entries to display", required = false, multiValued = false)
    int entries;

    @Option(name = "-p", aliases = {}, description="Pattern for formatting the output", required = false, multiValued = false)
    String overridenPattern;

    @Option(name = "--no-color", description="Disable syntax coloring of log events", required = false, multiValued = false)
    boolean noColor;

    @Option(aliases = {"-c"}, name = "--console-only", description="Log console created messages only", required = false, multiValued = false)
    boolean consoleOnly;
    
    @Option(name = "-l", aliases = { "--level" }, description = "The minimal log level to display", required = false, multiValued = false)
    @Completion(value = StringsCompleter.class, values = { "TRACE", "DEBUG", "INFO", "WARN", "ERROR", "DEFAULT" })
    String level;

    @Argument(index = 0, name = "logger", description = "The name of the logger. This can be ROOT, ALL, or the name of a logger specified in the org.ops4j.pax.logger.cfg file.", required = false, multiValued = false)
    String logger;

    @Reference
    LogService logService;

    @Reference
    LogEventFormatter formatter;


    @Reference
    Session session;

    @Reference
    BundleContext context;

    @Override
    public Object execute() throws Exception {
    	
    	LogTailContainer a = (LogTailContainer) session.get("__log_tail2");
		if (a != null) {
			System.out.println("Close log tail");
			a.close();
			session.put("__log_tail2",null);
		} else {
//	        if (entries == 0) {
//	            entries = 50;
//	        }
	        int minLevel = getMinLevel(level);
			a = new LogTailContainer(session,entries,minLevel,context,logger,logService,formatter,overridenPattern, noColor, consoleOnly ? Thread.currentThread() : null);
			session.put("__log_tail2",a);
		}

        return null;
    }
    
    protected static int getMinLevel(String levelSt) {
        int minLevel = Integer.MAX_VALUE;
        if (levelSt != null) {
            switch (levelSt.toLowerCase()) {
            case "debug": minLevel = DEBUG_INT; break;
            case "info":  minLevel = INFO_INT; break;
            case "warn":  minLevel = WARN_INT; break;
            case "error": minLevel = ERROR_INT; break;
            }
        }
        return minLevel;
    }
    
    
    /**
     * Track LogService dynamically so we can react when the log core bundle stops even while we block for the tail
     */
    private static final class LogServiceTracker extends ServiceTracker {

        private final static String SSHD_LOGGER = "org.apache.sshd";

        private final PaxAppender appender;

        private String sshdLoggerLevel;
    
        private LogServiceTracker(BundleContext context, Class clazz,
                                  ServiceTrackerCustomizer customizer,
                                  PaxAppender appender) {
            super(context, clazz, customizer);
            this.appender = appender;
        }
    
        @Override
        public LogService addingService(ServiceReference reference) {
            LogService service = super.addingService(reference);
            sshdLoggerLevel = service.getLevel(SSHD_LOGGER).get(SSHD_LOGGER);
            service.setLevel(SSHD_LOGGER, "ERROR");
            service.addAppender(appender);
            return service;
        }
    
        @Override
        public void removedService(ServiceReference reference, LogService service) {
            if (sshdLoggerLevel != null) {
                service.setLevel(SSHD_LOGGER, sshdLoggerLevel);
            }
            service.removeAppender(appender);
            //stopTail();
        }
    }
    
    public static class LogTailContainer {

    	ServiceTracker tracker = null;
		private LogService logService;
		private String logger;
		private LogEventFormatter formatter;
		private String overridenPattern;
		private int entries;
		private boolean noColor;
		private Thread threadFilter;
		private boolean closed;
    	
		public LogTailContainer(Session session, int entries, int minLevel, BundleContext context, String logger, LogService logService, LogEventFormatter formatter, String overridenPattern, boolean noColor, Thread threadFilter) {
			this.logService = logService;
			this.logger = logger;
			this.formatter = formatter;
			this.overridenPattern = overridenPattern;
			this.entries = entries;
			this.noColor = noColor;
			this.threadFilter = threadFilter;
			
			System.out.println("Start LogTail " + (threadFilter != null ? "for " + threadFilter : "" ) );
			
	        // Do not use System.out as it may write to the wrong console depending on the thread that calls our log handler
	        PrintStream out = session.getConsole();
	        display(out, minLevel);
	        out.flush();

	        PaxAppender appender = event -> printEvent(out, event, minLevel);
	        tracker = new LogServiceTracker(context, LogService.class, null, appender);
	        tracker.open();

		}

		public void close() {
			if (closed) return;
			closed = true;
            tracker.close();
		}

	    protected void display(final PrintStream out, int minLevel) {
	    	if (entries <= 0) return;
	        Iterable le = logService.getEvents(entries);
	        for (PaxLoggingEvent event : le) {
	            printEvent(out, event, minLevel);
	        }
	    }
	    
	    protected void printEvent(final PrintStream out, PaxLoggingEvent event) {
	    	if (closed) return;
	    	try {
	    		
	    		if (threadFilter != null && threadFilter != Thread.currentThread())
	    			return;
	    		
		        if ((logger != null) &&
		                (event != null) &&
		                (checkIfFromRequestedLog(event))) {
		            out.append(formatter.format(event, overridenPattern, noColor));
		        } else if ((event != null) && (logger == null)) {
		            out.append(formatter.format(event, overridenPattern, noColor));
		        }
	    	} catch (Throwable t) {
	    		// close
	    		close();
	    		System.out.println("close logtail by exception");
	    		t.printStackTrace();
	    	}
	    	
	    }

	    protected boolean checkIfFromRequestedLog(PaxLoggingEvent event) {
	    	return event.getLoggerName().contains(logger);
	    }
	    

	    protected void printEvent(PrintStream out, PaxLoggingEvent event, int minLevel) {
	        try {
	            if (event != null) {
	                int sl = event.getLevel().getSyslogEquivalent();
	                if (sl <= minLevel) {
	                    printEvent(out, event);
	                }
	            }
	        } catch (NoClassDefFoundError e) {
	            // KARAF-3350: Ignore NoClassDefFoundError exceptions
	            // Those exceptions may happen if the underlying pax-logging service
	            // bundle has been refreshed somehow.
	        }
	    }

    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy