IDEMPIERE-4072 iDempiere Monitor: Implement server and cache details for cluster node. Implement multi block streaming of log file from another server node.

This commit is contained in:
Heng Sin Low 2019-10-25 17:25:47 +08:00
parent d2ec523b00
commit 730d221357
4 changed files with 306 additions and 74 deletions

View File

@ -0,0 +1,134 @@
/**********************************************************************
* This file is part of iDempiere ERP Open Source *
* http://www.idempiere.org *
* *
* Copyright (C) Contributors *
* *
* This program 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. *
* *
* This program is distributed in the hope that it will be useful, *
* 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. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the Free Software *
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, *
* MA 02110-1301, USA. *
* *
* Contributors: *
* - Trek Global Corporation *
* - Heng Sin Low *
**********************************************************************/
package org.idempiere.server.cluster.callable;
import java.io.File;
import java.io.Serializable;
import java.util.concurrent.Callable;
import org.compiere.util.CLogFile;
/**
* @author hengsin
*
*/
public class GetLogInfoCallable extends LogFileCallable implements Callable<GetLogInfoCallable.LogInfo>, Serializable {
/**
* generated serial id
*/
private static final long serialVersionUID = 33969865104073117L;
private String fileName;
/**
*
* @param fileName
*/
public GetLogInfoCallable(String fileName) {
this.fileName = fileName;
}
@Override
public LogInfo call() throws Exception {
CLogFile fileHandler = CLogFile.get(false, null, false);
//
// Display current log File
if (fileHandler != null && fileHandler.getFileName().equals(fileName))
fileHandler.flush();
File file = new File(fileName);
if (!isAccessible(file))
return null;
long length = file.length();
int noOfBlocks = 1;
if (length > BLOCK_SIZE) {
int v = (int) (((length - 1) / BLOCK_SIZE) + 1);
if (v == 0)
v = 1;
noOfBlocks = v;
}
return new LogInfo(fileName, length, BLOCK_SIZE, noOfBlocks);
}
public static class LogInfo implements Serializable {
/**
* generated serial id
*/
private static final long serialVersionUID = -4154825003947713729L;
private String fileName;
private long length;
private int blockSize;
private int noOfBlocks;
public LogInfo(String fileName, long length, int blockSize, int noOfBlocks) {
super();
this.fileName = fileName;
this.length = length;
this.blockSize = blockSize;
this.noOfBlocks = noOfBlocks;
}
/**
* @return the serialversionuid
*/
public static long getSerialversionuid() {
return serialVersionUID;
}
/**
* @return the fileName
*/
public String getFileName() {
return fileName;
}
/**
* @return the length
*/
public long getLength() {
return length;
}
/**
* @return the blockSize
*/
public int getBlockSize() {
return blockSize;
}
/**
* @return the noOfBlocks
*/
public int getNoOfBlocks() {
return noOfBlocks;
}
}
}

View File

@ -0,0 +1,117 @@
/**********************************************************************
* This file is part of iDempiere ERP Open Source *
* http://www.idempiere.org *
* *
* Copyright (C) Contributors *
* *
* This program 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. *
* *
* This program is distributed in the hope that it will be useful, *
* 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. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the Free Software *
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, *
* MA 02110-1301, USA. *
* *
* Contributors: *
* - Trek Global Corporation *
* - Heng Sin Low *
**********************************************************************/
package org.idempiere.server.cluster.callable;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.logging.Level;
import org.compiere.Adempiere;
import org.compiere.util.CLogFile;
import org.compiere.util.CLogger;
/**
* @author hengsin
*
*/
public abstract class LogFileCallable {
protected static final int BLOCK_SIZE = 1024 * 1024 * 5;
protected static final String s_dirAccessFileName = "dirAccess.txt";
protected CLogger log = CLogger.getCLogger(getClass());
/**
* default constructor
*/
public LogFileCallable() {
}
/**
*
* @return list of files accessible
*/
protected ArrayList<File> getDirAcessList() {
final ArrayList<File> dirAccessList = new ArrayList<File>();
// by default has access to log directory
CLogFile fileHandler = CLogFile.get(true, null, false);
File logDir = fileHandler.getLogDirectory();
dirAccessList.add(logDir);
// load from dirAccess.properties file
String dirAccessPathName = Adempiere.getAdempiereHome() + File.separator + s_dirAccessFileName;
File dirAccessFile = new File(dirAccessPathName);
if (dirAccessFile.exists()) {
try (BufferedReader br = new BufferedReader(new FileReader(dirAccessFile))) {
while (true) {
String pathName = br.readLine();
if (pathName == null)
break;
File pathDir = new File(pathName);
if (pathDir.exists() && !dirAccessList.contains(pathDir))
dirAccessList.add(pathDir);
}
} catch (Exception e) {
log.log(Level.SEVERE, dirAccessPathName + " - " + e.toString());
}
}
return dirAccessList;
}
/**
*
* @param file
* @return true if log file is accessible, false otherwise
* @throws IOException
*/
protected boolean isAccessible(File file) throws IOException {
if (!file.exists() || !file.canRead()) {
return false;
}
boolean found = false;
ArrayList<File> dirAccessList = getDirAcessList();
for (File dir : dirAccessList) {
if (file.getCanonicalPath().startsWith(dir.getAbsolutePath())) {
found = true;
break;
}
}
if (!found) {
log.warning("Couldn't find file in directories that allowed to access");
return false;
}
return true;
}
}

View File

@ -25,42 +25,49 @@
**********************************************************************/
package org.idempiere.server.cluster.callable;
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileReader;
import java.io.RandomAccessFile;
import java.io.Serializable;
import java.util.ArrayList;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.util.concurrent.Callable;
import java.util.logging.Level;
import org.compiere.Adempiere;
import org.compiere.util.CLogFile;
import org.compiere.util.CLogger;
/**
* @author hengsin
*
*/
public class ReadLogCallable implements Callable<byte[]>, Serializable {
public class ReadLogCallable extends LogFileCallable implements Callable<byte[]>, Serializable {
/**
* generated serial id
*/
private static final long serialVersionUID = 33969865104073117L;
private static final String s_dirAccessFileName = "dirAccess.txt";
private static CLogger log = CLogger.getCLogger(ReadLogCallable.class);
private String fileName;
private int blockNo;
/**
* default constructor
*
* @param fileName
*/
public ReadLogCallable(String fileName) {
this.fileName = fileName;
this(fileName, 0);
}
/**
*
* @param fileName
* @param blockNo 0 base block index
*/
public ReadLogCallable(String fileName, int blockNo) {
this.fileName = fileName;
this.blockNo = blockNo;
}
@Override
public byte[] call() throws Exception {
CLogFile fileHandler = CLogFile.get(false, null, false);
@ -70,73 +77,37 @@ public class ReadLogCallable implements Callable<byte[]>, Serializable {
fileHandler.flush();
File file = new File(fileName);
if (!file.exists() || !file.canRead()) {
if (!isAccessible(file))
return null;
}
if (file.length() == 0) {
return new byte[0];
}
boolean found = false;
ArrayList<File> dirAccessList = getDirAcessList();
for (File dir : dirAccessList) {
if (file.getCanonicalPath().startsWith(dir.getAbsolutePath())) {
found = true;
break;
try(RandomAccessFile raf = new RandomAccessFile(file, "r"); FileChannel channel = raf.getChannel();) {
if (blockNo > 0) {
channel.position(blockNo * BLOCK_SIZE);
}
ByteBuffer buffer = ByteBuffer.allocate(1024);
ByteArrayOutputStream baos = new ByteArrayOutputStream(2048);
int bytesRead = 0;
int totalRead = 0;
while((bytesRead = channel.read(buffer)) > 0) {
totalRead += bytesRead;
if (totalRead > BLOCK_SIZE) {
int diff = BLOCK_SIZE - totalRead;
bytesRead = bytesRead - diff;
totalRead = BLOCK_SIZE;
}
baos.write(buffer.array(), 0, bytesRead);
buffer.clear();
if (totalRead == BLOCK_SIZE)
break;
}
}
if (!found) {
log.warning("Couldn't find file in directories that allowed to access");
return null;
}
// Stream Log
try (FileInputStream fis = new FileInputStream(file)) {
int bufferSize = 2048; // 2k Buffer
byte[] buffer = new byte[bufferSize];
//
ByteArrayOutputStream baos = new ByteArrayOutputStream(bufferSize);
//
int read = 0;
while ((read = fis.read(buffer)) > 0)
baos.write(buffer, 0, read);
return baos.toByteArray();
} catch (Exception ex) {
log.log(Level.SEVERE, "stream" + ex);
return null;
}
}
private ArrayList<File> getDirAcessList() {
final ArrayList<File> dirAccessList = new ArrayList<File>();
// by default has access to log directory
CLogFile fileHandler = CLogFile.get(true, null, false);
File logDir = fileHandler.getLogDirectory();
dirAccessList.add(logDir);
// load from dirAccess.properties file
String dirAccessPathName = Adempiere.getAdempiereHome() + File.separator + s_dirAccessFileName;
File dirAccessFile = new File(dirAccessPathName);
if (dirAccessFile.exists()) {
try {
BufferedReader br = new BufferedReader(new FileReader(dirAccessFile));
while (true) {
String pathName = br.readLine();
if (pathName == null)
break;
File pathDir = new File(pathName);
if (pathDir.exists() && !dirAccessList.contains(pathDir))
dirAccessList.add(pathDir);
}
br.close();
} catch (Exception e) {
log.log(Level.SEVERE, dirAccessPathName + " - " + e.toString());
}
}
return dirAccessList;
}
}
}

View File

@ -88,6 +88,7 @@ import org.idempiere.distributed.IClusterMember;
import org.idempiere.distributed.IClusterService;
import org.idempiere.server.cluster.ClusterServerMgr;
import org.idempiere.server.cluster.callable.DeleteLogsCallable;
import org.idempiere.server.cluster.callable.GetLogInfoCallable;
import org.idempiere.server.cluster.callable.ReadLogCallable;
import org.idempiere.server.cluster.callable.RotateLogCallable;
import org.idempiere.server.cluster.callable.SetTraceLevelCallable;
@ -442,18 +443,27 @@ public class AdempiereMonitor extends HttpServlet
if (!Util.isEmpty(nodeId, true))
{
ReadLogCallable callable = new ReadLogCallable(traceCmd);
GetLogInfoCallable infoCallable = new GetLogInfoCallable(traceCmd);
IClusterService service = ClusterServerMgr.getClusterService();
IClusterMember member = ClusterServerMgr.getClusterMember(nodeId);
try {
byte[] contents = ClusterServerMgr.getClusterService().execute(callable, ClusterServerMgr.getClusterMember(nodeId)).get();
if (contents == null || contents.length == 0)
GetLogInfoCallable.LogInfo logInfo = service.execute(infoCallable, member).get();
if (logInfo == null || logInfo.getLength() == 0)
return false;
try(ServletOutputStream out = response.getOutputStream ())
{
response.setContentType("text/plain");
response.setBufferSize(2048);
response.setContentLength(contents.length);
out.write(contents);
response.setContentLength((int) logInfo.getLength());
for(int i = 0; i < logInfo.getNoOfBlocks(); i++) {
ReadLogCallable callable = new ReadLogCallable(logInfo.getFileName(), i);
byte[] contents = service.execute(callable, member).get();
if (contents == null || contents.length == 0)
break;
out.write(contents);
}
out.flush();
}
return true;