前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >聊聊logback的FileAppender

聊聊logback的FileAppender

作者头像
code4it
发布2023-10-30 19:30:15
2040
发布2023-10-30 19:30:15
举报
文章被收录于专栏:码匠的流水账

本文主要研究一下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。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2023-10-30,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 码匠的流水账 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • FileAppender
  • start
    • checkForFileCollisionInPreviousFileAppenders
      • openFile
      • stop
      • writeOut
        • safeWrite
        • 小结
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档