blocxx

MultiProcessFileAppender.cpp

Go to the documentation of this file.
00001 /*******************************************************************************
00002 * Copyright (C) 2005, Quest Software, Inc. All rights reserved.
00003 * Copyright (C) 2006, Novell, Inc. All rights reserved.
00004 * 
00005 * Redistribution and use in source and binary forms, with or without
00006 * modification, are permitted provided that the following conditions are met:
00007 * 
00008 *     * Redistributions of source code must retain the above copyright notice,
00009 *       this list of conditions and the following disclaimer.
00010 *     * Redistributions in binary form must reproduce the above copyright
00011 *       notice, this list of conditions and the following disclaimer in the
00012 *       documentation and/or other materials provided with the distribution.
00013 *     * Neither the name of 
00014 *       Quest Software, Inc., 
00015 *       nor Novell, Inc., 
00016 *       nor the names of its contributors or employees may be used to 
00017 *       endorse or promote products derived from this software without 
00018 *       specific prior written permission.
00019 * 
00020 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
00021 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
00022 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
00023 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
00024 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
00025 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
00026 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
00027 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
00028 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
00029 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
00030 * POSSIBILITY OF SUCH DAMAGE.
00031 *******************************************************************************/
00032 
00033 
00038 #include "blocxx/BLOCXX_config.h"
00039 #include "blocxx/MultiProcessFileAppender.hpp"
00040 #include "blocxx/Format.hpp"
00041 #include "blocxx/Logger.hpp"
00042 #include "blocxx/LogMessage.hpp"
00043 #include "blocxx/FileSystem.hpp"
00044 
00045 #include <fcntl.h>
00046 #ifdef BLOCXX_HAVE_SYS_STAT_H
00047 #include <sys/stat.h>
00048 #endif
00049 
00050 namespace BLOCXX_NAMESPACE
00051 {
00052 
00053 namespace
00054 {
00055    using namespace blocxx;
00056 
00057    class FileLock
00058    {
00059    public:
00060       FileLock(File & file)
00061       : m_file(file)
00062       {
00063          m_ok = file.getLock() == 0;
00064       }
00065 
00066       bool ok() const
00067       {
00068          return m_ok;
00069       }
00070 
00071       ~FileLock()
00072       {
00073          if (m_ok)
00074          {
00075             m_file.unlock(); // nowhere to report error if unlock fails...
00076          }
00077       }
00078 
00079    private:
00080       File & m_file;
00081       bool m_ok;
00082    };
00083 
00084    typedef blocxx::MultiProcessFileAppender app_t;
00085 
00086    UInt64 kbytesToBytes(UInt64 max_size)
00087    {
00088       UInt64 const oneK = 1024;
00089       UInt64 const biggest = UInt64(-1); // largest possible UInt64
00090       return(
00091          max_size == app_t::NO_MAX_LOG_SIZE ? biggest
00092          : max_size > biggest / oneK ? biggest
00093          : max_size * oneK
00094       );
00095    }
00096 
00097 }
00098 
00099 MultiProcessFileAppender::MultiProcessFileAppender(
00100    const StringArray& components,
00101    const StringArray& categories,
00102    const String & filename,
00103    const String& pattern,
00104    UInt64 maxFileSize,
00105    UInt32 maxBackupIndex
00106 )
00107    : LogAppender(components, categories, pattern)
00108    , m_filename(filename)
00109    , m_maxFileSize(kbytesToBytes(maxFileSize))
00110    , m_maxBackupIndex(maxBackupIndex)
00111 {
00112    m_log = FileSystem::openForAppendOrCreateFile(filename);
00113    m_lock = FileSystem::openOrCreateFile(filename + ".lock");
00114 
00115    if (!m_log)
00116    {
00117       String msg = "Cannot create log file " + filename;
00118       BLOCXX_THROW(LoggerException, msg.c_str());
00119    }
00120    if (!m_lock)
00121    {
00122       String msg = "Cannot create lock file for log file " + filename + ".lock";
00123       BLOCXX_THROW(LoggerException, msg.c_str());
00124    }
00125 }
00126 
00127 MultiProcessFileAppender::~MultiProcessFileAppender()
00128 {
00129 }
00130 
00131 void MultiProcessFileAppender::doProcessLogMessage(
00132    const String& formattedMessage, const LogMessage& message) const
00133 {
00134    // On error, we just return, since doProcessLogMessage should not throw
00135 
00136    if (!m_log || !m_lock)
00137    {
00138       return;
00139    }
00140 
00141    FileLock lock(m_lock);
00142    if (!lock.ok())
00143    {
00144       return;
00145    }
00146 
00147    UInt64 size = m_log.size();
00148 
00149    if (size >= m_maxFileSize)
00150    {
00151       // Log file has been rotated
00152       File f = FileSystem::openForAppendOrCreateFile(m_filename);
00153       f.swap(m_log);
00154       if (!m_log)
00155       {
00156          return;
00157       }
00158       size = m_log.size();
00159    }
00160 
00161    String s = formattedMessage + "\n";
00162    m_log.write(s.c_str(), s.length());
00163    m_log.flush();
00164    size += s.length();
00165 
00166    // handle log rotation
00167    if (size >= m_maxFileSize)
00168    {
00169       // do the roll over
00170       m_log.close();
00171 
00172       if (m_maxBackupIndex > 0)
00173       {
00174          // delete the oldest file first, if it exists
00175          FileSystem::removeFile(m_filename + '.' + String(m_maxBackupIndex));
00176 
00177          // increment the numbers on all the files, if they exist
00178          for (UInt32 i = m_maxBackupIndex - 1; i >= 1; --i)
00179          {
00180             FileSystem::renameFile(m_filename + '.' + String(i), m_filename + '.' + String(i + 1));
00181          }
00182 
00183          if (!FileSystem::renameFile(m_filename, m_filename + ".1"))
00184          {
00185             // if we can't rename it, at least don't write to it anymore
00186             return;
00187          }
00188       }
00189 
00190       // open new log file
00191       File f = FileSystem::openForAppendOrCreateFile(m_filename);
00192       f.swap(m_log);
00193    }
00194 }
00195 
00196 } // end namespace BLOCXX_NAMESPACE
00197 
00198 
00199 
00200