IMathLib-ソースコード-

  • pocket
  • はてなブックマーク

#ifndef IMATHLIB_H_MEDIA_LOG_HPP
#define IMATHLIB_H_MEDIA_LOG_HPP

#include "IMathLib/math_traits.hpp"
#include "IMathLib/preprocessor.hpp"
#include <string>
#include <sstream>
#include <fstream>
#include <filesystem>
#include <chrono>


// ログを出力する
namespace iml {
	namespace ml {

		// ログ出力のためのクラス
		class logger {
			static inline std::string			file_name_m;			// 出力ファイル名
			static inline int_t					log_level_m;			// ログの出力レベル
			static inline int_t					prev_log_level_m;		// 前回のログの出力レベル
			static inline std::stringstream		str_stream_m;			// 出力対象を一時的に保持しておくためのストリーム

			// ログレベルの文字列テーブルから取得
			static const char* log_level_str(size_t n) {
				static const char* const table[] = {
					// デバッグ用のログ出力
					"Debug"
					// 以下はエラー
					,"Warn"
					,"Error"
					,"Fatal"
				};
				return table[n];
			}

			struct date {
				size_t	ms;			// ミリ秒
				size_t	sec;		// 秒
				size_t	mini;		// 分
				size_t	hour;		// 時
				size_t	day;		// 日
				size_t	month;		// 月
				size_t	year;		// 年
			};
			static void get_date(date& d) {
				auto chrono_time = std::chrono::system_clock::now();
				auto time_t_time = std::chrono::system_clock::to_time_t(chrono_time);
				std::tm t;
#if defined(__STDC__) && defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L)		// C11
				localtime_s(&time_t_time, &t);
#elif defined(_MSC_VER)				// MSVC
				localtime_s(&t, &time_t_time);
#elif defined(__GNUC__)				// gcc
				localtime_r(&time_t_time, &t);
#endif
				d.sec = t.tm_sec;
				d.mini = t.tm_min;
				d.hour = t.tm_hour;
				d.day = t.tm_mday;
				d.month = t.tm_mon + 1;
				d.year = t.tm_year + 1900;
				long long ms = std::chrono::duration_cast<std::chrono::milliseconds>(chrono_time.time_since_epoch()).count();
				d.ms = ms % 1000;
			}
		public:
			// ログレベルの識別定数
			static constexpr size_t debug = 0;
			static constexpr size_t warn = 1;
			static constexpr size_t error = 2;
			static constexpr size_t fatal = 3;

			static void init(size_t level = logger::debug) {
				namespace sys = std::filesystem;
				sys::path p("log.txt");
				// ログの出力ファイル名の決定(log[1-9]+\.txt)
				if (sys::exists(p)) {
					for (size_t i = 2; i < size_t(-1); ++i) {
						p = "log" + std::to_string(i) + ".txt";
						if (!sys::exists(p)) break;
					}
				}
				logger::file_name_m = p.string();
				logger::log_level_m = level;
				logger::prev_log_level_m = -1;
			}

			// 時間等のログ本文以外の出力要素の書き込み
			static logger wrrite_info(int_t level, const char* file_name, int line, const char* function) {
				logger::prev_log_level_m = level;
				if (level >= logger::log_level_m) {
					// logger::str_stream_mが空でなければ改行を挿入
					if (!logger::str_stream_m.str().empty()) logger::str_stream_m << "\n";
					// 現在時刻をミリ秒で取得
					date da;
					get_date(da);

					logger::str_stream_m << da.year << "/" << da.month << "/" << da.day << " " << da.hour << ":" << da.mini << ":" << da.sec << "." << da.ms
						// [ファイル名 at 行番号:関数]
						<< " [" << file_name << " at " << line << ":" << function << "] "
						<< log_level_str(level) << "\n";
				}
				return logger();
			}
			// エラー等の概要付き時間等のログ本文以外の出力要素の書き込み
			static logger wrrite_info(int_t level, const char* file_name, int line, const char* function, const char* error_str) {
				logger::prev_log_level_m = level;
				if (level >= logger::log_level_m) {
					// logger::str_stream_mが空でなければ改行を挿入
					if (!logger::str_stream_m.str().empty()) logger::str_stream_m << "\n";
					// 現在時刻をミリ秒で取得
					date da;
					get_date(da);

					logger::str_stream_m << da.year << "/" << da.month << "/" << da.day << " " << da.hour << ":" << da.mini << ":" << da.sec << "." << da.ms
						// [ファイル名 at 行番号:関数]
						<< " [" << file_name << " at " << line << ":" << function << "] "
						<< log_level_str(level) << ":" << error_str << "\n";
				}
				return logger();
			}
			// ストリームに蓄積された文字列を出力する
			static void wrrite() {
				if (!logger::str_stream_m.str().empty()) {
					// 文字列ストリームの中身を全て出力したら中身を空にする
					std::fstream ofs(logger::file_name_m, std::ios::app | std::ios::out);
					ofs << logger::str_stream_m.str() << "\n";
					logger::str_stream_m.str("");
					logger::str_stream_m.clear(std::stringstream::goodbit);
				}
			}

			template <class T>
			friend logger operator<<(logger os, const T& x) {
				// 出力可能ならば出力する
				if (logger::prev_log_level_m >= logger::log_level_m)
					logger::str_stream_m << x;
				return os;
			}
		};

		// ログレベルのみを指定
#define IMATHLIB_LOG1(LOG_LEVEL)				::iml::ml::logger::wrrite_info(LOG_LEVEL, __FILE__, __LINE__, __func__)
		// エラー等の概要付き
#define IMATHLIB_LOG2(LOG_LEVEL, SUMMARY)		::iml::ml::logger::wrrite_info(LOG_LEVEL, __FILE__, __LINE__, __func__, SUMMARY)

		// ログ出力時に呼び出すマクロ
#define IMATHLIB_LOG(...)						IMATHLIB_PP_EXPAND(IMATHLIB_PP_OVERLOAD(IMATHLIB_LOG, __VA_ARGS__)(__VA_ARGS__))
	}
}


#endif