From 06f3d931100bd538ce131c54867f12da7809ca0f Mon Sep 17 00:00:00 2001
From: 01Petard <1520394133@qq.com>
Date: Fri, 10 Jan 2025 13:01:03 +0800
Subject: [PATCH] =?UTF-8?q?doc:=20=E5=A4=87=E4=BB=BD=E6=96=87=E7=AB=A0?=
=?UTF-8?q?=EF=BC=9B?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
docs/.vitepress/config.mts | 3 +-
"docs/\345\274\200\345\217\221/index.md" | 25 +-
...02\346\225\260\346\240\241\351\252\214.md" | 4 +-
...02\346\225\260\346\240\241\351\252\214.md" | 23 ++
...45\345\277\227\350\256\260\345\275\225.md" | 276 ++++++++++++++++++
5 files changed, 316 insertions(+), 15 deletions(-)
rename "docs/\345\274\200\345\217\221/\346\227\245\345\277\227\350\256\260\345\275\225\346\234\200\344\275\263\345\256\236\350\267\265.md" => "docs/\345\274\200\345\217\221/\346\234\200\344\275\263\345\256\236\350\267\265 - \345\217\202\346\225\260\346\240\241\351\252\214.md" (94%)
create mode 100644 "docs/\345\274\200\345\217\221/\346\234\200\344\275\263\345\256\236\350\267\265-\345\217\202\346\225\260\346\240\241\351\252\214.md"
create mode 100644 "docs/\345\274\200\345\217\221/\346\234\200\344\275\263\345\256\236\350\267\265-\346\227\245\345\277\227\350\256\260\345\275\225.md"
diff --git a/docs/.vitepress/config.mts b/docs/.vitepress/config.mts
index 9b492c1..fd394a7 100644
--- a/docs/.vitepress/config.mts
+++ b/docs/.vitepress/config.mts
@@ -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: '/开发/抖音评论区设计'},
diff --git "a/docs/\345\274\200\345\217\221/index.md" "b/docs/\345\274\200\345\217\221/index.md"
index e27d2c5..9d1c66c 100644
--- "a/docs/\345\274\200\345\217\221/index.md"
+++ "b/docs/\345\274\200\345\217\221/index.md"
@@ -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)
\ No newline at end of file
+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)
\ No newline at end of file
diff --git "a/docs/\345\274\200\345\217\221/\346\227\245\345\277\227\350\256\260\345\275\225\346\234\200\344\275\263\345\256\236\350\267\265.md" "b/docs/\345\274\200\345\217\221/\346\234\200\344\275\263\345\256\236\350\267\265 - \345\217\202\346\225\260\346\240\241\351\252\214.md"
similarity index 94%
rename from "docs/\345\274\200\345\217\221/\346\227\245\345\277\227\350\256\260\345\275\225\346\234\200\344\275\263\345\256\236\350\267\265.md"
rename to "docs/\345\274\200\345\217\221/\346\234\200\344\275\263\345\256\236\350\267\265 - \345\217\202\346\225\260\346\240\241\351\252\214.md"
index 256b60b..b317499 100644
--- "a/docs/\345\274\200\345\217\221/\346\227\245\345\277\227\350\256\260\345\275\225\346\234\200\344\275\263\345\256\236\350\267\265.md"
+++ "b/docs/\345\274\200\345\217\221/\346\234\200\344\275\263\345\256\236\350\267\265 - \345\217\202\346\225\260\346\240\241\351\252\214.md"
@@ -1,6 +1,6 @@
-# 日志记录最佳实践
+# 最佳实践 - 参数校验
-
+
## 基本原则
diff --git "a/docs/\345\274\200\345\217\221/\346\234\200\344\275\263\345\256\236\350\267\265-\345\217\202\346\225\260\346\240\241\351\252\214.md" "b/docs/\345\274\200\345\217\221/\346\234\200\344\275\263\345\256\236\350\267\265-\345\217\202\346\225\260\346\240\241\351\252\214.md"
new file mode 100644
index 0000000..ba1bbd6
--- /dev/null
+++ "b/docs/\345\274\200\345\217\221/\346\234\200\344\275\263\345\256\236\350\267\265-\345\217\202\346\225\260\346\240\241\351\252\214.md"
@@ -0,0 +1,23 @@
+# 最佳实践 - 参数校验
+
+
+
+## 基本参数管理与校验
+
+- 🔒 同一参数字段,在不同接口中应保持统一的最大长度限制,并且确保与数据库中的最大长度相匹配。
+- ✅ 对所有请求参数使用注解的方式进行全面校验,包括但不限于非空、长度及取值范围,。
+- 💼 若输入参数过多,则考虑将它们封装进单一对象内来简化管理。
+
+## 安全性校验
+
+- 🔍 查询接口除了基本校验外,还需验证用户状态等信息。
+
+## 测试与质量保证
+
+- 👷♂️ 编码完成后,自行测试参数校验逻辑,以发现潜在问题。
+
+## 文档与维护
+
+- 📝 为每一个参数提供注释,以便他人理解维护。
+
+- 📄 接口变更需及时更新至接口文档,保证文档的一致性和完整性。
diff --git "a/docs/\345\274\200\345\217\221/\346\234\200\344\275\263\345\256\236\350\267\265-\346\227\245\345\277\227\350\256\260\345\275\225.md" "b/docs/\345\274\200\345\217\221/\346\234\200\344\275\263\345\256\236\350\267\265-\346\227\245\345\277\227\350\256\260\345\275\225.md"
new file mode 100644
index 0000000..5b7ad93
--- /dev/null
+++ "b/docs/\345\274\200\345\217\221/\346\234\200\344\275\263\345\256\236\350\267\265-\346\227\245\345\277\227\350\256\260\345\275\225.md"
@@ -0,0 +1,276 @@
+# 最佳实践 - 日志记录
+
+
+
+## 基本原则
+
+- ❌ 避免记录重复、无意义的日志,确保每一条日志都有助于调试
+- 🔄 避免在循环中使用日志,以免产生大量冗余数据
+- 📘 只记录必要的参数,而不是完整的对象结构
+
+## 风险防范
+
+- 🌐 将英文作为日志语言,以预防编码问题
+- 🔨 不滥用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
+ ...
+ ```