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

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

#1 change sprintf to snprintf and imporve performance of log

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