背景
log4netのsqliteデータベースを使用してプログラムのログを記録し、サーバーに入ることなく、ログの内容を表示する目的を達成するために、ログの内容を表示するには、htmlページを使用します。すべてのプログラムログは、log4netは、インクリメンタルログで、sqliteデータベースに書き込まれ、ログを書いている間、ログを表示すると、少し遅く表示されます。
アイデア
1, 3つのロギングレベルを持つロガーを設計する Off > Debug > Trace > Error information, もう一度、情報を記録するためにsqliteデータベースを使用します。
2、ログ情報を記録するために独立したsqliteデータベースをログの各レベルの毎日。この方法では、データベースが大きすぎる、読みやすいではありません。
3、デザインhtmlページは、単一または複数のデータベースは、ログを読み取ることができます。
実装
1、ログキャッシュプールを設定すると、すべてのログは、キャッシュプールに書き込まれるので、ログが書き込まれるのを待って、他の操作を実行するプログラムを回避することができます。
2、特にログキャッシュプール内のログを処理するためのログ処理スレッドを設定し、データベースに書き込みます。
/// <summary>
/// 初期化するかどうか
/// </summary>
private static bool IsInit = false;
/// <summary>
/// ログの処理間隔単位/ミリ秒、デフォルト3000ミリ秒、最小600ミリ秒。
/// </summary>
private static int ThreadTimeOut
{
get
{
try
{
string TimeOutString = System.Configuration.ConfigurationManager.AppSettings["LogHelper_LogThreadTimeOut"] ?? "3000";
if (string.IsNullOrEmpty(TimeOutString))
{
TimeOutString = "3000";
}
int TimeOut = Convert.ToInt32(TimeOutString);
if (TimeOut < 600)
{
TimeOut = 600;
}
return TimeOut;
}
catch
{
return 3000;
}
}
}
/// <summary>
/// ログファイルのルート・ディレクトリ
/// </summary>
private static string LogDbPathRoot
{
get
{
string root = System.Configuration.ConfigurationManager.AppSettings["LogHelper_LogDbPathRoot"] ?? "";
if (string.IsNullOrEmpty(root))
{
root = HttpRuntime.AppDomainAppPath + "App_Data\\logs";
}
return root;
}
}
/// <summary>
/// ログ・キャッシュ・プール
/// </summary>
private static Dictionary<string, Dictionary<string, LogContent>> _LogPool = new Dictionary<string, Dictionary<string, LogContent>>();
/// <summary>
/// キャッシュプールにログを書き込む
/// </summary>
/// <param name="DbName">データベース名(例:20200701_debug </param>
/// <param name="content">ログの内容</param>
public static void Write(string DbName, LogContent content)
{
//スレッドの初期化
if (!IsInit)
{
Thread thread = new Thread(WriterWork);
thread.Name = "LogHelper_LogDbHelper_Thread";
thread.IsBackground = true;
thread.Start();
IsInit = true;
}
if (!_LogPool.ContainsKey(DbName))
{
_LogPool[DbName] = new Dictionary<string, LogContent>();
}
_LogPool[DbName].Add(Guid.NewGuid().ToString("N"), content);
}
/// <summary>
/// ログのスレッドロック
/// </summary>
private static object locker = new object();
/// <summary>
/// キャッシュ・プールのログを処理する
/// </summary>
private static void WriterWork()
{
while (true)
{
lock (locker)
{
try
{
if (_LogPool.Count > 0)
{
KeyValuePair<string, Dictionary<string, LogContent>> dbDIC = _LogPool.FirstOrDefault();
if (dbDIC.Value.Count < 1)
{
_LogPool.Remove(dbDIC.Key);
Thread.Sleep(ThreadTimeOut);
}
else
{
List<SQLiteParams> parList = new List<SQLiteParams>();
List<string> keys = new List<string>();
foreach (KeyValuePair<string, LogContent> kvp in dbDIC.Value)
{
keys.Add(kvp.Key);
SQLiteParams par = new SQLiteParams();
#region sql
par.Sql = @"
INSERT INTO Logs (Time,Level,Type,Message,Data,UserID,UserName,UserIP)
VALUES(@Time,@Level,@Type,@Message,@Data,@UserID,@UserName,@UserIP)
"; #endregion
par.Parameters.Add(new SQLiteParameter("@Time", kvp.Value.Time));
par.Parameters.Add(new SQLiteParameter("@Type", string.IsNullOrEmpty(kvp.Value.Type) ? (object)DBNull.Value : kvp.Value.Type));
par.Parameters.Add(new SQLiteParameter("@Level", string.IsNullOrEmpty(kvp.Value.Level) ? (object)DBNull.Value : kvp.Value.Level));
par.Parameters.Add(new SQLiteParameter("@Message", string.IsNullOrEmpty(kvp.Value.Message) ? (object)DBNull.Value : kvp.Value.Message));
par.Parameters.Add(new SQLiteParameter("@Data", string.IsNullOrEmpty(kvp.Value.Data) ? (object)DBNull.Value : kvp.Value.Data));
par.Parameters.Add(new SQLiteParameter("@UserID", string.IsNullOrEmpty(kvp.Value.UserID) ? (object)DBNull.Value : kvp.Value.UserID));
par.Parameters.Add(new SQLiteParameter("@UserName", string.IsNullOrEmpty(kvp.Value.UserName) ? (object)DBNull.Value : kvp.Value.UserName));
par.Parameters.Add(new SQLiteParameter("@UserIP", string.IsNullOrEmpty(kvp.Value.UserIP) ? (object)DBNull.Value : kvp.Value.UserIP));
parList.Add(par);
}
if (parList.Count > 0)
{
WriteToDB(dbDIC.Key, parList);
foreach (string key in keys)
{
if (dbDIC.Value.ContainsKey(key))
{ dbDIC.Value.Remove(key);
}
}
}
}
}
}
catch { }
//スレッド "タイムアウト "ミリ秒ごとに処理される。
Thread.Sleep(ThreadTimeOut);
}
}
}
/// <summary>
/// キャッシュプールのログをデータベースに書き込む
/// </summary>
/// <param name="DbName">20200110_debug</param>
/// <param name="pars"></param>
private static void WriteToDB(string DbName, List<SQLiteParams> pars)
{
string year = DbName.Substring(0, 4);
string month = DbName.Substring(4, 2);
string DbFolderPath = LogDbPathRoot + "" + year + "" + month;
if (!Directory.Exists(DbFolderPath))
{
Directory.CreateDirectory(DbFolderPath);
}
string DbPath = DbFolderPath + "" + DbName + ".db";
bool IsExistDb = File.Exists(DbPath);
SQLiteConnection conn = new SQLiteConnection("Data Source=" + DbPath + ";Pooling=true;Max Pool Size=100;");
conn.Open();
if (!IsExistDb)
{
string sqlDB = @"
CREATE TABLE IF NOT EXISTS Logs (
ID INTEGER PRIMARY KEY AUTOINCREMENT
NOT NULL,
Time DATETIME NOT NULL
DEFAULT (datetime('now', 'localtime') ),
Level VARCHAR (150) NOT NULL,
Type VARCHAR (150) NOT NULL,
UserID VARCHAR (50),
UserName VARCHAR (150),
UserIP VARCHAR (50),
Message TEXT,
Data TEXT
);
";
SQLiteCommand cmdDB = new SQLiteCommand(sqlDB, conn);
cmdDB.ExecuteNonQuery();
cmdDB.Dispose();
}
SQLiteTransaction transaction = conn.BeginTransaction();
try
{
foreach (SQLiteParams par in pars)
{
SQLiteCommand cmd = new SQLiteCommand(par.Sql, conn);
cmd.Transaction = transaction;
cmd.CommandType = CommandType.Text;
cmd.CommandTimeout = 60;//単位は秒
if (par.Parameters != null && par.Parameters.Count > 0)
{
cmd.Parameters.AddRange(par.Parameters.ToArray());
}
cmd.ExecuteNonQuery();
}
transaction.Commit();
}
catch (Exception exp)
{
transaction.Rollback();
throw exp;
}
finally
{
transaction.Dispose();
}
}
3、ログ読み取り
最初にログ・データベース・ファイルを読み込みます。
/// <summary>
/// ログ一覧を取得する
/// </summary>
/// <param name="yearStr"></param>
/// <param name="monthStr"></param>
/// <param name="level"></param>
/// <returns></returns>
public static List<LogDbFile> GetLogDbFileList(string yearStr = "", string monthStr = "", string level = "")
{
List<LogDbFile> res = new List<LogDbFile>();
string logPath = LogDbPathRoot;
if (!Directory.Exists(logPath))
{
return res;
}
DirectoryInfo root = new DirectoryInfo(logPath);
foreach (DirectoryInfo year in root.GetDirectories())
{
if (!string.IsNullOrEmpty(yearStr) && yearStr != year.Name) { continue; }
foreach (DirectoryInfo month in year.GetDirectories())
{
if (!string.IsNullOrEmpty(monthStr) && monthStr != month.Name) { continue; }
foreach (FileInfo logFile in month.GetFiles())
{
if (!string.IsNullOrEmpty(level) && !logFile.Name.Contains(level)) { continue; }
try
{
LogDbFile logDb = new LogDbFile();
logDb.year = year.Name;
logDb.month = month.Name;
logDb.fileName = logFile.Name;
logDb.filePath = string.Format(@"{0}\{1}\{2}", year.Name, month.Name, logFile.Name);
logDb.fileUpdateTime = logFile.LastWriteTime;
logDb.fileSize = logFile.Length;
logDb.date = logFile.Name.Substring(0, 8);
logDb.level = Path.GetFileNameWithoutExtension(logFile.Name).Replace(logDb.date + "_", "");
logDb.logTime = DateTime.Parse(logDb.year + "-" + logDb.month + "-" + logFile.Name.Substring(6, 2) + " 00:00:00");
res.Add(logDb);
}
catch(Exception exp) {
LogHelper.Error("ログリストの例外を読む。ファイル "" + logFile.Name +"”ログデータの命名形式と一致しない。"+ exp.Message, exp);
}
}
}
}
return res;
}
次のステップは、sqliteデータテーブルのデータを普通に読み込むことです。
ページ効果は以下の通り:
以下に、GitHubのソースコードのアドレスを添付します。
"https://.com/penn6996"/.git




