Logback 快速入门

news2024/11/19 7:48:34

一、简介

Java 开源日志框架,以继承改善 log4j 为目的而生,是 log4j 创始人 Ceki Gülcü 的开源产品。
它声称有极佳的性能,占用空间更小,且提供其他日志系统缺失但很有用的特性。
其一大特色是,在 logback-classic 中本地(native)实现了 SLF4J API(也表示依赖 slf4j-api)

二、架构 / Logback知识

1. 项目分为三个模块:

  • logback-core:其他俩模块基础模块,作为通用模块 其中没有 logger 的概念
  • logback-classic:日志模块,完整实现了 SLF4J API
  • logback-access:配合Servlet容器,提供 http 访问日志功能

2. 在 logback 中主要概念(Logger、Appender、Layout、Encoder)

· Logger

日志记录器,logback-classic 的部分
每个 Logger 都附加到一个 LoggerContext 上,该 Context 负责构造 Logger 以及将其安排在层级结构中。

  1. 命名及层级关系
    Logger 名称区分大小写,并遵循层级命名规则。
    层级关系用 “.” 表示,如:“com.foo” 是 “com.foo.Bar” 的父Logger

    且所有 Logger 都可通过LoggerFactory#getlogger(String name)来获取,且相同名称返回的实例相同

  2. 根 Logger
    是所有层级结构的顶部Logger,可通过名称检索获取
    Logger rootLogger = LoggerFactory.getLogger(org.slf4j.Logger.ROOT_LOGGER_NAME);

  3. Level 与 继承关系
    Logger 可以被分配级别(TRACE、DEBUG、INFO、WARN、ERROR),可在 ch.qos.logback.classic 中查看
    若没有给 Logger 分配级别,则它将从最近的分配了等级的祖先处继承等级
    例:

    Logger nameAssigned levelEffective level
    rootDEBUGDEBUG
    XINFOINFO
    X.YnoneINFO
    X.Y.ZERRORERROR
  4. Level 与 log 规则(basic selection rule)
    Logger 只会启用等级 ≥ Logger等级的日志请求
    等级排序(严重程度,而非优先级):TRACE < DEBUG < INFO < WARN < ERROR
    如:

    Logger logger = LoggerFactory.getLogger("com.foo");
    logger.setLevel(Level. INFO);
    
    // 启用,因为 WARN >= INFO
    logger.warn("Low fuel level.");
    // 禁用, 因为 DEBUG < INFO. 
    logger.debug("Starting search for nearest gas station.");
    
· Appender

Logback 通过Appender#doAppend(E event)将日志事件打印到目的地(允许附加多个Appender即即多个目的地
目前官方已提供 console、文件、远程socket服务、JMS、远程UNIX Syslog进程、MySQL/PostgreSQL/Orcale等数据库的 appender 支持.
注意:

  • 一个 Logger 可以通过Logger#addAppender方法可被附加多个 appender
  • appender 同样适用于继承结构,且是以追加的方式而非覆盖;
    但继承行为可被 Logger 的 additivity 标志影响是否继承(通过Logger#setAdditive设置)
  • additivity 标志本身也是可继承的

继承示例如下:

Logger NameAttached AppendersAdditivity FlagOutput Targets
rootA1not applicableA1
xA-x1, A-x2trueA1, A-x1, A-x2
x.ynonetrueA1, A-x1, A-x2
x.y.zA-xyz1trueA1, A-x1, A-x2, A-xyz1
securityA-secfalseA-sec
security.accessnonetrueA-sec
· Layout

表示日志输出格式,其通过接口ch.qos.logback.core#doLayout(E event): String将日志事件格式化为String 返回。
Logback 官方提供 PatternLayout,允许以类似c语言printf来指定输出格式。
可通过将 layout 与 appender 关联,来实现自定义输出格式和目的地;但并不是每个 appender 都需要 layout,比如负责序列化的 SocketAppender 自然不需要 doLayout 转字符串

· Encoder

Encoder的概念在 Logback 0.9.19 中被引入,通过Encoder#encode可将 LoggingEvent 转为 byte[]
目前 Logback 仅提供了 PatternLayoutEncoder 这一个可用的实现,其逻辑很简单:内部构造/包装了 PatternLayout 实例,调用PatternLayout#doLayout得到格式化字串后,再调用String#getBytes返回 byte[]

引入原因:因为Layout#doLayout接口只能将 LoggingEvent 转为 String,这在某些情况下不太灵活,而现在 Encoder 能完全控制字节格式。
比如,在以前的版本中常在FileAppender中嵌套PatternLayout来使用,但现在仅需依赖Encoder

三、使用

1) 使用示例

  1. 引入依赖( logback-classic 背后会自动引入 logback-core、slf4j 等依赖包
<dependency>
  <groupId>ch.qos.logback</groupId>
  <artifactId>logback-classic</artifactId>
  <version>1.2.10</version>
</dependency>
  1. 基于slf4j用法,获取并使用Logger
public class Sample {
    private static final Logger LOGGER = LoggerFactory.getLogger(Sample.class);
    public static void main(String[] args){
        LOGGER.info("A Message From LOGGER:{}", "Hello World");
    }
}

输出:

16:55:54.091 [main] INFO logbacktest.Sample - A Message From LOGBACK:Hello World

2) logback 配置

Logback 初始化时,根据以下顺序尝试配置:

  1. 类路径下尝试寻找 logback-test.xml

  2. 若没有,类路径下尝试寻找 logback.groovy

  3. 若没有,类路径下尝试寻找 logback.xml

  4. 若没有,尝试基于 Java SPI 机制寻找 com.qos.logback.classic.spi.Configurator 接口的实现

  5. 若以上都没有,Logback 会使用最基本的 BasicConfigurator 配置自己。

    这将使用 TTLLLayout(类似 PatternLayout) 以"%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n"模式格式化日志,并将 ConsoleAppender 附加到 root Logger,这会输出到控制台,且 root 被指定为 DEBUG 等级。

    默认配置等效为以下xml配置:

    <configuration>
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
    <!-- encoders are assigned the type
    ch.qos.logback.classic.encoder.PatternLayoutEncoder by default -->
    <encoder>
    <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
    </encoder>
    </appender>
    
    <root level="debug">
    <appender-ref ref="STDOUT" />
    </root>
    </configuration>
    

可通过以下代码打印当前配置:StatusPrinter.print((LoggerContext) LoggerFactory.getILoggerFactory())
输出:

15:25:46,635 |-INFO in ch.qos.logback.classic.LoggerContext[default] - Could NOT find resource [logback-test.xml]
15:25:46,635 |-INFO in ch.qos.logback.classic.LoggerContext[default] - Could NOT find resource [logback.groovy]
15:25:46,636 |-INFO in ch.qos.logback.classic.LoggerContext[default] - Could NOT find resource [logback.xml]
15:25:46,647 |-INFO in ch.qos.logback.classic.BasicConfigurator@5f16132a - Setting up default configuration.

四、log 整体流程

  1. 获取 filter 链决策
  2. 应用 basic selection rule
  3. 创建 LoggingEvent 对象
  4. 调用 appenders
  5. 调 Layout 格式化
  6. 输出到目的地

五、配置详解

1.配置文件

一些写法/背景知识:
  1. 可在值中以 ${KEY:-defaultValue} 的形式引入属性,查找顺序如下:

    • 首先在本地作用域查找(详见<property>
    • 若没有,在上下文作用域查找
    • 若没有,在JVM系统属性查找
    • 最后,在系统环境变量中查找

    既支持名称(KEY)中的嵌套,也支持值/默认值的嵌套引用

  2. 预定义属性:

    • HOSTNAME:系统hostname,是配置时在 context 域中被定义被定义
    • CONTEXT_NAME:上下文名
  3. 一些标签涉及到类的标签(如<definer>、<appender>、<encoder>),其子标签除了规定之外,还可定义与 JavaBean 属性同名,这将调用相应setter注入。

顶级标签
属性:
  • debug:获知 Logback 内部状态,官方推荐在上设置

    debug
    

    属性,而非在代码中调用 StatusPrinter。例:

    <configuration debug="true">
    

    这也等同于设置状态监听器 OnConsoleStatusListener

    例:

    <configuration>
    	<statusListener class="ch.qos.logback.core.status.OnConsoleStatusListener" />  
    	<!-- … -->
    </configuration>
    
  • scan / scanPeriod:可定期扫描配置文件的更改,并在更改时自动应用。默认每分钟一次
    例:<configuration scan="true" scanPeriod="30 seconds" >

  • packagingData:可令每一行StackTrace输出其对应jar包。需注意,其计算成本高昂

    例:

    <configuration packagingData="true">
    

    输出示例:

    14:28:48.835 [btpool0-7] INFO  c.q.l.demo.prime.PrimeAction - 99 is not a valid value
    java.lang.Exception: 99 is invalid
    at ch.qos.logback.demo.prime.PrimeAction.execute(PrimeAction.java:28) [classes/:na]
    at org.apache.struts.action.RequestProcessor.processActionPerform(RequestProcessor.java:431) [struts-1.2.9.jar:1.2.9]
    at org.apache.struts.action.RequestProcessor.process(RequestProcessor.java:236) [struts-1.2.9.jar:1.2.9]
    at org.apache.struts.action.ActionServlet.doPost(ActionServlet.java:432) [struts-1.2.9.jar:1.2.9]
    
子标签
  • :从另外的文件引入配置,目标文件必须将其标签放入下。(SpringBoot基础配置便是如此,可参考spring-boot:2.4.0包中base.xml)
    • file
    • resource
    • url
    • optional:[true | false],默认若找不到目标文件,将输出警告;可设置为可选避免该行为
  • :设置上下文名称,可作为区分不同多个应用程序到同一target的区分,默认为"default"
    示例:
<contextName>myAppName</contextName>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
	<encoder>
	<pattern>%d %contextName [%t] %level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
  • :配置或引入外部属性,别名
    • name
    • value
    • file:引用文件系统的 .properties 文件
    • resource:引用类路径上的 variables.properties 文件
    • scope:
      • local:默认,会在配置解释时定义,并在结束后清除。
      • context:上下文作用域,属性将插入到LoggerContext中直到LoggerContext被清除,因此在所有日志事件中都可用。
      • system:系统作用域,属性将插入到JVM系统属性中
  • :代码动态定义属性,以 PropertyDefiner#getPropertyValue 方法返回其值
    • name
    • class:PropertyDefiner实现全类名
  • :自定义转换词
    • conversionWord:转换词
    • converterClass:ClassicConverter 实现类全类名
  • - *name:声明Appender名称 - *class:要使用的Appender的完全限定名 - 、子标签: - :0或1个 - class:若未声明,表示 PatternLayout - :0或多个 - class:若未声明,表示 PatternLayoutEncoder - :0或多个
  • :0或多个,配置 Logger
    • name(必须)
    • level:[TRACE | DEBUG | INFO | WARN | ERROR | ALL | OFF],或者 INHERITED / NULL 表示从上级继承。
    • additivity:[true | false]
    • 子标签:
      • :0或多个,表附加appender
        • ref:指定先前的名称
  • :配置根Logger
    • level:根Logger级别不能设置为 INHERITED / NULL
    • 子标签:

2.JVM系统属性

  • logback.configurationFile:指定配置文件位置,可以是 URL、类路径资源或外部文件系统。也可以在代码中设置(System#setProperty),但必须在创建任意Logger实例之前。
    文件扩展名必须是 .xml 或 .groovy
  • logback.statusListenerClass:可设置为希望注册的 StatusListener 名称

3. PatternLayout 的格式化消息编写规则

基础规则
  • 转换符 ‘%’ 可后跟规定的word,来获取数据字段,如 logger名称,日期,线程名等

  • 转换符可后接格式修饰符(Format modifiers)来指定每个字段最小、最大宽度 以及 是否左对齐
    示例:

    写法左对齐最小宽度最大宽度
    %20loggerfalse20none
    %.30loggerNAnone30
    %-20.30loggertrue2030
  • 括号"()" 可将部分内容分组,常配合格式修饰符使用
    如:
    %d{HH:mm:ss.SSS} [%thread]

    输出:
    13:09:30 [main] DEBUG c.q.logback.demo.ContextListener - Trying platform Mbean server
    13:09:30 [pool-1-thread-1] INFO  ch.qos.logback.demo.LoggingTask - Howdydy-diddly-ho - 0
    

    %-30(%d{HH:mm:ss.SSS} [%thread])
    image

  • 一些关键词可使用转义符\来输出

常用转换词
  • 日志事件的消息:m / msg / messages

  • 日志级别:p / le / level

  • Logger 名
    语法:

    c{length}
    lo{length}
    logger{length}
    

    含义/示例: length表示推荐最大长度,logback会根据该长度适当缩写Logger名,但始终会保证最右段不会缩写

    示例Logger原名称打印结果
    %loggermainPackage.sub.sample.BarmainPackage.sub.sample.Bar
    %loggermainPackage.sub.sample.BarBar
    %loggermainPackage.sub.sample.Barm.s.s.Bar
    %loggermainPackage.sub.sample.Barm.sub.sample.Bar
  • 上下文名:contextName

  • 日志事件的线程名: t / thread

  • 从"应用启动"到"产生该日志事件"经过的毫秒数:r / relative

  • 日期
    语法:

    d{pattern}
    date{pattern}
    d{pattern, timezone}
    date{pattern, timezone}
    

    含义/示例:输出日志事件的日期,其 pattern 语法与 java.text.SimpleDateFormat 格式兼容
    在没有 timezone 的情况下,使用 Java 平台默认时区

    示例结果
    %d2006-10-20 14:06:49,812
    %date14:06:49.812
    %date20 oct. 2006;14:06:49.812
    %date
  • 换行符:n

  • 异常:
    语法:

    ex{depth}
    exception{depth}
    throwable{depth}
    
    ex{depth, evaluator-1, ..., evaluator-n}
    exception{depth, evaluator-1, ..., evaluator-n}
    throwable{depth, evaluator-1, ..., evaluator-n}
    

    含义/示例:输出与日志事件关联的异常的stacktrace信息,默认输出完整的stacktrace
    image

  • 属性:property

  • 正则替换:replace§{r,t} 将消息模式p中的r替换为t
    如:%replace(%logger %msg){'\.', '/'}将所有".“替换为”/"

  • MDC(Mapped Diagnostic Contex)字段:%X{filedName}

  • 自定义转换词:

    1. 自定义实现 ClassicConverter,重写 convert(ILoggingEvent) 方法

      public class MySampleConverter extends ClassicConverter {
      	long start = System.nanoTime();
      
      	@Override
      	public String convert(ILoggingEvent event) {
      		long nowInNanos = System.nanoTime();
      		return Long.toString(nowInNanos-start);
      	}
      }
      
    2. 声明转换词

      <configuration>
      	<conversionRule conversionWord="nanos" converterClass="chapters.layouts.MySampleConverter" />
      	<!-- … -->
      </configuration>
      
性能考虑,应避免使用的转换词
  • 类名:C{length} / class
  • 文件名:F / file
  • 行号:L / line
  • 方法名:M / method
  • 包含jar包信息的异常stacktrace:xEx{depth} / xException{depth}…
    及root异常顺序反转的 rEx{depth} / rootException{depth}…

4. 着色(Coloring)

基于ANSI颜色代码,Logback可使用颜色关键词来为分组设置颜色。内置了:“%highlight”,“%black”, “%red”, “%green”,“%yellow”,“%blue”, “%magenta”,“%cyan”, “%white”, “%gray”, “%boldRed”,“%boldGreen”, “%boldYellow”, “%boldBlue”, “%boldMagenta”“%boldCyan”, “%boldWhite”
例:
<pattern>%d ${HOSTNAME} [%t] %highlight(%level) %logger{36} - %msg%n %ex{full}</pattern>

踩坑:官方说明使用 jansi 可以做到兼容不兼容ANSI的终端,但 Windows 下始终报错,即便引入了推荐版本 仍然报错

<dependency>
	<groupId>org.fusesource.jansi</groupId>
	<artifactId>jansi</artifactId>
	<version>1.17</version>
</dependency>

5.异步Appender

可通过声明AsyncAppender来执行异步日志打印,它只进行日志事件分发,因此必须引用另一个实际Appender才有意义

六、常见需求

  1. 根据环境变量动态附加Appender

    参考:

    How to select Logback appender based on property file or environment variable

    1. 使用 logback 官方支持的 语法,但同样需要引入 Janino library。参考:configuration

    2. 为 Appender 设置 ,并使用环境变量语法动态配置Filter的Level【有些曲线救国,应该仍有性能损耗

    3. 使用 SpringProfile

      例:

      application.yaml
      spring:
        profiles:
      	active:
      	  - ${ENV:classiclogs}
      
      logback.xml
      <configuration>
      	<!-- appender conf…-->
      	<springProfile name="jsonlogs">
      		<root level="info">
      			<appender-ref ref="stdout-json" />
      		</root>
      	</springProfile>
      	<springProfile name="classiclogs">
      		<root level="info">
      			<appender-ref ref="stdout-classic" />
      		</root>
      	</springProfile>
      </configuration>
      

七、踩坑

  1. 对于嵌套 .xml 配置文件,默认域"local"重复声明的话是无效的,不会覆盖。
    比如在我的logback.xml中声明,无法覆盖Spring自带的配置。

撰文参考:
http://logback.qos.ch/manual/index.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1955270.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

5G 基站特有的 5 个关键同步挑战

随着 5G 的推出和 O-RAN 联盟等举措&#xff0c;移动设备领域正在遭遇相当大的颠覆&#xff0c;这当然适用于基站和移动回程。 从手机到物联网设备&#xff0c;设备数量呈爆炸式增长&#xff0c;再加上移动视频流、工业物联网和汽车应用等新应用&#xff0c;给移动网络带来了容…

自学JavaScript(放假在家自学第一天)

目录 JavaScript介绍分为以下几点 1.1 JavaScript 是什么 1.2JavaScript书写位置 1.3 Javascript注释 1.4 Javascript结束符 1.5 Javascript输入输出语法 JavaScript(是什么?) 是一种运行在客户端(浏览器)的编程语言&#xff0c;实现人机交互效果。 2.作用(做什么?)网…

算法-插入排序

插入排序步骤 前面文章分享了两种排序算法&#xff1a;冒泡排序和选择排序。虽然它们的效率都是O(N2)&#xff0c;但其实选择排序比冒泡排序快一倍。现在来学第三种排序算法——插入排序。你会发现&#xff0c;顾及最坏情况以外的场景将是多么有用。 插入排序包括以下步骤。 …

从0开始搭建vue + flask 旅游景点数据分析系统(一):创建前端项目

根据前面的爬虫课程&#xff0c;我们重新开一个坑&#xff0c;就是基于爬取到的数据&#xff0c;搭建一个vueflask的前后端分离的数据分析系统 1 通过这个系列教程可以学习到什么&#xff1f; 从0开始搭建一个 vue flask 的数据分析系统&#xff1b;了解系统的整体架构&…

BSPTool工具

BSPTool工具 链接&#xff1a;https://pan.baidu.com/s/1UxMPjJtCHHkadFwnOfLqww?pwd1234 提取码&#xff1a;1234 1.使用方式 下载下来后&#xff0c;双击exe即可 2.MTK常用工具 2.1 MTK导出日志功能 2.2 导fulldump日志 2.3 .合并日志: 2.4 ADB指令集合 2.5 Fastboot指…

传统自然语言处理(NLP)与大规模语言模型(LLM)详解

自然语言处理&#xff08;NLP&#xff09;和大规模语言模型&#xff08;LLM&#xff09;是理解和生成人类语言的两种主要方法。本文将介绍传统NLP和LLM的介绍、运行步骤以及它们之间的比较&#xff0c;帮助新手了解这两个领域的基础知识。 传统自然语言处理&#xff08;NLP&…

指针!!C语言(第三篇)

目录 一. 二维数组传参的本质 二. 函数指针变量和函数指针数组 三. typedef关键字 四. 转移表 五. 回调函数以及qsort使用举例 一. 二维数组传参的本质 &#x1f35f;首先我们先回顾一下二维数组是怎样传参的&#xff1f;我们需要传入数组名以及行数和列数&#xff0c;这…

VS C++ Project(项目)的工作目录设置

如果只是简单创建一个VS CProject或者MFC Project&#xff0c;可能很多时候&#xff0c;只关心将Project放在硬盘的那个位置&#xff0c;与Project目录相关的的其他问题&#xff0c;并不引人注意&#xff0c;我们也不是十分在意。有时我们不得不进行工作目录方面的设置&#xf…

Javascript前端面试(七)

JavaScript 部分 1. JavaScript 有哪些数据类型&#xff0c;它们的区别&#xff1f; JavaScript 共有八种数据类型&#xff0c;分别是 Undefined、Null、Boolean、 Number、String、Object、Symbol、BigInt。 其中 Symbol 和 BigInt 是 ES6 中新增的数据类型&#xff1a; ●Sym…

卷积神经网络(一)---原理和结构

在介绍卷积神经网络之前&#xff0c;先提出三个观点&#xff0c;正是这三个观点使得卷积神经网络能够真正起作用。 1. 局部性 对于一张图片而言&#xff0c;需要检测图片中的特征来决定图片的类别&#xff0c;通常情况下这些特征都不是由整张图片决定的&#xff0c;而是由一些…

暑期集训周报三

题解 SMU Summer 2024 Contest Round 8-CSDN博客 CSDN 积累题目 dp 1.花店橱窗 思路&#xff1a;用dp[i][j]表示第i个放在j位置上的最大价值&#xff0c;那么我们可以枚举i-1被放在了区间[i-1,j-1]的那个位置&#xff0c;找到最大价值部分&#xff0c;然后更新dp[i][j]&a…

gitee的远程连接与公钥SSH的连接

目录 1. 登录注册gitee1.1 登录注册1.2 创建仓库 2. 远程连接3. 公钥连接4. 参考链接 1. 登录注册gitee 1.1 登录注册 gitee官网 进入后进行登录注册 1.2 创建仓库 2. 远程连接 在你想要上传文件的文件夹中进行git初始化&#xff08;我在其他文章已经写过&#xff0c;链接…

FastAPI(七十六)实战开发《在线课程学习系统》接口开发-- 课程详情

源码见&#xff1a;"fastapi_study_road-learning_system_online_courses: fastapi框架实战之--在线课程学习系统" 这个接口用户可以不登录&#xff0c;因为我们的课程随意浏览 那么我们梳理下这里的逻辑 1.根据课程id判断课程是否存在 2.课程需要返回课程的详情 3…

X-AnyLabeling标注软件使用方法

第一步 下载 官方X-AnyLabeling下载地址 github&#xff1a;X-AnyLabeling 第二步 配置环境 使用conda创建新的虚拟环境 conda create -n xanylabel python3.8进入环境 conda activate xanylabel进入X-AnyLabeling文件夹内&#xff0c;运行下面内容 依赖文件系统环境运行环…

【把玩数据结构】详解队列

目录 队列介绍队列概念队列的结构生活中的队列 队列的实现队列的初始化队列的销毁队尾入队列队头出队列获得队头元素获得队尾元素统计队列元素个数 队列介绍 队列概念 队列&#xff1a;只允许在一端进行插入数据操作&#xff0c;在另一端进行删除数据操作的特殊线性表。队列遵…

Python 代码中的 yield 到底是什么鬼?

在Python编程中&#xff0c;有一个强大而神秘的关键字&#xff0c;那就是yield。初学者常常被它搞得晕头转向&#xff0c;而高级开发者则借助它实现高效的代码。到底yield是什么&#xff1f;它又是如何在Python代码中发挥作用的呢&#xff1f;让我们一起来揭开它的面纱。 Pyth…

Chrome浏览器设置暗黑模式 - 护眼模式 - 亮度调节 - DarkReader - 地址栏和书签栏设置为黑色背景

效果图 全黑 浅灰 &#xff08;DarkReader设置开启亮色亮度-25&#xff09; 全白 前言 主要分两部分需要操作&#xff0c; 1&#xff09;地址栏和书签栏 》 需要修改浏览器的外观模式 2&#xff09;页面主体 》 需要安装darkreader插件进行设置 步骤 1&#xff09;地址栏和…

Unity UGUI 实战学习笔记(3)

仅作学习&#xff0c;不做任何商业用途 不是源码&#xff0c;不是源码! 是我通过"照虎画猫"写的&#xff0c;可能有些小修改 不提供素材&#xff0c;所以应该不算是盗版资源&#xff0c;侵权删 拼UI 提示面板的逻辑 using System.Collections; using System.Col…

加密溢出问题

今天编写程序&#xff0c;使用一个非常简单的对256取模的运算&#xff0c;但是总是得不到正确的结果。 后来发现&#xff0c;是数据的值的范围问题。 例如&#xff0c;处理图像时&#xff0c;值的范围是【0,255】. 异或等等运算都是没有问题的。 但是&#xff0c;如果进行加法…

websocket通信问题排查思路

websocket通信问题排查思路 一、websocket连接成功&#xff0c;但数据完全推不过来。 通过抓包发现&#xff0c;是回包时间太长超过了1分钟导致的。这种通常是推送数据的线程有问题导致的。 正常抓包的情况如下&#xff1a; 二、大量数据可以正常推送成功&#xff0c;不定时…