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

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

#1 modify test.c for more detail test of multi-threaded logging

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