背景:监控线上客户端传参日志,详见 https://blog.csdn.net/weixin_42498050/article/details/94219831
计划:提供接口,报警信息入库
报警信息入库-未完待续
odps数据迁移至idb
odps的DDL语句:
CREATE TABLE `table_A` ( `content` STRING, `rowkey` STRING ) COMMENT 'TT source table' PARTITIONED BY ( ds STRING COMMENT 'day', hh STRING COMMENT 'hour', mm STRING COMMENT 'minutes' ) LIFECYCLE 600;
idb的DDL语句:
CREATE TABLE `table_B` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '主键',
`gmt_create` datetime NOT NULL COMMENT '创建时间',
`gmt_modified` datetime NOT NULL COMMENT '修改时间',
`content` text NOT NULL COMMENT '客户端传参原始日志',
`rowkey` varchar(100) NOT NULL COMMENT '标识',
`ds` varchar(50) NOT NULL COMMENT 'day',
`hh` varchar(50) NOT NULL COMMENT 'hour',
`mm` varchar(50) NOT NULL DEFAULT '' COMMENT 'minutes',
PRIMARY KEY (`id`)
) DEFAULT CHARACTER SET=utf8mb4 COMMENT='客户端传参监控';
因为我自己的项目使用的日志配置是log4j 而迁移之后的项目是slf4j。发现代码里log是飘红的
首先解释一下为什么要安装Lombok插件–为什么呢?
因为在idea导入项目的时候,你会看见,卧槽,都是错误,打开一个一个错误,就没有不报错误的,log飘红,提示Cannot resolve symbol‘log’,这就是因为Lombok了原因了!
eclipse和idea开发环境下都有自动生成的bean,entity等类快捷方式,绝大部分数据类类中都需要get、set、toString、equals和hashCode方法,如果bean中的属性一旦有修改、删除或增加时,需要重新生成或删除get/set等方法,给代码维护增加负担。
而使用了lombok则不一样,使用了lombok的注解(@Setter, @Getter, @ToString, @RequiredArgsConstructor, @EqualsAndHashCode 或 @Data)之后,就不需要编写或生成get/set等方法。
在线安装bomlok插件
在intellig idea中选择preferences -- plugs -- Search in repositories搜索lombok
install后Restart IntelliJ IDEA 重启idea后,代码正常
关于mybatis-generator-maven-plugin:2.0.0报错解决:
百度了半天,找3个同事对比本地个人目录下/Users/lishan/.m2/repository/com/taobao/tddl
最终发现同事的/Users/lishan/.m2/repository/com/taobao/tddl/tddl-common 下的版本为 5.2.6-1 3.3.2.4 5.2.6
而我自己的都是5以上的版本,缺少3.3.2.4版本
经排查为/Users/lishan/.m2/settings.xml 与其他同事的配置不太一样,导致每次运行mybatis-generator:generate时报错如下。同事把tddl整个文化夹以及settings.xml发给我后,替换完成,重新加载maven配置,再运行generate就ok了
Downloading: http://jcenter.bintray.com/com/alibaba/configserver/google/code/gson/ali-gson/maven-metadata.xml
Downloading: https://jitpack.io/com/alibaba/configserver/google/code/gson/ali-gson/maven-metadata.xml
[INFO]
[INFO] --- mybatis-generator-maven-plugin:2.0.0:generate (default-cli) @ algo-testing-service ---
[INFO] ------------------------------------------------------------------------
[INFO] BUILD FAILURE
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 9.886 s
[INFO] Finished at: 2019-11-05T19:49:31+08:00
[INFO] Final Memory: 28M/323M
[INFO] ------------------------------------------------------------------------
[ERROR] Failed to execute goal com.tmall.mybatis.generator:mybatis-generator-maven-plugin:2.0.0:generate (default-cli) on project algo-testing-service: Execution default-cli of goal com.tmall.mybatis.generator:mybatis-generator-maven-plugin:2.0.0:generate failed: A required class was missing while executing com.tmall.mybatis.generator:mybatis-generator-maven-plugin:2.0.0:generate: com/taobao/tddl/common/util/DataSourceFetcher
[ERROR] -----------------------------------------------------
[ERROR] realm = plugin>com.tmall.mybatis.generator:mybatis-generator-maven-plugin:2.0.0
[ERROR] strategy = org.codehaus.plexus.classworlds.strategy.SelfFirstStrategy
[ERROR] urls[0] = file:/Users/lishan/.m2/repository/com/tmall/mybatis/generator/mybatis-generator-maven-plugin/2.0.0/mybatis-generator-maven-plugin-2.0.0.jar
[ERROR] urls[1] = file:/Users/lishan/.m2/repository/com/taobao/tddl/tddl-group-datasource/3.3.2.4/tddl-group-datasource-3.3.2.4.jar
[ERROR] urls[2] = file:/Users/lishan/.m2/repository/org/mybatis/generator/mybatis-generator-core/1.3.2/mybatis-generator-core-1.3.2.jar
[ERROR] urls[3] = file:/Users/lishan/.m2/repository/org/mybatis/generator/mybatis-generator-maven-plugin/1.3.0/mybatis-generator-maven-plugin-1.3.0.jar
[ERROR] urls[4] = file:/Users/lishan/.m2/repository/com/google/code/javaparser/javaparser/1.0.11/javaparser-1.0.11.jar
[ERROR] urls[5] = file:/Users/lishan/.m2/repository/javax/enterprise/cdi-api/1.0/cdi-api-1.0.jar
[ERROR] urls[6] = file:/Users/lishan/.m2/repository/javax/annotation/jsr250-api/1.0/jsr250-api-1.0.jar
[ERROR] urls[7] = file:/Users/lishan/.m2/repository/org/eclipse/sisu/org.eclipse.sisu.inject/0.3.0/org.eclipse.sisu.inject-0.3.0.jar
[ERROR] urls[8] = file:/Users/lishan/.m2/repository/org/codehaus/plexus/plexus-component-annotations/1.5.5/plexus-component-annotations-1.5.5.jar
[ERROR] urls[9] = file:/Users/lishan/.m2/repository/backport-util-concurrent/backport-util-concurrent/3.1/backport-util-concurrent-3.1.jar
[ERROR] urls[10] = file:/Users/lishan/.m2/repository/org/codehaus/plexus/plexus-interpolation/1.11/plexus-interpolation-1.11.jar
[ERROR] urls[11] = file:/Users/lishan/.m2/repository/org/codehaus/plexus/plexus-utils/1.5.15/plexus-utils-1.5.15.jar
[ERROR] urls[12] = file:/Users/lishan/.m2/repository/junit/junit/3.8.1/junit-3.8.1.jar
[ERROR] urls[13] = file:/Users/lishan/.m2/repository/org/apache/maven/reporting/maven-reporting-api/2.0.9/maven-reporting-api-2.0.9.jar
[ERROR] urls[14] = file:/Users/lishan/.m2/repository/org/apache/maven/doxia/doxia-sink-api/1.0-alpha-10/doxia-sink-api-1.0-alpha-10.jar
[ERROR] urls[15] = file:/Users/lishan/.m2/repository/commons-cli/commons-cli/1.0/commons-cli-1.0.jar
[ERROR] urls[16] = file:/Users/lishan/.m2/repository/org/codehaus/plexus/plexus-interactivity-api/1.0-alpha-4/plexus-interactivity-api-1.0-alpha-4.jar
[ERROR] urls[17] = file:/Users/lishan/.m2/repository/commons-io/commons-io/2.1/commons-io-2.1.jar
[ERROR] Number of foreign imports: 1
[ERROR] import: Entry[import from realm ClassRealm[maven.api, parent: null]]
[ERROR]
[ERROR] -----------------------------------------------------: com.taobao.tddl.common.util.DataSourceFetcher
[ERROR] -> [Help 1]
解决后运行生成
删除之前的dependency
mvn clean -U test-compile
刷新就ok了,效果如下
关于Error creating bean with name 'cpwLogInfoController'
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'cpwLogInfoController': Unsatisfied dependency expressed through field 'cpwLogInfoService'; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'cpwLogInfoService': Unsatisfied dependency expressed through field 'cpwLogInfoDao'; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.alibaba.algo.dao.CpwLogInfoDao' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}
原因:可能的原因1
#import com.alibaba.dubbo.config.annotation.Service;
import org.springframework.stereotype.Service;
导包错误导致,应为 import org.springframework.stereotype.Service;
实际导包为 #import com.alibaba.dubbo.config.annotation.Service;
是否确实与注入相关便签的依赖。比如dubbo服务下,添加了spring的相关依赖但是服务端并不需要Spring的@Service标签,而是dubbo的@Service标签。检查maven依赖是否正确,修改完毕记得Install,report下
参考博客 https://blog.csdn.net/butterfly_resting/article/details/80044863
但是并未解决:真正的原因如下 是dao下的CpwLogInfoDao需要添加@Mapper @Repository
import org.apache.ibatis.annotations.Mapper;
import org.springframework.stereotype.Repository;
@Mapper
@Repository
修改之后运行,ok
基于SpringBoot springframework框架的项目设计
=================================================================================
=================================================================================
1> 设计好自己的数据表。我这里是3张
从odps同步到idb的数据
CREATE TABLE `cpw_log_info` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键',
`gmt_create` datetime NOT NULL COMMENT '创建时间',
`gmt_modified` datetime NOT NULL COMMENT '修改时间',
`content` text NOT NULL COMMENT '客户端传参原始日志',
`rowkey` varchar(100) NOT NULL COMMENT '标识',
`ds` varchar(50) NOT NULL COMMENT 'day',
`hh` varchar(50) NOT NULL COMMENT 'hour',
`mm` varchar(50) NOT NULL DEFAULT '' COMMENT 'minutes',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8mb4 COMMENT='客户端传参监控'
;
存储传参日志的原始日志,odps读取的前N条数据
CREATE TABLE `cpw_log_save` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键',
`gmt_create` datetime NOT NULL COMMENT '创建时间',
`gmt_modified` datetime NOT NULL COMMENT '修改时间',
`log_id` int(11) NOT NULL COMMENT 'content读取odps的第N条数据',
`origial_param` text COMMENT 'odps的第N条日志原始参数',
`now_time` varchar(50) DEFAULT NULL COMMENT '读取opdps时间,当前时间,年月日',
`log_time` varchar(50) DEFAULT NULL COMMENT '日志的时间戳',
`utdid` varchar(50) DEFAULT NULL COMMENT '用户utdid信息',
`app_scene` varchar(50) DEFAULT NULL COMMENT '搜索场景',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=512 DEFAULT CHARSET=utf8mb4 COMMENT='客户端传参日志实时监控'
;
存储newcpw解析断言后的错误日志
CREATE TABLE `cpw_log_error` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键',
`gmt_create` datetime NOT NULL COMMENT '创建时间',
`gmt_modified` datetime NOT NULL COMMENT '修改时间',
`now_time` varchar(50) NOT NULL COMMENT 'log运行时间',
`log_id` int(11) NOT NULL COMMENT '读取odps的日志id',
`error_log` text COMMENT '错误参数信息',
`error_level` int(11) DEFAULT NULL COMMENT '错误级别',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='客户端传参错误日志入库'
;
2> 分为testing-service和testing-start,在service.resources下面的Generator.xml添加配置文件
<table tableName="cpw_log_info">
<generatedKey column="id" sqlStatement="SELECT LAST_INSERT_ID() as id" type="post"
identity="true"></generatedKey>
</table>
<table tableName="cpw_log_save">
<generatedKey column="id" sqlStatement="SELECT LAST_INSERT_ID() as id" type="post"
identity="true"></generatedKey>
</table>
<table tableName="cpw_log_error">
<generatedKey column="id" sqlStatement="SELECT LAST_INSERT_ID() as id" type="post"
identity="true"></generatedKey>
</table>
3> 生成beans文件
3.1> 点击idea右侧Maven-testing-serivice-Plugins-mybatis-generator
3.2> 在service- dao包下自动生成Dao接口和Query类===对数据库函数的封装,接口
注意:需要在所有Dao接口文件下添加如下注解(方法的标签,注解)
@Mapper
@Repository
3.3> 在service- mode包自动下生成Do文件===对数据库字段属性的封装,get set方法。即JavaBeans
3.4> 在service- resources- mapper自动生成xml文件
mapper是对数据库增删改查方法的封装
3.5> /XX-testing/algo-testing-service/src/main/java/com/xx/newcpw/CpwLogErrorServer.java
是执行对数据库操作的入口,Service层
3.6> /XX-testing/algo-testing-start/src/main/java/com/xx/algo/controller/CpwLogErrorController.java
是业务数据对数据库的处理(增删改查)Dao Do ,controller层
3.7> 启动Spring Boot:修改testing-start/Application.java运行程序入口,修改完成以debug模式启运行
package com.alibaba.algo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import com.taobao.pandora.boot.PandoraBootstrap; /** * Pandora Boot应用的入口类 * <p>LogUtil * 详情见http://xx/middleware-container/pandora-boot/wikis/spring-boot-diamond * * @author */ @SpringBootApplication(scanBasePackages = {"com.alibaba.algo","com.xx.xx","com.xx.xx","com.xx.newcpw"}) public class Application { public static void main(String[] args) { PandoraBootstrap.run(args); SpringApplication.run(Application.class, args); PandoraBootstrap.markStartupAndWait(); } }
3.8> 请求写好的接口,http://localhost:port/logsave 把原始日志写入数据表cpw_log_save
http://localhost:port/logerror 把错误日志写入数据表cpw_log_error
关于SpringBoot遇到的问题
如遇Failure to find com.aliyun:aliyun-java-sdk-core:pom:3.4.0 in http://mvnrepo.alibaba-inc.com/mvn/repository was cached in the local repository, resolution will not be reattempted until the update interval of tbmirror-all has elapsed or updates are forced
解决如下两个问题
解决settings.xml修改镜像地址不生效的问题,
解决maven打包构建时出现如下问题
Failure to find com.ibatis:xxx-xxx-plugin:jar:1.0.7 in http://maven.aliyun.com/nexus/content/repositories/central/ was cached in the local repository,
resolution will not be reattempted until the update interval of alimaven has elapsed or updates are forced ->
问题的意思是,阿里云maven仓库中,没有你需要的依赖包。此时需要修改一下你的maven settings.xml镜像地址。
还有一种可能是因为,多模块化工程。某一个模块依赖另一个模块,那个模块没有install。解决方式是,依赖的模块进行 mvn clean install 就行了
先执行mvn clean,然后再执行一下mvn package -U ,第二个命令能治百病
mvn clean install -Dmaven.test.skip=true -U试一下
不行就手动删除 .m2下被缓存的包
keyword断言增加version条件判断
// keyword判断,0923版本需要改为urlencode传参,((%[0-9A-Fa-f]{2}){2})+
// %E5%A4%A9%E5%9D%91%E9%B9%B0%E7%8C%8E
// 将需要转码的字符转为16进制,然后从右到左,取4位(不足4位直接处理),每2位做一位,前面加上%,编码成%XY格式,但考虑到用户输入的query可能为abc 123 《我爱我家》 亲爱的,热爱的特殊字符
String logVersion = json.getString("version");
String minVersion = "8.1.9";
int s = 0;
try {
s = compareVersion(logVersion, minVersion);
} catch (Exception e) {
e.printStackTrace();
}
if (json.containsKey("keyword")) {
String keyword = json.getString("keyword");
if (TextUtils.isEmpty(keyword)) {
System.err.println("第" + count + "条日志,keyword客户端传参value为空,结果为" + keyword + ",BUG BUG BUG!!!");
// 记录error级别的信息
logger.error("error.log======第" + count + "条日志,keyword客户端传参value为空,结果为" + keyword + ",BUG BUG BUG!!!");
} else if (s >= 0 && !(keyword.matches("((%[0-9A-Fa-f]{2}){2})+") || keyword.matches("[0-9a-zA-Z]+") || !keyword.matches("[^\\x00-\\xff]*[a-zA-Z0-9]*"))) {
System.err.println("第" + count + "条日志,keyword客户端传参不符合正则规则,没有urlencode,结果为" + keyword + ",BUG BUG BUG!!!");
// 记录error级别的信息
logger.error("error.log======第" + count + "条日志,keyword客户端传参不符合正则规则,没有urlencode,结果为" + keyword + ",BUG BUG BUG!!!");
// logger.error("第" + count + "条日志,keyword客户端传参不符合正则规则,没有urlencode,结果为" + keyword + ",BUG BUG BUG!!!");
/**
* 2019-10-28新增钉钉机器人接入报警,打印出问题的参数以及在数据库查询出的原始日志,便于排查
*/
sb.append("第" + count + "条日志,keyword客户端传参不符合正则规则,没有urlencode,结果为" + keyword + ",BUG BUG BUG!!!" +
currentTime + "日" + clientTimeStamp + ",第【" + count + "】条数据," +
"ytsoku.content.origialParam的json传参日志为=======" + json);
if (!sb.toString().isEmpty()) {
try {
// 机器人报警,此处填写自己创建机器人时的token
DingTalkUtil.alert("750e544acab37373edf5d872d7607738c8b29c6a133428ab599c51ea120aeccb", sb.toString(), null, false);
} catch (IOException e) {
e.printStackTrace();
}
}
} else if (s < 0 && !keyword.matches("\\S")) {
System.err.println("第" + count + "条日志,keyword客户端传参不符合正则规则,没有urlencode,结果为" + keyword + ",BUG BUG BUG!!!");
} else {
System.out.println("第" + count + "条日志,keyword客户端传参正确,校验通过~~~");
// 记录info级别的信息
logger.info("log======第" + count + "条日志,keyword客户端传参正确,校验通过~~~");
}
} else {
System.err.println("第" + count + "条日志,客户端传参缺少【keyword】数据节点,BUG BUG BUG!!!");
// 记录error级别的信息
logger.error("error.log======第" + count + "条日志,客户端传参缺少【keyword】数据节点,BUG BUG BUG!!!");
//2019-10-28新增钉钉机器人接入报警,打印出问题的参数以及在数据库查询出的原始日志,便于排查
sb.append("第" + count + "条日志,客户端传参缺少【keyword】数据节点,BUG BUG BUG!!!" +
currentTime + "日" + clientTimeStamp + ",第【" + count + "】条数据," +
"ytsoku.content.origialParam的json传参日志为=======" + json);
if (!sb.toString().isEmpty()) {
try {
// 机器人报警,此处填写自己创建机器人时的token
DingTalkUtil.alert("750e544acab37373edf5d872d7607738c8b29c6a133428ab599c51ea120aeccb", sb.toString(), null, false);
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 比较版本号的大小,前者大则返回一个正数,后者大返回一个负数,相等则返回0
*
* @param logVersion
* @param minVersion
* @return
*/
public static int compareVersion(String logVersion, String minVersion) throws Exception {
if (logVersion == null || minVersion == null) {
throw new Exception("compareVersion error:illegal params.");
}
String[] versionArray1 = logVersion.split("\\.");//注意此处为正则匹配,不能用".";
String[] versionArray2 = minVersion.split("\\.");
int idx = 0;
int minLength = Math.min(versionArray1.length, versionArray2.length);//取最小长度值,如8.5和8.5.1版本比较,取前者长度
int diff = 0;
// 按小数点拆分的长度比较,如8.1.0和8.11.0
while (idx < minLength
&& (diff = versionArray1[idx].length() - versionArray2[idx].length()) == 0//先比较长度
&& (diff = versionArray1[idx].compareTo(versionArray2[idx])) == 0) {//再比较字符
// while里先加再用
++idx;
}
//如果已经分出大小,则直接返回,如果未分出大小,则再比较位数,有子版本的为大;如果logVersion<minVersion 则返回<0(三目运算),否则logVersion>minVersion则为正数
diff = (diff != 0) ? diff : versionArray1.length - versionArray2.length;
if (diff == 0) {
}
return diff;
}
public static void main(String args[]) {
try {
int in = compareVersion("8.8.5", "8.33.6");
System.out.println("前者比后者大:" + in + " ==");
} catch (Exception e) {
e.printStackTrace();
}
try {
int in = compareVersion("8.2.1", "8.1.11");
System.out.println("后者比前者大:" + in + " ==");
} catch (Exception e) {
e.printStackTrace();
}
}
如遇
Exception in thread "main" java.lang.StackOverflowError
at com.alibaba.newcpw.sdksearch.logDeal.sbCommonPrint.sbCommonParam(sbCommonPrint.java:37)
at com.alibaba.newcpw.sdksearch.logDeal.sbCommonPrint.sbCommonParam(sbCommonPrint.java:37)
at com.alibaba.newcpw.sdksearch.logDeal.sbCommonPrint.sbCommonParam(sbCommonPrint.java:37)
StackOverflowError
原因 : 函数调用栈太深了,注意代码中是否有了循环调用方法而无法退出的情况
原理
StackOverflowError 是一个java中常出现的错误:在jvm运行时的数据区域中有一个java虚拟机栈,当执行java方法时会进行压栈弹栈的操作。在栈中会保存局部变量,操作数栈,方法出口等等。jvm规定了栈的最大深度,当执行时栈的深度大于了规定的深度,就会抛出StackOverflowError错误
如何解决 StackOverFlowError?
引发 StackOverFlowError 的常见原因有以下几种:
-
无限递归循环调用(最常见)。
-
执行了大量方法,导致线程栈空间耗尽。
-
方法内声明了海量的局部变量。
-
native 代码有栈上分配的逻辑,并且要求的内存还不小,比如 java.net.SocketInputStream.read0 会在栈上要求分配一个 64KB 的缓存(64位 Linux)。
除了程序抛出 StackOverflowError 错误以外,还有两种定位栈溢出的方法:
-
进程突然消失,但是留下了 crash 日志,可以检查 crash 日志里当前线程的 stack 范围,以及 RSP 寄存器的值。如果 RSP 寄存器的值超出这个 stack 范围,那就说明是栈溢出了。
-
如果没有 crash 日志,那只能通过 coredump 进行分析。在进程运行前,先执行 ulimit -c unlimited,当进程挂掉之后,会产生一个 core.[pid] 的文件,然后再通过 jstack $JAVA_HOME/bin/java core.[pid] 来看输出的栈。如果正常输出了,那就可以看是否存在很长的调用栈的线程,当然还有可能没有正常输出的,因为 jstack 的这条从 core 文件抓栈的命令其实是基于 Serviceability Agent 实现的,而 SA 在某些版本里有 Bug。
常见的解决方法包括以下几种:
-
修复引发无限递归调用的异常代码, 通过程序抛出的异常堆栈,找出不断重复的代码行,按图索骥,修复无限递归 Bug。
-
排查是否存在类之间的循环依赖。
-
排查是否存在在一个类中对当前类进行实例化,并作为该类的实例变量。
-
通过 JVM 启动参数 -Xss 增加线程栈内存空间, 某些正常使用场景需要执行大量方法或包含大量局部变量,这时可以适当地提高线程栈空间限制,例如通过配置 -Xss2m 将线程栈空间调整为 2 mb。
待续