当前位置:

首页 > 编程开发 > Java项目中错误日志如何打印

Java项目中错误日志如何打印

1.上层系统引入的非法参数。对于非法参数引入的错误,可以通过参数校验和前置条件校验来截获错误;2.与下层系统交互产生的错误。与下层交互产生的错误,有两种:a.下层系统处理成功了,但是通信出错了,这样会导致子系统之间的数据不一致;对于这种情况,可以采用超时补偿机制,预先将任务记录下来,通过定时任务在后续将数据订正过来。有什么更好的设计方案,也可以留言。b.通信成功了,但是下层处理出错了。对于这种情况,需要与下层开发人员沟通,协调子系统之间的交互;需要根据下层返回的错误码和错误描述做适当的处理或给予合理的提示

1.上层系统引入的非法参数。对于非法参数引入的错误, 可以通过参数校验和前置条件校验来截获错误;

2.与下层系统交互产生的错误。与下层交互产生的错误, 有两种:

a.下层系统处理成功了,但是通信出错了, 这样会导致子系统之间的数据不一致;

对于这种情况,可以采用超时补偿机制,预先将任务记录下来,通过定时任务在后续将数据订正过来。

有什么更好的设计方案,也可以留言。

b.通信成功了,但是下层处理出错了。

对于这种情况, 需要与下层开发人员沟通, 协调子系统之间的交互;

需要根据下层返回的错误码和错误描述做适当的处理或给予合理的提示信息。

无论哪一种情况, 都要假设下层系统可靠性一般, 做好出错的设计考虑。

3.本层系统处理出错。

本层系统产生错误的原因:

原因一:疏忽导致。疏忽是指程序员能力完全可避免此类错误但实际上没做到。比如将 && 敲成了 & , == 敲成了 = ;边界错误, 复合逻辑判断错误等。疏忽要么是程序员注意力不够集中, 比如处于疲倦状态、加班通宵、边开会边写程序;要么是急着实现功能,没有顾及程序的健壮性等。

改进措施:使用代码静态分析工具,通过单元测试行覆盖可有效避免此类问题。

原因二:错误与异常处理不够周全导致的。比如输入问题。计算两个数相加, 不仅要考虑计算溢出问题, 还要考虑输入非法的情形。对于前者,可能通过了解、犯错或经验就可以避免,而对于后者,则必须加以限定,以使之处于我们的智商能够控制的范围内,比如使用正则表达式过滤掉不合法的输入。对于正则表达式必须进行测试。对于不合法输入, 要给出尽可能详细、易懂、友好的提示信息、原因及建议方案。

改进措施:尽可能周全地考虑各种错误情形和异常处理。在实现主流程之后,增加一个步骤:仔细推敲可能的各种错误和异常,返回合理错误码和错误描述。每个接口或模块都有效处理好自己的错误和异常,可有效避免因场景交互复杂导致的bug。

譬如,一个业务用例由场景A.B.C交互完成。实际执行A.B成功了,C失败了,这时B需要根据C返回合理的代码和消息进行回滚并返回给A合理的代码和消息,A根据B的返回进行回滚,并返回给客户端合理的代码和消息。这是一种分段回滚的机制,要求每个场景都必须考虑异常情况下的回滚。

原因三:逻辑耦合紧密导致。 由于业务逻辑耦合紧密, 随着软件产品一步步发展, 各种逻辑关系错综复杂, 难以看到全局状况,导致局部修改影响波及到全局范围,造成不可预知的问题。

改进措施:编写短函数和短方法,每个函数或方法最好不超过 50 行。 编写无状态函数和方法, 只读全局状态, 相同的前提条件总是会输出相同的结果, 不会依赖外部状态而变更自己的行为;定义合理的结构、 接口和逻辑段, 使接口之间的交互尽可能正交、低耦合;对于服务层, 尽可能提供简单、正交的接口;持续重构, 保持应用模块化和松耦合, 理清逻辑依赖关系。

对于有大量业务接口相互影响的情况, 必须整理各个业务接口的逻辑流程及相互依赖关系, 从整体上进行优化;对于有大量状态的实体, 也需要梳理相关的业务接口, 整理状态之间的转换关系。

原因四:算法不正确导致。

改进措施:首先将算法从应用中分离出来。 若算法有多种实现, 可以通过交叉校验的单元测试找出来, 比如排序操作;如果算法具有可逆性质, 可以通过可逆校验的单元测试找出来, 比如加密解密操作。

原因五:相同类型的参数,传入顺序错误导致。比如,modifyFlow(int rx, int tx), 实际调用为 modifyFlow(tx,rx)

改进措施:尽可能使类型具体化。该用浮点数就用浮点数, 该用字符串就用字符串, 该用具体对象类型就用具体对象类型;相同类型的参数尽可能错开;如果上述都无法满足, 就必须通过接口测试来验证, 接口参数值务必是不同的。

原因六:空指针异常。空指针异常通常是对象没有正确初始化, 或者使用对象之前没有对对象是否非空做检测。

改进措施:对于配置对象, 检测其是否成功初始化;对于普通对象, 获取到实体对象使用之前, 检测是否非空。

原因七:网络通信错误。网络通信错误通常是因为网络延迟、阻塞或不通导致的错误。网络通信错误通常是小概率事件, 但小概率事件很可能会导致大面积的故障、 难以复现的BUG。

改进措施:在前一个子系统的结束点和后一个子系统的入口点分别打 INFO 日志。通过两者的时间差提供一点线索。

原因八:事务与并发错误。事务与并发结合在一起, 很容易产生非常难以定位的错误。

改进措施:对于程序中的并发操作, 涉及到共享变量及重要状态修改的, 要加 INFO 日志。

如果有更有效的做法,欢迎留言指出。

原因九:配置错误。

改进措施:在启动应用或启动相应配置时, 检测所有的配置项, 打印相应的INFO日志, 确保所有配置都加载成功。

原因十:业务不熟悉导致的错误。在中大型系统, 部分业务逻辑和业务交互都比较复杂, 整个的业务逻辑可能存在于多个开发同学的大脑里, 每个人的认识都不是完整的。这很容易导致业务编码错误。

改进措施:通过多人讨论和沟通, 设计正确的业务用例, 根据业务用例来编写和实现业务逻辑;最终的业务逻辑和业务用例必须完整存档;在业务接口中注明该业务的前置条件、处理逻辑、后置校验和注意事项;当业务变化时, 需要同步更新业务注释;代码REVIEW。业务注释是业务接口的重要文档, 对业务理解起着重要的缓存作用。

原因十一:设计问题导致的错误。比如同步串行方式会有性能、响应慢的问题, 而并发异步方式可以解决性能、响应慢的问题, 但会带来安全、正确性的隐患。异步方式会导致编程模型的改变, 新增异步消息推送和接收等新的问题。使用缓存能够提高性能, 但是又会存在缓存更新的问题。

改进措施:编写和仔细评审设计文档。设计文档必须阐述背景、需求、所满足的业务目标、要达到的业务性能指标、可能的影响、设计总体思路、详细方案、预见该方案的优缺点及可能的影响;通过测试和验收, 确保改设计方案确实满足业务目标和业务性能指标。

原因十二:未知细节问题导致的错误。比如缓冲区溢出、 SQL 注入攻击。从功能上看是没有问题的, 但是从恶意使用上看, 是存在漏洞的。再比如, 选择 jackson 库做 JSON 字符串解析, 默认情况下, 当对象新增字段时会导致解析出错。必须在对象上加 @JsonIgnoreProperties(ignoreUnknown = true) 注解才能正确应对变化。如果选用其他 JSON 库就不一定有这个问题。

改进措施:一方面要通过经验积累, 另一方面, 考虑安全问题和例外情况, 选择成熟的经过严格测试的库。

原因十三:随时间变化而出现的bug。有些解决方案在过去看来是很不错的,但在当前或者未来的情景中可能变得笨拙甚至不中用,也是常见的事情。比如像加密解密算法, 在过去可能认为是完善的, 在破解之后就要慎重使用了。

改进措施:关注变化以及漏洞修复消息,及时修正过时的代码、库、行为。

原因十四:硬件相关的错误。比如内存泄露, 存储空间不足, OutOfMemoryError 等。

改进措施:增加对应用系统的 CPU / 内存 / 网络等重要指标的性能监控。

系统出现的常见错误:

  •  实体在数据库中的记录不存在, 必须指明是哪个实体或实体标识;

  •  实体配置不正确, 必须指明是哪个配置有问题,正确的配置应该是什么;

  •  实体资源不满足条件, 必须指明当前资源是什么,资源要求是什么;

  •  实体操作前置条件不满足, 必须指明需要满足什么前置条件,当前的状态是什么;

  •  实体操作后置校验不满足, 必须指明需要满足什么后置校验, 当前的状态是什么;

  •  性能问题导致超时, 必须指明是什么导致的性能问题,后续如何优化;

  •  多个子系统交互通信出错导致之间的状态或数据不一致?

一般难以定位的错误会出现在比较底层的地方。因为底层无法预知具体的业务场景, 给出的错误消息都是比较通用的。

这就要求在业务上层提供尽可能丰富的线索。错误的产生一定是多个系统或层次交互的过程中在某一层栈上不满足前置条件导致。在编程时, 在每一层栈中尽可能确保所有必须的前置条件满足,尽可能避免错误的参数传递到底层, 尽可能地将错误截获在业务层。

大多数错误都是由多种原因组合产生。但每一种错误必定有其原因。在解决错误之后, 要深入分析错误是如何发生的, 如何避免这些错误再次发生。努力就能成功, 但是:反思才能进步 !推荐:Java优雅的记录日志:log4j实战篇

如何编写更容易排查问题的错误日志

打错误日志的基本原则: 

  1. 尽可能完整。每一条错误日志都完整描述了:什么场景下发生了什么错误, 什么原因(或者哪些可能原因), 如何解决(或解决提示);

  2. 尽可能具体。比如 NC 资源不足, 究竟具体指什么资源不足, 是否可以通过程序直接指明;通用错误,比如 VM NOT EXIST , 要指明在什么场景下发生的,可能便于后续统计的工作。

  3. 尽可能直接。最理想的错误日志应该让人在第一直觉下能够知道是什么原因导致,该怎么去解决,而不是还要通过若干步骤去查找真正的原因。

  4. 将已有经验集成直接到系统中。所有已经解决过的问题及经验都要尽可能以友好的方式集成到系统中,给新进人员更好的提示,而不是埋藏在其他地方。

  5. 排版要整洁有序, 格式统一化规范化。密密麻麻、随笔式的日志看着就揪心, 相当不友好, 也不便于排查问题。

  6. 采用多个关键字唯一标识请求,突出显示关键字:时间、实体标识(比如vmname)、操作名称。

排查问题的基本步骤:

登录到应用服务器 -> 打开日志文件 -> 定位到错误日志位置 -> 根据错误日志的线索的指导去排查、确认问题和解决问题。

其中:

  1.  从登陆到打开日志文件。由于应用服务器有多台, 要逐一登录上去查看实在不方便。需要编写一个工具放在 AG 上直接在 AG 上查看所有服务器日志, 甚至直接筛选出所需要的错误日志。

  2.  定位错误日志位置。目前日志的排版密密麻麻,不易定位到错误日志。一般可以先采用"时间"来定位到错误日志的附近前面的地方, 然后使用 实体关键字 / 操作名称 组合来锁定错误日志地方。根据 requestId 定位错误日志虽然比较符合传统,但是要先找到 requestId , 并且不具有描述性。最好能直接根据时间/内容关键字来定位错误日志位置。

    3.  分析错误日志。错误日志的内容最好能够更加直接明了, 能够明确指明与当前要排查的问题特征是吻合的, 并且给出重要线索。

通常, 程序错误日志的问题就是日志内容是针对当前代码情境才能理解,看上去简洁, 但总是写的不全, 半英文格式;一旦离开代码情境, 就很难知道究竟说的是什么, 非要让人思考一下或者去看看代码才能明白日志说的是什么含义。这不是自己给自己罪受?拓展:细说 Java 主流日志工具库

比如:

if ((storageType == StorageType.dfs1 || storageType == StorageType.dfs2)                  && (zone.hasStorageType(StorageType.io3) || zone.hasStorageType(StorageType.io4))) {  // 进入dfs1 和dfs2 在io3 io4 存储。  } else {        log.info("zone storage type not support, zone: " + zone.getZoneId() + ", storageType: "  + storageType.name());        throw new BizException(DeviceErrorCode.ZONE_STORAGE_TYPE_NOT_SUPPORT);  }

zone 要支持什么 storage type 才是正确的? Do Not Let Me Think !

错误日志应该做到:即使离开代码情境,也能清晰地描述发生了什么。

此外,如果能够直接在错误日志中说明清楚原因, 在做巡检日志的时候也可以省些力气。

从某种意义上来说, 错误日志也可以是一种非常有益的文档,记录着各种不合法的运行用例。

目前程序错误日志的内容可能存在如下问题:

1.错误日志没有指明错误参数和内容:

catch(Exception ex){        log.error("control ip insert failed", ex);        return new ResultSet(  ControlIpErrorCode.ERROR_CONTROL_IP_INSERT_FAILURE);  }

没有指明插入失败的 control ip. 如果加上 control ip 关键字, 更容易搜索和锁定错误。

类似的还有:

log.error("Get some errors when insert subnet and its IPs into database. Add subnet or IP failure.", e);

没有指明是哪个 subnet 的它下属的哪些 IP. 值得注意的是, 要指明这些要额外做一些事情, 可能会稍微影响性能。这时候需要权衡性能和可调试性。

解决方案:使用 String.format("Some msg to ErrorObj: %s", errobj) 方法指明错误参数及内容。

这通常要求对 DO 对象编写可读的 toString 方法。

2.错误场景不明确:

log.error("nc has exist, nc ip" + request.getIp());

在 createNc 中检测到 NC 已经存在报错。但是日志上没有指明错误场景, 让人猜测,为什么会报NC已存在错误。

可以改为

log.error("nc has exist when want to create nc, please check nc parameters. Given nc ip: " + request.getIp());  log.error("[create nc] nc has exist, please check nc parameters. Given nc ip: " + request.getIp());

类似的还有:

log.error("not all vm destroyed, nc id " + request.getNcId());

改成

log.error("[delete nc] some vms [%s] in the nc are not destroyed. nc id: %s", vmNames, request.getNcId());

解决方案:错误消息加上 when 字句, 或者错误消息前加上 【接口名】, 指明错误场景,直接从错误日志就知道明白了。

一般能够知道 executor 的可以加上 【接口名】, service 加上 when 字句。

3.内容不明确, 或不明其义:

if(aliMonitorReporter == null) {          log.error("aliMonitorReporter is null!");  } else {         aliMonitorReporter.attach(new ThreadPoolMonitor(namePrefix, asynTaskThreadPool.getThreadPoolExecutor()));  }

改为:

log.error("aliMonitorReporter is null, probably not initialized properly, please check configuration in file xxx.");

类似的还有:

if (diskWbps == null && diskRbps == null && diskWiops == null    && diskRiops == null) {        log.error("none of attribute is specified for modifying");        throw new BizException(DeviceErrorCode.NO_ATTRIBUTE_FOR_MODIFY);  }

改为

log.error("[modify disk attribute] None of [diskWbps,diskRbps,diskWiops,diskRiops] is specified for disk id:" + diskId);

解决方案:更清晰贴切地描述错误内容。

4.排查问题的引导内容不明确:

log.error("get gw group ip segment failed. zkPath: " + LockResource.getGwGroupIpSegmnetLockPath(request.getGwGroupId()));

zkPath ? 如何去排查这个问题?我该去找谁?到哪里去查找更具体的线索?

解决方案:加上相应的背景知识和引导排查措施。

5.错误内容不够具体细致:

if (!ncResourceService.isNcResourceEnough(ncResourceDO,    vmResourceCondition)) {        log.error("disk space is not enough at vm's nc, nc id:" + vmDO.getNcId());        throw new BizException(ResourceErrorCode.ERROR_RESOURCE_NOT_ENOUGH);  }

究竟是什么资源不够?目前剩余多少?现在需要多少?值得注意的是, 要指明这些要额外做一些事情, 可能会稍微影响性能。这时候需要权衡性能和可调试性。

解决方案:通过改进程序或程序技巧, 尽可能揭示出具体的差异所在, 减少人工比对的操作。

6.半英文句式读起来不够清晰明白,需要思考来拼凑起完整的意思:

log.warn("cache status conflict, device id "+deviceDO.getId()+" db status "+deviceDO.getStatus() +", nc status "+ status);

改为:

log.warn(String.format("[query cache status] device cache status conflicts between regiondb and nc, status of device '%s' in regiondb is %s , but is %s in nc.", deviceDO.getId(), deviceDO.getStatus(), status));

解决方案:改为自然可读的英文句式。

总结起来, 错误日志格式可以为:

log.error("[接口名或操作名] [Some Error Msg] happens. [params] [Probably Because]. [Probably need to do].");  log.error(String.format("[接口名或操作名] [Some Error Msg] happens. [%s]. [Probably Because]. [Probably need to do].", params));

log.error("[Some Error Msg] happens to 错误参数或内容 when [in some condition]. [Probably Because]. [Probably need to do].");  log.error(String.format("[Some Error Msg] happens to %s when [in some condition]. [Probably Because]. [Probably need to do].", parameters));

[Probably Reason]. [Probably need to do]. 在某些情况下可以省略;在一些重要接口和场景下最好能说明一下。

每一条错误日志都是独立的,尽可能完整、具体、直接说明何种场景下发生了什么错误,由什么原因导致,要采用什么措施或步骤。

问题:

1.String.format 的性能会影响打日志吗?

一般来说, 错误日志应该是比较少的, 使用 String.format 的频度并不会太高,不会对应用和日志造成影响。

2.开发时间非常紧张时, 有时间去斟酌字句吗?

建立一个标准化的内容格式,将内容往格式套,可以节省斟酌字句的时间。

3.什么时候使用 info, warn , error ?

  •  info 用于打印程序应该出现的正常状态信息, 便于追踪定位;

  •  warn 表明系统出现轻微的不合理但不影响运行和使用;

  •  error 表明出现了系统错误和异常,无法正常完成目标操作。

错误日志是排查问题的重要手段之一。当我们编程实现一项功能时, 通常会考虑可能发生的各种错误及相应原因:

要排查出相应的原因, 就需要一些关键描述来定位原因。

这就会形成三元组:

错误现象 -> 错误关键描述 -> 最终的错误原因。

需要针对每一种错误尽可能提供相应的错误关键描述,从而定位到相应的错误原因。

本文内容来源于互联网,如有侵权请联系删除。
作者最新文章
编程开发
相关文章 更多
谷歌浏览器Mac版入口
谷歌浏览器Mac版入口

谷歌浏览器Mac版官方安装指南 谷歌浏览器Mac版官方安装入口是https://www.google.com/chrome/,需macOS 12+系统、500MB空间,下载.dmg后拖入应用程序安装,支持多设备同步、性能优化与隐私保护功能。 苹果电脑Chrome的安装入口究竟在哪里?这个问题最近可是

Chrome浏览器JS脚本不运行怎么办
Chrome浏览器JS脚本不运行怎么办

Chrome中JavaScript未执行需依次检查:一、移除站点级禁用并添加允许域名;二、开启全局JavaScript开关;三、禁用干扰扩展;四、在开发者工具中启用JavaScript;五、重置内容设置为默认。 有时在Chrome里打开网页,会发现交互按钮点了没反应,数据加载不出来,页面仿佛“静止”

IE浏览器怀旧版在线网址
IE浏览器怀旧版在线网址

IE浏览器怀旧版在线网址:一次精准的技术时光回溯 最近,不少老用户和怀旧爱好者在反复搜索一个问题:那个经典的Internet Explorer,如今还能在哪里原汁原味地体验到?答案指向一个特定的地址:https://ie.microsoft.com/legacy/。 这个网站远不止是一个简单的“皮肤

火狐浏览器有哪些设置功能
火狐浏览器有哪些设置功能

火狐浏览器五大核心设置功能:解锁高效、安全与个性化体验 火狐浏览器功能强大,但如果不仔细挖掘,很多能大幅提升效率和安全性的设置可能就“藏着掖着”了。这就好比拥有一台高性能设备,却只用了基础模式。那么,如何把它调整到最顺手、最安全的状态?接下来,我们就聚焦于当前版本(截至2025年末)最关键的五大设置

chrome搜索免验证入口
chrome搜索免验证入口

Chrome官方免验证入口为https://www.google.cn/chrome/,提供全平台安装包、免登录即用、本地化安全机制及引擎级性能优化。 到底该去哪里找正版、免费且无需繁琐验证的Chrome浏览器入口?这个问题困扰了不少网友。今天,我们就来直通核心,为大家详细拆解Chrome引擎的官方

java heap space 选型思路:使用场景与区别整理
java heap space 选型思路:使用场景与区别整理

Java堆是JVM存储对象的核心内存区域,配置需结合场景:单体应用适中设置;大数据处理需大堆并关注GC停顿;微服务强调快速启动;高并发需精细划分堆区域。关键参数-Xms和-Xmx建议等值以稳定性能。垃圾回收器选择影响效率,如G1适用于大堆,ZGC可实现低停顿。内存错误时需监控堆状态。

java heap space 使用中遇到的问题怎么解决
java heap space 使用中遇到的问题怎么解决

Java堆内存溢出错误通常因内存泄漏、数据处理需求过大或JVM参数配置不当引起。排查时可借助jmap、堆转储及MAT等工具定位问题。解决方案包括调整JVM内存参数(如-Xmx)、修复代码中的内存泄漏、优化大数据处理逻辑,并建立持续监控与预防机制,以保障应用稳定运行。

java xml 选型思路:使用场景与区别整理
java xml 选型思路:使用场景与区别整理

XML在Java开发中用于配置、数据交换等场景。解析方式主要有DOM、SAX、StAX及第三方库。DOM适合操作小文件,SAX/StAX适合处理大文件流,JAXB用于对象与XML映射。选型需结合数据大小、内存、性能及团队熟悉度,现代框架常封装底层解析。

using namespace 使用中遇到的问题怎么解决
using namespace 使用中遇到的问题怎么解决

命名空间的基本概念与常见引入问题在C++等编程语言中,命名空间(namespace)是一种将代码标识符(如变量、函数、类名)封装在特定名称下的机制,其主要目的是避免命名冲突,尤其是在大型项目或使用多个第三方库时。使用“using namespace”指令可以将指定命名空间中的所有名称引入当前作用域,

c语言函数递归 实操经验总结:这些技巧很实用
c语言函数递归 实操经验总结:这些技巧很实用

理解递归的基本原理在C语言中,递归是一种函数调用自身的编程技术。要掌握它,首先需要理解其核心思想:将一个复杂的大问题,分解为一个或几个与原问题相似但规模更小的子问题,直到子问题足够简单,可以直接求解。这个过程通常包含两个关键部分:递归出口和递归体。递归出口定义了问题何时不再继续分解,即最简单、可直接

查看更多
精品专题 更多
装机必备
装机必备

正软商城装机必备专区,精选办公、浏览器、安全防护、影音播放、压缩解压、设计创作和系统工具等电脑常用正版软件,帮助用户快速完成新电脑软件配置。

Windows
Windows

正软商城Windows软件专区,汇集适用于Windows电脑的办公、设计、安全防护、影音播放、开发工具和系统优化软件,提供软件介绍、系统要求、正版授权及购买下载服务。

macOS软件
macOS软件

正软商城macOS软件专区,精选适用于Mac电脑的办公、设计、影音、效率、开发和系统工具,提供软件功能介绍、macOS兼容版本、正版授权及购买下载服务。

Mac软件 更多
灵活计算器
灵活计算器
macOS/iOS/Android

灵活计算器是一款笔记式算数应用,支持实时计算、动态关联和云端同步功能。记录、整理和输出之间的过渡会更自然,适合长期写作、做笔记或持续沉淀个人内容。

赤友清理大师
赤友清理大师
macOS

赤友清理大师是一款为 Mac 设计的智能清理优化工具,可精准扫描垃圾、大文件、重复文件等,释放磁盘空间。做扫描整理、文字提取和表格转换时,它能把识别后的处理步骤接得更顺,资料录入这类场景会省下不少时间。

极度公式
极度公式
Windows/macOS/Linux

极度公式是一款跨平台专业LaTeX公式识别编辑软件,支持OCR公式识别和多平台编辑。和使用说明,避免使用,享受完整功能与稳定支持。做扫描整理、文字提取和表格转换时,它能把识别后的处理步骤接得更顺,资料录入这类场景会省下不少时间。

WINDOWS 更多
Windows 10
Windows 10
Windows

Windows 10 是一款微软推出的经典操作系统,拥有硬件兼容性与多任务处理能力。它更偏向把系统状态查看和常用调节动作放在一起,适合需要持续观察和微调设备状态的场景。

极度公式
极度公式
Windows/macOS/Linux

极度公式是一款跨平台专业LaTeX公式识别编辑软件,支持OCR公式识别和多平台编辑。和使用说明,避免使用,享受完整功能与稳定支持。做扫描整理、文字提取和表格转换时,它能把识别后的处理步骤接得更顺,资料录入这类场景会省下不少时间。

密码键盘
密码键盘
Windows/macOS/iOS/Android

密码键盘是一款兼具安全性与便捷性的高效密码管理器。日常使用里的持续防护和信息管理会更突出,适合把安全控制放进长期使用流程中的场景。