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

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

#1 rename context-implemented structures for readability

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