ことれいのもり

C++ ファイルに書き込むログクラスの作成

はじめに

C++でDirectX11を使ったゲーム制作中に、実行結果の確認のためにログクラスを制作したので紹介します。

前提

言語: C++

参考リンク

設計

やりたいこと

  • シングルトンにする
  • 任意の場所でログクラスを呼び出してテキストファイルに任意の文字列を書き込みたい
  • テキストファイルは実行ごとに区切り線を入れて見やすくする
  • ログレベルを設けてログの情報を一目で分かるようにしたい

以上の要件を満たすクラスを制作しました。

Loggerクラス

Logger.h

#pragma once

#include <string>

class Logger
{
public:
    static Logger* getInstance();

 // ファイルに書き込む
    void Log(std::string message, int level);

    // ログレベル
    enum Loglevel
    {
        Info,
        Warn,
        Error,
    };

private:
    static Logger* instance;
    Logger();
    ~Logger();

    // ログレベルを文字列に変換する
    std::string LovLevelToString(Loglevel level);
    // タイムスタンプを取得する
    std::string GetTimeStamp();
};

Logger.cpp

プログラム開始時に呼ばれるコンストラクタで区切り線を設けています。

Logger::Logを呼ぶことでメッセージを書き込めます。

#include "Logger.h"
#include <iostream>
#include <fstream>
#include <string>
#include <ctime>
#include <chrono>
#include <sstream>
#include <time.h>

Logger* Logger::instance = nullptr;

Logger::Logger()
{
    // ログの最初に区切り線と起動時間を書き込む
    std::ofstream logFile("log.txt", std::ios::app);
    if (logFile.is_open())
    {
        logFile << "\n========================================" << std::endl;
        logFile << "[ " << GetTimeStamp() << "] プログラム開始" << std::endl;
        logFile << "========================================\n" << std::endl;
        logFile.close();
    }
}

Logger::~Logger()
{
    delete instance;
}

Logger* Logger::getInstance()
{
    static Logger instance;
    return &instance;
}

void Logger::Log(std::string message, int level)
{
    std::ofstream logFile("log.txt", std::ios::app);
    if (logFile.is_open())
    {
        logFile << "[" << GetTimeStamp() << "] [" << LovLevelToString(static_cast<Loglevel>(level)) << "]" << message << std::endl;
        logFile.close();
    }
    else
    {
        std::cout << "ファイルが開けませんでした" << std::endl;
    }

}

std::string Logger::LovLevelToString(Loglevel level)
{
    switch (level)
    {
    case Info: return "INFO";
    case Warn: return "WARN";
    case Error: return "Error";
    default: return "Unknown";
    }
}

std::string Logger::GetTimeStamp()
{
    // 現在時刻を取得
    auto now = std::chrono::system_clock::now();
    std::time_t now_c = std::chrono::system_clock::to_time_t(now);

    struct tm timeInfo;
    localtime_s(&timeInfo, &now_c);

    // 文字列に変換する
    std::string timestamp = static_cast<std::ostringstream&&>(std::ostringstream() << std::put_time(&timeInfo, "%Y-%m-%d %H:%M:%S")).str();
    return timestamp;
}

実行結果

実行してLogger::Logを呼び出すと、log.txtに書き込まれます。

中身はこのような感じになっています。

プログラム開始が区切られて見やすくなりました!

ログの出力画像

改善点

ログを出力するだけならこれでも問題ないですが、マルチスレッドの対応を行なっていません。

今のところこのままでも問題ないですが、後々対応させていきたいと思います。

完成したらまた記事にしておきます。

おわりに

ログクラスを作成しました。

このクラスを作るまではMessageBoxで表示させていたのですが、小さなウィンドウが出現してボタンを押すのが面倒だったので一気に楽になりました。

C++の書き込みや型の変換なども学ぶことができたので良かったです。