【SpringBoot應用篇】SpringBoot+MDC+自定義Filter操作traceId實現日志鏈路追蹤

【SpringBoot應用篇】SpringBoot+MDC+自定義Filter操作traceId實現日志鏈路追蹤

  • 解決的問題
  • 解決方案
  • MDC
  • 具體邏輯
    • yml
    • logback-spring.xml
    • TraceIdUtil操作工具類
    • TraceIdFilter自定義過濾器
    • GlobalExceptionHandler全局異常處理類
    • TraceIdAspect切面
    • UserController
    • 測試驗證
  • 多線程處理
    • MDCUtil工具類
    • ThreadPoolMdcWrapper
    • ContextTransferTaskDecorator
    • ThreadPoolConfig
    • UserController
    • 測試驗證

解決的問題

接口報錯,如何快速定位問題?這個需要日志的輔助,一般錯誤日志中有詳細的堆棧信息,具體是哪行代碼報錯,都可以看到。線程日志交差打印,要想快速定位問題,前提是要能夠快速定位日志。

日志量一般都是很大的,如何能夠從大量日志中找到自己需要的日志呢?

依賴原始的logback配置,很難從某服務龐雜的日志中,單獨找尋出某次線程API調用的全部日志。

解決方案

1、服務端入口處可以生成一個唯一的id,記做:traceId

2、日志中均需要輸出traceId的值

3、接口返回值中,添加一個通用的字段:traceId,將上面的traceId作為這個字段的值

  • Controller層返回的統一返回值對象R
  • 全局異常處理返回的統一返回值對象R

5、這樣前端發現接口有問題的時候,直接將這個traceId提供給我們,我們便可以在日志中快速查詢出對應的日志。使用 grep 'traceId' xxx.log 語句就能準確的定位到目標日志。

MDC

  • 日志追蹤目標是每次請求級別的,也就是說同一個接口的每次請求,都應該有不同的traceId。

  • 每次接口請求,都是一個單獨的線程,所以自然我們很容易考慮到通過ThreadLocal實現上述需求。

  • 考慮到logback本身已經提供了類似的功能MDC,所以直接使用MDC進行實現。

  • 關于MDC的簡述

    • Mapped Diagnostic Context,即:映射診斷環境。
    • MDC是 log4j 和 logback 提供的一種方便在多線程條件下記錄日志的功能。
    • MDC 可以看成是一個與當前線程綁定的哈希表,可以往其中添加鍵值對。
  • 關于MDC的關鍵操作

    • 向MDC中設置值:MDC.put(key, value);
    • 從MDC中取值:MDC.get(key);
    • 將MDC中內容打印到日志中:%X{key}

假定已經解決了traceId的存放問題,那么何時進行traceId的存放呢?其實有多重實現思路,例如:過濾器、AOP、攔截器等等。

具體邏輯

yml

server:port: 8081spring:profiles:active: devapplication:name: springboot-logmain:allow-bean-definition-overriding: true

logback-spring.xml

<?xml version="1.0" encoding="UTF-8"?>
<configuration  scan="true" scanPeriod="10 seconds"><!-- 日志級別從低到高分為TRACE < DEBUG < INFO < WARN < ERROR < FATAL,如果設置為WARN,則低于WARN的信息都不會輸出 --><!-- scan:當此屬性設置為true時,配置文件如果發生改變,將會被重新加載,默認值為true --><!-- scanPeriod:設置監測配置文件是否有修改的時間間隔,如果沒有給出時間單位,默認單位是毫秒。當scan為true時,此屬性生效。默認的時間間隔為1分鐘。 --><!-- debug:當此屬性設置為true時,將打印出logback內部日志信息,實時查看logback運行狀態。默認值為false--><contextName>logback</contextName><!-- name的值是變量的名稱,value的值時變量定義的值。通過定義的值會被插入到logger上下文中。定義變量后,可以使“${}”來使用變量。 --><property name="log.path" value="logs/log" /><!-- 彩色日志 --><!-- 配置格式變量:CONSOLE_LOG_PATTERN 彩色日志格式 --><!-- magenta:洋紅 --><!-- boldMagenta:粗紅--><!-- cyan:青色 --><!-- white:白色 --><!-- magenta:洋紅 --><property name="CONSOLE_LOG_PATTERN"value="%X{traceId}|%yellow(%date{yyyy-MM-dd HH:mm:ss}) |%highlight(%-5level) |%blue(%thread) |%blue(%file:%line) |%green(%logger) |%cyan(%msg%n)"/><!--輸出到控制臺--><appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender"><!--此日志appender是為開發使用,只配置最底級別,控制臺輸出的日志級別是大于或等于此級別的日志信息--><!-- 例如:如果此處配置了INFO級別,則后面其他位置即使配置了DEBUG級別的日志,也不會被輸出 --><!--<filter class="ch.qos.logback.classic.filter.ThresholdFilter"><level>INFO</level></filter>--><encoder><Pattern>${CONSOLE_LOG_PATTERN}</Pattern><!-- 設置字符集 --><charset>UTF-8</charset></encoder></appender><!--輸出到文件--><!-- 時間滾動輸出 level為 INFO 日志 --><appender name="INFO_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"><!-- 正在記錄的日志文件的路徑及文件名 --><file>${log.path}/log_info.log</file><!--日志文件輸出格式--><encoder><pattern>%X{traceId} %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern><charset>UTF-8</charset></encoder><!-- 日志記錄器的滾動策略,按日期,按大小記錄 --><rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"><!-- 每天日志歸檔路徑以及格式 --><fileNamePattern>${log.path}/info/log-info-%d{yyyy-MM-dd}.%i.log</fileNamePattern><timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP"><maxFileSize>100MB</maxFileSize></timeBasedFileNamingAndTriggeringPolicy><!--日志文件保留天數--><maxHistory>15</maxHistory></rollingPolicy><!-- 此日志文件只記錄info級別的 --><filter class="ch.qos.logback.classic.filter.LevelFilter"><level>INFO</level><onMatch>ACCEPT</onMatch><onMismatch>DENY</onMismatch></filter></appender><!-- 時間滾動輸出 level為 WARN 日志 --><appender name="WARN_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"><!-- 正在記錄的日志文件的路徑及文件名 --><file>${log.path}/log_warn.log</file><!--日志文件輸出格式--><encoder><pattern>%X{traceId} %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern><charset>UTF-8</charset> <!-- 此處設置字符集 --></encoder><!-- 日志記錄器的滾動策略,按日期,按大小記錄 --><rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"><fileNamePattern>${log.path}/warn/log-warn-%d{yyyy-MM-dd}.%i.log</fileNamePattern><timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP"><maxFileSize>100MB</maxFileSize></timeBasedFileNamingAndTriggeringPolicy><!--日志文件保留天數--><maxHistory>15</maxHistory></rollingPolicy><!-- 此日志文件只記錄warn級別的 --><filter class="ch.qos.logback.classic.filter.LevelFilter"><level>warn</level><onMatch>ACCEPT</onMatch><onMismatch>DENY</onMismatch></filter></appender><!-- 時間滾動輸出 level為 ERROR 日志 --><appender name="ERROR_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"><!-- 正在記錄的日志文件的路徑及文件名 --><file>${log.path}/log_error.log</file><!--日志文件輸出格式--><encoder><pattern>%X{traceId} %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern><charset>UTF-8</charset> <!-- 此處設置字符集 --></encoder><!-- 第一種方式:日志記錄器的滾動策略,按日期,按大小記錄 --><rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"><fileNamePattern>${log.path}/error/log-error-%d{yyyy-MM-dd}.%i.log</fileNamePattern><timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP"><maxFileSize>100MB</maxFileSize></timeBasedFileNamingAndTriggeringPolicy><!--日志文件保留天數--><maxHistory>15</maxHistory></rollingPolicy><!-- 第二種方式:指定日志文件拆分和壓縮規則 --><!-- 指定日志文件拆分和壓縮規則 --><!--        <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">--><!--            &lt;!&ndash; 通過指定壓縮文件名稱,來確定分割文件方式&ndash;&gt;--><!--            <fileNamePattern>${log.path}/error/log-error-%d{yyyy-MM-dd}.log%i.zip</fileNamePattern>--><!--            &lt;!&ndash;  文件拆分大小  &ndash;&gt;--><!--            <maxFileSize>1MB</maxFileSize>--><!--            &lt;!&ndash;日志文件保留天數&ndash;&gt;--><!--            <maxHistory>15</maxHistory>--><!--        </rollingPolicy>--><!-- 此日志文件只記錄ERROR級別的 --><filter class="ch.qos.logback.classic.filter.LevelFilter"><level>ERROR</level><onMatch>ACCEPT</onMatch><onMismatch>DENY</onMismatch></filter></appender><!--<logger>用來設置某一個包或者具體的某一個類的日志打印級別、以及指定<appender><logger>僅有一個name屬性,一個可選的level和一個可選的addtivity屬性。name:用來指定受此logger約束的某一個包或者具體的某一個類。level:用來設置打印級別,大小寫無關:TRACE, DEBUG, INFO, WARN, ERROR, ALLOFF,如果未設置此屬性,那么當前logger將會繼承上級的級別。--><!--使用mybatis的時候,sql語句是debug下才會打印,而這里我們只配置了info,所以想要查看sql語句的話,有以下兩種操作:第一種把<root level="INFO">改成<root level="DEBUG">這樣就會打印sql,不過這樣日志那邊會出現很多其他消息第二種就是單獨給mapper下目錄配置DEBUG模式,代碼如下,這樣配置sql語句會打印,其他還是正常DEBUG級別:<logger name="cn.zysheep.mapper" level="INFO" />--><!--結合spring多環境使用 開發環境:打印控制臺--><springProfile name="dev"><!--root節點是必選節點,用來指定最基礎的日志輸出級別,只有一個level屬性level:用來設置打印級別,大小寫無關:TRACE, DEBUG, INFO, WARN, ERROR, ALLOFF,不寫level屬性,默認是DEBUG可以包含零個或多個appender元素。--><root level="INFO"><appender-ref ref="CONSOLE" /><appender-ref ref="INFO_FILE" /><appender-ref ref="WARN_FILE" /><appender-ref ref="ERROR_FILE" /></root></springProfile><!--結合spring多環境使用:生產環境:輸出到文件--><springProfile name="pro"><root level="INFO"><appender-ref ref="ERROR_FILE" /><appender-ref ref="WARN_FILE" /></root></springProfile>
</configuration>

TraceIdUtil操作工具類

/*** <p>traceId工具類</P>**/
public class TraceIdUtil {public static final String TRACE_ID = "traceId";/*** 當traceId為空時,顯示的traceId。隨意。*/private static final String DEFAULT_TRACE_ID = "0";/*** 設置traceId*/public static void setTraceId(String traceId) {//如果參數為空,則設置默認traceIdtraceId = StringUtils.isBlank(traceId) ? DEFAULT_TRACE_ID : traceId;//將traceId放到MDC中MDC.put(TRACE_ID, traceId);}/*** 獲取traceId*/public static String getTraceId() {//獲取String traceId = MDC.get(TRACE_ID);//如果traceId為空,則返回默認值return StringUtils.isBlank(traceId) ? DEFAULT_TRACE_ID : traceId;}/*** 判斷traceId為默認值*/public static Boolean defaultTraceId(String traceId) {return DEFAULT_TRACE_ID.equals(traceId);}/*** 生成traceId*/public static String genTraceId() {return UUID.randomUUID().toString();}/*** 判斷traceId為默認值*/public static void removeTraceId() {MDC.clear();}
}

TraceIdFilter自定義過濾器

/*** <p>traceId過濾器,用于設置traceId</P>**/
@WebFilter(urlPatterns = "/*", filterName = "traceIdFilter")
@Order(1)
public class TraceIdFilter extends GenericFilterBean {public static Logger logger = LoggerFactory.getLogger(TraceIdFilter.class);@Overridepublic void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException {//traceId初始化initTraceId((HttpServletRequest) request);//執行后續過濾器long st = System.currentTimeMillis();try {filterChain.doFilter(request, response);} finally {long et = System.currentTimeMillis();logger.info("請求地址:{},耗時(ms):{}", ((HttpServletRequest) request).getRequestURI(), (et - st));TraceIdUtil.removeTraceId();}}/*** traceId初始化*/private void initTraceId(HttpServletRequest request) {//嘗試獲取http請求中的traceIdString traceId = request.getParameter("traceId");//如果當前traceId為空或者為默認traceId,則生成新的traceIdif (StringUtils.isBlank(traceId) || TraceIdUtil.defaultTraceId(traceId)){traceId = TraceIdUtil.genTraceId();}//設置traceIdTraceIdUtil.setTraceId(traceId);}@Overridepublic void destroy() {TraceIdUtil.removeTraceId();}
}

GlobalExceptionHandler全局異常處理類

@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {@ExceptionHandler(Exception.class)public R<String> otherExceptionHandler(Exception ex, HttpServletRequest request) {log.warn("Exception:", ex);return R.result(ExceptionCode.SYSTEM_BUSY.getCode(), StringUtils.EMPTY, ExceptionCode.SYSTEM_BUSY.getMsg()).setPath(request.getRequestURI());}
}

TraceIdAspect切面

@Component
@Aspect
@Order
public class TraceIdAspect {/*** 切點:* 1、所有controller包及其子包下的所有方法* 2、所有GlobalExceptionHandler類中的所有方法*/@Pointcut("execution(public * cn.zysheep.controller..*.*(..)) || execution(* cn.zysheep.exception.GlobalExceptionHandler.*(..))")public void pointCut() {}@Around("pointCut()")public Object around(ProceedingJoinPoint pjp) throws Throwable {Object object = pjp.proceed();if (object instanceof R) {((R<?>) object).setTraceId(TraceIdUtil.getTraceId());}return object;}
}

UserController

@RestController
@RequestMapping("/user")
public class UserController {private static final Logger log = LoggerFactory.getLogger(UserController.class);@GetMapping(value = "query")public R findByPage() throws InterruptedException {log.info("開始執行查詢用戶業務");TimeUnit.MILLISECONDS.sleep(500);log.info("查詢用戶業務執行結束");return R.success();}@GetMapping("/exception")public R exception()  {log.info("開始執行業務");//這里模擬了一個錯誤,10/0,會報錯System.out.println(10 / 0);log.info("業務執行結束");return R.success();}
}

測試驗證

在這里插入圖片描述

在這里插入圖片描述

多線程處理

MDCUtil工具類

/*** <p>* 封裝MDC用于向線程池傳遞* </p>*/
public class MDCUtil {// 設置MDC中的traceId值,不存在則新生成,針對不是子線程的情況,// 如果是子線程,MDC中traceId不為nullpublic static void setTraceIdIfAbsent() {if (MDC.get(TraceIdUtil.TRACE_ID) == null) {MDC.put(TraceIdUtil.TRACE_ID, TraceIdUtil.getTraceId());}}public static <T> Callable<T> wrap(final Callable<T> callable, final Map<String, String> context) {return () -> {if (CollectionUtils.isEmpty(context)) {MDC.clear();} else {MDC.setContextMap(context);}setTraceIdIfAbsent();try {return callable.call();} finally {//清除子線程的,避免內存溢出,就和ThreadLocal.remove()一個原因MDC.clear();}};}public static Runnable wrap(final Runnable runnable, final Map<String, String> context) {return () -> {if (context == null) {MDC.clear();} else {MDC.setContextMap(context);}setTraceIdIfAbsent();try {runnable.run();} finally {MDC.clear();}};}public static void setMDCContextMap(final Map<String, String> context) {if (CollectionUtils.isEmpty(context)) {MDC.clear();} else {MDC.setContextMap(context);}}
}

ThreadPoolMdcWrapper

public class ThreadPoolMdcWrapper extends ThreadPoolTaskExecutor {public ThreadPoolMdcWrapper() {}@Overridepublic void execute(Runnable task) {super.execute(MDCUtil.wrap(task, MDC.getCopyOfContextMap()));}@Overridepublic void execute(Runnable task, long startTimeout) {super.execute(MDCUtil.wrap(task, MDC.getCopyOfContextMap()), startTimeout);}@Overridepublic <T> Future<T> submit(Callable<T> task) {return super.submit(MDCUtil.wrap(task, MDC.getCopyOfContextMap()));}@Overridepublic Future<?> submit(Runnable task) {return super.submit(MDCUtil.wrap(task, MDC.getCopyOfContextMap()));}@Overridepublic ListenableFuture<?> submitListenable(Runnable task) {return super.submitListenable(MDCUtil.wrap(task, MDC.getCopyOfContextMap()));}@Overridepublic <T> ListenableFuture<T> submitListenable(Callable<T> task) {return super.submitListenable(MDCUtil.wrap(task, MDC.getCopyOfContextMap()));}
}

ContextTransferTaskDecorator

public class ContextTransferTaskDecorator implements TaskDecorator {@Overridepublic Runnable decorate(Runnable runnable) {Map<String, String> context = MDC.getCopyOfContextMap();RequestAttributes requestAttributes = RequestContextHolder.currentRequestAttributes();return () -> {try {MDC.setContextMap(context);RequestContextHolder.setRequestAttributes(requestAttributes);runnable.run();} finally {MDC.clear();RequestContextHolder.resetRequestAttributes();}};}
}

ThreadPoolConfig

@Configuration
public class ThreadPoolConfig {@Bean("poolTaskExecutor")public ThreadPoolTaskExecutor threadPoolTaskExecutor() {ThreadPoolTaskExecutor taskExecutor = new ThreadPoolMdcWrapper();//核心線程數,默認為1taskExecutor.setCorePoolSize(5);//最大線程數,默認為Integer.MAX_VALUEtaskExecutor.setMaxPoolSize(10);//隊列最大長度,一般需要設置值>=notifyScheduledMainExecutor.maxNum;默認為Integer.MAX_VALUEtaskExecutor.setQueueCapacity(200);//線程池維護線程所允許的空閑時間,默認為60staskExecutor.setKeepAliveSeconds(60);taskExecutor.setThreadNamePrefix("sif-async-executor-");//線程池對拒絕任務(無線程可用)的處理策略taskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.AbortPolicy());taskExecutor.setTaskDecorator(new ContextTransferTaskDecorator());// 初始化線程池taskExecutor.initialize();return  taskExecutor;}
}

UserController

@RestController
@RequestMapping("/user")
public class UserController {private static final Logger log = LoggerFactory.getLogger(UserController.class);@Resource(name = "poolTaskExecutor")private ThreadPoolTaskExecutor threadPoolExecutor;@GetMapping("/t1")public R test1(){log.info("開始....");CompletableFuture.runAsync(() ->{log.info("異步中....");}, threadPoolExecutor);log.info("結束....");return R.success();}@GetMapping("/t2")public R test2(){log.info("開始....");threadPoolExecutor.execute(() ->{log.info("線程池中....");});log.info("結束....");return R.success();}
}

測試驗證

在這里插入圖片描述

在這里插入圖片描述

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/66288.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/66288.shtml
英文地址,請注明出處:http://en.pswp.cn/web/66288.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

python如何解析word文件格式(.docx)

python如何解析word文件格式&#xff08;.docx&#xff09; .docx文件遵從開源的“Office Open XML標準”&#xff0c;這意味著我們能用python的文本操作對它進行操作&#xff08;實際上PPT和Excel也是&#xff09;。而且這并不是重復造輪子&#xff0c;因為市面上操作.docx的…

android wifi framework與wpa_supplicant的交互

android frmework直接與wpa_supplicant進行交互&#xff0c;使用aidl或者hidl 二、事件 framework注冊事件的地方&#xff1a; packages/modules/Wifi/service/java/com/android/server/wifi/SupplicantStaIfaceCallbackImpl.java class SupplicantStaIfaceCallbackImpl exte…

python爬蟲報錯日記

python爬蟲報錯日記 類未定義 原因&#xff1a;代碼檢查沒有問題**&#xff0c;位置錯了**&#xff0c;測試代碼包含在類里…… UnicodedecodeError錯誤 原因&#xff1a;字符沒有自動轉換成utf-8格式 KeyError&#xff1a;“href” 原因&#xff1a;前面運行正常&#x…

[ Spring ] Install Nacos on Ubuntu24

文章目錄 Download Nacos Package and UnzipConfigure NacosConfigure JAVA_HOME for Bash FileConfigure Default User and PasswordStartup NacosVisit Admin Webpage Download Nacos Package and Unzip https://github.com/alibaba/nacos/releases/download/3.0.0-alpha/na…

Swift語言的多線程編程

Swift語言的多線程編程 在現代軟件開發中&#xff0c;多線程編程是提高應用性能和響應速度的重要手段。尤其是在 iOS 和 macOS 開發中&#xff0c;由于用戶界面(UI)的交互性和復雜性&#xff0c;合理利用多線程可以極大地提升用戶體驗。本文將深入探討 Swift 語言中的多線程編…

第34天:Web開發-PHP應用鑒別修復AI算法流量檢測PHP.INI通用過濾內置函數

#知識點 1、安全開發-原生PHP-PHP.INI安全 2、安全開發-原生PHP-全局文件&單函數 3、安全開發-原生PHP-流量檢測&AI算法 一、通用-PHP.INI設置 參考&#xff1a; https://www.yisu.com/ask/28100386.html https://blog.csdn.net/u014265398/article/details/109700309 …

機器學習之決策樹(DecisionTree)

決策樹中選擇哪一個特征進行分裂&#xff0c;稱之為特征選擇。 特征選擇是找出某一個特征使得分裂后兩邊的樣本都有最好的“歸宿”&#xff0c;即左邊分支的樣本屬于一個類別、右邊分支的樣本屬于另外一個類別&#xff0c;左邊和右邊分支包含的樣本盡可能分屬同一類別&#xff…

進階——十六屆藍橋杯嵌入式熟練度練習(按鍵+LCD)

高亮&#xff08;一&#xff09; 聲明 char buf[21];unsigned char upled0x04;uint8_t key_val;uint8_t key_down,key_up,key_old;uint32_t key_time;uint8_t key_temp,key_flag;uint8_t line_flag; 按鍵代碼 void key_proc(void) { key_valkey_scan();key_downkey_val&…

基于PHP的校園新聞發布管理

摘要 近年來&#xff0c;隨著互聯網技術的迅速發展&#xff0c;人們獲取新聞的渠道也變得越來越多樣化&#xff0c;已經不再拘束于傳統的報紙、期刊、雜志等紙質化的方式&#xff0c;而是通過網絡滿足了人們獲得第一手新聞的愿望&#xff0c;這樣更加有助于實現新聞的規范化管…

MySQL程序之:使用DNS SRV記錄連接到服務器

在域名系統&#xff08;DNS&#xff09;中&#xff0c;SRV記錄&#xff08;服務位置記錄&#xff09;是一種資源記錄&#xff0c;它使客戶端能夠指定指示服務、協議和領域的名稱。DNS查找該名稱會返回一個回復&#xff0c;其中包含該領域中提供所需服務的多個可用服務器的名稱。…

代碼合并沖突解決push不上去的問題

環境&#xff1a;【IntelliJ IDEA】 【Gerrit】 1、錯誤信息 代碼合并&#xff0c;迭代1合并到迭代2&#xff0c;解決沖突后&#xff0c;依然push不上去&#xff0c;報錯信息如下&#xff1a; remote: Processing changes: refs: 1 remote: Processing changes: refs…

opencv projectPoints函數 computeCorrespondEpilines函數 undistortPoints函數

opencv projectPoints函數 cv::projectPoints 是 OpenCV 中用于將三維點投影到二維圖像平面的函數。它通常用于計算在相機坐標系下的三維點在圖像坐標系中的位置&#xff0c;考慮了相機的內參和外參。 函數原型 void cv::projectPoints(InputArray objectPoints,InputArray …

從玩具到工業控制--51單片機的跨界傳奇【3】

在科技的浩瀚宇宙中&#xff0c;51 單片機就像一顆獨特的星辰&#xff0c;散發著神秘而迷人的光芒。對于無數電子愛好者而言&#xff0c;點亮 51 單片機上的第一顆 LED 燈&#xff0c;不僅僅是一次簡單的操作&#xff0c;更像是開啟了一扇通往新世界的大門。這小小的 LED 燈&am…

boss直聘 __zp_stoken__ 逆向分析

聲明: 本文章中所有內容僅供學習交流使用&#xff0c;不用于其他任何目的&#xff0c;抓包內容、敏感網址、數據接口等均已做脫敏處理&#xff0c;嚴禁用于商業用途和非法用途&#xff0c;否則由此產生的一切后果均與作者無關&#xff01; 有相關問題請第一時間頭像私信聯系我刪…

【日志篇】(7.6) ? 01. 在macOS下刷新FortiAnalyzer固件 ? FortiAnalyzer 日志分析

【簡介】FortiAnalyzer 是 Fortinet Security Fabric 安全架構的基礎&#xff0c;提供集中日志記錄和分析&#xff0c;以及端到端可見性。因此&#xff0c;分析師可以更有效地管理安全狀態&#xff0c;將安全流程自動化&#xff0c;并快速響應威脅。具有分析和自動化功能的集成…

Linux 內核自旋鎖spinlock(一)

文章目錄 前言一、自旋鎖1.1 簡介1.2 API1.2.1 spin_lock/spin_unlock1.2.2 spin_lock_irq/spin_unlock_irq1.2.3 spin_lock_irqsave/spin_unlock_irqstore1.2.4 spin_lock_bh/spin_unlock_bh1.2.5 補充 二、自選鎖原理三、自旋鎖在內核的使用3.1 struct file3.2 struct dentry…

【太古新篇,智馭未來】 SFA系統成功上線

經過雙方團隊的不懈努力與緊密合作&#xff0c;eBest成功幫助香港太古可樂完成了SFA系統的全面上線&#xff01; 太古可樂&#xff0c;作為飲料行業的佼佼者&#xff0c;一直以來以其卓越的品質和深入人心的品牌形象深受消費者喜愛。然而&#xff0c;在快速變化的市場環境中&am…

Web安全|滲透測試|網絡安全

基礎入門(P1-P5) p1概念名詞 1.1域名 什么是域名&#xff1f; 域名&#xff1a;是由一串用點分隔的名字組成的Internet上某一臺計算機或計算機組的名稱&#xff0c;用于在數據傳輸時對計算機的定位標識&#xff08;有時也指地理位置&#xff09;。 什么是二級域名多級域名…

陳萍的設計創新:Kevlin Nexus榮獲倫敦設計獎,展示品牌設計的國際化與持續創新

近日,陳萍憑借其創新設計作品 Kevlin Nexus,成功斬獲 2024 倫敦設計獎。該獎項旨在表彰全球范圍內的優秀設計作品,表彰設計界最完美、最前沿的成就。倫敦設計獎是全球最具權威性和影響力的設計獎項之一,其評選標準以高水準的專業性和嚴格性著稱。作為全球設計界的頂級榮譽,倫敦…

qml DirectionalBlur詳解

1、概述 DirectionalBlur是QML&#xff08;Qt Modeling Language&#xff09;中用于創建方向模糊效果的一種圖形效果類型。它通過對源圖像的像素進行模糊處理&#xff0c;產生一種源項目朝著模糊方向移動的感知印象。這種模糊效果被應用到每個像素的兩側&#xff0c;因此設置方…