/* vim: ts=4 sw=4 sts=4 ff=unix fenc=utf-8 : * ===================================================================== * sc_logger.c * Copyright (c) 2003 - 2011 sys0tem * LICENSE : * LGPL (GNU Lesser General Public License - Version 3,29 June 2007) * http://www.gnu.org/copyleft/lesser.html * or * EPL (Eclipse Public License - v1.0) * http://www.eclipse.org/legal/epl-v10.html * ===================================================================== */ #include <stdio.h> #include <stdlib.h> #include <stdarg.h> #include <time.h> #include <sc_string.h> #include <sc_stdbool.h> #include <sc_map.h> #include <sc_logger.h> #define SC_LOGGER_HEADER_SIZE (1024) #define SC_LOGGER_CATEGORY_MAP_SIZE (32) static SC_Map* SC_Logger_categoryMap = NULL; static bool SC_Logger_isInit = false; /* -------------------------------------------------------------------- * 定数定義 * -------------------------------------------------------------------- */ /* -------------------------------------------------------------------- * 内部変数 * -------------------------------------------------------------------- */ /* -------------------------------------------------------------------- * 内部関数 プロトタイプ宣言 * -------------------------------------------------------------------- */ static SC_Logger_Config* SC_Logger_getConfig(const char* category); static void SC_Logger_getDate(char* buff); static const char* SC_Logger_getLevelString(SC_LogLevel level); static void SC_Logger_logprint(SC_Logger_Config* config, const char* file, int line, const char* format, va_list ap); /** * ログの基本設定を行います。 * * @param category カテゴリ * @param config 設定構造体 * @return true/false (設定完了/設定失敗) */ bool SC_Logger_setLogConfig(const char* category, SC_Logger_Config* config) { if (!SC_Logger_isInit) { /* 初期化 */ SC_Logger_categoryMap = SC_Map_new(SC_LOGGER_CATEGORY_MAP_SIZE); if (SC_Logger_categoryMap == NULL) { return false; } SC_Logger_isInit = true; } SC_Logger_categoryMap->put(SC_Logger_categoryMap, category, config, sizeof(SC_Logger_Config)); return true; } /** * ログをクリーンアップ(停止)します。 */ void SC_Logger_cleanup(void) { if (SC_Logger_isInit) { /* 初期化している場合のみクリーンアップする */ SC_Map_delete(SC_Logger_categoryMap); SC_Logger_isInit = false; } } /** * ログを書き出します。 */ void SC_Logger_log_(const char* category, SC_LogLevel level, const char* file, int line, const char* format, ...) { va_list ap; va_start(ap, format); SC_Logger_Config* config; if (SC_Logger_isInit) { /* 初期化されている場合のみログ出力する */ config = SC_Logger_getConfig(category); if (config->level >= level) { /* ログ出力 */ SC_Logger_logprint(config, file, line, format, ap); } } va_end(ap); } /** * 指定されたカテゴリに対応する SC_Logger_Config を取得します。 * * @param category カテゴリ * @return SC_Logger_Config */ static SC_Logger_Config* SC_Logger_getConfig(const char* category) { SC_String* key; size_t len; int pos; SC_Logger_Config* config = NULL; len = strlen(category) + 1; key = SC_String_new(len); key->append(key, category); config = SC_Logger_categoryMap->get(SC_Logger_categoryMap, key->buffer, NULL); while ((config == NULL) && (key->buffer[0] != '\0')) { pos = SC_lastIndexOf(key->buffer, "/"); if (pos < 0) { key->setLength(key, 0); } config = SC_Logger_categoryMap->get(SC_Logger_categoryMap, key->buffer, NULL); } return config; } /** * ログを出力します。 * * @param config ログ設定 * @param file ファイル * @param line 行番号 * @param format フォーマット * @param ap 引数 */ static void SC_Logger_logprint(SC_Logger_Config* config, const char* file, int line, const char* format, va_list ap) { char logBuffer[SC_LOGGER_HEADER_SIZE]; int len, tmpLen; memset(logBuffer, '\0', sizeof(logBuffer)); SC_Logger_getDate(logBuffer); strcat(logBuffer, " "); strcat(logBuffer, SC_Logger_getLevelString(config->level)); len = strlen(logBuffer); tmpLen = snprintf(&logBuffer[len], SC_LOGGER_HEADER_SIZE - len - 1, " %s:%d ", file, line); if (tmpLen > 0) { len += tmpLen; } vsnprintf(&logBuffer[len], SC_LOGGER_HEADER_SIZE - len - 1, format, ap); logBuffer[SC_LOGGER_HEADER_SIZE - 1] = '\0'; switch (config->type) { case LOGGER_STDOUT: printf("%s\n", logBuffer); break; case LOGGER_STDERR: fprintf(stderr, "%s\n", logBuffer); break; case LOGGER_MEMORY: break; case LOGGER_FILE: break; case LOGGER_SYSLOG: break; default: break; } } /** * 現在時刻を取得します。 * バッファサイズは、21 byte 以上必要です。 * * @param buff 時刻を格納するバッファ */ static void SC_Logger_getDate(char* buff) { time_t nowTime; struct tm *t_st; /* 現在時刻取得 */ time(&nowTime); /* 現在時刻を構造体に変換 */ t_st = localtime(&nowTime); /* フォーマットしてバッファに格納する */ sprintf(buff, "%04d/%02d/%02d %02d:%02d:%02d", t_st->tm_year + 1900, t_st->tm_mon + 1, t_st->tm_mday, t_st->tm_hour, t_st->tm_min, t_st->tm_sec); } /** * level の文字列表現を返します。 * * @param level レベル * @return 文字列表現 */ static const char* SC_Logger_getLevelString(SC_LogLevel level) { switch (level) { case LOGGER_FATAL: return "[FATAL ]"; case LOGGER_ALERT: return "[ALERT ]"; case LOGGER_CRIT: return "[CRIT ]"; case LOGGER_ERR: return "[ERR ]"; case LOGGER_WARNING: return "[WARNING]"; case LOGGER_NOTICE: return "[NOTICE ]"; case LOGGER_INFO: return "[INFO ]"; case LOGGER_DEBUG: return "[DEBUG ]"; default: return "[DEFAULT]"; } }