批量查询日志的脚本,自己用着还行,如果给别人也还需要复制一份。如能够把多台机器的日志搜索做成web后台,这样就方便了。
上篇博客解决了登录服务器的信任登录交互问题,那么我们就能够更方便的登录到机器上执行命令。这里我用的是SSHTools ,它是一组 Java 的 SSH 应用和开发包,包括:Java SSH API, SSH Terminal, SSH secured VNC client, SFTP client and SSH Daemon。
POM中引入sshtools:
<dependency> <groupId>sshtools</groupId> <artifactId>j2ssh-core</artifactId> <version>0.2.9</version> </dependency>
1、j2ssh有2种登录验证方式:PublicKeyAuthenticationClient(公钥)、PasswordAuthenticationClient(密码),验证通过之后获得session,用来向服务器发送命令
SessionChannelClient session = null; try { PublicKeyAuthenticationClient auth = new PublicKeyAuthenticationClient(); auth.setUsername("username"); SshPrivateKeyFile file = SshPrivateKeyFile.parse(new File(privateKeyFile)); SshPrivateKey key = file.toPrivateKey(""); auth.setKey(key); if(ssh.authenticate(auth) == AuthenticationProtocolState.COMPLETE){ session = ssh.openSessionChannel(); if(session.startShell()){ return session; } } } catch (IOException e) { //TODO }
2、根据参数组合命令,命令行末尾要带换行符'\n'
public static void doInput(String host, String queryStr, String logFullPath, int n, SessionChannelClient session, SshResult result) { OutputStream out = session.getOutputStream(); StringBuilder cmd = new StringBuilder("ssh username@"); cmd.append(host).append(" "); if(n == 0){ cmd.append("'grep \"").append(queryStr).append("\" ").append(logFullPath); }else{ cmd.append("'grep -C ").append(n).append(" \"").append(queryStr).append("\" ").append(logFullPath); } cmd.append("'\n"); result.setCmd(cmd.toString()); try { out.write(cmd.toString().getBytes()); out.flush(); out.write(SshConfig.ExitCMD.getBytes()); out.flush(); } catch (IOException e) { e.printStackTrace(); } finally{ try { out.close(); } catch (IOException e) { e.printStackTrace(); } } }
3、启用1个线程来读取session的流数据,主线程等待3s,超时后子线程也就终止执行,这样是为了解决BufferedReader阻塞的问题,这样处理会遇到2个问题:
1)数据量大,3s内数据处理不完:根据关键字查询日志,该关键字一般都是唯一的,其对应的返回结果都很少的,如果数据量大的话则不建议也没必要批量查询
2)执行时间:如果日志很少,查询执行很快,但还是要等待3s时间。有没有别的方法呢?
public static void doOutput(SessionChannelClient session, SshResult result) {
try {
new SshResHandler(session.getInputStream(), result).start();
Thread.sleep(3000);
} catch( Throwable e){
e.printStackTrace();
} finally{
if(session != null){
try {
session.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
-------------------------------------
SshResHandler.java
@Override
public void run() {
int i=0;
String line;
StringBuilder searchRes = new StringBuilder();
result.setResult(searchRes);
try {
while ((line = in.readLine()) != null && line.trim().length()>0) {
//System.out.println(i++ + ":" + line);
searchRes.append(line).append("\n");
result.setRows(i++);
if( i > SshConfig.maxLine){
break;
}
}
} catch (Exception e) {
e.printStackTrace();
} finally{
try {
this.in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
***********************************
BufferedReader的阻塞问题
this.in = new BufferedReader(new InputStreamReader(inputStream)); while ((line = in.readLine()) != null) { System.out.println(i++ + ":" + line); searchRes.append(line).append("\n"); } System.out.println("xxxx");
如果是读取普通的文件,则没有问题,一直读到文件末尾,in.readLine()返回null,则while循环终止。但这里程序一直阻塞了,始终执行不到while后面的代码。
如:BufferedReader br = new BufferedReader(new InputStreamReader(p.getErrorStream())),参考别人的解释,这里获取子进程的标准错误输出流,但jvm一直没有读取,缓冲区满了后就无法继续写入数据。错误输出流没有数据要输出,标准输出流有数据要输出,但由于标准输出流没有被读取,进程就不会结束,错误输出流也不会关闭,于是在执行到**readLine()**方法,这个程序就会被阻塞。
参考文章:
http://cnupdog.com/category/programming/
http://blog.csdn.net/dyc9891009/article/details/8141200