// Module: Log4cplus // File: clfsappender.cxx // Created: 5/2012 // Author: Vaclav Zeman // // // Copyright (C) 2012-2013, Vaclav Zeman. All rights reserved. // // Redistribution and use in source and binary forms, with or without modifica- // tion, are permitted provided that the following conditions are met: // // 1. Redistributions of source code must retain the above copyright notice, // this list of conditions and the following disclaimer. // // 2. Redistributions in binary form must reproduce the above copyright notice, // this list of conditions and the following disclaimer in the documentation // and/or other materials provided with the distribution. // // THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, // INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND // FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE // APACHE SOFTWARE FOUNDATION OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, // INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLU- // DING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS // OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON // ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF // THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include #include #include #include #include #include #include #include #include #include // Forward Declarations namespace log4cplus { const unsigned CLFS_APPENDER_INITIAL_LOG_SIZE = 512 * 1024; const ULONG CLFS_APPENDER_DEFAULT_BUFFER_SIZE = 1024 * 64; namespace { static void loglog_win32_error (tchar const * msg) { DWORD err = GetLastError (); tostringstream oss; oss << LOG4CPLUS_TEXT ("CLFSAppender: ") << msg << LOG4CPLUS_TEXT(": ") << err << LOG4CPLUS_TEXT (" / 0x") << std::setw (8) << std::setfill (LOG4CPLUS_TEXT ('0')) << std::hex << err; helpers::getLogLog ().error (oss.str ()); } } struct CLFSAppender::Data { Data () : log_name () , log_handle (INVALID_HANDLE_VALUE) , buffer (0) , buffer_size (0) { } tstring log_name; HANDLE log_handle; void * buffer; ULONG buffer_size; }; CLFSAppender::CLFSAppender (tstring const & logname, unsigned long logsize, unsigned long buffersize) : Appender () , data (new Data) { init (logname, logsize, buffersize); } CLFSAppender::CLFSAppender (helpers::Properties const & props) : Appender (props) , data (new Data) { tstring logname = props.getProperty (LOG4CPLUS_TEXT ("LogName")); unsigned long logsize = CLFS_APPENDER_INITIAL_LOG_SIZE; props.getULong (logsize, LOG4CPLUS_TEXT ("LogSize")); unsigned long buffersize = CLFS_APPENDER_DEFAULT_BUFFER_SIZE; props.getULong (buffersize, LOG4CPLUS_TEXT ("BufferSize")); init (logname, logsize, buffersize); } CLFSAppender::~CLFSAppender () { destructorImpl (); delete data; } void CLFSAppender::init (tstring const & logname, unsigned long logsize, unsigned long buffersize) { data->log_name = logname; data->buffer_size = buffersize; if (data->log_name.empty ()) helpers::getLogLog ().error ( LOG4CPLUS_TEXT ("CLFSAppender: empty log name"), true); CLFS_MGMT_POLICY log_policy; std::memset (&log_policy, 0, sizeof (log_policy)); log_policy.Version = CLFS_MGMT_POLICY_VERSION; log_policy.LengthInBytes = sizeof (log_policy); log_policy.PolicyFlags = 0; CLFS_INFORMATION log_info; ULONG info_size = sizeof (log_info); ULONGLONG desired_size; ULONGLONG resulting_size; data->log_handle = CreateLogFile ( helpers::towstring (data->log_name).c_str (), GENERIC_WRITE | GENERIC_READ, FILE_SHARE_DELETE | FILE_SHARE_WRITE | FILE_SHARE_READ, 0, OPEN_ALWAYS, FILE_ATTRIBUTE_ARCHIVE); if (data->log_handle == INVALID_HANDLE_VALUE) { loglog_win32_error (LOG4CPLUS_TEXT ("CreateLogFile()")); goto error; } if (! RegisterManageableLogClient (data->log_handle, 0)) { loglog_win32_error (LOG4CPLUS_TEXT ("RegisterManageableLogClient()")); goto error; } if (! GetLogFileInformation (data->log_handle, &log_info, &info_size)) { loglog_win32_error (LOG4CPLUS_TEXT ("GetLogFileInformation()")); goto error; } if (log_info.TotalContainers == 0) { log_policy.PolicyType = ClfsMgmtPolicyNewContainerSize; log_policy.PolicyParameters.NewContainerSize.SizeInBytes = logsize; if (! InstallLogPolicy (data->log_handle, &log_policy)) { loglog_win32_error ( LOG4CPLUS_TEXT ("InstallLogPolicy(ClfsMgmtPolicyNewContainerSize)")); goto error; } } desired_size = 0; resulting_size = 0; if (! SetLogFileSizeWithPolicy (data->log_handle, &desired_size, &resulting_size)) { loglog_win32_error (LOG4CPLUS_TEXT ("SetLogFileSizeWithPolicy()")); goto error; } log_policy.PolicyType = ClfsMgmtPolicyAutoGrow; log_policy.PolicyParameters.AutoGrow.Enabled = true; if (! InstallLogPolicy (data->log_handle, &log_policy)) { loglog_win32_error ( LOG4CPLUS_TEXT ("InstallLogPolicy(ClfsMgmtPolicyAutoGrow)")); goto error; } log_policy.PolicyType = ClfsMgmtPolicyGrowthRate; log_policy.PolicyParameters.GrowthRate.AbsoluteGrowthInContainers = 0; log_policy.PolicyParameters.GrowthRate.RelativeGrowthPercentage = 10; if (! InstallLogPolicy (data->log_handle, &log_policy)) { loglog_win32_error ( LOG4CPLUS_TEXT ("InstallLogPolicy(ClfsMgmtPolicyGrowthRate)")); goto error; } // TODO: Get underlying media sector size using GetDiskFreeSpace(). // TODO: What are reasonable values for cMaxWriteBuffers // and cMaxReadBuffers? if (! CreateLogMarshallingArea (data->log_handle, 0, 0, 0, data->buffer_size, 8, 1, &data->buffer)) { loglog_win32_error (LOG4CPLUS_TEXT ("CreateLogMarshallingArea")); goto error; } return; error: if (data->log_handle != INVALID_HANDLE_VALUE) { CloseHandle (data->log_handle); data->log_handle = INVALID_HANDLE_VALUE; } return; } void CLFSAppender::close () { if (data->log_handle != INVALID_HANDLE_VALUE) { CloseHandle (data->log_handle); data->log_handle = INVALID_HANDLE_VALUE; } } void CLFSAppender::append (spi::InternalLoggingEvent const & ev) { if (data->log_handle == INVALID_HANDLE_VALUE) return; // TODO: Expose log4cplus' internal TLS to use here. tostringstream oss; layout->formatAndAppend(oss, ev); tstring str; oss.str ().swap (str); if ((str.size () + 1) * sizeof (tchar) > data->buffer_size) str.resize (data->buffer_size / sizeof (tchar)); CLFS_WRITE_ENTRY clfs_write_entry; clfs_write_entry.Buffer = const_cast(str.c_str ()); clfs_write_entry.ByteLength = static_cast((str.size () + 1) * sizeof (tchar)); if (! ReserveAndAppendLog (data->buffer, &clfs_write_entry, 1, 0, 0, 0, 0, CLFS_FLAG_FORCE_APPEND, 0, 0)) loglog_win32_error (LOG4CPLUS_TEXT ("ReserveAndAppendLog")); } void CLFSAppender::registerAppender () { log4cplus::spi::AppenderFactoryRegistry & reg = log4cplus::spi::getAppenderFactoryRegistry (); LOG4CPLUS_REG_APPENDER (reg, CLFSAppender); } } // namespace log4cplus extern "C" BOOL WINAPI DllMain(LOG4CPLUS_DLLMAIN_HINSTANCE, // handle to DLL module DWORD fdwReason, // reason for calling function LPVOID) // reserved { // Perform actions based on the reason for calling. switch( fdwReason ) { case DLL_PROCESS_ATTACH: { log4cplus::CLFSAppender::registerAppender (); break; } case DLL_THREAD_ATTACH: break; case DLL_THREAD_DETACH: break; case DLL_PROCESS_DETACH: break; } return TRUE; // Successful DLL_PROCESS_ATTACH. }