Skip to content

Commit

Permalink
doc: 备份文章;
Browse files Browse the repository at this point in the history
  • Loading branch information
01Petard committed Jan 10, 2025
1 parent bbd8ea6 commit 06f3d93
Show file tree
Hide file tree
Showing 5 changed files with 316 additions and 15 deletions.
3 changes: 2 additions & 1 deletion docs/.vitepress/config.mts
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,8 @@ export default defineConfig({
{
text: '开发',
items: [
{text: '日志记录最佳实践', link: '/开发/日志记录最佳实践'},
{text: '最佳实践-参数校验', link: '/开发/最佳实践-参数校验'},
{text: '最佳实践-日志记录', link: '/开发/最佳实践-日志记录'},
{text: 'Nginx配置文件说明', link: '/开发/Nginx配置文件说明'},
{text: '搭建K8S集群环境', link: '/开发/搭建K8S集群环境'},
{text: '抖音评论区设计', link: '/开发/抖音评论区设计'},
Expand Down
25 changes: 13 additions & 12 deletions docs/开发/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,16 @@

## 目录

1. [日志记录最佳实践](./日志记录最佳实践.md)
2. [Nginx配置文件说明](./Nginx配置文件说明.md)
3. [搭建K8S集群环境](./搭建K8S集群环境.md)
4. [抖音评论区设计](./抖音评论区设计.md)
5. [MYDB操作手册](./MYDB操作手册.md)
6. [批量导出zip压缩包和Excel表格](./批量导出zip压缩包和Excel表格.md)
7. [阿里云OSS和内容安全Java实现参考代码](./阿里云OSS和内容安全Java实现参考代码.md)
8. [SSO单点登录的实现原理](./SSO单点登录的实现原理.md)
9. [ThreadLocal原理和使用](./ThreadLocal原理和使用.md)
10. [对Java中多态的理解](./对Java中多态的理解.md)
11. [JDBC和JAVA类型对比](./JDBC和JAVA类型对比.md)
12. [C++的STL库常见函数](./C++的STL库常见函数.md)
1. [最佳实践-参数校验](./最佳实践-参数校验.md)
2. [最佳实践-日志记录](./最佳实践-日志记录.md)
3. [Nginx配置文件说明](./Nginx配置文件说明.md)
4. [搭建K8S集群环境](./搭建K8S集群环境.md)
5. [抖音评论区设计](./抖音评论区设计.md)
6. [MYDB操作手册](./MYDB操作手册.md)
7. [批量导出zip压缩包和Excel表格](./批量导出zip压缩包和Excel表格.md)
8. [阿里云OSS和内容安全Java实现参考代码](./阿里云OSS和内容安全Java实现参考代码.md)
9. [SSO单点登录的实现原理](./SSO单点登录的实现原理.md)
10. [ThreadLocal原理和使用](./ThreadLocal原理和使用.md)
11. [对Java中多态的理解](./对Java中多态的理解.md)
12. [JDBC和JAVA类型对比](./JDBC和JAVA类型对比.md)
13. [C++的STL库常见函数](./C++的STL库常见函数.md)
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# 日志记录最佳实践
# 最佳实践 - 参数校验

<img src="https://cdn.jsdelivr.net/gh/01Petard/imageURL@main/img/202501091635953.png" alt="image-20250109163531835" style="zoom: 75%;" />
<img src="https://cdn.jsdelivr.net/gh/01Petard/imageURL@main/img/202501101245046.png" alt="image-20250110124552934" style="zoom:50%;" />

## 基本原则

Expand Down
23 changes: 23 additions & 0 deletions docs/开发/最佳实践-参数校验.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# 最佳实践 - 参数校验

<img src="https://cdn.jsdelivr.net/gh/01Petard/imageURL@main/img/202501101259876.png" alt="image-20250110125946771" style="zoom:60%;" />

## 基本参数管理与校验

- 🔒 同一参数字段,在不同接口中应保持统一的最大长度限制,并且确保与数据库中的最大长度相匹配。
- ✅ 对所有请求参数使用注解的方式进行全面校验,包括但不限于非空、长度及取值范围,。
- 💼 若输入参数过多,则考虑将它们封装进单一对象内来简化管理。

## 安全性校验

- 🔍 查询接口除了基本校验外,还需验证用户状态等信息。

## 测试与质量保证

- 👷‍♂️ 编码完成后,自行测试参数校验逻辑,以发现潜在问题。

## 文档与维护

- 📝 为每一个参数提供注释,以便他人理解维护。

- 📄 接口变更需及时更新至接口文档,保证文档的一致性和完整性。
276 changes: 276 additions & 0 deletions docs/开发/最佳实践-日志记录.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,276 @@
# 最佳实践 - 日志记录

<img src="https://cdn.jsdelivr.net/gh/01Petard/imageURL@main/img/202501101243595.png" alt="image-20250110124335414" style="zoom:50%;" />

## 基本原则

- ❌ 避免记录重复、无意义的日志,确保每一条日志都有助于调试
- 🔄 避免在循环中使用日志,以免产生大量冗余数据
- 📘 只记录必要的参数,而不是完整的对象结构

## 风险防范

- 🌐 将英文作为日志语言,以预防编码问题
- 🔨 不滥用Error级别的日志,减轻紧急响应负担

- 🚫 不要在日志记录过程中阻断业务流程

###### 错误方式

不要用可能会发生空指针异常的对象或方法

```java
public void createShop(Shop shop){
log.info("create and print log: {}", shop.getName().toLowerCase());
}
```

## 性能保障

- 💡 使用专用日志库,而非标准输出,来提高性能并便于管理

###### 错误方式

```java
public void love() {
System.out.println("i love java...");
// 业务逻辑
...
}
```

###### 原因:println 内部实现是带锁的

```java
public void println(boolean x) {
synchronized (this) {
print(x);
newLine();
}
}
```

##### 正确方式

```java
public void love() {
log.info("i love java...");
// 业务逻辑
...
}
```

- 🔒 统一日志框架减少依赖耦合,提升维护便利性

禁用`Log4j``LogBack`的API输出日志,而是用`Slf4j`,防止代码和日志强耦合

```java
// 直接使用
@Slf4j
public class Test {}

// 或者直接引入
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

private static final Logger log = LoggerFactory.getLogger(xxx.class);
```

- ⚙️ 根据日志级别控制输出,防止不必要的资源耗费

##### 错误方式

```java
public void hello(String name) {
log.trace("trace hello" + name);
log.debug("debug hello" + name);
log.info("info hello" + name);
// 业务逻辑
...
}
```

##### 正确方式

强烈建议使用占位符来输出内容,防止执行字符串拼接操作,浪费系统资源

```java
public void hello() {
if (log.isTraceEnabled()) {
log.trace("trace hello {}", name);
}
if (log.isDebugEnabled()) {
log.debug("debug hello {}", name);
}
if (log.isInfoEnabled()) {
log.info("info hello {}", name);
}
// 业务逻辑
...
}
```

- 👾 错误地使用`e.printStackTrace()`,可能导致严重的资源耗尽

###### 错误方式

```java
public void hello() {
try {
// 业务逻辑
...
} catch (Exception e) {
e.printStackTrace();
}
}
```

###### 原因:`e.printStackTrace()`会将产生的字符串存放到字符串常量池,降低可用内存空间

```java
public void printStackTrace() {
printStackTrace(System.err);
}
```

##### 正确方式

```java
public void hello() {
try {
// 业务逻辑
...
} catch (Exception e) {
log.error("execute failed", e);
}
}
```

- 🐛 在日志中禁用复杂对象序列化以防潜在风险

###### 错误方式

禁止使用JSON序列化工具,这些工具是通过调用对象的get方法将对象进行序列化的,如果对象的get方法被重写则可能会面临抛出异常的风险

```java
public void hello(Object data) {
log.info("print log, data={}", JSON.toJSONString(data));
// 业务逻辑
...
}
```

##### 正确方式

推荐自定义toString 方法

```java
public void hello() {
log.info("hello and print log, data={}", data);
}
```

## 开发简化

- 🕵️‍♂️ 提供详细异常信息有助于问题追踪

###### 错误方式

```java
public void hello() {
try {
// 业务逻辑
...
} catch (Exception e) {
// 没有打印异常 e,无法定位出现什么类型的异常
log.error("execute failed");
// 没有记录详细的堆栈异常信息,只记录错误基本描述信息,不利于排查问题
log.error("execute failed", e.getMessage());
}
}
```

##### 正确方式

```java
public void hello() {
try {
// 业务逻辑
...
} catch (Exception e) {
log.error("execute failed", e);
}
}
```

- 💼 记录关键函数的输入/输出,增强排错能力

```java
public String doSth(String id, String type) {
log.info("start: {}, {}", id, type); // 入口处记录初始值
String res = process(id, type);
log.info("end: {}, {}, {}", id, type, res); // 出口处记录结果
}
```

- 🤝 利用条件语句前后的日志,简化程序跟踪

```java
public void doSth() {
...
if (user.isVip()) {
log.info("vip member, Id: {}, start vip", userId());
// 会员逻辑
} else {
log.info("该用户是非会员, Id: {}, 开始处理非会员逻辑", userId());
// 非会员逻辑
}
...
}
```

- 📍 添加Trace ID用于关联相关事件流,串联一次性的日志链路

```java
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.UUID;

@Component
public class MDCCFilter implements Filter {

private static final String TRACE_ID = "traceId";

@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, javax.servlet.ServletException {
try {
// 为每个请求生成唯一的 Trace ID
String traceId = UUID.randomUUID().toString();
MDC.put(TRACE_ID, traceId);

// 如果需要,可以从请求中提取更多上下文信息,例如用户 ID
if (request instanceof HttpServletRequest) {
HttpServletRequest httpRequest = (HttpServletRequest) request;
String userId = httpRequest.getHeader("X-User-Id"); // 假设用户 ID 在请求头中

if (userId != null) {
MDC.put("userId", userId);
}
}

// 执行后续的 Filter 链
chain.doFilter(request, response);
} finally {
// 清理 MDC,避免线程数据污染
MDC.clear();
}
}
}

// 日志格式
%d{yyyy-MM-dd HH:mm:ss} [%thread] [%X{traceId}]
%logger{36} - %msg%n
...
```

0 comments on commit 06f3d93

Please sign in to comment.