diff --git a/.all-contributorsrc b/.all-contributorsrc index eb295b4a8e1..04f841d982a 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -1889,6 +1889,51 @@ "contributions": [ "doc" ] + }, + { + "login": "yykaue", + "name": "Limbo", + "avatar_url": "https://avatars.githubusercontent.com/u/22905143?v=4", + "profile": "https://github.com/yykaue", + "contributions": [ + "code" + ] + }, + { + "login": "irenhongyan", + "name": "哈哈哈哈哈哈哈哈哈", + "avatar_url": "https://avatars.githubusercontent.com/u/53438321?v=4", + "profile": "https://github.com/irenhongyan", + "contributions": [ + "code" + ] + }, + { + "login": "ileonli", + "name": "Leon Li", + "avatar_url": "https://avatars.githubusercontent.com/u/45332412?v=4", + "profile": "https://github.com/ileonli", + "contributions": [ + "code" + ] + }, + { + "login": "killme2008", + "name": "dennis zhuang", + "avatar_url": "https://avatars.githubusercontent.com/u/14142?v=4", + "profile": "http://fnil.net/", + "contributions": [ + "code" + ] + }, + { + "login": "kerwin612", + "name": "Kerwin Bryant", + "avatar_url": "https://avatars.githubusercontent.com/u/3371163?v=4", + "profile": "https://github.com/kerwin612", + "contributions": [ + "code" + ] } ], "contributorsPerLine": 7, diff --git a/.github/workflows/backend-build-test.yml b/.github/workflows/backend-build-test.yml index 88bccc66387..5e31c48b5b0 100644 --- a/.github/workflows/backend-build-test.yml +++ b/.github/workflows/backend-build-test.yml @@ -49,6 +49,10 @@ jobs: java-version: 17 - name: Build with Maven run: mvn clean -B package -Prelease --file pom.xml + - name: Upload coverage reports to Codecov + uses: codecov/codecov-action@v4.0.1 + with: + token: ${{ secrets.CODECOV_TOKEN }} - name: Build Image env: IMAGE_PUSH: false diff --git a/README.md b/README.md index 1f21c29f2c3..c4c60246262 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,8 @@ [![Reddit](https://img.shields.io/badge/Reddit-Community-7289DA?logo=reddit)](https://www.reddit.com/r/hertzbeat/) [![Twitter](https://img.shields.io/twitter/follow/hertzbeat1024?logo=twitter)](https://twitter.com/hertzbeat1024) [![OpenSSF Best Practices](https://www.bestpractices.dev/projects/8139/badge)](https://www.bestpractices.dev/projects/8139) -[![Docker Pulls](https://img.shields.io/docker/pulls/apache/hertzbeat?style=%20for-the-badge&logo=docker&label=DockerHub%20Download)](https://hub.docker.com/repository/docker/apache/hertzbeat/general) +[![codecov](https://codecov.io/gh/apache/HertzBeat/branch/master/graph/badge.svg)](https://app.codecov.io/gh/apache/hertzbeat) +[![Docker Pulls](https://img.shields.io/docker/pulls/apache/hertzbeat?style=%20for-the-badge&logo=docker&label=DockerHub%20Download)](https://hub.docker.com/r/apache/hertzbeat) [![Artifact Hub](https://img.shields.io/endpoint?url=https://artifacthub.io/badge/repository/hertzbeat)](https://artifacthub.io/packages/search?repo=hertzbeat) [![YouTube Channel Subscribers](https://img.shields.io/youtube/channel/subscribers/UCri75zfWX0GHqJFPENEbLow?logo=youtube&label=YouTube%20Channel)](https://www.youtube.com/channel/UCri75zfWX0GHqJFPENEbLow) @@ -160,13 +161,13 @@ Detailed config refer to [Install HertzBeat via Package](https://hertzbeat.apach Detailed steps refer to [CONTRIBUTING](CONTRIBUTING.md) -##### 4:Install All(hertzbeat+mysql+tsdb) via Docker-compose +##### 4:Install All(hertzbeat+postgresql+tsdb) via Docker-compose -Install the mysql/postgresql database, iotdb/tdengine/victoria-metrics database and hertzbeat at one time through [docker-compose deployment script](script/docker-compose). +Install the postgresql/mysql database, victoria-metrics/iotdb/tdengine database and hertzbeat at one time through [docker-compose deployment script](script/docker-compose). Detailed steps refer to [Install via Docker-Compose](script/docker-compose/README.md) -##### 5. Install All(hertzbeat+collector+mysql+tsdb) via kubernetes helm charts +##### 5. Install All(hertzbeat+collector+postgresql+tsdb) via kubernetes helm charts Install HertzBeat cluster in a Kubernetes cluster by Helm chart. @@ -446,6 +447,13 @@ Thanks to these wonderful people, welcome to join us:
tomsun28 💻 📖 🎨 |
- 会编程的王学长 💻 📖 🎨 |
- zcx 💻 🐛 🎨 |
- 进击的阿晨 💻 🎨 🐛 |
- 铁甲小宝 🐛 💻 📖 |
- cuipiheqiuqiu 💻 ⚠️ 🎨 |
-
javax.validation.requirements
+
+3. 如果方法中的代码行数太多,请尝试在适当的点上使用多个子方法来分段方法体。
+
+ 一般来说,需要坚持以下原则:
+ - 便于测试
+ - 有好的语义
+ - 易于阅读
+
+ 此外,还需要考虑在组件、逻辑、抽象和场景等方面的切割是否合理。
+
+ > 然而,目前还没有明确的演示定义。在演变过程中,我们将为开发者提供更多的示例,以便他们有更清晰的参考和理解。
+
+
+### 3.4 集合规则
+
+1. 对于返回的 `collection` 值,除非有特殊的 `concurrent` (如线程安全),总是返回 `interface`,例如:
+
+ - 如果使用 `ArrayList`,则返回 List
+ - 如果使用 `HashMap`,则返回 Map
+ - 如果使用 `HashSet`,则返回 Set
+
+2. 如果存在多线程,可以使用以下声明或返回类型:
+
+ ```java
+ private CurrentHashMap map;
+ public CurrentHashMap funName();
+ ```
+
+3. 使用 `isEmpty()` 而不是 `length() == 0` 或者 `size() == 0`
+
+ - 负面示例:
+
+ ```java
+ if (pathPart.length() == 0) {
+ return;
+ }
+ ```
+
+ - 正面示例:
+
+ ```java
+ if (pathPart.isEmpty()) {
+ return;
+ }
+ ```
+
+### 3.5 并发处理
+
+1. 需要管理 `线程池`,使用统一的入口点获取 `线程池`。
+
+ 注意:在演变过程中,我们将为开发者提供更多的示例,以便他们有更清晰的参考和理解。
+
+2. `线程池` 需要进行资源约束,以防止因处理不当导致的资源泄露。
+
+### 3.6 控制/条件语句
+
+1. 避免因不合理的 `条件/控制` 分支顺序导致:
+
+ - 多个代码行的 `深度` 为 `n+1`
+ - 多余的行
+
+一般来说,如果一个方法的代码行深度由于连续嵌套的 `if... else..` 超过了 `2+ Tabs`,那么应该考虑试图
+- `合并分支`,
+- `反转分支条件`
+- `提取私有方法`
+
+以减少代码行深度并提高可读性,例如:
+- 联合或将逻辑合并到下一级调用中
+ - 负面示例:
+ ```java
+ if (isInsert) {
+ save(platform);
+ } else {
+ updateById(platform);
+ }
+ ```
+ - 正面示例:
+ ```java
+ saveOrUpdate(platform);
+ ```
+- 合并条件
+ - 负面示例:
+ ```java
+ if (expression1) {
+ if(expression2) {
+ ......
+ }
+ }
+ ```
+ - 正面示例:
+ ```java
+ if (expression1 && expression2) {
+ ......
+ }
+ ```
+- 反转条件
+ - 负面示例:
+
+ ```java
+ public void doSomething() {
+ // 忽略更深的代码块行
+ // .....
+ if (condition1) {
+ ...
+ } else {
+ ...
+ }
+ }
+ ```
+
+ - 正面示例:
+
+ ```java
+ public void doSomething() {
+ // 忽略更深的代码块行
+ // .....
+ if (!condition1) {
+ ...
+ return;
+ }
+ // ...
+ }
+ ```
+- 使用单一变量或方法减少复杂的条件表达式
+ - 负面示例:
+ ```java
+ if (dbType.indexOf("sqlserver") >= 0 || dbType.indexOf("sql server") >= 0) {
+ ...
+ }
+ ```
+
+ - 正面示例:
+ ```java
+ if (containsSqlServer(dbType)) {
+ ....
+ }
+ //.....
+ // containsSqlServer的定义
+ ```
+
+> 在未来,使用 `sonarlint` 和 `better highlights` 检查代码深度看起来是个不错的选择。
+
+### 3.7 代码注释规则
+
+1. 方法缺少注释:
+
+ - `When`:该方法何时可以被调用
+ - `How`:如何使用此方法以及如何传递参数等
+ - `What`:此方法实现了哪些功能
+ - `Note`:在调用此方法时开发人员应注意什么
+
+2. 缺少必要的类头部描述注释。
+
+ 添加 `What`,`Note` 等,如上述 `1` 中提到的。
+
+3. 在接口中的方法声明必须被注释。
+
+ - 如果实现的语义和接口声明的注释内容不一致,则具体的实现方法也需要用注释重写。
+
+ - 如果方法实现的语义与接口声明的注释内容一致,则建议不写注释以避免重复的注释。
+
+4. 在注释行中的第一个词需要大写,如 `param` 行,`return` 行。
+ 如果特殊引用作为主题不需要大写,需要注意特殊符号,例如引号。
+
+### 3.8 Java Lambda 表达式
+
+1. 更倾向于使用 `non-capturing` lambda(不包含对外部范围的引用的lambda)。
+ Capturing lambda 在每次调用时都需要创建一个新的对象实例。`Non-capturing` lambda 可以为每次调用使用相同的实例。
+
+ - 负面示例:
+
+ ```java
+ map.computeIfAbsent(key, x -> key.toLowerCase())
+ ```
+
+ - 正面示例:
+
+ ```java
+ map.computeIfAbsent(key, k -> k.toLowerCase());
+ ```
+
+2. 考虑使用方法引用而不是内联lambda
+
+ - 负面示例:
+
+ ```java
+ map.computeIfAbsent(key, k-> Loader.load(k));
+ ```
+
+ - 正面示例:
+
+ ```java
+ map.computeIfAbsent(key, Loader::load);
+ ```
+
+### 3.9 Java Streams
+
+- 在任何对性能敏感的代码中避免使用 Java Streams。
+
+- 使用 Java Streams 的主要动机是为了提高代码的可读性。因此,它们可以在代码的某些部分中很好地匹配,这些部分不是数据密集型的,而是处理协调。
+
+- 即使在后一种情况下,也试图限制范围到一个方法,或者一个内部类中的几个私有方法。
+
+### 3.10 前置条件检查
+
+1. 使用统一的 `Utils.requireXXX` 来完成前提的验证,如果可能的话,用新的前置条件检查替换 `AlertXXException.throwIfXXX`。
+
+### 3.11 StringUtils
+
+1. 使用 `StringUtils.isBlank` 而不是 `StringUtils.isEmpty`
+
+ - 负面示例:
+
+ ```java
+ if (StringUtils.isEmpty(name)) {
+ return;
+ }
+ ```
+
+ - 正面示例:
+
+ ```java
+ if (StringUtils.isBlank(name)) {
+ return;
+ }
+ ```
+
+2. 使用 `StringUtils.isNotBlank` 而不是 `StringUtils.isNotEmpty`
+
+ - 负面示例:
+
+ ```java
+ if (StringUtils.isNotEmpty(name)) {
+ return;
+ }
+ ```
+
+ - 正面示例:
+
+ ```java
+ if (StringUtils.isNotBlank(name)) {
+ return;
+ }
+ ```
+
+3. 使用 `StringUtils.isAllBlank` 而不是 `StringUtils.isAllEmpty`
+
+ - 负面示例:
+
+ ```java
+ if (StringUtils.isAllEmpty(name, age)) {
+ return;
+ }
+ ```
+
+ - 正面示例:
+
+ ```java
+ if (StringUtils.isAllBlank(name, age)) {
+ return;
+ }
+ ```
+
+### 3.12 `Enum` 类
+
+1. 枚举值比较
+
+ - 负面示例:
+
+ ```java
+ if (status.equals(JobStatus.RUNNING)) {
+ return;
+ }
+ ```
+
+ - 正面示例:
+
+ ```java
+ if (status == JobStatus.RUNNING) {
+ return;
+ }
+ ```
+
+2. 枚举类不需要实现 Serializable
+
+ - 负面示例:
+
+ ```java
+ public enum JobStatus implements Serializable {
+ ...
+ }
+ ```
+
+ - 正面示例:
+
+ ```java
+ public enum JobStatus {
+ ...
+ }
+ ```
+
+3. 使用 `Enum.name()` 而不是 `Enum.toString()`
+
+ - 负面示例:
+
+ ```java
+ System.out.println(JobStatus.RUNNING.toString());
+ ```
+
+ - 正面示例:
+
+ ```java
+ System.out.println(JobStatus.RUNNING.name());
+ ```
+
+4. 枚举类名称统一使用 Enum 后缀
+
+ - 负面示例:
+
+ ```java
+ public enum JobStatus {
+ ...
+ }
+ ```
+
+ - 正面示例:
+
+ ```java
+ public enum JobStatusEnum {
+ ...
+ }
+ ```
+
+### 3.13 `Deprecated` 注解
+
+ - 负面示例:
+
+ ```java
+ @deprecated
+ public void process(String input) {
+ ...
+ }
+ ```
+
+ - 正面示例:
+
+ ```java
+ @Deprecated
+ public void process(String input) {
+ ...
+ }
+ ```
+
+## 4 日志
+
+1. 使用 `占位符` 进行日志输出:
+
+ - 负面示例
+ ```java
+ log.info("Deploy cluster request " + deployRequest);
+ ```
+ - 正面示例
+ ```java
+ log.info("load plugin:{} to {}", file.getName(), appPlugins);
+ ```
+
+2. 打印日志时,注意选择 `日志级别`
+
+ 当打印日志内容时,如果传递了日志占位符的实际参数,必须避免过早评估,以避免由日志级别导致的不必要评估。
+
+ - 负面示例:
+
+ 假设当前日志级别为 `INFO`:
+
+ ```java
+ // 忽略声明行。
+ Listtomsun28 💻 📖 🎨 |
- 会编程的王学长 💻 📖 🎨 |
- zcx 💻 🐛 🎨 |
- 进击的阿晨 💻 🎨 🐛 |
- 铁甲小宝 🐛 💻 📖 |
- cuipiheqiuqiu 💻 ⚠️ 🎨 |
-
javax.validation.requirements
+
+3. If there are too many lines of code in the method, please have a try on using multiple sub methods at appropriate points to segment the method body.
+
+ Generally speaking, it needs to adhere to the following principles:
+ - Convenient testing
+ - Good semantics
+ - Easy to read
+
+ In addition, it is also necessary to consider whether the splitting is reasonable in terms of components, logic, abstraction, and other aspects in the scenario.
+
+ > However, there is currently no clear definition of demo. During the evolution process, we will provide additional examples for developers to have a clearer reference and understanding.
+
+### 3.4 Collection Rule
+
+1. For `collection` returned values, unless there are special `concurrent` (such as thread safety), always return the `interface`, such as:
+
+ - returns List if use `ArrayList`
+ - returns Map if use `HashMap`
+ - returns Set if use `HashSet`
+
+2. If there are multiple threads, the following declaration or returned types can be used:
+
+ ```java
+ private CurrentHashMap map;
+ public CurrentHashMap funName();
+ ```
+
+3. Use `isEmpty()` instead of `length() == 0` or `size() == 0`
+
+ - Negative demo:
+
+ ```java
+ if (pathPart.length() == 0) {
+ return;
+ }
+ ```
+
+ - Positive demo:
+
+ ```java
+ if (pathPart.isEmpty()) {
+ return;
+ }
+ ```
+
+### 3.5 Concurrent Processing
+
+1. The `thread pool` needs to be managed, using a unified entry point to obtain the `thread pool`.
+
+ Note: During the evolution process, we will provide additional examples for developers to have a clearer reference and understanding.
+
+2. `Thread pool` needs to be resource constrained to prevent resource leakage caused by improper handling
+
+### 3.6 Control/Condition Statements
+
+1. Avoid unreasonable `condition/control` branches order leads to:
+
+ - Multiple code line `depths` of `n+1`
+ - Redundant lines
+
+Generally speaking, if a method's code line depth exceeds `2+ Tabs` due to continuous nested `if... else..`, it should be considered to try
+- `merging branches`,
+- `inverting branch conditions`
+- `extracting private methods`
+
+to reduce code line depth and improve readability like follows:
+- Union or merge the logic into the next level calling
+ - Negative demo:
+ ```java
+ if (isInsert) {
+ save(platform);
+ } else {
+ updateById(platform);
+ }
+ ```
+ - Positive demo:
+ ```java
+ saveOrUpdate(platform);
+ ```
+- Merge the conditions
+ - Negative demo:
+ ```java
+ if (expression1) {
+ if(expression2) {
+ ......
+ }
+ }
+ ```
+ - Positive demo:
+ ```java
+ if (expression1 && expression2) {
+ ......
+ }
+ ```
+- Reverse the condition
+ - Negative demo:
+
+ ```java
+ public void doSomething() {
+ // Ignored more deeper block lines
+ // .....
+ if (condition1) {
+ ...
+ } else {
+ ...
+ }
+ }
+ ```
+
+ - Positive demo:
+
+ ```java
+ public void doSomething() {
+ // Ignored more deeper block lines
+ // .....
+ if (!condition1) {
+ ...
+ return;
+ }
+ // ...
+ }
+ ```
+- Using a single variable or method to reduce the complex conditional expression
+ - Negative demo:
+ ```java
+ if (dbType.indexOf("sqlserver") >= 0 || dbType.indexOf("sql server") >= 0) {
+ ...
+ }
+ ```
+
+ - Positive demo:
+ ```java
+ if (containsSqlServer(dbType)) {
+ ....
+ }
+ //.....
+ // definition of the containsSqlServer
+ ```
+
+> Using `sonarlint` and `better highlights` to check code depth looks like good in the future.
+
+### 3.7 Code Comments Rule
+
+1. Method lacks comments:
+
+ - `When`: When can the method be called
+ - `How`: How to use this method and how to pass parameters, etc.
+ - `What`: What functions does this method achieve
+ - `Note`: What should developers pay attention to when calling this method
+
+2. Missing necessary class header description comments.
+
+ Add `What`, `Note`, etc. like mentioned in the `1`.
+
+3. The method declaration in the interface must be annotated.
+
+ - If the semantics of the implementation and the annotation content at the interface declaration are inconsistent, the specific implementation method also needs to be rewritten with annotations.
+
+ - If the semantics of the method implementation are consistent with the annotation content at the interface declaration, it is not recommended to write annotations to avoid duplicate annotations.
+
+4. The first word in the comment lines need to be capitalized, like `param` lines, `return` lines.
+ If a special reference as a subject does not need to be capitalized, special symbols such as quotation marks need to be noted.
+
+### 3.8 Java Lambdas
+
+1. Prefer `non-capturing` lambdas (lambdas that do not contain references to the outer scope).
+ Capturing lambdas need to create a new object instance for every call. `Non-capturing` lambdas can use the same instance for each invocation.
+
+ - Negative demo:
+
+ ```java
+ map.computeIfAbsent(key, x -> key.toLowerCase())
+ ```
+
+ - Positive demo:
+
+ ```java
+ map.computeIfAbsent(key, k -> k.toLowerCase());
+ ```
+
+2. Consider method references instead of inline lambdas
+
+ - Negative demo:
+
+ ```java
+ map.computeIfAbsent(key, k-> Loader.load(k));
+ ```
+
+ - Positive demo:
+
+ ```java
+ map.computeIfAbsent(key, Loader::load);
+ ```
+
+### 3.9 Java Streams
+
+- Avoid Java Streams in any performance critical code.
+
+- The main motivation to use Java Streams would be to improve code readability. As such, they can be a good match in parts of the code that are not data-intensive, but deal with coordination.
+
+- Even in the latter case, try to limit the scope to a method, or a few private methods within an internal class.
+
+### 3.10 Pre-Conditions Checking
+
+1. Use a unified `Utils.requireXXX` to complete the validation of the prerequisite, and if possible, replace the `AlertXXException.throwIfXXX` by new pre-conditions checking.
+
+### 3.11 StringUtils
+
+1. Use `StringUtils.isBlank` instead of `StringUtils.isEmpty`
+
+ - Negative demo:
+
+ ```java
+ if (StringUtils.isEmpty(name)) {
+ return;
+ }
+ ```
+
+ - Positive demo:
+
+ ```java
+ if (StringUtils.isBlank(name)) {
+ return;
+ }
+ ```
+
+2. Use `StringUtils.isNotBlank` instead of `StringUtils.isNotEmpty`
+
+ - Negative demo:
+
+ ```java
+ if (StringUtils.isNotEmpty(name)) {
+ return;
+ }
+ ```
+
+ - Positive demo:
+
+ ```java
+ if (StringUtils.isNotBlank(name)) {
+ return;
+ }
+ ```
+
+3. Use `StringUtils.isAllBlank` instead of `StringUtils.isAllEmpty`
+
+ - Negative demo:
+
+ ```java
+ if (StringUtils.isAllEmpty(name, age)) {
+ return;
+ }
+ ```
+
+ - Positive demo:
+
+ ```java
+ if (StringUtils.isAllBlank(name, age)) {
+ return;
+ }
+ ```
+
+### 3.12 `Enum` Class
+
+1. Enumeration value comparison
+
+ - Negative demo:
+
+ ```java
+ if (status.equals(JobStatus.RUNNING)) {
+ return;
+ }
+ ```
+
+ - Positive demo:
+
+ ```java
+ if (status == JobStatus.RUNNING) {
+ return;
+ }
+ ```
+
+2. Enumeration classes do not need to implement Serializable
+
+ - Negative demo:
+
+ ```java
+ public enum JobStatus implements Serializable {
+ ...
+ }
+ ```
+
+ - Positive demo:
+
+ ```java
+ public enum JobStatus {
+ ...
+ }
+ ```
+
+3. Use `Enum.name()` instead of `Enum.toString()`
+
+ - Negative demo:
+
+ ```java
+ System.out.println(JobStatus.RUNNING.toString());
+ ```
+
+ - Positive demo:
+
+ ```java
+ System.out.println(JobStatus.RUNNING.name());
+ ```
+
+4. Enumeration class names uniformly use the Enum suffix
+
+ - Negative demo:
+
+ ```java
+ public enum JobStatus {
+ ...
+ }
+ ```
+
+ - Positive demo:
+
+ ```java
+ public enum JobStatusEnum {
+ ...
+ }
+ ```
+
+### 3.13 `Deprecated` Annotation
+
+ - Negative demo:
+
+ ```java
+ @deprecated
+ public void process(String input) {
+ ...
+ }
+ ```
+
+ - Positive demo:
+
+ ```java
+ @Deprecated
+ public void process(String input) {
+ ...
+ }
+ ```
+
+## 4 Log
+
+1. Use `placeholders` for log output:
+
+ - Negative demo
+ ```java
+ log.info("Deploy cluster request " + deployRequest);
+ ```
+ - Positive demo
+ ```java
+ log.info("load plugin:{} to {}", file.getName(), appPlugins);
+ ```
+
+2. Pay attention to the selection of `log level` when printing logs
+
+ When printing the log content, if the actual parameters of the log placeholder are passed, it is necessary to avoid premature evaluation to avoid unnecessary evaluation caused by the log level.
+
+ - Negative demo:
+
+ Assuming the current log level is `INFO`:
+
+ ```java
+ // ignored declaration lines.
+ List