一般而言,jacoco的覆盖率文件是在maven build时生成exec,再调用jacoco-tool生成html-report,整个过程都可以交给maven做完,但是最近碰到这种需求,要求目标服务正常运行,jacoco动态的获取目标service的覆盖率信息。
客户端:
需要让目标服务配置javaagent,该agent的配置在jacoco官方文档中给出过案例如下:
(https://www.eclemma.org/jacoco/trunk/doc)
-javaagent:E:\jacocoagent.jar=includes=*,append=true,output=tcpserver,port=8494,address=127.0.0.1
可以看到我依赖了一个jacocoagent.jar的包,其次需要暴露自己的port和address供远程访问。
服务端:
我查看了jacoco的官方文档,发现它提供maven的插件:
(https://www.eclemma.org/jacoco/trunk/doc/maven.html)
<dependency>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>0.7.8</version>
</dependency>
里面提供了大量的jacoco 下载,合并等方法。
由于jacoco原理的插桩,即探针(Probe)插入,每次生成覆盖率都依赖探针。所以我们可以利用jacoco-maven-tool的reset方法来清除探针,再生成覆盖率文件。这样就可以满足每次去生成的jacoco覆盖率文件都是最近的覆盖率文件。如果需要得到所有的覆盖率文件,则使用merge方法可以实现。
好了,有了上面的这些认识,我便把jacoco官方的tool进行封装,得到我想要的一个覆盖率工具类,如下:
import java.io.File;
import java.io.IOException;
import java.net.InetAddress;
import java.util.List;
import org.apache.commons.collections.CollectionUtils;
import org.jacoco.core.tools.ExecDumpClient;
import org.jacoco.core.tools.ExecFileLoader;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class ExecDumpClientGenericService implements ExecDumpClientService {
private static final Logger LOG = LoggerFactory.getLogger(ExecDumpClientGenericService.class);
private String host;
private int port;
private int retryCount;
private File dumpDestExecFile;
private File mergeDestExecFile;
private ExecDumpClientGenericService() {
this.retryCount = 2;
}
/**
* ExecDumpClientGenericService
*
* @param host host
* @param port port
* @return ExecDumpClientGenericService
*/
public static ExecDumpClientGenericService build(String host, int port) {
return new ExecDumpClientGenericService().setHost(host).setPort(port);
}
/**
* ExecDumpClientGenericService
*
* @param dumpJacocoExecConfigBo dumpJacocoExecConfigBo
* @return ExecDumpClientGenericService
*/
public static ExecDumpClientGenericService build(DumpJacocoExecConfigBo dumpJacocoExecConfigBo) {
return new ExecDumpClientGenericService().setHost(dumpJacocoExecConfigBo.getHost()).setPort(dumpJacocoExecConfigBo.getPort());
}
@Override
public void dump(boolean reset) throws IOException {
dump(reset, true);
}
@Override
public void dump(boolean reset, boolean append) throws IOException {
ExecDumpClient dumpClient = new ExecDumpClient() {
@Override
protected void onConnecting(InetAddress address, int port) {
LOG.info("Connecting to {}:{}", address, port);
}
@Override
protected void onConnectionFailure(IOException exception) {
LOG.info(exception.getMessage());
}
};
dumpClient.setReset(reset);
dumpClient.setRetryCount(retryCount);
final ExecFileLoader dump = dumpClient.dump(host, port);
dump.save(dumpDestExecFile, append);
LOG.info(CommonUtil.info("Dump successful! Create dumped file: {}", dumpDestExecFile.getAbsolutePath()));
}
@Override
public void merge(List<File> requireMergedFiles) throws IOException {
merge(requireMergedFiles, true);
}
@Override
public void merge(List<File> requireMergedFiles, boolean append) throws IOException {
if (CollectionUtils.isEmpty(requireMergedFiles)) {
LOG.warn("The require merged files must NOT be empty!");
return;
}
LOG.info("Execute merge: ");
final ExecFileLoader loader = new ExecFileLoader();
for (File file : requireMergedFiles) {
loader.load(file);
LOG.info(file.getAbsolutePath());
}
loader.save(mergeDestExecFile, append);
LOG.info(CommonUtil.info("Merge successful! Create merged file: {}", mergeDestExecFile.getAbsolutePath()));
}
/**
* setHost
*
* @param host host
* @return ExecDumpClientGenericService
*/
public ExecDumpClientGenericService setHost(String host) {
this.host = host;
return this;
}
/**
* setPort
*
* @param port port
* @return ExecDumpClientGenericService
*/
public ExecDumpClientGenericService setPort(int port) {
this.port = port;
return this;
}
/**
* setDumpDestExecFile
*
* @param dumpDestExecFile dumpDestExecFile
* @return ExecDumpClientGenericService
*/
public ExecDumpClientGenericService setDumpDestExecFile(File dumpDestExecFile) {
this.dumpDestExecFile = dumpDestExecFile;
return this;
}
/**
* setMergeDestExecFile
*
* @param mergeDestExecFile mergeDestExecFile
* @return ExecDumpClientGenericService
*/
public ExecDumpClientGenericService setMergeDestExecFile(File mergeDestExecFile) {
this.mergeDestExecFile = mergeDestExecFile;
return this;
}
/**
* setRetryCount
*
* @param retryCount try to reconnection count, and default is 3
* @return ExecDumpClientGenericService
*/
public ExecDumpClientGenericService setRetryCount(int retryCount) {
this.retryCount = retryCount;
return this;
}
}