1 // Module:  Log4cplus
2 // File:    clfsappender.cxx
3 // Created: 5/2012
4 // Author:  Vaclav Zeman
5 //
6 //
7 //  Copyright (C) 2012, 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 "dcmtk/oflog/config.h"
31 #include "dcmtk/oflog/helpers/loglog.h"
32 #include "dcmtk/oflog/helpers/property.h"
33 #include "dcmtk/oflog/clfsap.h"
34 #include "dcmtk/oflog/spi/factory.h"
35 #include <sstream>
36 #include <iomanip>
37 #include <cstring>
38 #include "dcmtk/oflog/config/windowsh.h"
39 #include <clfsw32.h>
40 #include <clfsmgmtw32.h>
41 
42 
43 // Forward Declarations
44 namespace dcmtk
45 {
46 namespace log4cplus
47 {
48 
49 const unsigned CLFS_APPENDER_INITIAL_LOG_SIZE = 512 * 1024;
50 const ULONG CLFS_APPENDER_DEFAULT_BUFFER_SIZE = 1024 * 64;
51 
52 
53 namespace
54 {
55 
56 static
57 void
loglog_win32_error(tchar const * msg)58 loglog_win32_error (tchar const * msg)
59 {
60     DWORD err = GetLastError ();
61     tostringstream oss;
62     oss << DCMTK_LOG4CPLUS_TEXT ("CLFSAppender: ") << msg << DCMTK_LOG4CPLUS_TEXT(": ")
63         << err << DCMTK_LOG4CPLUS_TEXT (" / 0x")
64         << STD_NAMESPACE setw (8) << STD_NAMESPACE setfill (DCMTK_LOG4CPLUS_TEXT ('0')) << STD_NAMESPACE hex
65         << err;
66     helpers::getLogLog ().error(OFString(oss.str().c_str(), oss.str().length()));
67 }
68 
69 }
70 
71 
72 struct CLFSAppender::Data
73 {
Datadcmtk::log4cplus::CLFSAppender::Data74     Data ()
75         : log_name ()
76         , log_handle (INVALID_HANDLE_VALUE)
77         , buffer (0)
78         , buffer_size (0)
79     { }
80 
81     tstring log_name;
82     HANDLE log_handle;
83     void * buffer;
84     ULONG buffer_size;
85 };
86 
87 
CLFSAppender(tstring const & logname,unsigned long logsize,unsigned long buffersize)88 CLFSAppender::CLFSAppender (tstring const & logname, unsigned long logsize,
89     unsigned long buffersize)
90     : Appender ()
91     , data (new Data)
92 {
93     init (logname, logsize, buffersize);
94 }
95 
96 
CLFSAppender(helpers::Properties const & props)97 CLFSAppender::CLFSAppender (helpers::Properties const & props)
98     : Appender (props)
99     , data (new Data)
100 {
101     tstring logname = props.getProperty (DCMTK_LOG4CPLUS_TEXT ("LogName"));
102 
103     unsigned long logsize = CLFS_APPENDER_INITIAL_LOG_SIZE;
104     props.getULong (logsize, DCMTK_LOG4CPLUS_TEXT ("LogSize"));
105 
106     unsigned long buffersize = CLFS_APPENDER_DEFAULT_BUFFER_SIZE;
107     props.getULong (buffersize, DCMTK_LOG4CPLUS_TEXT ("BufferSize"));
108 
109     init (logname, logsize, buffersize);
110 }
111 
112 
~CLFSAppender()113 CLFSAppender::~CLFSAppender ()
114 {
115     destructorImpl ();
116     delete data;
117 }
118 
119 
120 void
init(tstring const & logname,unsigned long logsize,unsigned long buffersize)121 CLFSAppender::init (tstring const & logname, unsigned long logsize,
122     unsigned long buffersize)
123 {
124     data->log_name = logname;
125     data->buffer_size = buffersize;
126 
127     if (data->log_name.empty ())
128         helpers::getLogLog ().error (
129             DCMTK_LOG4CPLUS_TEXT ("CLFSAppender: empty log name"), true);
130 
131     CLFS_MGMT_POLICY log_policy;
132     memset (&log_policy, 0, sizeof (log_policy));
133     log_policy.Version = CLFS_MGMT_POLICY_VERSION;
134     log_policy.LengthInBytes = sizeof (log_policy);
135     log_policy.PolicyFlags = 0;
136 
137     CLFS_INFORMATION log_info;
138     ULONG info_size = sizeof (log_info);
139     ULONGLONG desired_size;
140     ULONGLONG resulting_size;
141 
142     data->log_handle = CreateLogFile (
143         helpers::towstring (data->log_name).c_str (), GENERIC_WRITE | GENERIC_READ,
144         FILE_SHARE_DELETE | FILE_SHARE_WRITE | FILE_SHARE_READ, 0,
145         OPEN_ALWAYS, FILE_ATTRIBUTE_ARCHIVE);
146 
147     if (data->log_handle == INVALID_HANDLE_VALUE)
148     {
149         loglog_win32_error (DCMTK_LOG4CPLUS_TEXT ("CreateLogFile()"));
150         goto error;
151     }
152 
153     if (! RegisterManageableLogClient (data->log_handle, 0))
154     {
155         loglog_win32_error (DCMTK_LOG4CPLUS_TEXT ("RegisterManageableLogClient()"));
156         goto error;
157     }
158 
159 
160     if (! GetLogFileInformation (data->log_handle, &log_info, &info_size))
161     {
162         loglog_win32_error (DCMTK_LOG4CPLUS_TEXT ("GetLogFileInformation()"));
163         goto error;
164     }
165 
166     if (log_info.TotalContainers == 0)
167     {
168         log_policy.PolicyType = ClfsMgmtPolicyNewContainerSize;
169         log_policy.PolicyParameters.NewContainerSize.SizeInBytes = logsize;
170         if (! InstallLogPolicy (data->log_handle, &log_policy))
171         {
172             loglog_win32_error (
173                 DCMTK_LOG4CPLUS_TEXT ("InstallLogPolicy(ClfsMgmtPolicyNewContainerSize)"));
174             goto error;
175         }
176     }
177 
178     desired_size = 0;
179     resulting_size = 0;
180     if (! SetLogFileSizeWithPolicy (data->log_handle, &desired_size,
181         &resulting_size))
182     {
183         loglog_win32_error (DCMTK_LOG4CPLUS_TEXT ("SetLogFileSizeWithPolicy()"));
184         goto error;
185     }
186 
187     log_policy.PolicyType = ClfsMgmtPolicyAutoGrow;
188     log_policy.PolicyParameters.AutoGrow.Enabled = true;
189     if (! InstallLogPolicy (data->log_handle, &log_policy))
190     {
191         loglog_win32_error (
192             DCMTK_LOG4CPLUS_TEXT ("InstallLogPolicy(ClfsMgmtPolicyAutoGrow)"));
193         goto error;
194     }
195 
196     log_policy.PolicyType = ClfsMgmtPolicyGrowthRate;
197     log_policy.PolicyParameters.GrowthRate.AbsoluteGrowthInContainers = 0;
198     log_policy.PolicyParameters.GrowthRate.RelativeGrowthPercentage = 10;
199     if (! InstallLogPolicy (data->log_handle, &log_policy))
200     {
201         loglog_win32_error (
202             DCMTK_LOG4CPLUS_TEXT ("InstallLogPolicy(ClfsMgmtPolicyGrowthRate)"));
203         goto error;
204     }
205 
206     // TODO: Get underlying media sector size using GetDiskFreeSpace().
207     // TODO: What are reasonable values for cMaxWriteBuffers
208     // and cMaxReadBuffers?
209     if (! CreateLogMarshallingArea (data->log_handle, 0, 0, 0,
210         data->buffer_size, 8, 1, &data->buffer))
211     {
212         loglog_win32_error (DCMTK_LOG4CPLUS_TEXT ("CreateLogMarshallingArea"));
213         goto error;
214     }
215 
216     return;
217 
218 error:
219     if (data->log_handle != INVALID_HANDLE_VALUE)
220     {
221         CloseHandle (data->log_handle);
222         data->log_handle = INVALID_HANDLE_VALUE;
223     }
224 
225     return;
226 }
227 
228 
229 void
close()230 CLFSAppender::close ()
231 {
232     if (data->log_handle != INVALID_HANDLE_VALUE)
233     {
234         CloseHandle (data->log_handle);
235         data->log_handle = INVALID_HANDLE_VALUE;
236     }
237 }
238 
239 
240 void
append(spi::InternalLoggingEvent const & ev)241 CLFSAppender::append (spi::InternalLoggingEvent const & ev)
242 {
243     if (data->log_handle == INVALID_HANDLE_VALUE)
244         return;
245 
246     // TODO: Expose log4cplus' internal TLS to use here.
247     tostringstream oss;
248     layout->formatAndAppend(oss, ev);
249 
250     tstring str = OFString(oss.str().c_str(), oss.str().length());
251     //oss.str ().swap (str);
252     if ((str.size () + 1) * sizeof (tchar) > data->buffer_size)
253         str.resize (data->buffer_size / sizeof (tchar));
254 
255     CLFS_WRITE_ENTRY clfs_write_entry;
256     clfs_write_entry.Buffer = OFconst_cast(tchar *, str.c_str ());
257     clfs_write_entry.ByteLength
258         = OFstatic_cast(ULONG, (str.size () + 1) * sizeof (tchar));
259 
260     if (! ReserveAndAppendLog (data->buffer, &clfs_write_entry, 1, 0, 0, 0, 0,
261         CLFS_FLAG_FORCE_APPEND, 0, 0))
262         loglog_win32_error (DCMTK_LOG4CPLUS_TEXT ("ReserveAndAppendLog"));
263 }
264 
265 
266 void
registerAppender()267 CLFSAppender::registerAppender ()
268 {
269     dcmtk::log4cplus::spi::AppenderFactoryRegistry & reg
270         = dcmtk::log4cplus::spi::getAppenderFactoryRegistry ();
271     DCMTK_LOG4CPLUS_REG_APPENDER (reg, CLFSAppender);
272 }
273 
274 
275 } // namespace log4cplus
276 } // end namespace dcmtk
277 
278 
279 extern "C"
DllMain(DCMTK_LOG4CPLUS_DLLMAIN_HINSTANCE,DWORD fdwReason,LPVOID)280 BOOL WINAPI DllMain(DCMTK_LOG4CPLUS_DLLMAIN_HINSTANCE,  // handle to DLL module
281                     DWORD fdwReason,     // reason for calling function
282                     LPVOID)  // reserved
283 {
284     // Perform actions based on the reason for calling.
285     switch( fdwReason )
286     {
287     case DLL_PROCESS_ATTACH:
288     {
289         dcmtk::log4cplus::CLFSAppender::registerAppender ();
290         break;
291     }
292 
293     case DLL_THREAD_ATTACH:
294         break;
295 
296     case DLL_THREAD_DETACH:
297         break;
298 
299     case DLL_PROCESS_DETACH:
300         break;
301     }
302 
303     return TRUE;  // Successful DLL_PROCESS_ATTACH.
304 }
305