目录
1.1 设计一个日志系统,能实现对不同格式日志的读写(.ini, .properties, .html)
3.3 (重头戏!!!)HTML日志相关的类:HtmlFile, HtmlReportFormatter, HtmlUtil, RecordStore
3.5 Properties日志相关的类: PropertiesFile
1. 要求
1.1 设计一个日志系统,能实现对不同格式日志的读写(.ini, .properties, .html)
1.2 实现效果
2. 思路
(1)设计一个接口IFile,接口中声明多个方法:创建/读/写/关闭日志
(2)针对每种日志,设计不同的类,继承接口IFile, 根据不同日志的特性,重写接口中的方法
(3)定义报告入口类,根据Main类传进来的日志名称的后缀,创建不同的日志类的对象 (这一步类似于策略模式?)。对象再调用其所属的类的方法,来实现不同的读写等功能。
(4)日志系统的Main类,程序入口。
(5)相关工具/资料:
IDEA2020, 相关jar包(如果在maven工程中完成,则配置好pom.xml即可)
demo_report_style.css(提前准备好的html报告的样式文件)
html报告的一个模板(用于获取一些现成的前端的设计代码,在重写formatter方法时直接使用)
3. 代码设计
项目代码结构如下图
3.1 IFile类:接口
public interface IFile {
void createLog(String p_info); //创建日志文件
void write(String p_info);//1参数
void write(String p_info1, String p_info2);//2参数,例如properties文件,key和value
void write(String p_info1, String p_info2, String p_info3);//3参数,例如ini文件,section,key和value
String read();//读文件全部内容
String read(String p_info);//比如:读properties文件文件,通过key读value
String read(String p_info1, String p_info2);//比如:读ini文件,通过section、key读value
void closeLog(); //关闭日志文件
}
3.2 入口类:ReportEntry 和 MyMain
(1)ReportEntry类
public class ReportEntry {
IFile myFile; //声明接口
//根据入参进来的文件后缀,判断生成哪种日志对象,然后调用对应的"创建文件"方法
public void createLog(String p_file){
if(p_file.endsWith(".ini")){ //如果要处理的是ini日志文件,则new一个IniFile类的对象
myFile = new IniFile();
}else if(p_file.endsWith(".properties")){ //同上,处理properties
myFile = new PropertiesFile();
}else if(p_file.endsWith(".html")){ //同上,处理html
myFile = new HtmlFile();
}else if(p_file.endsWith(".txt")){ //同上,处理txt
myFile = new TxtFile();
}else if(p_file.endsWith(".log")){ 同上,处理log
myFile = new LogFile();
}else {
myFile = null;
}
if(myFile != null){
myFile.createLog(p_file); //调用日志对象的"创建文件"方法
}else {
System.out.println("不支持的文件类型");
System.exit(0); //退出程序
}
}
//调用日志对象的"写文件"方法
public void write(String p_info){
myFile.write(p_info);
}
public void write(String p_info1, String p_info2){
myFile.write(p_info1, p_info2);
}
public void write(String p_info1, String p_info2, String p_info3){
myFile.write(p_info1, p_info2, p_info3);
}
//调用日志对象的"读文件"方法
public String read(String p_key){
return myFile.read(p_key).toString();
}
public String read(String p_section, String p_key){
return myFile.read(p_section, p_key);
}
//调用日志对象的"关闭文件"方法
public void closeLog(){
myFile.closeLog();
}
}
(2)MyMain类
public class MyMain {
public static void main(String[] args) {
ReportEntry re=new ReportEntry();
//ini日志
re.createLog("C:/log/config1.ini");
re.write("URL", "url","www.google.com");
System.out.println(re.read("URL", "url"));
re.closeLog();
//properties日志
re.createLog("C:/log/config2.properties");
re.write("url", "www.163.com");
re.write("user", "Sheryl");
System.out.println(re.read("url"));
System.out.println(re.read("user"));
re.closeLog();
//html日志,会另外生成hmtl文件,在浏览器中打开,所以这里就不调用read
re.createLog("C:/log/config3.html");
re.write("testcase1", "nice","good");
re.closeLog();
}
}
3.3 (重头戏!!!)HTML日志相关的类:HtmlFile, HtmlReportFormatter, HtmlUtil, RecordStore
(1)HtmlFile类
public class HtmlFile implements IFile {
private Logger logger = Logger.getLogger(HtmlFile.class.getName());
private FileHandler fileHTML;
//带两个参数的创建日志的方法,新方法,非重写
public void createLog(String p_logName, boolean p_append){
try {
fileHTML = new FileHandler(p_logName, p_append);
} catch (IOException e) {
e.printStackTrace();
}
fileHTML.setFormatter(new HtmlReportFormatter()); //调用格式设置方法,传格式设置的文件实例
logger.addHandler(fileHTML);
}
//带一个参数的创建日志的方法,重写
@Override
public void createLog(String p_logName) {
try {
fileHTML = new FileHandler(p_logName);
} catch (IOException e) {
e.printStackTrace();
}
fileHTML.setFormatter(new HtmlReportFormatter());
logger.addHandler(fileHTML);
}
//带一个参数的写日志的方法,重写
@Override
public void write(String p_info) {
logger.info(p_info);
}
//带二个参数的写日志的方法,重写
@Override
public void write(String p_info, String p_result) {
RecordStore.result = p_result;
logger.info(p_info);
}
//带三个参数的写日志的方法,重写
@Override
public void write(String p_info, String p_expected, String p_actual) {
RecordStore.actual = p_actual;
RecordStore.expected = p_expected;
if(p_expected.equals(p_actual)){
RecordStore.result = "Pass";
}else {
RecordStore.result = "Fail";
}
logger.info(p_info);
}
@Override
public String read() {
return null;
}
@Override
public String read(String p_info) {
return null;
}
@Override
public String read(String p_info1, String p_info2) {
return null;
}
@Override
public void closeLog() {
fileHTML.close();
RecordStore.p_pass = 0;
RecordStore.p_fail = 0;
RecordStore.result = "";
RecordStore.expected = "";
RecordStore.actual = "";
}
}
(2)HtmlReportFormatter类
public class HtmlReportFormatter extends Formatter {
private int i = 0;
private long setStartTime;
private long setEndTime;
private final String HTML_HEADER = "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">"
+ "<html xmlns=\"http://www.w3.org/1999/xhtml\"><head><meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\">"
+ "<META HTTP-EQUIV=\"CACHE-CONTROL\" CONTENT=\"NO-CACHE\">"
+ "<META HTTP-EQUIV=\"PRAGMA\" CONTENT=\"NO-CACHE\">"
+ "<link rel=\"stylesheet\" href=\"demo_report_style.css\"/>"
+ "<html><head><title>测试报告</title></head>"
+ "<body>"
+ "<div class=\"page_title\"><center>"
+ "<h1>测试报告</h1></center></div>"
+ "<div class=\"statistics\"><table id=\"statistics_table\" class=\"sortable\" align=\"center\" border=\"0\" style=\"width:100%;\"><tr>"
+ "<th><b>序号</b></th>"
+ "<th><b>用例描述</b></th>"
+ "<th><b>期待结果</b></th>"
+ "<th><b>实际结果</b></th>"
+ "<th><b>执行时间</b></th>" + "<th><b>状态</b></th>" + "</tr>";
private int recordStep(){
i = i + 1;
return i;
}
@Override
public String format(LogRecord record) {
StringBuffer buf = new StringBuffer(1000);
// Bold any levels >= WARNING
buf.append("<div class=\"statistics\">");
buf.append("<tr>");
buf.append("<td>");
buf.append(recordStep());
buf.append("</td>");
buf.append("<td>");
buf.append(formatMessage(record)); //Formatter自带的方法
buf.append('\n');
buf.append("</td>");
buf.append("<td>");
buf.append(RecordStore.expected);
buf.append("</td>");
buf.append("<td>");
buf.append(RecordStore.actual);
buf.append("</td>");
buf.append("<td>");
buf.append(com.report.html.HtmlUtil.getCalcDate(record.getMillis()));
buf.append("</td>");
buf.append("<td>");
if (RecordStore.result.matches("Pass")||RecordStore.result.matches("PASS")) {
RecordStore.p_pass = RecordStore.p_pass + 1;
buf.append("<b>");
buf.append("<font color=Green>");
buf.append(RecordStore.result);
buf.append("</font>");
buf.append("</b>");
} else if (RecordStore.result.matches("Fail")||RecordStore.result.matches("FAIL")) {
RecordStore.p_fail = RecordStore.p_fail + 1;
buf.append("<b>");
buf.append("<font color=Red>");
buf.append(RecordStore.result);
buf.append("</font>");
buf.append("</b>");
}
else{
buf.append("<b>");
// buf.append("<font color=Black>");
buf.append("");
buf.append("</b>");
}
buf.append("</td>");
buf.append("</tr>");
buf.append("</div>\n");
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
return buf.toString();
}
public String getHead(Handler h){
this.setStartTime = System.currentTimeMillis();
System.out.println("starttime: " + this.setStartTime);
return HTML_HEADER;
}
public String getTail(Handler h){
this.setEndTime = System.currentTimeMillis();
System.out.println("endtime: " + this.setEndTime);
String HTML_Tail;
int p_total = RecordStore.p_pass + RecordStore.p_fail;
if (p_total > 0)
if (RecordStore.p_fail > 0)
HTML_Tail = "</table></PRE>" + "<br> 开始时间 :" + HtmlUtil.getCalcDate(this.setStartTime)
+ "<br> 结束时间 :"+ HtmlUtil.getCalcDate(this.setEndTime)
+ "<br> 运行时间 :"+ HtmlUtil.getDeltaTime(this.setEndTime, this.setStartTime)
+ "<br> 执行用例 :" + p_total
+"<br> 用例成功 :"+ RecordStore.p_pass
+ "<br> <font color=Red>用例失败 :"+ RecordStore.p_fail + "</font>"
+ "<br> 成功率(%) :"+ HtmlUtil.getPercnet(RecordStore.p_pass, p_total)
+ "<br> <font color=Red>失败率(%) :"+ HtmlUtil.getPercnet(RecordStore.p_fail, p_total) + "</font>"
+ "<br><br>"
+ "</BODY></HTML>";
else
HTML_Tail = "</table></PRE>" + "<br> 开始时间 :" + HtmlUtil.getCalcDate(this.setStartTime)
+ "<br> 结束时间 :" + HtmlUtil.getCalcDate(this.setEndTime)
+ "<br> 运行时间 :" + HtmlUtil.getDeltaTime(this.setEndTime, this.setStartTime)
+ "<br> 执行用例 :" + p_total
+ "<br> 用例成功 :"+ RecordStore.p_pass
+ "<br> 用例失败 :" + RecordStore.p_fail
+ "<br> 成功率(%) :" + HtmlUtil.getPercnet(RecordStore.p_pass, p_total)
+ "<br> 失败率(%) :" + HtmlUtil.getPercnet(RecordStore.p_fail, p_total)
+ "<br><br>"
+ "</BODY></HTML>";
else
HTML_Tail = "</table></PRE>" + "<br> 用例执行异常!" + "<br><br>"
+ "</BODY></HTML>";
return HTML_Tail;
}
}
(3)HtmlUtil类
public class HtmlUtil {
public static String getPercnet(double p_numerator, double p_denominator) {
double percent = p_numerator / p_denominator;
NumberFormat nt = NumberFormat.getPercentInstance();
// 设置百分数精确度2即保留两位小数
nt.setMinimumFractionDigits(1);
return nt.format(percent);
}
public static String getCalcDate(long millisecs) {
SimpleDateFormat date_format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date resultdate = new Date(millisecs);
return date_format.format(resultdate);
}
public static String formatCurrentTime(){
SimpleDateFormat date_format = new SimpleDateFormat("yyyy_MM_dd_HH_mm_ss");
Date resultdate = new Date(System.currentTimeMillis());
return date_format.format(resultdate);
}
public static String getDeltaTime(long p_startTime, long p_endTime) {
long day = (p_endTime - p_startTime) / (24 * 60 * 60 * 1000);
long hour = ((p_endTime - p_startTime) / (60 * 60 * 1000) - day * 24);
long min = (((p_endTime - p_startTime) / (60 * 1000)) - day * 24 * 60 - hour * 60);
long s = ((p_endTime - p_startTime) / 1000 - day * 24 * 60 * 60 - hour * 60 * 60 - min * 60);
return day + "天" + hour + "小时" + min + "分" + s + "秒";
}
public static void sleep(int p_time){
try {
Thread.sleep(p_time*1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
(4)RecordStore类
public class RecordStore {
static int i = 0; //记录步骤数目
static int p_pass=0; // Pass用例的个数
static int p_fail=0; // Fail用例的个数
static String result=""; // case结果
static Object expected=""; // 期待值
static Object actual=""; // 实际值
}
3.4 INI日志相关的类: IniFile
(1) IniFile类
public class IniFile implements IFile {
private String fileName = null;
HierarchicalINIConfiguration ini = null;
@Override
public void createLog(String p_fileName) {
this.fileName = p_fileName;
File file = new File(this.fileName);
if(!file.exists()){
try {
file.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
}
try {
ini = new HierarchicalINIConfiguration(this.fileName);
ini.load(new File(this.fileName)); //到这一步,文件已存在,这里导入需要的ini文件
} catch (ConfigurationException e) {
e.printStackTrace();
}
}
@Override
public void write(String p_info) {
}
@Override
public void write(String p_info1, String p_info2) {
}
@Override
public void write(String section, String key, String value) {
ini.setProperty(section + "." + key, value); //注意格式
}
@Override
public String read() {
return null;
}
@Override
public String read(String p_info) {
return null;
}
@Override
public String read(String section, String key) {
return ini.getString(section + "." + key);
}
@Override
public void closeLog() {
try {
ini.save();
} catch (ConfigurationException e) {
e.printStackTrace();
}
this.fileName = null;
this.ini = null;
}
}
3.5 Properties日志相关的类: PropertiesFile
(1) Properties类
public class PropertiesFile implements IFile {
File file;
String filePath = null;
PropertiesConfiguration config = new PropertiesConfiguration();
@Override
public void createLog(String p_filePath) {
this.filePath = p_filePath;
file = new File(this.filePath);
if(!file.exists()){
try {
file.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
}
try {
config.load(new File(this.filePath)); // 此时文件已经存在,导入需要的properties文件
} catch (ConfigurationException e) {
e.printStackTrace();
}
}
@Override
public void write(String p_info) {
}
@Override
public void write(String p_key, String p_value) {
config.setProperty(p_key, p_value);
}
@Override
public void write(String p_info1, String p_info2, String p_info3) {
}
@Override
public String read() {
return null;
}
@Override
public String read(String p_key) {
return config.getProperty(p_key).toString();
}
@Override
public String read(String p_info1, String p_info2) {
return null;
}
@Override
public void closeLog() {
File f = new File(this.filePath);
try {
config.save(f);
} catch (ConfigurationException e) {
e.printStackTrace();
}
this.file = null;
this.config = null;
}
}