[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;
}
以下に有料記事ですが、ログメッセージの定義や出力例を含めたプログラムファイルおよびメイクファイルを掲載しています。
ここから先は
28,938字
¥ 200
この記事が気に入ったらチップで応援してみませんか?