日志文件转储压缩实现

日志的转储和压缩是非常关键的,它不仅可以减少硬盘空间占用,主要还可以在发生故障时根据日志定位出故障原因。下面来看看golang和java的文件转储实现。

go语言:

用到了filepath包下的Walk方法,具体说明可以参看历史文章:

go语言path/filepath包之Walk源码解析

package main

import (
  "fmt"
  "os"
  "io"
  "archive/zip"
  "path/filepath"
  "time"
  "log"
)

func main() {

  logFile := "D:/tmp/successLog/logs/root.log"

  backFile := "D:/tmp/successLog/logs/root_" + time.Now().Format("20060102150405") + ".zip"
  
  err := zipFile(logFile, backFile)
  if err != nil {
    log.Println(fmt.Sprintf("zip file %s to %s error : %v", logFile, backFile, err))
    return
  } else {
    os.Remove(logFile)
  }

  //转储后创建新文件
  //createFile()

  //修改文件权限
  //os.Chmod(backfile, 0400)

  //删除备份文件
  //deleteOldBackfiles(dir)
}


func zipFile(source, target string) error {

  zipFile, err := os.OpenFile(target, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0440)
  if err != nil {
    log.Println(err)
    return err
  }
  defer zipFile.Close()

  archive := zip.NewWriter(zipFile)
  defer archive.Close()

  return filepath.Walk(source, func(path string, info os.FileInfo, err error) error {
    if err != nil {
      return err
    }

    header, err := zip.FileInfoHeader(info)
    if err != nil {
      return err
    }

    if !info.IsDir() {
      header.Method = zip.Deflate
    }
    header.SetModTime(time.Now().UTC())
    header.Name = path
    writer, err := archive.CreateHeader(header)
    if err != nil {
      return err
    }

    if info.IsDir() {
      return nil
    }
    file, err := os.Open(path)

    if err != nil {
      return err
    }
    defer file.Close()

    _, err = io.Copy(writer, file)
    return err
  })
}

java版:

说明见注释。

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.*;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.zip.CRC32;
import java.util.zip.CheckedOutputStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

/**
 * @program: website
 * @description: 转储压缩文件
 * @author: smallsoup
 * @create: 2018-08-12 17:58
 **/

public class ZipFile {

    private static final Logger LOGGER = LoggerFactory.getLogger(ZipFile.class);

    /**
     * 格式化文件名格式
     */
    private static final String AUDIT_LOG_FORMAT = "yyyyMMddHHmmssSSS";

    /**
     * 压缩后文件后缀
     */
    private static final String AUDIT_FILE_ZIP_SUFFIX = ".zip";

    /**
     * 压缩前文件后缀
     */
    private static final String AUDIT_FILE_EXT = ".log";

    private static final int ZIP_BUFFER = 4096;

    /**
     * 控制压缩后的文件解压后是否带base路径
     */
    private static final String rootPath = "";


    public static void main(String[] args) throws IOException {

        System.out.println();

        new ZipFile().zipAuditLogFile("D:/tmp/successLog/logs/root.log");
    }

    /**
     * 日志压缩
     *
     * @param waitZipFile 要压缩文件名
     * @throws IOException
     */
    private void zipAuditLogFile(String waitZipFile) throws IOException {
        File oldFile = new File(waitZipFile);

        if (!oldFile.exists()) {
            LOGGER.error("zipAuditLogFile name is {} not exist", waitZipFile);
            return;
        }

        //生成zip文件名
        DateFormat dataFormat = new SimpleDateFormat(AUDIT_LOG_FORMAT);
        String formatTime = dataFormat.format(oldFile.lastModified());

        int end = waitZipFile.length() - AUDIT_FILE_EXT.length();
        String zipFileName = waitZipFile.subSequence(0, end) + "_" + formatTime + AUDIT_FILE_ZIP_SUFFIX;

        File zipFile = new File(zipFileName);

        FileOutputStream zipfos = null;
        ZipOutputStream zipOs = null;
        CheckedOutputStream cos = null;


        try {
            zipfos = new FileOutputStream(zipFile);
            cos = new CheckedOutputStream(zipfos, new CRC32());

            zipOs = new ZipOutputStream(cos);

            compress(oldFile, zipOs, rootPath);

            if (zipFile.exists()) {
                // 写完的日志文件权限改为400
                try {
                    //linux上才可以运行,windows上需要装cygwin并且把cygwin的bin目录加到环境变量的path中才可以
                    Runtime.getRuntime().exec("chmod 400 -R " + zipFile);
                    //压缩后删除旧文件
                    boolean isDelete = oldFile.delete();
                    //创建新文件
                    if (isDelete) {
                        oldFile.createNewFile();
                    }
//                    boolean isSuccess = PathUtil.setFilePermision(zipFile.toPath(), ARCHIVE_LOGFILE_PERMISION);
//                    LOGGER.warn("set archive file: {}, permision result is {}", zipFile.getAbsolutePath(), isSuccess);
                } catch (IOException e) {
                    LOGGER.error("set archive file:{} permision catch an error: {}", zipFile, e);
                }
            }

        } finally {

            if (null != zipOs) {
                zipOs.close();
            }

            if (null != cos) {
                cos.close();
            }

            if (null != zipfos) {
                zipfos.close();
            }
        }
    }

    /**
     * 压缩文件或目录
     *
     * @param oldFile 要压缩的文件
     * @param zipOut  压缩文件流
     * @param baseDir baseDir
     * @throws IOException
     */
    private void compress(File oldFile, ZipOutputStream zipOut, String baseDir) throws IOException {

        if (oldFile.isDirectory()) {

            compressDirectory(oldFile, zipOut, baseDir);

        } else {
            compressFile(oldFile, zipOut, baseDir);
        }
    }

    /**
     * 压缩目录
     *
     * @param dir     要压缩的目录
     * @param zipOut  压缩文件流
     * @param baseDir baseDir
     * @throws IOException
     */
    private void compressDirectory(File dir, ZipOutputStream zipOut, String baseDir) throws IOException {

        File[] files = dir.listFiles();

        for (File file : files) {
            compress(file, zipOut, baseDir + dir.getName() + File.separator);
        }
    }

    /**
     * 压缩文件
     *
     * @param oldFile 要压缩的文件
     * @param zipOut  压缩文件流
     * @param baseDir baseDir
     * @throws IOException
     */
    private void compressFile(File oldFile, ZipOutputStream zipOut, String baseDir) throws IOException {

        if (!oldFile.exists()) {
            LOGGER.error("zipAuditLogFile name is {} not exist", oldFile);
            return;
        }

        BufferedInputStream bis = null;

        try {

            bis = new BufferedInputStream(new FileInputStream(oldFile));

            ZipEntry zipEntry = new ZipEntry(baseDir + oldFile.getName());

            zipOut.putNextEntry(zipEntry);

            int count;

            byte data[] = new byte[ZIP_BUFFER];

            while ((count = bis.read(data, 0, ZIP_BUFFER)) != -1) {
                zipOut.write(data, 0, count);
            }

        } finally {
            if (null != bis) {
                bis.close();
            }
        }

    }

}

修改权限也可以利用Java7中NIO.2对元数据文件操作的支持,具体可以查看NIO包的使用,其相关教程见文末说明。

代码如下:

package com.website.common;

import java.io.IOException;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.attribute.PosixFilePermission;
import java.nio.file.attribute.PosixFilePermissions;
import java.util.Set;

/**
 * 提供文件路径公共函数 改变权限,判断是否正规文件,判断是否路径在安全路径下等
 *
 * @program: website
 * @description: 路径工具, 修改权限
 * @author: smallsoup
 * @create: 2018-08-14 07:56
 **/

public class PathUtil {

    /**
     * POSIX表示可移植操作系统接口,并不局限于unix类系统
     */
    private static final boolean ISPOSIX = FileSystems.getDefault().supportedFileAttributeViews().contains("posix");

    /**
     * 数字权限格式,如600
     */
    private static final int PERM_LEN_THREE = 3;

    /**
     * 如765   rwxrw_r_x
     */
    private static final int PERM_LEN_NINE = 9;


    /**
     * 设置文件的权限,尽在posix下有效
     *
     * @param file 文件
     * @param perm 权限 类似 “rw-r-----”, "640"
     * @return true 修改成功 false 修改失败
     * @throws IOException
     */
    public static boolean setFilePermision(Path file, String perm) throws IOException {
        if (!ISPOSIX) {
            return true;
        }
        // 750 -> "rwxr-x---"
        if (perm.length() == PERM_LEN_THREE) {
            perm = trans2StrPerm(perm);
        }

        if (perm.length() != PERM_LEN_NINE) {
            return false;
        }

        Set<PosixFilePermission> perms = PosixFilePermissions.fromString(perm);
        Files.setPosixFilePermissions(file, perms);
        return true;
    }

    /**
     * 转换
     *
     * @param digitPerm 长度为3的数字字符串
     * @return
     */
    private static String trans2StrPerm(String digitPerm) {
        StringBuilder builder = new StringBuilder(9);
        // owner
        builder.append(toStringPerm(digitPerm.charAt(0)));
        // group
        builder.append(toStringPerm(digitPerm.charAt(1)));
        // other
        builder.append(toStringPerm(digitPerm.charAt(2)));
        return builder.toString();
    }

    private static String toStringPerm(char ch) {
        switch (ch - '0') {
            case 7:
                return "rwx";
            case 6:
                return "rw-";
            case 5:
                return "r-x";
            case 4:
                return "r--";
            case 3:
                return "-wx";
            case 2:
                return "-w-";
            case 1:
                return "--x";
            case 0:
                return "---";
            default:
                return "";
        }
    }
}

原文发布于微信公众号 - 我的小碗汤(mysmallsoup)

原文发表时间:2018-08-15

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏分布式系统进阶

Librdkafka的操作处理队列

32620
来自专栏wannshan(javaer,RPC)

dubbo监控机制之监控中心实现分析

这里的监控中心以dubbo-ops\dubbo-monitor-simple项目说 总的来说是监控中心启动一个sevlet容器,通过web页面向用户多维度的展...

1.4K60
来自专栏zhisheng

看透 Spring MVC 源代码分析与实践 —— 俯视 Spring MVC

Spring MVC 之初体验 环境搭建 在 IDEA 中新建一个 web 项目,用 Maven 管理项目的话,在 pom.xml 中加入 Spring MVC...

42760
来自专栏linux驱动个人学习

高通HAL层之bmp18x.cpp

下面bmp18x sensor为例进行分析。 BMP18x Sensor: 我们以打开bmp180为例,代码为hardware\qcom\sensors\Bmp...

42070
来自专栏Java成神之路

Java企业微信开发_05_消息推送之被动回复消息

微信加解密包 下载地址:http://qydev.weixin.qq.com/java.zip      ,此包中封装好了AES加解密方法,直接调用方法即可。

38720
来自专栏潇涧技术专栏

Lint Tool Analysis (2)

本系列的几篇源码分析文档意义不大,如果你正好也在研究lint源码,或者你想知道前面自定义lint规则中提出的那几个问题,抑或你只是想大致了解下lint的源码都有...

20910
来自专栏MasiMaro 的技术博文

Windows内核函数

在驱动中一般使用的是ANSI字符串和宽字节字符串,在驱动中我们仍然可以使用C中提供的字符串操作函数,但是在DDK中不提倡这样做,由于C函数容易导致缓冲区溢出漏洞...

18240
来自专栏chenssy

【追光者系列】HikariCP源码分析之evict、时钟回拨、连接创建生命周期

evict定义在com.zaxxer.hikari.pool.PoolEntry中,evict的汉语意思是驱逐、逐出,用来标记连接池中的连接不可用。

50140
来自专栏java 成神之路

Spring mvc 内置编码过滤器原理解析

392110
来自专栏码匠的流水账

JCTools简介

JCTools是一款对jdk并发数据结构进行增强的并发工具,主要提供了map以及queue的增强数据结构。原来netty还是自己写的MpscLinkedQueu...

22410

扫码关注云+社区

领取腾讯云代金券