`
winnerbao
  • 浏览: 10370 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

[实践]Log4j 1.X BufferedIO不工作(<8k时)原因分析, 暨深入探查Java IO Output BufferSize

    博客分类:
  • Java
 
阅读更多

[介绍]

本人在两年前写过一个关于此标题的总结,先发布于此与君共享。
 
两年前(2013),工作中需要设置log4j 1.x的bufferIO为2048, 但是发现其并没有在达到2048个字节是写入本地文件。而是一直到8k个字节是才写入。设置为其他任何小于8k的值也都没有用。8k好象是一个hardcode value。
因此,我做了一些深入的调查。
 
本文将涉及如下方面
1. log4j bufferIO的使用(code example)
2. log4j bufferIO的限制(1.X), 以及与log4j 2.0的相关区别
3. java BufferWriter的bufferSize的工作方式
4. 操作系统在IO在IO输出方面的基础知识
 
[根本原因]
log4j的bufferIO使用了java 的BufferedWriter. Log4j所设置的bufferSize,即为BufferedWriter的BufferSize.
BufferedWriter底层最终调用为sun.nio.cs.StreamEncode。由于StreamEncode hardcode了8k的buffer,不管底BufferedWriter设置size为多少,其都将数据达到8k时才写入磁盘(flush)。
简单示意如下
Log4j
  |
BufferedWriter
  |
StreamEncode (hardcode 8k buffer)
 
 
[问题引入]
 
Log4j代码示例
1)创建buffered Logger

Logger bufferedLogger = Logger.getLogger("dummy_logger");

bufferedLogger.removeAllAppenders();

bufferedLogger.setLevel(Level.INFO);

 

 

PatternLayout layout = new PatternLayout(PATTERN_LAYOUT);

DailyRollingFileAppenderappender = new DailyRollingFileAppender(layout,logfile,DATE_PATTERN);

 

appender.setBufferSize(bufferSize);

appender.setBufferedIO(true);

 

appender.setThreshold(Level.INFO);

appender.setAppend(true);

appender.activateOptions();

bufferedLogger.addAppender(appender);

2) 调用buffered Logger(bufferSize=2048),每次写入1024字节.观察发现只有写入8次以后,磁盘中才看到log的身影。

Logger bufferedLogger = createBufferedLogger(“1.log”, 2048)’

 

int        sizeEachLog  1024;

 

 

for (inti = 0; i < 100; i++)

{

     TimeUnit.SECONDS.sleep(3 );

 

     bufferedLogger. info(

          DummyDataCreatorgetDummyString

         sizeEachLog,  logSeed  + Integer     

           .toStringi)  + " : "));

 

}

[直接原因]
通过查看OpenSDK源代码(openjdk-6-src-b27-26_oct_2012),得到如下的调用栈
Log4j
  |
BufferedWriter(这里的buffer size只决定这里何时将数据写入到StreamEncode,这里将不进行任何显式flush操作)
  |
sun.nio.cs.StreamEncode (hardcode 8k buffer)

所以说,log4j将buffersize设置为2k,则写入4k以后,bufferwriter将写入4k数据到StreamEncode里面。由于还没有到达StreamEncode的8k的buffersize,数据没有被写入到磁盘/内核中。
 
[深入/扩展]
 
[比较log4j without buffer, log4j with bufferedIO, 和 logj 2.0 bufferedIO]
下表为 1)Log4j 1.X 默认调用输出, 2)log4j 1.X bufferediIO, 3)log4j 2.0 bufferedIO的调用栈。可见log4j2.0没有这个问题,因为它使用了BufferedOutputStream.

1)Log4j –

Default

2) Log4j –

BufferedIO Enabled

3)Log4j 2.0 – beta6

BufferedIO Enabled

Log4j

Log4j

Log4j

FileOutputStream

备注:在这里,将沿着JNI->JVM->System Call,一直调用到system call的write方法。均为同步调用。

BufferedWritter

(configurable buffer)

BufferedOutputSteam

(configurable buffer)

备注:当达到指定的buffersize时,就沿着调用关系,一直同步调用到system call.

 

N/A

sun.nio.cs.StreamEncoder

 

备注:StreamEncode有hardcode的8k buffer size. 只有数据达到8k的时候,才触发继续JNI->JVN->SYSTEM CALL.

N/A

--------------------------------------------------------

Java Native Interface 

--------------------------------------------------------

JVM

--------------------------------------------------------

System Call

 

 
[Java IO(including NIO)(JSK1.6) 在 IO 调用关系中的位置]


 
 
 

IO类型

描述

函数

NOTE
JAVA IO
java IO几乎没有使用C标准IO。只有在ZIP相关功能中使用了C标准IO. 所有Java IO/NIO/NIO2 etc
KB - JVM - IO - hotspot JVM just use Standard IO a little

C标准IO( Standard I/O)

 

对文件描述符的更高级封装。使用了缓存与流的概念。C库中提供了用户空间的缓存(Higher level abstraction on descriptor. Buffer/Stream concept is used. There is user-space buffers provided by the C library.)

fflush()方法调用,会将用户空间缓存flush到内核空间。

fopen/fclose/gets/fgets/puts/fputs/fflush/fseek/etc

 

 

内核IO(Kernel I/O | UNIX I/O)

写入到内核后(flush后),虽然还没有sync到磁盘中,通过tail -f还是可以看到文件变化。

内核IO有自己的缓存。(Kernel has its own buffer.)

 

fsync()方法将刷新内核缓存到设备中

 

open/close/write/read/flush/seed

 
设备驱动(Device Driver)

CPU将调用设备驱动程序指令与设备交互(

CPU will execute Device Driver instruction to drive the communication with I/O.)

 

 
 
[继续实验]
有兴趣的话,请完成以下实验
实验
参数
期望行为
备注
Java FileOutputStream
每次写入都能直接文件中看到变化,即使没有flush。
call kernerl io function. It will reach kernel buffer directly.
Java BufferedOutputSteam
bufsiz-512, 
write 256 bytes each time
每次达到buffersize都,都能看到文件内容变化
log4j 2.0 用的就是这个
Java BufferedWriter 
bufsiz-512, 
write 256 bytes each time
即使达到了512,文件中也看不到。直到8k才看到。
Just the problem of log4j 1.X.
Java FileChannel
direct buffer
 
it will invoke kernel I/O write directly. [KB - JVM - IO - invokation stack of FileChannel.write()
C Standard IO
bufsiz-512, 
write 256 bytes each time
每次达到buffersize都,都能看到文件内容变化
使用setbuf设置buffer大小
there is lib buffer. But it support setup the buffer size
C Kernel/Unix IO
每次写入都能直接文件中看到变化,即使没有fsync。
write to kernel buffer directly
 
 
[环境]
JDK 1.6
 
[参考]
  1. HotSpot JVM Source Code http://download.java.net/openjdk/jdk6/
  2. Log4j 1.2.17 source code
  3. Chapter 13 of <<Operating System>> 9th edition
  4. Chapter 12 of <<Computer.Systems.A.Programmer_s.Perspective>> 1st edistion
  5. [KB JVM - IO -  call stack of Writer.write()] 
  6. [KB JVM - IO -  call stack of OutputSteam.write()] 
  7. [KB JVM - IO -  call stack of FileChannel.write()]
  8. [KB JDK - how BufferdOutputStream.write() work]
  9. [KB JVM - IO -  writing difference between FileOutputstream and BufferedWriter ]
  10. [KB JVM - IO - Java IO Reading with call stack - FileInputSteam, RandomAccessFile, and FileChannel]
 
本文从 Buffering_On_JAVA_IO_Write.ppt 重新编排而来

 

  • 大小: 43.2 KB
分享到:
评论

相关推荐

    log4j自定义日志文件名及日志输出格式

    当我们面对特定项目需求,比如需要自定义日志文件名和日志输出格式时,Log4j同样提供了相应的解决方案。 首先,让我们深入理解如何自定义日志文件名。默认情况下,Log4j的日志文件名通常是固定的或者基于时间戳生成...

    log4j 1.2.12.zip

    《深入理解Log4j 1.2.12:日志管理的核心工具》 在软件开发过程中,日志记录是一项至关重要的任务,它能够帮助开发者追踪程序运行状态、定位问题和进行性能分析。Log4j作为Java平台上的一个经典日志框架,自推出...

    Log4j 详细配置

    Log4j 的源码可以帮助我们深入理解其工作原理,而相关工具如 LogViewer、LogFaces 可以提供图形化的日志查看和分析界面,提升开发效率。 总之,Log4j 的详细配置涵盖了日志级别设定、输出目的地、输出格式等多个...

    log4j常用Appender配置

    Log4j 是一个 Java 语言下的日志记录工具库,它提供了灵活的日志记录机制,可以将日志信息写入到控制台、文件、数据库等多种目标中。在 Log4j 中,Appender 是一种输出目标,它负责将日志信息写入到指定的目标中。...

    log4j完全手册

    《Log4j完全手册》是一本详尽阐述Log4j日志框架的指南,它针对Java开发者,提供了全面的配置信息和深入的参数解析。Log4j是Apache软件基金会的一个项目,是Java平台上广泛使用的日志记录工具,因其灵活性、可配置性...

    java_IO.rar

    4. **BufferedIO(04_BufferedIO.avi)**:缓冲流(Buffered Stream)提高了IO操作的效率,通过内部缓冲区来减少实际的物理I/O操作。BufferedReader和BufferedWriter增加了读写效率,而BufferedInputStream和...

    Log4j自已配置

    标题中的“Log4j自定义配置”指的是在Java应用程序中,使用Log4j框架进行个性化配置,以便更好地管理和控制日志记录的过程。Log4j是一个广泛使用的开源日志记录库,它提供了灵活的日志配置,使得开发者可以根据项目...

    java sdk01.rar_io_java 类库

    Java SDK是Java开发的核心工具包,它包含了大量用于构建和运行Java应用程序的类库。在Java SDK中,`java.io`包是极其重要的一个部分,主要用于处理输入输出操作。这个包提供了许多类和接口,使得开发者能够有效地...

    Practical Mod Perl

    Practical Mod Perl&lt;br&gt;&lt;br&gt; Copyright &lt;br&gt; Preface &lt;br&gt; What You Need to Know &lt;br&gt; Who This Book Is For &lt;br&gt; How This Book Is Organized &lt;br&gt; Reference Sections &lt;br&gt; Filesystem Conventions &lt;br&gt; Apache ...

    java io流的实践案例大全

    在这个“java io流的实践案例大全”中,你将找到一系列全面且深入的示例,帮助你理解和掌握Java IO流的使用。 IO流在Java中分为四类:字节流(Byte Stream)、字符流(Character Stream)、对象流(Object Stream)...

    log4net日志输出

    `log4net`是Apache软件基金会的Jakarta项目的一部分,灵感来源于Java的log4j。它是.NET平台上一个强大的日志工具,支持多种日志目标,如控制台、文件、数据库等,并且具有丰富的日志级别,如DEBUG、INFO、WARN、...

    java_io详解

    ### Java IO详解 #### 1. 什么是IO 在计算机科学中,IO(Input/Output,输入/输出)指的是程序与外部系统之间进行数据交换的过程。在Java中,IO操作主要通过`java.io`包来实现,这个包包含了用于文件读写、标准...

    彻底明白java的io系统

    Java的IO系统是Java编程中的核心部分,它允许程序与外部世界进行数据交换,包括读取文件、写入文件、网络通信以及设备交互等。理解并掌握Java的IO系统对于任何Java开发者,尤其是新手,都是至关重要的。在这个实例...

    java io 系列操作代码练习 Java学习资料

    Java IO(Input/Output)是Java编程语言中用于处理输入输出操作的重要部分,它提供了丰富的类库来实现数据的读写、文件操作、网络通信等功能。在这个“java io 系列操作代码练习”中,我们可以深入理解并掌握Java IO...

    java io读取文件

    在Java编程语言中,`IO`(Input/Output)是处理数据输入和输出的核心部分,尤其是在处理大数据量文件时显得尤为重要。Java IO API提供了一系列类和接口,使得开发者能够高效地读取、写入和操作文件。下面我们将深入...

    Java-Io流,练习

    Java的IO流是Java编程语言中的重要组成部分,它主要用于数据的输入和输出操作。在Java中,IO流被设计为处理任何类型的数据,包括字符、字节甚至对象。本练习旨在帮助初学者理解和掌握Java IO流的基础知识。 一、IO...

    IBM-ETP-java培训10.Java IO.ppt

    IBM-ETP的Java培训课程针对这一主题进行深入探讨,旨在帮助开发者熟练掌握IO流的概念、使用方法以及在实际项目中的应用。以下是对Java IO系统的一些关键知识点的详细阐述: 1. **IO流的概念**:在Java中,IO流是...

    java io流源代码

    在提供的链接中,你可能会找到关于这些概念的源代码实现,这将有助于深入理解Java IO流的工作原理和用法。通过学习和分析这些源代码,你可以更好地掌握Java的输入输出机制,并能更有效地处理各种IO操作。

    Java IO

    ### Java IO概述 Java IO(输入/输出)是Java编程语言中用于处理各种形式的数据传输的核心部分。它涉及从文件系统、网络等不同资源中读取数据或将数据写入这些资源的过程。Java IO的设计采用了面向对象的方法,通过...

Global site tag (gtag.js) - Google Analytics