source: libcf++/trunk/src/logger.cpp@ 4

Last change on this file since 4 was 4, checked in by cheese, 9 years ago

#1 commit prototype

File size: 7.8 KB
Line 
1/**
2 * @file logger.cpp
3 * @author myusgun@gmail.com
4 * @brief logger
5 */
6#include "cf/logger.h"
7#include "cf/codec.h"
8#include "cf/file.h"
9#include "cf/task.h"
10#include "cf/util.h"
11
12#ifdef _ON_WINDOWS
13# include <direct.h>
14#else
15# include <sys/stat.h>
16# include <sys/types.h>
17#endif
18
19#include <errno.h>
20#include <string.h>
21
22#define LOG_SUFFIX ".log"
23#define DATE_NUM(_d) ((_d.mYear * 10000) + (_d.mMonth * 100) + (_d.mDay))
24
25/*--------------------------------------------------------------*/
26
27static std::string getFullPath(const std::string & path,
28 const std::string & prefix,
29 const cf::int32_t option)
30 throw (cf::exception)
31{
32 try
33 {
34 std::string filepath = path + cf::file::getDelimiter() + prefix;
35
36 /* OPTINAL : append date if daily-rotated */
37 if (option & cf::logger::DAILY)
38 {
39 cf::util::datetime dt = cf::util::getInstance()->getDateTime();
40
41 cf::char_t buf[16] = {0x00,};
42 snprintf(buf, sizeof(buf) - 1,
43 ".%04d%02d%02d",
44 dt.mYear,
45 dt.mMonth,
46 dt.mDay);
47
48 filepath += buf;
49 }
50
51 filepath += STR("." << cf::task::process::id());
52
53 /* FORCED : append file-extension */
54 filepath += LOG_SUFFIX;
55
56 return filepath;
57 }
58 catch (cf::exception &e)
59 {
60 FORWARD_EXCEPTION(e);
61 }
62}
63
64/*--------------------------------------------------------------*/
65
66cf::logger::logger()
67 throw (cf::exception)
68{
69 /* none : level 0 */
70 cf::logger::LOG_CTX ctx;
71
72 ctx.mOption = 0;
73 memset(&ctx.mOptionParam, 0x00, sizeof(ctx.mOptionParam));
74 ctx.mFP = NULL;
75
76 mLogPool.push_back(ctx);
77}
78
79cf::logger::~logger()
80{
81 /* close fp / release-vector-item */
82 std::vector<FILE *> fpPool;
83 std::vector<FILE *>::iterator fpIter;
84 std::vector<cf::logger::LOG_CTX>::iterator ctxIter;
85
86 for (ctxIter = mLogPool.begin() ; ctxIter != mLogPool.end() ; ctxIter++)
87 {
88 cf::bool_t found = false;
89 for (fpIter = fpPool.begin() ; fpIter != fpPool.end() ; fpIter++)
90 {
91 if (ctxIter->mFP == (*fpIter))
92 {
93 found = true;
94 break;
95 }
96 }
97
98 if (!found)
99 fpPool.push_back(ctxIter->mFP);
100 }
101
102 for (fpIter = fpPool.begin() ; fpIter != fpPool.end() ; fpIter++)
103 {
104 if (*fpIter)
105 fclose(*fpIter);
106 }
107}
108
109cf::logger * cf::logger::getInstance()
110{
111 static logger instance;
112
113 return &instance;
114}
115
116cf::void_t cf::logger::rotateByDate(const cf::int32_t level,
117 const cf::util::datetime & dt)
118 throw (cf::exception)
119{
120 try
121 {
122 /* check datetime */
123 cf::int32_t current = DATE_NUM(dt);
124
125 if (mLogPool[level].mOptionParam.mCurrentDateTime == current)
126 return;
127
128 open(level);
129
130 /* replace datetime */
131 std::vector<cf::logger::LOG_CTX>::iterator iter;
132 for (iter = mLogPool.begin() ; iter != mLogPool.end() ; iter++)
133 {
134 if (iter->mFullPath == mLogPool[level].mFullPath)
135 iter->mOptionParam.mCurrentDateTime = current;
136 }
137 }
138 catch (cf::exception &e)
139 {
140 FORWARD_EXCEPTION(e);
141 }
142}
143
144cf::void_t cf::logger::rotateBySize(const cf::int32_t level)
145 throw (cf::exception)
146{
147 try
148 {
149 /* check size */
150 cf::file logFile(mLogPool[level].mFullPath.c_str());
151 if (logFile.size() < mLogPool[level].mOptionParam.mMaxLogSize)
152 return;
153
154 rename(level);
155 open(level);
156 }
157 catch (cf::exception &e)
158 {
159 FORWARD_EXCEPTION(e);
160 }
161}
162
163cf::void_t cf::logger::open(const cf::int32_t level)
164 throw (cf::exception)
165{
166 try
167 {
168 if (!mOpenMutex.trylock())
169 {
170 while (mOpenMutex.locked())
171 /* waiting */;
172
173 return;
174 }
175
176 if (!cf::file(mPath.c_str()).exists())
177 cf::file::makedir(mPath.c_str());
178
179 std::string prefix = mLogPool[level].mPrefix;
180 const cf::int32_t option = mLogPool[level].mOption;
181
182 std::string fullpath = getFullPath(mPath, prefix, option);
183
184#ifdef _ON_WINDOWS
185# define FOPEN_MODE "ab+"
186#else
187# define FOPEN_MODE "a+"
188#endif
189 FILE * fp = fopen(fullpath.c_str(), FOPEN_MODE);
190 if (!fp)
191 THROW_EXCEPTION("cannot open logfile {" << fullpath << "} ("
192 << errno << ":" << strerror(errno) << ")");
193
194 replace(prefix, option, fp);
195
196 mOpenMutex.unlock();
197 }
198 catch (cf::exception &e)
199 {
200 mOpenMutex.unlock();
201 FORWARD_EXCEPTION(e);
202 }
203}
204
205cf::void_t cf::logger::rename(const cf::int32_t level)
206 throw (cf::exception)
207{
208 try
209 {
210 if (!mRenameMutex.trylock())
211 {
212 while (mRenameMutex.locked())
213 /* waiting */;
214
215 return;
216 }
217
218 std::string oldpath = mLogPool[level].mFullPath;
219 std::string newpath = oldpath;
220
221 cf::int32_t idx = 0;
222 cf::bool_t continued = true;
223
224 while (continued)
225 {
226 std::string name = newpath + "." + STR(++idx);
227
228 file file(name.c_str());
229
230 if (!file.exists())
231 break;
232 }
233
234 newpath += "." + STR(idx);
235
236 replace(mLogPool[level].mPrefix, mLogPool[level].mOption, NULL);
237
238 file(oldpath.c_str()).rename(newpath.c_str());
239
240 mRenameMutex.unlock();
241 }
242 catch (cf::exception &e)
243 {
244 mRenameMutex.unlock();
245 FORWARD_EXCEPTION(e);
246 }
247}
248
249cf::void_t cf::logger::replace(const std::string & prefix,
250 const cf::int32_t option,
251 FILE * newfp)
252{
253 FILE * oldfp = NULL;
254
255 std::vector<cf::logger::LOG_CTX>::iterator iter;
256 for (iter = mLogPool.begin() ; iter != mLogPool.end() ; iter++)
257 {
258 if (iter->mPrefix == prefix && iter->mOption == option)
259 {
260 if (!oldfp)
261 oldfp = iter->mFP;
262
263 iter->mFP = newfp;
264 }
265 }
266
267 if (oldfp)
268 fclose(oldfp);
269}
270
271cf::void_t cf::logger::init(const std::string & path)
272{
273 mPath = path;
274}
275
276cf::void_t cf::logger::add(const std::string & prefix,
277 const cf::int32_t level,
278 const std::string & description,
279 const cf::int32_t option,
280 const cf::int32_t rotationSize)
281 throw (cf::exception)
282{
283 try
284 {
285 /* if already registered level */
286 if (level < static_cast<cf::int32_t>(mLogPool.size()))
287 return;
288
289 if (level > static_cast<cf::int32_t>(mLogPool.size()))
290 THROW_EXCEPTION("level {" << level
291 << "} is not sequential(expected {"
292 << mLogPool.size() << "}");
293
294 cf::logger::LOG_CTX ctx;
295
296 ctx.mOption = option;
297 ctx.mOptionParam.mMaxLogSize = rotationSize * 1024 * 1024;
298 ctx.mPrefix = prefix;
299 ctx.mDescription = description;
300 ctx.mFullPath = getFullPath(mPath, prefix, option);
301 ctx.mFP = NULL;
302
303 mLogPool.push_back(ctx);
304
305 open(level);
306 }
307 catch (cf::exception &e)
308 {
309 FORWARD_EXCEPTION(e);
310 }
311}
312
313cf::int32_t cf::logger::getLevel()
314{
315 return mLogLevel;
316}
317
318cf::void_t cf::logger::setLevel(const cf::int32_t level)
319{
320 mLogLevel = level;
321}
322
323std::string cf::logger::getPath(const cf::int32_t level) const
324 throw (cf::exception)
325{
326 return mLogPool[level].mFullPath;
327}
328
329cf::bool_t cf::logger::isRegistered(const cf::int32_t level) const
330{
331 if (level >= static_cast<cf::int32_t>(mLogPool.size()))
332 return false;
333
334 return true;
335}
336
337cf::bool_t cf::logger::isEnabled(const cf::int32_t level) const
338{
339 /* check registered */
340 if (!isRegistered(level))
341 return false;
342
343 /* check option */
344 if (mLogPool[level].mOption & logger::FORCED)
345 return true;
346
347 /* check max level */
348 if (level > mLogLevel)
349 return false;
350
351 return true;
352}
353
354cf::void_t cf::logger::write(const cf::int32_t level,
355 const std::string & msg)
356{
357 try
358 {
359 /* check level */
360 if (!isRegistered(level))
361 THROW_EXCEPTION("{" << level << "} not registered");
362
363 if (!isEnabled(level))
364 THROW_EXCEPTION("not enabled {" << level << "}");
365
366 /* ready */
367 cf::int32_t option = mLogPool[level].mOption;
368
369 cf::util::datetime dt = cf::util::getInstance()->getDateTime();
370
371 /* rotate */
372 if (option & logger::DAILY)
373 rotateByDate(level, dt);
374
375 if (option & logger::SIZE)
376 rotateBySize(level);
377
378 if (!cf::file(mLogPool[level].mFullPath.c_str()).exists())
379 open(level);
380
381 /* set current */
382 const cf::char_t * desc = mLogPool[level].mDescription.c_str();
383 cf::char_t info[64] = {0x00,};
384 snprintf(info, sizeof(info) - 1,
385 "[%04d.%02d.%02d %02d:%02d:%02d.%03d][%u]",
386 dt.mYear, dt.mMonth, dt.mDay,
387 dt.mHour, dt.mMin, dt.mSec, dt.mUsec,
388 task::thread::id());
389
390 /* write */
391 if (mLogPool[level].mFP)
392 {
393 fprintf(mLogPool[level].mFP, "%s [%s] %s\n", info, desc, msg.c_str());
394 fflush(mLogPool[level].mFP);
395 }
396 }
397 catch (cf::exception &e)
398 {
399 fprintf(stderr, "%s\n", e.stackTrace().c_str());
400 fprintf(stderr, "%s\n", msg.c_str());
401 }
402}
Note: See TracBrowser for help on using the repository browser.