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

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

#1 refactoring log writing functions

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