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:
parent
d2ec523b00
commit
730d221357
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue