log4j2 默认打印流程:
好了,正题开始
RollingRandomAccessFileAppender#appender()
/**
* Write the log entry rolling over the file when required.
*
* @param event The LogEvent.
*/
@Override
public void append(final LogEvent event) {
final RollingRandomAccessFileManager manager = (RollingRandomAccessFileManager) getManager();
// 日志滚动切割的位置
manager.checkRollover(event);
// Leverage the nice batching behaviour of async Loggers/Appenders:
// we can signal the file manager that it needs to flush the buffer
// to disk at the end of a batch.
// From a user's point of view, this means that all log events are
// _always_ available in the log file, without incurring the overhead
// of immediateFlush=true.
manager.setEndOfBatch(event.isEndOfBatch());
// 写文件操作
super.append(event);
}
RollingFileManager#rollover()
/**
* Determine if a rollover should occur.
* @param event The LogEvent.
*/
public synchronized void checkRollover(final LogEvent event) {
if (triggeringPolicy.isTriggeringEvent(event)) {
rollover();
}
}
public synchronized void rollover() {
if (rollover(rolloverStrategy)) {
try {
size = 0;
initialTime = System.currentTimeMillis();
createFileAfterRollover();
} catch (final IOException e) {
logError("failed to create file after rollover", e);
}
}
}
private boolean rollover(final RolloverStrategy strategy) {
try {
// Block until the asynchronous operation is completed.
semaphore.acquire();
} catch (final InterruptedException e) {
logError("Thread interrupted while attempting to check rollover", e);
return false;
}
boolean success = false;
Thread thread = null;
try {
final RolloverDescription descriptor = strategy.rollover(this);
if (descriptor != null) {
writeFooter();
// (2),刷buffer 以及randomAccessFile.close(),后面要进行改名了嘛
close();
if (descriptor.getSynchronous() != null) {
LOGGER.debug("RollingFileManager executing synchronous {}", descriptor.getSynchronous());
try {
// 滚动改名执行的是 FileRenameAction, 这里就不做解释了
success = descriptor.getSynchronous().execute();
} catch (final Exception ex) {
logError("caught error in synchronous task", ex);
}
}
if (success && descriptor.getAsynchronous() != null) {
LOGGER.debug("RollingFileManager executing async {}", descriptor.getAsynchronous());
thread = new Log4jThread(new AsyncAction(descriptor.getAsynchronous(), this));
thread.start();
}
return true;
}
return false;
} finally {
if (thread == null || !thread.isAlive()) {
semaphore.release();
}
}
}
AbstractOutputStreamAppender#append
/**
* Actual writing occurs here.
* <p>
* Most subclasses of <code>AbstractOutputStreamAppender</code> will need to override this method.
* </p>
*
* @param event The LogEvent.
*/
@Override
public void append(final LogEvent event) {
readLock.lock();
try {
final byte[] bytes = getLayout().toByteArray(event);
if (bytes.length > 0) {
// 一开始的疑惑就在这里(1)
manager.write(bytes);
if (this.immediateFlush || event.isEndOfBatch()) {
manager.flush();
}
}
} catch (final AppenderLoggingException ex) {
error("Unable to write to stream " + manager.getName() + " for appender " + getName());
throw ex;
} finally {
readLock.unlock();
}
}
RandomAccessFileManager.java
@Override
protected synchronized void write(final byte[] bytes, int offset, int length) {
super.write(bytes, offset, length); // writes to dummy output stream
int chunk = 0;
do {
if (length > buffer.remaining()) {
flush();
}
chunk = Math.min(length, buffer.remaining());
buffer.put(bytes, offset, chunk);
offset += chunk;
length -= chunk;
} while (length > 0);
if (isImmediateFlush || isEndOfBatch.get() == Boolean.TRUE) {
flush();
}
}
@Override
public synchronized void flush() {
buffer.flip();
try {
randomAccessFile.write(buffer.array(), 0, buffer.limit());
} catch (final IOException ex) {
final String msg = "Error writing to RandomAccessFile " + getName();
throw new AppenderLoggingException(msg, ex);
}
buffer.clear();
}
@Override
public synchronized void close() {
flush();
try {
randomAccessFile.close();
} catch (final IOException ex) {
logError("unable to close RandomAccessFile", ex);
}
}
如果线程Thread-1运行到这里(1)位置,线程Thread-0执行到(2)位置,会不会出现数据丢失问题。
哈哈哈——肯定不会的,
比如Thread-0先执行close 方法,然后直到最外层的createFileAfterRollover 执行完(退出checkRollover() 之前 ) , thread-1 在 write 要做的操作都是阻塞状态的。所以不会丢数据。
它用synchronized 直接在 rollover() 一开始就锁上了。
但可能出现一些数据滚动的不太好的情况。(几率很低)