1 // Module:  Log4cplus
2 // File:    clfsappender.cxx
3 // Created: 5/2012
4 // Author:  Vaclav Zeman
5 //
6 //
7 //  Copyright (C) 2012-2013, Vaclav Zeman. All rights reserved.
8 //
9 //  Redistribution and use in source and binary forms, with or without modifica-
10 //  tion, are permitted provided that the following conditions are met:
11 //
12 //  1. Redistributions of  source code must  retain the above copyright  notice,
13 //     this list of conditions and the following disclaimer.
14 //
15 //  2. Redistributions in binary form must reproduce the above copyright notice,
16 //     this list of conditions and the following disclaimer in the documentation
17 //     and/or other materials provided with the distribution.
18 //
19 //  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
20 //  INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
21 //  FITNESS  FOR A PARTICULAR  PURPOSE ARE  DISCLAIMED.  IN NO  EVENT SHALL  THE
22 //  APACHE SOFTWARE  FOUNDATION  OR ITS CONTRIBUTORS  BE LIABLE FOR  ANY DIRECT,
23 //  INDIRECT, INCIDENTAL, SPECIAL,  EXEMPLARY, OR CONSEQUENTIAL  DAMAGES (INCLU-
24 //  DING, BUT NOT LIMITED TO, PROCUREMENT  OF SUBSTITUTE GOODS OR SERVICES; LOSS
25 //  OF USE, DATA, OR  PROFITS; OR BUSINESS  INTERRUPTION)  HOWEVER CAUSED AND ON
26 //  ANY  THEORY OF LIABILITY,  WHETHER  IN CONTRACT,  STRICT LIABILITY,  OR TORT
27 //  (INCLUDING  NEGLIGENCE OR  OTHERWISE) ARISING IN  ANY WAY OUT OF THE  USE OF
28 //  THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 
30 #include <log4cplus/config.hxx>
31 #include <log4cplus/helpers/loglog.h>
32 #include <log4cplus/helpers/property.h>
33 #include <log4cplus/clfsappender.h>
34 #include <log4cplus/spi/factory.h>
35 #include <sstream>
36 #include <iomanip>
37 #include <cstring>
38 #include <log4cplus/config/windowsh-inc.h>
39 #include <clfsw32.h>
40 #include <clfsmgmtw32.h>
41 
42 
43 // Forward Declarations
44 namespace log4cplus
45 {
46 
47 const unsigned CLFS_APPENDER_INITIAL_LOG_SIZE = 512 * 1024;
48 const ULONG CLFS_APPENDER_DEFAULT_BUFFER_SIZE = 1024 * 64;
49 
50 
51 namespace
52 {
53 
54 static
55 void
loglog_win32_error(tchar const * msg)56 loglog_win32_error (tchar const * msg)
57 {
58     DWORD err = GetLastError ();
59     tostringstream oss;
60     oss << LOG4CPLUS_TEXT ("CLFSAppender: ") << msg << LOG4CPLUS_TEXT(": ")
61         << err << LOG4CPLUS_TEXT (" / 0x")
62         << std::setw (8) << std::setfill (LOG4CPLUS_TEXT ('0')) << std::hex
63         << err;
64     helpers::getLogLog ().error (oss.str ());
65 }
66 
67 }
68 
69 
70 struct CLFSAppender::Data
71 {
Datalog4cplus::CLFSAppender::Data72     Data ()
73         : log_name ()
74         , log_handle (INVALID_HANDLE_VALUE)
75         , buffer (0)
76         , buffer_size (0)
77     { }
78 
79     tstring log_name;
80     HANDLE log_handle;
81     void * buffer;
82     ULONG buffer_size;
83 };
84 
85 
CLFSAppender(tstring const & logname,unsigned long logsize,unsigned long buffersize)86 CLFSAppender::CLFSAppender (tstring const & logname, unsigned long logsize,
87     unsigned long buffersize)
88     : Appender ()
89     , data (new Data)
90 {
91     init (logname, logsize, buffersize);
92 }
93 
94 
CLFSAppender(helpers::Properties const & props)95 CLFSAppender::CLFSAppender (helpers::Properties const & props)
96     : Appender (props)
97     , data (new Data)
98 {
99     tstring logname = props.getProperty (LOG4CPLUS_TEXT ("LogName"));
100 
101     unsigned long logsize = CLFS_APPENDER_INITIAL_LOG_SIZE;
102     props.getULong (logsize, LOG4CPLUS_TEXT ("LogSize"));
103 
104     unsigned long buffersize = CLFS_APPENDER_DEFAULT_BUFFER_SIZE;
105     props.getULong (buffersize, LOG4CPLUS_TEXT ("BufferSize"));
106 
107     init (logname, logsize, buffersize);
108 }
109 
110 
~CLFSAppender()111 CLFSAppender::~CLFSAppender ()
112 {
113     destructorImpl ();
114     delete data;
115 }
116 
117 
118 void
init(tstring const & logname,unsigned long logsize,unsigned long buffersize)119 CLFSAppender::init (tstring const & logname, unsigned long logsize,
120     unsigned long buffersize)
121 {
122     data->log_name = logname;
123     data->buffer_size = buffersize;
124 
125     if (data->log_name.empty ())
126         helpers::getLogLog ().error (
127             LOG4CPLUS_TEXT ("CLFSAppender: empty log name"), true);
128 
129     CLFS_MGMT_POLICY log_policy;
130     std::memset (&log_policy, 0, sizeof (log_policy));
131     log_policy.Version = CLFS_MGMT_POLICY_VERSION;
132     log_policy.LengthInBytes = sizeof (log_policy);
133     log_policy.PolicyFlags = 0;
134 
135     CLFS_INFORMATION log_info;
136     ULONG info_size = sizeof (log_info);
137     ULONGLONG desired_size;
138     ULONGLONG resulting_size;
139 
140     data->log_handle = CreateLogFile (
141         helpers::towstring (data->log_name).c_str (), GENERIC_WRITE | GENERIC_READ,
142         FILE_SHARE_DELETE | FILE_SHARE_WRITE | FILE_SHARE_READ, 0,
143         OPEN_ALWAYS, FILE_ATTRIBUTE_ARCHIVE);
144 
145     if (data->log_handle == INVALID_HANDLE_VALUE)
146     {
147         loglog_win32_error (LOG4CPLUS_TEXT ("CreateLogFile()"));
148         goto error;
149     }
150 
151     if (! RegisterManageableLogClient (data->log_handle, 0))
152     {
153         loglog_win32_error (LOG4CPLUS_TEXT ("RegisterManageableLogClient()"));
154         goto error;
155     }
156 
157 
158     if (! GetLogFileInformation (data->log_handle, &log_info, &info_size))
159     {
160         loglog_win32_error (LOG4CPLUS_TEXT ("GetLogFileInformation()"));
161         goto error;
162     }
163 
164     if (log_info.TotalContainers == 0)
165     {
166         log_policy.PolicyType = ClfsMgmtPolicyNewContainerSize;
167         log_policy.PolicyParameters.NewContainerSize.SizeInBytes = logsize;
168         if (! InstallLogPolicy (data->log_handle, &log_policy))
169         {
170             loglog_win32_error (
171                 LOG4CPLUS_TEXT ("InstallLogPolicy(ClfsMgmtPolicyNewContainerSize)"));
172             goto error;
173         }
174     }
175 
176     desired_size = 0;
177     resulting_size = 0;
178     if (! SetLogFileSizeWithPolicy (data->log_handle, &desired_size,
179         &resulting_size))
180     {
181         loglog_win32_error (LOG4CPLUS_TEXT ("SetLogFileSizeWithPolicy()"));
182         goto error;
183     }
184 
185     log_policy.PolicyType = ClfsMgmtPolicyAutoGrow;
186     log_policy.PolicyParameters.AutoGrow.Enabled = true;
187     if (! InstallLogPolicy (data->log_handle, &log_policy))
188     {
189         loglog_win32_error (
190             LOG4CPLUS_TEXT ("InstallLogPolicy(ClfsMgmtPolicyAutoGrow)"));
191         goto error;
192     }
193 
194     log_policy.PolicyType = ClfsMgmtPolicyGrowthRate;
195     log_policy.PolicyParameters.GrowthRate.AbsoluteGrowthInContainers = 0;
196     log_policy.PolicyParameters.GrowthRate.RelativeGrowthPercentage = 10;
197     if (! InstallLogPolicy (data->log_handle, &log_policy))
198     {
199         loglog_win32_error (
200             LOG4CPLUS_TEXT ("InstallLogPolicy(ClfsMgmtPolicyGrowthRate)"));
201         goto error;
202     }
203 
204     // TODO: Get underlying media sector size using GetDiskFreeSpace().
205     // TODO: What are reasonable values for cMaxWriteBuffers
206     // and cMaxReadBuffers?
207     if (! CreateLogMarshallingArea (data->log_handle, 0, 0, 0,
208         data->buffer_size, 8, 1, &data->buffer))
209     {
210         loglog_win32_error (LOG4CPLUS_TEXT ("CreateLogMarshallingArea"));
211         goto error;
212     }
213 
214     return;
215 
216 error:
217     if (data->log_handle != INVALID_HANDLE_VALUE)
218     {
219         CloseHandle (data->log_handle);
220         data->log_handle = INVALID_HANDLE_VALUE;
221     }
222 
223     return;
224 }
225 
226 
227 void
close()228 CLFSAppender::close ()
229 {
230     if (data->log_handle != INVALID_HANDLE_VALUE)
231     {
232         CloseHandle (data->log_handle);
233         data->log_handle = INVALID_HANDLE_VALUE;
234     }
235 }
236 
237 
238 void
append(spi::InternalLoggingEvent const & ev)239 CLFSAppender::append (spi::InternalLoggingEvent const & ev)
240 {
241     if (data->log_handle == INVALID_HANDLE_VALUE)
242         return;
243 
244     // TODO: Expose log4cplus' internal TLS to use here.
245     tostringstream oss;
246     layout->formatAndAppend(oss, ev);
247 
248     tstring str;
249     oss.str ().swap (str);
250     if ((str.size () + 1) * sizeof (tchar) > data->buffer_size)
251         str.resize (data->buffer_size / sizeof (tchar));
252 
253     CLFS_WRITE_ENTRY clfs_write_entry;
254     clfs_write_entry.Buffer = const_cast<tchar *>(str.c_str ());
255     clfs_write_entry.ByteLength
256         = static_cast<ULONG>((str.size () + 1) * sizeof (tchar));
257 
258     if (! ReserveAndAppendLog (data->buffer, &clfs_write_entry, 1, 0, 0, 0, 0,
259         CLFS_FLAG_FORCE_APPEND, 0, 0))
260         loglog_win32_error (LOG4CPLUS_TEXT ("ReserveAndAppendLog"));
261 }
262 
263 
264 void
registerAppender()265 CLFSAppender::registerAppender ()
266 {
267     log4cplus::spi::AppenderFactoryRegistry & reg
268         = log4cplus::spi::getAppenderFactoryRegistry ();
269     LOG4CPLUS_REG_APPENDER (reg, CLFSAppender);
270 }
271 
272 
273 } // namespace log4cplus
274 
275 
276 extern "C"
DllMain(LOG4CPLUS_DLLMAIN_HINSTANCE,DWORD fdwReason,LPVOID)277 BOOL WINAPI DllMain(LOG4CPLUS_DLLMAIN_HINSTANCE,  // handle to DLL module
278                     DWORD fdwReason,     // reason for calling function
279                     LPVOID)  // reserved
280 {
281     // Perform actions based on the reason for calling.
282     switch( fdwReason )
283     {
284     case DLL_PROCESS_ATTACH:
285     {
286         log4cplus::CLFSAppender::registerAppender ();
287         break;
288     }
289 
290     case DLL_THREAD_ATTACH:
291         break;
292 
293     case DLL_THREAD_DETACH:
294         break;
295 
296     case DLL_PROCESS_DETACH:
297         break;
298     }
299 
300     return TRUE;  // Successful DLL_PROCESS_ATTACH.
301 }
302