mirror of
https://gitee.com/ja-netfilter/ja-netfilter.git
synced 2024-11-16 23:49:38 +08:00
1. new feature: log to file
2. new feature: attach mode Signed-off-by: pengzhile <pengzhile@gmail.com>
This commit is contained in:
parent
be4d4fa72f
commit
389380dda1
16
README.md
16
README.md
@ -1,4 +1,4 @@
|
||||
# ja-netfilter v2.1.1
|
||||
# ja-netfilter v2.2.0
|
||||
|
||||
### A javaagent framework
|
||||
|
||||
@ -9,12 +9,13 @@
|
||||
* add as an argument of the `java` command. eg: `java -javaagent:/absolute/path/to/ja-netfilter.jar -jar executable_jar_file.jar`
|
||||
* some apps support the `JVM Options file`, you can add as a line of the `JVM Options file`.
|
||||
* **WARNING: DO NOT put some unnecessary whitespace characters!**
|
||||
* or execute `java -jar /path/to/ja-netfilter.jar` to use `attach mode`.
|
||||
|
||||
* edit your plugin config files: `${lower plugin name}.conf` file in the `config` dir where `ja-netfilter.jar` is located.
|
||||
* the `config` and `plugins` directory can be specified through **the javaagent args**.
|
||||
* eg: `-javaagent:/path/to/ja-netfilter.jar=appName`, your config and plugins directories will be `config-appname` and `plugins-appname`.
|
||||
* if no javaagent args, they default to `config` and `plugins`.
|
||||
* this mechanism will avoid extraneous and bloated `config` and `plugins`.
|
||||
* the `config`, `logs` and `plugins` directories can be specified through **the javaagent args**.
|
||||
* eg: `-javaagent:/path/to/ja-netfilter.jar=appName`, your config, logs and plugins directories will be `config-appname`, `logs-appname` and `plugins-appname`.
|
||||
* if no javaagent args, they default to `config`, `logs` and `plugins`.
|
||||
* this mechanism will avoid extraneous and bloated `config`, `logs` and `plugins`.
|
||||
|
||||
* run your java application and enjoy~
|
||||
|
||||
@ -46,8 +47,9 @@ EQUAL,somedomain
|
||||
## Debug info
|
||||
|
||||
* the `ja-netfilter` will **NOT** output debugging information by default
|
||||
* add environment variable `JANF_DEBUG=1` and start to enable it
|
||||
* or add system property `-Djanf.debug=1` to enable it
|
||||
* add environment variable `JANF_DEBUG=1` (log level) and start to enable it
|
||||
* or add system property `-Djanf.debug=1` (log level) to enable it
|
||||
* log level: `NONE=0`, `DEBUG=1`, `INFO=2`, `WARN=3`, `ERROR=4`;
|
||||
|
||||
## Plugin system
|
||||
|
||||
|
12
pom.xml
12
pom.xml
@ -6,7 +6,7 @@
|
||||
|
||||
<groupId>com.ja-netfilter</groupId>
|
||||
<artifactId>ja-netfilter</artifactId>
|
||||
<version>2.1.1</version>
|
||||
<version>2.2.0</version>
|
||||
|
||||
<name>ja-netfilter</name>
|
||||
<description>A javaagent framework</description>
|
||||
@ -82,6 +82,7 @@
|
||||
<manifestEntries>
|
||||
<Built-By>neo</Built-By>
|
||||
<Premain-Class>com.janetfilter.core.Launcher</Premain-Class>
|
||||
<Agent-Class>com.janetfilter.core.Launcher</Agent-Class>
|
||||
<Main-Class>com.janetfilter.core.Launcher</Main-Class>
|
||||
<Can-Redefine-Classes>true</Can-Redefine-Classes>
|
||||
<Can-Retransform-Classes>true</Can-Retransform-Classes>
|
||||
@ -163,4 +164,13 @@
|
||||
</distributionManagement>
|
||||
</profile>
|
||||
</profiles>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.sun</groupId>
|
||||
<artifactId>tools</artifactId>
|
||||
<version>1.8</version>
|
||||
<scope>system</scope>
|
||||
<systemPath>${java.home}/../lib/tools.jar</systemPath>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
|
@ -70,7 +70,7 @@ public final class Dispatcher implements ClassFileTransformer {
|
||||
classFileBuffer = transformer.transform(className, classFileBuffer, order++);
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
DebugInfo.output("Transform class failed: " + className, e);
|
||||
DebugInfo.error("Transform class failed: " + className, e);
|
||||
}
|
||||
} while (false);
|
||||
|
||||
|
@ -1,14 +1,17 @@
|
||||
package com.janetfilter.core;
|
||||
|
||||
import com.janetfilter.core.utils.ProcessUtils;
|
||||
import com.janetfilter.core.utils.StringUtils;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
public final class Environment {
|
||||
private final String pid;
|
||||
private final File baseDir;
|
||||
private final File agentFile;
|
||||
private final File configDir;
|
||||
private final File pluginsDir;
|
||||
private final File logsDir;
|
||||
private final String nativePrefix;
|
||||
|
||||
public Environment(File agentFile) {
|
||||
@ -22,13 +25,20 @@ public final class Environment {
|
||||
if (StringUtils.isEmpty(app)) {
|
||||
configDir = new File(baseDir, "config");
|
||||
pluginsDir = new File(baseDir, "plugins");
|
||||
logsDir = new File(baseDir, "logs");
|
||||
} else {
|
||||
app = app.toLowerCase();
|
||||
configDir = new File(baseDir, "config-" + app);
|
||||
pluginsDir = new File(baseDir, "plugins-" + app);
|
||||
logsDir = new File(baseDir, "logs-" + app);
|
||||
}
|
||||
|
||||
nativePrefix = StringUtils.randomMethodName(15) + "_";
|
||||
pid = ProcessUtils.currentId();
|
||||
}
|
||||
|
||||
public String getPid() {
|
||||
return pid;
|
||||
}
|
||||
|
||||
public File getBaseDir() {
|
||||
@ -47,6 +57,10 @@ public final class Environment {
|
||||
return pluginsDir;
|
||||
}
|
||||
|
||||
public File getLogsDir() {
|
||||
return logsDir;
|
||||
}
|
||||
|
||||
public String getNativePrefix() {
|
||||
return nativePrefix;
|
||||
}
|
||||
@ -54,11 +68,13 @@ public final class Environment {
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Environment: {" +
|
||||
"\n\tbaseDir=" + baseDir +
|
||||
", \n\tagentFile=" + agentFile +
|
||||
", \n\tconfigDir=" + configDir +
|
||||
", \n\tpluginsDir=" + pluginsDir +
|
||||
", \n\tnativePrefix=" + nativePrefix +
|
||||
"\n\tpid = " + pid +
|
||||
", \n\tbaseDir = " + baseDir +
|
||||
", \n\tagentFile = " + agentFile +
|
||||
", \n\tconfigDir = " + configDir +
|
||||
", \n\tpluginsDir = " + pluginsDir +
|
||||
", \n\tlogsDir = " + logsDir +
|
||||
", \n\tnativePrefix = " + nativePrefix +
|
||||
"\n}";
|
||||
}
|
||||
}
|
||||
|
@ -8,7 +8,8 @@ import java.util.Set;
|
||||
|
||||
public class Initializer {
|
||||
public static void init(Instrumentation inst, Environment environment) {
|
||||
DebugInfo.output(environment.toString());
|
||||
DebugInfo.useFile(environment.getLogsDir());
|
||||
DebugInfo.info(environment.toString());
|
||||
|
||||
Dispatcher dispatcher = new Dispatcher();
|
||||
new PluginManager(inst, dispatcher, environment).loadPlugins();
|
||||
@ -26,7 +27,7 @@ public class Initializer {
|
||||
try {
|
||||
inst.retransformClasses(c);
|
||||
} catch (Throwable e) {
|
||||
DebugInfo.output("Retransform class failed: " + name, e);
|
||||
DebugInfo.error("Retransform class failed: " + name, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,25 +1,49 @@
|
||||
package com.janetfilter.core;
|
||||
|
||||
import com.janetfilter.core.attach.VMLauncher;
|
||||
import com.janetfilter.core.attach.VMSelector;
|
||||
import com.janetfilter.core.commons.DebugInfo;
|
||||
import com.janetfilter.core.utils.WhereIsUtils;
|
||||
|
||||
import java.io.File;
|
||||
import java.lang.instrument.Instrumentation;
|
||||
import java.net.URI;
|
||||
import java.net.URL;
|
||||
import java.util.jar.JarFile;
|
||||
|
||||
public class Launcher {
|
||||
private static final String VERSION = "v2.1.1";
|
||||
public static final String ATTACH_ARG = "--attach";
|
||||
public static final String VERSION = "v2.2.0";
|
||||
|
||||
private static boolean loaded = false;
|
||||
|
||||
public static void main(String[] args) {
|
||||
URI jarURI;
|
||||
try {
|
||||
jarURI = WhereIsUtils.getJarURI();
|
||||
} catch (Throwable e) {
|
||||
DebugInfo.error("Can not locate `ja-netfilter` jar file.", e);
|
||||
return;
|
||||
}
|
||||
|
||||
String jarPath = jarURI.getPath();
|
||||
if (args.length > 1 && args[0].equals(ATTACH_ARG)) {
|
||||
VMLauncher.attachVM(jarPath, args[1], args.length > 2 ? args[2] : null);
|
||||
return;
|
||||
}
|
||||
|
||||
printUsage();
|
||||
|
||||
try {
|
||||
new VMSelector(new File(jarPath)).select();
|
||||
} catch (Throwable e) {
|
||||
System.err.println(" ERROR: Select virtual machine failed.");
|
||||
e.printStackTrace(System.err);
|
||||
}
|
||||
}
|
||||
|
||||
public static void premain(String args, Instrumentation inst) {
|
||||
if (loaded) {
|
||||
DebugInfo.output("WARN: You have multiple `ja-netfilter` as -javaagent.");
|
||||
DebugInfo.warn("You have multiple `ja-netfilter` as javaagent.");
|
||||
return;
|
||||
}
|
||||
|
||||
@ -28,9 +52,9 @@ public class Launcher {
|
||||
URI jarURI;
|
||||
try {
|
||||
loaded = true;
|
||||
jarURI = getJarURI();
|
||||
jarURI = WhereIsUtils.getJarURI();
|
||||
} catch (Throwable e) {
|
||||
DebugInfo.output("ERROR: Can not locate ja-netfilter jar file.", e);
|
||||
DebugInfo.error("Can not locate `ja-netfilter` jar file.", e);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -38,13 +62,17 @@ public class Launcher {
|
||||
try {
|
||||
inst.appendToBootstrapClassLoaderSearch(new JarFile(agentFile));
|
||||
} catch (Throwable e) {
|
||||
DebugInfo.output("ERROR: Can not access ja-netfilter jar file.", e);
|
||||
DebugInfo.error("Can not access `ja-netfilter` jar file.", e);
|
||||
return;
|
||||
}
|
||||
|
||||
Initializer.init(inst, new Environment(agentFile, args)); // for some custom UrlLoaders
|
||||
}
|
||||
|
||||
public static void agentmain(String args, Instrumentation inst) {
|
||||
premain(args, inst);
|
||||
}
|
||||
|
||||
private static void printUsage() {
|
||||
String content = "\n ============================================================================ \n" +
|
||||
"\n" +
|
||||
@ -57,28 +85,5 @@ public class Launcher {
|
||||
" ============================================================================ \n\n";
|
||||
|
||||
System.out.print(content);
|
||||
System.out.flush();
|
||||
}
|
||||
|
||||
private static URI getJarURI() throws Exception {
|
||||
URL url = Launcher.class.getProtectionDomain().getCodeSource().getLocation();
|
||||
if (null != url) {
|
||||
return url.toURI();
|
||||
}
|
||||
|
||||
String resourcePath = "/4cc9c353c626d6510ca855ab6907ed7f64400257.txt";
|
||||
url = Launcher.class.getResource(resourcePath);
|
||||
if (null == url) {
|
||||
throw new Exception("Can not locate resource file.");
|
||||
}
|
||||
|
||||
String path = url.getPath();
|
||||
if (!path.endsWith("!" + resourcePath)) {
|
||||
throw new Exception("Invalid resource path.");
|
||||
}
|
||||
|
||||
path = path.substring(0, path.length() - resourcePath.length() - 1);
|
||||
|
||||
return new URI(path);
|
||||
}
|
||||
}
|
||||
|
51
src/main/java/com/janetfilter/core/attach/VMDescriptor.java
Normal file
51
src/main/java/com/janetfilter/core/attach/VMDescriptor.java
Normal file
@ -0,0 +1,51 @@
|
||||
package com.janetfilter.core.attach;
|
||||
|
||||
public class VMDescriptor {
|
||||
private String id;
|
||||
private String className;
|
||||
private String args;
|
||||
private Boolean old = true;
|
||||
|
||||
public VMDescriptor(String id, String className, String args) {
|
||||
this.id = id;
|
||||
this.className = className;
|
||||
this.args = args;
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getClassName() {
|
||||
return className;
|
||||
}
|
||||
|
||||
public void setClassName(String className) {
|
||||
this.className = className;
|
||||
}
|
||||
|
||||
public String getArgs() {
|
||||
return args;
|
||||
}
|
||||
|
||||
public void setArgs(String args) {
|
||||
this.args = args;
|
||||
}
|
||||
|
||||
public Boolean getOld() {
|
||||
return old;
|
||||
}
|
||||
|
||||
public void setOld(Boolean old) {
|
||||
this.old = old;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return id + " " + className;
|
||||
}
|
||||
}
|
81
src/main/java/com/janetfilter/core/attach/VMLauncher.java
Normal file
81
src/main/java/com/janetfilter/core/attach/VMLauncher.java
Normal file
@ -0,0 +1,81 @@
|
||||
package com.janetfilter.core.attach;
|
||||
|
||||
import com.janetfilter.core.Launcher;
|
||||
import com.janetfilter.core.utils.ProcessUtils;
|
||||
import com.janetfilter.core.utils.WhereIsUtils;
|
||||
import com.sun.tools.attach.VirtualMachine;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
public class VMLauncher {
|
||||
public static void attachVM(String agentFile, String pid, String args) {
|
||||
try {
|
||||
VirtualMachine vm = VirtualMachine.attach(pid);
|
||||
vm.loadAgent(agentFile, args);
|
||||
vm.detach();
|
||||
} catch (IOException e) {
|
||||
if (e.getMessage().startsWith("Non-numeric value found")) {
|
||||
System.out.println("WARN: The jdk used by `ja-netfilter` does not match the attached jdk version");
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
System.err.println("Attach failed: " + pid);
|
||||
e.printStackTrace(System.err);
|
||||
return;
|
||||
}
|
||||
|
||||
System.out.println("ATTACHED SUCCESSFULLY: " + pid);
|
||||
}
|
||||
|
||||
public static void launch(File thisJar, VMDescriptor descriptor, String args) throws Exception {
|
||||
File javaCommand = WhereIsUtils.findJava();
|
||||
if (null == javaCommand) {
|
||||
throw new Exception("Can not locate java command, unable to start attach mode.");
|
||||
}
|
||||
|
||||
ProcessBuilder pb;
|
||||
double version = Double.parseDouble(System.getProperty("java.specification.version"));
|
||||
if (version > 1.8D) {
|
||||
pb = buildProcess(javaCommand, thisJar, descriptor.getId(), args);
|
||||
} else {
|
||||
File toolsJar = WhereIsUtils.findToolsJar();
|
||||
if (null == toolsJar) {
|
||||
throw new Exception("Can not locate tools.jar file, unable to start attach mode.");
|
||||
}
|
||||
|
||||
pb = buildProcess(javaCommand, thisJar, descriptor.getId(), args, toolsJar);
|
||||
}
|
||||
|
||||
int exitValue = ProcessUtils.start(pb);
|
||||
if (0 != exitValue) {
|
||||
throw new Exception("Attach mode failed: " + exitValue);
|
||||
}
|
||||
}
|
||||
|
||||
private static ProcessBuilder buildProcess(File java, File thisJar, String id, String args) {
|
||||
String[] cmdArray = new String[]{
|
||||
java.getAbsolutePath(),
|
||||
"-Djanf.debug=" + System.getProperty("janf.debug", "0"),
|
||||
"-jar",
|
||||
thisJar.getAbsolutePath(),
|
||||
Launcher.ATTACH_ARG,
|
||||
id, args
|
||||
};
|
||||
|
||||
return new ProcessBuilder(cmdArray);
|
||||
}
|
||||
|
||||
private static ProcessBuilder buildProcess(File java, File thisJar, String id, String args, File toolsJar) {
|
||||
String[] cmdArray = new String[]{
|
||||
java.getAbsolutePath(),
|
||||
"-Djanf.debug=" + System.getProperty("janf.debug", "0"),
|
||||
"-Xbootclasspath/a:" + toolsJar.getAbsolutePath(),
|
||||
"-jar",
|
||||
thisJar.getAbsolutePath(),
|
||||
Launcher.ATTACH_ARG,
|
||||
id, args
|
||||
};
|
||||
|
||||
return new ProcessBuilder(cmdArray);
|
||||
}
|
||||
}
|
124
src/main/java/com/janetfilter/core/attach/VMSelector.java
Normal file
124
src/main/java/com/janetfilter/core/attach/VMSelector.java
Normal file
@ -0,0 +1,124 @@
|
||||
package com.janetfilter.core.attach;
|
||||
|
||||
import com.janetfilter.core.utils.DateUtils;
|
||||
import com.janetfilter.core.utils.ProcessUtils;
|
||||
import com.janetfilter.core.utils.WhereIsUtils;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class VMSelector {
|
||||
private final File thisJar;
|
||||
private List<VMDescriptor> descriptors;
|
||||
|
||||
public VMSelector(File thisJar) {
|
||||
this.thisJar = thisJar;
|
||||
}
|
||||
|
||||
private List<VMDescriptor> getVMList() throws Exception {
|
||||
File jpsCommand = WhereIsUtils.findJPS();
|
||||
if (null == jpsCommand) {
|
||||
throw new Exception("jps command not found");
|
||||
}
|
||||
|
||||
List<String> list = new ArrayList<>();
|
||||
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||
ProcessUtils.start(new ProcessBuilder(jpsCommand.getAbsolutePath(), "-lv"), bos);
|
||||
|
||||
String line;
|
||||
BufferedReader reader = new BufferedReader(new InputStreamReader(new ByteArrayInputStream(bos.toByteArray())));
|
||||
while ((line = reader.readLine()) != null) {
|
||||
list.add(line);
|
||||
}
|
||||
|
||||
String processId = ProcessUtils.currentId();
|
||||
return list.stream()
|
||||
.map(s -> {
|
||||
String[] section = (s + " ").split(" ", 3);
|
||||
return new VMDescriptor(section[0].trim(), section[1].trim(), section[2].trim());
|
||||
})
|
||||
.filter(d -> !d.getId().equals(processId) && !"sun.tools.jps.Jps".equals(d.getClassName()) && !"jdk.jcmd/sun.tools.jps.Jps".equals(d.getClassName()))
|
||||
.sorted(Comparator.comparingInt(d -> Integer.parseInt(d.getId())))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private String getInput() throws IOException {
|
||||
return new BufferedReader(new InputStreamReader(System.in)).readLine().trim();
|
||||
}
|
||||
|
||||
private void processSelect() throws Exception {
|
||||
System.out.print(" Select: ");
|
||||
String input = getInput();
|
||||
|
||||
switch (input) {
|
||||
case "Q":
|
||||
case "q":
|
||||
System.exit(0);
|
||||
case "R":
|
||||
case "r":
|
||||
System.out.println(" =========================== " + DateUtils.formatDateTime() + " ============================");
|
||||
select();
|
||||
return;
|
||||
case "":
|
||||
processSelect();
|
||||
return;
|
||||
default:
|
||||
int index;
|
||||
try {
|
||||
index = Integer.parseInt(input);
|
||||
} catch (NumberFormatException e) {
|
||||
invalidInput(input);
|
||||
return;
|
||||
}
|
||||
|
||||
if (index < 1) {
|
||||
invalidInput(input);
|
||||
return;
|
||||
}
|
||||
|
||||
if (index > descriptors.size()) {
|
||||
invalidInput(input);
|
||||
return;
|
||||
}
|
||||
|
||||
System.out.print(" Agent args: ");
|
||||
input = getInput();
|
||||
try {
|
||||
VMLauncher.launch(thisJar, descriptors.get(index - 1), input);
|
||||
} catch (Exception e) {
|
||||
System.err.println("> Attach to: " + index + " failed.");
|
||||
e.printStackTrace(System.err);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void invalidInput(String input) throws Exception {
|
||||
System.err.println("> Invalid input: " + input);
|
||||
processSelect();
|
||||
}
|
||||
|
||||
public void select() throws Exception {
|
||||
boolean first = null == descriptors;
|
||||
List<VMDescriptor> temp = getVMList();
|
||||
if (null != descriptors && !descriptors.isEmpty()) {
|
||||
temp.forEach(d -> d.setOld(descriptors.stream().anyMatch(d1 -> d.getId().equals(d1.getId()))));
|
||||
}
|
||||
|
||||
descriptors = temp;
|
||||
System.out.println(" Java Virtual Machine List: (Select and attach" + (first ? "" : ", + means the new one") + ")");
|
||||
|
||||
int index = 1;
|
||||
for (VMDescriptor d : descriptors) {
|
||||
System.out.printf(" %3d]:%s%s %s%n", index++, d.getOld() ? " " : "+", d.getId(), d.getClassName());
|
||||
}
|
||||
System.out.println(" r]: <Refresh virtual machine list>");
|
||||
System.out.println(" q]: <Quit the ja-netfilter>");
|
||||
|
||||
processSelect();
|
||||
}
|
||||
}
|
@ -80,13 +80,13 @@ public class ConfigParser {
|
||||
}
|
||||
|
||||
map.get(lastSection).add(rule);
|
||||
DebugInfo.output("Add section: " + lastSection + ", rule: " + rule);
|
||||
DebugInfo.debug("Add section: " + lastSection + ", rule: " + rule);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DebugInfo.output("Config file loaded: " + file);
|
||||
DebugInfo.debug("Config file loaded: " + file);
|
||||
return map;
|
||||
}
|
||||
}
|
||||
|
@ -1,18 +1,95 @@
|
||||
package com.janetfilter.core.commons;
|
||||
|
||||
import com.janetfilter.core.utils.DateUtils;
|
||||
import com.janetfilter.core.utils.ProcessUtils;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintStream;
|
||||
|
||||
public class DebugInfo {
|
||||
private static final boolean DEBUG = "1".equals(System.getenv("JANF_DEBUG")) || "1".equals(System.getProperty("janf.debug"));
|
||||
private static final String CLASS_NAME = DebugInfo.class.getName();
|
||||
private static final String LOG_TEMPLATE = "[%s] %s DEBUG : %s%n";
|
||||
private static final String LOG_TEMPLATE = "[%s] %s %s : %s%n";
|
||||
private static final Level LOG_LEVEL;
|
||||
private static File logFile;
|
||||
|
||||
static {
|
||||
Level level = Level.of(System.getProperty("janf.debug"));
|
||||
LOG_LEVEL = Level.NONE == level ? Level.of(System.getenv("JANF_DEBUG")) : level;
|
||||
}
|
||||
|
||||
public static void useFile(File dir) {
|
||||
if (LOG_LEVEL == Level.NONE || null == dir) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!dir.exists() && !dir.mkdirs()) {
|
||||
error("Can't make directory: " + dir);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!dir.isDirectory()) {
|
||||
error("It's not a directory: " + dir);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!dir.canWrite()) {
|
||||
error("Read-only directory: " + dir);
|
||||
return;
|
||||
}
|
||||
|
||||
File file = new File(dir, String.format("%s-%s.log", DateUtils.formatDate(), ProcessUtils.currentId()));
|
||||
if (file.exists()) {
|
||||
error("Log file exists: " + file);
|
||||
return;
|
||||
}
|
||||
|
||||
logFile = file;
|
||||
}
|
||||
|
||||
public static void debug(String content, Throwable e) {
|
||||
output(Level.DEBUG, content, e);
|
||||
}
|
||||
|
||||
public static void debug(String content) {
|
||||
debug(content, null);
|
||||
}
|
||||
|
||||
public static void info(String content, Throwable e) {
|
||||
output(Level.INFO, content, e);
|
||||
}
|
||||
|
||||
public static void info(String content) {
|
||||
info(content, null);
|
||||
}
|
||||
|
||||
public static void warn(String content, Throwable e) {
|
||||
output(Level.WARN, content, e);
|
||||
}
|
||||
|
||||
public static void warn(String content) {
|
||||
warn(content, null);
|
||||
}
|
||||
|
||||
public static void error(String content, Throwable e) {
|
||||
output(Level.ERROR, content, e);
|
||||
}
|
||||
|
||||
public static void error(String content) {
|
||||
error(content, null);
|
||||
}
|
||||
|
||||
public static void output(String content) {
|
||||
output(content, null);
|
||||
debug(content);
|
||||
}
|
||||
|
||||
public static void output(String content, Throwable e) { // No logger lib required
|
||||
if (!DEBUG) {
|
||||
debug(content, e);
|
||||
}
|
||||
|
||||
public static void output(Level level, String content, Throwable e) { // No logger lib required
|
||||
if (Level.NONE == LOG_LEVEL || level.ordinal() < LOG_LEVEL.ordinal()) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -26,15 +103,74 @@ public class DebugInfo {
|
||||
}
|
||||
}
|
||||
|
||||
String outContent = String.format(LOG_TEMPLATE, DateUtils.formatNow(), caller, content);
|
||||
String outContent = String.format(LOG_TEMPLATE, DateUtils.formatDateTime(), caller, level, content);
|
||||
if (null == e) {
|
||||
System.out.print(outContent);
|
||||
writeContent(outContent);
|
||||
return;
|
||||
}
|
||||
|
||||
synchronized (DebugInfo.class) {
|
||||
System.out.print(outContent);
|
||||
e.printStackTrace(System.err);
|
||||
writeContent(outContent, System.err);
|
||||
writeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private static void writeContent(String content) {
|
||||
writeContent(content, System.out);
|
||||
}
|
||||
|
||||
private static void writeContent(String content, PrintStream fallback) {
|
||||
if (null == logFile) {
|
||||
fallback.print(content);
|
||||
return;
|
||||
}
|
||||
|
||||
try (PrintStream ps = new PrintStream(new FileOutputStream(logFile, true))) {
|
||||
ps.print(content);
|
||||
} catch (IOException e) {
|
||||
fallback.println(content);
|
||||
}
|
||||
}
|
||||
|
||||
private static void writeException(Throwable e) {
|
||||
writeException(e, System.err);
|
||||
}
|
||||
|
||||
private static void writeException(Throwable e, PrintStream fallback) {
|
||||
if (null == logFile) {
|
||||
e.printStackTrace(fallback);
|
||||
return;
|
||||
}
|
||||
|
||||
try (PrintStream ps = new PrintStream(new FileOutputStream(logFile, true))) {
|
||||
e.printStackTrace(ps);
|
||||
} catch (IOException ex) {
|
||||
e.printStackTrace(fallback);
|
||||
}
|
||||
}
|
||||
|
||||
private enum Level {
|
||||
NONE, DEBUG, INFO, WARN, ERROR;
|
||||
|
||||
public static Level of(String valueStr) {
|
||||
if (null == valueStr) {
|
||||
return NONE;
|
||||
}
|
||||
|
||||
int value;
|
||||
try {
|
||||
value = Integer.parseInt(valueStr);
|
||||
} catch (NumberFormatException e) {
|
||||
return NONE;
|
||||
}
|
||||
|
||||
for (Level level : values()) {
|
||||
if (level.ordinal() == value) {
|
||||
return level;
|
||||
}
|
||||
}
|
||||
|
||||
return NONE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -52,9 +52,9 @@ public final class PluginManager {
|
||||
throw new RuntimeException("Load plugin timeout");
|
||||
}
|
||||
|
||||
DebugInfo.output(String.format("============ All plugins loaded, %.2fs elapsed ============", (System.currentTimeMillis() - startTime) / 1000D));
|
||||
DebugInfo.debug(String.format("============ All plugins loaded, %.2fs elapsed ============", (System.currentTimeMillis() - startTime) / 1000D));
|
||||
} catch (Throwable e) {
|
||||
DebugInfo.output("Load plugin failed", e);
|
||||
DebugInfo.error("Load plugin failed", e);
|
||||
}
|
||||
}
|
||||
|
||||
@ -93,9 +93,9 @@ public final class PluginManager {
|
||||
|
||||
dispatcher.addTransformers(pluginEntry.getTransformers());
|
||||
|
||||
DebugInfo.output("Plugin loaded: {name=" + pluginEntry.getName() + ", version=" + pluginEntry.getVersion() + ", author=" + pluginEntry.getAuthor() + "}");
|
||||
DebugInfo.debug("Plugin loaded: {name=" + pluginEntry.getName() + ", version=" + pluginEntry.getVersion() + ", author=" + pluginEntry.getAuthor() + "}");
|
||||
} catch (Throwable e) {
|
||||
DebugInfo.output("Parse plugin info failed", e);
|
||||
DebugInfo.error("Parse plugin info failed", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -14,10 +14,18 @@ public class DateUtils {
|
||||
return FULL_DF.format(date);
|
||||
}
|
||||
|
||||
public static String formatDateTime() {
|
||||
return FULL_DF.format(new Date());
|
||||
}
|
||||
|
||||
public static String formatDate(Date date) {
|
||||
return DATE_DF.format(date);
|
||||
}
|
||||
|
||||
public static String formatDate() {
|
||||
return formatDate(new Date());
|
||||
}
|
||||
|
||||
public static String formatTime(Date date) {
|
||||
return TIME_DF.format(date);
|
||||
}
|
||||
@ -33,8 +41,4 @@ public class DateUtils {
|
||||
public static Date parseDateTime(String dateTimeStr) throws ParseException {
|
||||
return FULL_DF.parse(dateTimeStr);
|
||||
}
|
||||
|
||||
public static String formatNow() {
|
||||
return formatDateTime(new Date());
|
||||
}
|
||||
}
|
||||
|
75
src/main/java/com/janetfilter/core/utils/ProcessUtils.java
Normal file
75
src/main/java/com/janetfilter/core/utils/ProcessUtils.java
Normal file
@ -0,0 +1,75 @@
|
||||
package com.janetfilter.core.utils;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.lang.management.ManagementFactory;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class ProcessUtils {
|
||||
private static String processId;
|
||||
|
||||
public synchronized static String currentId() {
|
||||
if (null == processId) {
|
||||
String name = ManagementFactory.getRuntimeMXBean().getName() + "@";
|
||||
|
||||
processId = name.split("@", 2)[0];
|
||||
}
|
||||
|
||||
return processId;
|
||||
}
|
||||
|
||||
public static int start(ProcessBuilder pb) throws Exception {
|
||||
return start(pb, System.out, System.err);
|
||||
}
|
||||
|
||||
public static int start(ProcessBuilder pb, OutputStream out) throws Exception {
|
||||
return start(pb, out, null);
|
||||
}
|
||||
|
||||
public static int start(ProcessBuilder pb, OutputStream out, OutputStream err) throws Exception {
|
||||
Process p = pb.start();
|
||||
|
||||
List<Thread> threads = new ArrayList<>();
|
||||
if (null != out) {
|
||||
threads.add(new Thread(new RedirectOutput(p.getInputStream(), out)));
|
||||
}
|
||||
if (null != err) {
|
||||
threads.add(new Thread(new RedirectOutput(p.getErrorStream(), err)));
|
||||
}
|
||||
|
||||
for (Thread thread : threads) {
|
||||
thread.start();
|
||||
}
|
||||
for (Thread thread : threads) {
|
||||
thread.join();
|
||||
}
|
||||
|
||||
return p.waitFor();
|
||||
}
|
||||
|
||||
static class RedirectOutput implements Runnable {
|
||||
private static final int BUFF_SIZE = 1024;
|
||||
private final InputStream origin;
|
||||
private final OutputStream dest;
|
||||
|
||||
RedirectOutput(InputStream origin, OutputStream dest) {
|
||||
this.origin = origin;
|
||||
this.dest = dest;
|
||||
}
|
||||
|
||||
public void run() {
|
||||
int length;
|
||||
byte[] buffer = new byte[BUFF_SIZE];
|
||||
|
||||
try {
|
||||
while ((length = origin.read(buffer)) != -1) {
|
||||
dest.write(buffer, 0, length);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("ERROR: Redirect output failed.", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
82
src/main/java/com/janetfilter/core/utils/WhereIsUtils.java
Normal file
82
src/main/java/com/janetfilter/core/utils/WhereIsUtils.java
Normal file
@ -0,0 +1,82 @@
|
||||
package com.janetfilter.core.utils;
|
||||
|
||||
import com.janetfilter.core.Launcher;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.net.URL;
|
||||
|
||||
public class WhereIsUtils {
|
||||
private static final String JAVA_HOME = System.getProperty("java.home");
|
||||
|
||||
public static File findJPS() {
|
||||
String[] paths = new String[]{"bin/jps", "bin/jps.exe", "../bin/jps", "../bin/jps.exe"};
|
||||
|
||||
for (String path : paths) {
|
||||
File file = new File(JAVA_HOME, path);
|
||||
if (file.exists() && file.isFile() && file.canExecute()) {
|
||||
return getCanonicalFile(file);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static File findJava() {
|
||||
String[] paths = new String[]{"bin/java", "bin/java.exe", "../bin/java", "../bin/java.exe"};
|
||||
|
||||
for (String path : paths) {
|
||||
File file = new File(JAVA_HOME, path);
|
||||
if (file.exists() && file.isFile() && file.canExecute()) {
|
||||
return getCanonicalFile(file);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static File findToolsJar() {
|
||||
String[] paths = new String[]{"lib/tools.jar", "../lib/tools.jar", "../../lib/tools.jar"};
|
||||
|
||||
for (String path : paths) {
|
||||
File file = new File(JAVA_HOME, path);
|
||||
if (file.exists() && file.isFile()) {
|
||||
return getCanonicalFile(file);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static URI getJarURI() throws Exception {
|
||||
URL url = Launcher.class.getProtectionDomain().getCodeSource().getLocation();
|
||||
if (null != url) {
|
||||
return url.toURI();
|
||||
}
|
||||
|
||||
String resourcePath = "/288daf08f4ba46dfde71b7f0624b0ad7f234a67a.txt";
|
||||
url = Launcher.class.getResource(resourcePath);
|
||||
if (null == url) {
|
||||
throw new Exception("Can not locate resource file.");
|
||||
}
|
||||
|
||||
String path = url.getPath();
|
||||
if (!path.endsWith("!" + resourcePath)) {
|
||||
throw new Exception("Invalid resource path.");
|
||||
}
|
||||
|
||||
path = path.substring(0, path.length() - resourcePath.length() - 1);
|
||||
|
||||
return new URI(path);
|
||||
|
||||
}
|
||||
|
||||
private static File getCanonicalFile(File file) {
|
||||
try {
|
||||
return file.getCanonicalFile();
|
||||
} catch (IOException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user