大数据学习day39----数据仓库02------1. log4j 2. 父子maven工程(子spring项目的创建)3.项目开发(埋点日志预处理-json数据解析、清洗过滤、数据集成实现、uid回补)
1. log4j(具体见log4j文档)
log4j是一个java系统中用于输出日志信息的工具。log4j可以将日志定义成多种级别:ERROR / WARN / INFO / DEBUG
log4j通过获取到一个logger对象来输出日志:
val logger = Logger.getLogger("logger名称");
logger.info("日志内容")
所拿到的这些logger对象之间是有“父子”关系的,所有logger都是rootLogger的子!
"org.apache" 这个名字的logger是 "org"这个名字的logger的子!
log4j的日志输出格式和目的地,都是可以通过参数配置的;
目的地的控制用Appender输出组件
常用的Appender组件:
log4j.appender.xx=org.apache.log4j.ConsoleAppender
log4j.appender.rollingFile=org.apache.log4j.RollingFileAppender
格式的控制用LayOut布局组件
log4j.appender.xx.layout=org.apache.log4j.PatternLayout
log4j.appender.xx.layout.ConversionPattern=[%-5p] %d(%r) --> [%t] %l: %m %x %n
2. 父子maven工程
(1)创建一个父工程(如平常创建一样),父工程中不写代码,所以最好将src文件夹删除(比如公司新手会将代码误写入该文件夹)
(2)创建子工程
得到如下图
接着如下所示
到此,一个子maven项目dataware即建立成功,子项目的pom文件如下所示
若是子工程中的父工程配置删除后,子工程不认识父工程,但是父工程认识子工程
(3)说明
A. 父工程pom文件中引入公共的依赖和插件(会被子工程pom继承),此处有几处规范
- 依赖定义的管理(不是真正引入依赖) 标签:<dependencyManagement><dependencyManagement>
作用:父项目中某个子项目需要用到某个依赖,这个时候若是在子项目的pom文件中定义这个依赖的版本,当另外一个子项目也要这个依赖时,由于需要统一依赖的版本,这时另外一个子项目中也需要定义相同版本的依赖。这样就比较麻烦,这个时候就可以使用依赖定义的管理(在父工程中定义子项目需要依赖的版本,子项目中就不需要写依赖的版本),如下
父工程pom文件(部分)
<dependencyManagement>
<dependency>
<groupId>ch.hsr</groupId>
<artifactId>geohash</artifactId>
<version>1.3.0</version>
</dependency>
</dependencies>
</dependencyManagement>
子工程pom文件
- 属性定义(标签:<properties><properties>)
- 依赖排除(标签:<exclusions><exclusions>): 解决jar包的版本冲突
比如下面的spark使用的hadoop版本就出现依赖的冲突
解决办法(排除依赖)
<dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-sql_2.11</artifactId>
<version>${spark.version}</version>
<exclusions>
<exclusion>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-client</artifactId>
</exclusion>
</exclusions>
</dependency>
- 当在idea删除某个项目时,再创建一个同名的项目时,会出错(Idea中记录的东西会冲突)
解决办法:
直接到项目的目录中将idea的相关文件删除掉,如下图所示
spring子项目的创建
3.项目开发(埋点日志预处理-json数据解析、清洗过滤、数据集成实现、uid回补)
3.1 json数据格式如下:
3.2 需求说明
3.2.1 清洗过滤
此处为了记录数据方便,定义一个AppLogBean,该类中定义了两个方法(1.解析json 返回一个case class, 2. 判断一个bean是否有效),并在该类中定义一个case class AppLogBean
AppLogBean代码
package com._51doit.tian.dw.pre import com.alibaba.fastjson.{JSON, JSONObject}
import org.apache.commons.lang3.StringUtils import scala.collection.mutable case class AppLogBean(
eventid :String ,
timestamp :Double ,
event :Map[String,String] ,
uid :String ,
phoneNbr :String ,
sessionId :String ,
imei :String ,
mac :String ,
imsi :String ,
osName :String ,
osVer :String ,
androidId :String ,
resolution :String ,
deviceType :String ,
deviceId :String ,
uuid :String ,
appid :String ,
appVer :String ,
release_ch :String ,
promotion_ch :String ,
longtitude :Double ,
latitude :Double ,
carrier :String ,
netType :String ,
cid_sn :String ,
ip :String,
var province:String = "",
var city:String = "",
var district:String = "",
var dateStr:String = "",
var timeStr:String = ""
) object AppLogBean {
/**
* 解析app埋点json日志,返回一个case class
*/
def parseJson2Bean(line:String): AppLogBean ={
try {
val obj: JSONObject = JSON.parseObject(line)
val eventid: String = obj.getString("eventid")
val timestamp = obj.getString("timestamp").toDouble
val event: JSONObject = obj.getJSONObject("event")
val eventMap: mutable.HashMap[String, String] = new mutable.HashMap[String, String]()
import scala.collection.JavaConversions._
for(ent <- event.entrySet()){
eventMap.put(ent.getKey,ent.getValue.toString)
}
val user = obj.getJSONObject("user")
val uid = user.getString("uid")
val phoneNbr = user.getString("phoneNbr")
val sessionId = user.getString("sessionId") val phone = user.getJSONObject("phone")
val imei = phone.getString("imei")
val mac = phone.getString("mac")
val imsi = phone.getString("imsi")
val osName = phone.getString("osName")
val osVer = phone.getString("osVer")
val androidId = phone.getString("androidId")
val resolution = phone.getString("resolution")
val deviceType = phone.getString("deviceType")
val deviceId = phone.getString("deviceId")
val uuid = phone.getString("uuid") val app = user.getJSONObject("app")
val appid = app.getString("appid")
val appVer = app.getString("appVer")
val release_ch = app.getString("release_ch")
val promotion_ch = app.getString("promotion_ch") val loc = user.getJSONObject("loc")
val longtitude = loc.getDouble("longtitude")
val latitude = loc.getDouble("latitude")
val carrier = loc.getString("carrier")
val netType = loc.getString("netType")
val cid_sn = loc.getString("cid_sn")
val ip = loc.getString("ip") AppLogBean(
eventid ,
timestamp,
eventMap.toMap,
uid ,
phoneNbr ,
sessionId ,
imei ,
mac ,
imsi ,
osName ,
osVer ,
androidId ,
resolution ,
deviceType ,
deviceId ,
uuid ,
appid ,
appVer ,
release_ch ,
promotion_ch ,
longtitude ,
latitude ,
carrier ,
netType ,
cid_sn ,
ip
)
} catch {
case e: Exception => null
case _: Throwable => null
}
} /**
* 判断一条bean是否有效
*/
def isValidBean(bean:AppLogBean): Boolean ={
val uid: String = bean.uid
val imei: String = bean.imei
val uuid: String = bean.uuid
val mac: String = bean.mac
val androidId: String = bean.androidId
val ip: String = bean.ip
// 以上参数不能全为空
var flag1 = StringUtils.isNotBlank((uid + imei + uuid + mac + androidId + ip).replaceAll("null", ""))
val event: Map[String, String] = bean.event
val eventid: String = bean.eventid
val sessionId = bean.sessionId
var flag2 = (event != null) && (StringUtils.isNotBlank(eventid) ) && (StringUtils.isNotBlank(sessionId))
flag1 && flag2
} }
3.2.2 数据解析
此处event数据不用扁平化的原因是,event内的数据类型也不一样
3.2.3 数据集成
3.2.4 数据修正
思路图
注意:此处手机标识 比如imei为空时,作为join on相等的条件时会出错,一定要判断非空,由于sql语句很麻烦(如下),所以开发一个自定义函数,用来判断两个字符串在非空情况下是否相等
每一个手机识别方式都要这样写,很麻烦,以下是自定义的函数
// 开发一个自定义函数,用来判断两个字符串在非空情况下是否相等
val is_equal = (x: String, y: String) => {
if (x != y || StringUtils.isBlank(x) || StringUtils.isBlank(y)) false else true
}
spark.udf.register("is_equal",is_equal)
业务代码
AppEventLogPreprocess
package com._51doit.tian.dw.pre import java.text.SimpleDateFormat import ch.hsr.geohash.GeoHash
import com._51doit.tian.commons.utils.{DictLoadUtil, SparkUtils}
import org.apache.commons.lang3.StringUtils
import org.apache.log4j.{Level, Logger}
import org.apache.spark.sql.{DataFrame, Dataset, SparkSession} object AppEventLogPreprocess {
def main(args: Array[String]): Unit = {
Logger.getLogger("org").setLevel(Level.WARN)
val spark: SparkSession = SparkUtils.getSpark(this.getClass.getSimpleName)
import spark.implicits._
// 加载原始日志文件
val ds: Dataset[String] = spark.read.textFile("E:\\javafile\\dataware\\2019-10-29")
// 解析json
val beans: Dataset[AppLogBean] = ds.map(AppLogBean.parseJson2Bean) /**
* 清洗过滤
*/
val validBeans: Dataset[AppLogBean] = beans
.filter(_ != null)
.filter(AppLogBean.isValidBean(_)) /**
* 数据集成
*/
val dictDF: DataFrame = spark.read.parquet("E:/javafile/spark/out11")
val dictMap: collection.Map[String, (String, String, String)] = DictLoadUtil.loadAreaDict(dictDF)
val bc_dict = spark.sparkContext.broadcast(dictMap)
// 然后进行集成
val integrated: Dataset[AppLogBean] = validBeans.mapPartitions(iter => {
// 取广播变量
val dict: collection.Map[String, (String, String, String)] = bc_dict.value iter.map(bean => {
// 处理GPS坐标
val longtitude: Double = bean.longtitude
val latitude: Double = bean.latitude
// 如果经纬度坐标在中国的经纬度范围之内,才去转geohash编码并从字典中查找省市区
if (longtitude > 0 && longtitude < 120 && latitude > 0 && latitude < 70) {
val geo = GeoHash.withCharacterPrecision(latitude, longtitude, 5).toBase32
val area = dict.getOrElse(geo, ("", "", ""))
bean.province = area._1
bean.city = area._2
bean.district = area._3
}
// 处理时间戳
val sdf: SimpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
val str: Array[String] = sdf.format(bean.timestamp).split(" ")
bean.dateStr = str(0)
bean.timeStr = str(1)
// 返回集成完成的bean
bean
})
})
// integrated.where("trim(province) != '' ").show(10,false)
/**
* 数据修正
*/
val haveUid = integrated.where("uid is not null and trim(uid) !='' ")
val noUid = integrated.where(" uid is null or trim(uid) ='' ")
import org.apache.spark.sql.functions._
val uids = haveUid
.groupBy($"uid")
.agg(
max("imei").as("imei"),
max("imsi").as("imsi"),
max("mac").as("mac"),
max("uuid").as("uuid"),
max("androidId").as("androidId"),
max("deviceId").as("deviceId")
) noUid.createTempView("nouid")
uids.createTempView("uids") // 开发一个自定义函数,用来判断两个字符串在非空情况下是否相等
val is_equal = (x: String, y: String) => {
if (x != y || StringUtils.isBlank(x) || StringUtils.isBlank(y)) false else true
}
spark.udf.register("is_equal",is_equal) // 对没有uid的数据进行回补操作
val part1: DataFrame = spark.sql(
"""
|
|select
|
|a.eventid ,
|a.timestamp ,
|a.event ,
|if(b.uid is not null,b.uid,a.uid) as uid,
|a.phoneNbr ,
|a.sessionId ,
|a.imei ,
|a.mac ,
|a.imsi ,
|a.osName ,
|a.osVer ,
|a.androidId ,
|a.resolution ,
|a.deviceType ,
|a.deviceId ,
|a.uuid ,
|a.appid ,
|a.appVer ,
|a.release_ch ,
|a.promotion_ch ,
|a.longtitude ,
|a.latitude ,
|a.carrier ,
|a.netType ,
|a.cid_sn ,
|a.ip ,
|a.province,
|a.city,
|a.district,
|a.dateStr,
|a.timeStr
|
|from
|
|nouid a left join uids b
| on is_equal(a.imei,b.imei)
| or is_equal(a.imsi,b.imsi)
| or is_equal(a.mac,b.mac)
| or is_equal(a.uuid,b.uuid)
| or is_equal(a.androidId,b.androidId)
| or is_equal(a.deviceId,b.deviceId)
|
""".stripMargin) // 将回补好的数据 union 原来就有uid的数据
val result = haveUid.toDF.union(part1) // 输出结果
result.write.parquet("E:\\javafile\\dataware1\\2019-10-29") spark.close() }
}
大数据学习day39----数据仓库02------1. log4j 2. 父子maven工程(子spring项目的创建)3.项目开发(埋点日志预处理-json数据解析、清洗过滤、数据集成实现、uid回补)的更多相关文章
- Maven 工程下 Spring MVC 站点配置 (二) Mybatis数据操作
详细的Spring MVC框架搭配在这个连接中: Maven 工程下 Spring MVC 站点配置 (一) Maven 工程下 Spring MVC 站点配置 (二) Mybatis数据操作 这篇主 ...
- spring 学习(一):使用 intellijIDEA 创建 maven 工程进行 Spring ioc 测试
spring学习(一):使用 intellijIDEA 创建 maven 工程进行 Spring ioc 测试 ioc 概念 控制反转(Inversion of Control,缩写为IOC),是面向 ...
- Django项目的创建与介绍.应用的创建与介绍.启动项目.pycharm创建启动项目.生命周期.三件套.静态文件.请求及数据.配置Mysql完成数据迁移.单表ORM记录的增删改查
一.Django项目的创建与介绍 ''' 安装Django #在cmd中输入pip3 #出现这个错误Fatal error in launcher: Unable to create process ...
- 大数据学习(12)—— Hive Server2服务
什么是Hive Server2 上一篇我们启动了hive --service metastore服务,可以通过命令行来访问hive服务,但是它不支持多客户端同时访问,参见官网说明:HiveServer ...
- 大数据学习之Linux进阶02
大数据学习之Linux进阶 1-> 配置IP 1)修改配置文件 vi /sysconfig/network-scripts/ifcfg-eno16777736 2)注释掉dhcp #BOOTPR ...
- java 与大数据学习较好的网站
C# C#中 Thread,Task,Async/Await,IAsyncResult 的那些事儿!https://www.cnblogs.com/doforfuture/p/6293926.html ...
- 大数据学习系列之七 ----- Hadoop+Spark+Zookeeper+HBase+Hive集群搭建 图文详解
引言 在之前的大数据学习系列中,搭建了Hadoop+Spark+HBase+Hive 环境以及一些测试.其实要说的话,我开始学习大数据的时候,搭建的就是集群,并不是单机模式和伪分布式.至于为什么先写单 ...
- 大数据学习之Linux基础01
大数据学习之Linux基础 01:Linux简介 linux是一种自由和开放源代码的类UNIX操作系统.该操作系统的内核由林纳斯·托瓦兹 在1991年10月5日首次发布.,在加上用户空间的应用程序之后 ...
- 大数据系列之数据仓库Hive安装
Hive系列博文,持续更新~~~ 大数据系列之数据仓库Hive原理 大数据系列之数据仓库Hive安装 大数据系列之数据仓库Hive中分区Partition如何使用 大数据系列之数据仓库Hive命令使用 ...
随机推荐
- 二进制小数 牛客网 程序员面试金典 C++ Python
二进制小数 牛客网 程序员面试金典 题目描述 有一个介于0和1之间的实数,类型为double,返回它的二进制表示.如果该数字无法精确地用32位以内的二进制表示,返回"Error". ...
- poj 3020 Antenna Placement(二分图最大匹配)
题意: N行M列的矩阵,每个格子里不是 * 就是 O . * :是一个利益点. O:是一个空白点. 每次可以用一个圈覆盖相邻的两个*.(左右相邻或上下相邻). 问最少需要多少个圈可以覆盖所有的*. 思 ...
- ARM 链接配置.lds文件学习<转>
本文由Jacky原创,来自http://blog.chinaunix.net/u1/58780/showart.php?id=462971 对于.lds文件,它定义了整个程序编译之后的连接过程,决定了 ...
- 设计模式二--模板方法Template method
模式分类: 书籍推荐:重构-改善既有代码的设计 重构获得模式 设计模式:现代软件设计的特征是"需求的频繁变化".设计模式的要点是 "寻找变化点,然后在变化点处应用设计模式 ...
- HVV奇兵—网页防篡改系统在网络安全实战演习中的妙用(上)
近年来,网络安全实战演习受到各大关基单位的高度关注.对于网络安全实战演习的防守方,防火墙.Web应用防火墙.态势感知.EDR.蜜罐等都是较为常见的防守工具,而网页防篡改系统则鲜有露脸的机会-- 很多人 ...
- [luogu7740]机器人游戏
考虑容斥,令$f(S)$为要求$\forall p\in S,p$可以作为起点的方案数,答案即$\sum_{S\subseteq[0,n)}(-1)^{|S|}f(S)$ 关于计算$f(S)$,对于第 ...
- Hi3516开发笔记(三):Hi3516虚拟机基础环境搭建之交叉编译环境境搭建以及开机启动脚本分析
前言 前面进行了可以传输,那么写一个简单的C程序来交叉编译并传入运行. 虚拟机 上一篇搭建的虚拟机环境,包含了sftp传递文件,网络能ping通,基于上一篇的虚拟机继续搭建. 海思交叉 ...
- pycharm的selenium设置
如果运行文件,提示 no model named selenium 那就需要添加selenium的安装地址 如上图 在python>lib>site-packages 在pycharm的f ...
- SpringBoot 动态代理实现三方接口调用
目录 一.定义注解 二.建立动态代理类 三.注入spring容器 四.编写拦截器 五.创建客户端调用类 六.main方法测试 七.启动项目 在某些业务场景中,我们只需要业务代码中定义相应的接口或者相应 ...
- Qt Creator 源码学习笔记03,大型项目如何管理工程
阅读本文大概需要 6 分钟 一个项目随着功能开发越来越多,项目必然越来越大,工程管理成本也越来越高,后期维护成本更高.如何更好的组织管理工程,是非常重要的 今天我们来学习下 Qt Creator 是如 ...