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

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

#1 add author (vfire) for log

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