批量查询日志的脚本,自己用着还行,如果给别人也还需要复制一份。如能够把多台机器的日志搜索做成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