花鬼の庵

開発とかゲームとか実況とか

【C#】コピペで出来る!ログ出力実装

あらゆるアプリ必ず実装すべきログ出力処理。
とりあえず下記をコピペすれば、すぐ実装できます。

using System;
using System.Text;
using System.IO;

namespace LogProcess
{
    class LogWriter
    {
        static private string mBaseDebugLogFileName;
        static private bool mIsInitialize = false;
        static private string mLogPath;
        static private StringBuilder mInitErrLog;
        static private string mLogRotateFileName = "Log.rotate";

        static private readonly object mSyncObject = new object();
        /// <summary>
        /// 初期化
        /// </summary>
        /// <param name="path">ログファイルを保存するディレクトリまでの絶対path</param>
        /// <param name="baseDebugLogName">ログファイル名</param>
        /// <returns></returns>
        static public bool initialize(string path, string baseDebugLogName)
        {
            clear();

            if (setLogPath(path, baseDebugLogName) == false) return false;

            mIsInitialize = true;
            logRotate();

            return true;
        }
        /// <summary>
        /// classで保有しているログ情報初期化
        /// </summary>
        static public void clear()
        {
            mLogPath = null;
            mBaseDebugLogFileName = null;
            mInitErrLog = new StringBuilder();
        }
        /// <summary>
        /// ログファイルpath設定
        /// </summary>
        /// <param name="path"></param>
        /// <param name="baseDebugLogName"></param>
        /// <returns></returns>
        static public bool setLogPath(string path, string baseDebugLogName)
        {
            isLogPath(path);

            if (isLogBaseName(baseDebugLogName) == false) return false;

            mLogPath = path;
            mBaseDebugLogFileName = baseDebugLogName;
            return true;
        }
        /// <summary>
        /// ログの書き込み
        /// </summary>
        /// <param name="inFormat"></param>
        /// <param name="inArgs">可変長引数</param>
        static public void writeDebugLog(string inFormat, params object[] inArgs)
        {
            if (mIsInitialize == false) return;

            lock (mSyncObject)
            {
                try
                {
                    DateTime now = DateTime.Now;
                    string theNow = now.Year.ToString("0000") + "/" + now.Month.ToString("00") + "/" + now.Day.ToString("00");

                    string theLogFileName = getDebugLogFileName(theNow);
                    string outLogString = createNowDateString(theNow);
                    outLogString += " " + inFormat;

                    using (StreamWriter writer = new StreamWriter(theLogFileName, true))
                    {
                        if (inArgs == null || inArgs.Length == 0)
                        {
                            writer.WriteLine(outLogString);
                        }
                        else
                        {
                            writer.WriteLine(outLogString, inArgs);
                        }
                    }
                }
                catch (Exception)
                {
                }
            }
        }
        /// <summary>
        /// 指定変数にログファイル名が入っているか確認
        /// </summary>
        /// <param name="inLogBaseName"></param>
        /// <returns></returns>
        static private bool isLogBaseName(string inLogBaseName)
        {
            if (inLogBaseName == null ||inLogBaseName.Length == 0)
            {   
                mIsInitialize = false;
                mInitErrLog.Append("Not specify the log file base name");
                return false;
            }
            return true;
        }
        /// <summary>
        /// 指定pathにディレクトリが存在しているか確認
        /// </summary>
        /// <param name="inLogPath"></param>
        /// <returns></returns>
        static private void isLogPath(string inLogPath)
        {
            bool isExist = Directory.Exists(inLogPath);

            if (isExist == false)
            {
                mInitErrLog.Append("Log folder does not exist. Create folder:" + inLogPath);
                Directory.CreateDirectory(inLogPath);
            }
        }
        /// <summary>
        /// 年月日に時分秒をくっつけた文字列を取得
        /// </summary>
        /// <param name="inDateTime"></param>
        /// <returns></returns>
        static string createNowDateString(string inDateTime)
        {
            string dateString = "[" + inDateTime + " " + DateTime.Now.ToString("HH:mm:ss:fff") + "] ";

            return dateString;
        }
        /// <summary>
        /// 年月日が該当するファイル名を取得
        /// </summary>
        /// <param name="inDateTime"></param>
        /// <returns></returns>
        static string getDebugLogFileName(string inDateTime)
        {
            string theFileNameDate = inDateTime.Replace("/", "");
            string theLogFileName = mLogPath + mBaseDebugLogFileName + "_" + theFileNameDate + ".log";

            return theLogFileName;
        }
        /// <summary>
        /// 過去ログデータの削除
        /// </summary>
        static private void logRotate()
        {
            string theNow = DateTime.Now.ToString("yyyyMMdd");
            // 作成指定日数を過ぎたら削除
            // 決めうちで30日前に設定
            string rotateDate = DateTime.Now.AddDays(-30).ToString("yyyyMMdd");

            // ログに関するローテーションファイルがあるならその処理を優先
            string logRotateFile = mLogPath + mLogRotateFileName;
            string nextRotateDate = DateTime.Now.AddDays(1).ToString("yyyyMMdd");

            if (true == File.Exists(logRotateFile))
            {
                if (string.Compare(theNow, File.ReadAllText(logRotateFile)) > 0)
                {
                    // delete rotate file
                    File.Delete(mLogRotateFileName);               
                    File.WriteAllText(mLogRotateFileName, nextRotateDate);

                    // ログ削除
                    runRotate(rotateDate);
                }
            }
            else
            {
                File.WriteAllText(mLogRotateFileName, nextRotateDate);

                // ログ削除
                runRotate(rotateDate);
            }
        }
        /// <summary>
        /// 過去ログデータの削除判定と実施
        /// </summary>
        /// <param name="inRotateDate"></param>
        static private void runRotate(string inRotateDate)
        {
            DirectoryInfo di = new DirectoryInfo(mLogPath);
            FileInfo[] fis = di.GetFiles(mBaseDebugLogFileName + "*");
            foreach (FileInfo fi in fis)
            {
                if (string.Compare(inRotateDate, fi.LastWriteTime.ToString("yyyyMMdd")) > 0)
                {
                    fi.Delete();
                }
            }
        }
    }
}

書き込みだけでなく初期化処理の際に、
指定日数を過ぎたら削除してくれる処理にもなっています。
ソースの中でも決め打ちで整数を入れてますが定数にして管理しやすくした方が良いでしょう。

実装例

実際に確認してみます。 Hello World!の文字の描画とログ処理が走るだけの簡単なプロジェクトを作成。

using System;

namespace MainProcess
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Hello World!");

            // ログ初期化
            LogProcess.LogWriter.initialize("./log/", "LogTest");

            //
            LogProcess.LogWriter.writeDebugLog(System.Reflection.MethodBase.GetCurrentMethod().Name + " " + "ログテストstart");
            LogProcess.LogWriter.writeDebugLog("");
            LogProcess.LogWriter.writeDebugLog("Version : 1.0.0");
            
        }
    }
}

動作確認

んで、LogProcess.LogWriter.initialize にて指定したpathに
同じく指定したタイトル + ”_年月日”で出力されているか確認。

出力フォルダ

問題なさそうです。
書き込まれているかも見てみます。

書き込み確認

しっかり出力されていますね。
出力されたログには指定テキストの頭に年月日と時分秒コンマが書き込まれるようにしています。

締め

ログ処理は開発時には、
特に個人で作っているとありがたみは感じずらいと思います(Debug.WriteLine入れれば済むから)
ただ公開、配布するのであれば必ず実装しましょう。
ユーザーが操作中に発生したバグが、
何故か自分のところでは再現出来ない…という事態が稀によくあるからです。
そんな時、ユーザーのログファイルをもしかしたらそのバグの原因が分かるかもしれません。
まぁ、適切な場所にLogProcess.LogWriter.writeDebugLogを配置出来ていればの話なんですが…orz

なにか質問等々ございましたらコメントいただければと思います。