Files
TimberWinR/TimberWinR/Inputs/LogsFileDatabase.cs
2015-04-29 08:35:07 -04:00

279 lines
8.6 KiB
C#

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using NLog;
using TimberWinR.Parser;
namespace TimberWinR.Inputs
{
//
// Maintain persistent state for Log files (to be used across restarts)
//
public class LogsFileDatabase
{
private static readonly object _locker = new object();
private List<LogsFileDatabaseEntry> Entries { get; set; }
private string DatabaseDirectory { get; set; }
public string DatabaseFileName
{
get { return Path.Combine(DatabaseDirectory, ".timberwinrdb"); }
}
public static Manager Manager { get; set; }
private static LogsFileDatabase instance;
private bool ExistingFile(string logName)
{
lock (_locker)
{
return ExistingFileTest(logName);
}
}
//
// Lookup the database entry for this log file, returns null if there isnt one.
//
private LogsFileDatabaseEntry FindFile(string logName)
{
lock (_locker)
{
var existingEntry = (from e in Entries where e.FileName == logName select e).FirstOrDefault();
return existingEntry;
}
}
private bool ExistingFileTest(string logName)
{
var existingEntry = (from e in Entries where e.FileName == logName select e).FirstOrDefault();
return existingEntry != null;
}
private void RemoveFileEntry(string logName)
{
lock (_locker)
{
var existingEntry = (from e in Entries where e.FileName == logName select e).FirstOrDefault();
if (existingEntry != null)
{
Entries.Remove(existingEntry);
WriteDatabaseFileNoLock();
}
}
}
private LogsFileDatabaseEntry AddFileEntry(string logName)
{
var de = new LogsFileDatabaseEntry();
lock (_locker)
{
var fi = new FileInfo(logName);
de.FileName = logName;
de.LogFileExists = fi.Exists;
de.Previous = "";
de.NewFile = true;
de.ProcessedFile = false;
de.LastPosition = fi.Length;
de.SampleTime = DateTime.UtcNow;
de.CreationTimeUtc = fi.CreationTimeUtc;
Entries.Add(de);
WriteDatabaseFileNoLock();
}
return de;
}
public static LogsFileDatabaseEntry LookupLogFile(string logName)
{
LogsFileDatabaseEntry dbe = Instance.FindFile(logName);
if (dbe == null)
dbe = Instance.AddFileEntry(logName);
FileInfo fi = new FileInfo(logName);
dbe.LogFileExists = fi.Exists;
var creationTime = fi.CreationTimeUtc;
if (dbe.LogFileExists && creationTime != dbe.CreationTimeUtc)
{
dbe.NewFile = true;
dbe.Previous = "";
}
dbe.CreationTimeUtc = creationTime;
return dbe;
}
// Find all the non-existent entries and remove them.
private void PruneFiles()
{
lock (_locker)
{
foreach(var entry in Entries.ToList())
{
FileInfo fi = new FileInfo(entry.FileName);
if (!fi.Exists)
Entries.Remove(entry);
}
WriteDatabaseFileNoLock();
}
}
public static void Update(LogsFileDatabaseEntry dbe, bool processedFile, long lastOffset)
{
dbe.ProcessedFile = processedFile;
dbe.LogFileExists = File.Exists(dbe.FileName);
Instance.UpdateEntry(dbe, lastOffset);
}
public static void Roll(LogsFileDatabaseEntry dbe)
{
dbe.ProcessedFile = false;
dbe.LastPosition = 0;
dbe.Previous = "";
Instance.UpdateEntry(dbe, 0);
dbe.NewFile = true;
}
private void UpdateEntry(LogsFileDatabaseEntry dbe, long lastOffset)
{
lock (_locker)
{
var fi = new FileInfo(dbe.FileName);
dbe.NewFile = !fi.Exists;
dbe.CreationTimeUtc = fi.CreationTimeUtc;
dbe.SampleTime = DateTime.UtcNow;
dbe.LastPosition = lastOffset;
WriteDatabaseFileNoLock();
}
}
public static LogsFileDatabase Instance
{
get
{
if (instance == null)
{
instance = new LogsFileDatabase(Manager.LogfileDir);
lock (_locker)
{
if (!Directory.Exists(instance.DatabaseDirectory))
Directory.CreateDirectory(instance.DatabaseDirectory);
// If it exists, read the current state, otherwise create an empty database.
if (File.Exists(instance.DatabaseFileName))
instance.ReadDatabaseNoLock();
else
instance.WriteDatabaseFileNoLock();
if (instance.Entries == null)
instance.Entries = new List<LogsFileDatabaseEntry>();
instance.PruneFiles();
}
}
return instance;
}
}
// Serialize in the Database
private void ReadDatabaseNoLock()
{
try
{
var serializer = new JsonSerializer();
if (File.Exists(DatabaseFileName))
Entries =
JsonConvert.DeserializeObject<List<LogsFileDatabaseEntry>>(File.ReadAllText(DatabaseFileName));
}
catch (Exception ex)
{
LogManager.GetCurrentClassLogger()
.Error("Error reading database '{0}': {1}", DatabaseFileName, ex.ToString());
try
{
if (File.Exists(DatabaseFileName))
File.Delete(DatabaseFileName);
LogManager.GetCurrentClassLogger().Info("Creating New Database '{0}'", DatabaseFileName);
WriteDatabaseLock();
}
catch (Exception ex2)
{
LogManager.GetCurrentClassLogger().Info("Error Creating New Database '{0}': {1}", DatabaseFileName, ex2.ToString());
}
}
}
private void WriteDatabaseFileNoLock()
{
try
{
File.WriteAllText(DatabaseFileName, JsonConvert.SerializeObject(instance.Entries), Encoding.UTF8);
}
catch (Exception ex)
{
LogManager.GetCurrentClassLogger()
.Error("Error saving database '{0}': {1}", DatabaseFileName, ex.ToString());
}
}
private void ReadDatabaseLock()
{
lock (_locker)
{
ReadDatabaseNoLock();
}
}
private void WriteDatabaseLock()
{
lock (_locker)
{
WriteDatabaseFileNoLock();
}
}
private LogsFileDatabase(string databaseDirectory)
{
DatabaseDirectory = databaseDirectory;
Entries = new List<LogsFileDatabaseEntry>();
}
}
//
// Represents a log file to be tailed
//
public class LogsFileDatabaseEntry
{
[JsonIgnore]
public bool NewFile { get; set; }
public bool ProcessedFile { get; set; }
public bool LogFileExists { get; set; }
public string FileName { get; set; }
public DateTime CreationTimeUtc { get; set; }
public DateTime SampleTime { get; set; }
public long LastPosition { get; set; }
public long LinesProcessed
{
get { return _linesProcessed; }
}
private int _linesProcessed;
public void IncrementLineCount()
{
Interlocked.Increment(ref _linesProcessed);
}
public string Previous { get; set; }
}
}