package com.redhat.qe.tools.remotelog; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.util.ArrayList; import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; import com.redhat.qe.tools.SSHCommandRunner; /** * this class provides access to any remote log file using SSH * @author lzoubek@redhat.com * */ public class RemoteLogAccess { protected static final Logger log = Logger.getLogger(RemoteLogAccess.class.getName()); private final String logFile; private SSHCommandRunner client; private SSHCommandRunner backgroundClient; private int startLine = -1; private final String user; private final String host; private final String pass; private String filter = null; /** * creates new instance of RemoteLogAccess * @throws IOException */ public RemoteLogAccess(String user, String host, String pass, String logFile) throws IOException { this.logFile = logFile; this.host = host; this.user = user; this.pass = pass; log.fine("Creating "+toString()); this.client = new SSHCommandRunner(host, user, pass, ""); this.backgroundClient = new SSHCommandRunner(host, user, pass, ""); } public String getFilter() { return filter; } public void setFilter(String filter) { this.filter = filter; } public String getLogfile() { return logFile; } /** * checks whether agent.log file exists * @return */ public boolean existsLogFile() { return existsFile(getLogfile()); } private boolean existsFile(String file) { return client.runCommandAndWait("ls "+file).getExitCode().equals(0); } private int getLineNumbers(String file) { String line = client.runCommandAndWait("cat "+file+" | wc -l").getStdout(); try { return Integer.parseInt(line.trim()); } catch (Exception ex) { throw new RuntimeException("Unable to parse line number count of agent log "+getLogfile(),ex); } } /** * starts watching log file {@link RemoteLogAccess#getLogfile()} */ public void watch() { this.startLine = getLineNumbers(getLogfile()); } public void disconnect() { client.getConnection().close(); backgroundClient.getConnection().close(); } /** * returns content of <b>agent.log</b> since {@link RemoteLogAccess#watch()} * or {@link RemoteLogAccess#getContent()} was called. Note that calling this also calls {@link RemoteLogAccess#watch()} * so you get only appended content * @return */ public String getContent() { return getContent(null); } /** * returns content of <b>agent.log</b> since {@link RemoteLogAccess#watch()} * or {@link RemoteLogAccess#getContent()} was called. Note that calling this also calls {@link RemoteLogAccess#watch()} * so you get only appended content * @param grep expression to filter results, if null, grep is not used at all * @return */ public String getContent(String grep) { if (this.startLine<0) { return ""; } String grepCmd = ""; if (grep!=null) { grepCmd = " | grep "+grep; } int current = getLineNumbers(getLogfile()); int lines = current - this.startLine; if (lines==0) { this.startLine = -1; return ""; } if (lines>0) { this.startLine = -1; return client.runCommandAndWait("tail -n "+lines+" "+getLogfile()+grepCmd).getStdout(); } else { // it seems'like log files was rotated, we'll return tail of agent.log.1 + whole agent.log String rotatedLog = getLogfile()+".1"; StringBuilder sb = new StringBuilder(); if (existsFile(rotatedLog)) { current = getLineNumbers(rotatedLog); lines = current - this.startLine; if (lines>0) { sb.append(client.runCommandAndWait("tail -n "+lines+" "+rotatedLog+grepCmd).getStdout()); } } sb.append(client.runCommandAndWait("cat "+getLogfile()+grepCmd).getStdout()); this.startLine = -1; return sb.toString(); } } /** * @param filter expression passed to <b>grep</b> tool * @return lines from file {@link RemoteLogAccess#getLogfile()} matching <b>grep</b> param expression since {@link RemoteLogAccess#watch()} was called */ public List<String> lines(String grep) { String content = getContent(grep).trim(); String[] lines = content.split("\n"); List<String> ret = new ArrayList<String>(); for (String line : lines) { if (line.length()>0) { ret.add(line); } } return ret; } /** * * @return lines from file {@link RemoteLogAccess#getLogfile()} matching expression {@link RemoteLogAccess#getFilter()} since {@link RemoteLogAccess#watch()} was called */ public List<String> filteredLines() { return lines(getFilter()); } /** * this method enables <b>agent.log</b> messages being redirected to this logger with level {@link Level#FINE} and prefix <b>[agent]</b> * this starts separate SSH session and background thread - so its completely independent from other methods of this class */ public void startRedirectingLog() { startRedirectingLog("[agent]",Level.FINE); } /** * this method enables all log messages from {@link RemoteLogAccess#getLogfile()} being redirected to this/current logger * this starts separate SSH session and background thread - so its completely independent from other methods of this class * @param level of agent messages * @param prefix of each message line */ public void startRedirectingLog(final String prefix, final Level level) { this.backgroundClient.runCommand("tail -f "+getLogfile()); Thread thread = new Thread(new Runnable(){ @Override public void run() { InputStream is = backgroundClient.getStdoutStream(); BufferedReader reader = new BufferedReader(new InputStreamReader(is)); String line = null; try { while ((line = reader.readLine()) != null) { log.log(level,prefix+" "+line); } Thread.currentThread().join(200); } catch (IOException e) { e.printStackTrace(); } catch (InterruptedException e) { e.printStackTrace(); } }}); thread.start(); log.fine("Redirecting enabled for log "+toString()); } /** * this method disables agent.log messages being redirected to classic logger * @throws IOException */ public void stopRedirectingLog() throws IOException { this.backgroundClient.reset(); this.backgroundClient = new SSHCommandRunner(host, user, pass, ""); log.fine("Redirecting disabled for "+toString()); } @Override public String toString() { return "["+user+"@"+host+":"+getLogfile()+"]"; } }