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

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

#1 separate example code and doxygen comment and fix logging push logic by vfire

File size: 11.4 KB
RevLine 
[23]1/**
[28]2 * @file cf_log.c
3 * @author myusgun <myusgun@gmail.com>
4 * @version 0.1
[11]5 */
[14]6#ifdef _WIN32
7# define _USE_32BIT_TIME_T
8#endif
9
[11]10#include "cf_log.h"
[12]11#include "cf_file.h"
[11]12#include "cf_thread.h"
[12]13#include "cf_local.h"
[11]14
[12]15#include <stdio.h>
16#include <stdlib.h>
[11]17#include <string.h>
[12]18#include <stdarg.h>
19#include <time.h>
[11]20
21#ifdef _WIN32
[14]22# define snprintf _snprintf
[13]23# include <Windows.h>
[11]24#else // #ifdef _WIN32
[12]25# include <sys/time.h>
[11]26#endif // #ifdef _WIN32
27
[25]28#define CHECK_INVALID_CTX(__ctx) (__ctx == NULL)
29#define LOCK_LOG_CTX(__ctx) CF_Mutex_Lock (&__ctx->mutex)
30#define UNLOCK_LOG_CTX(__ctx) CF_Mutex_Unlock (&__ctx->mutex)
31#define CHECK_INITIALIZED() (gLogEnvironment.ctxPool == NULL|| \
32 gLogEnvironment.ctxSize <= 0 )
33#define CHECK_INVALID_MAPID(__mapid) (gLogEnvironment.ctxSize <= __mapid)
34#define CHECK_MAPPED_ID(__mapid) (gLogEnvironment.ctxPool[__mapid] != NULL) \
[12]35
36
37#define CF_LOG_BUFFER_DEFAULT_SIZE 128 * 1024
38
[34]39#define CF_LOG_DATETIME_LENGTH sizeof ("0000-00-00 00:00:00.000") - 1
40
[12]41typedef struct __cf_util_datetime__
42{
43 int year;
44 int month;
45 int day;
46 int week; /* SUN:0 ~ SAT:6 */
47
48 int hour;
49 int min;
50 int sec;
51 int usec;
[30]52} S_CF_LOG_DATETIME, CF_LOG_DATETIME;
[12]53
[11]54typedef struct __cf_log_ctx__ {
55 char path[NAME_LENGTH + 1];
56 int fd;
57 char * buffer;
[12]58 size_t size; /* entire size of buffer */
59 size_t length; /* data length in current */
[11]60 CF_Mutex mutex;
61} S_CF_LOG_CTX, CF_LOG_CTX;
62
63typedef struct __cf_log_environment__ {
[12]64 CF_Log_Ctx * ctxPool;
65 int ctxSize;
66} S_CF_LOG_ENVIRONMENT, CF_LOG_ENVIRONMENT;
[11]67
[29]68static CF_LOG_ENVIRONMENT gLogEnvironment;
[11]69
[12]70#if defined(_WIN32) || defined(_WIN64)
71/* #if defined(_WIN32) || defined(_WIN64) {{{ */
72struct timezone
73{
74 int tz_minuteswest; /* minutes W of Greenwich */
75 int tz_dsttime; /* type of dst correction */
76};
77
78// Definition of a gettimeofday function
79int gettimeofday(struct timeval *tv, struct timezone *tz)
80{
81 // Define a structure to receive the current Windows filetime
82 FILETIME ft;
83
84 // Initialize the present time to 0 and the timezone to UTC
85 unsigned __int64 ui64 =0;
86 static int tzflag = 0;
87
88 if (NULL != tv)
89 {
90 GetSystemTimeAsFileTime(&ft);
91
92 ui64 = (((unsigned __int64) ft.dwHighDateTime << 32)
93 + (unsigned __int64) ft.dwLowDateTime);
94
95 if (ui64)
96 {
97 ui64 /= 10;
98 ui64 -= ((369 * 365 + 89) * (unsigned __int64) 86400) * 1000000;
99 }
100
101 // Finally change microseconds to seconds and place in the seconds value.
102 // The modulus picks up the microseconds.
103 tv->tv_sec = (long)(ui64 / 1000000UL);
104 tv->tv_usec = (long)(ui64 % 1000000UL);
105 }
106
107 if (NULL != tz)
108 {
109 if (!tzflag)
110 {
111 _tzset();
112 tzflag++;
113 }
114
115 // Adjust for the timezone west of Greenwich
116 tz->tz_minuteswest = _timezone / 60;
117 tz->tz_dsttime = _daylight;
118 }
119
120 return 0;
121}
122#endif // #if defined(_WIN32) || defined(_WIN64)
123/* }}} #if defined(_WIN32) || defined(_WIN64) */
124
[23]125static int
[30]126CF_Log_Local_GetTime (CF_LOG_DATETIME * dt)
[11]127{
[12]128 struct timeval timeVal;
129 struct tm * timeSt;
130
131 gettimeofday (&timeVal, NULL);
[13]132 timeSt = localtime ((const time_t *)&timeVal.tv_sec);
[12]133
134 dt->year = timeSt->tm_year + 1900;
135 dt->month = timeSt->tm_mon + 1;
136 dt->day = timeSt->tm_mday;
137 dt->week = timeSt->tm_wday;
138
139 dt->hour = timeSt->tm_hour;
140 dt->min = timeSt->tm_min;
141 dt->sec = timeSt->tm_sec;
142
143 dt->usec = (int) timeVal.tv_usec;
144
145 return CF_OK;
146}
147
[23]148static int
[12]149CF_Log_Local_GetTimeString (char * buffer)
150{
[30]151 CF_LOG_DATETIME dt;
[12]152 CF_Log_Local_GetTime (&dt);
153
[34]154 snprintf (buffer, CF_LOG_DATETIME_LENGTH,
[14]155 "%02d-%02d-%02d %02d:%02d:%02d.%03d",
156 dt.year, dt.month, dt.day,
157 dt.hour, dt.min, dt.sec, dt.usec);
158
[12]159 return CF_OK;
160}
161
[23]162static int
[12]163CF_Log_Local_Flush (CF_LOG_CTX * ctx)
164{
165 if (CF_File_Write (ctx->fd, ctx->buffer, ctx->length) < 0)
166 return CF_ERROR_LOG_FLUSH;
167
168 memset (ctx->buffer, 0x00, ctx->size);
169 ctx->length = 0;
170
171 return CF_OK;
172}
173
[35]174/**
175 * 로그 데이터 처리
176 *
177 * @return 성공 시, CF_OK; 실패 시, 오류 코드
178 *
179 * @param ctx 로그 컨텍스트
180 * @param buffer 로그 데이터
181 * @param demandSize 로그 데이터 길이
182 *
183 * @author vfire
184 */
185/* static */int
186CF_Log_Local_Push (CF_LOG_CTX * ctx,
187 const char * buffer,
188 const size_t demandSize)
189{
190 if( ctx->size > 0 ) /* 버퍼단위 버퍼링.... */
191 {
192 size_t writeSize;
193 size_t remainSize;
194
195 remainSize = demandSize;
196 while (remainSize)
197 {
198 writeSize = (ctx->size - ctx->length) < remainSize ? (ctx->size - ctx->length) : remainSize;
199
200 memcpy (ctx->buffer + ctx->length, buffer + demandSize - remainSize, writeSize);
201 ctx->length += writeSize;
202
203 if (ctx->length == ctx->size)
204 CF_Log_Local_Flush (ctx);
205
206 remainSize -= writeSize;
207 }
208 }
209 else /* flush되어야 함. */
210 {
211 if (CF_File_Write (ctx->fd, buffer, demandSize) < 0)
212 return CF_ERROR_LOG_FLUSH;
213 }
214
215 return CF_OK;
216}
217
218/**
219 * 로그를 사용하기 위해 초기화
220 *
221 * @return 성공 시, CF_OK; 실패 시, 오류 코드
222 *
223 * @param logPool 아이디 넘버 최대 값
224 */
[12]225int
226CF_Log_Initialize (const int logPool)
227{
228 memset (&gLogEnvironment, 0x00, sizeof (CF_LOG_ENVIRONMENT));
[11]229
[12]230 if (logPool > 0)
231 {
232 gLogEnvironment.ctxPool =
233 (CF_Log_Ctx *) calloc ((size_t) logPool, sizeof (CF_Log_Ctx));
[26]234 if (gLogEnvironment.ctxPool == NULL)
235 return CF_ERROR_LOG_INITIALIZE;
[12]236 gLogEnvironment.ctxSize = logPool;
237 }
[11]238
239 return CF_OK;
240}
241
[35]242/**
243 * 로그가 모두 사용된 후 자원 해제
244 *
245 * @return CF_OK 반환
246 */
[11]247int
248CF_Log_Finalize (void)
249{
[19]250 int mapid = 0;
251
252 for (mapid = 0 ; mapid < gLogEnvironment.ctxSize ; mapid++)
253 {
254 CF_Log_UnmapCtxID (mapid);
255 }
256
257 if (gLogEnvironment.ctxPool != NULL)
258 free (gLogEnvironment.ctxPool);
259
[12]260 memset (&gLogEnvironment, 0x00, sizeof (CF_LOG_ENVIRONMENT));
[11]261
262 return CF_OK;
263}
[12]264
[35]265/**
266 * 로그 컨텍스트 생성
267 *
268 * @return 성공 시, 로그 컨텍스트; 실패 시, NULL
269 *
270 * @param path 로그 파일 경로
271 * @param memsize 로그 버퍼 크기
272 *
273 * @see CF_LOG_BUFFER_DEFAULT, CF_LOG_BUFFER_NO
274 */
[12]275CF_Log_Ctx
276CF_Log_CreateCtx (const char * path,
277 const int memsize)
278{
279 int result = 0;
280 int size = (memsize == CF_LOG_BUFFER_DEFAULT)
281 ? CF_LOG_BUFFER_DEFAULT_SIZE
282 : memsize;
283 CF_LOG_CTX * context = NULL;
284
285 if (path == NULL)
286 return NULL;
287
288 TRY
289 {
290 context = (CF_LOG_CTX *) calloc (sizeof (CF_LOG_CTX), 1);
291 if (context == NULL)
292 {
293 result = -1;
294 TRY_BREAK;
295 }
296
297 context->fd = CF_File_Create (path);
298 if (context->fd < 0)
299 {
300 result = -2;
301 TRY_BREAK;
302 }
303 sprintf (context->path, "%s", path);
304
305 if (size > 0)
306 {
307 context->buffer = (char *) calloc ((size_t) size + 1, 1);
308 if (context->buffer == NULL)
309 {
310 result = -3;
311 TRY_BREAK;
312 }
313 context->size = (size_t) size;
314 }
315 }
316 CATCH_IF (result < 0)
317 {
318 CF_Log_DestroyCtx ((CF_Log_Ctx) context);
319 return NULL;
320 }
321
322 return (CF_Log_Ctx) context;
323}
324
[35]325/**
326 * 로그 컨텍스트 해제
327 *
328 * @return 성공 시, CF_OK; 실패 시, 오류 코드
329 *
330 * @param ctx 로그 컨텍스트
331 */
[12]332int
333CF_Log_DestroyCtx (CF_Log_Ctx ctx)
334{
335 CF_LOG_CTX * context = (CF_LOG_CTX *) ctx;
336
[25]337 if (CHECK_INVALID_CTX (ctx))
338 return CF_ERROR_LOG_INVALID_CTX;
[12]339
340 memset (context->path, 0x00, sizeof (context->path));
341
342 if (context->buffer != NULL)
343 {
[14]344 CF_Log_Flush (ctx);
[12]345 free (context->buffer);
346 context->buffer = NULL;
347 context->size = 0;
348 }
349
350 CF_File_Close (context->fd);
351
352 if (context->mutex != NULL)
353 {
354 if (CF_Mutex_Destory (&context->mutex) < 0)
355 /* do something ? */;
356
357 context->mutex = NULL;
358 }
359
360 return CF_OK;
361}
362
[35]363/**
364 * 로그 컨텍스트에 멀티쓰레드 모드 설정
365 *
366 * @return 성공 시, CF_OK; 실패 시, 오류 코드
367 *
368 * @param ctx 로그 컨텍스트
369 */
[12]370int
[19]371CF_Log_SetMultiThread (CF_Log_Ctx ctx)
372{
373 CF_LOG_CTX * context = (CF_LOG_CTX *) ctx;
374
[25]375 if (CHECK_INVALID_CTX (ctx))
376 return CF_ERROR_LOG_INVALID_CTX;
[19]377
378 if (CF_Mutex_Create (&context->mutex) < 0)
379 return CF_ERROR_LOG_SET_MULTITHREAD;
380
381 return CF_OK;
382}
383
[35]384/**
385 * 로그 컨텍스트에 멀티쓰레드 모드 설정 해제
386 *
387 * @return 성공 시, CF_OK; 실패 시, 오류 코드
388 *
389 * @param ctx 로그 컨텍스트
390 */
[19]391int
392CF_Log_UnsetMultiThread (CF_Log_Ctx ctx)
393{
394 CF_LOG_CTX * context = (CF_LOG_CTX *) ctx;
395
[25]396 if (CHECK_INVALID_CTX (ctx))
397 return CF_ERROR_LOG_INVALID_CTX;
[19]398
399 if (CF_Mutex_Destory (&context->mutex) < 0)
400 return CF_ERROR_LOG_UNSET_MULTITHREAD;
401
402 return CF_OK;
403}
404
[35]405/**
406 * 로그 컨텍스트에 따라 로그 쓰기
407 *
408 * @return 성공 시, CF_OK; 실패 시, 오류 코드
409 *
410 * @param ctx 로그 컨텍스트
411 * @param prefix 로그의 프리픽스 문자열
412 * @param fmt 포맷 스트링
413 * @param ... 가변 인자
414 */
[19]415int
[12]416CF_Log_Write (CF_Log_Ctx ctx,
417 const char * prefix,
418 const char * fmt, ...)
419{
420 CF_LOG_CTX * context = (CF_LOG_CTX *) ctx;
421 va_list valist;
[35]422 char buffer[16 * 1024] = {0x00,};
[34]423 char datetime[CF_LOG_DATETIME_LENGTH + 1] = {0x00,};
[12]424
[25]425 if (CHECK_INVALID_CTX (ctx))
426 return CF_ERROR_LOG_INVALID_CTX;
[12]427
428 LOCK_LOG_CTX (context);
429 va_start (valist, fmt);
430
[34]431 CF_Log_Local_GetTimeString (datetime);
432 sprintf (buffer, "[%s][%s] ", datetime, prefix);
[12]433 vsprintf (buffer + strlen (buffer), fmt, valist);
434
[35]435 CF_Log_Local_Push (context, buffer, strlen (buffer));
[12]436
437 va_end (valist);
438 UNLOCK_LOG_CTX (context);
439
440 return CF_OK;
441}
442
[35]443/**
444 * 로그 버퍼의 데이터를 즉시 로그 파일에 쓰기
445 *
446 * @return 성공 시, CF_OK; 실패 시, 오류 코드
447 *
448 * @param ctx 로그 컨텍스트
449 */
[12]450int
451CF_Log_Flush (CF_Log_Ctx ctx)
452{
453 CF_LOG_CTX * context = (CF_LOG_CTX *) ctx;
454
[25]455 if (CHECK_INVALID_CTX (ctx))
456 return CF_ERROR_LOG_INVALID_CTX;
[12]457
458 LOCK_LOG_CTX (context);
459 CF_Log_Local_Flush (context);
460 UNLOCK_LOG_CTX (context);
461
462 return CF_OK;
463}
[19]464
[35]465/**
466 * 로그 컨텍스트에 아이디 넘버 할당<br />
467 * 로그 기록 시, 아이디 넘버를 사용하면 해당 로그로 기록할 수 있음
468 *
469 * @return 성공 시, CF_OK; 실패 시, 오류 코드
470 *
471 * @param mapid 부여할 아이디 넘버
472 * @param ctx 로그 컨텍스트
473 *
474 * @remark 반드시 먼저 초기화 해야하며, 초기화 시에 주어진 번호보다 작은 아이디 넘버를 사용해야 함
475 *
476 * @see CF_LOG_OPEN, CF_Log_CreateCtx
477 */
[19]478int
479CF_Log_MapCtxID (const int mapid,
480 const CF_Log_Ctx ctx)
481{
[25]482 int result = 0;
[19]483
[25]484 TRY
485 {
486 if (CHECK_INITIALIZED())
487 {
488 result = CF_ERROR_LOG_NOT_INITIALIZE;
489 TRY_BREAK;
490 }
491 if (CHECK_INVALID_MAPID(mapid))
492 {
493 result = CF_ERROR_LOG_INVALID_MAPID;
494 TRY_BREAK;
495 }
496 if (CHECK_MAPPED_ID(mapid))
497 {
498 result = CF_ERROR_LOG_ALREADY_MAPPED_ID;
499 TRY_BREAK;
500 }
501 }
502 CATCH_IF (result < 0)
503 {
504 CF_Log_DestroyCtx (ctx);
505 return result;
506 }
[19]507
508 gLogEnvironment.ctxPool[mapid] = ctx;
509
510 return CF_OK;
511}
512
[35]513/**
514 * 아이디 넘버에 해당하는 로그를 닫고 해당하는 컨텍스트를 해제
515 *
516 * @return 성공 시, CF_OK; 실패 시, 오류 코드
517 *
518 * @param mapid 로그의 아이디 넘버
519 *
520 * @remark 아이디 넘버에 해당하는 컨텍스트가 해제되므로 주의
521 *
522 * @see CF_LOG_CLOSE, CF_Log_DestroyCtx
523 */
[19]524int
525CF_Log_UnmapCtxID (const int mapid)
526{
[25]527 if (CHECK_INITIALIZED())
[19]528 return CF_ERROR_LOG_NOT_INITIALIZE;
[25]529 if (CHECK_INVALID_MAPID(mapid))
530 return CF_ERROR_LOG_INVALID_MAPID;
531 if (!CHECK_MAPPED_ID(mapid))
[19]532 return CF_ERROR_LOG_NOT_MAPPED_ID;
533
534 CF_Log_DestroyCtx (gLogEnvironment.ctxPool[mapid]);
535
536 free (gLogEnvironment.ctxPool[mapid]);
537 gLogEnvironment.ctxPool[mapid] = NULL;
538
539 return CF_OK;
540}
541
[35]542/**
543 * 아이디 넘버에 해당하는 로그 컨텍스트를 얻기
544 *
545 * @return 성공 시, 로그 컨텍스트; 실패 시, NULL
546 *
547 * @param mapid 로그의 아이디 넘버
548 */
[19]549CF_Log_Ctx
550CF_Log_GetMappedCtx (const int mapid)
551{
[25]552 if (CHECK_INITIALIZED())
[19]553 return NULL;
[25]554 if (CHECK_INVALID_MAPID(mapid))
555 return NULL;
556 if (!CHECK_MAPPED_ID(mapid))
557 return NULL;
[19]558
559 return gLogEnvironment.ctxPool[mapid];
560}
Note: See TracBrowser for help on using the repository browser.