/** * \file cf_log.c * \author myusgun * \author vfire * * \brief ·Î±× ±¸Çö */ #if defined(_WIN32) || defined(_WIN64) # define _USE_32BIT_TIME_T # if (_MSC_VER >= 1400) # define localtime _localtime32 # endif #endif #include "cf_log.h" #include "cf_file.h" #include "cf_thread.h" #include "cf_local.h" #include "cf_error.h" #include #include #include #include #include #if defined(_WIN32) || defined(_WIN64) # include #else # include #endif #define ASSERT_CTX(__ctx) \ if (__ctx == NULL) \ return CF_ERROR_LOG_INVALID_CTX #define ASSERT_INIT() \ if (gLogArray.ctxPool == NULL || \ gLogArray.ctxSize <= 0 ) \ return CF_ERROR_LOG_NOT_INITIALIZE #define ASSERT_MAPID(__mapid) \ if (gLogArray.ctxSize <= __mapid) \ return CF_ERROR_LOG_INVALID_MAPID #define ASSERT_MAPPED_CTX(__mapid) \ if (gLogArray.ctxPool[__mapid] != NULL) \ return CF_ERROR_LOG_ALREADY_MAPPED_ID #define ASSERT_NOT_MAPPED_CTX(__mapid) \ if (gLogArray.ctxPool[__mapid] == NULL) \ return CF_ERROR_LOG_NOT_MAPPED_ID #define LOG_BUFFER_DEFAULT_SIZE 128 * 1024 #define LOG_DATETIME_LENGTH sizeof ("0000-00-00 00:00:00.000") - 1 /** ·Î±× ÄÁÅؽºÆ® (Opaque) */ typedef void * CF_Log_Ctx; typedef struct __cf_util_datetime__ { int year; int month; int day; int week; /* SUN:0 ~ SAT:6 */ int hour; int min; int sec; int usec; } CF_LOG_DATETIME; /** ·Î±× ÄÁÅؽºÆ® (CF_Log_CtxÀÇ ±¸Çö) */ typedef struct __cf_log_ctx__ { char path[NAME_LENGTH + 1]; int fd; char * buffer; size_t size; /* entire size of buffer */ size_t length; /* data length in current */ CF_Mutex_Ctx mutex; } CF_LOG_CTX; typedef struct __cf_log_array__ { CF_Log_Ctx * ctxPool; int ctxSize; } CF_LOG_ARRAY; static CF_LOG_ARRAY gLogArray; #if defined(_WIN32) || defined(_WIN64) /* {{{ */ struct timezone { int tz_minuteswest; /* minutes W of Greenwich */ int tz_dsttime; /* type of dst correction */ }; int gettimeofday (struct timeval *tv, struct timezone *tz) { FILETIME ft; unsigned __int64 buf =0; //static int tzflag = 0; if (NULL != tv) { GetSystemTimeAsFileTime (&ft); buf |= ft.dwHighDateTime; buf <<= 32; buf |= ft.dwLowDateTime; if (buf) { buf /= 10; buf -= ((369 * 365 + 89) * (unsigned __int64) 86400) * 1000000; } tv->tv_sec = (long)(buf / 1000000UL); tv->tv_usec = (long)(buf % 1000000UL); } /* if (NULL != tz) { if (!tzflag) { _tzset(); tzflag++; } // Adjust for the timezone west of Greenwich tz->tz_minuteswest = _timezone / 60; tz->tz_dsttime = _daylight; } */ return 0; } /* }}} */ #endif static int CF_Log_Local_GetTime (CF_LOG_DATETIME * dt) { struct timeval timeVal; struct tm * timeSt; gettimeofday (&timeVal, NULL); timeSt = localtime ((const time_t *)&timeVal.tv_sec); dt->year = timeSt->tm_year + 1900; dt->month = timeSt->tm_mon + 1; dt->day = timeSt->tm_mday; dt->week = timeSt->tm_wday; dt->hour = timeSt->tm_hour; dt->min = timeSt->tm_min; dt->sec = timeSt->tm_sec; dt->usec = (int) timeVal.tv_usec; return CF_OK; } static int CF_Log_Local_GetTimeString (char * buffer) { CF_LOG_DATETIME dt; CF_Log_Local_GetTime (&dt); snprintf (buffer, LOG_DATETIME_LENGTH, "%02d-%02d-%02d %02d:%02d:%02d.%03d", dt.year, dt.month, dt.day, dt.hour, dt.min, dt.sec, dt.usec); return CF_OK; } static int CF_Log_Local_Flush (CF_LOG_CTX * ctx) { if (CF_File_Write (ctx->fd, ctx->buffer, ctx->length) < 0) return CF_ERROR_LOG_FLUSH; ctx->length = 0; return CF_OK; } /** * ·Î±× µ¥ÀÌÅÍ Ã³¸® * * \return ¼º°ø ½Ã, CF_OK; ½ÇÆÐ ½Ã, ¿À·ù ÄÚµå * * \param ctx ·Î±× ÄÁÅؽºÆ® * \param buffer ·Î±× µ¥ÀÌÅÍ * \param demandSize ·Î±× µ¥ÀÌÅÍ ±æÀÌ * * \author vfire */ /* static */int CF_Log_Local_Push (CF_LOG_CTX * ctx, const char * buffer, const size_t demandSize) { int result = CF_OK; if (ctx->size > 0) /* ¹öÆÛ´ÜÀ§ ¹öÆÛ¸µ.... */ { size_t writeSize; size_t remainSize; remainSize = demandSize; while (remainSize) { writeSize = (ctx->size - ctx->length) < remainSize ? (ctx->size - ctx->length) : remainSize; memcpy (ctx->buffer + ctx->length, buffer + demandSize - remainSize, writeSize); ctx->length += writeSize; if (ctx->length == ctx->size) CF_Log_Local_Flush (ctx); remainSize -= writeSize; } } else /* flushµÇ¾î¾ß ÇÔ. */ { ctx->buffer = (char *)buffer; ctx->length = demandSize; result = CF_Log_Local_Flush (ctx); } return result; } /** * ·Î±× ÄÁÅؽºÆ®¿¡ ¸ÖƼ¾²·¹µå ¸ðµå ¼³Á¤ * * \return ¼º°ø ½Ã, CF_OK; ½ÇÆÐ ½Ã, ¿À·ù ÄÚµå * * \param ctx ·Î±× ÄÁÅؽºÆ® * * \see CF_Log_UnsetMultiThread */ static int CF_Log_SetMultiThread (CF_Log_Ctx ctx) { CF_LOG_CTX * context = (CF_LOG_CTX *) ctx; ASSERT_CTX (ctx); if (CF_Mutex_CreateCtx (&context->mutex) < 0) return CF_ERROR_LOG_SET_MULTITHREAD; return CF_OK; } /** * ·Î±× ÄÁÅؽºÆ®¿¡ ¸ÖƼ¾²·¹µå ¸ðµå ¼³Á¤ ÇØÁ¦ * * \return ¼º°ø ½Ã, CF_OK; ½ÇÆÐ ½Ã, ¿À·ù ÄÚµå * * \param ctx ·Î±× ÄÁÅؽºÆ® * * \see CF_Log_SetMultiThread */ static int CF_Log_UnsetMultiThread (CF_Log_Ctx ctx) { CF_LOG_CTX * context = (CF_LOG_CTX *) ctx; ASSERT_CTX (ctx); if (CF_Mutex_DestoryCtx (context->mutex) < 0) return CF_ERROR_LOG_UNSET_MULTITHREAD; context->mutex = NULL; return CF_OK; } /** * ·Î±× ÄÁÅؽºÆ®¿¡ µû¶ó ·Î±× ¾²±â * * \return ¼º°ø ½Ã, CF_OK; ½ÇÆÐ ½Ã, ¿À·ù ÄÚµå * * \param ctx ·Î±× ÄÁÅؽºÆ® * \param prefix ·Î±×ÀÇ ÇÁ¸®ÇȽº ¹®ÀÚ¿­ * \param fmt Æ÷¸Ë ½ºÆ®¸µ * \param ... °¡º¯ ÀÎÀÚ */ static int CF_Log_WriteCtx (CF_Log_Ctx ctx, const char * prefix, const char * fmt, // ...) va_list valist) { #define BUF_LEN 16 * 1024 CF_LOG_CTX * context = (CF_LOG_CTX *) ctx; // va_list valist; char buffer[BUF_LEN + 1] = {0x00,}; char datetime[LOG_DATETIME_LENGTH + 1] = {0x00,}; int length = 0; ASSERT_CTX (ctx); CF_Mutex_Lock (context->mutex); // va_start (valist, fmt); CF_Log_Local_GetTimeString (datetime); length = snprintf (buffer, BUF_LEN, "[%s][%s] ", datetime, prefix); vsnprintf (&buffer[length], BUF_LEN - (size_t)length, fmt, valist); CF_Log_Local_Push (context, buffer, strlen (buffer)); // va_end (valist); CF_Mutex_Unlock (context->mutex); return CF_OK; } /** * ·Î±× ¹öÆÛÀÇ µ¥ÀÌÅ͸¦ Áï½Ã ·Î±× ÆÄÀÏ¿¡ ¾²±â * * \return ¼º°ø ½Ã, CF_OK; ½ÇÆÐ ½Ã, ¿À·ù ÄÚµå * * \param ctx ·Î±× ÄÁÅؽºÆ® */ static int CF_Log_FlushCtx (CF_Log_Ctx ctx) { CF_LOG_CTX * context = (CF_LOG_CTX *) ctx; ASSERT_CTX (ctx); CF_Mutex_Lock (context->mutex); CF_Log_Local_Flush (context); CF_Mutex_Unlock (context->mutex); return CF_OK; } /** * ·Î±× ÄÁÅؽºÆ® ÇØÁ¦ * * \return ¼º°ø ½Ã, CF_OK; ½ÇÆÐ ½Ã, ¿À·ù ÄÚµå * * \param ctx ·Î±× ÄÁÅؽºÆ® */ static int CF_Log_DestroyCtx (CF_Log_Ctx ctx) { CF_LOG_CTX * context = (CF_LOG_CTX *) ctx; ASSERT_CTX (ctx); if (context->size > 0) { CF_Log_FlushCtx (ctx); free (context->buffer); context->buffer = NULL; context->size = 0; } CF_File_Close (context->fd); CF_Mutex_DestoryCtx (context->mutex); context->mutex = NULL; return CF_OK; } /** * ·Î±× ÄÁÅؽºÆ® »ý¼º * * \return ¼º°ø ½Ã, ·Î±× ÄÁÅؽºÆ®; ½ÇÆÐ ½Ã, NULL * * \param ctx ·Î±× ÄÁÅؽºÆ® ÁÖ¼Ò * \param path ·Î±× ÆÄÀÏ °æ·Î * \param memsize ·Î±× ¹öÆÛ Å©±â * * \see CF_LOG_DEFAULT_BUFFER, CF_LOG_NO_BUFFER */ static int CF_Log_CreateCtx (CF_Log_Ctx * ctx, const char * path, const int memsize) { int result = 0; int fileFlag = CF_FILE_CREATE|CF_FILE_WRITE|CF_FILE_APPEND; int size = (memsize == CF_LOG_DEFAULT_BUFFER) ? LOG_BUFFER_DEFAULT_SIZE : memsize; CF_LOG_CTX * context = NULL; if (path == NULL) return CF_ERROR_LOG_INVALID_ARGS; TRY { context = (CF_LOG_CTX *) calloc (sizeof (CF_LOG_CTX), 1); if (context == NULL) { result = CF_ERROR_LOG_CREATE_CTX; TRY_BREAK; } context->fd = CF_File_Open (path, fileFlag); if (context->fd < 0) { result = CF_ERROR_LOG_CREATE_FILE; TRY_BREAK; } snprintf (context->path, sizeof (context->path) -1, "%s", path); if (size > 0) { context->buffer = (char *) calloc ((size_t) size + 1, 1); if (context->buffer == NULL) { result = CF_ERROR_LOG_ALLOCATE_BUFFER; TRY_BREAK; } context->size = (size_t) size; } *ctx = (CF_Log_Ctx) context; } CATCH_IF (result < 0) { CF_Log_DestroyCtx ((CF_Log_Ctx) context); } return result; } /** * ·Î±× ÄÁÅؽºÆ®¿¡ ¾ÆÀ̵ð ³Ñ¹ö ÇÒ´ç
* ·Î±× ±â·Ï ½Ã, ¾ÆÀ̵ð ³Ñ¹ö¸¦ »ç¿ëÇϸé ÇØ´ç ·Î±×·Î ±â·ÏÇÒ ¼ö ÀÖÀ½ * * \return ¼º°ø ½Ã, CF_OK; ½ÇÆÐ ½Ã, ¿À·ù ÄÚµå * * \param mapid ºÎ¿©ÇÒ ¾ÆÀ̵ð ³Ñ¹ö * \param ctx ·Î±× ÄÁÅؽºÆ® * * \remarks ¹Ýµå½Ã ¸ÕÀú ÃʱâÈ­ ÇؾßÇϸç, ÃʱâÈ­ ½Ã¿¡ ÁÖ¾îÁø ¹øÈ£º¸´Ù ÀÛÀº ¾ÆÀ̵ð ³Ñ¹ö¸¦ »ç¿ë * * \see CF_LOG_OPEN, CF_Log_CreateCtx */ static int CF_Log_MapCtxID (const int mapid, const CF_Log_Ctx ctx) { ASSERT_INIT (); ASSERT_MAPID (mapid); ASSERT_MAPPED_CTX (mapid); gLogArray.ctxPool[mapid] = ctx; return CF_OK; } /** * ¾ÆÀ̵ð ³Ñ¹ö¿¡ ÇØ´çÇÏ´Â ·Î±×¸¦ ´Ý°í ÇØ´çÇÏ´Â ÄÁÅؽºÆ®¸¦ ÇØÁ¦ * * \return ¼º°ø ½Ã, CF_OK; ½ÇÆÐ ½Ã, ¿À·ù ÄÚµå * * \param mapid ·Î±×ÀÇ ¾ÆÀ̵ð ³Ñ¹ö * * \remarks ¾ÆÀ̵ð ³Ñ¹ö¿¡ ÇØ´çÇÏ´Â ÄÁÅؽºÆ®°¡ ÇØÁ¦µÇ¹Ç·Î ÁÖÀÇ * * \see CF_LOG_CLOSE, CF_Log_DestroyCtx */ static int CF_Log_UnmapCtxID (const int mapid) { ASSERT_INIT (); ASSERT_MAPID (mapid); ASSERT_NOT_MAPPED_CTX (mapid); CF_Log_DestroyCtx (gLogArray.ctxPool[mapid]); free (gLogArray.ctxPool[mapid]); gLogArray.ctxPool[mapid] = NULL; return CF_OK; } /** * ¾ÆÀ̵ð ³Ñ¹ö¿¡ ÇØ´çÇÏ´Â ·Î±× ÄÁÅؽºÆ®¸¦ ¾ò±â * * \return ¼º°ø ½Ã, CF_OK; ½ÇÆÐ ½Ã, ¿À·ù ÄÚµå * * \param mapid ·Î±×ÀÇ ¾ÆÀ̵ð ³Ñ¹ö * \param ctx ·Î±× ÄÁÅؽºÆ® ¹ÞÀ» ÁÖ¼Ò */ static int CF_Log_GetMappedCtx (const int mapid, CF_Log_Ctx * ctx) { ASSERT_INIT (); ASSERT_MAPID (mapid); ASSERT_NOT_MAPPED_CTX (mapid); *ctx = gLogArray.ctxPool[mapid]; return CF_OK; } /** * ·Î±×¸¦ »ç¿ëÇϱâ À§ÇØ ÃʱâÈ­ * * \return ¼º°ø ½Ã, CF_OK; ½ÇÆÐ ½Ã, ¿À·ù ÄÚµå * * \param poolSize ·Î±× Ç® Å©±â·Î, ·Î±× ¾ÆÀ̵ð ³Ñ¹öÀÇ ÃÖ´ë °ª */ int CF_Log_Initialize (const int poolSize) { memset (&gLogArray, 0x00, sizeof (CF_LOG_ARRAY)); if (poolSize > 0) { gLogArray.ctxPool = (CF_Log_Ctx *) calloc ((size_t) poolSize, sizeof (CF_Log_Ctx)); if (gLogArray.ctxPool == NULL) return CF_ERROR_LOG_INITIALIZE; gLogArray.ctxSize = poolSize; } return CF_OK; } /** * ·Î±×°¡ ¸ðµÎ »ç¿ëµÈ ÈÄ ÀÚ¿ø ÇØÁ¦ * * \return CF_OK ¹Ýȯ */ int CF_Log_Finalize (void) { int mapid = 0; for (mapid = 0 ; mapid < gLogArray.ctxSize ; mapid++) { CF_Log_UnmapCtxID (mapid); } if (gLogArray.ctxPool != NULL) free (gLogArray.ctxPool); memset (&gLogArray, 0x00, sizeof (CF_LOG_ARRAY)); return CF_OK; } /** * ·Î±× ¿­±â * * \return ¼º°ø ½Ã, CF_OK; ½ÇÆÐ ½Ã, ¿À·ù ÄÚµå * * \param mapid ·Î±×ÀÇ ¾ÆÀ̵ð ³Ñ¹ö * \param path ·Î±× ÆÄÀÏ °æ·Î * \param memsize ·Î±× ¹öÆÛ Å©±â * * \see CF_LOG_DEFAULT_BUFFER, CF_LOG_NO_BUFFER */ int CF_Log_Open (const int mapid, const char * path, const int memsize) { int result = 0; CF_Log_Ctx ctx = NULL; result = CF_Log_CreateCtx (&ctx, path, memsize); if (result < 0) return result; result = CF_Log_MapCtxID (mapid, ctx); if (result < 0) CF_Log_DestroyCtx (ctx); return result; } /** * ·Î±× ´Ý±â * * \return ¼º°ø ½Ã, CF_OK; ½ÇÆÐ ½Ã, ¿À·ù ÄÚµå * * \param mapid ·Î±×ÀÇ ¾ÆÀ̵ð ³Ñ¹ö */ int CF_Log_Close (const int mapid) { return CF_Log_UnmapCtxID (mapid); } /** * ·Î±× ¹öÆÛÀÇ µ¥ÀÌÅ͸¦ Áï½Ã ·Î±× ÆÄÀÏ¿¡ ¾²±â * * \return ¼º°ø ½Ã, CF_OK; ½ÇÆÐ ½Ã, ¿À·ù ÄÚµå * * \param mapid ·Î±×ÀÇ ¾ÆÀ̵ð ³Ñ¹ö */ int CF_Log_Flush (const int mapid) { int result = 0; CF_Log_Ctx ctx = NULL; result = CF_Log_GetMappedCtx (mapid, &ctx); if (result < 0) return result; result = CF_Log_FlushCtx (ctx); return result; } /** * ·Î±× ÄÁÅؽºÆ®¿¡ ¸ÖƼ¾²·¹µå ¸ðµå ¼³Á¤ * * \return ¼º°ø ½Ã, CF_OK; ½ÇÆÐ ½Ã, ¿À·ù ÄÚµå * * \param mapid ·Î±×ÀÇ ¾ÆÀ̵ð ³Ñ¹ö * \param flag ¼³Á¤/ÇØÁ¦ bool Ç÷¡±× * * \see CF_BOOL */ int CF_Log_SetMT (const int mapid, const CF_BOOL flag) { int result = 0; CF_Log_Ctx ctx = NULL; result = CF_Log_GetMappedCtx (mapid, &ctx); if (result < 0) return result; result = (flag) ? CF_Log_SetMultiThread (ctx) : CF_Log_UnsetMultiThread (ctx); return result; } /** * ·Î±× ¾²±â * * \return ¼º°ø ½Ã, CF_OK; ½ÇÆÐ ½Ã, ¿À·ù ÄÚµå * * \param mapid ·Î±×ÀÇ ¾ÆÀ̵ð ³Ñ¹ö * \param prefix ·Î±×ÀÇ ÇÁ¸®ÇȽº ¹®ÀÚ¿­ * \param fmt Æ÷¸Ë ½ºÆ®¸µ * \param ... °¡º¯ ÀÎÀÚ */ int CF_Log_Write (const int mapid, const char * prefix, const char * fmt, ...) { int result = 0; CF_Log_Ctx ctx = NULL; va_list valist; result = CF_Log_GetMappedCtx (mapid, &ctx); if (result < 0) return result; va_start (valist, fmt); result = CF_Log_WriteCtx (ctx, prefix, fmt, valist); va_end (valist); return result; }