手机app日志分析系统(一)

一、项目介绍
-------------------------------------------------
   1.App开发商
      每个开发商可以有多个App产品

   2.App软件

   3.数据服务平台提供商
      Umeng,
      向App开发商提供服务。提供App使用情况的统计服务。

   4.SDK
      数据服务平台提供商提供给App开发商的软件包。
      内置log上报程序。

   5.用户
      每个使用App的设备。

   6.租户
      购买了数据服务平台提供商服务的App开发商。


二、项目分析
------------------------------------------------------
   1.用户
      设备id,唯一性

   2.新增用户
      首次打开应用的用户。
      卸载再安装不是新增

   3.活跃用户
      指定时间段内打开过app的用户即为活跃用户。多次打开算一次。

   4.月活率
      活跃用户 / 截止到当月累计用户总数。

   5.沉默用户
      两天时间没有启动过app的用户就算沉默用户。

   6.版本分布
      计算各版本的新增用户、活跃用户、启动次数。

   7.本周回流用户
      上周没启动,本周启动的用户

   8.连续n周活跃用户
      连续n周,每周至少启动一次。

   9.忠诚用户
      连续5周以上活跃用户

   10.连续活跃用户
      连续2周以上

   11.近期流失用户
      连续n(2<= n <= 4)周没有启动应用的用户。

   12.留存用户
      某段时间内的新增用户,在经过一段时间后,仍然使用app的用户。

   13.用户新鲜度
      每天启动app的新老用户比例

   14.单次使用时长
      每次启动使用的时间长度。

   15.日使用时长
      每天的使用累加值。

   16.启动次数计算标准
      两次之间<30s,算作一次启动.


三、日志组成
--------------------------------------------------
    1.启动日志

    2.页面访问日志

    3.事件日志

    4.用户使用日志

    5.错误日志


四、开始项目 -- 初始化web日志收集程序
----------------------------------------------------
    1.创建新项目UmengProject

    2.创建公共模块app-analyze-common,存放用于跨模块间访问的类。添加maven依赖
        a.创建日志类AppLogEntity          
package com.test.app.common;

            /**
             * AppLog实体类
             * 内部含有各种日志时间的集合。
             */
            public class AppLogEntity {
                private String appId;                //应用唯一标识
                private String tenantId;             //租户唯一标识,企业用户
                private String deviceId;            //设备唯一标识
                private String appVersion;            //版本
                private String appChannel;            //渠道,安装时就在清单中制定了,appStore等。
                private String appPlatform;            //平台
                private String osType;                //操作系统
                private String deviceStyle;            //机型

                private AppStartupLog[] appStartupLogs;        //启动相关信息的数组
                private AppPageLog[] appPageLogs;            //页面跳转相关信息的数组
                private AppEventLog[] appEventLogs;            //事件相关信息的数组
                private AppUsageLog[] appUsageLogs;            //app使用情况相关信息的数组
                private AppErrorLog[] appErrorLogs;            //错误相关信息的数组

                /** get / set  **/
            }
        b.抽取共性,创建日志基本父类AppBaseLog
            
package com.test.app.common;

              import java.io.Serializable;

              /**
               * AppBaseLog
               */
              public class AppBaseLog implements Serializable {
                  private Long createdAtMs;            //日志创建时间
                  private String appId;                //应用唯一标识
                  private String tenantId;             //租户唯一标识,企业用户
                  private String deviceId;            //设备唯一标识
                  private String appVersion;            //版本
                  private String appChannel;            //渠道,安装时就在清单中制定了,appStore等。
                  private String appPlatform;            //平台
                  private String osType;                //操作系统
                  private String deviceStyle;            //机型

                  /*省略get set*/
              }
        c.创建日志具体分类的相关类AppStartupLog等
            ===================================================
            
package com.test.app.common;

            /**
             * 启动日志
             */
            public class AppStartupLog extends AppBaseLog {
                private String country;                 //国家,终端不用上报,服务器自动填充该属性
                private String province;                //省份,终端不用上报,服务器自动填充该属性
                private String ipAddress;               //ip地址

                private String network;                //网络
                private String carrier;                //运营商

                private String brand;               //品牌
                private String deviceStyle;            //机型
                private String screenSize;            //分辨率
                private String osType;                //操作系统

                //省略getset

            }
            ====================================================================
            
package com.test.app.common;

            /**
             * 应用上报的app错误日志相关信息
             */
            public class AppErrorLog extends AppBaseLog {

                private static final long serialVersionUID = 1L;

                private String errorBrief;    //错误摘要
                private String errorDetail;       //错误详情
            }
            =======================================================================

            
package com.test.app.common;

            import java.util.Map;

            /**
             * 应用上报的事件相关信息
             *
             */
            public class AppEventLog extends AppBaseLog {

                private static final long serialVersionUID = 1L;

                private String eventId;          //事件唯一标识
                private Long eventDurationSecs;    //事件持续时长
                private Map<String,String> paramKeyValueMap;      //参数名/值对
            }
            =========================================================================
           
 package com.test.app.common;

            /**
             * 应用上报的页面相关信息
             *
             */
            public class AppPageLog extends AppBaseLog {

                private static final long serialVersionUID = 1L;

                /*
                 * 一次启动中的页面访问次数(应保证每次启动的所有页面日志在一次上报中,即最后一条上报的页面记录的nextPage为空)
                 */
                private int pageViewCntInSession = 0;

                private String pageId;      //页面id
                private int visitIndex = 0;    //访问顺序号,0为第一个页面
                private String nextPage;   //下一个访问页面,如为空则表示为退出应用的页面
                private Long stayDurationSecs = (long) 0;  //当前页面停留时长
            }
            =============================================================================
           
 package com.test.app.common;

            /**
             * 应用上报的使用时长相关信息
             */
            public class AppUsageLog extends AppBaseLog {

                private static final long serialVersionUID = 1L;

                private Long singleUseDurationSecs;    //单次使用时长(秒数),指一次启动内应用在前台的持续时长
                private Long singleUploadTraffic;     //单次使用过程中的上传流量
                private Long singleDownloadTraffic;       //单次使用过程中的下载流量
            }

        d.创建util工具包com.test.app.util和属性拷贝工具类PropertiesUtil
            
package com.test.app.util;

            import java.beans.BeanInfo;
            import java.beans.IntrospectionException;
            import java.beans.Introspector;
            import java.beans.PropertyDescriptor;
            import java.lang.reflect.Method;

            /**
             * 通过内省实现属性复制
             */
            public class PropertiesUtil {

                //复制属性
                //要将AppLogEntity中的所有相关属性的值赋值给对用的具体log的属性值[因为具体log的属性值是空的]
                public static void copyProperties(Object src, Object des)
                {
                    try {
                        BeanInfo srcInfo = Introspector.getBeanInfo(src.getClass());
                        //属性描述符
                        PropertyDescriptor[] sarr = srcInfo.getPropertyDescriptors();
                        for(PropertyDescriptor p : sarr)
                        {
                            Method getter = p.getReadMethod();
                            Method setter = p.getWriteMethod();
                            //获取set方法描述符的name
                            String setName = setter.getName();
                            //获取set方法的参数类型
                            Class [] param = setter.getParameterTypes();
                            try {
                                //通过get方法描述符,获取src的属性值
                                Object value = getter.invoke(src);
                                //通过src set方法描述符找到des的set方法,给des的属性赋值
                                Method desSettrer = des.getClass().getMethod(setName, param);
                                desSettrer.invoke(des, value);

                            } catch (Exception e) {
                                //出现异常说明des中没有src的属性
                                continue;
                            }
                        }
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }

    3.创建app日志收集web模块app-log-collect-web
        a.添加maven依赖
            <?xml version="1.0" encoding="UTF-8"?>
            <project xmlns="http://maven.apache.org/POM/4.0.0"
                     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                     xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
                <modelVersion>4.0.0</modelVersion>

                <groupId>com.test</groupId>
                <artifactId>app-logs-collect-web</artifactId>
                <version>1.0-SNAPSHOT</version>

                <dependencies>

                    <dependency>
                        <groupId>junit</groupId>
                        <artifactId>junit</artifactId>
                        <version>4.11</version>
                    </dependency>
                    <dependency>
                        <groupId>com.fasterxml.jackson.core</groupId>
                        <artifactId>jackson-core</artifactId>
                        <version>2.8.8</version>
                    </dependency>
                    <dependency>
                        <groupId>com.fasterxml.jackson.core</groupId>
                        <artifactId>jackson-databind</artifactId>
                        <version>2.8.3</version>
                    </dependency>

                    <dependency>
                        <groupId>com.maxmind.db</groupId>
                        <artifactId>maxmind-db</artifactId>
                        <version>1.0.0</version>
                    </dependency>

                    <dependency>
                        <groupId>org.springframework</groupId>
                        <artifactId>spring-webmvc</artifactId>
                        <version>4.3.5.RELEASE</version>
                    </dependency>
                    <dependency>
                        <groupId>javax.servlet</groupId>
                        <artifactId>servlet-api</artifactId>
                        <version>2.5</version>
                    </dependency>
                    <dependency>
                        <groupId>com.test</groupId>
                        <artifactId>app-analyze-common</artifactId>
                        <version>1.0-SNAPSHOT</version>
                    </dependency>
                    <dependency>
                        <groupId>com.alibaba</groupId>
                        <artifactId>fastjson</artifactId>
                        <version>1.2.24</version>
                    </dependency>
                </dependencies>
            </project>

        b.添加app-analyze-common模块依赖,共享模块资源
            Project-Structure --> dependencies --> 3.Module De...
            别忘记打钩,不然引用不了

        c.创建java包
            com.test.applogs.collect.web.controller

        d.编写WEB-INF/web.xml
            <?xml version="1.0" encoding="UTF-8"?>
            <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
                     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                     xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
                     version="3.1">
                <servlet>
                    <servlet-name>controller</servlet-name>
                    <servlet-class>org.springframework.web.servlet.DispatcherServlet
                    </servlet-class>
                </servlet>
                <servlet-mapping>
                    <servlet-name>controller</servlet-name>
                    <url-pattern>/</url-pattern>
                </servlet-mapping>

            </web-app>

        e.创建新文件WEB-INF/controller-servlet.xml
            <?xml version="1.0" encoding="UTF-8"?>
            <beans xmlns="http://www.springframework.org/schema/beans"
                   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                   xmlns:mvc="http://www.springframework.org/schema/mvc"
                   xmlns:context="http://www.springframework.org/schema/context"
                   xsi:schemaLocation="http://www.springframework.org/schema/beans
                                    http://www.springframework.org/schema/beans/spring-beans.xsd
                                    http://www.springframework.org/schema/mvc
                                    http://www.springframework.org/schema/mvc/spring-mvc-4.3.xsd
                                    http://www.springframework.org/schema/context
                                    http://www.springframework.org/schema/context/spring-context-4.3.xsd">
                <!-- 配置扫描路径 -->
                <context:component-scan
                        base-package="com.test.applogs.collect.web.controller"/>
                <!-- 使用注解驱动 -->
                <mvc:annotation-driven/>
                <!-- 内部资源视图解析器 -->
                <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
                    <property name="prefix" value="/views"/>
                    <property name="suffix" value=".jsp"/>
                </bean>
                <!-- 此处乃进行json数据传输的关键,当配置 -->
                <bean id="jsonMapping"  class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter"/>
                <bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
                    <property name="messageConverters">
                        <list>
                            <ref bean="jsonMapping"/>
                        </list>
                    </property>
                </bean>
            </beans>

        f.创建日志收集控制器
            
package com.test.applogs.collect.web.controller;

            import com.alibaba.fastjson.JSONObject;
            import com.test.app.util.PropertiesUtil;
            import com.test.app.common.*;
            import com.test.app.common.AppLogEntity;
            import com.test.app.common.AppStartupLog;
            import org.springframework.stereotype.Controller;
            import org.springframework.web.bind.annotation.RequestBody;
            import org.springframework.web.bind.annotation.RequestMapping;
            import org.springframework.web.bind.annotation.RequestMethod;
            import org.springframework.web.bind.annotation.ResponseBody;

            import javax.servlet.http.HttpServletRequest;

            /**
             */
            @Controller()
            @RequestMapping("/coll")
            public class CollectLogController {

                /**
                 * 启动日志收集
                 */
                @RequestMapping(value = "/index", method = RequestMethod.POST)
                @ResponseBody
                public AppLogEntity collect(@RequestBody AppLogEntity e, HttpServletRequest req) {

                    System.out.println("=============================");
                    //server时间
                    long myTime = System.currentTimeMillis() ;
                    //客户端时间
                    long clientTime = Long.parseLong(req.getHeader("clientTime"));
                    //时间校对
                    long diff = myTime - clientTime ;

                    //对e进行处理,将具体日志分类的属性值填充完毕
                    processLogs(e);
                    //修正日志时间
                    verifyTime(e,diff);
                    String json = JSONObject.toJSONString(e);

                    System.out.println(json);
                    return e;
                }

                /**
                 * 校对各个具体日志的创建时间(使用服务器时间差diff)
                 */
                private void verifyTime(AppLogEntity e, long diff)
                {
                    //启动修正
                    //startuplog
                    for(AppBaseLog log : e.getAppStartupLogs()){
                        log.setCreatedAtMs(log.getCreatedAtMs() + diff );
                    }
                    for(AppBaseLog log : e.getAppUsageLogs()){
                        log.setCreatedAtMs(log.getCreatedAtMs() + diff );
                    }
                    for(AppBaseLog log : e.getAppPageLogs()){
                        log.setCreatedAtMs(log.getCreatedAtMs() + diff );
                    }
                    for(AppBaseLog log : e.getAppEventLogs()){
                        log.setCreatedAtMs(log.getCreatedAtMs() + diff );
                    }
                    for(AppBaseLog log : e.getAppErrorLogs()){
                        log.setCreatedAtMs(log.getCreatedAtMs() + diff );
                    }
                }

                /**
                 * 将Log的属性分类复制到各个具体的log中
                 */
                private void processLogs(AppLogEntity e){

                    for(AppStartupLog log : e.getAppStartupLogs()){
                        PropertiesUtil.copyProperties(e,log);
                    }
                    for(AppErrorLog log : e.getAppErrorLogs()){
                        PropertiesUtil.copyProperties(e,log);
                    }
                    for(AppEventLog log : e.getAppEventLogs()){
                        PropertiesUtil.copyProperties(e,log);
                    }
                    for(AppPageLog log : e.getAppPageLogs()){
                        PropertiesUtil.copyProperties(e,log);
                    }
                    for(AppUsageLog log : e.getAppUsageLogs()){
                        PropertiesUtil.copyProperties(e,log);
                    }
                }
            }
        g.创建tomcat-server启动程序
            Run --> Edit Configurations --> + --> tomcat server --> ...
            Project Structure --> Artifacts --> Add All Maven Jars --> ...
            [注意!]Project Structure --> Artifacts --> Add Common模块到out class --> 不然依赖的其他模块是加载不到的,同时添加maven依赖的支持


    4.创建app-logs-phone模块,用于模拟手机生成日志
        a.添加maven依赖[可以通过maven的手段,达到访问其他模块的内容,比如common模块]
            <?xml version="1.0" encoding="UTF-8"?>
            <project xmlns="http://maven.apache.org/POM/4.0.0"
                     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                     xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
                <modelVersion>4.0.0</modelVersion>

                <groupId>com.test</groupId>
                <artifactId>app-logs-phone</artifactId>
                <version>1.0-SNAPSHOT</version>

                <dependencies>
                    <dependency>
                        <groupId>com.alibaba</groupId>
                        <artifactId>fastjson</artifactId>
                        <version>1.2.24</version>
                    </dependency>
                    <dependency>
                        <groupId>com.test</groupId>
                        <artifactId>app-analyze-common</artifactId>
                        <version>1.0-SNAPSHOT</version>
                    </dependency>
                </dependencies>
            </project>

        b.创建海量日志生成模拟程序
            
package com.test.app.client;

            import com.alibaba.fastjson.JSONObject;
            import com.test.app.common.*;
            //import com.test.app.util.PropertiesUtil;

            import java.io.File;
            import java.io.FileWriter;
            import java.io.InputStream;
            import java.net.HttpURLConnection;
            import java.net.URL;
            import java.util.HashMap;
            import java.util.Map;
            import java.util.Random;

            /**
             * 数据生成程序
             */
            public class TestGenData {

               /**
                *
                */
               private static String url = "http://localhost:8080/coll/index";

               private static Random random = new Random();

               private static String appId = "sdk34734";
               private static String[] tenantIds = {"cake"};
               private static String[] deviceIds = initDeviceId();
               private static String[] appVersions = {"3.2.1", "3.2.2"};
               private static String[] appChannels = {"youmeng1", "youmeng2"};
               private static String[] appPlatforms = {"android", "ios"};

               private static Long[] createdAtMsS = initCreatedAtMs();
               //国家,终端不用上报,服务器自动填充该属性
               private static String[] countrys = {"America", "china"};
               //省份,终端不用上报,服务器自动填充该属性
               private static String[] provinces = {"Washington", "jiangxi", "beijing"};
               //网络
               private static String[] networks = {"WiFi", "CellNetwork"};
               //运营商
               private static String[] carriers = {"中国移动", "中国电信", "EE"};
               //机型
               private static String[] deviceStyles = {"iPhone 6", "iPhone 6 Plus", "红米手机1s"};
               //分辨率
               private static String[] screenSizes = {"1136*640", "960*640", "480*320"};
               //操作系统
               private static String[] osTypes = {"8.3", "7.1.1"};
               //品牌
               private static String[] brands = {"三星", "华为", "Apple", "魅族", "小米", "锤子"};
               //事件唯一标识
               private static String[] eventIds = {"popMenu", "autoImport", "BookStore"};
               //事件持续时长
               private static Long[] eventDurationSecsS = {new Long(25), new Long(67), new Long(45)};

               static Map<String, String> map1 = new HashMap<String, String>() {
                  {
                     put("testparam1key", "testparam1value");
                     put("testparam2key", "testparam2value");
                  }
               };
               static Map<String, String> map2 = new HashMap<String, String>() {
                  {
                     put("testparam3key", "testparam3value");
                     put("testparam4key", "testparam4value");
                  }
               };
               private static Map[] paramKeyValueMapsS = {map1, map2};

               //单次使用时长(秒数),指一次启动内应用在前台的持续时长
               private static Long[] singleUseDurationSecsS = initSingleUseDurationSecs();

               private static String[] errorBriefs = {"at cn.lift.dfdf.web.AbstractBaseController.validInbound(AbstractBaseController.java:72)", "at cn.lift.appIn.control.CommandUtil.getInfo(CommandUtil.java:67)"};        //错误摘要
               private static String[] errorDetails = {"java.lang.NullPointerException\\n    " + "at cn.lift.appIn.web.AbstractBaseController.validInbound(AbstractBaseController.java:72)\\n " + "at cn.lift.dfdf.web.AbstractBaseController.validInbound", "at cn.lift.dfdfdf.control.CommandUtil.getInfo(CommandUtil.java:67)\\n " + "at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)\\n" + " at java.lang.reflect.Method.invoke(Method.java:606)\\n"};        //错误详情
               //页面id
               private static String[] pageIds = {"list.html", "main.html", "test.html"};
               //访问顺序号,0为第一个页面
               private static int[] visitIndexs = {0, 1, 2, 3, 4};
               //下一个访问页面,如为空则表示为退出应用的页面
               private static String[] nextPages = {"list.html", "main.html", "test.html", null};
               //当前页面停留时长
               private static Long[] stayDurationSecsS = {new Long(45), new Long(2), new Long(78)};

               //启动相关信息的数组
               private static AppStartupLog[] appStartupLogs = initAppStartupLogs();
               //页面跳转相关信息的数组
               private static AppPageLog[] appPageLogs = initAppPageLogs();
               //事件相关信息的数组
               private static AppEventLog[] appEventLogs = initAppEventLogs();
               //app使用情况相关信息的数组
               private static AppUsageLog[] appUsageLogs = initAppUsageLogs();
               //错误相关信息的数组
               private static AppErrorLog[] appErrorLogs = initAppErrorLogs();

               private static String[] initDeviceId() {
                  String base = "device22";
                  String[] result = new String[100];
                  for (int i = 0; i < 100; i++) {
                     result[i] = base + i + "";
                  }
                  return result;
               }

               private static Long[] initCreatedAtMs() {
                  Long createdAtMs = System.currentTimeMillis();
                  Long[] result = new Long[11];
                  for (int i = 0; i < 10; i++) {
                     result[i] = createdAtMs - (long) (i * 24 * 3600 * 1000);
                  }
                  result[10] = createdAtMs;
                  return result;
               }

               private static Long[] initSingleUseDurationSecs() {
                  Random random = new Random();
                  Long[] result = new Long[200];
                  for (int i = 1; i < 200; i++) {
                     result[i] = (long) random.nextInt(200);
                  }
                  return result;
               }

               //启动相关信息的数组
               private static AppStartupLog[] initAppStartupLogs() {
                  AppStartupLog[] result = new AppStartupLog[10];
                  for (int i = 0; i < 10; i++) {
                     AppStartupLog appStartupLog = new AppStartupLog();
                     appStartupLog.setCountry(countrys[random.nextInt(countrys.length)]);
                     appStartupLog.setProvince(provinces[random.nextInt(provinces.length)]);
                     appStartupLog.setNetwork(networks[random.nextInt(networks.length)]);
                     appStartupLog.setCarrier(carriers[random.nextInt(carriers.length)]);
                     appStartupLog.setDeviceStyle(deviceStyles[random.nextInt(deviceStyles.length)]);
                     appStartupLog.setScreenSize(screenSizes[random.nextInt(screenSizes.length)]);
                     appStartupLog.setOsType(osTypes[random.nextInt(osTypes.length)]);
                     appStartupLog.setBrand(brands[random.nextInt(brands.length)]);
                     appStartupLog.setCreatedAtMs(createdAtMsS[random.nextInt(createdAtMsS.length)]);
                     result[i] = appStartupLog;
                  }
                  return result;
               }

               //页面跳转相关信息的数组
               private static AppPageLog[] initAppPageLogs() {
                  AppPageLog[] result = new AppPageLog[10];
                  for (int i = 0; i < 10; i++) {
                     AppPageLog appPageLog = new AppPageLog();
                     String pageId = pageIds[random.nextInt(pageIds.length)];
                     int visitIndex = visitIndexs[random.nextInt(visitIndexs.length)];
                     String nextPage = nextPages[random.nextInt(nextPages.length)];
                     while (pageId.equals(nextPage)) {
                        nextPage = nextPages[random.nextInt(nextPages.length)];
                     }
                     Long stayDurationSecs = stayDurationSecsS[random.nextInt(stayDurationSecsS.length)];

                     appPageLog.setPageId(pageId);
                     appPageLog.setStayDurationSecs(stayDurationSecs);
                     appPageLog.setVisitIndex(visitIndex);
                     appPageLog.setNextPage(nextPage);
                     appPageLog.setCreatedAtMs(createdAtMsS[random.nextInt(createdAtMsS.length)]);
                     result[i] = appPageLog;
                  }
                  return result;
               }

               ;

               //事件相关信息的数组
               private static AppEventLog[] initAppEventLogs() {
                  AppEventLog[] result = new AppEventLog[10];
                  for (int i = 0; i < 10; i++) {
                     AppEventLog appEventLog = new AppEventLog();
                     appEventLog.setEventId(eventIds[random.nextInt(eventIds.length)]);
                     appEventLog.setParamKeyValueMap(paramKeyValueMapsS[random.nextInt(paramKeyValueMapsS.length)]);
                     appEventLog.setEventDurationSecs(eventDurationSecsS[random.nextInt(eventDurationSecsS.length)]);
                     appEventLog.setCreatedAtMs(createdAtMsS[random.nextInt(createdAtMsS.length)]);
                     result[i] = appEventLog;
                  }
                  return result;
               }

               ;

               //app使用情况相关信息的数组
               private static AppUsageLog[] initAppUsageLogs() {
                  AppUsageLog[] result = new AppUsageLog[10];
                  for (int i = 0; i < 10; i++) {
                     AppUsageLog appUsageLog = new AppUsageLog();
                     appUsageLog.setSingleUseDurationSecs(singleUseDurationSecsS[random.nextInt(singleUseDurationSecsS.length)]);
                     appUsageLog.setCreatedAtMs(createdAtMsS[random.nextInt(createdAtMsS.length)]);
                     result[i] = appUsageLog;
                  }
                  return result;
               }

               ;

               //错误相关信息的数组
               private static AppErrorLog[] initAppErrorLogs() {
                  AppErrorLog[] result = new AppErrorLog[10];
                  for (int i = 0; i < 10; i++) {
                     AppErrorLog appErrorLog = new AppErrorLog();
                     appErrorLog.setErrorBrief(errorBriefs[random.nextInt(errorBriefs.length)]);
                     appErrorLog.setErrorDetail(errorDetails[random.nextInt(errorDetails.length)]);
                     appErrorLog.setCreatedAtMs(createdAtMsS[random.nextInt(createdAtMsS.length)]);
                     appErrorLog.setOsType(osTypes[random.nextInt(osTypes.length)]);
                     appErrorLog.setDeviceStyle(deviceStyles[random.nextInt(deviceStyles.length)]);
                     result[i] = appErrorLog;
                  }
                  return result;
               }

               private static void httpPost(String urlString, String params) {
                  URL url;

                  try {
                     url = new URL(urlString);
                     HttpURLConnection conn = (HttpURLConnection) url.openConnection();
                     conn.setRequestMethod("POST");
                     conn.setDoOutput(true);
                     conn.setDoInput(true);
                     conn.setUseCaches(false);
                     conn.setInstanceFollowRedirects(true);
                     conn.setRequestProperty("User-Agent",
                           "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:26.0) Gecko/20100101 Firefox/26.0");
                     conn.setRequestProperty("Content-Type", "application/json");
                     conn.setConnectTimeout(1000 * 5);
                     conn.connect();
                     conn.getOutputStream().write(params.getBytes("utf8"));
                     conn.getOutputStream().flush();
                     conn.getOutputStream().close();
                     byte[] buffer = new byte[1024];
                     StringBuffer sb = new StringBuffer();
                     InputStream in = conn.getInputStream();
                     int httpCode = conn.getResponseCode();
                     System.out.println(in.available());
                     while (in.read(buffer, 0, 1024) != -1) {
                        sb.append(new String(buffer));
                     }
                     System.out.println("sb:" + sb.toString());
                     in.close();
                     System.out.println(httpCode);
                  } catch (Exception e) {
                     e.printStackTrace();
                  }
               }

               public static void main(String[] args) {
                  Test1();
               }

               private static void Test1() {
                  Random random = new Random();
                  try {
                     //发送数据
                     for (int i = 1; i <= 2000; i++) {
                        AppLogEntity logEntity = new AppLogEntity();
                        //渠道
                        logEntity.setAppChannel(appChannels[random.nextInt(appChannels.length)]);
                        //appid
                        logEntity.setAppId(appId);
                        //platform
                        logEntity.setAppPlatform(appPlatforms[random.nextInt(appPlatforms.length)]);
                        logEntity.setAppVersion(appVersions[random.nextInt(appVersions.length)]);
                        String tenantId = tenantIds[random.nextInt(tenantIds.length)];
                        if (tenantId != null) {
                           logEntity.setTenantId(tenantId);
                        }
                        logEntity.setTenantId(tenantIds[random.nextInt(tenantIds.length)]);
                        logEntity.setDeviceId(deviceIds[random.nextInt(deviceIds.length)]);

                        //模拟startup log集合
                        logEntity.setAppStartupLogs(new AppStartupLog[]{appStartupLogs[random.nextInt(appStartupLogs.length)]});
                        logEntity.setAppEventLogs(new AppEventLog[]{appEventLogs[random.nextInt(appEventLogs.length)]});
                        logEntity.setAppErrorLogs(new AppErrorLog[]{appErrorLogs[random.nextInt(appErrorLogs.length)]});
                        logEntity.setAppPageLogs(new AppPageLog[]{appPageLogs[random.nextInt(appPageLogs.length)]});
                        logEntity.setAppUsageLogs(new AppUsageLog[]{appUsageLogs[random.nextInt(appUsageLogs.length)]});
                        try {
                           //将对象转换成json string
                           String json = JSONObject.toJSONString(logEntity);
                           UploadUtil.upload(json);
                           Thread.sleep(2000);
                        } catch (Exception ex) {
                           System.out.println(ex);
                        }
                     }
                  } catch (Exception ex) {
                     ex.printStackTrace();
                  }
               }

               private static void Test2() {
                  boolean result = map1.isEmpty();
                  System.out.println(result);
               }
            }
        c.创建UploadUtil类,模拟日志上传到web
           
package com.test.app.client;

           import java.io.InputStream;
           import java.io.OutputStream;
           import java.net.HttpURLConnection;
           import java.net.URL;

           /**
            * 模拟手机上报日志程序
            */
           public class UploadUtil {

               /**
                * 上传日志
                */
               public static void upload(String json) throws Exception {
                  try{
                     //输入流
                     InputStream in = ClassLoader.getSystemResourceAsStream("log.json");

                     URL url = new URL("http://localhost:8080/coll/index");
                     HttpURLConnection conn = (HttpURLConnection) url.openConnection();
                     //设置请求方式为post
                     conn.setRequestMethod("POST");

                     //时间头用来供server进行时钟校对的
                     conn.setRequestProperty("clientTime",System.currentTimeMillis() + "");
                     //允许上传数据
                     conn.setDoOutput(true);
                     //设置请求的头信息,设置内容类型
                     conn.setRequestProperty("Content-Type", "application/json");


                     //输出流
                     OutputStream out = conn.getOutputStream();
                     out.write(json.getBytes());
                     out.flush();
                     out.close();
                     in.close();
                     int code = conn.getResponseCode();
                     System.out.println(code);
                  }
                  catch (Exception e){
                     e.printStackTrace();
                  }
               }
           }
        d.生产环境下特别注意时钟问题
            手机客户端的时间可能不准,所以不能按照客户端提供的时间来算
            所以。服务器端收集文件之后要进行时间校对
                1)client
                    发送数据同时,写入clientTime头。
                    conn.setRequestProperty("clientTime",System.currentTimeMillis() + "");
                2)web server
                    //server时间
                    long myTime = System.currentTimeMillis() ;
                    //客户端时间
                    long clientTime = Long.parseLong(req.getHeader("clientTime"));
                    //时间校对
                    long diff = myTime - clientTime ;

                3)完成Log实体中公共部分属性和Log类中间属性复制。







评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值