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

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

#1 modify id to context in thread

File size: 13.3 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 LOCK_LOG_CTX(__ctx) CF_Mutex_Lock (__ctx->mutex)
55#define UNLOCK_LOG_CTX(__ctx) CF_Mutex_Unlock (__ctx->mutex)
56
57#define LOG_BUFFER_DEFAULT_SIZE 128 * 1024
58
59#define LOG_DATETIME_LENGTH sizeof ("0000-00-00 00:00:00.000") - 1
60
61/** 로그 컨텍스트 (Opaque) */
62typedef void * CF_Log_Ctx;
63
64typedef struct __cf_util_datetime__
65{
66 int year;
67 int month;
68 int day;
69 int week; /* SUN:0 ~ SAT:6 */
70
71 int hour;
72 int min;
73 int sec;
74 int usec;
75} CF_LOG_DATETIME;
76
77/** 로그 컨텍스트 (CF_Log_Ctx의 구현) */
78typedef struct __cf_log_ctx__
79{
80 char path[NAME_LENGTH + 1];
81 int fd;
82 char * buffer;
83 size_t size; /* entire size of buffer */
84 size_t length; /* data length in current */
85 CF_Mutex_Ctx mutex;
86} CF_LOG_CTX;
87
88typedef struct __cf_log_array__
89{
90 CF_Log_Ctx * ctxPool;
91 int ctxSize;
92} CF_LOG_ARRAY;
93
94static CF_LOG_ARRAY gLogArray;
95
96#if defined(_WIN32) || defined(_WIN64)
97/* {{{ */
98struct timezone
99{
100 int tz_minuteswest; /* minutes W of Greenwich */
101 int tz_dsttime; /* type of dst correction */
102};
103
104int gettimeofday (struct timeval *tv, struct timezone *tz)
105{
106 FILETIME ft;
107 unsigned __int64 buf =0;
108 //static int tzflag = 0;
109
110 if (NULL != tv)
111 {
112 GetSystemTimeAsFileTime (&ft);
113
114 buf |= ft.dwHighDateTime;
115 buf <<= 32;
116 buf |= ft.dwLowDateTime;
117
118 if (buf)
119 {
120 buf /= 10;
121 buf -= ((369 * 365 + 89) * (unsigned __int64) 86400) * 1000000;
122 }
123
124 tv->tv_sec = (long)(buf / 1000000UL);
125 tv->tv_usec = (long)(buf % 1000000UL);
126 }
127
128 /*
129 if (NULL != tz)
130 {
131 if (!tzflag)
132 {
133 _tzset();
134 tzflag++;
135 }
136
137 // Adjust for the timezone west of Greenwich
138 tz->tz_minuteswest = _timezone / 60;
139 tz->tz_dsttime = _daylight;
140 }
141 */
142
143 return 0;
144}
145/* }}} */
146#endif
147
148static int
149CF_Log_Local_GetTime (CF_LOG_DATETIME * dt)
150{
151 struct timeval timeVal;
152 struct tm * timeSt;
153
154 gettimeofday (&timeVal, NULL);
155 timeSt = localtime ((const time_t *)&timeVal.tv_sec);
156
157 dt->year = timeSt->tm_year + 1900;
158 dt->month = timeSt->tm_mon + 1;
159 dt->day = timeSt->tm_mday;
160 dt->week = timeSt->tm_wday;
161
162 dt->hour = timeSt->tm_hour;
163 dt->min = timeSt->tm_min;
164 dt->sec = timeSt->tm_sec;
165
166 dt->usec = (int) timeVal.tv_usec;
167
168 return CF_OK;
169}
170
171static int
172CF_Log_Local_GetTimeString (char * buffer)
173{
174 CF_LOG_DATETIME dt;
175 CF_Log_Local_GetTime (&dt);
176
177 snprintf (buffer, LOG_DATETIME_LENGTH,
178 "%02d-%02d-%02d %02d:%02d:%02d.%03d",
179 dt.year, dt.month, dt.day,
180 dt.hour, dt.min, dt.sec, dt.usec);
181
182 return CF_OK;
183}
184
185static int
186CF_Log_Local_Flush (CF_LOG_CTX * ctx)
187{
188 if (CF_File_Write (ctx->fd, ctx->buffer, ctx->length) < 0)
189 return CF_ERROR_LOG_FLUSH;
190
191 ctx->length = 0;
192
193 return CF_OK;
194}
195
196/**
197 * 로그 데이터 처리
198 *
199 * \return 성공 시, CF_OK; 실패 시, 오류 코드
200 *
201 * \param ctx 로그 컨텍스트
202 * \param buffer 로그 데이터
203 * \param demandSize 로그 데이터 길이
204 *
205 * \author vfire
206 */
207/* static */int
208CF_Log_Local_Push (CF_LOG_CTX * ctx,
209 const char * buffer,
210 const size_t demandSize)
211{
212 int result = CF_OK;
213
214 if (ctx->size > 0) /* 버퍼단위 버퍼링.... */
215 {
216 size_t writeSize;
217 size_t remainSize;
218
219 remainSize = demandSize;
220 while (remainSize)
221 {
222 writeSize = (ctx->size - ctx->length) < remainSize ? (ctx->size - ctx->length) : remainSize;
223
224 memcpy (ctx->buffer + ctx->length, buffer + demandSize - remainSize, writeSize);
225 ctx->length += writeSize;
226
227 if (ctx->length == ctx->size)
228 CF_Log_Local_Flush (ctx);
229
230 remainSize -= writeSize;
231 }
232 }
233 else /* flush되어야 함. */
234 {
235 ctx->buffer = (char *)buffer;
236 ctx->length = demandSize;
237
238 result = CF_Log_Local_Flush (ctx);
239 }
240
241 return result;
242}
243
244/**
245 * 로그 컨텍스트에 멀티쓰레드 모드 설정
246 *
247 * \return 성공 시, CF_OK; 실패 시, 오류 코드
248 *
249 * \param ctx 로그 컨텍스트
250 *
251 * \see CF_Log_UnsetMultiThread
252 */
253static int
254CF_Log_SetMultiThread (CF_Log_Ctx ctx)
255{
256 CF_LOG_CTX * context = (CF_LOG_CTX *) ctx;
257
258 ASSERT_CTX (ctx);
259
260 if (CF_Mutex_CreateCtx (&context->mutex) < 0)
261 return CF_ERROR_LOG_SET_MULTITHREAD;
262
263 return CF_OK;
264}
265
266/**
267 * 로그 컨텍스트에 멀티쓰레드 모드 설정 해제
268 *
269 * \return 성공 시, CF_OK; 실패 시, 오류 코드
270 *
271 * \param ctx 로그 컨텍스트
272 *
273 * \see CF_Log_SetMultiThread
274 */
275static int
276CF_Log_UnsetMultiThread (CF_Log_Ctx ctx)
277{
278 CF_LOG_CTX * context = (CF_LOG_CTX *) ctx;
279
280 ASSERT_CTX (ctx);
281
282 if (CF_Mutex_DestoryCtx (context->mutex) < 0)
283 return CF_ERROR_LOG_UNSET_MULTITHREAD;
284
285 context->mutex = NULL;
286
287 return CF_OK;
288}
289
290/**
291 * 로그 컨텍스트에 따라 로그 쓰기
292 *
293 * \return 성공 시, CF_OK; 실패 시, 오류 코드
294 *
295 * \param ctx 로그 컨텍스트
296 * \param prefix 로그의 프리픽스 문자열
297 * \param fmt 포맷 스트링
298 * \param ... 가변 인자
299 */
300static int
301CF_Log_WriteCtx (CF_Log_Ctx ctx,
302 const char * prefix,
303 const char * fmt,
304// ...)
305 va_list valist)
306{
307#define BUF_LEN 16 * 1024
308 CF_LOG_CTX * context = (CF_LOG_CTX *) ctx;
309// va_list valist;
310 char buffer[BUF_LEN + 1] = {0x00,};
311 char datetime[LOG_DATETIME_LENGTH + 1] = {0x00,};
312 int length = 0;
313
314 ASSERT_CTX (ctx);
315
316 LOCK_LOG_CTX (context);
317// va_start (valist, fmt);
318
319 CF_Log_Local_GetTimeString (datetime);
320 length = snprintf (buffer, BUF_LEN, "[%s][%s] ", datetime, prefix);
321 vsnprintf (&buffer[length], BUF_LEN - (size_t)length, 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_DestoryCtx (context->mutex);
377 context->mutex = NULL;
378
379 return CF_OK;
380}
381
382/**
383 * 로그 컨텍스트 생성
384 *
385 * \return 성공 시, 로그 컨텍스트; 실패 시, NULL
386 *
387 * \param ctx 로그 컨텍스트 주소
388 * \param path 로그 파일 경로
389 * \param memsize 로그 버퍼 크기
390 *
391 * \see CF_LOG_DEFAULT_BUFFER, CF_LOG_NO_BUFFER
392 */
393static int
394CF_Log_CreateCtx (CF_Log_Ctx * ctx,
395 const char * path,
396 const int memsize)
397{
398 int result = 0;
399 int fileFlag = CF_FILE_CREATE|CF_FILE_WRITE|CF_FILE_APPEND;
400 int size = (memsize == CF_LOG_DEFAULT_BUFFER)
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_CREATE_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 * \remarks 반드시 먼저 초기화 해야하며, 초기화 시에 주어진 번호보다 작은 아이디 넘버를 사용
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 * \remarks 아이디 넘버에 해당하는 컨텍스트가 해제되므로 주의
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 */
527int
528CF_Log_Initialize (const int poolSize)
529{
530 memset (&gLogArray, 0x00, sizeof (CF_LOG_ARRAY));
531
532 if (poolSize > 0)
533 {
534 gLogArray.ctxPool =
535 (CF_Log_Ctx *) calloc ((size_t) poolSize, sizeof (CF_Log_Ctx));
536 if (gLogArray.ctxPool == NULL)
537 return CF_ERROR_LOG_INITIALIZE;
538 gLogArray.ctxSize = poolSize;
539 }
540
541 return CF_OK;
542}
543
544/**
545 * 로그가 모두 사용된 후 자원 해제
546 *
547 * \return CF_OK 반환
548 */
549int
550CF_Log_Finalize (void)
551{
552 int mapid = 0;
553
554 for (mapid = 0 ; mapid < gLogArray.ctxSize ; mapid++)
555 {
556 CF_Log_UnmapCtxID (mapid);
557 }
558
559 if (gLogArray.ctxPool != NULL)
560 free (gLogArray.ctxPool);
561
562 memset (&gLogArray, 0x00, sizeof (CF_LOG_ARRAY));
563
564 return CF_OK;
565}
566
567/**
568 * 로그 열기
569 *
570 * \return 성공 시, CF_OK; 실패 시, 오류 코드
571 *
572 * \param mapid 로그의 아이디 넘버
573 * \param path 로그 파일 경로
574 * \param memsize 로그 버퍼 크기
575 *
576 * \see CF_LOG_DEFAULT_BUFFER, CF_LOG_NO_BUFFER
577 */
578int
579CF_Log_Open (const int mapid,
580 const char * path,
581 const int memsize)
582{
583 int result = 0;
584 CF_Log_Ctx ctx = NULL;
585
586 result = CF_Log_CreateCtx (&ctx, path, memsize);
587 if (result < 0)
588 return result;
589
590 result = CF_Log_MapCtxID (mapid, ctx);
591 if (result < 0)
592 CF_Log_DestroyCtx (ctx);
593
594 return result;
595}
596
597/**
598 * 로그 닫기
599 *
600 * \return 성공 시, CF_OK; 실패 시, 오류 코드
601 *
602 * \param mapid 로그의 아이디 넘버
603 */
604int
605CF_Log_Close (const int mapid)
606{
607 return CF_Log_UnmapCtxID (mapid);
608}
609
610/**
611 * 로그 버퍼의 데이터를 즉시 로그 파일에 쓰기
612 *
613 * \return 성공 시, CF_OK; 실패 시, 오류 코드
614 *
615 * \param mapid 로그의 아이디 넘버
616 */
617int
618CF_Log_Flush (const int mapid)
619{
620 int result = 0;
621 CF_Log_Ctx ctx = NULL;
622
623 result = CF_Log_GetMappedCtx (mapid, &ctx);
624 if (result < 0)
625 return result;
626
627 result = CF_Log_FlushCtx (ctx);
628
629 return result;
630}
631
632/**
633 * 로그 컨텍스트에 멀티쓰레드 모드 설정
634 *
635 * \return 성공 시, CF_OK; 실패 시, 오류 코드
636 *
637 * \param mapid 로그의 아이디 넘버
638 * \param flag 설정/해제 bool 플래그
639 *
640 * \see CF_BOOL
641 */
642int
643CF_Log_SetMT (const int mapid,
644 const CF_BOOL flag)
645{
646 int result = 0;
647 CF_Log_Ctx ctx = NULL;
648
649 result = CF_Log_GetMappedCtx (mapid, &ctx);
650 if (result < 0)
651 return result;
652
653 result = (flag) ? CF_Log_SetMultiThread (ctx) :
654 CF_Log_UnsetMultiThread (ctx);
655
656 return result;
657}
658
659/**
660 * 로그 쓰기
661 *
662 * \return 성공 시, CF_OK; 실패 시, 오류 코드
663 *
664 * \param mapid 로그의 아이디 넘버
665 * \param prefix 로그의 프리픽스 문자열
666 * \param fmt 포맷 스트링
667 * \param ... 가변 인자
668 */
669int
670CF_Log_Write (const int mapid,
671 const char * prefix,
672 const char * fmt, ...)
673{
674 int result = 0;
675 CF_Log_Ctx ctx = NULL;
676 va_list valist;
677
678 result = CF_Log_GetMappedCtx (mapid, &ctx);
679 if (result < 0)
680 return result;
681
682 va_start (valist, fmt);
683 result = CF_Log_WriteCtx (ctx, prefix, fmt, valist);
684 va_end (valist);
685
686 return result;
687}
Note: See TracBrowser for help on using the repository browser.