1. 背景
随着项目数量的增加,项目的长期维护和演进,代码库中的代码数量会不断增长,特别是在大型项目或团队合作项目中。这意味着会有越来越多的代码需要管理和维护,同时也会带来更多潜在的代码质量问题。
2. Sonar相关介绍
Sonar是一个用于代码质量管理的开放平台。通过插件机制,Sonar可以集成不同的测试工具、代码分析工具,以及持续集成工具。
与持续集成工具(例如 Hudson/Jenkins 等)不同,Sonar并不是简单地把不同的代码检查工具结果(例如 FindBugs,PMD 等)直接显示在Web页面上,而是通过不同的插件对这些结果进行再加工处理,通过量化的方式度量代码质量的变化,从而可以方便地对不同规模和种类的工程进行代码质量管理。
2.1 Sonarlint
SonarLint 是一个免费的开源IDE扩展,可识别并帮助您在编写代码时解决质量和安全问题。像拼写检查器一样,SonarLint 会显示缺陷并提供实时反馈和清晰的修复指导,以便从一开始就提供干净的代码。
可以直接通过插件库里搜索安装
2.2 SonarScanner
SonarScanner 是一种代码扫描工具,专门用来扫描和分析项目代码,与SonarQube服务器连接将运行项目分析。支持20+语言。代码扫描和分析完成之后,会将扫描结果存储到数据库当中,在SonarQube平台可以看到扫描数据。
Scanner是在本地执行扫描分析的工具,也就是说SonarScanner是在本地执行扫描分析之后将结果上传到服务端进行分析。
SonarScanner的源码主要有三部分:
-
SonarScannerCli: 用来执行命令和接收参数
-
SonarScannerApi: 主要用来下载插件包和加载类
-
SonarScannerEngine: 用来执行扫描
2.3 SonarQube
SonarQube 是一种自动代码审查工具,用于检测代码中的错误,漏洞和代码格式上的问题。它可以与用户现有的工作流程集成,以实现跨项目分支和提取请求的连续代码检查,同时也提供了可视化的管理页面,用于查看检测出的结果。
作为Sonar解决方案的核心元素,SonarQube 集成到用户现有的工作流程中并检测代码中的问题,以帮助用户对项目执行持续的代码检查。该工具可分析支持Java、Python、PHP、JavaScript、CSS等30多种不同的编程语言,并集成到IDEA、Jenkins、GIt等服务中,方便随时查看代码质量分析报告,以确保其代码符合高质量标准。
SonarQube 通过配置的代码分析规则,从可靠性、安全性、可维护性、覆盖率、重复率等方面分析项目,风险等级从A~E划分为5个等级,同时SonarQube可以集成 PMD、Findbugs、CheckStyle 等插件来扩展使用其他规则来检验代码质量;SonarQube 还设置了质量阈,通过设置的质量阈评定此次提交分析的项目代码是否达到了规定要求。
SonarQube提供SonarLint绑定,为SonarLint提供在线的代码规范管理服务,使得全公司软件从开发到集成构建采用统⼀的代码规范。
2.3.1 SonarQube结构组成
Sonarqube的架构可以分为以下几个部分
-
数据库层:Sonarqube使用一个数据库来存储所有的代码质量数据。
-
应用程序层:Sonarqube的应用程序层包括一系列基于Java的Web应用程序,这些应用程序负责收集数据、分析代码和生成报告等任务。
-
插件层:Sonarqube的插件层是一个可扩展的架构,它允许用户安装和使用各种不同的插件来增强Sonarqube的功能和灵活性。
-
数据采集层:Sonarqube支持多种不同的代码仓库和版本控制系统,包括SVN、Git、Mercurial和ClearCase等。使用这些数据采集插件,Sonarqube可以轻松地从不同的代码库中收集数据。
2.3.2 SonarQube工作原理
-
代码收集:首先,Sonarqube向代码仓库请求代码,并将代码下载到本地。
-
代码分析:然后,Sonarqube使用其内置的代码分析器分析代码并生成有关代码质量的重要信息,例如代码复杂性、代码重复性、代码测试覆盖率等。
-
数据存储:Sonarqube将收集的数据存储在其数据库中,以供后续使用。
-
报告生成 :Sonarqube使用其内置的报告生成器生成各种数据可视化图表、报告和警告,并将其呈现给用户。
2.4 Sonar和SonarQube的关系
Sonar是开源的代码质量管理平台,而SonarQube是Sonar的商业版本(之前叫做Sonar Enterprise Edition)。SonarQube有许多增强功能,如更强大的规则引擎、更好的报告和更高级的集成等。
SonarQube是开源的,但它还包括了许多收费的插件和额外的支持服务,这些只能在商业许可下使用。Sonar和SonarQube之间的区别在于SonarQube提供了一些高级功能,特别是在企业环境中需要更多的规则和细粒度的安全,并且需要承担更多的管理和支持责任。
2.5 使用Sonar的目的
对于团队而言,编写干净的代码对于维护健康的代码至关重要。 代码是否健康主要由以下四个指标来判断:
-
可读性:代码是否可读易读,对于一个团队来说,编码标准是否一致,编码风格是否一致;
-
功能性:代码正确得实现了业务逻辑;
-
可维护性:代码逻辑是有层次的,是容易修改的;
-
高效性:代码实现在时间和空间的使用上是高效的; 团队成员可能每个人对于代码的标准及风格不同,即使通过eslint等约束也不一定能从多个维度保证代码质量,只能检测基础语法等代码质量问题。 所以引入一个可以保证团队成员代码标准一致,质量稳定,风格稳定的工具也许是有必要的。
3. Sonar集成
3.1 集成Jenkins
SonarQube可以与持续集成工具(如Jenkins)集成,实现在开发过程中的自动化代码质量检查。每次代码提交或构建时都会自动运行SonarQube分析,使得代码质量成为开发过程中的一部分,更早地发现和解决问题。
关于如何集成可以查看官方文档:Setting up Jenkins for SonarQube integration
3.2 项目流程集成Sonar
4. Sonar代码检测纬度
Sonar可以从Architecture Design(架构设计), Coding Rule(编码规则), Potential Bugs(潜在错误), Duplications(重复代码), Comments(注释), Unit Tests(单元测试), Complexity(复杂度) 这7个维度检查代码质量的。 相比lint工具检测维度比较全面, 有可视化的友好展示代码缺陷的界面,结合CI/CD工具,可以不依赖手工检查,定时清查代码。
-
糟糕的设计: 通过sonar可以找出循环,展示包与包、类与类之间的相互依赖关系,可以展示自定义的架构规则。通过Sonar可以管理第三方jar包,可以利用LCOM4检测单个任务规则的应用秦高,检测耦合。
-
不遵循代码标准: sonar可以通过PMD,CheckStyle,Findbugs等代码规则检测工具规范代码的编写。
-
潜在的缺陷: sonar可以通过PMD,CheckStyle,Findbugs等代码规则检测工具检测出潜在的缺陷。
-
重复: 显然程序中包含大量复制粘贴的代码质量低下的,sonar可以展示源码中重复严重的地方。
-
注释不足或者过多: 没有注释将使代码可读性变差,特别是当不可避免地出现人员变动时,程序的可读性将大幅度下降。
-
缺乏单元测试: sonar可以很方便地统计并展示单元测试覆盖率。
-
糟糕的代码复杂度分布: 文件、类、方法等,如果复杂度过高将难以改变,这使得开发热女难以理解它们,且如果没有自动化的单元测试,对于程序中的任何组件的改变都将可能导致需要全面的回归测试。
5. Sonar指标定义
5.1 复杂度
复杂度 ( complexity
):是指圈复杂度,是一种用于计算代码路径数量的定量指标。
每当函数的控制流分裂时,复杂性计数器就会加一。每个函数的最小复杂度为1。此计算因语言而略有不同,因为关键字和功能会有所不同。
5.2 重复
重复块 ( duplicated_blocks
):重复行块的数量。
重复文件 ( duplicated_files
):涉及重复的文件数量。
重复行 ( duplicated_lines
):涉及重复的行数。
重复行(%) ( duplicated_lines_density
):duplicated_lines / (代码行数) * 100
5.3 问题
新问题 ( new_violations
):关于新代码首次提出的问题数量。
新 xxx 问题 ( new_xxx_violations
):新代码中首次提出的指定严重性问题的数量,其中 xxx 是以下之一: blocker, critical, major, minor, info。
问题 ( violations
):所有州的问题总数。
xxx 问题 ( xxx_violations
):指定严重性的问题总数,其中 xxx 是以下之一:blocker, critical, major, minor, info。
误报问题 ( false_positive_issues
):标记为误报的问题总数。
未解决问题 ( open_issues
):处于未解决状态的问题总数。
已确认问题 ( confirmed_issues
):处于已确认状态的问题总数。
重新打开的问题 ( reopened_issues
):处于重新打开状态的问题总数。
5.4可维护性
代码异味 ( code_smells
):代码异味问题的总数。
新代码异味 ( new_code_smells
):新代码中首次提出的代码异味问题总数。
可维护性评级( ) :以前称为SQALE评级,对项目的评级与技术负债率sqale_rating的值相关。默认的可维护性评级网格为:A=0-0.05,B=0.06-0.1,C=0.11-0.20,D=0.21-0.5,E=0.51-1。也可以这样表述
- 如果未完成的修复成本是:
- <=5% 已进入申请的时间,评级为 A
- 6% 到 10% 之间,评级为 B
- 11% 到 20% 之间,评级为 C
- 21% 到 50% 之间,评级为 D
- 任何超过 50% 的都是 E
技术债务 ( sqale_index
):修复所有代码异味的努力程度。该测量值以分钟为单位存储在数据库中。当值以天为单位显示时,假定每天有 8 小时。
新代码的技术债务 ( new_technical_debt
):修复新代码上首次出现的所有代码异味所需的工作量。
技术负债率 ( sqale_debt_ratio
):开发软件的成本与修复软件的成本之间的比率。
技术负债率公式为:Remediation cost / Development cost 可以重述为:Remediation cost / (Cost to develop 1 line of code * Number of lines of code),开发一行代码的成本值为 0.06 天。
新代码的技术负债率 ( new_sqale_debt_ratio
):开发新代码更改代码的成本与与其相关的问题的成本之间的比率。
5.5 可靠性
Bugs ( bugs
): Bug 问题的总数。
新错误 ( new_bugs
):新错误问题的数量。
可靠性评级 ( reliability_rating
)
- A = 0 个错误
- B = 至少 1 个小错误
- C = 至少 1 个主要错误
- D = 至少 1 个严重错误
- E = 至少 1 个阻止错误
安全漏洞 ( vulnerabilities
):漏洞问题的数量。
新代码上的漏洞 ( new_vulnerabilities
):新漏洞问题的数量。
安全评级 ( security_rating
)
- A = 0 个漏洞
- B = 至少 1 个次要漏洞
- C = 至少 1 个主要漏洞
- D = 至少 1 个严重漏洞
- E = 至少 1 个阻止程序漏洞
5.6 尺寸
类 ( classes
):类的数量(包括嵌套类、接口、枚举和注释)。
注释行 ( comment_lines
):包含注释或注释掉的代码的行数。
非重要注释行(空注释行、仅包含特殊字符的注释行等)不会增加注释行数。
注释数(%) ( comment_lines_density
):注释行密度=注释行数/(代码行数+注释行数)*100
- 50%表示代码行数等于注释行数
- 100% 表示该文件仅包含注释行
目录 ( directories
):目录的数量。
文件 ( files
):文件数量。
行数 ( lines
):物理行数(回车数)。
代码行数 ( ncloc
):包含至少一个字符的物理行数,该字符既不是空格,也不是表格,也不是注释的一部分。
每种语言的代码行数 ( ncloc_language_distribution
):按语言分布的非注释代码行。
函数 ( functions
):函数的数量。根据语言的不同,函数可以定义为函数、方法或段落。
项目 ( projects
):投资组合中的项目数量。
语句 ( statements
):语句数。
5.7 测试
条件覆盖率 ( branch_coverage
):在包含一些布尔表达式的每一行代码上,条件覆盖率回答以下问题:"每个布尔表达式是否都被评估为true和false?"。这是单元测试执行期间遵循的流量控制结构中可能条件的密度。
条件覆盖率 = (CT + CF) / (2*B),其中:
- CT = 至少一次被评估为"真"的条件
- CF = 至少一次被评估为"假"的条件
- B = 条件总数
新代码的条件覆盖 ( ):此定义与条件覆盖new_branch_coverage相同,但仅限于新的/更新的源代码。
条件承保范围命中 ( branch_coverage_hits_data
):承保条件列表。
按行的条件 ( conditions_by_line
):按行的条件数。
按行覆盖的条件 ( covered_conditions_by_line
):按行覆盖的条件数。
覆盖范围 ():线路覆盖范围和条件覆盖范围的混合。其目标是提供更准确的答案"单元测试覆盖了多少源代码?"。
- 覆盖范围 = (CT + CF + LC) / (2 * B + EL)
- CT = 至少一次被评估为"真"的条件
- CF = 至少一次被评估为"假"的条件
- LC = 覆盖线 = 覆盖线-未覆盖线 (
lines_to_cover
-uncovered_lines
) - B = 条件总数
- EL = 可执行行总数 (
lines_to_cover
)
新代码的覆盖率 ( ):此定义与覆盖率new_coverage相同,但仅限于新的/更新的源代码。
行覆盖率 ( line_coverage
):在给定的代码行上,行覆盖率只是回答"在执行单元测试期间是否执行了这行代码?"的问题。它是单元测试覆盖的线的密度:
- 行覆盖率 = LC / EL
新代码的行覆盖率 ( ):此定义与行覆盖率new_line_coverage相同,但仅限于新的/更新的源代码。
线路覆盖命中 ( coverage_line_hits_data
):覆盖线路的列表。
要覆盖的行数 ( lines_to_cover
):单元测试可以覆盖的代码行数(例如,空白行或完整注释行不被视为要覆盖的行)。
新代码上要覆盖的行 ( new_lines_to_cover
):此定义与要覆盖的行相同,但仅限于新的/更新的源代码。
未覆盖的条件 ( uncovered_conditions
):单元测试未覆盖的条件数。
新代码上的未覆盖条件 ( ):此定义与未覆盖条件new_uncovered_conditions相同,但仅限于新的/更新的源代码。
未覆盖行数 ( uncovered_lines
):单元测试未覆盖的代码行数。
新代码上未覆盖的行 ( new_uncovered_lines
):此定义与未覆盖的行相同,但仅限于新的/更新的源代码。
单元测试 ( tests
):单元测试的数量。
单元测试持续时间 ( test_execution_time
):执行所有单元测试所需的时间。
单元测试错误 ( test_errors
):失败的单元测试数。
单元测试失败 ( test_failures
):因意外异常而失败的单元测试数量。
跳过的单元测试 ( skipped_tests
):跳过的单元测试的数量。
单元测试成功密度(%) ( test_success_density
)
- 测试成功密度 = (单元测试 - (单元测试错误 + 单元测试失败)) / (单元测试) * 100