0

New to Java. I would like to use the logger, but with a different file persistence scheme. Instead of rotating files and overriding, I would like the logs to be created in a time based file system hierarchy, where log files contain logs of the past minute: Example: if a log is generated on 2015-03-08 13:05, it will be placed in log_05.txt under /home/myUser/logs/2015/03/08/13 in other words, the file full path would be /home/myUser/logs/2015/03/08/13/log_05.txt.

Any suggestions?

user4212919
  • 305
  • 1
  • 2
  • 7

2 Answers2

0

This is covered in How to create the log file for each record in a specific format using java util logging framework. You have to modify those examples to create directories since the FileHandler will not create directories. If you are going to create an asynchronous handler, you should follow the advice in Using java.util.logger with a separate thread to write on file.

Community
  • 1
  • 1
jmehrens
  • 10,580
  • 1
  • 38
  • 47
  • Awesome! I edit the answer to include some tips on asynchronous handlers in case you are interested that too. – jmehrens Mar 12 '15 at 18:15
0

I ended up implementing a library. Tested on Linux & Windows. It provides the desired file persistence scheme, and allows asynchronous logging. Would appreciate comments.

package com.signin.ems;

/**
 * The EMSLogger JAR wraps the Java Logger for two purposes:
 * 1. Implement a custom file persistence scheme (other than a single file, or a rotating scheme).
 * In particular, the scheme implemented is one minute files, placed in hourly directories.
 * The file name format is <mm>.log (mm=00..59), and the directory name format is YYYYMMDD24HH.
 * 
 * 2. Logging should be done asynchronously. For this, a dedicated thread is created. When a message is logged,
 * the LogRecord is placed in a BlockingQueue instead of writing the LogRecord to file. The dedicated thread 
 * performs a blocking wait on the queue. Upon retrieving a LogRecord object, it writes the LogRecord to the 
 * proper file 
 *   
 * 
 */

public class EMSLogger 
{
    private static final int m_iQueSize = 100000;
    private static BlockingQueue<LogRecord> m_LogRecordQueue;
    private static EMSLoggerThread m_EMSLoggerThread;
    private static Thread m_thread;
    private static final Logger m_instance = createInstance();

    protected EMSLogger() 
    {
    }

    public static Logger getInstance() {
        return m_instance;
    }

    private static Logger createInstance() 
    {
        MyFileHandler fileHandler  = null;
        Logger LOGGER = null;
        try 
        {
            // initialize the Log queue
            m_LogRecordQueue = new ArrayBlockingQueue<LogRecord>(m_iQueSize);

            // get top level logger
            LOGGER = Logger.getLogger("");
            LOGGER.setLevel(Level.ALL);

            // create our file handler
            fileHandler  = new MyFileHandler(m_LogRecordQueue);
            fileHandler.setLevel(Level.ALL);            
            LOGGER.addHandler(fileHandler);

            // create the logging thread
            m_EMSLoggerThread = new EMSLoggerThread(m_LogRecordQueue, fileHandler);
            m_thread = new Thread(m_EMSLoggerThread);
            m_thread.start();

        } 
        catch (IOException e) 
        {
            e.printStackTrace();
        }       
        return LOGGER;
    }
    public static void Terminate () 
    {
        m_thread.interrupt();
    }
}


public class MyFileHandler extends FileHandler 
{
    private final BlockingQueue<LogRecord> m_queue;
    private BufferedOutputStream m_BufferedOutputStream;
    private String m_RootFolderName;
    private String m_CurrentDirectoryName;
    private String m_CurrentFileName;
    private SimpleDateFormat m_SDfh;
    private SimpleDateFormat m_SDfm;

    public MyFileHandler (BlockingQueue<LogRecord> q) throws IOException, SecurityException 
    {
        super ();
        // use simple formatter. Do not use the default XML
        super.setFormatter (new SimpleFormatter ());

        // get root folder from which to create the log directory hierarchy
        m_RootFolderName = System.getProperty ("user.home") + "/logs";

        // Service can optionally set its name. All hourly directories will
        // be created below the provided name. If no name is given, "Default" 
        // is used
        String sName = System.getProperty ("EMS.ServiceName");
        if (sName != null)
        {
            System.out.println ("EMS.ServiceName = " + sName);
        }
        else
        {
            sName = "Default";
            System.out.println ("Using \"" + sName + "\" as service name");
        }
        m_RootFolderName += "/" + sName;

        // make sure the root folder is created
        new File (m_RootFolderName).mkdirs ();

        // initialize format objects
        m_SDfh = new SimpleDateFormat ("yyyyMMddHH");
        m_SDfm = new SimpleDateFormat ("mm");
        m_CurrentDirectoryName = "";
        m_CurrentFileName = "";
        m_BufferedOutputStream = null;
        m_queue = q;
    }

    // post the record the the queue. Actual writing to the log is done in a dedicated thread
    // note that placing in the queue is done without blocking while waiting for available space 
    @Override
    public void publish (LogRecord record)
    {
        m_queue.offer (record);
    }

    // check if a new file needs to be created
    private void SetCurrentFile ()
    {
        boolean bChangeFile = false;
        Date d = new Date (System.currentTimeMillis());
        String newDirectory = m_RootFolderName + "/" +  m_SDfh.format(d);
        String newFile = m_SDfm.format(d);

        if (!newDirectory.equals(m_CurrentDirectoryName))
        {
            // need to create a new directory and a new file
            m_CurrentDirectoryName = newDirectory;
            new File(m_CurrentDirectoryName).mkdirs();
            bChangeFile = true;
        }
        if (!newFile.equals(m_CurrentFileName))
        {
            // need to create a new file
            m_CurrentFileName = newFile;
            bChangeFile = true;         
        }
        if (bChangeFile)
        {
                try 
                {
                    if (m_BufferedOutputStream != null)
                    {
                        m_BufferedOutputStream.close ();
                    }
                    System.out.println("Creating File: " + m_CurrentDirectoryName + "/" + m_CurrentFileName + ".log");
                    m_BufferedOutputStream = new BufferedOutputStream 
                                                    (new FileOutputStream (m_CurrentDirectoryName + "/" + m_CurrentFileName + ".log", true),2048);

                    this.setOutputStream(m_BufferedOutputStream);
                } 
                catch (IOException e) 
                {
                    e.printStackTrace();
                }                                               
        }
    }

    // method _published is called from the dedicated thread
    public void _publish(LogRecord record)
    {
        // check if a new file needs to be created
        SetCurrentFile ();
        super.publish(record);
    }
}

class EMSLoggerThread implements Runnable 
{
       private final BlockingQueue<LogRecord> m_queue;
       private final MyFileHandler m_MyFileHandler;

       // Constructor
       EMSLoggerThread(BlockingQueue<LogRecord> q, MyFileHandler fh) 
       { 
           m_queue = q;
           m_MyFileHandler = fh;
       }

       public void run() 
       {
           try 
           {
               while (true) 
               {
                   m_MyFileHandler._publish(m_queue.take());
               }
           } 
           catch (InterruptedException ex) 
           {
           }
       }
}
user4212919
  • 305
  • 1
  • 2
  • 7