理财网站建设阿里云手机版网站建设

张小明 2026/3/13 5:57:48
理财网站建设,阿里云手机版网站建设,百度搜索关键词统计,网站开发处理大量用户请求一、操作日志的核心价值与挑战1.1 操作日志与系统日志的本质区别维度系统日志操作日志目标用户开发人员、运维人员最终用户、客服、运营人员可读性要求低#xff08;包含代码信息#xff09;高#xff08;自然语言描述#xff09;记录目的问题排查、系统监控业务追踪、审计…一、操作日志的核心价值与挑战1.1 操作日志与系统日志的本质区别维度系统日志操作日志目标用户开发人员、运维人员最终用户、客服、运营人员可读性要求低包含代码信息高自然语言描述记录目的问题排查、系统监控业务追踪、审计记录格式要求机器可解析人类可理解存储方式日志文件、集中式日志数据库、专用存储1.2 操作日志的典型场景java// 操作日志的四种典型格式 // 1. 纯文字记录 2021-09-16 10:00 订单创建 // 2. 带变量的文本记录 2021-09-16 10:00 订单创建订单号NO.11089999 // 3. 修改前后的对比 2021-09-16 10:00 用户小明修改了订单的配送地址从金灿灿小区修改到银盏盏小区 // 4. 批量字段修改 2021-09-16 10:00 用户修改了订单信息{ 配送地址: [金灿灿小区, 银盏盏小区], 联系电话: [13800138000, 13900139000] }二、传统实现方案的局限性分析2.1 基于Canal的方案java// Canal监听数据库变化的局限性 public class CanalBasedLogRecorder { /** * 优点 * 1. 完全解耦不影响业务代码 * 2. 自动捕获所有数据变更 * * 缺点 * 1. 只能监听数据库变更无法记录业务操作 * 2. 无法获取操作人信息 * 3. 无法理解业务语义 * 4. 无法处理复杂业务逻辑如RPC调用 */ }2.2 基于Log文件的方案java// 使用SLF4J MDC记录操作人 Component Slf4j public class LogFileBasedRecorder { // 问题1操作人记录 - 通过拦截器设置 Component public class UserInterceptor extends HandlerInterceptorAdapter { Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { String userNo getUserNo(request); MDC.put(userId, userNo); // 放入线程上下文 return super.preHandle(request, response, handler); } } // 问题2操作日志分离 - 独立的日志配置 // logback-spring.xml /* appender namebusinessLogAppender class...RollingFileAppender Filelogs/business.log/File filter classch.qos.logback.classic.filter.LevelFilter levelINFO/level onMatchACCEPT/onMatch onMismatchDENY/onMismatch /filter encoder pattern%d{yyyy-MM-dd HH:mm:ss.SSS} %t %-5level %X{userId} - %msg%n/pattern /encoder /appender */ // 问题3生成可读文案 - 通过工具类拼接 public void updateAddress(UpdateDeliveryRequest request) { DeliveryOrder oldOrder queryOldAddress(request.getDeliveryOrderNo()); doUpdate(request); // 业务逻辑中嵌入日志记录代码 String logContent String.format( 用户%s修改了订单的配送地址从\%s\修改到\%s\, request.getUserName(), oldOrder.getAddress(), request.getAddress() ); businessLog.info(logContent); } }传统方案的痛点业务代码污染日志记录代码与业务逻辑混杂重复劳动每个业务方法都需要手动拼接日志难以维护日志格式变更需要修改大量代码可读性差业务逻辑被日志代码淹没三、基于注解AOP的优雅解决方案3.1 核心设计思路text┌─────────────────────────────────────────┐ │ 业务方法纯净无染 │ │ ┌───────────────────────────────────┐ │ │ │ LogRecord │ │ │ │ public void updateOrder() { ... } │ │ │ └───────────────────────────────────┘ │ └─────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────┐ │ AOP切面统一处理 │ │ ┌───────────────────────────────────┐ │ │ │ 1. 解析注解 │ │ │ │ 2. 提取参数 │ │ │ │ 3. 渲染模板 │ │ │ │ 4. 记录日志 │ │ │ └───────────────────────────────────┘ │ └─────────────────────────────────────────┘3.2 核心注解设计javaTarget({ElementType.METHOD}) Retention(RetentionPolicy.RUNTIME) Inherited Documented public interface LogRecord { /** * 成功时的操作日志模板 * 支持SpEL表达式#{#order.orderNo} * 支持自定义函数{parseUser{#userId}} */ String success(); /** * 失败时的操作日志模板可选 */ String fail() default ; /** * 操作人SpEL表达式 * 默认从线程上下文获取 */ String operator() default ; /** * 业务编号SpEL表达式 * 用于关联业务对象 */ String bizNo(); /** * 操作类别用于分类查询 */ String category() default ; /** * 详情信息JSON格式记录修改详情 */ String detail() default ; /** * 记录条件SpEL表达式 * 满足条件才记录日志 */ String condition() default ; }3.3 AOP切面实现javaAspect Component Slf4j public class LogRecordAspect { Autowired private LogRecordService logRecordService; Autowired private SpelExpressionParser spelParser; Around(annotation(logRecord)) public Object around(ProceedingJoinPoint joinPoint, LogRecord logRecord) throws Throwable { MethodSignature signature (MethodSignature) joinPoint.getSignature(); Method method signature.getMethod(); Object[] args joinPoint.getArgs(); // 1. 构建评估上下文 EvaluationContext context createEvaluationContext(method, args); // 2. 检查记录条件 if (!shouldRecord(logRecord.condition(), context)) { return joinPoint.proceed(); } // 3. 执行前准备如查询旧值 prepareBeforeExecution(logRecord, context, args); // 4. 执行业务方法 Object result; boolean success true; String errorMsg null; try { result joinPoint.proceed(); context.setVariable(_result, result); success true; } catch (Exception e) { success false; errorMsg e.getMessage(); context.setVariable(_error, e); throw e; } // 5. 记录操作日志 try { recordLog(logRecord, context, success, errorMsg, args); } catch (Exception e) { // 记录日志失败不影响主业务 log.error(记录操作日志失败, e); } // 6. 清理上下文 LogRecordContext.clear(); return result; } private void recordLog(LogRecord annotation, EvaluationContext context, boolean success, String errorMsg, Object[] args) { // 选择模板 String template success ? annotation.success() : annotation.fail(); if (StringUtils.isEmpty(template)) { return; } // 渲染模板 String content renderTemplate(template, context); // 解析操作人 String operator parseOperator(annotation.operator(), context); // 解析业务编号 String bizNo parseBizNo(annotation.bizNo(), context); // 构建操作日志 OperationLog log OperationLog.builder() .content(content) .operator(operator) .bizNo(bizNo) .category(annotation.category()) .detail(parseDetail(annotation.detail(), context)) .success(success) .errorMsg(errorMsg) .build(); // 保存日志 logRecordService.save(log); } private String renderTemplate(String template, EvaluationContext context) { // 处理自定义函数{function{#param}} template processCustomFunctions(template, context); // 处理SpEL表达式#param template processSpELExpressions(template, context); return template; } }3.4 模板渲染引擎javaComponent public class LogTemplateParser { // 自定义函数模式{function{#param}} private static final Pattern CUSTOM_FUNCTION_PATTERN Pattern.compile(\\{([^}])\\{(#[^}])\\}\\}); // SpEL表达式模式#param private static final Pattern SPEL_PATTERN Pattern.compile(#([a-zA-Z0-9_.])); /** * 解析模板中的自定义函数 */ public String parseCustomFunctions(String template, EvaluationContext context) { Matcher matcher CUSTOM_FUNCTION_PATTERN.matcher(template); StringBuffer result new StringBuffer(); while (matcher.find()) { String functionName matcher.group(1); String paramExpression matcher.group(2); // 解析参数 String param parseSpEL(paramExpression, context); // 执行自定义函数 String functionResult executeCustomFunction(functionName, param); matcher.appendReplacement(result, functionResult); } matcher.appendTail(result); return result.toString(); } /** * 解析SpEL表达式 */ public String parseSpEL(String expression, EvaluationContext context) { try { Expression exp spelParser.parseExpression(expression); Object value exp.getValue(context); return value ! null ? value.toString() : ; } catch (Exception e) { log.warn(SpEL解析失败: {}, expression, e); return ; } } }3.5 日志上下文管理java/** * 操作日志上下文用于在方法执行前后传递数据 */ public class LogRecordContext { private static final ThreadLocalMapString, Object VARIABLE_MAP ThreadLocal.withInitial(HashMap::new); /** * 设置变量用于模板中引用 */ public static void putVariable(String key, Object value) { VARIABLE_MAP.get().put(key, value); } /** * 获取变量 */ public static Object getVariable(String key) { return VARIABLE_MAP.get().get(key); } /** * 清除上下文 */ public static void clear() { VARIABLE_MAP.remove(); } /** * 获取所有变量 */ public static MapString, Object getVariables() { return new HashMap(VARIABLE_MAP.get()); } }四、完整使用示例4.1 基础使用javaService Slf4j public class OrderService { /** * 简单场景创建订单 */ LogRecord( success 创建订单订单号#{#order.orderNo}金额#{#order.amount}元, operator #{#currentUser.name}, bizNo #{#order.orderNo}, category ORDER_CREATE ) public Order createOrder(OrderCreateRequest request) { // 纯净的业务逻辑 Order order Order.builder() .orderNo(generateOrderNo()) .amount(request.getAmount()) .status(OrderStatus.CREATED) .build(); return orderRepository.save(order); } /** * 修改场景更新配送地址 */ LogRecord( success 修改订单配送地址从{getOldAddress{#orderId}}修改到#{#request.newAddress}, operator #{#currentUser.name}, bizNo #{#orderId}, category ORDER_UPDATE ) public void updateDeliveryAddress(String orderId, UpdateAddressRequest request) { // 查询旧地址通过自定义函数在切面中完成 // 业务代码专注于更新逻辑 orderRepository.updateAddress(orderId, request.getNewAddress()); } }4.2 复杂场景批量修改javaService public class UserService { /** * 批量修改用户信息 */ LogRecord( success 批量修改用户信息#{#changes.size()}项变更, detail { changes: #{#changes}, operator: #{#currentUser.name}, timestamp: #{T(java.time.LocalDateTime).now()} } , operator #{#currentUser.name}, bizNo BATCH_UPDATE_ T(java.time.LocalDate).now(), category USER_BATCH_UPDATE ) public void batchUpdateUsers(ListUserUpdateDTO changes) { // 批量更新逻辑 changes.forEach(change - { userRepository.update(change.getUserId(), change.getUpdates()); }); } }4.3 自定义函数的使用java/** * 自定义函数注册中心 */ Component public class LogFunctionRegistry { private final MapString, FunctionString, String functionMap new HashMap(); public LogFunctionRegistry() { // 注册内置函数 registerFunction(parseUser, this::parseUser); registerFunction(getOldAddress, this::getOldAddress); registerFunction(formatDate, this::formatDate); registerFunction(maskPhone, this::maskPhone); } /** * 用户ID转用户姓名 */ private String parseUser(String userId) { User user userRepository.findById(userId) .orElse(new User(userId, 未知用户)); return String.format(%s(%s), user.getName(), user.getPhone()); } /** * 获取旧地址 */ private String getOldAddress(String orderId) { Order order orderRepository.findById(orderId) .orElseThrow(() - new OrderNotFoundException(orderId)); return order.getDeliveryAddress(); } /** * 日期格式化 */ private String formatDate(String timestamp) { LocalDateTime dateTime LocalDateTime.parse(timestamp); return dateTime.format(DateTimeFormatter.ofPattern(yyyy-MM-dd HH:mm:ss)); } /** * 手机号脱敏 */ private String maskPhone(String phone) { if (phone null || phone.length() ! 11) { return phone; } return phone.substring(0, 3) **** phone.substring(7); } public void registerFunction(String name, FunctionString, String function) { functionMap.put(name, function); } public FunctionString, String getFunction(String name) { return functionMap.get(name); } }五、高级特性实现5.1 条件记录javaService public class PaymentService { /** * 只有支付成功时才记录日志 */ LogRecord( success 用户支付成功订单号#{#orderNo}支付金额#{#amount}元, condition #{#result.success true}, operator #{#currentUser.name}, bizNo #{#orderNo}, category PAYMENT_SUCCESS ) public PaymentResult payOrder(String orderNo, BigDecimal amount) { // 支付逻辑 boolean success paymentGateway.pay(orderNo, amount); return PaymentResult.builder() .success(success) .orderNo(orderNo) .amount(amount) .build(); } }5.2 修改详情记录javaService public class ProductService { /** * 记录商品修改详情 */ LogRecord( success 修改商品信息#{#product.name}, detail { productId: #{#product.id}, changes: { price: [#{#oldProduct.price}, #{#product.price}], stock: [#{#oldProduct.stock}, #{#product.stock}], status: [#{#oldProduct.status}, #{#product.status}] }, operator: #{#currentUser.name} } , operator #{#currentUser.name}, bizNo #{#product.id}, category PRODUCT_UPDATE ) public void updateProduct(Product product) { // 查询旧数据可通过切面自动完成 Product oldProduct productRepository.findById(product.getId()); // 更新逻辑 productRepository.save(product); } }5.3 异步记录与性能优化javaComponent Slf4j public class AsyncLogRecordService { Autowired private ThreadPoolTaskExecutor logExecutor; Autowired private LogStorageService logStorageService; /** * 异步记录日志 */ Async(logExecutor) public void saveAsync(OperationLog log) { try { logStorageService.save(log); } catch (Exception e) { // 异步记录失败降级到同步记录或本地文件 log.error(异步记录操作日志失败降级处理, e); saveToLocalFile(log); } } /** * 批量记录优化 */ public void batchSave(ListOperationLog logs) { if (logs.size() BATCH_THRESHOLD) { // 分批处理 Lists.partition(logs, BATCH_SIZE) .forEach(batch - logExecutor.execute(() - logStorageService.batchSave(batch) )); } else { logs.forEach(this::saveAsync); } } }六、存储与查询设计6.1 数据模型设计javaEntity Table(name operation_log) Data Builder NoArgsConstructor AllArgsConstructor public class OperationLog { Id GeneratedValue(strategy GenerationType.IDENTITY) private Long id; /** * 操作内容渲染后的文本 */ Column(length 2000) private String content; /** * 操作人 */ private String operator; /** * 业务编号 */ private String bizNo; /** * 操作类别 */ private String category; /** * 详情JSON格式 */ Column(columnDefinition json) private String detail; /** * 是否成功 */ private Boolean success; /** * 错误信息 */ private String errorMsg; /** * 操作时间 */ CreationTimestamp private LocalDateTime operateTime; /** * 操作IP */ private String operateIp; /** * 用户代理 */ private String userAgent; /** * 请求路径 */ private String requestPath; /** * 方法名 */ private String methodName; /** * 参数JSON格式 */ Column(columnDefinition json) private String params; }6.2 查询接口设计javapublic interface OperationLogRepository extends JpaRepositoryOperationLog, Long { /** * 按业务编号查询 */ ListOperationLog findByBizNoOrderByOperateTimeDesc(String bizNo); /** * 按操作人查询 */ PageOperationLog findByOperator(String operator, Pageable pageable); /** * 按类别和时间范围查询 */ ListOperationLog findByCategoryAndOperateTimeBetween( String category, LocalDateTime startTime, LocalDateTime endTime ); /** * 复杂条件查询 */ Query(SELECT ol FROM OperationLog ol WHERE (:bizNo IS NULL OR ol.bizNo :bizNo) AND (:operator IS NULL OR ol.operator LIKE %:operator%) AND (:category IS NULL OR ol.category :category) AND (:success IS NULL OR ol.success :success) AND ol.operateTime BETWEEN :startTime AND :endTime) PageOperationLog search( Param(bizNo) String bizNo, Param(operator) String operator, Param(category) String category, Param(success) Boolean success, Param(startTime) LocalDateTime startTime, Param(endTime) LocalDateTime endTime, Pageable pageable ); }6.3 扩展存储方案java/** * 存储策略接口 */ public interface LogStorageStrategy { /** * 保存日志 */ void save(OperationLog log); /** * 批量保存 */ void batchSave(ListOperationLog logs); /** * 查询日志 */ ListOperationLog query(LogQueryCondition condition); } /** * 数据库存储策略 */ Component Primary public class DatabaseStorageStrategy implements LogStorageStrategy { Autowired private OperationLogRepository repository; Override public void save(OperationLog log) { repository.save(log); } Override public void batchSave(ListOperationLog logs) { repository.saveAll(logs); } } /** * Elasticsearch存储策略用于海量日志 */ Component ConditionalOnProperty(name log.storage.strategy, havingValue elasticsearch) public class ElasticsearchStorageStrategy implements LogStorageStrategy { Autowired private ElasticsearchRestTemplate elasticsearchTemplate; Override public void save(OperationLog log) { IndexQuery query new IndexQueryBuilder() .withObject(log) .withId(log.getId().toString()) .build(); elasticsearchTemplate.index(query, IndexCoordinates.of(operation_log)); } Override public ListOperationLog query(LogQueryCondition condition) { // 构建ES查询 NativeSearchQuery searchQuery new NativeSearchQueryBuilder() .withQuery(buildQuery(condition)) .withPageable(condition.getPageable()) .build(); return elasticsearchTemplate.queryForList(searchQuery, OperationLog.class); } }七、最佳实践与部署方案7.1 Spring Boot Starter封装javaConfiguration EnableAspectJAutoProxy AutoConfigureAfter({WebMvcAutoConfiguration.class}) ConditionalOnProperty(prefix operation.log, name enabled, havingValue true, matchIfMissing true) public class OperationLogAutoConfiguration { Bean ConditionalOnMissingBean public LogRecordAspect logRecordAspect() { return new LogRecordAspect(); } Bean ConditionalOnMissingBean public LogTemplateParser logTemplateParser() { return new LogTemplateParser(); } Bean ConditionalOnMissingBean public LogFunctionRegistry logFunctionRegistry() { return new LogFunctionRegistry(); } Bean ConditionalOnMissingBean public LogStorageStrategy logStorageStrategy(OperationLogRepository repository) { return new DatabaseStorageStrategy(repository); } }7.2 配置文件示例yaml# application-operation-log.yml operation: log: enabled: true storage: strategy: database # database, elasticsearch, mixed async: true batch-size: 100 template: default-operator: #{T(com.xxx.UserContext).getCurrentUser()} date-format: yyyy-MM-dd HH:mm:ss function: enabled: true packages: com.xxx.functions7.3 监控与告警javaComponent Slf4j public class LogMonitor { Autowired private MeterRegistry meterRegistry; private final Counter logRecordCounter; private final Timer logRecordTimer; private final Counter logErrorCounter; public LogMonitor(MeterRegistry meterRegistry) { this.meterRegistry meterRegistry; // 记录操作日志数量 this.logRecordCounter Counter.builder(operation.log.count) .description(操作日志记录数量) .register(meterRegistry); // 记录操作日志耗时 this.logRecordTimer Timer.builder(operation.log.time) .description(操作日志记录耗时) .register(meterRegistry); // 记录操作日志错误 this.logErrorCounter Counter.builder(operation.log.error) .description(操作日志记录错误) .register(meterRegistry); } public void recordLog(OperationLog log, long duration) { logRecordCounter.increment(); logRecordTimer.record(duration, TimeUnit.MILLISECONDS); // 按类别统计 Tags tags Tags.of(category, log.getCategory(), success, String.valueOf(log.getSuccess())); meterRegistry.counter(operation.log.category, tags).increment(); } public void recordError(String category, Exception e) { logErrorCounter.increment(); log.error(操作日志记录失败: {}, category, e); } }7.4 测试策略javaSpringBootTest AutoConfigureMockMvc class LogRecordAspectTest { Autowired private MockMvc mockMvc; Autowired private OperationLogRepository logRepository; Test void testLogRecordAnnotation() throws Exception { // 准备测试数据 OrderCreateRequest request new OrderCreateRequest(); request.setAmount(new BigDecimal(100.00)); // 执行测试 mockMvc.perform(post(/api/orders) .contentType(MediaType.APPLICATION_JSON) .content(JsonUtils.toJson(request))) .andExpect(status().isOk()); // 验证日志记录 ListOperationLog logs logRepository.findAll(); assertThat(logs).hasSize(1); OperationLog log logs.get(0); assertThat(log.getContent()).contains(创建订单); assertThat(log.getBizNo()).isNotNull(); assertThat(log.getSuccess()).isTrue(); } Test void testCustomFunction() { // 测试自定义函数 String template 用户{parseUser{#userId}}修改了订单; String userId 123; EvaluationContext context new StandardEvaluationContext(); context.setVariable(userId, userId); String result logTemplateParser.parseCustomFunctions(template, context); assertThat(result).contains(张三(13800138000)); } }八、总结8.1 方案优势总结彻底解耦业务代码零侵入专注业务逻辑灵活扩展支持自定义函数、存储策略、模板引擎高性能异步记录、批量处理、条件过滤易维护统一配置、集中管理、标准格式强可读自然语言模板、用户友好8.2 适用场景电商系统订单状态变更、物流跟踪CRM系统客户信息修改、跟进记录OA系统审批流程、文档操作金融系统资金变动、审核记录后台管理系统配置修改、数据操作8.3 部署建议通过本文介绍的设计方案我们可以实现一个高可用、高性能、易扩展的操作日志系统。这个系统不仅能够满足当前业务需求还能随着业务发展灵活扩展真正实现操作日志记录的优雅之道。
版权声明:本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!

西安建网站公司哪家好中国备案查询网站

记录一下,大部分也是ai写的 注意的是,逆地理编码需要用的key是web服务端的,所以得重新申请一个key 意外的是,我在官方的文档里面运行,用我自己的key,不好使。(官方直接调用逆地理编码的那个方法就可以)&…

张小明 2026/3/5 3:28:54 网站建设

图片模板 网站源码wordpress图片分页浏览器

Pydantic数据验证实战:5大场景解决Python类型安全痛点 【免费下载链接】pydantic Data validation using Python type hints 项目地址: https://gitcode.com/GitHub_Trending/py/pydantic 在现代Python开发中,数据验证和类型安全是确保应用稳定性…

张小明 2026/3/5 3:28:02 网站建设

网站调用优酷视频去除广告wordpress图片下一页

关注AI圈的小伙伴应该注意到了,最近有个叫做『N8N』的AI项目突然火了起来,目前在GitHub上的start已经突破100K,成为当前开源AI工具类的No.1,非常的牛批! N8N之所以能这么受欢迎,是因为它被称之为史上最强AI…

张小明 2026/3/5 3:28:01 网站建设

环艺毕业设计代做网站品牌红酒网站建设

Windows Server管理与监控全解析 1. WSUS更新同步任务 在从Windows Update站点或其他WSUS服务器同步更新时,需要按以下顺序完成任务: 1. 配置更新源和代理服务器 2. 指定文件存储位置 3. 指定同步计划 4. 指定要下载的产品和更新类型 5. 定义计算机组 6. 指定要支持的…

张小明 2026/3/5 3:28:05 网站建设

ps企业网站模板郑州有哪些互联网公司

✅ 博主简介:擅长数据搜集与处理、建模仿真、程序设计、仿真代码、论文写作与指导,毕业论文、期刊论文经验交流。✅ 具体问题可以私信或扫描文章底部二维码。1) 针对加速器驱动系统堆顶屏蔽设计中缺乏快中子注量率标准的问题,通过辐射剂量与释…

张小明 2026/3/5 3:28:06 网站建设

有什么做设计的兼职网站快速创建一个网页

以下是基于2025年权威榜单及行业测评的电商系统开发公司综合推荐,结合技术实力、服务特色、行业适配性等维度分类整理,供企业高效决策参考:一、高端定制开发公司商联达推荐指数:★★★★★核心优势:为企业提供B2B2C/B2…

张小明 2026/3/5 3:34:02 网站建设