/** * \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_mutex.h" #include "cf_local.h" #include "cf_error.h" #include "cf_util.h" #include #include #include #include #include #if defined(_WIN32) || defined(_WIN64) # include #else #endif #define LOG_BUFFER_DEFAULT_SIZE 128 * 1024 #define ASSERT_CTX(__ctx) \ if (__ctx == NULL) \ return CF_ERROR_LOG_INVALID_CTX #define ASSERT_ARGS(x) \ if ((x)) \ return CF_ERROR_LOG_INVALID_ARGS /** 로그 컨텍스트 (cf_ctx의 구현) */ typedef struct __cf_log_ctx__ { cf_ctx file; char * buffer; size_t size; /* entire size of buffer */ size_t length; /* data length in current */ cf_ctx mutex; } CF_LOG_CONTEXT; static int CF_Log_Local_Flush (CF_LOG_CONTEXT * ctx) { if (CF_File_Write (ctx->file, 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_CONTEXT * ctx, const char * buffer, const size_t demandSize) { int result = 0; size_t writeSize; size_t remainSize; if (ctx->size > 0) /* 버퍼단위 버퍼링.... */ { 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 로그 컨텍스트 */ int CF_Log_SetMultiThread (cf_ctx ctx) { CF_LOG_CONTEXT * context = (CF_LOG_CONTEXT *) ctx; ASSERT_CTX (ctx); if (CF_Mutex_Create (&context->mutex) < 0) return CF_ERROR_LOG_SET_MULTITHREAD; return CF_OK; } /** * 로그 컨텍스트에 멀티쓰레드 모드 설정 해제 * * \return 성공 시, CF_OK; 실패 시, 오류 코드 * * \param ctx 로그 컨텍스트 */ int CF_Log_UnsetMultiThread (cf_ctx ctx) { CF_LOG_CONTEXT * context = (CF_LOG_CONTEXT *) ctx; ASSERT_CTX (ctx); if (CF_Mutex_Destory (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 valist 가변 인자 리스트 */ static int CF_Log_Local_WriteVA (CF_LOG_CONTEXT * context, const char * prefix, const char * fmt, va_list valist) { #define BUF_LEN 16 * 1024 char buffer[BUF_LEN + 1] = {0x00,}; char datetime[CF_UTIL_DATETIME_LENGTH + 1] = {0x00,}; int length = 0; int result = 0; CF_UTIL_DATETIME dt; ASSERT_CTX (context); CF_Util_GetCurrentTime (&dt); CF_Util_GetTimeString (&dt, datetime); length = snprintf (buffer, BUF_LEN, "[%s][%s] ", datetime, prefix); vsnprintf (&buffer[length], BUF_LEN - (size_t)length, fmt, valist); CF_Mutex_Lock (context->mutex); result = CF_Log_Local_Push (context, buffer, strlen (buffer)); CF_Mutex_Unlock (context->mutex); return result; } /** * 로그 컨텍스트에 따라 로그 쓰기 * * \return 성공 시, CF_OK; 실패 시, 오류 코드 * * \param ctx 로그 컨텍스트 * \param prefix 로그의 프리픽스 문자열 * \param fmt 포맷 스트링 * \param ... 가변 인자 */ int CF_Log_Write (cf_ctx ctx, const char * prefix, const char * fmt, ...) { CF_LOG_CONTEXT * context = (CF_LOG_CONTEXT *) ctx; int result = 0; va_list valist; va_start (valist, fmt); result = CF_Log_Local_WriteVA (context, prefix, fmt, valist); va_end (valist); return result; } /** * 로그 버퍼의 데이터를 즉시 로그 파일에 쓰기 * * \return 성공 시, CF_OK; 실패 시, 오류 코드 * * \param ctx 로그 컨텍스트 */ int CF_Log_Flush (cf_ctx ctx) { int result = 0; CF_LOG_CONTEXT * context = (CF_LOG_CONTEXT *) ctx; ASSERT_CTX (ctx); CF_Mutex_Lock (context->mutex); result = CF_Log_Local_Flush (context); CF_Mutex_Unlock (context->mutex); return result; } /** * 로그 컨텍스트 해제 * * \return 성공 시, CF_OK; 실패 시, 오류 코드 * * \param ctx 로그 컨텍스트 */ int CF_Log_Destroy (cf_ctx ctx) { CF_LOG_CONTEXT * context = (CF_LOG_CONTEXT *) ctx; ASSERT_CTX (ctx); if (context->size > 0) { CF_Log_Flush (ctx); free (context->buffer); context->buffer = NULL; context->size = 0; } CF_File_Close (context->file); CF_Mutex_Destory (context->mutex); context->mutex = NULL; free (context); return CF_OK; } /** * 로그 컨텍스트 생성 * * \return 성공 시, 로그 컨텍스트; 실패 시, NULL * * \param ctx 로그 컨텍스트 주소 * \param path 로그 파일 경로 * \param memsize 로그 버퍼 크기 * * \see CF_LOG_DEFAULT_BUFFER, CF_LOG_NO_BUFFER */ int CF_Log_Create (cf_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_CONTEXT * context = NULL; ASSERT_CTX (ctx); ASSERT_ARGS (path == NULL); TRY { context = NEWCTX (CF_LOG_CONTEXT); if (context == NULL) { result = CF_ERROR_LOG_CREATE_CTX; TRY_BREAK; } result = CF_File_Open (&context->file, path, fileFlag); if (result < 0) { result = CF_ERROR_LOG_CREATE_FILE; TRY_BREAK; } 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_ctx) context; } CATCH_IF (result < 0) { CF_Log_Destroy ((cf_ctx) context); } return result; }