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

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

#1 change interface of log from context to id-number

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