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

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

#1 fix crashed text

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