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

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

#1 fix log level check logic

File size: 13.5 KB
Line 
1/**
2 * @file cf_log.c
3 * @author myusgun <myusgun@gmail.com>
4 * @author vfire
5 */
6#if defined(_WIN32) || defined(_WIN64)
7# define _USE_32BIT_TIME_T
8# if (_MSC_VER >= 1400)
9# define localtime _localtime32
10# endif
11#endif
12
13#include "cf_log.h"
14#include "cf_file.h"
15#include "cf_thread.h"
16#include "cf_local.h"
17#include "cf_error.h"
18
19#include <stdio.h>
20#include <stdlib.h>
21#include <string.h>
22#include <stdarg.h>
23#include <time.h>
24
25#if defined(_WIN32) || defined(_WIN64)
26# include <Windows.h>
27#else
28# include <sys/time.h>
29#endif
30
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
52#define CHECK_LOG_LEVEL(__mapid) \
53 if (gLogArray.level < mapid) \
54 return CF_OK
55
56#define LOCK_LOG_CTX(__ctx) CF_Mutex_Lock (&__ctx->mutex)
57#define UNLOCK_LOG_CTX(__ctx) CF_Mutex_Unlock (&__ctx->mutex)
58
59#define LOG_BUFFER_DEFAULT_SIZE 128 * 1024
60
61#define LOG_DATETIME_LENGTH sizeof ("0000-00-00 00:00:00.000") - 1
62
63/**
64 * 로그 컨텍스트
65 *
66 * @remark change from public to private
67 */
68typedef void * CF_Log_Ctx;
69
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;
81} S_CF_LOG_DATETIME, CF_LOG_DATETIME;
82
83typedef struct __cf_log_ctx__ {
84 char path[NAME_LENGTH + 1];
85 int fd;
86 char * buffer;
87 size_t size; /* entire size of buffer */
88 size_t length; /* data length in current */
89 CF_Mutex mutex;
90} S_CF_LOG_CTX, CF_LOG_CTX;
91
92typedef struct __cf_log_array__ {
93 CF_Log_Ctx * ctxPool;
94 int ctxSize;
95 int level;
96} S_CF_LOG_ARRAY, CF_LOG_ARRAY;
97
98static CF_LOG_ARRAY gLogArray;
99
100#if defined(_WIN32) || defined(_WIN64)
101/* {{{ */
102struct timezone
103{
104 int tz_minuteswest; /* minutes W of Greenwich */
105 int tz_dsttime; /* type of dst correction */
106};
107
108int gettimeofday (struct timeval *tv, struct timezone *tz)
109{
110 FILETIME ft;
111 unsigned __int64 buf =0;
112 //static int tzflag = 0;
113
114 if (NULL != tv)
115 {
116 GetSystemTimeAsFileTime (&ft);
117
118 buf |= ft.dwHighDateTime;
119 buf <<= 32;
120 buf |= ft.dwLowDateTime;
121
122 if (buf)
123 {
124 buf /= 10;
125 buf -= ((369 * 365 + 89) * (unsigned __int64) 86400) * 1000000;
126 }
127
128 tv->tv_sec = (long)(buf / 1000000UL);
129 tv->tv_usec = (long)(buf % 1000000UL);
130 }
131
132 /*
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 }
145 */
146
147 return 0;
148}
149/* }}} */
150#endif
151
152static int
153CF_Log_Local_GetTime (CF_LOG_DATETIME * dt)
154{
155 struct timeval timeVal;
156 struct tm * timeSt;
157
158 gettimeofday (&timeVal, NULL);
159 timeSt = localtime ((const time_t *)&timeVal.tv_sec);
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
175static int
176CF_Log_Local_GetTimeString (char * buffer)
177{
178 CF_LOG_DATETIME dt;
179 CF_Log_Local_GetTime (&dt);
180
181 snprintf (buffer, LOG_DATETIME_LENGTH,
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
186 return CF_OK;
187}
188
189static int
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
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{
216 int result = CF_OK;
217
218 if (ctx->size > 0) /* 버퍼단위 버퍼링.... */
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 {
239 ctx->buffer = (char *)buffer;
240 ctx->length = demandSize;
241
242 result = CF_Log_Local_Flush (ctx);
243 }
244
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
262 ASSERT_CTX (ctx);
263
264 if (CF_Mutex_Create (&context->mutex) < 0)
265 return CF_ERROR_LOG_SET_MULTITHREAD;
266
267 return CF_OK;
268}
269
270/**
271 * 로그 컨텍스트에 멀티쓰레드 모드 설정 해제
272 *
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
284 ASSERT_CTX (ctx);
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 *
295 * @return 성공 시, CF_OK; 실패 시, 오류 코드
296 *
297 * @param ctx 로그 컨텍스트
298 * @param prefix 로그의 프리픽스 문자열
299 * @param fmt 포맷 스트링
300 * @param ... 가변 인자
301 */
302static int
303CF_Log_WriteCtx (CF_Log_Ctx ctx,
304 const char * prefix,
305 const char * fmt,
306// ...)
307 va_list valist)
308{
309 CF_LOG_CTX * context = (CF_LOG_CTX *) ctx;
310// va_list valist;
311 char buffer[16 * 1024] = {0x00,};
312 char datetime[LOG_DATETIME_LENGTH + 1] = {0x00,};
313
314 ASSERT_CTX (ctx);
315
316 LOCK_LOG_CTX (context);
317// va_start (valist, fmt);
318
319 CF_Log_Local_GetTimeString (datetime);
320 snprintf (buffer, sizeof (buffer) - 1, "[%s][%s] ", datetime, prefix);
321 vsprintf (buffer + strlen (buffer), fmt, valist);
322
323 CF_Log_Local_Push (context, buffer, strlen (buffer));
324
325// va_end (valist);
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_FlushCtx (CF_Log_Ctx ctx)
340{
341 CF_LOG_CTX * context = (CF_LOG_CTX *) ctx;
342
343 ASSERT_CTX (ctx);
344
345 LOCK_LOG_CTX (context);
346 CF_Log_Local_Flush (context);
347 UNLOCK_LOG_CTX (context);
348
349 return CF_OK;
350}
351
352/**
353 * 로그 컨텍스트 해제
354 *
355 * @return 성공 시, CF_OK; 실패 시, 오류 코드
356 *
357 * @param ctx 로그 컨텍스트
358 */
359static int
360CF_Log_DestroyCtx (CF_Log_Ctx ctx)
361{
362 CF_LOG_CTX * context = (CF_LOG_CTX *) ctx;
363
364 ASSERT_CTX (ctx);
365
366 if (context->size > 0)
367 {
368 CF_Log_FlushCtx (ctx);
369 free (context->buffer);
370 context->buffer = NULL;
371 context->size = 0;
372 }
373
374 CF_File_Close (context->fd);
375
376 CF_Mutex_Destory (&context->mutex);
377 context->mutex = NULL;
378
379 return CF_OK;
380}
381
382/**
383 * 로그 컨텍스트 생성
384 *
385 * @return 성공 시, 로그 컨텍스트; 실패 시, NULL
386 *
387 * @param path 로그 파일 경로
388 * @param memsize 로그 버퍼 크기
389 * @param ctx 로그 컨텍스트 받을 주소
390 *
391 * @see CF_LOG_BUFFER_DEFAULT, CF_LOG_BUFFER_NO
392 */
393static int
394CF_Log_CreateCtx (const char * path,
395 const int memsize,
396 CF_Log_Ctx * ctx)
397{
398 int result = 0;
399 int fileFlag = CF_FILE_CREATE|CF_FILE_WRITE|CF_FILE_APPEND;
400 int size = (memsize == CF_LOG_BUFFER_DEFAULT)
401 ? LOG_BUFFER_DEFAULT_SIZE
402 : memsize;
403
404 CF_LOG_CTX * context = NULL;
405
406 if (path == NULL)
407 return CF_ERROR_LOG_INVALID_ARGS;
408
409 TRY
410 {
411 context = (CF_LOG_CTX *) calloc (sizeof (CF_LOG_CTX), 1);
412 if (context == NULL)
413 {
414 result = CF_ERROR_LOG_ALLOCATE_CTX;
415 TRY_BREAK;
416 }
417
418 context->fd = CF_File_Open (path, fileFlag);
419 if (context->fd < 0)
420 {
421 result = CF_ERROR_LOG_CREATE_FILE;
422 TRY_BREAK;
423 }
424 snprintf (context->path, sizeof (context->path) -1, "%s", path);
425
426 if (size > 0)
427 {
428 context->buffer = (char *) calloc ((size_t) size + 1, 1);
429 if (context->buffer == NULL)
430 {
431 result = CF_ERROR_LOG_ALLOCATE_BUFFER;
432 TRY_BREAK;
433 }
434 context->size = (size_t) size;
435 }
436
437 *ctx = (CF_Log_Ctx) context;
438 }
439 CATCH_IF (result < 0)
440 {
441 CF_Log_DestroyCtx ((CF_Log_Ctx) context);
442 }
443
444 return result;
445}
446
447/**
448 * 로그 컨텍스트에 아이디 넘버 할당<br />
449 * 로그 기록 시, 아이디 넘버를 사용하면 해당 로그로 기록할 수 있음
450 *
451 * @return 성공 시, CF_OK; 실패 시, 오류 코드
452 *
453 * @param mapid 부여할 아이디 넘버
454 * @param ctx 로그 컨텍스트
455 *
456 * @remark 반드시 먼저 초기화 해야하며, 초기화 시에 주어진 번호보다 작은 아이디 넘버를 사용
457 *
458 * @see CF_LOG_OPEN, CF_Log_CreateCtx
459 */
460static int
461CF_Log_MapCtxID (const int mapid,
462 const CF_Log_Ctx ctx)
463{
464 ASSERT_INIT ();
465 ASSERT_MAPID (mapid);
466 ASSERT_MAPPED_CTX (mapid);
467
468 gLogArray.ctxPool[mapid] = ctx;
469
470 return CF_OK;
471}
472
473/**
474 * 아이디 넘버에 해당하는 로그를 닫고 해당하는 컨텍스트를 해제
475 *
476 * @return 성공 시, CF_OK; 실패 시, 오류 코드
477 *
478 * @param mapid 로그의 아이디 넘버
479 *
480 * @remark 아이디 넘버에 해당하는 컨텍스트가 해제되므로 주의
481 *
482 * @see CF_LOG_CLOSE, CF_Log_DestroyCtx
483 */
484static int
485CF_Log_UnmapCtxID (const int mapid)
486{
487 ASSERT_INIT ();
488 ASSERT_MAPID (mapid);
489 ASSERT_NOT_MAPPED_CTX (mapid);
490
491 CF_Log_DestroyCtx (gLogArray.ctxPool[mapid]);
492
493 free (gLogArray.ctxPool[mapid]);
494 gLogArray.ctxPool[mapid] = NULL;
495
496 return CF_OK;
497}
498
499/**
500 * 아이디 넘버에 해당하는 로그 컨텍스트를 얻기
501 *
502 * @return 성공 시, CF_OK; 실패 시, 오류 코드
503 *
504 * @param mapid 로그의 아이디 넘버
505 * @param ctx 로그 컨텍스트 받을 주소
506 */
507static int
508CF_Log_GetMappedCtx (const int mapid,
509 CF_Log_Ctx * ctx)
510{
511 ASSERT_INIT ();
512 ASSERT_MAPID (mapid);
513 ASSERT_NOT_MAPPED_CTX (mapid);
514
515 *ctx = gLogArray.ctxPool[mapid];
516
517 return CF_OK;
518}
519
520/**
521 * 로그를 사용하기 위해 초기화
522 *
523 * @return 성공 시, CF_OK; 실패 시, 오류 코드
524 *
525 * @param poolSize 로그 풀 크기로, 로그 아이디 넘버의 최대 값
526 * @param level 로그 레벨
527 *
528 * @see CF_LOG_NO_LEVEL
529 */
530int
531CF_Log_Initialize (const int poolSize,
532 const int level)
533{
534 memset (&gLogArray, 0x00, sizeof (CF_LOG_ARRAY));
535
536 if (poolSize > 0)
537 {
538 gLogArray.ctxPool =
539 (CF_Log_Ctx *) calloc ((size_t) poolSize, sizeof (CF_Log_Ctx));
540 if (gLogArray.ctxPool == NULL)
541 return CF_ERROR_LOG_INITIALIZE;
542 gLogArray.ctxSize = poolSize;
543 gLogArray.level = (level == CF_LOG_NO_LEVEL) ? poolSize : level;
544 }
545
546 return CF_OK;
547}
548
549/**
550 * 로그가 모두 사용된 후 자원 해제
551 *
552 * @return CF_OK 반환
553 */
554int
555CF_Log_Finalize (void)
556{
557 int mapid = 0;
558
559 for (mapid = 0 ; mapid < gLogArray.ctxSize ; mapid++)
560 {
561 CF_Log_UnmapCtxID (mapid);
562 }
563
564 if (gLogArray.ctxPool != NULL)
565 free (gLogArray.ctxPool);
566
567 memset (&gLogArray, 0x00, sizeof (CF_LOG_ARRAY));
568
569 return CF_OK;
570}
571
572/**
573 * 로그 열기
574 *
575 * @return 성공 시, CF_OK; 실패 시, 오류 코드
576 *
577 * @param mapid 로그의 아이디 넘버
578 * @param path 로그 파일 경로
579 * @param memsize 로그 버퍼 크기
580 *
581 * @see CF_LOG_BUFFER_DEFAULT, CF_LOG_BUFFER_NO
582 */
583int
584CF_Log_Open (const int mapid,
585 const char * path,
586 const int memsize)
587{
588 int result = 0;
589 CF_Log_Ctx ctx = NULL;
590
591 CHECK_LOG_LEVEL (mapid);
592
593 result = CF_Log_CreateCtx (path, memsize, &ctx);
594 if (result < 0)
595 return result;
596
597 result = CF_Log_MapCtxID (mapid, ctx);
598 if (result < 0)
599 CF_Log_DestroyCtx (ctx);
600
601 return result;
602}
603
604/**
605 * 로그 닫기
606 *
607 * @return 성공 시, CF_OK; 실패 시, 오류 코드
608 *
609 * @param mapid 로그의 아이디 넘버
610 */
611int
612CF_Log_Close (const int mapid)
613{
614 CHECK_LOG_LEVEL (mapid);
615
616 return CF_Log_UnmapCtxID (mapid);
617}
618
619/**
620 * 로그 버퍼의 데이터를 즉시 로그 파일에 쓰기
621 *
622 * @return 성공 시, CF_OK; 실패 시, 오류 코드
623 *
624 * @param mapid 로그의 아이디 넘버
625 */
626int
627CF_Log_Flush (const int mapid)
628{
629 int result = 0;
630 CF_Log_Ctx ctx = NULL;
631
632 result = CF_Log_GetMappedCtx (mapid, &ctx);
633 if (result < 0)
634 return result;
635
636 result = CF_Log_FlushCtx (ctx);
637
638 return result;
639}
640
641/**
642 * 로그 컨텍스트에 멀티쓰레드 모드 설정
643 *
644 * @return 성공 시, CF_OK; 실패 시, 오류 코드
645 *
646 * @param mapid 로그의 아이디 넘버
647 * @param flag 설정/해제 bool 플래그
648 *
649 * @see CF_BOOL
650 */
651int
652CF_Log_SetMT (const int mapid,
653 const CF_BOOL flag)
654{
655 int result = 0;
656 CF_Log_Ctx ctx = NULL;
657
658 result = CF_Log_GetMappedCtx (mapid, &ctx);
659 if (result < 0)
660 return result;
661
662 result = (flag) ? CF_Log_SetMultiThread (ctx) :
663 CF_Log_UnsetMultiThread (ctx);
664
665 return result;
666}
667
668/**
669 * 로그 쓰기
670 *
671 * @return 성공 시, CF_OK; 실패 시, 오류 코드
672 *
673 * @param mapid 로그의 아이디 넘버
674 * @param prefix 로그의 프리픽스 문자열
675 * @param fmt 포맷 스트링
676 * @param ... 가변 인자
677 */
678int
679CF_Log_Write (const int mapid,
680 const char * prefix,
681 const char * fmt, ...)
682{
683 int result = 0;
684 CF_Log_Ctx ctx = NULL;
685 va_list valist;
686
687 CHECK_LOG_LEVEL (mapid);
688
689 result = CF_Log_GetMappedCtx (mapid, &ctx);
690 if (result < 0)
691 return result;
692
693 va_start (valist, fmt);
694 result = CF_Log_WriteCtx (ctx, prefix, fmt, valist);
695 va_end (valist);
696
697 return result;
698}
Note: See TracBrowser for help on using the repository browser.