目录

  • List删除满足条件的元素,并且避免索引错误或并发修改异常常用方法
    • 使用迭代器删除元素
    • 通过逆向循环删除元素
    • Java8+ 的 removeIf()方法
  • 获取不到日志内容问题排查
    • 尝试解决
    • 最终解决

List删除满足条件的元素,并且避免索引错误或并发修改异常常用方法

使用迭代器删除元素

List list = new ArrayList<>();
list.add("A");
list.add("B");
list.add("C");
Iterator iterator = list.iterator();
while (iterator.hasNext()) {String element = iterator.next();if (element.equals("B")) {iterator.remove(); // 使用迭代器的 remove() 方法删除元素}
}

通过逆向循环删除元素

List list = new ArrayList<>();
list.add("A");
list.add("B");
list.add("C");
for (int i = list.size() - 1; i >= 0; i--) {if (list.get(i).equals("B")) {list.remove(i); // 通过逆向循环删除元素}
}

Java8+ 的 removeIf()方法

  • 简单的,使用 Lambda 表达式
List list = new ArrayList<>();
list.add("A");
list.add("B");
list.add("C");
list.removeIf(element -> element.equals("B"));
  • 复杂的,通过使用匿名类实现 Predicate 接口的方式
List<String> list = new ArrayList<>();
list.add("A");
list.add("B");
list.add("C");
list.add("D");
// 假如 List<Test> tests,则为new Predicate<Test>()
list.removeIf(new Predicate<String>() {@Overridepublic boolean test(String element) {// 在这里编写复杂的逻辑来判断是否删除元素// 返回 true 表示删除该元素,返回 false 表示保留该元素return element.startsWith("A") || element.endsWith("D");}
});

获取不到日志内容问题排查

通过FileUtils.readFileToString()读取日志文件获取文件内容,日志文件有数据,但是获取不到内容,可能原因:

  • 文件路径问题:请确保logFile参数指定的文件路径是正确的路径,并且可以访问该文件。您可以在代码中添加一些调试语句,输出logFile的路径,然后验证该路径是否正确。
  • 文件读取权限:确保您正在以足够的权限运行代码,以便能够读取指定的日志文件。如果您在一个受限制的环境中运行代码,则可能需要提升您的权限或更改文件的权限设置。
  • 文件内容编码问题:如果日志文件使用了特定的编码方式进行编码(例如UTF-8),请确保在使用FileUtils.readFileToString()时指定正确的编码方式。可以尝试使用FileUtils.readFileToString(logFile, “UTF-8”)来明确指定编码方式。
  • 文件访问冲突:如果您的代码与其他程序同时访问该日志文件,可能发生访问冲突或文件锁定导致无法读取文件内容。在调试期间,确保没有其他程序或进程锁定或占用了该日志文件。

尝试解决

  • 改成异步方法:日志依然读取不到,但是作业状态确实是完结状态,日志应该已经写完了(springboot异步方法配置:启动类添加@EnableAsync,异步方法上添加@Async注解
  • 判断文件写入完成后再进行读取操作:依然无效,而且假如中间有几秒没有写入日志,但是写文件并没有结束,容易误判结束
import java.io.File;public class LogFileReader {private static final int CHECK_INTERVAL_MS = 1000; // 检查间隔时间,单位为毫秒public static void main(String[] args) {String filePath = "path/to/logfile.txt"; // 替换为您的日志文件路径File logFile = new File(filePath);// 获取初始文件长度和修改时间long initialSize = logFile.length();long lastModified = logFile.lastModified();while (true) {// 等待指定时间间隔try {Thread.sleep(CHECK_INTERVAL_MS);} catch (InterruptedException e) {e.printStackTrace();}// 检查文件是否发生变化long currentSize = logFile.length();long currentModified = logFile.lastModified();if (currentSize == initialSize && currentModified == lastModified) {// 文件大小和修改时间未发生变化,文件写入完成String logContent = FileUtils.readFileToString(logFile, "UTF-8");System.out.println(logContent);break; // 退出循环}// 更新初始文件长度和修改时间initialSize = currentSize;lastModified = currentModified;}}}
  • 替换FileUtils.readFileToString(),通过BufferedReader读取日志:BufferedReader 和 FileReader 来逐行读取文件的内容。通过调用 readLine() 方法,可以一次读取一行内容,并将其存储到 line 变量中。然后可以对每行内容进行处理,例如打印出来或进行其他操作。使用这种方式,即使文件未完全写入,也能读取到已经写入的部分内容。但是结果是依然读取不到。
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;public class LogFileReader {public static void main(String[] args) {String filePath = "path/to/logfile.txt"; // 替换为您的日志文件路径File logFile = new File(filePath);try (BufferedReader reader = new BufferedReader(new FileReader(logFile))) {String line;while ((line = reader.readLine()) != null) {// 处理每行日志内容System.out.println(line);}} catch (IOException e) {e.printStackTrace();}}}

最终解决

没有调用项目中日志处理类的end()方法导致的。对于BufferedReader来说,在文件正在写入的过程中,它是可以读取到已经写入部分内容的。之所以依然查看不到日志内容,是因为没有调用 end() 方法来完成日志的写入操作和关闭文件流的操作,可能导致读取不到已经写入的内容:

  • 写入缓冲区未刷新:使用FileWriter来写入文件,而FileWriter内部使用了写入缓冲区,它会先将数据写入缓冲区,然后根据一定条件将缓冲区的数据刷新到文件中。如果没有调用end()方法,缓冲区的数据可能还未被刷新到文件中,从而导致BufferedReader读取不到正确的文件内容。
  • 文件流未关闭: 在调用end()方法时,会关闭文件流(FileWriter),关闭文件流会将缓冲区的剩余数据刷新到文件中,并释放系统资源。如果没有调用end()方法关闭文件流,可能会导致缓冲区中的数据没有被刷新到文件中,从而无法读取到正确的文件内容。

以下为日志处理类源码,供参考分析:

import com.google.common.base.Throwables;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.map.HashedMap;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;/*** 日志处理器.*/
@Slf4j
@Component
public class LogHandler {private static Map<Long, List<String>> logMap = new HashedMap();private static Map<Long, FileWriter> fileWriterMap = new HashMap<>();private static String taskLogPath;@PostConstructpublic void init() {write();}@PreDestroypublic void destory() {fileWriterMap.forEach((batchId, fw) -> {try {fw.flush();fw.close();} catch (IOException e) {log.error("关闭流失败:{}", e);}});}public static synchronized void start(Long batchId) {try {if (logMap.containsKey(batchId)) {log.info("已存在批次号:{}的任务!", batchId);return;}File logFile = new File(getLogPath(batchId));if (logFile.exists()) {log.info("批次号:{}已存在日志文件,不能重复提交!", batchId);return;}logFile.createNewFile();logMap.put(batchId, new ArrayList<String>());fileWriterMap.put(batchId, new FileWriter(logFile, true));} catch (Exception e) {log.error("批次号:{}开始记录任务失败:{}", batchId, e);}}/*** 记录日志.*/public static void log(Long batchId, String logContent) {if (null != logMap.get(batchId)) {logMap.get(batchId).add(DateUtils.formatDateTime(DateUtils.now()) + " 批次号:{" + batchId + "} " + logContent);}}/*** 记录异常.*/public static void exception(Long batchId, Exception e) {if (null != logMap.get(batchId)) {logMap.get(batchId).add(DateUtils.formatDateTime(DateUtils.now()) + " " + Throwables.getStackTraceAsString(e));}}/*** 日志记录到文件中.*/private static void write() {Thread logWriteThread = new Thread(new Runnable() {@Overridepublic void run() {while (true) {logMap.forEach((batchId, logContentList) -> {if (CollectionUtils.isEmpty(logContentList)) {return;}try {FileWriter fw = fileWriterMap.get(batchId);if (null != fw) {writeLog2File(fw, logContentList, true);logContentList.clear();}} catch (Exception e) {log.error("批次号为:{}的日志写入文件报错:{}", batchId, e);}});sleep(15);}}});logWriteThread.start();}/*** 该batchId的日志记录结束.*/public static synchronized void end(Long batchId) {if (!fileWriterMap.containsKey(batchId)) {log.info("批次号为:{}的输出流不存在", batchId);return;}FileWriter fw = fileWriterMap.get(batchId);try {List<String> logContentList = logMap.get(batchId);if (CollectionUtils.isNotEmpty(logContentList)) {try {writeLog2File(fw, logContentList, true);logContentList = null;} catch (Exception e) {log.error("批次号为:{}的日志写入文件报错:{}", batchId, e);}}logMap.remove(batchId);fileWriterMap.remove(batchId);} finally {try {fw.close();} catch (IOException e) {log.error("关闭流错误:{}", e);}}}private static void writeLog2File(FileWriter fw, List<String> logContentList, boolean flush) throws Exception {for (String logContent : logContentList) {fw.write(logContent);fw.write("\r\n");}if (flush) {fw.flush();}}public static String getLogPath(Long batchId) {return taskLogPath + batchId + ".log";}private static void sleep(long timeOut) {try {Thread.sleep(timeOut * 1000);} catch (InterruptedException e) {log.error("sleep error:{}", e);}}@Value("${log_path}")public void setTaskLogPath(String taskLogPath) {LogHandler.taskLogPath = taskLogPath;}}

20230727-随笔相关推荐

  1. Echo团队Alpha冲刺随笔 - 第九天

    项目冲刺情况 进展 已经进入测试阶段,正在消除系统的bug 问题 通过测试,找出了系统中存在的较多bug...... 体会 测试太重要了,很多原本以为没什么bug,一测就能找到好几个,而且改个bug真 ...

  2. 第一篇随笔——新的开端

    新的开端 这是我第一次开通博客也是第一次在博客上写随笔,这既是博客的新开端也是JAVA学习的新开端,希望能好好经营这第一个博客~. 觉得自己专业吗?对专业的期望 对于这个问题我不得不坦然承认虽然进入信 ...

  3. 我先了解一下博客园创建随笔/文章/日记的过程与三者的区别(隐私等级,是否审核等)...

    我先了解一下博客园创建随笔/文章/日记的过程与三者的区别(隐私等级,是否审核等) 转载于:https://www.cnblogs.com/Totooria-Hyperion/p/5260289.htm ...

  4. web高性能开发系列随笔

    在BlogJava里写了一些关于高性能WEB开发的随笔,因为都是跟前端技术相关(html,http,js,css等),所以也贴到博客园来,吸收下人气. 1. HTTP服务器. 2.性能测试工具推荐 3 ...

  5. cuda图像处理_CUDA随笔之图像直方图(优化历程)

    在忙忙碌碌许久之后,终于有时间写 "CUDA随笔" 系列的第二集了! 这次给大家带来了一个图像处理的应用例子:计算图片的直方图. 虽然使用CUDA可以很轻松地在性能上超越CPU,如 ...

  6. 博客园的“随笔、文章、新闻、日记有啥区别”

    随笔:是一些比较杂乱的技术类东西且有待修改和讨论的文章,假如是原创则只是作者随时提笔写下的内容,而并没有经过太多的推敲,甚至措辞也没有得到很合理的运用. 文章:直接获得的有正规性质的文章,或当随笔中的 ...

  7. [Object-C语言随笔之二] 《NSLog》常用的打印调试语句与自动排版

    本站文章均为 李华明Himi 原创,转载务必在明显处注明: 转载自[黑米GameDev街区] 原文链接: http://www.himigame.com/iphone-object/395.html ...

  8. 做一个略调皮的个人主页--相册与随笔篇

    目录 做一个略调皮的个人主页--菜单篇 做一个略调皮的个人主页--结构篇 做一个略调皮的个人主页--相册与随笔篇 总算在北京找到个便宜的房子租了,算是稳定下来啦. 新工作的节奏本来很快,有些不适用的, ...

  9. Ibatis学习随笔

    Ibatis学习随笔 < person >       < id > 1 </ id >     < firstName > Clinton </ ...

  10. 作为一枚第二天上班的小小.net程序员(技术宅的那种)很迷茫哦,第一个随笔

    作为一枚第二天上班的小小.net程序员(技术宅的那种)很迷茫哦,第一个随笔

最新文章

  1. C++ 判断字符串是否为空
  2. (原創) 如何將CMOS所擷取的影像傳到PC端? (SOC) (DE2) (TRDB-DC2)
  3. minicom的使用,发送AT指令
  4. 使用Python内置函数对变量类型进行转换
  5. Spring框架学习笔记(2)——IOCDI
  6. spring源码:资源管理器Resource
  7. 如何打开eclipse进行编写Java程序
  8. word2003如何设置护眼模式_ERP系统上线,如何设置采购收货的模式,提升企业的采购效率...
  9. Spring和SpringMVC整合
  10. java dojo_Dojo入门三种HelloWorld!
  11. vmware复制fedora16出现网络启动错误
  12. python所遇到的坑
  13. Docker快速入门-腾讯云
  14. 中山c 语言培训中心,中山英语口语培训中心
  15. 小白一键重装有linux,linux小白说说用linux的感受
  16. 支付功能----第三方支付公司
  17. https信任证书的三种方式
  18. DQN、DDQN、Dueling DQN、PER DQN
  19. SAP中MF47处理COGI欠料分析测试
  20. Workflow设计模式指的什么?

热门文章

  1. 【软件测试】性能测试工具基础-loadrunner12+jmeter
  2. 注册机 逆向 注册码
  3. 怎么把图片内容转换成文字?这两个小妙招轻松拿捏
  4. 动态规划7--例9.7友好城市
  5. Charles设置断点
  6. 编写一个程序,用户输入某个大写字母,产生一个金字塔图案。例如用户输入字母 E,则产生如下图案:
  7. 网易云音乐用户画像资产治理及业务赋能
  8. Autojs 查找控件
  9. C++MFC(13)-双缓冲技术实现绘图
  10. 使用旋转动画让图片转起来