C言語のログ(logging:ロギング)処理(4)
改造前の自作ロギング処理
とりあえず改造前の自作のロギング処理を掲載できるように削って載せてみる。
まぁ最低限ログ出力するにはこれくらいでよいのだが...
まず、世代管理を改造していこう。
今のこのプログラムでは application.log.1 から application.log.2 application.log.3 と末尾の番号が増えていき最大値超えたらまた1に戻るのだが、log4xなどでは、常にapplication.log に出力され、世代として末尾番号付きにスライドしていく。
そこから改造します。
//TinyLogger.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include "logger.h"
// カレントログNo
int gLogCurNo;
int main(void) {
gLogCurNo = 0;
// ログファイルのデフォルトパス
strcpy(gIniValLog.logFilePathName, LOG_FILE_DEF_PATH);
// ログファイルのデフォルト最大サイズ
gIniValLog.logFileSizeMax = LOG_FILE_DEF_SIZE_MAX;
// ログファイルのデフォルト最大世代数
gIniValLog.logFileNumMax = LOG_FILE_DEF_NUM_MAX;
// ログファイルNo取得
getCurrentLogFileNo(LOG_TYPE_APL);
LogError(M000001, "main");
return EXIT_SUCCESS;
}
//logger.h
#ifndef LOGGER_H_
#define LOGGER_H_
//#define LOG_FILE_SIZE_KB 1024 // KB
#define LOG_FILE_SIZE_MAX_COL_MAX 7 // ログファイルサイズカラム数
#define LOG_FILE_SIZE_MAX_MIN 1 // ログファイルサイズの最大サイズKB(1)
#define LOG_FILE_SIZE_MAX_MAX 2097151 // ログファイルサイズの最大サイズKB個数(2097151)7FFFFC00
#define LOG_FILE_NUM_MAX_COL_MAX 2 // カラム数
#define LOG_FILE_NUM_MAX_MIN 1 // ログファイル世代数の最小値
#define LOG_FILE_NUM_MAX_MAX 999 // ログファイル世代数の最大値
#define LOG_FILE_DEF_PATH "." // ログファイルのデフォルトパス
#define LOG_FILE_DEF_SIZE_MAX 1024 // ログファイルのデフォルト最大サイズ
#define LOG_FILE_DEF_NUM_MAX 3 // ログファイルのデフォルト最大世代数
#define LOG_LEVEL_TRACE (0) // トレース
#define LOG_LEVEL_DEBUG (20) // デバッグ
#define LOG_LEVEL_INFO (40) // インフォメーション
#define LOG_LEVEL_WARN (60) // ワーニング
#define LOG_LEVEL_ERROR (80) // エラー
#define LOG_LEVEL_FATAL (100) // フェータル
#define LogTrace(...) putLog(LOG_LEVEL_TRACE, __FILE__, __LINE__, __func__, __VA_ARGS__)
#define LogDebug(...) putLog(LOG_LEVEL_DEBUG, __FILE__, __LINE__, __func__, __VA_ARGS__)
#define LogInfo(...) putLog(LOG_LEVEL_INFO, __FILE__, __LINE__, __func__, __VA_ARGS__)
#define LogWarn(...) putLog(LOG_LEVEL_WARN, __FILE__, __LINE__, __func__, __VA_ARGS__)
#define LogError(...) putLog(LOG_LEVEL_ERROR, __FILE__, __LINE__, __func__, __VA_ARGS__)
#define LogFatal(...) putLog(LOG_LEVEL_FATAL, __FILE__, __LINE__, __func__, __VA_ARGS__)
#define M000001 "A system call error occurred: FUNC=[%s]"
#define M000002 "A system call error occurred: FUNC=[%s] ERRNO=[%d]"
#define M000003 "A system call error occurred: FUNC=[%s] ERRNO=[%d] FILE=[%s]"
#define LOG_TYPE_APL 0
typedef struct _INI_VALUE_LOG{
char logFilePathName[1024]; // ログファイルのパス名(フルパス)
int logFileSizeMax; // ログファイルの最大サイズ(KB)
int logFileNumMax; // ログファイルの最大世代数(1-99)
}INI_VALUE_LOG;
// ログファイルNo 0:ファイル未確定 1以上:ファイル名確定中
extern int gLogCurNo;
extern INI_VALUE_LOG gIniValLog;
void getCurrentLogFileNo(int);
void putLog(int, char *, int, const char *, char *, ...);
#endif /* LOGGER_H_ */
//logger.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/stat.h>
#include <pthread.h>
#include <stdarg.h>
#include <sys/time.h>
#include "logger.h"
#define LOG_FILE_SIZE_KB 1024
// ログファイル名
#define LOG_FILE_NAME "application"
// 日時文字列フォーマット YYYY-MM-DD HH:MM:SS.ssssss
#define LOG_DATETIME_FORMAT "%04d-%02d-%02d %02d:%02d:%02d.%06d"
// グローバル変数
// ログファイルNo 0:ファイル未確定 1以上:ファイル名確定中
int gLogCurNo;
// INI定義値(LOG)
INI_VALUE_LOG gIniValLog;
// ログファイル排他用ミューテックス
pthread_mutex_t mutexLog = PTHREAD_MUTEX_INITIALIZER;
/**
* 現在日時文字列取得処理\n
* マシンの現在日時文字列を取得する(ローケル)
* @param [in] format 日付フォーマット
* @param [in] dateTimeLen 現在日時文字列の格納領域サイズ
* @param [out] dateTime 現在日時文字列(日付フォーマットの形式)を設定する 正常終了でも設定できていない場合があるので呼び出し元で設定文字列長をチェックする事
* @return 0:正常終了 -1:異常終了
*/
int getDateTime(char *format, int dateTimeLen, char *dateTime)
{
int ret = 0;
struct timeval tiemvalData;
struct tm *timeData;
ret = gettimeofday(&tiemvalData, NULL);
if(ret == -1){
return(-1);
}
timeData = localtime(&tiemvalData.tv_sec);
if(timeData == NULL){
return(-1);
}
sprintf(dateTime, format,
timeData->tm_year + 1900,
timeData->tm_mon + 1,
timeData->tm_mday,
timeData->tm_hour,
timeData->tm_min,
timeData->tm_sec,
tiemvalData.tv_usec);
return(ret);
}
/**
* ログ出力処理\n
* ログをファイルに出力します\n
* 定義ファイルで出力先ディレクトリ、ログファイルサイズ、ログ世代の定義が可能
* @param [in] logLevel ログレベル
* @param [in] programFileName プログラムファイル名
* @param [in] programLineNo プログラムファイル行番号
* @param [in] programFuncName プログラム関数名
* @param [in] format 可変文字列
*/
void putLog(int logLevel, char *programFileName, int programLineNo, const char *programFuncName, char *format, ...)
{
int ret; // 戻り値
char dateTime[32]; // 現在日時
struct stat stStat; // ファイル情報の格納領域
FILE *fp; // ファイルディスクプリタ
char fileName[2048]; // ログファイル名
off_t fileSize; // ファイルサイズ
va_list vaList; // 可変文字列
int renewFile; // ファイル作成し直し要否
// 現在日時文字列取得(YYYY-MM-DD HH:MM:SS.ssssss)
memset(dateTime, 0x0, sizeof(dateTime));
ret = getDateTime(LOG_DATETIME_FORMAT, sizeof(dateTime), dateTime);
if(ret != 0){
// エラーの場合ログ出力なし
return;
}
ret = pthread_mutex_lock(&mutexLog);
// ファイル名を取得
memset(fileName, 0x0, sizeof(fileName));
sprintf(fileName, "%s/%s%06d.log", gIniValLog.logFilePathName, LOG_FILE_NAME, gLogCurNo);
ret = stat(fileName, &stStat);
if(ret != 0){
// エラー
// ファイルが存在しない場合、ファイルサイズに0を設定
fileSize = 0;
}
else{
// ファイルサイズ
fileSize = stStat.st_size;
}
// ファイルサイズチェック
renewFile = 0;
if(fileSize >= gIniValLog.logFileSizeMax * LOG_FILE_SIZE_KB){
renewFile = 1;
gLogCurNo++;
if(gLogCurNo > gIniValLog.logFileNumMax){
gLogCurNo = 1;
}
}
// ログファイル名取得(フルパス)
sprintf(fileName, "%s/%s.log.%d", gIniValLog.logFilePathName, LOG_FILE_NAME, gLogCurNo);
fp = NULL;
if(renewFile == 0){
// 既存ファイルに追加書き込み(ファイルがなければ新規作成)
fp = fopen(fileName, "a");
}
else{
// ファイルを新規作成し直す
fp = fopen(fileName, "w");
}
if(fp == NULL){
// ファイルオープンエラー
// ファイルパス/ファイル名が不正の場合
// パーミッションエラーの場合
// ログ出力しない
;
}
else{
va_start(vaList, format);
// 現在日時出力
fprintf(fp, "%s ", dateTime);
// ログレベル出力
switch(logLevel){
case LOG_LEVEL_TRACE:
fprintf(fp, "TRACE ");
break;
case LOG_LEVEL_DEBUG:
fprintf(fp, "DEBUG ");
break;
case LOG_LEVEL_INFO:
fprintf(fp, "INFO ");
break;
case LOG_LEVEL_WARN:
fprintf(fp, "WARN ");
break;
case LOG_LEVEL_ERROR:
fprintf(fp, "ERROR ");
break;
case LOG_LEVEL_FATAL:
fprintf(fp, "FATAL ");
break;
}
// ファイル名、行番号出力、関数名
fprintf(fp, "%s(%04d):%-20s ", programFileName, programLineNo, programFuncName);
// 可変文字列出力
vfprintf(fp, format, vaList);
// 改行
fprintf(fp, "\n");
va_end(vaList);
// ファイルフラッシュ
ret = fflush(fp);
if(ret == -1){
// ファイルフラッシュエラー
;
}
// ファイルクローズ
ret = fclose(fp);
if(ret == -1){
// ファイルクローズエラー
;
}
}
ret = pthread_mutex_unlock(&mutexLog);
return;
}
/**
* カレントログファイル番号取得処理\n
* 存在するログファイルで更新日付が最新の拡張子番号(世代番号)をグローバル変数に設定します\n
* ログファイルが存在しない場合は1を設定します
*
* @param fileType ファイル種別
*/
void getCurrentLogFileNo(int fileType)
{
// カウンタ
int cnt;
// ファイル情報の格納領域
struct stat stStat;
// ファイル名
char fileName[2048];
// ファイル更新日時
long fileUpdateTime;
int fileNumMax = 0;
char filePathName[1024];
char fileNameBase[64];
// ログファイルNo
switch(fileType){
case LOG_TYPE_APL:
gLogCurNo = 0;
fileNumMax = gIniValLog.logFileNumMax;
strcpy(filePathName, gIniValLog.logFilePathName);
strcpy(fileNameBase, LOG_FILE_NAME);
break;
}
// ファイル更新日時 初期値設定
fileUpdateTime = 0L;
// 更新日時の最も後日付のファイルを取得
for(cnt = 0; cnt < fileNumMax; cnt++){
// ファイル名設定
sprintf(fileName,"%s/%s%06d.log", filePathName, fileNameBase, cnt + 1);
if((stat(fileName, &stStat)) != 0){
// エラーは継続
continue;
}
// ファイル更新日付 st_mtime:更新日時
if(stStat.st_mtime >= fileUpdateTime){
fileUpdateTime = stStat.st_mtime;
switch(fileType){
case LOG_TYPE_APL:
gLogCurNo = cnt + 1;
break;
}
}
}
// 1ファイルも存在しない場合は1を設定
switch(fileType){
case LOG_TYPE_APL:
if(gLogCurNo == 0){
gLogCurNo = 1;
}
break;
}
return;
}
Author And Source
この問題について(C言語のログ(logging:ロギング)処理(4)), 我々は、より多くの情報をここで見つけました https://zenn.dev/open_sesame/articles/323c47c43ef0e1著者帰属:元の著者の情報は、元のURLに含まれています。著作権は原作者に属する。
Collection and Share based on the CC protocol