[TOC]

搭建日志分析系统
日志分析系统报表展示是一个纯粹的Java EE项目,本节将采用基于Servlet的MVC模式搭建,详细搭建步骤如下。

  1. 创建项目,添加依赖

打开Eclipse,创建名为“Weblog”的Maven工程,并选择war的打包方式,创建成功后,会提示“web.xml is missing and is set to true”的错误,这是由于缺少Web工程的web.xml文件所导致,只需要在工程的src/main/webapp/WEB-INF文件夹下创建web.xml文件即可,读者也可以通过右键项目,选择“Java EE Tools”选项,单击“Generate Deployment Descriptor Stub”选项可以快速创建web.xml文件。
下面编写pom文件添加项目所需的依赖,如文件1-1所示。

  1. pom.xml
  2. <project xmlns=http://maven.apache.org/POM/4.0.0
  3. xmlns:xsi=”http://www.w3.org/2001/XMLSchema-instance
  4. xsi:schemaLocation=”http://maven.apache.org/POM/4.0.0
  5. http://maven.apache.org/xsd/maven-4.0.0.xsd”>
  6. 4.0.0
  7. cn.itcast
  8. Weblog
  9. 0.0.1-SNAPSHOT
  10. war
  11. mysql
  12. mysql-connector-java
  13. 5.1.32
  14. jstl
  15. jstl
  16. 1.2
  17. javax.servlet
  18. servlet-api
  19. 2.5
  20. provided
  21. com.fasterxml.jackson.core
  22. jackson-databind
  23. 2.4.2
  24. log4j
  25. log4j
  26. 1.2.17
  27. Weblog
  28. <!— 指定maven编译的jdk版本,如果不指定,
  29. maven3默认用jdk 1.5 maven2默认用jdk1.3 —>
  30. org.apache.maven.plugins
  31. maven-compiler-plugin
  32. 3.2
  33. 1.8
  34. 1.8
  35. UTF-8
  36. org.apache.tomcat.maven
  37. tomcat7-maven-plugin
  38. 2.2
  39. /
  40. 8080

上述代码中,第12-16行代码添加Java 连接MySQL 需要驱动包;第18-28行代码添加开发jsp和servlet所需依赖;第30-34行代码添加操作Json数据的Jar包;第36-40行代码添加操作日志的jar包;第47-59行代码指定maven编译的jdk版本;第61-69行代码配置Tomcat插件。
在正式讲解项目的编写之前,先来了解一下项目中所涉及的包文件、配置文件以及页面文件等在项目中的组织结构,读者需根据项目组织架构对包含的内容进行创建,如图1-1所示。
网络日志流数据可视化 - 图1

  1. 项目组织架构

在后面的内容,会针对实现数据可视化的关键性代码进行详细讲解,部分文件内容将不做陈述,读者可通过提供的项目源代码自行查看。

  1. 编辑配置文件
  2. 在项目src/main/resources→properties文件夹下的db. properties文件中,编写数据库配置参数,用于项目的解耦,代码如下:
  3. MySQL Configuration

  4. JDBC.DRIVER=com.mysql.jdbc.Driver
  5. JDBC.URL=jdbc:MySql://hadoop01:3306/sqoopdb?characterEncoding=utf8
  6. MYSQLDB.USER=root
  7. MYSQLDB.PASSWORD=123456

上述代码是数据库连接参数,需要注意的是jdbc.url参数,连接的数据库是虚拟机中节点名称为hadoop01的MySQL数据库,这里需要根据读者具体配置情况填写。

  1. 编写web.xml文件,配置请求的servlet容器及映射、欢迎页和全局错误页面等信息,如文件6-2所示。
  2. web.xml
  3. <?xml version=”1.0” encoding=”UTF-8”?>
  4. <web-app xmlns:xsi=http://www.w3.org/2001/XMLSchema-instance
  5. xmlns=http://java.sun.com/xml/ns/javaee
  6. xsi:schemaLocation=”http://java.sun.com/xml/ns/javaee
  7. http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd” version=”2.5”>
  8. Weblog
  9. index.jsp
  10. TavgpvNumServlet
  11. cn.itcast.servlet.TavgpvNumServlet
  12. TavgpvNumServlet
  13. /TavgpvNumServlet
  14. 404
  15. /WEB-INF/jsp/404.jsp

上述代码中,第10-13行代码配置Servlet请求的类;第14-17行代码配置Servlet与URL映射。
小提示:
servlet-mapping中的servlet-name 与 servlet中的servlet-name相同。

  1. 实现报表展示功能

这部分内容主要讲解编写前后端代码,实现报表展示功能,具体步骤如下。

  1. 创建工具类

创建一个cn.itcast.util包,在包中创建PropertyUtil工具类,读取db.peopertis数据库配置文件中的配置信息,如文件1-3所示。

  1. PropertyUtil.java
  2. import java.io.InputStream;
  3. import java.util.Properties;
  4. public class PropertyUtil {
  5. //设置配置文件的路径
  6. static String propertyPathString = “properties/db.properties”;
  7. //实例化Properties类,用于读取properties配置文件
  8. public static Properties properties = new Properties();
  9. static{
  10. try {
  11. //获取配置文件的相对路径
  12. InputStream inStream = Thread.currentThread()
  13. .getContextClassLoader()
  14. .getResourceAsStream(propertyPathString);
  15. //读取Properties文件
  16. properties.load(inStream);
  17. //关闭读取文件流
  18. inStream.close();
  19. } catch (Exception e) {
  20. e.printStackTrace();
  21. }
  22. }
  23. //通过传入配置文件中的key值获取对应得value值
  24. public static String getKeyValue(String key) {
  25. return properties.getProperty(key);
  26. }
  27. //如果配置文件中key没有对应得value可制定value
  28. public static String getKeyValue(String key,
  29. String defaultValue) {
  30. String strValue = getKeyValue(key);
  31. if (strValue!=null && strValue!=””){
  32. return strValue;
  33. }
  34. else{
  35. return defaultValue;
  36. }
  37. }
  38. }

上述代码中,第5行代码设置数据库配置文件的文件名及路径;第11-15行代码获取配置文件路径并读取文件内容;第23-25行代码通过调用getKeyValue方法传入参数key获取对应的value值;第27-36行代码如果配置文件中key没有对应得value便可指定value值。
在cn.itcast.util包中创建DBContent.java工具类,配置数据库操作内容,如文件1-4所示。

  1. DBContent.java
  2. import java.sql.Connection;
  3. import java.sql.DriverManager;
  4. import java.sql.ResultSet;
  5. import java.sql.SQLException;
  6. import java.sql.Statement;
  7. public class DBContent {
  8. //数据库连接方法
  9. public static Connection connDB() {
  10. Connection conn = null;
  11. try {
  12. //定义MySQL的数据库驱动程序
  13. String driver = PropertyUtil
  14. .getKeyValue(“JDBC.DRIVER”);
  15. //定义MySQL数据库的连接地址
  16. String url = PropertyUtil
  17. .getKeyValue(“JDBC.URL”);
  18. // MySQL数据库的连接用户名
  19. String user = PropertyUtil
  20. .getKeyValue(“MYSQLDB.USER”);
  21. // MySQL数据库的连接密码
  22. String password = PropertyUtil
  23. .getKeyValue(“MYSQLDB.PASSWORD”);
  24. //加载驱动程序
  25. Class.forName(driver);
  26. if (null == conn) {
  27. //通过数据库的连接地址,用户名,密码创建连接
  28. conn = DriverManager
  29. .getConnection(url, user, password);
  30. }
  31. } catch (ClassNotFoundException e) {
  32. System.out.println(“Sorry,can’t find the Driver!damai”);
  33. e.printStackTrace();
  34. } catch (SQLException e) {
  35. e.printStackTrace();
  36. } catch (Exception e) {
  37. e.printStackTrace();
  38. }
  39. return conn;
  40. }
  41. // 关闭对象
  42. public static void freeRs(ResultSet rs) {
  43. try {
  44. if (rs != null) {
  45. rs.close();
  46. }
  47. } catch (SQLException e) {
  48. e.printStackTrace();
  49. }
  50. }
  51. // 关闭操作
  52. public static void freeSt(Statement st) {
  53. try {
  54. if (st != null) {
  55. st.close();
  56. }
  57. } catch (SQLException e) {
  58. e.printStackTrace();
  59. }
  60. }
  61. // 关闭数据库连接
  62. public static void freeConn(Connection conn) {
  63. try {
  64. if (conn != null) {
  65. conn.close();
  66. }
  67. } catch (SQLException e) {
  68. e.printStackTrace();
  69. }
  70. }
  71. // 释放资源
  72. public static void free(ResultSet rs,
  73. Statement st, Connection conn) {
  74. freeRs(rs);
  75. freeSt(st);
  76. freeConn(conn);
  77. }
  78. }

上述代码中,第12-22行代码通过PropertyUtil工具类加载数据库配置信息;第27-28行代码通过获取的数据库连接地址、用户名和密码创建数据库连接;第41-76行代码定义关闭对象、关闭操作和关闭数据库连接的方法,并在最后的释放资源方法中同一调用进行数据库连接相关资源的释放。
在cn.itcast.util包中创建BaseServlet工具类继承HttpServlet类,用于处理处理HTTP请求,如文件1-5所示。

  1. BaseServlet.java
  2. import java.io.IOException;
  3. import java.lang.reflect.Method;
  4. import javax.servlet.ServletException;
  5. import javax.servlet.http.HttpServlet;
  6. import javax.servlet.http.HttpServletRequest;
  7. import javax.servlet.http.HttpServletResponse;
  8. public abstract class BaseServlet extends HttpServlet {
  9. private static final long serialVersionUID = 1L;
  10. @Override
  11. public void service(HttpServletRequest request,
  12. HttpServletResponse response)
  13. throws ServletException, IOException {
  14. String methodName = request.getParameter(“cmd”);
  15. if(methodName==null || methodName.trim().equals(“”)){
  16. methodName=”execute”;
  17. }
  18. try{
  19. Method method = this.getClass()
  20. .getMethod(methodName,
  21. HttpServletRequest.class,
  22. HttpServletResponse.class);
  23. method.invoke(this,request,response);
  24. }catch(Exception e){
  25. throw new RuntimeException(e);
  26. }
  27. }
  28. public abstract void execute(HttpServletRequest request,
  29. HttpServletResponse response)
  30. throws Exception;
  31. }
  32. 创建持久化类

前端与后端数据大多是通过Json数据进行交互,本项目同样是通过后端查询MySQL数据进行封装,返回与前端接口一致的数据格式。
创建一个cn.itcast.pojo包,在包中创建TavgpvNum实体类对象,并在该类中定义属性的get/set方法,如文件1-6所示。

  1. TavgpvNum.java
  2. import java.math.BigDecimal;
  3. public class TavgpvNum {
  4. private String datastr;
  5. private BigDecimal avgpvnum;
  6. public String getDatastr() {
  7. return datastr;
  8. }
  9. public void setDatastr(String datastr) {
  10. this.datastr = datastr;
  11. }
  12. public BigDecimal getAvgpvnum() {
  13. return avgpvnum;
  14. }
  15. public void setAvgpvnum(BigDecimal avgpvnum) {
  16. this.avgpvnum = avgpvnum;
  17. }
  18. }

从文件1-3可以看出,实体类对象的属性值与t_avgpv_num表中字段保持一致。
在pojo包下创建AvgToPageBean.java文件,将后端查询的数据封装此对象中,便于与前端页面交互,代码如文件1-7所示。

  1. AvgToPageBean.java
  2. public class AvgToPageBean {
  3. private String[] dates;
  4. private double[] data;
  5. public String[] getDates() {
  6. return dates;
  7. }
  8. public void setDates(String[] dates) {
  9. this.dates = dates;
  10. }
  11. public double[] getData() {
  12. return data;
  13. }
  14. public void setData(double[] data) {
  15. this.data = data;
  16. }
  17. }
  18. 实现Dao层

Dao层叫数据访问层,全称为data access object,属于一种比较底层,比较基础的操作,用于对于数据库中某个表的增删改查。
创建一个cn.itcast.dao包,在包中创建TavgpvNumDao类,并在类中编写通过日期查询数据的方法,如文件1-8所示。

  1. TavgpvNumDao.java
  2. import java.sql.Connection;
  3. import java.sql.PreparedStatement;
  4. import java.sql.ResultSet;
  5. import java.util.ArrayList;
  6. import java.util.List;
  7. import org.apache.log4j.Logger;
  8. import cn.itcast.pojo.TavgpvNum;
  9. import cn.itcast.util.DBContent;
  10. public class TAvgpvNumDao {
  11. public static Logger logger =
  12. Logger.getLogger(TAvgpvNumDao.class);
  13. public static List getTAvgpvNum(
  14. String startDate,String endDate) {
  15. // 数据库连接
  16. Connection conn= null;
  17. //结果集是一个存储查询结果的对象
  18. ResultSet set = null;
  19. // 数据库操作
  20. List list = new ArrayList();
  21. // 实例化PreparedStatement对象
  22. PreparedStatement stmt = null;
  23. try {
  24. conn = DBContent.connDB();
  25. stmt = conn.prepareStatement(“select * from t_avgpv_num “
    • “where dateStr between ? and ? “
    • “order by dateStr asc;”);
  26. stmt.setString(1, startDate);
  27. stmt.setString(2, endDate);
  28. //执行的sql语句将查询结果返回给对象
  29. set = stmt.executeQuery();
  30. //将sql语句查询结果放入到list集合中
  31. while (set.next()) {
  32. TavgpvNum bean = new TavgpvNum();
  33. bean.setDatastr(set.getString(“dateStr”));
  34. bean.setAvgpvnum(set.getBigDecimal(“avgPvNum”));
  35. list.add(bean);
  36. }
  37. } catch (Exception e) {
  38. logger.error(e.getMessage());
  39. } finally{
  40. //释放资源
  41. DBContent.free(set, stmt, conn);
  42. }
  43. return list;
  44. }
  45. }

上述代码中,第25-29行代码指定执行的SQL语句,用来查询指定日期区间的平均PV量,并按照日期dateStr升序排序,并指定传入SQL语句的两个参数;第33-38行代码将数据库获取的数据通过实体类TavgpvNum对象bean储存,将bean放入TavgpvNum类型的集合list中,作为最终返回值。需要注意的是35行和36行中数据类型要与实体类中定义的数据类型一致并且定义的字符串要与数据库对应的字段名一致。

  1. 实现Service层

创建一个cn.itcast.service包,在包中创建TavgpvNumServlet类继承BaseServlet工具类,处理Jsp页面请求的Servlet类,将数据库获取的数据按照业务逻辑进行处理并传给请求的页面,如文件1-9所示。

  1. TavgpvNumServlet.java
  2. import java.io.PrintWriter;
  3. import java.util.ArrayList;
  4. import java.util.List;
  5. import javax.servlet.http.HttpServletRequest;
  6. import javax.servlet.http.HttpServletResponse;
  7. import org.apache.log4j.Logger;
  8. import com.fasterxml.jackson.databind.ObjectMapper;
  9. import cn.itcast.pojo.AvgToPageBean;
  10. import cn.itcast.pojo.TavgpvNum;
  11. import cn.itcast.util.BaseServlet;
  12. import cn.itcast.manager.TAvgpvNumManager;
  13. public class TavgpvNumServlet extends BaseServlet {
  14. private static final long serialVersionUID = 1L;
  15. public static Logger logger =
  16. Logger.getLogger(TavgpvNumServlet.class);
  17. public void execute(HttpServletRequest request
  18. , HttpServletResponse response) throws Exception {
  19. response.setContentType(“text/html;charset=utf-8”);
  20. TAvgpvNumManager scale= new TAvgpvNumManager();
  21. List list = new ArrayList();
  22. //获取数据库传过来的数据
  23. list = scale.getTAvgpvNumList(“2013-09-18”, “2013-09-24”);
  24. int size = 7;
  25. String[] dates = new String[size];
  26. double[] datas = new double[size];
  27. int i = 0;
  28. for (TavgpvNum tavgpvNum : list) {
  29. dates[i] = tavgpvNum.getDatastr();
  30. datas[i] = tavgpvNum.getAvgpvnum().doubleValue();
  31. i++;
  32. }
  33. AvgToPageBean bean = new AvgToPageBean();
  34. bean.setDates(dates);
  35. bean.setData(datas);
  36. ObjectMapper om = new ObjectMapper();
  37. String beanJson = null;
  38. beanJson = om.writeValueAsString(bean);
  39. PrintWriter out = response.getWriter();
  40. out.print(beanJson);
  41. out.flush();
  42. out.close();
  43. }
  44. }

上述代码中,第22行代码指定查询日期的起始与结束获取查询结果数据;第27-31行代码遍历list(查询结果)集合将日期和pv量分别放入dates和datas两个列表中;第32-34行代码将数据封装到AvgToPageBean类的实例化对象bean中;第37行代码将数据转为Json格式;第38-39行代码将数据返回给前端。

  1. 实现Manager层

Manager层为通用处理层,作为Service层与Dao层的中间件,与Dao层交互,对多个 Dao 的组合复用
创建一个cn.itcast.manager包,在包中创建TAvgpvNumManager类,定义getTAvgpvNumList方法获取Dao层的数据,如文件1-10所示。

  1. TavgpvNumManager.java
  2. import java.util.List;
  3. import cn.itcast.dao.TAvgpvNumDao;
  4. import cn.itcast.pojo.TavgpvNum;
  5. public class TAvgpvNumManager {
  6. public List getTAvgpvNumList(
  7. String startDate,String endDate) {
  8. //调用TAvgpvNumDao类中的getTAvgpvNum方法
  9. return TAvgpvNumDao.getTAvgpvNum(startDate, endDate);
  10. }
  11. }
  12. 实现页面功能

最终在index.jsp页面编写JS代码,利用Echarts工具,生成Echarts图例,接收后端发送的Json数据,Echarts使用非常简单,读者可以参照此案例作为模板,完成index.jsp页面的其它图例,核心代码片段如下所示。

从上述代码片段可以看出,首先编写了一个div标签,id=“main1”,然后使用JQuery事件创建Echarts图例,在setOption方法中的参数是固定模板,只需要添加所需的说明文字即可,Echarts是通过Ajax异步加载数据来实现动态填充X轴与Y轴坐标系数据,那么可以利用Service层返回的数据data来实现。
至此编写代码完毕,下面右键单击项目,选择“Run As”→“Maven build”,在Goals文本框输入“tomcat7:run”启动Tomcat服务,在浏览器输入http://localhost:8080/index.jsp网站,即可打开网站流量日志分析系统的主界面,功能模块效果如图1-2所示。
网络日志流数据可视化 - 图2

  1. 七日人均浏览量