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

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

#1 fix and add interfaces

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