在Linux中使用Java管理系统日志文件, 使其不超过N GB

如今,大多数应用程序都需要管理自己的日志文件,包括在达到特定大小限制时将其删除。在本文中,我将尝试为这种日志文件管理提出一种解决方案。我还将建议捕获Linux进程输出并将其记录到一些单独的日志文件中的方法。

这篇文章是在我以前的帖子的延续:自动重装配置的,其中我约了应用程序如何照顾它的配置,而无需重新启动应用程序,每当有人改变文件系统中的配置文件,重装的讨论。

这篇文章中的部分:

  • 确定方法
  • 编写bash脚本
  • 编写脚本 Actuator
  • 添加食 Stream 者
  • 查看全部

确定方法

我相信,要把工作做好,我们肯定需要一个bash脚本。Linux有一些非常有用的内置命令,通过命令窗口执行时,这些命令可以立即执行此工作。bash脚本的优势包括将实际应用程序的文件处理程序代码解耦,如果需要任何特定于平台的更改,可以对其进行修改。

编写bash脚本

我已经编写了一个演示脚本。您可以根据需要使用它:

#!/bin/bash
###############################################################################
#
# discCleanUp.sh
#
################################################################################
DIR_PATH=$1
NUM_OF_DAYS=$2
DIR_SIZE=$3
SIZE=1024
DIR_SIZE=$(($DIR_SIZE * $SIZE * $SIZE))
# Command to find the File older then NUM_OF_DAYS and removing them from directory DIR_PATH
find $DIR_PATH -mtime +$NUM_OF_DAYS -exec rm {} ;
#Go to specified directory
cd $DIR_PATH
# Command to get the current directory size.
CURR_DIR_SIZE=`du -sk | cut -f1`
while [[ $CURR_DIR_SIZE -gt DIR_SIZE ]];
do
echo $CURR_DIR_SIZE
FILE=`ls -1tra | head -1`
rm -rf $FILE
# Command to get the current directory size.
CURR_DIR_SIZE=`du -sk | cut -f1`
done
exit 0

在上面的脚本中,DIR_PATH,NUM_OF_DAYS和DIR_SIZE是命令行参数。而SIZE是用于计算目的的常数。在上面的脚本中,第16行将删除早于n天的日志文件。

第22行中,脚本使用du命令检查目录的当前大小。如果大小超过DIR_SIZE(以GB为单位),脚本将开始使用’ ls -1tra |选择最旧的文件head -1 ‘命令,并开始将它们一一删除。它将继续直到目录大小未达到所需的限制。

编写脚本 Actuator

至于本文,核心组件将保留在bash脚本之上,但仍然需要一种从应用程序运行.sh文件的方法。最好的方法是使用线程(如果需要,可以使用执行程序)。该线程将以可配置的速率定期执行以上bash脚本。我不会写那部分代码。如果执行代码时遇到任何问题,请写下注解。

让我给你一个示例代码::

package corejava.dischandler;
import java.io.IOException;
/**
* Title: DiskFileManager.java
* Description :- This class provides API to check file size and file time threshold and
* if threshold is crossed then deletes file to come within threshold.
*/
public final class DiskFileManager
{
    private static DiskFileManager diskFileManager=new DiskFileManager();
    private DiskFileManager()
    {
    }
    /**
    * <pre>
    * <b>checkAndDeleteFiles</b>
    * <b>Description:This method is called to clean the files from the file system.</b>
    * </pre>
    * @throws InterruptedException
    * @throws IOException
    * @throws InstantiationException
    */
    public   void checkAndDeleteFiles() throws InterruptedException, IOException, InstantiationException
    {
        try
        {
            StringBuffer outStr = new StringBuffer();
            StringBuffer errStr = new StringBuffer();
            String scriptFileName = "./discfilehandler.sh";
            String command = scriptFileName + "/logs 1 1";
            System.out.println("Command to be executed  :- " + command);
            Process output = Runtime.getRuntime().exec(command);
            StreamEater errorEater = new StreamEater(output.getErrorStream(),errStr);
            StreamEater outputEater = new StreamEater(output.getInputStream(),outStr);
            errorEater.start();
            outputEater.start();
            try
            {
                int ret = output.waitFor();
                errorEater.join();
                outputEater.join();
                System.out.println();
                //logger.info("execute(): Error Stream:" + errStr + " ; execute(): Output Stream:" + outStr + " ; execute(): ExitValue:" + ret);
            } catch (InterruptedException e)
            {
                throw e;
            }
        } catch (IOException e)
        {
            throw e;
        }
    }
    /**
    * <pre>
    * <b>getInstance</b>
    * <b>Description:This method is used to get the instance of Disk File Manager.</b>
    * </pre>
    * @return
    */
    public static DiskFileManager getInstance()
    {
        return diskFileManager;
    }
}

上面的代码将在类路径中找到脚本文件,并传递所需的参数。在我们的例子中,“ / logs 1 1”是3个参数,这意味着

  • 要监视的目录是/ logs,
  • 删除一天的日志文件,然后
  • 请勿在任何时候存储超过1 GB的日志文件。

添加食 Stream 者

因此,我们已经添加了一个bash脚本和一个用于运行脚本的执行程序代码。您必须编写自己的线程才能在执行程序上方定期运行。

现在,我们将研究StreamEater,它实际上收集命令输出并提供给您的应用程序代码,因此应用程序可以记录它或执行所需的任何操作。

我们已经在上面的 Actuator 中使用过StreamEater,现在我给出其代码:

package corejava.dischandler;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
public class StreamEater extends Thread
{
    /**
    * Stream Eater thread to eat up every thing that is the output to the
    * execute command
    */
    private InputStream  iStream;
    public InputStream getiStream() {
        return iStream;
    }
    public void setiStream(InputStream iStream) {
        this.iStream = iStream;
    }
    private StringBuffer stringBuffer;
    public StringBuffer getStringBuffer() {
        return stringBuffer;
    }
    public void setStringBuffer(StringBuffer stringBuffer) {
        this.stringBuffer = stringBuffer;
    }
    /**
    * This is the constructor.
    *
    * @param is - It is the input stream
    * @param type - type of input stream
    * @param redirect - string buffer to contain the output.
    * @throws InstantiationException
    */
    public StreamEater(InputStream is, StringBuffer redirect) throws InstantiationException
    {
        this.iStream = is;
        if (redirect == null)
        {
            throw new InstantiationException("String Buffer Reference , second param can not be null");
        }
        this.stringBuffer = redirect;
    }
    /**
    * This is method called when the thread is started. It captures the stream
    * output and and store it into the buffer.
    */
    public void run()
    {
        InputStreamReader isr = null;
        BufferedReader br = null;
        try
        {
            isr = new InputStreamReader(iStream);
            br = new BufferedReader(isr);
            String line;
            while ((line = br.readLine()) != null)
            {
                stringBuffer.append(line).append(System.getProperty("line.separator"));
            }
        }
        catch (Exception ex)
        {
            ex.printStackTrace();
        }
        finally
        {
        if (isr != null)
        {
            try
            {
                isr.close();
            }
            catch (Exception e)
            {
                e.printStackTrace();
            }
        }
        if (br != null)
        {
            try
            {
                br.close();
            }
            catch (Exception e)
            {
                e.printStackTrace();
            }
        }
        }
    }
}

如您所见,这些都是等待收集进程输出的小线程。

查看全部

使用上述类和给定的bash脚本,您可以轻松开发一些应用程序组件,以确保日志文件的大小不超过某些预定义的数目。当您的应用程序负责删除实际的日志文件时,可以在任何级别对​​其进行自定义。

希望您在这篇文章中得到一些有用的信息。如果需要任何帮助或有任何疑问,请给我写信。

学习愉快!

saigon has written 1440 articles

Leave a Reply