聊聊logback的FileAppender

2023-10-30 19:30:15 浏览数 (2)

本文主要研究一下logback的FileAppender

FileAppender

ch/qos/logback/core/FileAppender.java

代码语言:javascript复制
public class FileAppender<E> extends OutputStreamAppender<E> {

    public static final long DEFAULT_BUFFER_SIZE = 8192;

    static protected String COLLISION_WITH_EARLIER_APPENDER_URL = CODES_URL   "#earlier_fa_collision";

    /**
     * Append to or truncate the file? The default value for this variable is
     * <code>true</code>, meaning that by default a <code>FileAppender</code> will
     * append to an existing file and not truncate it.
     */
    protected boolean append = true;

    /**
     * The name of the active log file.
     */
    protected String fileName = null;

    private boolean prudent = false;

    private FileSize bufferSize = new FileSize(DEFAULT_BUFFER_SIZE);

    //......
}    

FileAppender继承了OutputStreamAppender,它定义了append、prudent、bufferSize属性

start

代码语言:javascript复制
    public void start() {
        int errors = 0;
        if (getFile() != null) {
            addInfo("File property is set to ["   fileName   "]");

            if (prudent) {
                if (!isAppend()) {
                    setAppend(true);
                    addWarn("Setting "Append" property to true on account of "Prudent" mode");
                }
            }

            if (checkForFileCollisionInPreviousFileAppenders()) {
                addError("Collisions detected with FileAppender/RollingAppender instances defined earlier. Aborting.");
                addError(MORE_INFO_PREFIX   COLLISION_WITH_EARLIER_APPENDER_URL);
                errors  ;
            } else {
                // file should be opened only if collision free
                try {
                    openFile(getFile());
                } catch (java.io.IOException e) {
                    errors  ;
                    addError("openFile("   fileName   ","   append   ") call failed.", e);
                }
            }
        } else {
            errors  ;
            addError(""File" property not set for appender named ["   name   "].");
        }
        if (errors == 0) {
            super.start();
        }
    }

start方法要求fileName必须有值,在prudent模式下会强制开启append;另外start的时候会执行checkForFileCollisionInPreviousFileAppenders判断是否有冲突,没有冲突则执行openFile方法

checkForFileCollisionInPreviousFileAppenders

代码语言:javascript复制
    protected boolean checkForFileCollisionInPreviousFileAppenders() {
        boolean collisionsDetected = false;
        if (fileName == null) {
            return false;
        }
        @SuppressWarnings("unchecked")
        Map<String, String> map = (Map<String, String>) context.getObject(CoreConstants.FA_FILENAME_COLLISION_MAP);
        if (map == null) {
            return collisionsDetected;
        }
        for (Entry<String, String> entry : map.entrySet()) {
            if (fileName.equals(entry.getValue())) {
                addErrorForCollision("File", entry.getValue(), entry.getKey());
                collisionsDetected = true;
            }
        }
        if (name != null) {
            map.put(getName(), fileName);
        }
        return collisionsDetected;
    }

checkForFileCollisionInPreviousFileAppenders方法从上下文读取FA_FILENAME_COLLISION_MAP,判断有没有文件名重复的,有则返回true

openFile

代码语言:javascript复制
    public void openFile(String file_name) throws IOException {
        lock.lock();
        try {
            File file = new File(file_name);
            boolean result = FileUtil.createMissingParentDirectories(file);
            if (!result) {
                addError("Failed to create parent directories for ["   file.getAbsolutePath()   "]");
            }

            ResilientFileOutputStream resilientFos = new ResilientFileOutputStream(file, append, bufferSize.getSize());
            resilientFos.setContext(context);
            setOutputStream(resilientFos);
        } finally {
            lock.unlock();
        }
    }

openFile方法加锁创建file,然后通过createMissingParentDirectories来创建不存在的父目录,最后创建根据file、append参数、bufferSize来创建ResilientFileOutputStream

stop

代码语言:javascript复制
    public void stop() {
        super.stop();

        Map<String, String> map = ContextUtil.getFilenameCollisionMap(context);
        if (map == null || getName() == null)
            return;

        map.remove(getName());
    }

stop方法会获取FA_FILENAME_COLLISION_MAP,移除当前文件名

writeOut

代码语言:javascript复制
    protected void writeOut(E event) throws IOException {
        if (prudent) {
            safeWrite(event);
        } else {
            super.writeOut(event);
        }
    }

FileAppender覆盖了OutputStreamAppender的writeOut方法,在prudent为true时执行safeWrite

safeWrite

代码语言:javascript复制
    private void safeWrite(E event) throws IOException {
        ResilientFileOutputStream resilientFOS = (ResilientFileOutputStream) getOutputStream();
        FileChannel fileChannel = resilientFOS.getChannel();
        if (fileChannel == null) {
            return;
        }

        // Clear any current interrupt (see LOGBACK-875)
        boolean interrupted = Thread.interrupted();

        FileLock fileLock = null;
        try {
            fileLock = fileChannel.lock();
            long position = fileChannel.position();
            long size = fileChannel.size();
            if (size != position) {
                fileChannel.position(size);
            }
            super.writeOut(event);
        } catch (IOException e) {
            // Mainly to catch FileLockInterruptionExceptions (see LOGBACK-875)
            resilientFOS.postIOFailure(e);
        } finally {
            if (fileLock != null && fileLock.isValid()) {
                fileLock.release();
            }

            // Re-interrupt if we started in an interrupted state (see LOGBACK-875)
            if (interrupted) {
                Thread.currentThread().interrupt();
            }
        }
    }

safeWrite会通过fileChannel.lock()进行加锁,然后执行fileChannel.position(size),最后通过父类writeOut进行写入;对于IOException会执行resilientFOS.postIOFailure(e)

小结

logback的FileAppender继承了OutputStreamAppender,它定义了append、prudent、bufferSize属性,它使用的是ResilientFileOutputStream,其writeOut方法主要是新增了对prudent模式的支持,在prudent为true时采用的是safeWrite。

0 人点赞