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

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

#1 fix datetime in log for win64 and rename some enumerations and functions

File size: 13.1 KB
Line 
1/**
2 * @file cf_log.c
3 * @author myusgun <myusgun@gmail.com>
4 */
5#if defined(_WIN32) || defined(_WIN64)
6# define _USE_32BIT_TIME_T
7# if (_MSC_VER >= 1400)
8# define localtime _localtime32
9# endif
10#endif
11
12#include "cf_log.h"
13#include "cf_file.h"
14#include "cf_thread.h"
15#include "cf_local.h"
16#include "cf_error.h"
17
18#include <stdio.h>
19#include <stdlib.h>
20#include <string.h>
21#include <stdarg.h>
22#include <time.h>
23
24#if defined(_WIN32) || defined(_WIN64)
25# include <Windows.h>
26#else
27# include <sys/time.h>
28#endif
29
30#define ASSERT_CTX(__ctx) \
31 if (__ctx == NULL) \
32 return CF_ERROR_LOG_INVALID_CTX
33
34#define ASSERT_INIT() \
35 if (gLogArray.ctxPool == NULL || \
36 gLogArray.ctxSize <= 0 ) \
37 return CF_ERROR_LOG_NOT_INITIALIZE
38
39#define ASSERT_MAPID(__mapid) \
40 if (gLogArray.ctxSize <= __mapid) \
41 return CF_ERROR_LOG_INVALID_MAPID
42
43#define ASSERT_MAPPED_CTX(__mapid) \
44 if (gLogArray.ctxPool[__mapid] != NULL) \
45 return CF_ERROR_LOG_ALREADY_MAPPED_ID
46
47#define ASSERT_NOT_MAPPED_CTX(__mapid) \
48 if (gLogArray.ctxPool[__mapid] == NULL) \
49 return CF_ERROR_LOG_NOT_MAPPED_ID
50
51#define LOCK_LOG_CTX(__ctx) CF_Mutex_Lock (&__ctx->mutex)
52#define UNLOCK_LOG_CTX(__ctx) CF_Mutex_Unlock (&__ctx->mutex)
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/**
59 * 로그 컨텍스트
60 *
61 * @remark change from public to private
62 */
63typedef void * CF_Log_Ctx;
64
65typedef struct __cf_util_datetime__
66{
67 int year;
68 int month;
69 int day;
70 int week; /* SUN:0 ~ SAT:6 */
71
72 int hour;
73 int min;
74 int sec;
75 int usec;
76} S_CF_LOG_DATETIME, CF_LOG_DATETIME;
77
78typedef struct __cf_log_ctx__ {
79 char path[NAME_LENGTH + 1];
80 int fd;
81 char * buffer;
82 size_t size; /* entire size of buffer */
83 size_t length; /* data length in current */
84 CF_Mutex mutex;
85} S_CF_LOG_CTX, CF_LOG_CTX;
86
87typedef struct __cf_log_array__ {
88 CF_Log_Ctx * ctxPool;
89 int ctxSize;
90} S_CF_LOG_ARRAY, CF_LOG_ARRAY;
91
92static CF_LOG_ARRAY gLogArray;
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 "%02d-%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_CTX * 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_CTX * 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 *
249 * @see CF_Log_UnsetMultiThread
250 */
251static int
252CF_Log_SetMultiThread (CF_Log_Ctx ctx)
253{
254 CF_LOG_CTX * context = (CF_LOG_CTX *) ctx;
255
256 ASSERT_CTX (ctx);
257
258 if (CF_Mutex_Create (&context->mutex) < 0)
259 return CF_ERROR_LOG_SET_MULTITHREAD;
260
261 return CF_OK;
262}
263
264/**
265 * 로그 컨텍스트에 멀티쓰레드 모드 설정 해제
266 *
267 * @return 성공 시, CF_OK; 실패 시, 오류 코드
268 *
269 * @param ctx 로그 컨텍스트
270 *
271 * @see CF_Log_SetMultiThread
272 */
273static int
274CF_Log_UnsetMultiThread (CF_Log_Ctx ctx)
275{
276 CF_LOG_CTX * context = (CF_LOG_CTX *) ctx;
277
278 ASSERT_CTX (ctx);
279
280 if (CF_Mutex_Destory (&context->mutex) < 0)
281 return CF_ERROR_LOG_UNSET_MULTITHREAD;
282
283 return CF_OK;
284}
285
286/**
287 * 로그 컨텍스트에 따라 로그 쓰기
288 *
289 * @return 성공 시, CF_OK; 실패 시, 오류 코드
290 *
291 * @param ctx 로그 컨텍스트
292 * @param prefix 로그의 프리픽스 문자열
293 * @param fmt 포맷 스트링
294 * @param ... 가변 인자
295 */
296static int
297CF_Log_WriteCtx (CF_Log_Ctx ctx,
298 const char * prefix,
299 const char * fmt,
300// ...)
301 va_list valist)
302{
303 CF_LOG_CTX * context = (CF_LOG_CTX *) ctx;
304// va_list valist;
305 char buffer[16 * 1024] = {0x00,};
306 char datetime[LOG_DATETIME_LENGTH + 1] = {0x00,};
307
308 ASSERT_CTX (ctx);
309
310 LOCK_LOG_CTX (context);
311// va_start (valist, fmt);
312
313 CF_Log_Local_GetTimeString (datetime);
314 snprintf (buffer, sizeof (buffer) - 1, "[%s][%s] ", datetime, prefix);
315 vsprintf (buffer + strlen (buffer), fmt, valist);
316
317 CF_Log_Local_Push (context, buffer, strlen (buffer));
318
319// va_end (valist);
320 UNLOCK_LOG_CTX (context);
321
322 return CF_OK;
323}
324
325/**
326 * 로그 버퍼의 데이터를 즉시 로그 파일에 쓰기
327 *
328 * @return 성공 시, CF_OK; 실패 시, 오류 코드
329 *
330 * @param ctx 로그 컨텍스트
331 */
332static int
333CF_Log_FlushCtx (CF_Log_Ctx ctx)
334{
335 CF_LOG_CTX * context = (CF_LOG_CTX *) ctx;
336
337 ASSERT_CTX (ctx);
338
339 LOCK_LOG_CTX (context);
340 CF_Log_Local_Flush (context);
341 UNLOCK_LOG_CTX (context);
342
343 return CF_OK;
344}
345
346/**
347 * 로그 컨텍스트 해제
348 *
349 * @return 성공 시, CF_OK; 실패 시, 오류 코드
350 *
351 * @param ctx 로그 컨텍스트
352 */
353static int
354CF_Log_DestroyCtx (CF_Log_Ctx ctx)
355{
356 CF_LOG_CTX * context = (CF_LOG_CTX *) ctx;
357
358 ASSERT_CTX (ctx);
359
360 if (context->size > 0)
361 {
362 CF_Log_FlushCtx (ctx);
363 free (context->buffer);
364 context->buffer = NULL;
365 context->size = 0;
366 }
367
368 CF_File_Close (context->fd);
369
370 CF_Mutex_Destory (&context->mutex);
371 context->mutex = NULL;
372
373 return CF_OK;
374}
375
376/**
377 * 로그 컨텍스트 생성
378 *
379 * @return 성공 시, 로그 컨텍스트; 실패 시, NULL
380 *
381 * @param path 로그 파일 경로
382 * @param memsize 로그 버퍼 크기
383 * @param ctx 로그 컨텍스트 받을 주소
384 *
385 * @see CF_LOG_BUFFER_DEFAULT, CF_LOG_BUFFER_NO
386 */
387static int
388CF_Log_CreateCtx (const char * path,
389 const int memsize,
390 CF_Log_Ctx * ctx)
391{
392 int result = 0;
393 int size = (memsize == CF_LOG_BUFFER_DEFAULT)
394 ? LOG_BUFFER_DEFAULT_SIZE
395 : memsize;
396 CF_LOG_CTX * context = NULL;
397
398 if (path == NULL)
399 return CF_ERROR_LOG_INVALID_ARGS;
400
401 TRY
402 {
403 context = (CF_LOG_CTX *) calloc (sizeof (CF_LOG_CTX), 1);
404 if (context == NULL)
405 {
406 result = CF_ERROR_LOG_ALLOCATE_CTX;
407 TRY_BREAK;
408 }
409
410 context->fd = CF_File_Create (path);
411 if (context->fd < 0)
412 {
413 result = CF_ERROR_LOG_CREATE_FILE;
414 TRY_BREAK;
415 }
416 snprintf (context->path, sizeof (context->path) -1, "%s", path);
417
418 if (size > 0)
419 {
420 context->buffer = (char *) calloc ((size_t) size + 1, 1);
421 if (context->buffer == NULL)
422 {
423 result = CF_ERROR_LOG_ALLOCATE_BUFFER;
424 TRY_BREAK;
425 }
426 context->size = (size_t) size;
427 }
428 }
429 CATCH_IF (result < 0)
430 {
431 CF_Log_DestroyCtx ((CF_Log_Ctx) context);
432 return result;
433 }
434
435 *ctx = (CF_Log_Ctx) context;
436
437 return CF_OK;
438}
439
440/**
441 * 로그 컨텍스트에 아이디 넘버 할당<br />
442 * 로그 기록 시, 아이디 넘버를 사용하면 해당 로그로 기록할 수 있음
443 *
444 * @return 성공 시, CF_OK; 실패 시, 오류 코드
445 *
446 * @param mapid 부여할 아이디 넘버
447 * @param ctx 로그 컨텍스트
448 *
449 * @remark 반드시 먼저 초기화 해야하며, 초기화 시에 주어진 번호보다 작은 아이디 넘버를 사용해야 함
450 *
451 * @see CF_LOG_OPEN, CF_Log_CreateCtx
452 */
453static int
454CF_Log_MapCtxID (const int mapid,
455 const CF_Log_Ctx ctx)
456{
457 ASSERT_INIT ();
458 ASSERT_MAPID (mapid);
459 ASSERT_MAPPED_CTX (mapid);
460
461 gLogArray.ctxPool[mapid] = ctx;
462
463 return CF_OK;
464}
465
466/**
467 * 아이디 넘버에 해당하는 로그를 닫고 해당하는 컨텍스트를 해제
468 *
469 * @return 성공 시, CF_OK; 실패 시, 오류 코드
470 *
471 * @param mapid 로그의 아이디 넘버
472 *
473 * @remark 아이디 넘버에 해당하는 컨텍스트가 해제되므로 주의
474 *
475 * @see CF_LOG_CLOSE, CF_Log_DestroyCtx
476 */
477static int
478CF_Log_UnmapCtxID (const int mapid)
479{
480 ASSERT_INIT ();
481 ASSERT_MAPID (mapid);
482 ASSERT_NOT_MAPPED_CTX (mapid);
483
484 CF_Log_DestroyCtx (gLogArray.ctxPool[mapid]);
485
486 free (gLogArray.ctxPool[mapid]);
487 gLogArray.ctxPool[mapid] = NULL;
488
489 return CF_OK;
490}
491
492/**
493 * 아이디 넘버에 해당하는 로그 컨텍스트를 얻기
494 *
495 * @return 성공 시, CF_OK; 실패 시, 오류 코드
496 *
497 * @param mapid 로그의 아이디 넘버
498 * @param ctx 로그 컨텍스트 받을 주소
499 */
500static int
501CF_Log_GetMappedCtx (const int mapid,
502 CF_Log_Ctx * ctx)
503{
504 ASSERT_INIT ();
505 ASSERT_MAPID (mapid);
506 ASSERT_NOT_MAPPED_CTX (mapid);
507
508 *ctx = gLogArray.ctxPool[mapid];
509
510 return CF_OK;
511}
512
513/**
514 * 로그를 사용하기 위해 초기화
515 *
516 * @return 성공 시, CF_OK; 실패 시, 오류 코드
517 *
518 * @param logPool 아이디 넘버 최대 값
519 */
520int
521CF_Log_Initialize (const int logPool)
522{
523 memset (&gLogArray, 0x00, sizeof (CF_LOG_ARRAY));
524
525 if (logPool > 0)
526 {
527 gLogArray.ctxPool =
528 (CF_Log_Ctx *) calloc ((size_t) logPool, sizeof (CF_Log_Ctx));
529 if (gLogArray.ctxPool == NULL)
530 return CF_ERROR_LOG_INITIALIZE;
531 gLogArray.ctxSize = logPool;
532 }
533
534 return CF_OK;
535}
536
537/**
538 * 로그가 모두 사용된 후 자원 해제
539 *
540 * @return CF_OK 반환
541 */
542int
543CF_Log_Finalize (void)
544{
545 int mapid = 0;
546
547 for (mapid = 0 ; mapid < gLogArray.ctxSize ; mapid++)
548 {
549 CF_Log_UnmapCtxID (mapid);
550 }
551
552 if (gLogArray.ctxPool != NULL)
553 free (gLogArray.ctxPool);
554
555 memset (&gLogArray, 0x00, sizeof (CF_LOG_ARRAY));
556
557 return CF_OK;
558}
559
560/**
561 * 로그 열기
562 *
563 * @return 성공 시, CF_OK; 실패 시, 오류 코드
564 *
565 * @param mapid 로그의 아이디 넘버
566 * @param path 로그 파일 경로
567 * @param memsize 로그 버퍼 크기
568 *
569 * @see CF_LOG_BUFFER_DEFAULT, CF_LOG_BUFFER_NO
570 */
571int
572CF_Log_Open (const int mapid,
573 const char * path,
574 const int memsize)
575{
576 int result = 0;
577 CF_Log_Ctx ctx = NULL;
578
579 result = CF_Log_CreateCtx (path, memsize, &ctx);
580 if (result < 0)
581 return result;
582
583 result = CF_Log_MapCtxID (mapid, ctx);
584 if (result < 0)
585 CF_Log_DestroyCtx (ctx);
586
587 return result;
588}
589
590/**
591 * 로그 닫기
592 *
593 * @return 성공 시, CF_OK; 실패 시, 오류 코드
594 *
595 * @param mapid 로그의 아이디 넘버
596 */
597int
598CF_Log_Close (const int mapid)
599{
600 return CF_Log_UnmapCtxID (mapid);
601}
602
603/**
604 * 로그 쓰기
605 *
606 * @return 성공 시, CF_OK; 실패 시, 오류 코드
607 *
608 * @param mapid 로그의 아이디 넘버
609 * @param prefix 로그의 프리픽스 문자열
610 * @param fmt 포맷 스트링
611 * @param ... 가변 인자
612 */
613int
614CF_Log_Write (const int mapid,
615 const char * prefix,
616 const char * fmt, ...)
617{
618 int result = 0;
619 CF_Log_Ctx ctx = NULL;
620 va_list valist;
621
622 result = CF_Log_GetMappedCtx (mapid, &ctx);
623 if (result < 0)
624 return result;
625
626 va_start (valist, fmt);
627 result = CF_Log_WriteCtx (ctx, prefix, fmt, valist);
628 va_end (valist);
629
630 return result;
631}
632
633/**
634 * 로그 버퍼의 데이터를 즉시 로그 파일에 쓰기
635 *
636 * @return 성공 시, CF_OK; 실패 시, 오류 코드
637 *
638 * @param mapid 로그의 아이디 넘버
639 */
640int
641CF_Log_Flush (const int mapid)
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 = CF_Log_FlushCtx (ctx);
651
652 return result;
653}
654
655/**
656 * 로그 컨텍스트에 멀티쓰레드 모드 설정
657 *
658 * @return 성공 시, CF_OK; 실패 시, 오류 코드
659 *
660 * @param mapid 로그의 아이디 넘버
661 * @param flag 설정/해제 bool 플래그
662 *
663 * @see CF_BOOL
664 */
665int
666CF_Log_SetMT (const int mapid,
667 const CF_BOOL flag)
668{
669 int result = 0;
670 CF_Log_Ctx ctx = NULL;
671
672 result = CF_Log_GetMappedCtx (mapid, &ctx);
673 if (result < 0)
674 return result;
675
676 result = (flag == CF_TRUE)
677 ? CF_Log_SetMultiThread (ctx)
678 : CF_Log_UnsetMultiThread (ctx);
679
680 return result;
681}
Note: See TracBrowser for help on using the repository browser.