private void openNewFile(boolean recoverFromError) {
        ZonedDateTime now = ZonedDateTime.now(TimeZoneConst.ZONE_ID);
        ZonedDateTime next = now.plus(1, ChronoUnit.DAYS)
                .truncatedTo(ChronoUnit.DAYS);
        Path dir = fileDirectory;
        if (recoverFromError) {
            try {
                Files.createDirectories(dir);
            } catch (FileAlreadyExistsException e) {
                InternalLogger.INSTANCE.error("The path ("
                        + dir
                        + ") is pointing to a file instead of a directory. "
                        + "This may happen if the directory has been replaced with a file unexpectedly by users. "
                        + "Start deleting the file to recover");
                try {
                    Files.deleteIfExists(dir);
                } catch (Exception ignored) {
                }
                try {
                    Files.createDirectories(dir);
                } catch (IOException exception) {
                    throw new InputOutputException(exception);
                }
            } catch (Exception e) {
                InternalLogger.INSTANCE.error("Caught an error while creating the directory: "
                        + dir, e);
            }
        } else {
            try {
                Files.createDirectories(dir);
            } catch (Exception e) {
                throw new InputOutputException(
                        "Caught an error while creating the directory: "
                                + dir,
                        e);
            }
        }
        String fileName;
        Path filePath;
        while (true) {
            if (StringUtil.isBlank(filePrefix)) {
                fileName = fileDateTimeFormatter.format(now) + FIELD_DELIMITER + nextIndex
                        + fileSuffix;
            } else {
                fileName = filePrefix + FIELD_DELIMITER + fileDateTimeFormatter.format(now)
                        + FIELD_DELIMITER + nextIndex + fileSuffix;
            }
            filePath = fileDirectory.resolve(fileName);
            try {
                channel = FileChannel.open(filePath, APPEND_OPTIONS);
                break;
            } catch (IOException e) {
                String message = "Failed to open a file channel for the file: "
                        + filePath;
                if (recoverFromError) {
                    InternalLogger.INSTANCE.error(message, e);
                    nextIndex++;
                    try {
                        Thread.sleep(1000L);
                    } catch (InterruptedException exc) {
                        throw new InputOutputException(message, e);
                    }
                } else {
                    throw new InputOutputException(message, e);
                }
            }
        }
        Path archivePath = enableCompression
                ? filePath.resolveSibling(fileName + ARCHIVE_FILE_SUFFIX)
                : null;
        currentFile = new LogFile(filePath, archivePath, now, nextIndex);
        files.add(currentFile);

        try {
            nextFileBytes = channel.size();
        } catch (IOException e) {
            String message = "Failed to read the file channel size: "
                    + filePath;
            if (recoverFromError) {
                message += ". Fallback to 0";
                InternalLogger.INSTANCE.error(message, e);
                nextFileBytes = 0;
            } else {
                throw new InputOutputException(message, e);
            }
        }
        nextDay = TimeUnit.SECONDS.toNanos(next.toEpochSecond());
        nextIndex++;
    }
Note that it is fine to close file channel and open a new one not atomically because there is
always only one thread will call roll() and append()
turms-server-common/src/main/java/im/turms/server/common/infra/logging/core/appender/file/RollingFileAppender.java