見出し画像

[2024年05月]C言語でログ出力プログラム

C言語のログ出力ライブラリは、いろいろあるけれど…
なぜか、いつも自作ログ出力を使ってしまう。
ログファイルに出力できて、ファイルはローテートできて、ファイル上限サイズができて(or 日付単位に出力できたり)
ログレベルが出力できて…
ミリセカンドまで出力時間が出力できて…
可変文字列が複数埋め込みできて…
ぐらいで良いかなぁと思ってしまい、いつも自作ログ出力を使ってしまう。

こんな風に…

/**
 * @file           log.c
 * @brief          ログ処理
 * @author         Me
 * @date           20xx.xx.xx
 * @note           -
 * @attention      -
 * @par            History
 *                 1.0.0 初版
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/stat.h>
#include <pthread.h>
#include <stdarg.h>

#include "util.h"
#include "log.h"

/// ログファイル KBサイズ
#define	LOG_FILE_SIZE_KB			1024

/// ログファイル名(ベース名)
#define	LOG_FILE_NAME	"fc_app_"

/// 日時文字列フォーマット YYYY-MM-DD HH:MM:SS.999999
#define	DATETIME_FORMAT_01			"%04d-%02d-%02d %02d:%02d:%02d.%06d"

/// ログファイルNo 0:ファイル未確定 1以上:ファイル名確定中
int			gLogCurNo;

/// INI定義値(LOG)
INI_VALUE_LOG	tIniValLog;

/// ログファイル排他用ミューテックス
pthread_mutex_t thMutexLog = PTHREAD_MUTEX_INITIALIZER;

/**
 * @brief ログ出力処理\n
 * ログをファイルに出力します\n
 * 定義ファイルで出力先ディレクトリ、ログファイルサイズ、ログ世代の定義が可能
 * @param [in] logLevel         ログレベル
 * @param [in] programFileName プログラムファイル名
 * @param [in] programLineNo   プログラムファイル行番号
 * @param [in] programFuncName プログラム関数名
 * @param [in] format          可変文字列
 */
void putLogFile(int logLevel, char *programFileName, int programLineNo, const char *programFuncName, char *format, ...)
{
	int		ret;				// 戻り値
	char	dateTime[32];		// 現在日時
	struct stat	tStat;			// ファイル情報の格納領域
	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(DATETIME_FORMAT_01, sizeof(dateTime), dateTime);
	if(ret != FC_NORMAL){
		// エラーの場合ログ出力なし
		return;
	}

	ret = pthread_mutex_lock(&thMutexLog);

	// ファイル名を取得
	memset(fileName, 0x0, sizeof(fileName));
	sprintf(fileName, "%s/%s%06d.log", tIniValLog.cLogFilePathName, LOG_FILE_NAME, gLogCurNo);
	ret = stat(fileName, &tStat);
	if(ret != 0){
		// エラー
		// ファイルが存在しない場合、ファイルサイズに0を設定
		fileSize = 0;
	}
	else{
		// ファイルサイズ
		fileSize = tStat.st_size;
	}

	// ファイルサイズチェック
	renewFile = 0;
	if(fileSize >= tIniValLog.iLogFileSizeMax * LOG_FILE_SIZE_KB){
		renewFile = 1;
		gLogCurNo++;
		if(gLogCurNo > tIniValLog.iLogFileNumMax){
			gLogCurNo = 1;
		}
	}

	// ログファイル名取得(フルパス)
	sprintf(fileName, "%s/%s%06d.log", tIniValLog.cLogFilePathName, 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);

		// ログレベル出力
		switch(logLevel){
		case LOG_I:
			fprintf(fp, "I ");
			break;
		case LOG_W:
			fprintf(fp, "W ");
			break;
		case LOG_E:
			fprintf(fp, "E ");
			break;
		}

		// 現在日時出力
		fprintf(fp, "%s ", dateTime);
		// ファイル名、行番号出力、関数名
		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(&thMutexLog);

	return;
}


この記事が気に入ったらサポートをしてみませんか?