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

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

#1 change interface to set log level

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