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

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

#1 fix for windows

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