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;
}

https://zenn.dev/open_sesame/articles/7bf28fb24db0ca
https://zenn.dev/open_sesame/articles/763d04a261b98d
https://zenn.dev/open_sesame/articles/c060a7c4b71198
https://zenn.dev/open_sesame/articles/f03cab7fe97f43
https://zenn.dev/open_sesame/articles/864545624bcd91
https://zenn.dev/open_sesame/articles/40f17ab519d397
https://zenn.dev/open_sesame/articles/3a71858f10046f
https://zenn.dev/open_sesame/articles/e383b5b100a6e2
https://zenn.dev/open_sesame/articles/88a8ad7dabdb25