当前位置: 首页 > news >正文

Java实现日志全链路追踪.精确到一次请求的全部流程

广大程序员在排除线上问题时,会经常遇见各种BUG.处理这些BUG的时候日志就格外的重要.只有完善的日志才能快速有效的定位问题.为了提高BUG处理效率.我决定在日志上面优化.实现每次请求有统一的id.通过id能获取当前接口的全链路流程走向. 

实现效果如下: 一次查询即可找到所有关键信息.不再被多线程日志进行困扰了.

1:日志打印框架log4j ->  logback

logback是springboot默认自带的日志框架。不仅速度更快,而且内存占用也更小. (如果之前没用过log4j的建议先去学习下怎么使用).

打印日志的配置文件如下:  logback.xml

<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="true" scanPeriod="60 seconds">
<include resource="org/springframework/boot/logging/logback/base.xml"/>
<jmxConfigurator/>
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<Target>System.out</Target>
<encoder>
<charset>UTF-8</charset>
<pattern>%d - [%t] %-5p %c:%L %X - %m%n</pattern>
</encoder>
</appender>
<appender name="DAILY_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<Append>true</Append>
<!--  日志输出路径 -->
<File>/opt/logs/logOut.log</File>
<encoder>
<charset>UTF-8</charset>
<pattern>%d - [%t] %-5p %c:%L %X - %m%n</pattern>
</encoder>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>/opt/logs/logOut.log.%d{yyyy-MM-dd}</fileNamePattern>
<maxHistory>7</maxHistory>
</rollingPolicy>
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符-->
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread][%X{traceId}] %-5level %logger{50} - %msg%n</pattern>
</encoder>
</appender>
<appender name="ROLLING_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<File>/opt/logs/logOut-error.log</File>
<Append>true</Append>
<encoder>
<charset>UTF-8</charset>
<pattern>%d - [%t] %-5p %c:%L %X - %m%n</pattern>
</encoder>
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>ERROR</level>
</filter>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>/opt/logs/logOut-error.log.%d{yyyy-MM-dd}</fileNamePattern>
<maxHistory>7</maxHistory>
</rollingPolicy>
</appender><logger name="org.springframework" level="INFO"/>
<root level="INFO">
<appender-ref ref="DAILY_FILE"/>
<appender-ref ref="ROLLING_FILE"/>
</root>
</configuration>

重点是这行代码

<!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符-->
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread][%X{traceId}] %-5level %logger{50} - %msg%n</pattern>

此时的配置文件打印只能出现 年月日-线程名这些关键信息.无法获得每次请求的唯一id.所以我们需要创建一个拦截器.将每次请求生成一个id.通过id把本次请求覆盖到每个流程中.

2.1: 编写 http请求 拦截器

public class TraceWebInterceptor extends HandlerInterceptorAdapter {private static final Logger LOGGER = LoggerFactory.getLogger(TraceWebInterceptor.class);@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {request.setAttribute("startTime", System.currentTimeMillis());//traceOrigin、traceCaller、traceIdString traceOrigin = request.getHeader(TraceConstants.LOG_TRACE_ORIGIN);String traceCaller = request.getHeader(TraceConstants.LOG_TRACE_CALLER);String traceId = request.getHeader(TraceConstants.LOG_TRACE_ID);//如果不存在traceId需要生成if (StringUtils.isBlank(traceId)) {boolean generate = TraceUtil.loadTraceInfo();if(generate) {LOGGER.debug("[生成追踪信息]" + TraceUtil.getTraceInfoString());}}else {//设置MDCMDC.put(TraceConstants.LOG_TRACE_ORIGIN, traceOrigin);MDC.put(TraceConstants.LOG_TRACE_CALLER, traceCaller);MDC.put(TraceConstants.LOG_TRACE_ID, traceId);}//IPString traceIp = IpUtil.getIp(request);MDC.put(TraceConstants.LOG_TRACE_IP, traceIp);//响应返回response.setHeader(TraceConstants.LOG_TRACE_ID, TraceUtil.getTraceId());return super.preHandle(request, response, handler);}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws IOException {if (LOGGER.isInfoEnabled()) {long upmsStartTime = (long) request.getAttribute("startTime");long upmsEndTime = System.currentTimeMillis();long upmsIntervalTime = upmsEndTime - upmsStartTime;LOGGER.info("{} {}接口耗时{}毫秒", request.getRequestURL(), request.getMethod(), upmsIntervalTime);}MDC.clear();}

2.2 编写Config类, 将拦截器TraceWebInterceptor添加到容器

@Configuration
@ConditionalOnClass({HandlerInterceptorAdapter.class, MDC.class, HttpServletRequest.class})
public class TraceWebAutoConfiguration implements WebMvcConfigurer {private static List<String> EXCLUDE_PATHS = new ArrayList<>();@Value("${" + TraceConstants.CONFIG_TRACE_EXCLUDE_PATHS + ":}")private String excludePaths;@Beanpublic TraceWebInterceptor traceWebInterceptor() {return new TraceWebInterceptor();}@Overridepublic void addInterceptors(InterceptorRegistry registry) {EXCLUDE_PATHS.add("/error");EXCLUDE_PATHS.add("/actuator/**");if (StringUtils.isNotBlank(excludePaths)) {if (excludePaths.contains(",")) {String[] split = excludePaths.split(",");EXCLUDE_PATHS.addAll(Arrays.asList(split));} else {EXCLUDE_PATHS.add(excludePaths);}}//该方式不能过全部过滤掉registry.addInterceptor(traceWebInterceptor()).order(-100).excludePathPatterns(EXCLUDE_PATHS);}
}

2.3 编写工具类

import javax.servlet.http.HttpServletRequest;public class IpUtil {private static final String UNKNOWN = "unknown";public static String getIp(HttpServletRequest request) {if (request == null) {return UNKNOWN;}String ip = request.getHeader("x-forwarded-for");if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {ip = request.getHeader("Proxy-Client-IP");}if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {ip = request.getHeader("X-Forwarded-For");}if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {ip = request.getHeader("WL-Proxy-Client-IP");}if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {ip = request.getHeader("X-Real-IP");}if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {ip = request.getRemoteAddr();}return "0:0:0:0:0:0:0:1".equals(ip) ? "127.0.0.1" : ip;}
}

public class TraceConstants {public static final String LOG_TRACE_ORIGIN = "traceOrigin";public static final String LOG_TRACE_CALLER = "traceCaller";public static final String LOG_TRACE_IP = "traceIp";public static final String LOG_TRACE_ID = "traceId";public static final String CONFIG_TRACE_EXCLUDE_PATHS = "trace.exclude.paths";public TraceConstants() {}
}

import org.apache.commons.lang3.StringUtils;
import org.slf4j.MDC;import java.util.UUID;public class TraceUtil {private static boolean simbaHttpClientInterceptorFlag = true;private static boolean sdkInterceptorFlag = false;private static String applicationName;public TraceUtil() {}public static void setApplicationName(String applicationName) {TraceUtil.applicationName = applicationName;}public static String getApplicationName() {return applicationName;}public static boolean getSimbaHttpClientInterceptorFlag() {return simbaHttpClientInterceptorFlag;}public static void setSimbaHttpClientInterceptorFlag(boolean simbaHttpClientInterceptorFlag) {TraceUtil.simbaHttpClientInterceptorFlag = simbaHttpClientInterceptorFlag;}public static boolean getSdkInterceptorFlag() {return sdkInterceptorFlag;}public static void setSdkInterceptorFlag(boolean sdkInterceptorFlag) {TraceUtil.sdkInterceptorFlag = sdkInterceptorFlag;}public static void setTraceCaller(String traceCaller) {MDC.put("traceCaller", traceCaller);}public static String getTraceCaller() {return MDC.get("traceCaller");}public static void setTraceOrigin(String traceOrigin) {MDC.put("traceOrigin", traceOrigin);}public static String getTraceOrigin() {return MDC.get("traceOrigin");}public static void setTraceId(String traceId) {MDC.put("traceId", traceId);}public static void removeTraceId() {MDC.remove("traceId");}public static void clearMdc() {MDC.clear();}public static String getTraceId() {return MDC.get("traceId");}public static String genTraceId() {return UUID.randomUUID().toString().replace("-", "");}public static String getTraceIp() {return MDC.get("traceIp");}public static void setTraceIp(String traceIp) {MDC.put("traceIp", traceIp);}public static boolean loadTraceInfo() {boolean generate = false;String traceId = getTraceId();if (StringUtils.isBlank(traceId)) {traceId = genTraceId();generate = true;}setTraceId(traceId);return generate;}public static String getTraceInfoString() {return "TraceId:" + getTraceId() + ". traceCaller:" + getTraceCaller() + ". traceOrigin:" + getTraceOrigin();}
}

import org.apache.commons.lang3.StringUtils;
import org.slf4j.MDC;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;import javax.servlet.http.HttpServletRequest;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;@Configuration
@ConditionalOnClass({HandlerInterceptorAdapter.class, MDC.class, HttpServletRequest.class})
public class TraceWebAutoConfiguration implements WebMvcConfigurer {private static List<String> EXCLUDE_PATHS = new ArrayList<>();@Value("${" + TraceConstants.CONFIG_TRACE_EXCLUDE_PATHS + ":}")private String excludePaths;@Beanpublic TraceWebInterceptor traceWebInterceptor() {return new TraceWebInterceptor();}@Overridepublic void addInterceptors(InterceptorRegistry registry) {EXCLUDE_PATHS.add("/error");EXCLUDE_PATHS.add("/actuator/**");if (StringUtils.isNotBlank(excludePaths)) {if (excludePaths.contains(",")) {String[] split = excludePaths.split(",");EXCLUDE_PATHS.addAll(Arrays.asList(split));} else {EXCLUDE_PATHS.add(excludePaths);}}//该方式不能过全部过滤掉registry.addInterceptor(traceWebInterceptor()).order(-100).excludePathPatterns(EXCLUDE_PATHS);}
}

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;public class TraceWebInterceptor extends HandlerInterceptorAdapter {private static final Logger LOGGER = LoggerFactory.getLogger(TraceWebInterceptor.class);@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {request.setAttribute("startTime", System.currentTimeMillis());//traceOrigin、traceCaller、traceIdString traceOrigin = request.getHeader(TraceConstants.LOG_TRACE_ORIGIN);String traceCaller = request.getHeader(TraceConstants.LOG_TRACE_CALLER);String traceId = request.getHeader(TraceConstants.LOG_TRACE_ID);//如果不存在traceId需要生成if (StringUtils.isBlank(traceId)) {boolean generate = TraceUtil.loadTraceInfo();if (generate) {LOGGER.debug("[生成追踪信息]" + TraceUtil.getTraceInfoString());}} else {//设置MDCMDC.put(TraceConstants.LOG_TRACE_ORIGIN, traceOrigin);MDC.put(TraceConstants.LOG_TRACE_CALLER, traceCaller);MDC.put(TraceConstants.LOG_TRACE_ID, traceId);}//IPString traceIp = IpUtil.getIp(request);MDC.put(TraceConstants.LOG_TRACE_IP, traceIp);//响应返回response.setHeader(TraceConstants.LOG_TRACE_ID, TraceUtil.getTraceId());return super.preHandle(request, response, handler);}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws IOException {if (LOGGER.isInfoEnabled()) {long upmsStartTime = (long) request.getAttribute("startTime");long upmsEndTime = System.currentTimeMillis();long upmsIntervalTime = upmsEndTime - upmsStartTime;LOGGER.info("{} {}接口耗时{}毫秒", request.getRequestURL(), request.getMethod(), upmsIntervalTime);}MDC.clear();}
}

import org.slf4j.MDC;import java.util.Map;
import java.util.concurrent.*;public class WrapUtil {public static <T> Callable<T> wrap(final Callable<T> callable, final Map<String, String> context) {return () -> {if (context == null) {MDC.clear();} else {MDC.setContextMap(context);}TraceUtil.loadTraceInfo();try {return callable.call();} finally {MDC.clear();}};}public static <T> Callable<T> wrap(final Callable<T> callable) {return wrap(callable, MDC.getCopyOfContextMap());}public static Runnable wrap(final Runnable runnable, final Map<String, String> context) {return () -> {if (context == null) {MDC.clear();} else {MDC.setContextMap(context);}TraceUtil.loadTraceInfo();try {runnable.run();} finally {MDC.clear();}};}public static Runnable wrap(final Runnable runnable) {return wrap(runnable, MDC.getCopyOfContextMap());}public static ThreadPoolExecutor newThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit,BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory,RejectedExecutionHandler handler) {return new ThreadPoolExecutorMdcWrapper(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, handler);}public static ThreadPoolExecutor newThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit,BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory) {return new ThreadPoolExecutorMdcWrapper(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory);}public static ThreadPoolExecutor newThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit,BlockingQueue<Runnable> workQueue) {return new ThreadPoolExecutorMdcWrapper(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);}public static ThreadPoolExecutor newThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit,BlockingQueue<Runnable> workQueue,RejectedExecutionHandler handler) {return new ThreadPoolExecutorMdcWrapper(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, handler);}public static ForkJoinPool newForkJoinPool() {return new ForkJoinPoolMdcWrapper();}public static class ThreadPoolExecutorMdcWrapper extends ThreadPoolExecutor {public ThreadPoolExecutorMdcWrapper(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit,BlockingQueue<Runnable> workQueue) {super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);}public ThreadPoolExecutorMdcWrapper(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit,BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory) {super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory);}public ThreadPoolExecutorMdcWrapper(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit,BlockingQueue<Runnable> workQueue, RejectedExecutionHandler handler) {super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, handler);}public ThreadPoolExecutorMdcWrapper(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit,BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory,RejectedExecutionHandler handler) {super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, handler);}@Overridepublic void execute(Runnable task) {super.execute(WrapUtil.wrap(task, MDC.getCopyOfContextMap()));}@Overridepublic <T> Future<T> submit(Runnable task, T result) {return super.submit(WrapUtil.wrap(task, MDC.getCopyOfContextMap()), result);}@Overridepublic <T> Future<T> submit(Callable<T> task) {return super.submit(WrapUtil.wrap(task, MDC.getCopyOfContextMap()));}@Overridepublic Future<?> submit(Runnable task) {return super.submit(WrapUtil.wrap(task, MDC.getCopyOfContextMap()));}}public static class ForkJoinPoolMdcWrapper extends ForkJoinPool {public ForkJoinPoolMdcWrapper() {super();}public ForkJoinPoolMdcWrapper(int parallelism) {super(parallelism);}public ForkJoinPoolMdcWrapper(int parallelism, ForkJoinWorkerThreadFactory factory,Thread.UncaughtExceptionHandler handler, boolean asyncMode) {super(parallelism, factory, handler, asyncMode);}@Overridepublic void execute(Runnable task) {super.execute(WrapUtil.wrap(task, MDC.getCopyOfContextMap()));}@Overridepublic <T> ForkJoinTask<T> submit(Runnable task, T result) {return super.submit(WrapUtil.wrap(task, MDC.getCopyOfContextMap()), result);}@Overridepublic <T> ForkJoinTask<T> submit(Callable<T> task) {return super.submit(WrapUtil.wrap(task, MDC.getCopyOfContextMap()));}}
}

把工具类加上后.此时运行项目. 如果报错就处理下依赖导包. 不报错就说明可以正常使用了. 然后发布代码.运行方法.去查看日志吧. 此时每次请求都已经生成唯一ID了.

相关文章:

Java实现日志全链路追踪.精确到一次请求的全部流程

广大程序员在排除线上问题时,会经常遇见各种BUG.处理这些BUG的时候日志就格外的重要.只有完善的日志才能快速有效的定位问题.为了提高BUG处理效率.我决定在日志上面优化.实现每次请求有统一的id.通过id能获取当前接口的全链路流程走向. 实现效果如下: 一次查询即可找到所有关…...

你敢相信吗,AI绘画正在逐渐取代你的工作!

前言 在当今信息技术高速发展的时代&#xff0c;AI绘画技术的崛起已引起了广泛关注和讨论。许多人开始担心AI技术是否会逐渐取代传统绘画师的工作。人类无疑是感性的动物&#xff0c;创作出来的艺术作品常常带有浓郁的个人风格和情感。但AI绘画在某些方面的突破&#xff0c;使…...

博途PLC轴工艺对象随动误差监视功能

S7-1200PLC和V90总线伺服通过工艺对象实现定位控制时在组态工艺对象里有这样的随动误差监视功能介绍,关于这个功能,今天我们解读下,工艺对象组态编程可以参考下面文章链接: S7-1200PLC和V90总线伺服通过工艺对象实现定位控制(标准报文3应用)_v90工艺对象3号报文-CSDN博客文…...

《昇思25天学习打卡营第24天 | 昇思MindSporeResNet50图像分类》

24天 本节学习了使用ResNet50网络对CIFAR-10数据集进行分类。 步骤&#xff1a; 1.数据集准备与加载 2.构建网络 残差网络结构(Residual Network)是ResNet网络的主要亮点&#xff0c;ResNet使用残差网络结构后可有效地减轻退化问题&#xff0c;实现更深的网络结构设计&#x…...

糟糕的管理者都有这几个特征

在我们的职业生涯中&#xff0c;我们都期望能遇到一位英明睿智、引领团队走向辉煌的管理者。然而&#xff0c;现实往往并非总是如此美好&#xff0c;总会有一些管理能力差的人混迹其中&#xff0c;给团队带来诸多困扰。今天&#xff0c;我们就来看看糟糕的管理者身上都有哪些特…...

Python (Ansbile)脚本高效批量管理服务器和安全

1、简介 在现代 IT 基础设施中&#xff0c;管理大量服务器是一项复杂而繁琐的任务。特别是在检查服务器的存活状态以及 SSH 登录等任务上&#xff0c;手动操作非常耗时且容易出错。本文将介绍如何使用 Python 脚本实现对多台服务器的批量检查和管理&#xff0c;包括检查服务器…...

《数字图像处理与机器视觉》案例三 (基于数字图像处理的物料堆积角快速测量)

一、前言 物料堆积角是反映物料特性的重要参数&#xff0c;传统的测量方法将物料自然堆积&#xff0c;测量物料形成的圆锥表面与水平面的夹角即可&#xff0c;该方法检测效率低。随着数字成像设备的推广和应用&#xff0c;应用数字图像处理可以更准确更迅速地进行堆积角测量。 …...

Postman接口测试工具的原理及应用详解(四)

本系列文章简介&#xff1a; 在当今软件开发的世界中&#xff0c;接口测试作为保证软件质量的重要一环&#xff0c;其重要性不言而喻。随着前后端分离开发模式的普及&#xff0c;接口测试已成为连接前后端开发的桥梁&#xff0c;确保前后端之间的数据交互准确无误。在这样的背景…...

扛鼎中国AI搜索,天工凭什么?

人类的创作不会没有瓶颈&#xff0c;但AI的热度可不会消停。 大模型之战依旧精彩&#xff0c;OpenAI选择在Google前一天举行发布会&#xff0c;两家AI企业之间的拉扯赚足了热度。 反观国内&#xff0c;百模大战激发了大家对于科技变革的热切期盼&#xff0c;而如今行业已逐渐…...

【Ant Design Vue的更新日志】

&#x1f308;个人主页: 程序员不想敲代码啊 &#x1f3c6;CSDN优质创作者&#xff0c;CSDN实力新星&#xff0c;CSDN博客专家 &#x1f44d;点赞⭐评论⭐收藏 &#x1f91d;希望本文对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出指正&#xff0c;让我们共…...

Elasticsearch环境搭建|ES单机|ES单节点模式启动|ES集群搭建|ES集群环境搭建

文章目录 版本选择单机ES安装与配置创建非root用户导入安装包安装包解压配置JDK环境变量配置single-node配置JVM参数后台启动|启动日志查看启动成功&#xff0c;访问终端访问浏览器访问 Kibana安装修改配置后台启动|启动日志查看浏览器访问 ES三节点集群搭建停止es服务域名配置…...

System.currentTimeMillis() JAVA 转C#

JAVA中的System.currentTimeMillis() &#xff0c;指获取当前时间与1970年1月1日00:00:00 GMT之间所差的毫秒数的方法。 这个方法返回的是一个long类型的值&#xff0c;表示从某个固定时间点&#xff08;通常是UNIX纪元&#xff0c;即1970年1月1日00:00:00 GMT&#xff09;到…...

人机交互新维度|硕博电子发布双编码器操作面板、无线操作面板等新品

6月15日&#xff0c;硕博电子召开了一场新品发布会&#xff0c;向业界展示了多项前沿技术成果&#xff0c;其中备受瞩目的当属SPM-KEYP-D08双编码器操作面板、SPM-KEYP-D16W无线操作面板、SPR-HT-XK12A无线手持发射端以及SPQ-WT-B01洒水车专用控制面板。这些创新产品的亮相&…...

简单shell

目录 预备知识 fork 进程等待 wait waitpid 环境变量 概念 分类 常见的环境变量及其用途 环境变量的查看与设置 exec系列 函数解释 命名理解 简单shell 预备知识 fork fork 是 Linux 和许多其他类 Unix 系统中的一个重要系统调用&#xff0c;它用于创建一个新的…...

Spring Boot + FreeMarker 实现动态Word文档导出

Spring Boot FreeMarker 实现动态Word文档导出 在现代企业应用中&#xff0c;文档自动化生成是一项提升工作效率的重要功能。Spring Boot与FreeMarker的组合&#xff0c;为开发者提供了一个强大的平台&#xff0c;可以轻松实现动态Word文档的导出。本文将指导你如何使用Sprin…...

3D生物打印的未来:多材料技术的突破

多材料生物打印技术是近年来发展迅速的一项技术&#xff0c;为组织工程和再生医学带来了新的机遇&#xff0c;可以帮助我们更好地理解人体组织的结构和功能&#xff0c;并开发新的治疗方法。 1. 组织构建 复杂性模拟&#xff1a;多材料生物打印技术能够构建具有层次结构和异质…...

充电宝口碑哪个好?好用充电宝品牌有哪些?好用充电宝推荐

充电宝作为我们日常生活和出行的重要伙伴&#xff0c;其品质和性能直接影响着我们的使用体验。今天&#xff0c;就来和大家探讨一下充电宝口碑哪个好&#xff0c;为大家盘点那些备受赞誉的好用充电宝品牌&#xff0c;并向您推荐几款值得入手的充电宝&#xff0c;外出时不再担心…...

Pytorch-----(6)

一 、问题 如何计算基于不同变量的操作如矩阵乘法。 二、具体实现 0.4版本以前&#xff0c;张量是包裹在变量之中的&#xff0c;后者有三个属性grad、volatile和 requires_grad属性。&#xff08;grad 就是梯度属性&#xff0c;requires_grad属性就是 是否需要存储梯度&#x…...

leetcode hot100 第三题:最长连续序列(Java)

给定一个未排序的整数数组 nums &#xff0c;找出数字连续的最长序列&#xff08;不要求序列元素在原数组中连续&#xff09;的长度。 请你设计并实现时间复杂度为 O(n) 的算法解决此问题。 示例 1&#xff1a; 输入&#xff1a;nums [100,4,200,1,3,2] 输出&#xff1a;4 解…...

利用Jaspar进行转录因子结合位点预测

前期我们介绍了如何进行ChIP-qPCR验证&#xff0c;里面提到了一个比较重要的因素——扩增范围的选择及引物的设计。相比双荧光素酶、酵母单杂-点对点验证等允许完整启动子验证的实验&#xff0c;ChIP-qPCR要求单次验证的范围尽量控制在150-200bp内。但一个基因的启动子一般有2-…...

Ubuntu添加系统字体

&#xff08;2024.6.30&#xff09; 系统字体保存路径在/usr/share/fonts下&#xff0c;如果此目录下缺少字体&#xff0c;则使用其他可视化api&#xff08;如Python的pygame库&#xff09;的默认配置时可能会出现乱码问题。 往Ubuntu中添加字体的方法 方法一&#xff1a;手…...

深度学习相关概念及术语总结2

目录 76.AUC77.DBSCAN聚类78.贝叶斯个性化排序79.BPRBandit算法 76.AUC AUC&#xff08;Area Under the Curve&#xff09;是一种常用的评价指标&#xff0c;用于衡量分类模型的性能。AUC值代表了模型在不同阈值下的真阳性率&#xff08;True Positive Rate&#xff09;和假阳…...

基于改进滑模、经典滑模、最优滑模控制的永磁同步电机调速系统MATLAB仿真

微❤关注“电气仔推送”获得资料&#xff08;专享优惠&#xff09; 模型简介 针对永磁同步电机调速系统的响应性能和抗干扰能力问题&#xff0c;本文做了四个仿真&#xff0c;分别为&#xff1a;永磁同步电机的PID控制调速系统、基于传统滑模控制的永磁同步电机的调速系统、最…...

windows环境下创建python虚拟环境

windows环境下创建python虚拟环境 使用virtualenv库创建虚拟环境&#xff0c;可使不同的项目处于不同的环境中 安装方法&#xff1a; pip install virtualenv -i https://pypi.tuna.tsinghua.edu.cn/simple pip install virtualenvwrapper-win -i https://pypi.tuna.tsinghua…...

Fragment切换没变化?解决办法在这里

大家好&#xff0c;今天跟大家分享下如何避免fragment切换失败。方法其实很简单&#xff0c;只要在onCreate方法中初始化一个默认的fragment即可。 //开始事务FragmentTransaction transaction getActivity().getSupportFragmentManager().beginTransaction();transaction.rep…...

Linux系统防火墙iptables(下)

备份与还原iptables规则设置 1、yum -y install iptables iptables-services 安装iptables软件包 2、systemctl start iptables.service 开启服务 3、systemctl enable iptables.service 开机自启 我们对iptables命令行中的设置&#xff0c;都是临时设置&#xff0c;只要遇到服…...

你需要精益管理咨询公司的N+1个理由

近年来&#xff0c;精益管理作为一种被全球众多知名企业验证过的成功管理模式&#xff0c;越来越受到企业的青睐。但是&#xff0c;为何在实施精益管理的过程中&#xff0c;众多企业纷纷选择请咨询公司来协助呢&#xff1f;今天&#xff0c;我们就来一起揭秘这背后的原因。 1. …...

[机器学习]-3 万字话清从传统神经网络到深度学习

神经网络&#xff08;Neural Networks, NNs&#xff09;是机器学习的一种重要方法&#xff0c;灵感来源于生物神经系统&#xff0c;由大量互联的节点&#xff08;称为神经元或单元&#xff09;组成&#xff0c;通过调整这些节点间的连接权重来学习和表示复杂的非线性关系。传统…...

网络安全等级保护2.0(等保2.0)全面解析

一、等保2.0的定义和背景 网络安全等级保护2.0&#xff08;简称“等保2.0”&#xff09;是我国网络安全领域的基本制度、基本策略、基本方法。它是在《中华人民共和国网络安全法》指导下&#xff0c;对我国网络安全等级保护制度进行的重大升级。等保2.0的发布与实施&#xff0c…...

用Lobe Chat部署本地化, 搭建AI聊天机器人

Lobe Chat可以关联多个模型&#xff0c;可以调用外部OpenAI, gemini,通义千问等, 也可以关联内部本地大模型Ollama, 可以当作聊天对话框消息框来集成使用 安装方法参考&#xff1a; https://github.com/lobehub/lobe-chat https://lobehub.com/zh/docs/self-hosting/platform/…...

基于ARM的通用的Qt移植思路

文章目录 实验环境介绍一、确认Qt版本二、确认交叉编译工具链三、配置Qt3.1、修改qmake.conf3.2、创建autoConfig.sh配置文件 四、编译安装Qt五、移植Qt安装目录六、配置Qt creator6.1、配置qmake6.2、配置GCC编译器6.3、配置G编译器6.4、配置编译器套件6.5、创建应用 七、总结…...

IT专业入门,高考假期预习指南

七月来临&#xff0c;各省高考分数已揭榜完成。而高考的完结并不意味着学习的结束&#xff0c;而是新旅程的开始。对于有志于踏入IT领域的高考少年们&#xff0c;这个假期是开启探索IT世界的绝佳时机。 一、基础课程预习指南 IT专业是一个广泛的领域&#xff0c;涵盖了从软件开…...

芯片详解——AD7606C

芯片详解——AD7606C AD7607C 是一款由 Analog Devices(模拟器件公司)生产的 6 通道同步采样模数转换器(ADC),适用于高速数据采集系统。 工作原理 AD7607C 的工作原理主要包括以下几个步骤: 模拟信号输入:AD7607C 有六个模拟输入通道,可以同时进行采样。这些模拟信号…...

IDEA 编译单个Java文件

文章目录 一、class文件的生成位置二、编译单个文件编译项目报错Error:java: 无效的源发行版: 8 一、class文件的生成位置 file->project structure->Modules 二、编译单个文件 选中文件&#xff0c;点击recompile 编译项目报错 Error:java: 无效的源发行版: 8 Fi…...

人工智能业务分析

人工智能业务分析的组成图 #mermaid-svg-SKV0WrbMSANzQz4U {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-SKV0WrbMSANzQz4U .error-icon{fill:#552222;}#mermaid-svg-SKV0WrbMSANzQz4U .error-text{fill:#552222;s…...

随机文本生成器

目录 开头程序程序的流程图程序打印的效果(不必细看&#xff0c;因为字符太多)例1例2例3 结尾 开头 大家好&#xff0c;我叫这是我58。看&#xff01;这下面有一个程序。 程序 #define _CRT_SECURE_NO_WARNINGS 1 #include <stdio.h> #include <random> #includ…...

java中输入输出流的继承关系

在 Java 中,输入输出流的继承关系主要围绕两个抽象基类展开:字节流基类 InputStream 和 OutputStream,以及字符流基类 Reader 和 Writer。这些类形成了 Java I/O 系统的基础,提供了丰富的子类以适应不同的输入输出需求。 字节流 字节流用于处理原始的二进制数据。 Input…...

c++应用网络编程之一基本介绍

一、网络编程介绍 c编程的应用场景在前面分析过&#xff0c;一个重要的方向就是网络编程。一般来说&#xff0c;开发者说的服务端编程在c方向上简单的可以认为是网络编程。首先需要说明的&#xff0c;本系列不对网络编程的相关基础知识展开详细的说明&#xff0c;因为这种知识…...

Web后端开发概述环境搭建项目创建servlet生命周期

Web开发概述 web开发指的就是网页向后再让发送请求,与后端程序进行交互 web后端(javaEE)程序需要运行在服务器中 这样前端才可以对其进行进行访问 什么是服务器? 解释1: 服务器就是一款软件,可以向其发送请求,服务器会做出一个响应.可以在服务器中部署文件&#xff0c;让…...

Java 位运算详解

位运算是一种直接在二进制位上进行操作的方式。位运算符包括按位与 (&)、按位或 (|)、按位异或 (^)、按位非 (~)、左移 (<<)、右移 (>>) 和无符号右移 (>>>)。这些操作符用于操作整型数据类型&#xff0c;如 int 和 long。 一、按位与 (&) 按位…...

智能体实战:开发一个集成国内AI平台的GPTs,自媒体高效智能助手

文章目录 一&#xff0c;什么是GPTs二&#xff0c;开发GPTs1&#xff0c;目标2&#xff0c;开发2.1 打开 GPTS&#xff1a;https://chat.openai.com/gpts2.2 点击 Create 创建一个自己的智能体 2.3 配置GPTs2.4 配置外挂工具2.4.1 配置Authentication-授权2.4.1.1 生成语聚AI的…...

完美世界|单机版合集(共22个版本)

前言 我是研究单机的老罗&#xff0c;今天给大家带来的是完美世界的单机版合集&#xff0c;一共22个版本。本人亲自测试了一个版本&#xff0c;运行视频如下&#xff1a; 完美世界|单机版合集 先看所有的版本的文件&#xff0c;文件比较大&#xff0c;准备好空间&#xff0c;差…...

Jenkins的一些记录

设置环境变量 在 Jenkins 流水线中&#xff0c;取决于使用的是声明式还是脚本式流水线&#xff0c;设置环境变量的方法不同。 声明式流水线支持 environment 指令&#xff0c;而脚本式流水线的使用者必须使用 withEnv 步骤。 pipeline {agent anyenvironment { CC clang}stag…...

讲讲js中的prototype和__proto__

在Javascript中&#xff0c;prototype和__proto__是两个重要的概念&#xff0c;在对象的原型链中扮演重要的角色。 prototype prototype是js函数的内置属性&#xff0c;每个函数都有一个prototype属性&#xff0c;它是一个指针&#xff0c;指向一个对象&#xff08;原型对象&a…...

JavaScript的学习之DOM的查询(一)

一、获得元素 通过document对象调用&#xff1a; getElementById()&#xff1a;通过id属性获取一个元素节点对象getElementsByTagName()&#xff1a;通过标签名获取一组元素节点对象getElementsByName()&#xff1a;通过name属性来获取一组元素节点对象 核心学习代码 <scrip…...

充电宝哪个品牌比较好一点?多维度实测西圣、绿联、倍思充电宝!

在这个快节奏的时代&#xff0c;智能手机已成为我们日常生活不可或缺的一部分&#xff0c;而充电宝作为其能量补给站&#xff0c;重要性不言而喻。面对市场上琳琅满目的充电宝品牌与型号&#xff0c;如何挑选一款既实用又高效的充电伴侣&#xff0c;成为了许多消费者的难题。今…...

ubuntu安装QT

以QT5.15.14为例 下载地址&#xff1a;Index of /archive/qt 安装步骤&#xff1a; 解压qt-everywhere-src-5.15.14运行&#xff1a; cd qt-everywhere-src-5.15.14 mkdir build cd build ../configure -prefix /opt/qt5.15.14 -opensource -confirm-license make -j16 sudo…...

DataGrip 2024 po for Mac 数据库管理工具解

Mac分享吧 文章目录 效果一、下载软件二、开始安装1、双击运行软件&#xff08;适合自己的M芯片版或Intel芯片版&#xff09;&#xff0c;将其从左侧拖入右侧文件夹中&#xff0c;等待安装完毕2、应用程序显示软件图标&#xff0c;表示安装成功3、打开访达&#xff0c;点击【文…...

《C++ Primer》导学系列:第 13 章 - 拷贝控制

13.1 拷贝、赋值与析构函数 拷贝控制是C++中类设计的重要组成部分,用于管理对象的复制、赋值和销毁过程。理解并正确实现拷贝控制函数(拷贝构造函数、拷贝赋值运算符和析构函数)对于编写健壮和高效的C++程序至关重要。 13.1.1 拷贝构造函数 拷贝构造函数用于创建对象的副…...

c++ 图论2 深度优先算法和广度优先算法

修改一下深度优先算法和广度优先算法&#xff0c;标出每一个节点相对于遍历起始位置的层级&#xff0c;遍历起始起点为第一层&#xff0c;和第一层相连的节点为第二层&#xff0c;以此类推 定义一个新的结构 struct NodeWithLevel {TreeNode* node;int level;NodeWithLevel(T…...

C++_02

1、类 1.1 什么是类 C 中的类&#xff08;class&#xff09;是一种编程结 构&#xff0c;用于创建对象。这些对象可以拥有属性&#xff08;即数据成员&#xff09;和行为&#xff08;即成员函数或方法&#xff09;。类的概念是面向对象编程的核心之一&#xff0c;其主要目的是…...

【HarmonyOS】模拟器一直停留在开机页面,无法进入手机桌面

环境:window 10, 开发者工具:DevEco Studio NEXT Developer Beta1 Build #DS-233.14475.28.36.503402 Build Version: 5.0.3.402, built on June 16, 2024 Runtime version: 17.0.101-b1087.17 amd64 VM: OpenJDK 64-Bit Server VM by JetBrains s.r.o. Windows 10.0 GC: G1 Y…...

vivado联合modelsim仿真

一. 编译Vivado仿真库 打开Vivado&#xff0c;Tools -> Compile Simulation Libraries 二. 设置仿真工具和库路径 因为新建工程的默认仿真工具是Vivado Simulator&#xff0c;所以要使用Modelsim仿真&#xff0c;每个新工程都要设置一次&#xff0c;方法如下&#xff1a; …...

ai智能电销机器人系统稳不稳,效果好不好

智能ai电销机器人好不好用&#xff1f;智能AI电销机器人系统现如今已经不是一个新名词了&#xff0c;在人工智能这个新趋势的风潮下&#xff0c;一些企业已经在电销部门引入了智能AI机器人系统。我一起来看看ai智能电销机器人系统稳不稳。 1.数据有效 通常我们需要收集潜在用…...

计算机网络微课堂(湖科大教书匠)TCP部分

计算机网络微课堂&#xff08;湖科大教书匠&#xff09;TCP部分 【计算机网络微课堂&#xff08;有字幕无背景音乐版&#xff09;】 TCP的流量控制 一般来说&#xff0c;我们希望数据传输得更快一些。但如果发送方把数据发送得过快&#xff0c;接收方就可能来不及接收&#…...

【神经网络】深入理解多层神经网络(深度神经网络

&#x1f388;个人主页&#xff1a;豌豆射手^ &#x1f389;欢迎 &#x1f44d;点赞✍评论⭐收藏 &#x1f91d;希望本文对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出指正&#xff0c;让我们共同学习、交流进步&#xff01; 深入理解多层神经网络&#x…...

2种动力配置,标配48英寸大屏,全新雷克萨斯LM马来西亚上市

近日,从海外媒体报道获悉,雷克萨斯正式宣布在马来西亚市场上市全新雷克萨斯LM。据悉,新车推出LM350h 和LM500h两款车型,售价分别为1,228,000 马来西亚林吉特 与1,468,000 马来西亚林吉特 (折合人民币约226.80万元)。外观上,全新雷克萨斯LM依旧承袭雷克萨斯最新一代家族设计…...

vmware中Ubuntu虚拟机和本地电脑Win10互相ping通

初始状态 使用vmware17版本安装的Ubuntu的20版本&#xff0c;安装之后什么配置都要不懂&#xff0c;然后进行下述配置。 初始的时候是NAT&#xff0c;没动的. 设置 点击右键编辑“属性” 常规选择“启用”&#xff1a; 高级选择全部&#xff1a; 打开网络配置&#xff0c;右键属…...

快团团供货大团长如何打印电子面单?

一、功能说明 快团团打单平台是目前唯一一个服务于快团团团长的打单发货工具&#xff0c;免费提供给团长使用。可帮助团长快速打印面单、分拣包裹、完成发货。 目前快团团打单平台已支持大批量打印快递单、自定义快递面单、自动发货、绑定拼多多电子面单账号等功能&#xff0c…...

Nginx配置若依前后端分离项目验证码不显示,403,405,404错误解决方式

server { listen 80; server_name 域名; location / { # 静态文件服务配置&#xff08;可选&#xff09; 前端打包后的位置dist里面的文件root /www/wwwroot/web; index index.html; try_files $uri $uri/ /index.html; } # 根据文件前端项目 .env.production里面内容进…...

Java GC问题排查的一些个人总结和问题复盘

个人博客 Java GC问题排查的一些个人总结和问题复盘 | iwts’s blog 是否存在GC问题判断指标 有的比较明显&#xff0c;比如发布上线后内存直接就起飞了&#xff0c;这种也是比较好排查的&#xff0c;也是最多的。如果单纯从优化角度&#xff0c;看当前应用是否需要优化&…...

AI数字人及其应用

本文将简单了解下AI数字人、应用场景。 一、基本认识 AI数字人&#xff1a;使用人工智能技术创建的虚拟数字化人物。这些数字人物可以被设计成具有人类般的外观、行为和交互能力。它们通常用于虚拟现实、视频、游戏、培训模拟、客户服务等领域。 其中AI数字人在视频制作中是…...