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

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

#1 fix and arrange doxygen comments

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