发表于: 2022-05-25 20:27:50

1 245



12、日志应该怎么打,在什么位置,需要打印出来什么样的关键参数?


什么是日志

简单的说,日志就是记录程序的运行轨迹,方便查找关键信息,也方便快速定位解决问题。

我们 Java 程序员在开发项目时都是依赖 Eclipse/ Idea 等开发工具的 Debug 调试功能来跟踪解决 Bug,

在开发环境可以这么做,但项目发布到了测试、生产环境呢?你有可能会说可以使用远程调试,但实际并不能允许让你这么做。

所以,日志的作用就是在测试、生产环境没有 Debug 调试工具时开发、测试人员定位问题的手段。

日志打得好,就能根据日志的轨迹快速定位并解决线上问题,反之,日志输出不好不能定位到问题不说反而会影响系统的性能。

优秀的项目都是能根据日志定位问题的,而不是在线调试,或者半天找不到有用的日志而抓狂…


常用日志框架

log4j、Logging、commons-logging、slf4j、logback,开发的同学对这几个日志相关的技术不陌生吧,为什么有这么多日志技术,

它们都是什么区别和联系呢?

相信大多数人搞不清楚它们的关系,下面我将一一介绍一下,以后大家再也不用傻傻分不清楚了。


什么时候需要打印日志?

简单来说,分为如下几种场景:

代码调试

问题定位

用户行为日志(记录操作日志)

根因分析


日志打印规范

1、增删改操作需要打印参数日志(以便定位一些异常业务问题);


2、条件分支需要打印日志:包括条件值以及重要参数;


3、明确日志打印级别与包含的信息

1)提供方服务,建议以 INFO 级别记录入参,出参可选

2)消费队列消息,务必打印消息内容

3)调用方服务,建议以 INFO 级别记录入参和出参

4)运行环境问题,如网络错误、建议以 WARN 级别记录错误堆栈

5)定时任务,务必打印任务开始时间、结束时间。涉及扫描数据的任务,务必打印扫描范围


4、异常信息应该包括两类信息:案发现场信息和异常堆栈信息。如果不处理,那么通过关键字throws/throw 往上抛出,由父级方法处理


5、谨慎地记录日志

1)生产环境禁止输出 debug 日志

2)有选择地输出 info 日志

3)如果使用 warn 来记录刚上线时的业务行为信息,一定要注意日志输出量的问题,避免把服务器磁盘撑爆,并记得及时删除这些观察日志


6、可以使用 warn 日志级别来记录用户输入参数错误的情况,避免用户投诉时,无所适从

注意日志输出的级别,error 级别只记录系统逻辑出错、异常等重要的错误信息。如非必要,

请不要在此场景打出 error 级别。(上述已经说明了 error 与 warn 级别日志的区别)


7、对 trace/debug/info级别的日志输出,必须使用条件输出形式或者使用占位符的方式


8、不允许记录日志后又抛出异常,因为这样会多次记录日志,只允许记录一次日志


9、不允许出现System print(包括System.out.println和System.error.println)语句作为日志的打印


10、不允许出现 e.printStackTrace


11、日志性能的考虑。如果代码为核心代码,执行频率非常高,则输出日志建议增加判断,尤其是低级别的输出


12、【强制】应用中不可直接使用日志系统(Log4j、Logback)中的 API,

而应依赖使用日志框架 SLF4J 中的 API,使用门面模式的日志框架,有利于维护和各个类的日志处理方式统一


13、【强制】日志文件推荐至少保存 15 天,因为有些异常具备以“周”为频次发生的特点


14、【强制】应用中的扩展日志(如打点、临时监控、访问日志等)命名方式: appName_logType_logName.loglogType:日志类型,

推荐分类有 stats/desc/monitor/visit 等logName:日志描述。

这种命名的好处:通过文件名就可知 道日志文件属于什么应用,什么类型,什么目的,也有利于归类查找。

  正例:mppserver 应用中单独监控时区转换异常,如: mppserver_monitor_timeZoneConvert.log


15、【强制】避免重复打印日志,浪费磁盘空间。务必在 log4j.xml 中设置 additivity=false




参数:

date表示记录访问日期;

time访问时间;

s-sitename表示你的虚拟主机的代称。

s-ip访问者IP;

cs-method表示访问方法,常见的有两种,一是GET,就是平常我们打开一个URL访问的动作,二是POST,提交表单时的动作;

cs-uri-stem就是访问哪一个文件;

cs-uri-query是指访问地址的附带参数,如asp文件?后面的字符串id=12等等,如果没有参数则用-表示;

s-port 访问的端口

cs-username 访问者名称

c-ip 来源ip

cs(User-Agent)访问来源;

sc-status状态,200表示成功,403表示没有权限,404表示打不到该页面,500表示程序有错;

sc-substatus 服务端传送到客户端的字节大小;

cs–win32-statu客户端传送到服务端的字节大小;


-------------------------------------------------------------------------------------------------------------

13、为什么需要单步调试?Debug的时候IDE(集成开发环境)是怎么找到源码的?


IDE 是 Integrated Development Environment 的缩写,中文称为集成开发环境,用来表示辅助程序员开发的应用软件,是它们的一个总称。


为什么需要单步调试?

单步调试是指程序开发中,为了找到程序的bug,通常采用的一种调试手段,一步一步跟踪程序执行的流程,根据变量的值,找到错误的原因。


基本用法&快捷键

Debug调试的功能主要对应着图一中4和5两组按钮:

  1、首先说第一组按钮,共8个按钮,从左到右依次如下:

    

    > Show Execution Point (Alt + F10):如果你的光标在其它行或其它页面,点击这个按钮可跳转到当前代码执行的行。

    > Step Over (F8):步过,一行一行地往下走,如果这一行上有方法不会进入方法。

    > Step Into (F7):步入,如果当前行有方法,可以进入方法内部,一般用于进入自定义方法内,不会进入官方类库的方法,如第25行的put方法。

    > Force Step Into (Alt + Shift + F7):强制步入,能进入任何方法,查看底层源码的时候可以用这个进入官方类库的方法。

    > Step Out (Shift + F8):步出,从步入的方法内退出到方法调用处,此时方法已执行完毕,只是还没有完成赋值。

    > Drop Frame (默认无):回退断点,后面章节详细说明。

    > Run to Cursor (Alt + F9):运行到光标处,你可以将光标定位到你需要查看的那一行,然后使用这个功能,代码会运行至光标行,而不需要打断点。

    > Evaluate Expression (Alt + F8):计算表达式,后面章节详细说明。

  2、第二组按钮,共7个按钮,从上到下依次如下:

      

    > Rerun 'xxxx':重新运行程序,会关闭服务后重新启动程序。

    > Update 'tech' application (Ctrl + F5):更新程序,一般在你的代码有改动后可执行这个功能。而这个功能对应的操作则是在服务配置里,如图2.3。

    > Resume Program (F9):恢复程序,比如,你在第20行和25行有两个断点,当前运行至第20行,按F9,则运行到下一个断点(即第25行),再按F9,则运行完整个流程,因为后面已经没有断点了。

    > Pause Program:暂停程序,启用Debug。目前没发现具体用法。

    > Stop 'xxx' (Ctrl + F2):连续按两下,关闭程序。有时候你会发现关闭服务再启动时,报端口被占用,这是因为没完全关闭服务的原因,你就需要查杀所有JVM进程了。

    > View Breakpoints (Ctrl + Shift + F8):查看所有断点,后面章节会涉及到。

    > Mute Breakpoints:哑的断点,选择这个后,所有断点变为灰色,断点失效,按F9则可以直接运行完程序。再次点击,断点变为红色,有效。如果只想使某一个断点失效,可以在断点上右键取消Enabled,如图2.4,则该行断点失效。


查找源代码:

在编码的过程中,需要查阅所引用的jar类库中的源代码,可以在Configure Build Path中的jar关联上源码。

操作如下

右键-> build path-> configure build path

如果是maven工程,可以采用如下形式:

右键-> Maven-> Download source

此时,maven会从远程仓库中查找所依赖的jar的源码,自动下载,并配置好关联关系。

-----------------------------------------------------------------------------------

14、可否远程连接到线上直接调试?真实的项目中,遇到问题的排查方案是什么?


远程调试:服务端程序运行在一台远程服务器上,我们可以在本地服务端的代码(前提是本地的代码必须和远程服务器运行的代码一致)中设置断点,

每当有请求到远程服务器时时能够在本地知道远程服务端的此时的内部状态。


最正统的方法

日志

 这是排查问题的最常用的方法,需要预估自己每日日志量和需要存储的日志时间。申请磁盘空间时一般会留35%的冗余以备突发流量。

 一般需要打日志的有:每个对外提供方法的入口和出口,调用第三方的调用前和调用后。打印内容主要包括入参和出参。

https://github.com/xiexiaojing/concise-logger 我在简明日志规范里定义:几种常用的类里用切面的形式注入日志。


监控

传统的方法如果JVM出现gc等问题需要先打开gc日志,这会牺牲一些效率。但是现在业界已经普遍将这些数据上报到系统中,

比如欧美常用的prometheus,国内用小米的falcon比较多。

对于数据库、调用量、响应时长等监控系统也都有统计。美团点评的CAT不仅集成了这些,

还集成了依赖分析、依赖分析、心跳报表、业务大盘等。挺方便的。


报警

报警不仅可以发现问题,还可以将发生问题时已经执行到的阶段作为报警信息一起发出,便于快速定位。


-----------------------------------------------------------------------------------


15、Spring中的IOC是什么意思,为什么要用IOC而不是New来创建实例?


Spring是一个开源框架,Spring是于2003 年兴起的一个轻量级的Java 开发框架,它是为了解决企业应用开发的复杂性而创建的。

框架的主要优势之一就是其分层架构,分层架构允许使用者选择使用哪一个组件,同时为 J2EE 应用程序开发提供集成的框架。

Spring的核心是控制反转(IoC)和面向切面(AOP)。


1. IoC是什么?

即“控制反转”,不是什么技术,而是一种设计思想。在Java开发中,

Ioc意味着将你设计好的对象交给容器控制,而不是传统的在你的对象内部直接控制。

 

传统Java SE程序设计,我们直接在对象内部通过new进行创建对象,是程序主动去创建依赖对象;

而IoC是有专门一个容器来创建这些对象,即由Ioc容器来控制对象的创建   


IoC容器就是具有依赖注入功能的容器 

BeanFactory提供了IoC容器最基本功能,而 ApplicationContext (应用程序上下文)则增加了更多支持企业级功能支持。

ApplicationContext(应用程序上下文)完全继承BeanFactory,因而BeanFactory所具有的语义也适用于ApplicationContext。


谁控制谁,控制什么?

传统Java SE程序设计,我们直接在对象内部通过new进行创建对象,是程序主动去创建依赖对象;而IoC是有专门一个容器来创建这些对象,

即由Ioc容器来控制对象的创建;谁控制谁?当然是IoC 容器控制了对象;控制什么?那就是主要控制了外部资源获取(不只是对象包括比如文件等)。


为何是反转,哪些方面反转了:

有反转就有正转,传统应用程序是由我们自己在对象中主动控制去直接获取依赖对象,也就是正转;而反转则是由容器来帮忙创建及注入依赖对象;

为何是反转?因为由容器帮我们查找及注入依赖对象,对象只是被动的接受依赖对象,所以是反转;哪些方面反转了?依赖对象的获取被反转了。



为什么使用IOC而不是New来创建实例:


 为什么不使用new来创建实例?
  1.增加耦合,会访问到内部属性

  2.更新的地方比较多,难于维护


 java程序中的类互相依赖,大量耦合

SpringIOC(Inversion of Control)去创建管理这些依赖,从而尽可能的降低耦合,这些被它创建管理的类被称为bean。

------------------------------------------------------------------------------------------------------------------------------------------------------

16、什么是贫血模型,什么是充血模型?为什么我们会强制要求使用贫血模型?


贫血模型

是指领域对象里只有get和set方法,或者包含少量的CRUD方法,所有的业务逻辑都不包含在内而是放在Business Logic层。


优点

系统的层次结构清楚,各层之间单向依赖,Client->(Business Facade)->Business Logic->Data Access(ADO.NET)。

当然Business Logic是依赖Domain Object的。似乎现在流行的架构就是这样,当然层次还可以细分。


缺点

是不够面向对象,领域对象只是作为保存状态或者传递状态使用,所以就说只有数据没有行为的对象不是真正的对象。

在Business Logic里面处理所有的业务逻辑,在POEAA(企业应用架构模式)一书中被称为Transaction Script模式。


充血模型

层次结构和上面的差不多,不过大多业务逻辑和持久化放在Domain Object里面,

Business Logic只是简单封装部分业务逻辑以及控制事务、权限等,

这样层次结构就变成Client->(Business Facade)->Business Logic->Domain Object->Data Access。


优点

是面向对象,Business Logic符合单一职责,不像在贫血模型里面那样包含所有的业务逻辑太过沉重。


缺点

是如何划分业务逻辑,什么样的逻辑应该放在Domain Object中,什么样的业务逻辑应该放在Business Logic中,这是很含糊的。

即使划分好了业务逻辑,由于分散在Business Logic和Domain Object层中,不能更好的分模块开发。

熟悉业务逻辑的开发人员需要渗透到Domain Logic中去,而在Domian Logic又包含了持久化,对于开发者来说这十分混乱。 

其次,因为Business Logic要控制事务并且为上层提供一个统一的服务调用入口点,

它就必须把在Domain Logic里实现的业务逻辑全部重新包装一遍,完全属于重复劳动。


17、clean,install,package,deploy分别代表什么含义?



clean:我们在使用maven的构建项目会产生一个target文件,但我们修改了代码后就需要使用clean清楚target,重新生成target。


package:打包到本项目,一般是在项目target目录下。如果a项目依赖于b项目,打包b项目时,只会打包到b项目下target下,

编译a项目时就会报错,因为找不到所依赖的b项目,说明a项目在本地仓库是没有找到它所依赖的b项目。

 

install:打包会安装到本地的maven仓库中,如果没有设置过maven本地仓库,一般在用户/.m2目录下。如果a项目依赖于b项目,

那么install b项目时,会在本地仓库同时生成pom文件和jar文件


maven deploy:项目打包上传至远程仓库,将最终版本的包拷贝到远程

--------------------------------------------------------------------

18、怎么样能让Maven跳过JUnit?


JUnit是由 Erich Gamma 和 Kent Beck 编写的一个 回归测试 框架(regression testing framework)。


JUnit的特点

JUnit是用于编写和运行测试的开源框架。

提供了注释,以确定测试方法。

提供断言测试预期结果。

提供了测试运行的运行测试。

JUnit测试让您可以更快地编写代码,提高质量

JUnit是优雅简洁。它是不那么复杂以及不需要花费太多的时间。

JUnit测试可以自动运行,检查自己的结果,并提供即时反馈。没有必要通过测试结果报告来手动梳理。

JUnit测试可以组织成测试套件包含测试案例,甚至其他测试套件。

Junit显示测试进度的,如果测试是没有问题条形是绿色的,测试失败则会变成红色。



一、命令行方式跳过测试

我们可以用两种命令来跳过测试

  • mvn clean install -DskipTests
  • mvn clean install -Dmaven.test.skip=true

- DskipTests,不执行测试用例,但编译测试用例类生成相应的class文件至target/test-classes下

- Dmaven.test.skip=true,不执行测试用例,也不编译测试用例类

使用maven.test.skip,不但跳过单元测试的运行,也跳过测试代码的编译;

使用 mvn package -DskipTests 跳过单元测试,但是会继续编译。


2.可以在pom.xml中添加如下配置来跳过测试 

示例:

<build>
<plugins>
 <!-- maven 打包时跳过测试 -->
 <plugin>
   <groupId>org.apache.maven.plugins</groupId>
   <artifactId>maven-surefire-plugin</artifactId>
   <configuration>
     <skip>true</skip>
   </configuration>
 </plugin>
</plugins>
<build>

<build>  <plugins> <!-- maven 打包时跳过测试 --> <plugin>                <groupId>org.apache.maven.plugins</groupId>                <artifactId>maven-surefire-plugin</artifactId>                <configuration>                    <skip>true</skip>                </configuration>            </plugin>       </plugins>   <build>         

--------------------------------------------------------------------------

19、为什么要用Log4j来替代System.out.println?


Log4j是什么?

Log4j 是Apache的一个开放源代码项目,通过使用Log4j,

我们可以控制日志信息输送的目的地是控制台、文件、GUI组件、甚至是套接口服务器、NT的事件记录器、UNIX Syslog守护进程等;

我们也可以控制每一条日志的输出格式;通过定义每一条日志信息的级别,我们能够更加细致地控制日志的生成过程。最令人感兴趣的就是,

这些可以通过一个配置文件来灵活地进行配置,而不需要修改应用的代码。

此外,通过Log4j其他语言接口,您可以在 C、C++、.Net、PL/SQL程序中使用Log4j,

其语法和用法与在Java程序中一样,使得多语言分布式系统得到一个统一一致的日志组件模块。而且,通过使用各种第三方扩展,

您可以很方便地将Log4j集成到J2EE、JINI甚至是SNMP应用中。


Log4j有三个主要的组件:Loggers(记录器),Appenders  (输出源)和Layouts(布局)。这里可简单理解为日志类别,

日志要输出的地方和日志以何种形式输出。综合使用这三个组件可以轻松地记录信息的类型和级别,

并可以在运行时控制日志输出的样式和位置。


Log4j的特点

可以轻松的控制log信息是否显示、log信息的输出端类型、输出方式、输出格式,

更加细致地控制日志的生成过程,而其通过配置文件可以灵活的进行配置而不需要大量的更改代码。


常使用的类如下:

org.apache.log4j.ConsoleAppender(控制台)
org.apache.log4j.FileAppender(文件)
org.apache.log4j.DailyRollingFileAppender(每天产生一个日志文件)
org.apache.log4j.RollingFileAppender(文件大小到达指定尺寸的时候产生一个新的文件)
org.apache.log4j.WriterAppender(将日志信息以流格式发送到任意指定的地方)


配置文件的具体介绍过多我就不一一复制了,链接:http://www.cnblogs.com/ITtangtang/p/3926665.html


为什么代替System.out.println?


1、可以通过配置文件可以灵活的进行配置而不需要大量的更改代码。


2、 Log4j除了可以记录程序运行日志信息外还有一重要的功能就是用来显示调试信息。


3、Log4j就是帮助开发人员进行日志输出管理的API类库。

它最重要的特点就可以配置文件灵活的设置日志信息的优先级、日志信息的输出目的地以及日志信息的输出格式。


4、程序员经常会遇到脱离java ide环境调试程序的情况,这时大多数人会选择使用System.out.println语句输出某个变量值的方法进行调试。

这样会带来一个非常麻烦的问题:一旦哪天程序员决定不要显示这些System.out.println的东西了就只能一行行的把这些垃圾语句注释掉。

若哪天又需调试变量值,则只能再一行行去掉这些注释恢复System.out.println语句。使用log4j可以很好的处理类似情况。


---------------------------------------------------------------------------------------------------------------------------------------------

20.为什么DB的设计中要使用Long来替换掉Date类型?


Database是按照数据结构来组织、存储和管理数据的仓库,随着信息技术和市场的发展,

数据管理不再仅仅是存储和管理数据,而转变成用户所需要的各种数据管理的方式。数据库有很多种类型,

从最简单的存储有各种数据的表格比如excel,到能够进行海量数据存储的大型数据库系统都在各个方面得到了广泛的应用。

目前主流数据库有:Oracle、Mysql、SQLServer等。数据库,顾名思义,是存入数据的仓库。只不过这个仓库是在计算机存储设备上的,

而且数据是按一定格式存放的。修真院基本上是使用Mysql数据库的,因此是以Mysql及其相关知识为背景的。


常见问题

1.java中对时间处理的类比较混乱,处理时间的类有:

java.util.Date,java.sql.Date ,java.sql.Time ,java.sql.Timestamp,java.util.Calendar ,java.util.TimeZone


2.时区,在获取当前时间的时候,各个地区在同一个时间点会有不同的时间表示


3.精度,将java.util.Date转为java.sql.Date时候,日期的时分秒会被去掉,数据的精度发生变化了。

而JDBC中定义接口时候,用的是java.sql.Date,而我们常常用到的Date都是java.util.Date,这往往导致一些转换过程中发生误差。

java.sql.Timestamp类,它保持了日期数据原有的精度。可以实现和java.util.Date的无损转换。

但是Timestamp这个类在一些预定义SQL中常常会出问题java.sql.Date,在JDBC接口中使用了,

如果对其进行修改,JDBC接口规范也要改,那么将引发各个数据库厂商对数据库驱动也要改,这是不可接受的。


解决方案

将date类型转换为long类型

使用整数存储时间,用相关函数来转换。

有利于计算时间差

方便java与数据库之间的传输




明天计划:完成之后的深度思考。







返回列表 返回列表
评论

    分享到