source: libcf/trunk/src/cf_log.c@ 99

Last change on this file since 99 was 99, checked in by cheese, 11 years ago

#1 fix doxygen for r98

File size: 13.9 KB
RevLine 
[23]1/**
[28]2 * @file cf_log.c
3 * @author myusgun <myusgun@gmail.com>
[11]4 */
[50]5#if defined(_WIN32) || defined(_WIN64)
[14]6# define _USE_32BIT_TIME_T
[86]7# if (_MSC_VER >= 1400)
8# define localtime _localtime32
9# endif
[14]10#endif
11
[11]12#include "cf_log.h"
[12]13#include "cf_file.h"
[11]14#include "cf_thread.h"
[12]15#include "cf_local.h"
[40]16#include "cf_error.h"
[11]17
[12]18#include <stdio.h>
19#include <stdlib.h>
[11]20#include <string.h>
[12]21#include <stdarg.h>
22#include <time.h>
[11]23
[50]24#if defined(_WIN32) || defined(_WIN64)
[13]25# include <Windows.h>
[50]26#else
[12]27# include <sys/time.h>
[50]28#endif
[11]29
[85]30#define ASSERT_CTX(__ctx) \
31 if (__ctx == NULL) \
32 return CF_ERROR_LOG_INVALID_CTX
33
34#define ASSERT_INIT() \
35 if (gLogArray.ctxPool == NULL || \
36 gLogArray.ctxSize <= 0 ) \
37 return CF_ERROR_LOG_NOT_INITIALIZE
38
39#define ASSERT_MAPID(__mapid) \
40 if (gLogArray.ctxSize <= __mapid) \
41 return CF_ERROR_LOG_INVALID_MAPID
42
43#define ASSERT_MAPPED_CTX(__mapid) \
44 if (gLogArray.ctxPool[__mapid] != NULL) \
45 return CF_ERROR_LOG_ALREADY_MAPPED_ID
46
47#define ASSERT_NOT_MAPPED_CTX(__mapid) \
48 if (gLogArray.ctxPool[__mapid] == NULL) \
49 return CF_ERROR_LOG_NOT_MAPPED_ID
50
[25]51#define LOCK_LOG_CTX(__ctx) CF_Mutex_Lock (&__ctx->mutex)
52#define UNLOCK_LOG_CTX(__ctx) CF_Mutex_Unlock (&__ctx->mutex)
[12]53
[42]54#define LOG_BUFFER_DEFAULT_SIZE 128 * 1024
[12]55
[51]56#define LOG_DATETIME_LENGTH sizeof ("0000-00-00 00:00:00.000") - 1
[34]57
[40]58/**
59 * 로그 컨텍스트
60 *
61 * @remark change from public to private
62 */
63typedef void * CF_Log_Ctx;
64
[12]65typedef struct __cf_util_datetime__
66{
67 int year;
68 int month;
69 int day;
70 int week; /* SUN:0 ~ SAT:6 */
71
72 int hour;
73 int min;
74 int sec;
75 int usec;
[30]76} S_CF_LOG_DATETIME, CF_LOG_DATETIME;
[12]77
[11]78typedef struct __cf_log_ctx__ {
79 char path[NAME_LENGTH + 1];
80 int fd;
81 char * buffer;
[12]82 size_t size; /* entire size of buffer */
83 size_t length; /* data length in current */
[11]84 CF_Mutex mutex;
85} S_CF_LOG_CTX, CF_LOG_CTX;
86
[51]87typedef struct __cf_log_array__ {
[12]88 CF_Log_Ctx * ctxPool;
89 int ctxSize;
[98]90 int level;
[51]91} S_CF_LOG_ARRAY, CF_LOG_ARRAY;
[11]92
[51]93static CF_LOG_ARRAY gLogArray;
[11]94
[12]95#if defined(_WIN32) || defined(_WIN64)
[66]96/* {{{ */
[12]97struct timezone
98{
[66]99 int tz_minuteswest; /* minutes W of Greenwich */
100 int tz_dsttime; /* type of dst correction */
[12]101};
102
[66]103int gettimeofday (struct timeval *tv, struct timezone *tz)
[12]104{
[66]105 FILETIME ft;
106 unsigned __int64 buf =0;
[38]107 //static int tzflag = 0;
[12]108
109 if (NULL != tv)
110 {
[66]111 GetSystemTimeAsFileTime (&ft);
[12]112
[66]113 buf |= ft.dwHighDateTime;
[67]114 buf <<= 32;
[66]115 buf |= ft.dwLowDateTime;
[12]116
[66]117 if (buf)
[12]118 {
[66]119 buf /= 10;
120 buf -= ((369 * 365 + 89) * (unsigned __int64) 86400) * 1000000;
[12]121 }
122
[66]123 tv->tv_sec = (long)(buf / 1000000UL);
124 tv->tv_usec = (long)(buf % 1000000UL);
[12]125 }
126
[38]127 /*
[12]128 if (NULL != tz)
129 {
130 if (!tzflag)
131 {
132 _tzset();
133 tzflag++;
134 }
135
136 // Adjust for the timezone west of Greenwich
137 tz->tz_minuteswest = _timezone / 60;
138 tz->tz_dsttime = _daylight;
139 }
[38]140 */
[12]141
142 return 0;
143}
[66]144/* }}} */
[50]145#endif
[12]146
[23]147static int
[30]148CF_Log_Local_GetTime (CF_LOG_DATETIME * dt)
[11]149{
[12]150 struct timeval timeVal;
151 struct tm * timeSt;
152
153 gettimeofday (&timeVal, NULL);
[13]154 timeSt = localtime ((const time_t *)&timeVal.tv_sec);
[12]155
156 dt->year = timeSt->tm_year + 1900;
157 dt->month = timeSt->tm_mon + 1;
158 dt->day = timeSt->tm_mday;
159 dt->week = timeSt->tm_wday;
160
161 dt->hour = timeSt->tm_hour;
162 dt->min = timeSt->tm_min;
163 dt->sec = timeSt->tm_sec;
164
165 dt->usec = (int) timeVal.tv_usec;
166
167 return CF_OK;
168}
169
[23]170static int
[12]171CF_Log_Local_GetTimeString (char * buffer)
172{
[30]173 CF_LOG_DATETIME dt;
[12]174 CF_Log_Local_GetTime (&dt);
175
[42]176 snprintf (buffer, LOG_DATETIME_LENGTH,
[14]177 "%02d-%02d-%02d %02d:%02d:%02d.%03d",
178 dt.year, dt.month, dt.day,
179 dt.hour, dt.min, dt.sec, dt.usec);
180
[12]181 return CF_OK;
182}
183
[23]184static int
[12]185CF_Log_Local_Flush (CF_LOG_CTX * ctx)
186{
187 if (CF_File_Write (ctx->fd, ctx->buffer, ctx->length) < 0)
188 return CF_ERROR_LOG_FLUSH;
189
190 ctx->length = 0;
191
192 return CF_OK;
193}
194
[35]195/**
196 * 로그 데이터 처리
197 *
198 * @return 성공 시, CF_OK; 실패 시, 오류 코드
199 *
200 * @param ctx 로그 컨텍스트
201 * @param buffer 로그 데이터
202 * @param demandSize 로그 데이터 길이
203 *
204 * @author vfire
205 */
206/* static */int
207CF_Log_Local_Push (CF_LOG_CTX * ctx,
208 const char * buffer,
209 const size_t demandSize)
210{
[40]211 int result = CF_OK;
212
[64]213 if (ctx->size > 0) /* 버퍼단위 버퍼링.... */
[35]214 {
215 size_t writeSize;
216 size_t remainSize;
217
218 remainSize = demandSize;
219 while (remainSize)
220 {
221 writeSize = (ctx->size - ctx->length) < remainSize ? (ctx->size - ctx->length) : remainSize;
222
223 memcpy (ctx->buffer + ctx->length, buffer + demandSize - remainSize, writeSize);
224 ctx->length += writeSize;
225
226 if (ctx->length == ctx->size)
227 CF_Log_Local_Flush (ctx);
228
229 remainSize -= writeSize;
230 }
231 }
232 else /* flush되어야 함. */
233 {
[38]234 ctx->buffer = (char *)buffer;
235 ctx->length = demandSize;
236
[40]237 result = CF_Log_Local_Flush (ctx);
[35]238 }
239
[40]240 return result;
241}
242
243/**
244 * 로그 컨텍스트에 멀티쓰레드 모드 설정
245 *
246 * @return 성공 시, CF_OK; 실패 시, 오류 코드
247 *
248 * @param ctx 로그 컨텍스트
249 *
250 * @see CF_Log_UnsetMultiThread
251 */
252static int
253CF_Log_SetMultiThread (CF_Log_Ctx ctx)
254{
255 CF_LOG_CTX * context = (CF_LOG_CTX *) ctx;
256
[85]257 ASSERT_CTX (ctx);
[40]258
259 if (CF_Mutex_Create (&context->mutex) < 0)
260 return CF_ERROR_LOG_SET_MULTITHREAD;
261
[35]262 return CF_OK;
263}
264
265/**
[40]266 * 로그 컨텍스트에 멀티쓰레드 모드 설정 해제
[35]267 *
[40]268 * @return 성공 시, CF_OK; 실패 시, 오류 코드
269 *
270 * @param ctx 로그 컨텍스트
271 *
272 * @see CF_Log_SetMultiThread
273 */
274static int
275CF_Log_UnsetMultiThread (CF_Log_Ctx ctx)
276{
277 CF_LOG_CTX * context = (CF_LOG_CTX *) ctx;
278
[85]279 ASSERT_CTX (ctx);
[40]280
281 if (CF_Mutex_Destory (&context->mutex) < 0)
282 return CF_ERROR_LOG_UNSET_MULTITHREAD;
283
284 return CF_OK;
285}
286
287/**
288 * 로그 컨텍스트에 따라 로그 쓰기
289 *
[35]290 * @return 성공 시, CF_OK; 실패 시, 오류 코드
291 *
[40]292 * @param ctx 로그 컨텍스트
293 * @param prefix 로그의 프리픽스 문자열
294 * @param fmt 포맷 스트링
295 * @param ... 가변 인자
[35]296 */
[40]297static int
298CF_Log_WriteCtx (CF_Log_Ctx ctx,
299 const char * prefix,
300 const char * fmt,
301// ...)
302 va_list valist)
[12]303{
[40]304 CF_LOG_CTX * context = (CF_LOG_CTX *) ctx;
305// va_list valist;
306 char buffer[16 * 1024] = {0x00,};
[42]307 char datetime[LOG_DATETIME_LENGTH + 1] = {0x00,};
[11]308
[85]309 ASSERT_CTX (ctx);
[11]310
[40]311 LOCK_LOG_CTX (context);
312// va_start (valist, fmt);
313
314 CF_Log_Local_GetTimeString (datetime);
[46]315 snprintf (buffer, sizeof (buffer) - 1, "[%s][%s] ", datetime, prefix);
[40]316 vsprintf (buffer + strlen (buffer), fmt, valist);
317
318 CF_Log_Local_Push (context, buffer, strlen (buffer));
319
320// va_end (valist);
321 UNLOCK_LOG_CTX (context);
322
[11]323 return CF_OK;
324}
325
[35]326/**
[40]327 * 로그 버퍼의 데이터를 즉시 로그 파일에 쓰기
[35]328 *
[40]329 * @return 성공 시, CF_OK; 실패 시, 오류 코드
330 *
331 * @param ctx 로그 컨텍스트
[35]332 */
[40]333static int
334CF_Log_FlushCtx (CF_Log_Ctx ctx)
[11]335{
[40]336 CF_LOG_CTX * context = (CF_LOG_CTX *) ctx;
[19]337
[85]338 ASSERT_CTX (ctx);
[40]339
340 LOCK_LOG_CTX (context);
341 CF_Log_Local_Flush (context);
342 UNLOCK_LOG_CTX (context);
343
344 return CF_OK;
345}
346
347/**
348 * 로그 컨텍스트 해제
349 *
350 * @return 성공 시, CF_OK; 실패 시, 오류 코드
351 *
352 * @param ctx 로그 컨텍스트
353 */
354static int
355CF_Log_DestroyCtx (CF_Log_Ctx ctx)
356{
357 CF_LOG_CTX * context = (CF_LOG_CTX *) ctx;
358
[85]359 ASSERT_CTX (ctx);
[40]360
361 if (context->size > 0)
[19]362 {
[40]363 CF_Log_FlushCtx (ctx);
364 free (context->buffer);
365 context->buffer = NULL;
366 context->size = 0;
[19]367 }
368
[40]369 CF_File_Close (context->fd);
[19]370
[45]371 CF_Mutex_Destory (&context->mutex);
372 context->mutex = NULL;
[11]373
374 return CF_OK;
375}
[12]376
[35]377/**
378 * 로그 컨텍스트 생성
379 *
380 * @return 성공 시, 로그 컨텍스트; 실패 시, NULL
381 *
382 * @param path 로그 파일 경로
383 * @param memsize 로그 버퍼 크기
[40]384 * @param ctx 로그 컨텍스트 받을 주소
[35]385 *
386 * @see CF_LOG_BUFFER_DEFAULT, CF_LOG_BUFFER_NO
387 */
[40]388static int
[12]389CF_Log_CreateCtx (const char * path,
[40]390 const int memsize,
391 CF_Log_Ctx * ctx)
[12]392{
393 int result = 0;
[93]394 int fileFlag = CF_FILE_CREATE|CF_FILE_WRITE|CF_FILE_APPEND;
[12]395 int size = (memsize == CF_LOG_BUFFER_DEFAULT)
[42]396 ? LOG_BUFFER_DEFAULT_SIZE
[12]397 : memsize;
[87]398
[12]399 CF_LOG_CTX * context = NULL;
400
401 if (path == NULL)
[40]402 return CF_ERROR_LOG_INVALID_ARGS;
[12]403
404 TRY
405 {
406 context = (CF_LOG_CTX *) calloc (sizeof (CF_LOG_CTX), 1);
407 if (context == NULL)
408 {
[40]409 result = CF_ERROR_LOG_ALLOCATE_CTX;
[12]410 TRY_BREAK;
411 }
412
[93]413 context->fd = CF_File_Open (path, fileFlag);
[12]414 if (context->fd < 0)
415 {
[40]416 result = CF_ERROR_LOG_CREATE_FILE;
[12]417 TRY_BREAK;
418 }
[46]419 snprintf (context->path, sizeof (context->path) -1, "%s", path);
[12]420
421 if (size > 0)
422 {
423 context->buffer = (char *) calloc ((size_t) size + 1, 1);
424 if (context->buffer == NULL)
425 {
[40]426 result = CF_ERROR_LOG_ALLOCATE_BUFFER;
[12]427 TRY_BREAK;
428 }
429 context->size = (size_t) size;
430 }
[87]431
432 *ctx = (CF_Log_Ctx) context;
[12]433 }
434 CATCH_IF (result < 0)
435 {
436 CF_Log_DestroyCtx ((CF_Log_Ctx) context);
437 }
438
[93]439 return result;
[12]440}
441
[35]442/**
[40]443 * 로그 컨텍스트에 아이디 넘버 할당<br />
444 * 로그 기록 시, 아이디 넘버를 사용하면 해당 로그로 기록할 수 있음
[35]445 *
[40]446 * @return 성공 시, CF_OK; 실패 시, 오류 코드
[35]447 *
[40]448 * @param mapid 부여할 아이디 넘버
449 * @param ctx 로그 컨텍스트
450 *
[98]451 * @remark 반드시 먼저 초기화 해야하며, 초기화 시에 주어진 번호보다 작은 아이디 넘버를 사용
[40]452 *
453 * @see CF_LOG_OPEN, CF_Log_CreateCtx
[35]454 */
[40]455static int
456CF_Log_MapCtxID (const int mapid,
457 const CF_Log_Ctx ctx)
[12]458{
[85]459 ASSERT_INIT ();
460 ASSERT_MAPID (mapid);
461 ASSERT_MAPPED_CTX (mapid);
[12]462
[51]463 gLogArray.ctxPool[mapid] = ctx;
[40]464
[12]465 return CF_OK;
466}
467
[35]468/**
[40]469 * 아이디 넘버에 해당하는 로그를 닫고 해당하는 컨텍스트를 해제
[35]470 *
[40]471 * @return 성공 시, CF_OK; 실패 시, 오류 코드
[35]472 *
[40]473 * @param mapid 로그의 아이디 넘버
474 *
475 * @remark 아이디 넘버에 해당하는 컨텍스트가 해제되므로 주의
476 *
477 * @see CF_LOG_CLOSE, CF_Log_DestroyCtx
[35]478 */
[40]479static int
480CF_Log_UnmapCtxID (const int mapid)
[19]481{
[85]482 ASSERT_INIT ();
483 ASSERT_MAPID (mapid);
484 ASSERT_NOT_MAPPED_CTX (mapid);
[19]485
[51]486 CF_Log_DestroyCtx (gLogArray.ctxPool[mapid]);
[19]487
[51]488 free (gLogArray.ctxPool[mapid]);
489 gLogArray.ctxPool[mapid] = NULL;
[19]490
491 return CF_OK;
492}
493
[35]494/**
[40]495 * 아이디 넘버에 해당하는 로그 컨텍스트를 얻기
[35]496 *
[40]497 * @return 성공 시, CF_OK; 실패 시, 오류 코드
[35]498 *
[40]499 * @param mapid 로그의 아이디 넘버
500 * @param ctx 로그 컨텍스트 받을 주소
[35]501 */
[40]502static int
503CF_Log_GetMappedCtx (const int mapid,
504 CF_Log_Ctx * ctx)
[19]505{
[85]506 ASSERT_INIT ();
507 ASSERT_MAPID (mapid);
508 ASSERT_NOT_MAPPED_CTX (mapid);
[19]509
[51]510 *ctx = gLogArray.ctxPool[mapid];
[19]511
512 return CF_OK;
513}
514
[35]515/**
[40]516 * 로그를 사용하기 위해 초기화
[35]517 *
518 * @return 성공 시, CF_OK; 실패 시, 오류 코드
519 *
[40]520 * @param logPool 아이디 넘버 최대 값
[35]521 */
[19]522int
[40]523CF_Log_Initialize (const int logPool)
[12]524{
[51]525 memset (&gLogArray, 0x00, sizeof (CF_LOG_ARRAY));
[12]526
[40]527 if (logPool > 0)
528 {
[51]529 gLogArray.ctxPool =
[40]530 (CF_Log_Ctx *) calloc ((size_t) logPool, sizeof (CF_Log_Ctx));
[51]531 if (gLogArray.ctxPool == NULL)
[40]532 return CF_ERROR_LOG_INITIALIZE;
[51]533 gLogArray.ctxSize = logPool;
[98]534 gLogArray.level = CF_LOG_NO_LEVEL;
[40]535 }
[12]536
[40]537 return CF_OK;
538}
[12]539
[40]540/**
541 * 로그가 모두 사용된 후 자원 해제
542 *
543 * @return CF_OK 반환
544 */
545int
546CF_Log_Finalize (void)
547{
548 int mapid = 0;
[12]549
[51]550 for (mapid = 0 ; mapid < gLogArray.ctxSize ; mapid++)
[40]551 {
552 CF_Log_UnmapCtxID (mapid);
553 }
[12]554
[51]555 if (gLogArray.ctxPool != NULL)
556 free (gLogArray.ctxPool);
[12]557
[51]558 memset (&gLogArray, 0x00, sizeof (CF_LOG_ARRAY));
[40]559
[12]560 return CF_OK;
561}
562
[35]563/**
[40]564 * 로그 열기
[35]565 *
566 * @return 성공 시, CF_OK; 실패 시, 오류 코드
567 *
[98]568 * @param mapid 로그의 아이디 넘버
[40]569 * @param path 로그 파일 경로
570 * @param memsize 로그 버퍼 크기
571 *
572 * @see CF_LOG_BUFFER_DEFAULT, CF_LOG_BUFFER_NO
[35]573 */
[12]574int
[40]575CF_Log_Open (const int mapid,
576 const char * path,
577 const int memsize)
[12]578{
[40]579 int result = 0;
580 CF_Log_Ctx ctx = NULL;
[12]581
[40]582 result = CF_Log_CreateCtx (path, memsize, &ctx);
583 if (result < 0)
584 return result;
[12]585
[40]586 result = CF_Log_MapCtxID (mapid, ctx);
[85]587 if (result < 0)
588 CF_Log_DestroyCtx (ctx);
[12]589
[40]590 return result;
[12]591}
[19]592
[35]593/**
[40]594 * 로그 닫기
[35]595 *
596 * @return 성공 시, CF_OK; 실패 시, 오류 코드
597 *
[40]598 * @param mapid 로그의 아이디 넘버
599 */
600int
601CF_Log_Close (const int mapid)
602{
603 return CF_Log_UnmapCtxID (mapid);
604}
605
606/**
[98]607 * 로그 버퍼의 데이터를 즉시 로그 파일에 쓰기
[35]608 *
[40]609 * @return 성공 시, CF_OK; 실패 시, 오류 코드
[35]610 *
[98]611 * @param mapid 로그의 아이디 넘버
[35]612 */
[19]613int
[98]614CF_Log_Flush (const int mapid)
[19]615{
[40]616 int result = 0;
617 CF_Log_Ctx ctx = NULL;
[19]618
[40]619 result = CF_Log_GetMappedCtx (mapid, &ctx);
620 if (result < 0)
[25]621 return result;
[19]622
[98]623 result = CF_Log_FlushCtx (ctx);
[19]624
[40]625 return result;
[19]626}
627
[35]628/**
[98]629 * 로그 컨텍스트에 멀티쓰레드 모드 설정
[35]630 *
[98]631 * @return 성공 시, CF_OK; 실패 시, 오류 코드
[35]632 *
[98]633 * @param mapid 로그의 아이디 넘버
634 * @param flag 설정/해제 bool 플래그
635 *
636 * @see CF_BOOL
[35]637 */
[19]638int
[98]639CF_Log_SetMT (const int mapid,
640 const CF_BOOL flag)
[19]641{
[40]642 int result = 0;
643 CF_Log_Ctx ctx = NULL;
[19]644
[40]645 result = CF_Log_GetMappedCtx (mapid, &ctx);
646 if (result < 0)
647 return result;
[19]648
[98]649 result = (flag) ? CF_Log_SetMultiThread (ctx) :
650 CF_Log_UnsetMultiThread (ctx);
[19]651
[40]652 return result;
[19]653}
654
[35]655/**
[98]656 * 현재 설정된 로그 레벨을 가져옴
[35]657 *
[99]658 * @return 로그 레벨이 설정된 경우, 로그 풀 크기; 그 외에, 설정된 로그 레벨
[98]659 */
660static int
661CF_Log_GetLevel (void)
662{
663 return (gLogArray.level == CF_LOG_NO_LEVEL) ? gLogArray.ctxSize
664 : gLogArray.level;
665}
666
667/**
668 * 로그 레벨을 설정
669 * 지정된 로그 레벨 이하의 아이디 넘버만 로그로 기록하도록 설정
[40]670 *
[98]671 * @return 성공 시, CF_OK; 실패 시, 오류 코드
672 *
[99]673 * @param level 설정할 로그 레벨
[98]674 *
675 * @see CF_LOG_NO_LEVEL
[35]676 */
[40]677int
[98]678CF_Log_SetLevel (const int level)
[19]679{
[98]680 gLogArray.level = level;
681
682 return CF_OK;
683}
684
685/**
686 * 로그 쓰기
687 *
688 * @return 성공 시, CF_OK; 실패 시, 오류 코드
689 *
690 * @param mapid 로그의 아이디 넘버
691 * @param prefix 로그의 프리픽스 문자열
692 * @param fmt 포맷 스트링
693 * @param ... 가변 인자
694 */
695int
696CF_Log_Write (const int mapid,
697 const char * prefix,
698 const char * fmt, ...)
699{
[40]700 int result = 0;
701 CF_Log_Ctx ctx = NULL;
[98]702 va_list valist;
[19]703
[98]704 result = CF_Log_GetLevel ();
705 if (result < mapid)
706 return CF_OK;
707
[40]708 result = CF_Log_GetMappedCtx (mapid, &ctx);
709 if (result < 0)
710 return result;
711
[98]712 va_start (valist, fmt);
713 result = CF_Log_WriteCtx (ctx, prefix, fmt, valist);
714 va_end (valist);
[40]715
716 return result;
[19]717}
Note: See TracBrowser for help on using the repository browser.