一个基于java的日志解析JFrame可视化工具
package com.king.editor.common.util;
import io.netty.util.concurrent.DefaultThreadFactory;
import javax.swing.*;
import javax.swing.filechooser.FileFilter;
import java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
/**
* 文件解析JFrame可视化工具
*
* @Description
* @Author HHJ
* @Date 2020-03-16 13:50
*/
public class WebLogParser {
/**
* 原文件编码.输出默认为:"utf-8"
*/
private static String fileCharsetName = "ISO-8859-1";
/**
* 原文件路径
*/
private static String logfilePath = "C:\\Users\\Administrator\\Desktop\\test.log";
/**
* 说明:
* 1.可以自己定义.默认等于用户自己选择的文件的同级目录
* 2.文件名字为:原文件路径+原文件名称+"_x"
* 3.文件后缀为:原文件后缀
*/
private static String targerFilePath = null;
/**
* 默认线程数量
*/
private static String defaultThreadNum = "1";
/**
* 关键词提示字样
*/
private static String keywordsTip = "you can use \";\" split this string.";
/**
* 加入队列写出日志
*/
public static LinkedBlockingQueue<String> logQueue = new LinkedBlockingQueue<>();
public static ThreadPoolExecutor executor;
public static JTextField filePath, threadNum, keywords;
public static JTextArea logs;
public static JButton start, stop, choose;
public static JLabel text;
public static WorkerListener listener;
public static Timer timer;
public static File file;
public static DefaultThreadFactory factory;
public static String jFrameName = "文件分析";
public static boolean isStop = false;
public static double processLen = 0.0;
public static double splitLenSum = 0;
public static double fileLen = 0.0;
public static void main(String[] args) {
WebLogParser.runJFrame(jFrameName);
}
private static void runJFrame(String name) {
// TODO Auto-generated method stub
JFrame windows = createWindows(name, windowListener());
createPanel(windows);
ActionListener actionListener = createClickListener();
start.addActionListener(actionListener);
stop.addActionListener(actionListener);
choose.addActionListener(actionListener);
filePath.setText(logfilePath);
// keywords.setText("867186033556969");
threadNum.setText(defaultThreadNum);
windows.setBounds(100, 100, 700, 480);
windows.setVisible(true);
StringBuffer sb = new StringBuffer();
timer = new Timer();
timer.schedule(new TimerTask() {
@Override
public void run() {
StringBuffer sb = new StringBuffer();
if (executor != null) {
sb.append("核心线程:" + executor.getCorePoolSize() + " ")
.append("激活线程:" + executor.getActiveCount() + " ");
}
if (processLen > 0 || splitLenSum > 0) {
if (processLen > 0) {
text.setText("搜索进度:" + Math.ceil(processLen / fileLen * 100) + "% - " + nowDateTime() + " - " + sb.toString());
if (Math.ceil(processLen / fileLen * 100) == 100) {
initView(true);
}
} else {
text.setText("分割进度:" + formatNumber(splitLenSum / fileLen * 100, null) + "% - " + nowDateTime() + " - " + sb.toString());
}
} else {
text.setText(String.format("init success. - " + sb.toString()));
}
}
}, 1000, 1000);
initView(true);
}
private static void initView(boolean isEnable) {
out("init view.");
if (isEnable) {
isStop = true;
start.setEnabled(true);
stop.setEnabled(false);
threadNum.setEnabled(true);
keywords.setEnabled(true);
filePath.setEnabled(true);
} else {
isStop = false;
start.setEnabled(false);
stop.setEnabled(true);
threadNum.setEnabled(false);
keywords.setEnabled(false);
filePath.setEnabled(false);
}
processLen = 0;
splitLenSum = 0;
}
private static void doWork() {
factory = new DefaultThreadFactory("logs");
factory.newThread(new Runnable() {
@Override
public void run() {
while (!isStop) {
if (logQueue.size() > 0) {
try {
logs.setText(logQueue.take() + "\n" + logs.getText());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}).start();
int tnum = Integer.valueOf(threadNum.getText());
if (file == null) {
file = new File(filePath.getText());
}
out("read file " + file.getName());
String key = keywords.getText();
if (key.contains(keywordsTip)) {
key = null;
}
listener = createWorkerListener();
out("Create Thread...");
if (executor == null) {
executor = new ThreadPoolExecutor(20, 20, 30, TimeUnit.SECONDS, new LinkedBlockingQueue<>(100));
}
for (int i = 0; i < tnum; i++) {
executor.execute(new Worker(file, i, key, null, logQueue, listener, tnum, fileCharsetName, targerFilePath));
}
out("Create Thread Success...");
}
private static WorkerListener createWorkerListener() {
return new WorkerListener() {
@Override
public void process(long f, long process, long pos) {
processLen = process;
fileLen = f;
}
@Override
public void splitFile(long fl, long splitLen, long len) {
splitLenSum = splitLen;
fileLen = fl;
}
@Override
public boolean stop() {
return isStop;
}
};
}
private static ActionListener createClickListener() {
return new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
if (e.getActionCommand().equals("start")) {
if ("".equals(filePath.getText()) || filePath.getText() == null) {
JOptionPane.showMessageDialog(null, "请选择日志文件路径", "info", JOptionPane.PLAIN_MESSAGE);
return;
}
if ("".equals(threadNum.getText()) || threadNum.getText() == null) {
JOptionPane.showMessageDialog(null, "请输入线程数", "info", JOptionPane.PLAIN_MESSAGE);
return;
}
if (Integer.valueOf(threadNum.getText()) <= 0) {
JOptionPane.showMessageDialog(null, "线程数必须>0", "info", JOptionPane.PLAIN_MESSAGE);
return;
}
if (Integer.valueOf(threadNum.getText()) > 20) {
JOptionPane.showMessageDialog(null, "线程数必须<=20", "info", JOptionPane.PLAIN_MESSAGE);
return;
}
if ("".equals(keywords.getText()) || keywordsTip.equals(keywords.getText()) || keywords.getText() == null) {
JOptionPane.showMessageDialog(null, "请输入搜索关键词", "info", JOptionPane.PLAIN_MESSAGE);
return;
}
initView(false);
doWork();
} else if (e.getActionCommand().equals("stop")) {
initView(true);
} else if (e.getActionCommand().equals(">>")) {
chooseFile();
} else {
out(e.getActionCommand());
}
}
};
}
private static void chooseFile() {
JFileChooser jfc = new JFileChooser();
jfc.addChoosableFileFilter(new FileFilter() {
@Override
public boolean accept(File f) {
String name = f.getName();
return f.isDirectory() || name.toLowerCase().endsWith(".log");
}
@Override
public String getDescription() {
return "*.log";
}
});
jfc.showDialog(new JLabel(), "选择文件");
jfc.setFileSelectionMode(JFileChooser.FILES_ONLY);
file = jfc.getSelectedFile();
if (file != null && file.isFile()) {
String s = file.getAbsolutePath();
filePath.setText(s);
}
}
private static WindowListener windowListener() {
return new WindowAdapter() {
@Override
public void windowOpened(WindowEvent e) {
super.windowOpened(e);
}
@Override
public void windowClosing(WindowEvent e) {
isStop = true;
timer.cancel();
super.windowClosing(e);
if (executor != null) {
executor.shutdownNow();
}
}
@Override
public void windowClosed(WindowEvent e) {
super.windowClosed(e);
if (executor != null) {
executor.shutdown();
}
}
@Override
public void windowActivated(WindowEvent e) {
super.windowActivated(e);
}
@Override
public void windowStateChanged(WindowEvent e) {
super.windowStateChanged(e);
}
};
}
private static JFrame createWindows(String title, WindowListener listener) {
JFrame jFrame = new JFrame(title);
jFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jFrame.addWindowListener(listener);
return jFrame;
}
private static JPanel createPanel(JFrame jFrame) {
JPanel jPanel = new JPanel();
jPanel.setLayout(null);
jFrame.add(jPanel);
/**
* lable
*/
JLabel label1 = new JLabel("Log File Absolute Path:", JLabel.RIGHT);
label1.setBounds(20, 20, 140, 26);
JLabel label2 = new JLabel("Thread Number:", JLabel.RIGHT);
label2.setBounds(20, 56, 140, 26);
JLabel label4 = new JLabel("keywords:", JLabel.RIGHT);
label4.setBounds(20, 92, 140, 26);
JLabel label3 = new JLabel("logs:", JLabel.RIGHT);
label3.setBounds(4, 132, 48, 26);
label3.setBackground(Color.BLACK);
text = new JLabel();
text.setBounds(20, 400, 500, 26);
text.setText("init success.");
text.setForeground(Color.gray);
/**
* editor
*/
filePath = new JTextField();
filePath.setBounds(165, 20, 400, 26);
choose = new JButton(">>");
choose.setBounds(565, 20, 26, 25);
threadNum = new JTextField();
threadNum.setBounds(165, 56, 80, 26);
threadNum.addKeyListener(new KeyListener() {
@Override
public void keyTyped(KeyEvent e) {
int temp = e.getKeyChar();
//按回车时
if (temp == 10) {
} else if (temp != 46) {
if (temp != 8) {
if (temp > 57) {
e.consume();
} else if (temp < 48) {
e.consume();
}
}
}
}
@Override
public void keyPressed(KeyEvent e) {
}
@Override
public void keyReleased(KeyEvent e) {
}
});
keywords = new JTextField();
keywords.setBounds(165, 92, 495, 26);
keywords.setText(keywordsTip);
keywords.setForeground(Color.gray);
keywords.addFocusListener(new FocusAdapter() {
@Override
public void focusGained(FocusEvent e) {
super.focusGained(e);
if (keywords.getText().contains(keywordsTip)) {
keywords.setText("");
}
keywords.setForeground(Color.black);
}
@Override
public void focusLost(FocusEvent e) {
super.focusLost(e);
if ("".equals(keywords.getText())) {
keywords.setForeground(Color.gray);
keywords.setText(keywordsTip);
}
}
});
logs = new JTextArea(10, 20);
logs.setBounds(60, 130, 600, 200);
logs.setLineWrap(true);
JScrollPane logSp = new JScrollPane(logs);
logSp.setBounds(60, 130, 600, 200);
/**
* button
*/
start = new JButton("start");
start.setBounds(580, 360, 65, 26);
stop = new JButton("stop");
stop.setBounds(500, 360, 65, 26);
jPanel.add(label1);
jPanel.add(label2);
jPanel.add(label3);
jPanel.add(label4);
jPanel.add(text);
jPanel.add(filePath);
jPanel.add(choose);
jPanel.add(threadNum);
jPanel.add(keywords);
// jPanel.add(logs);
jPanel.add(logSp);
jPanel.add(start);
jPanel.add(stop);
return jPanel;
}
private static void out(String s) {
if (s != null) {
s = nowDateTime() + " " + s;
System.out.println(s);
logs.setText(s + "\n" + logs.getText());
}
}
private static String nowDateTime() {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
return sdf.format(new Date());
}
private static String formatNumber(Double number, String pattern) {
if (pattern == null) {
pattern = "#0.00";
}
DecimalFormat df = new DecimalFormat(pattern);
return df.format(number);
}
}
class Worker implements Runnable {
private static final boolean debug = true;
private File file;
private String charsetName = "ISO-8859-1";
/**
* index must be start in 0;
*/
private long index = 0;
private int threadNum = 1;
/**
* you can use ";" split this string.
*/
private String keywords = null;
private String targerFilePath = null;
private File targetFile = null;
private FileOutputStream fos = null;
private LinkedBlockingQueue<String> logQueue;
private long stime = 0L;
long pos = 0L;
private WorkerListener listener;
public Worker() {
// TODO Auto-generated constructor stub
}
public Worker(File f) {
this(f, 1L);
}
public Worker(File f, long num) {
this(f, num, null);
}
public Worker(File file, long index, String keywords) {
this(file, index, keywords, null);
}
public Worker(File file, long index, String keywords, File targetFile) {
this(file, index, keywords, targetFile, null, null);
}
public Worker(File file, long index, String keywords, File targetFile, LinkedBlockingQueue<String> logQueue) {
this(file, index, keywords, targetFile, logQueue, null);
}
public Worker(File file, long index, String keywords, File targetFile, LinkedBlockingQueue<String> logQueue, WorkerListener listener) {
this(file, index, keywords, targetFile, logQueue, listener, 1);
}
public Worker(File file, long index, String keywords, File targetFile, LinkedBlockingQueue<String> logQueue, WorkerListener listener, int threadNum) {
this.file = file;
this.index = index;
this.keywords = keywords;
this.targetFile = targetFile;
this.logQueue = logQueue;
this.listener = listener;
this.threadNum = threadNum;
}
public Worker(File file, long index, String keywords, File targetFile, LinkedBlockingQueue<String> logQueue, WorkerListener listener, int threadNum, String charsetName, String targerFilePath) {
this.file = file;
this.index = index;
this.keywords = keywords;
this.targetFile = targetFile;
this.targerFilePath = targerFilePath;
this.logQueue = logQueue;
this.listener = listener;
this.threadNum = threadNum;
this.charsetName = charsetName;
}
@Override
public void run() {
// TODO Auto-generated method stub
out("file exists: " + file.exists());
if (!file.exists()) {
JOptionPane.showMessageDialog(null, "目标文件不存在!!!", "提示", JOptionPane.PLAIN_MESSAGE);
return;
}
if (file.exists()) {
stime = System.currentTimeMillis();
out("Thread start....");
this.checkTargetFile();
RandomAccessFile raf = null;
try {
long len = 0L;
long lenAll = 0L;
long lenSeach = 0L;
if (index > 0) {
pos = (long) Math.floor(file.length() / (index + 1));
}
file = splitFiles(file);
raf = new RandomAccessFile(file, "r");
raf.seek(pos);
String line;
out("init point is " + pos);
out("temp file is " + file.getAbsolutePath() + file.getName());
while ((line = raf.readLine()) != null) {
lenAll++;
len += line.length();
if (listener != null) {
if (listener.stop()) {
break;
}
listener.process(file.length(), len, pos);
}
if (keywords != null) {
try {
line = new String(line.getBytes(this.charsetName), "utf-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
if (this.checkKeywords(line)) {
this.put(line);
lenSeach++;
}
}
}
this.overWorker(lenAll, lenSeach);
out(String.format("查询结束: file length is %s,search total is %s.", file.length(), len));
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (raf != null) {
raf.close();
}
if (fos != null) {
fos.close();
}
} catch (IOException e) {
e.printStackTrace();
}
stime = (System.currentTimeMillis() - stime) / 1000;
out("Thread end,The work use time is " + stime + "s.");
delSplitFile(file);
}
}
}
private void delSplitFile(File file) {
if (threadNum > 1) {
file.delete();
}
}
private File splitFiles(File file) throws Exception {
if (threadNum < 1) {
throw new Exception("Thread number must be >= 1.");
}
if (threadNum == 1) {
return file;
}
long len = (file.length() / threadNum);
File newFile = new File(getFilePath() + getFileName() + Thread.currentThread().getName());
FileInputStream _in = new FileInputStream(file);
FileOutputStream _out = new FileOutputStream(newFile);
out("split file and skip " + pos + ",split length " + len);
_in.skip(pos);
byte[] b = new byte[1024];
int n = 0;
long l = 0;
stime = System.currentTimeMillis();
out("start split file.");
while ((n = _in.read(b)) != -1) {
l += n;
if (listener != null) {
if (listener.stop()) {
break;
}
listener.splitFile(len, l, n);
}
// out(String.format(">>test>>fileLen:%s sum:%s now:%s",len,l,n));
if (l > len) {
break;
}
_out.write(b, 0, n);
}
out("end split file use time is " + ((System.currentTimeMillis() - stime) / 1000) + "s");
_in.close();
_out.close();
pos = 0;
return newFile;
}
private synchronized void overWorker(long lenAll, long lenSeach) {
String s = String.format("Search %s (%s hits in %s files)", keywords == null ? "" : keywords, lenAll, lenSeach);
this.out(s);
this.put("\n");
this.put(s);
return;
}
private synchronized void put(String data) {
try {
if (fos == null) {
fos = new FileOutputStream(this.targetFile);
}
fos.write(data.trim().getBytes());
fos.write("\n".getBytes());
} catch (Exception e) {
System.out.println("---写入异常---");
e.printStackTrace();
} finally {
}
}
private boolean checkKeywords(String line) {
String[] keys = this.keywords.split(";");
boolean res = false;
for (String key : keys) {
if ((line.indexOf(key) != -1) || line.contains(key)) {
out("key:" + key + ",value: " + line);
res = true;
break;
}
}
return res;
}
private synchronized void checkTargetFile() {
if (this.targetFile == null) {
if (this.targerFilePath == null) {
//路径
String s1 = this.getFilePath();
//文件名
String s2 = this.getFileName();
//后缀
String s3 = this.getFileExt();
//生成文件路径
this.targerFilePath = s1 + File.separator + s2 + "_" + index + "." + s3;
}
// 保证创建一个新文件
File file = new File(this.targerFilePath);
// 如果父目录不存在,创建父目录
if (!file.getParentFile().exists()) {
file.getParentFile().mkdirs();
}
// 如果已存在,删除旧文件
if (file.exists()) {
file.delete();
}
try {
file.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
this.targetFile = file;
}
out("out put new file is " + this.targetFile.getAbsolutePath());
}
private String getFileExt() {
String s = file.getName();
return s.substring(s.indexOf(".") + 1);
}
private String getFileName() {
String s = file.getName();
return s.substring(0, s.indexOf("."));
}
private String getFilePath() {
String absolutePath = file.getAbsolutePath();
return absolutePath.substring(0, absolutePath.lastIndexOf(File.separator) + 1);
}
private void out(String s) {
if (debug) {
if (s != null) {
if (!Thread.interrupted()) {
s = nowDateTime() + " - " + Thread.currentThread().getName() + " " + s;
System.out.println(s);
try {
logQueue.put(s);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}
private String nowDateTime() {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
return sdf.format(new Date());
}
}
interface WorkerListener {
void process(long flen, long process, long pos);
void splitFile(long fileLen, long splitLen, long len);
boolean stop();
}