├── .gitignore ├── Console Client ├── FuzzyComparer │ ├── IComparisonAlgorithm.cs │ ├── Aggregate.cs │ └── FuzzyStringComparisonAlgorithm.cs ├── ILogRecordClassifier.cs ├── Filters │ ├── IInputFilter.cs │ ├── NullFilter.cs │ ├── StringEqualityFilter.cs │ ├── IntegerEqualityFilter.cs │ ├── StringContainsFilter.cs │ ├── StringStartsWithFilter.cs │ ├── AtDateTimeFilter.cs │ ├── AfterDateTimeFilter.cs │ ├── BeforeDateTimeFilter.cs │ ├── EventTypeFilter.cs │ └── AbstractInputFilter.cs ├── Console Client.csproj ├── ProcessControlArguments.cs ├── Reporting │ ├── AbstractSummaryWriter.cs │ ├── FileNameGenerator.cs │ ├── TextSummaryWriter.cs │ ├── ReportGenerator.cs │ └── HtmlSummaryWriter.cs ├── Properties │ ├── AssemblyInfo.cs │ ├── Resources.cs │ ├── Resources.Designer.cs │ └── Resources.resx ├── OutputFilterArguments.cs ├── EventLogRecordClassifier.cs ├── CommandLineArgumentHandler.cs ├── CLI.cs └── InputFilterArguments.cs ├── EventLog ├── IEventLogFile.cs ├── EventLog.csproj ├── LogRecordSearchCriteria.cs ├── IEventLogRecord.cs ├── Globals.cs ├── XmlEventLogFile.cs ├── EventLogFileFactory.cs ├── CsvEventLogFile.cs ├── EventLogFileCollection.cs ├── BinaryEventLogFile.cs ├── AbstractEventLogFile.cs ├── Header.cs ├── Footer.cs ├── Metadata.cs ├── XmlEventLogRecord.cs ├── BinaryEventLogRecord.cs └── CsvEventLogRecord.cs ├── UnitTests ├── EventLog │ ├── StubEventLogRecord.cs │ ├── StubEventLogFileTest.cs │ └── StubEventLogFile.cs ├── UnitTests.csproj ├── FuzzyStringComparisonAlgorithmTest.cs └── SpikeTests.cs ├── .github └── workflows │ └── dotnet-build.yml ├── LICENSE.txt ├── EventLog Analyzer - VS 2005.sln ├── EventLog Analyzer - VS 2008.sln ├── EventLogAnalyzer.sln └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | *.suo 2 | *.user 3 | *.sln.cache 4 | *.resharper 5 | _ReSharper.* 6 | bin 7 | obj 8 | Debug 9 | Release 10 | 11 | # Build output folder 12 | Output/ 13 | 14 | # Visual Studio cache 15 | .vs/ -------------------------------------------------------------------------------- /Console Client/FuzzyComparer/IComparisonAlgorithm.cs: -------------------------------------------------------------------------------- 1 | namespace pal.EventLogAnalyzer.ConsoleClient.FuzzyComparer 2 | { 3 | public interface IComparisonAlgorithm 4 | { 5 | double DisSimilarityCoefficient(TypeBeingCompared rhs); 6 | } 7 | } -------------------------------------------------------------------------------- /Console Client/ILogRecordClassifier.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace pal.EventLogAnalyzer.ConsoleClient 4 | { 5 | public interface ILogRecordClassifier 6 | { 7 | void Classify(List records, double toleranceMargin); 8 | } 9 | } -------------------------------------------------------------------------------- /Console Client/Filters/IInputFilter.cs: -------------------------------------------------------------------------------- 1 | using pal.EventLog; 2 | 3 | namespace pal.EventLogAnalyzer.ConsoleClient.Filters 4 | { 5 | internal interface IInputFilter 6 | { 7 | object Parameter { get; set; } //RHS for evaluation 8 | 9 | bool Filter(IEventLogRecord recordToFilter); 10 | } 11 | } -------------------------------------------------------------------------------- /EventLog/IEventLogFile.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | 5 | namespace pal.EventLog 6 | { 7 | public interface IEventLogFile : IEnumerable, IDisposable 8 | { 9 | int RecordCount { get; } 10 | IEventLogRecord this[int recordNo] { get; } 11 | void Parse(); 12 | ArrayList Find(LogRecordSearchCriteria searchCriteria); 13 | } 14 | } -------------------------------------------------------------------------------- /EventLog/EventLog.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | net6.0 4 | pal.EventLog 5 | pal.EventLog 6 | enable 7 | enable 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /Console Client/Filters/NullFilter.cs: -------------------------------------------------------------------------------- 1 | using pal.EventLog; 2 | 3 | namespace pal.EventLogAnalyzer.ConsoleClient.Filters 4 | { 5 | internal class NullFilter : IInputFilter 6 | { 7 | #region IInputFilter Members 8 | 9 | public object Parameter 10 | { 11 | get { return new object(); } 12 | set { } 13 | } 14 | 15 | public bool Filter(IEventLogRecord recordToFilter) 16 | { 17 | return false; 18 | } 19 | 20 | #endregion 21 | } 22 | } -------------------------------------------------------------------------------- /Console Client/Console Client.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | Exe 4 | net6.0 5 | pal.EventLogAnalyzer.ConsoleClient 6 | EventLogAnalyzer.ConsoleClient 7 | enable 8 | enable 9 | false 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /EventLog/LogRecordSearchCriteria.cs: -------------------------------------------------------------------------------- 1 | namespace pal.EventLog 2 | { 3 | public class LogRecordSearchCriteria 4 | { 5 | private string _message; 6 | private string _source; 7 | 8 | public LogRecordSearchCriteria(string source, string message) 9 | { 10 | _source = source; 11 | _message = message; 12 | } 13 | 14 | public string Source 15 | { 16 | get { return _source; } 17 | } 18 | 19 | public string Message 20 | { 21 | get { return _message; } 22 | } 23 | } 24 | } -------------------------------------------------------------------------------- /Console Client/Filters/StringEqualityFilter.cs: -------------------------------------------------------------------------------- 1 | namespace pal.EventLogAnalyzer.ConsoleClient.Filters 2 | { 3 | internal class StringEqualityFilter : AbstractInputFilter 4 | { 5 | public StringEqualityFilter(string propertyName) : base(propertyName) 6 | { 7 | } 8 | 9 | protected override bool recordMatchesCriteria(string propertyValue) 10 | { 11 | return propertyValue.Equals(_parameter); 12 | } 13 | 14 | protected override string setParameter(string value) 15 | { 16 | return value; 17 | } 18 | } 19 | } -------------------------------------------------------------------------------- /Console Client/Filters/IntegerEqualityFilter.cs: -------------------------------------------------------------------------------- 1 | namespace pal.EventLogAnalyzer.ConsoleClient.Filters 2 | { 3 | internal class IntegerEqualityFilter : AbstractInputFilter 4 | { 5 | public IntegerEqualityFilter(string propertyName) : base(propertyName) 6 | { 7 | } 8 | 9 | protected override bool recordMatchesCriteria(int propertyValue) 10 | { 11 | return propertyValue == (int) _parameter; 12 | } 13 | 14 | protected override int setParameter(string value) 15 | { 16 | return int.Parse(value); 17 | } 18 | } 19 | } -------------------------------------------------------------------------------- /Console Client/Filters/StringContainsFilter.cs: -------------------------------------------------------------------------------- 1 | namespace pal.EventLogAnalyzer.ConsoleClient.Filters 2 | { 3 | internal class StringContainsFilter : AbstractInputFilter 4 | { 5 | public StringContainsFilter(string propertyName) : base(propertyName) 6 | { 7 | } 8 | 9 | protected override bool recordMatchesCriteria(string propertyValue) 10 | { 11 | return ((string) _parameter).Contains(propertyValue); 12 | } 13 | 14 | protected override string setParameter(string value) 15 | { 16 | return value; 17 | } 18 | } 19 | } -------------------------------------------------------------------------------- /Console Client/ProcessControlArguments.cs: -------------------------------------------------------------------------------- 1 | namespace pal.EventLogAnalyzer.ConsoleClient 2 | { 3 | internal class ProcessControlArguments 4 | { 5 | private double _toleranceMargin = 0; 6 | 7 | public double ToleranceMargin 8 | { 9 | get { return _toleranceMargin; } 10 | } 11 | 12 | public bool IsKnown(string @switch) 13 | { 14 | return @switch.Equals("-tolerance"); 15 | } 16 | 17 | public void Add(string @switch, string parameter) 18 | { 19 | _toleranceMargin = double.Parse(parameter); 20 | } 21 | } 22 | } -------------------------------------------------------------------------------- /Console Client/Filters/StringStartsWithFilter.cs: -------------------------------------------------------------------------------- 1 | namespace pal.EventLogAnalyzer.ConsoleClient.Filters 2 | { 3 | internal class StringStartsWithFilter : AbstractInputFilter 4 | { 5 | public StringStartsWithFilter(string propertyName) : base(propertyName) 6 | { 7 | } 8 | 9 | protected override bool recordMatchesCriteria(string propertyValue) 10 | { 11 | return ((string) _parameter).StartsWith(propertyValue); 12 | } 13 | 14 | protected override string setParameter(string value) 15 | { 16 | return value; 17 | } 18 | } 19 | } -------------------------------------------------------------------------------- /Console Client/Filters/AtDateTimeFilter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace pal.EventLogAnalyzer.ConsoleClient.Filters 4 | { 5 | internal class AtDateTimeFilter : AbstractInputFilter 6 | { 7 | public AtDateTimeFilter(string propertyName) : base(propertyName) 8 | { 9 | } 10 | 11 | protected override bool recordMatchesCriteria(DateTime propertyValue) 12 | { 13 | return propertyValue.CompareTo(_parameter) == 0; 14 | } 15 | 16 | protected override DateTime setParameter(string value) 17 | { 18 | return DateTime.Parse(value); 19 | } 20 | } 21 | } -------------------------------------------------------------------------------- /Console Client/Filters/AfterDateTimeFilter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace pal.EventLogAnalyzer.ConsoleClient.Filters 4 | { 5 | internal class AfterDateTimeFilter : AbstractInputFilter 6 | { 7 | public AfterDateTimeFilter(string propertyName) : base(propertyName) 8 | { 9 | } 10 | 11 | protected override bool recordMatchesCriteria(DateTime propertyValue) 12 | { 13 | return propertyValue.CompareTo(_parameter) < 0; 14 | } 15 | 16 | protected override DateTime setParameter(string value) 17 | { 18 | return DateTime.Parse(value); 19 | } 20 | } 21 | } -------------------------------------------------------------------------------- /Console Client/Filters/BeforeDateTimeFilter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace pal.EventLogAnalyzer.ConsoleClient.Filters 4 | { 5 | internal class BeforeDateTimeFilter : AbstractInputFilter 6 | { 7 | public BeforeDateTimeFilter(string propertyName) : base(propertyName) 8 | { 9 | } 10 | 11 | protected override bool recordMatchesCriteria(DateTime propertyValue) 12 | { 13 | return propertyValue.CompareTo(_parameter) > 0; 14 | } 15 | 16 | protected override DateTime setParameter(string value) 17 | { 18 | return DateTime.Parse(value); 19 | } 20 | } 21 | } -------------------------------------------------------------------------------- /EventLog/IEventLogRecord.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | 5 | namespace pal.EventLog 6 | { 7 | public interface IEventLogRecord 8 | { 9 | EventLogEntryType Type { get; } 10 | 11 | DateTime GeneratedTime { get; } 12 | 13 | string Source { get; } 14 | 15 | ushort Category { get; } 16 | 17 | int EventId { get; } 18 | 19 | string User { get; } 20 | 21 | string Computer { get; } 22 | 23 | string Message { get; } 24 | 25 | IEnumerable ContainingFile { get; set; } 26 | 27 | bool Matches(LogRecordSearchCriteria searchCriteria); 28 | } 29 | } -------------------------------------------------------------------------------- /UnitTests/EventLog/StubEventLogRecord.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using pal.EventLog; 3 | 4 | namespace pal.EventLogAnalyzer.UnitTests.EventLog 5 | { 6 | internal class StubEventLogRecord : BinaryEventLogRecord 7 | { 8 | public StubEventLogRecord() 9 | { 10 | _computerName = Environment.GetEnvironmentVariable("COMPUTERNAME"); 11 | _message = "Boo Hoo"; 12 | _sourceName = "Stupid source"; 13 | _userSid = new byte[0]; 14 | _metadata = new Metadata(); 15 | _metadata.EventCategory = 0; 16 | _metadata.EventID = 0; 17 | _metadata.EventType = 0; 18 | _metadata.TimeGenerated = 0; 19 | } 20 | } 21 | } -------------------------------------------------------------------------------- /UnitTests/EventLog/StubEventLogFileTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using NUnit.Framework; 3 | 4 | namespace pal.EventLogAnalyzer.UnitTests.EventLog 5 | { 6 | [TestFixture] 7 | public class StubEventLogFileTest 8 | { 9 | [Test] 10 | public void JustCallToStringForNow() 11 | { 12 | StubEventLogFile stubEventLogFile = new StubEventLogFile(); 13 | stubEventLogFile.Parse(); 14 | Console.Out.WriteLine("eventLogFileContents = {0}", stubEventLogFile); 15 | for (int i = 0; i < stubEventLogFile.RecordCount; i++) 16 | { 17 | Console.Out.WriteLine("stubEventLogFile[{0}] = {1}", i, stubEventLogFile[i]); 18 | } 19 | } 20 | } 21 | } -------------------------------------------------------------------------------- /Console Client/Filters/EventTypeFilter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | 4 | namespace pal.EventLogAnalyzer.ConsoleClient.Filters 5 | { 6 | internal class EventTypeFilter : AbstractInputFilter 7 | { 8 | public EventTypeFilter(string propertyName) : base(propertyName) 9 | { 10 | } 11 | 12 | protected override bool recordMatchesCriteria(EventLogEntryType propertyValue) 13 | { 14 | return propertyValue == (EventLogEntryType) _parameter; 15 | } 16 | 17 | protected override EventLogEntryType setParameter(string value) 18 | { 19 | return (EventLogEntryType) Enum.Parse(typeof (EventLogEntryType), value, true); 20 | } 21 | } 22 | } -------------------------------------------------------------------------------- /EventLog/Globals.cs: -------------------------------------------------------------------------------- 1 | namespace pal.EventLog 2 | { 3 | internal class Globals 4 | { 5 | public static int MetadataSize 6 | { 7 | get { return 56; /*Marshal.SizeOf(typeof (Metadata))*/ } 8 | } 9 | 10 | public static int HeaderSize 11 | { 12 | get { return 48; /*Marshal.SizeOf(typeof (Header))*/ } 13 | } 14 | 15 | public static int FooterSize 16 | { 17 | get { return 40; /* Marshal.SizeOf(typeof(Footer)) */ } 18 | } 19 | 20 | public static int NullCharSize 21 | { 22 | get { return UnicodeCharSize; } 23 | } 24 | 25 | public static int UnicodeCharSize 26 | { 27 | get { return 2; } 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /UnitTests/UnitTests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | net6.0 4 | pal.EventLogAnalyzer.UnitTests 5 | EventLogAnalyzer.UnitTests 6 | enable 7 | enable 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /Console Client/Reporting/AbstractSummaryWriter.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using pal.EventLog; 3 | using pal.EventLogAnalyzer.ConsoleClient.FuzzyComparer; 4 | 5 | namespace pal.EventLogAnalyzer.ConsoleClient.Reporting 6 | { 7 | internal abstract class AbstractSummaryWriter 8 | { 9 | protected readonly ICollection> _aggregates; 10 | protected readonly FileNameGenerator _fileNameGenerator; 11 | 12 | public AbstractSummaryWriter(ICollection> aggregates, 13 | FileNameGenerator fileNameGenerator) 14 | { 15 | _aggregates = aggregates; 16 | _fileNameGenerator = fileNameGenerator; 17 | } 18 | 19 | public abstract void WriteIn(string folder); 20 | } 21 | } -------------------------------------------------------------------------------- /.github/workflows/dotnet-build.yml: -------------------------------------------------------------------------------- 1 | name: .NET Build and Test 2 | 3 | on: 4 | push: 5 | branches: [ '*' ] 6 | pull_request: 7 | branches: [ master ] 8 | workflow_dispatch: 9 | 10 | jobs: 11 | build: 12 | runs-on: windows-latest # Changed to windows-latest due to EventLog dependency 13 | 14 | steps: 15 | - name: Checkout code 16 | uses: actions/checkout@v3 17 | 18 | - name: Setup .NET 6 19 | uses: actions/setup-dotnet@v3 20 | with: 21 | dotnet-version: '6.0.x' 22 | 23 | - name: Restore dependencies 24 | run: dotnet restore "EventLogAnalyzer.sln" 25 | 26 | - name: Build 27 | run: dotnet build "EventLogAnalyzer.sln" --configuration Release --no-restore 28 | 29 | - name: Test 30 | run: dotnet test "EventLogAnalyzer.sln" --configuration Release --no-build --no-restore 31 | -------------------------------------------------------------------------------- /UnitTests/EventLog/StubEventLogFile.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using pal.EventLog; 3 | 4 | namespace pal.EventLogAnalyzer.UnitTests.EventLog 5 | { 6 | internal class StubEventLogFile : AbstractEventLogFile 7 | { 8 | public StubEventLogFile() : base("Dummy - Non Existent File") 9 | { 10 | } 11 | 12 | public override void Parse() 13 | { 14 | _records = generateDummyEventLogRecords(); 15 | } 16 | 17 | private List generateDummyEventLogRecords() 18 | { 19 | List eventLogRecords = new List(); 20 | eventLogRecords.Add(new StubEventLogRecord()); 21 | return eventLogRecords; 22 | } 23 | 24 | public override void Dispose() 25 | { 26 | } 27 | } 28 | } -------------------------------------------------------------------------------- /EventLog/XmlEventLogFile.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using System.Xml; 3 | 4 | namespace pal.EventLog 5 | { 6 | public class XmlEventLogFile : AbstractEventLogFile 7 | { 8 | private XmlDocument _xmlDocument; 9 | 10 | public XmlEventLogFile(string fileName) : base(fileName) 11 | { 12 | _xmlDocument = new XmlDocument(); 13 | } 14 | 15 | public override void Parse() 16 | { 17 | _xmlDocument.Load(_fileName); 18 | 19 | foreach (XmlNode node in _xmlDocument.DocumentElement.ChildNodes) 20 | { 21 | XmlEventLogRecord eventLogRecord = XmlEventLogRecord.Fetch(node); 22 | eventLogRecord.ContainingFile = this; 23 | _records.Add(eventLogRecord); 24 | } 25 | } 26 | 27 | public override string ToString() 28 | { 29 | return Path.GetFileName(_fileName); 30 | } 31 | 32 | public override void Dispose() 33 | { 34 | _xmlDocument = null; 35 | } 36 | } 37 | } -------------------------------------------------------------------------------- /Console Client/Reporting/FileNameGenerator.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace pal.EventLogAnalyzer.ConsoleClient.Reporting 5 | { 6 | class FileNameGenerator 7 | { 8 | private readonly string _prefix; 9 | private readonly string _suffix; 10 | private readonly IEnumerator _fileNameGenerator; 11 | 12 | public FileNameGenerator(string prefix, string suffix) 13 | { 14 | _prefix = prefix; 15 | _suffix = suffix; 16 | _fileNameGenerator = fileNameGenerator(); 17 | } 18 | 19 | private IEnumerator fileNameGenerator() 20 | { 21 | for (int i = 0; i < Int32.MaxValue; i++) 22 | { 23 | yield return _prefix + " " + i + _suffix; 24 | } 25 | } 26 | 27 | public string NextName 28 | { 29 | get 30 | { 31 | _fileNameGenerator.MoveNext(); 32 | return _fileNameGenerator.Current; 33 | } 34 | } 35 | } 36 | } -------------------------------------------------------------------------------- /UnitTests/FuzzyStringComparisonAlgorithmTest.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | using pal.EventLogAnalyzer.ConsoleClient.FuzzyComparer; 3 | 4 | namespace pal.EventLogAnalyzer.UnitTests 5 | { 6 | [TestFixture] 7 | public class FuzzyStringComparisonAlgorithmTest 8 | { 9 | [Test] 10 | public void ShouldIgnoreGuids() 11 | { 12 | string lhs = 13 | "{b736467c-b1ff-4bbd-9654-a5312f1ba846}\0 and some text and {47484565-22b5-4e72-a365-b0bdda187465}"; 14 | string rhs1 = 15 | "{b736467c-b1ff-4bbd-9654-a5312f1ba846} and some text and {a3e8ee65-3c13-4c48-bc94-253da3fac798}"; 16 | string rhs2 = 17 | "{b736467c-b1ff-4bbd-9654-a5312f1ba846} and some different text and {a3e8ee65-3c13-4c48-bc94-253da3fac798}"; 18 | FuzzyStringComparisonAlgorithm comparisonAlgorithm = new FuzzyStringComparisonAlgorithm(lhs); 19 | Assert.AreEqual(0, comparisonAlgorithm.DisSimilarityCoefficient(rhs1)); 20 | Assert.AreNotEqual(0, comparisonAlgorithm.DisSimilarityCoefficient(rhs2)); 21 | } 22 | } 23 | } -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2007 CodeMangler 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /EventLog/EventLogFileFactory.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace pal.EventLog 4 | { 5 | public class EventLogFileFactory 6 | { 7 | private readonly string _fileName; 8 | 9 | public EventLogFileFactory(string fileName) 10 | { 11 | _fileName = fileName; 12 | } 13 | 14 | public IEventLogFile EventLogFile 15 | { 16 | get 17 | { 18 | if (_fileName.ToLower().EndsWith(".evt")) 19 | { 20 | return new BinaryEventLogFile(_fileName); 21 | } 22 | else if (_fileName.ToLower().EndsWith(".csv")) 23 | { 24 | return new CsvEventLogFile(_fileName); 25 | } 26 | else if (_fileName.ToLower().EndsWith(".xml")) 27 | { 28 | return new XmlEventLogFile(_fileName); 29 | } 30 | else 31 | { 32 | throw new Exception("Not smart enuf yet to understand that format :P"); 33 | } 34 | } 35 | } 36 | } 37 | } -------------------------------------------------------------------------------- /EventLog/CsvEventLogFile.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | 3 | namespace pal.EventLog 4 | { 5 | public class CsvEventLogFile : AbstractEventLogFile 6 | { 7 | private readonly StreamReader _fileReader; 8 | 9 | public CsvEventLogFile(string fileName) : base(fileName) 10 | { 11 | _fileReader = new StreamReader(_fileName); 12 | } 13 | 14 | public override void Parse() 15 | { 16 | while (!_fileReader.EndOfStream) 17 | { 18 | string line = _fileReader.ReadLine(); 19 | if (!isComment(line)) 20 | { 21 | CsvEventLogRecord eventLogRecord = CsvEventLogRecord.Fetch(line); 22 | eventLogRecord.ContainingFile = this; 23 | _records.Add(eventLogRecord); 24 | } 25 | } 26 | _fileReader.Close(); 27 | } 28 | 29 | private bool isComment(string line) 30 | { 31 | return line.Trim().StartsWith("#"); 32 | } 33 | 34 | public override string ToString() 35 | { 36 | return Path.GetFileName(_fileName); 37 | } 38 | 39 | public override void Dispose() 40 | { 41 | _fileReader.Dispose(); 42 | } 43 | } 44 | } -------------------------------------------------------------------------------- /EventLog/EventLogFileCollection.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | 5 | namespace pal.EventLog 6 | { 7 | public class EventLogFileCollection : IEnumerable, IDisposable 8 | { 9 | private List _eventLogFiles = new List(); 10 | 11 | #region IEnumerable Members 12 | 13 | IEnumerator IEnumerable.GetEnumerator() 14 | { 15 | foreach (IEnumerable file in _eventLogFiles) 16 | { 17 | foreach (IEventLogRecord eventLogRecord in file) 18 | { 19 | yield return eventLogRecord; 20 | } 21 | } 22 | } 23 | 24 | public IEnumerator GetEnumerator() 25 | { 26 | return ((IEnumerable) this).GetEnumerator(); 27 | } 28 | 29 | #endregion 30 | 31 | public void Add(IEventLogFile file) 32 | { 33 | _eventLogFiles.Add(file); 34 | } 35 | 36 | public void Dispose() 37 | { 38 | foreach (IEventLogFile eventLogFile in _eventLogFiles) 39 | { 40 | eventLogFile.Dispose(); 41 | } 42 | } 43 | } 44 | } -------------------------------------------------------------------------------- /Console Client/Filters/AbstractInputFilter.cs: -------------------------------------------------------------------------------- 1 | using pal.EventLog; 2 | 3 | namespace pal.EventLogAnalyzer.ConsoleClient.Filters 4 | { 5 | internal abstract class AbstractInputFilter : IInputFilter 6 | { 7 | private readonly string _propertyName; 8 | protected object _parameter; 9 | 10 | public AbstractInputFilter(string propertyName) 11 | { 12 | _propertyName = propertyName; 13 | } 14 | 15 | #region IInputFilter Members 16 | 17 | public object Parameter 18 | { 19 | get { return _parameter; } 20 | set { _parameter = setParameter(value as string); } 21 | } 22 | 23 | 24 | public bool Filter(IEventLogRecord recordToFilter) 25 | { 26 | PropertyType propertyValue = (PropertyType) getPropertyFrom(recordToFilter); 27 | return recordMatchesCriteria(propertyValue); 28 | } 29 | 30 | #endregion 31 | 32 | protected abstract bool recordMatchesCriteria(PropertyType propertyValue); 33 | 34 | protected abstract PropertyType setParameter(string value); 35 | 36 | private object getPropertyFrom(IEventLogRecord recordToFilter) 37 | { 38 | return typeof (IEventLogRecord).GetProperty(_propertyName).GetValue(recordToFilter, null); 39 | } 40 | } 41 | } -------------------------------------------------------------------------------- /Console Client/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.InteropServices; 3 | 4 | // General Information about an assembly is controlled through the following 5 | // set of attributes. Change these attribute values to modify the information 6 | // associated with an assembly. 7 | 8 | [assembly : AssemblyTitle("Console Client")] 9 | [assembly : AssemblyDescription("")] 10 | [assembly : AssemblyConfiguration("")] 11 | [assembly : AssemblyCompany("ThoughtWorks")] 12 | [assembly : AssemblyProduct("Console Client")] 13 | [assembly : AssemblyCopyright("Copyright © ThoughtWorks 2007")] 14 | [assembly : AssemblyTrademark("")] 15 | [assembly : AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | 21 | [assembly : ComVisible(false)] 22 | 23 | // The following GUID is for the ID of the typelib if this project is exposed to COM 24 | 25 | [assembly : Guid("ef56ed61-287c-4861-9668-5944dac58bde")] 26 | 27 | // Version information for an assembly consists of the following four values: 28 | // 29 | // Major Version 30 | // Minor Version 31 | // Build Number 32 | // Revision 33 | // 34 | 35 | [assembly : AssemblyVersion("1.0.0.0")] 36 | [assembly : AssemblyFileVersion("1.0.0.0")] -------------------------------------------------------------------------------- /EventLog/BinaryEventLogFile.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | 4 | namespace pal.EventLog 5 | { 6 | public class BinaryEventLogFile : AbstractEventLogFile 7 | { 8 | private Footer _footer; 9 | private Header _header; 10 | private BinaryReader _logReader; 11 | 12 | public BinaryEventLogFile(string fileName) : base(fileName) 13 | { 14 | } 15 | 16 | public override void Parse() 17 | { 18 | FileStream logStream = new FileStream(_fileName, FileMode.Open, FileAccess.Read); 19 | _logReader = new BinaryReader(logStream); 20 | 21 | _header = Header.Fetch(_logReader); 22 | _header.Verify(); 23 | 24 | while (BinaryEventLogRecord.CanHaveALogRecord(_logReader)) 25 | { 26 | BinaryEventLogRecord eventLogRecord = BinaryEventLogRecord.Fetch(_logReader); 27 | eventLogRecord.ContainingFile = this; 28 | _records.Add(eventLogRecord); 29 | } 30 | 31 | _footer = Footer.Fetch(_logReader); 32 | _footer.Verify(); 33 | 34 | logStream.Close(); 35 | _logReader.Close(); 36 | } 37 | 38 | public override string ToString() 39 | { 40 | return Path.GetFileName(_fileName); 41 | } 42 | 43 | public override void Dispose() 44 | { 45 | _logReader = null; 46 | } 47 | } 48 | } -------------------------------------------------------------------------------- /Console Client/OutputFilterArguments.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace pal.EventLogAnalyzer.ConsoleClient 4 | { 5 | internal class OutputFilterArguments 6 | { 7 | private const string DEFAULT_REPORT_FIELDS = "All"; 8 | private readonly List _knownOutputArguments = new List(); 9 | private readonly Dictionary _providedOutputArguments = new Dictionary(); 10 | 11 | public OutputFilterArguments() 12 | { 13 | _knownOutputArguments.Add("-include"); 14 | _knownOutputArguments.Add("-output"); 15 | } 16 | 17 | public string OutputFolder 18 | { 19 | get { return _providedOutputArguments["-output"]; } 20 | } 21 | 22 | public object ReportFields 23 | { 24 | get 25 | { 26 | string argument = DEFAULT_REPORT_FIELDS; 27 | if (_providedOutputArguments.ContainsKey("-include")) 28 | argument = _providedOutputArguments["-include"]; 29 | 30 | return argument; 31 | } 32 | } 33 | 34 | public bool IsKnown(string @switch) 35 | { 36 | return _knownOutputArguments.Contains(@switch); 37 | } 38 | 39 | public void Add(string @switch, string parameter) 40 | { 41 | _providedOutputArguments.Add(@switch, parameter); 42 | } 43 | } 44 | } -------------------------------------------------------------------------------- /Console Client/Reporting/TextSummaryWriter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using pal.EventLog; 5 | using pal.EventLogAnalyzer.ConsoleClient.FuzzyComparer; 6 | 7 | namespace pal.EventLogAnalyzer.ConsoleClient.Reporting 8 | { 9 | internal class TextSummaryWriter : AbstractSummaryWriter 10 | { 11 | public TextSummaryWriter(ICollection> aggregates, FileNameGenerator fileNameGenerator) 12 | : base(aggregates, fileNameGenerator) 13 | { 14 | } 15 | 16 | public override void WriteIn(string folder) 17 | { 18 | StreamWriter summary = new StreamWriter(File.Open(folder + "\\Summary.txt", FileMode.Create)); 19 | summary.WriteLine("Analysis summary: {0}", DateTime.Now); 20 | summary.WriteLine("No of groups: {0}", _aggregates.Count); 21 | summary.WriteLine(); 22 | summary.WriteLine("Grouping details"); 23 | summary.WriteLine("----------------------------------------------------------------"); 24 | 25 | foreach (Aggregate aggregate in _aggregates) 26 | { 27 | summary.WriteLine("Following message occurs {0} times", aggregate.Entries.Count); 28 | summary.WriteLine(); 29 | summary.WriteLine(aggregate.AggregationCriteria); 30 | summary.WriteLine("############################################################################"); 31 | } 32 | summary.Close(); 33 | } 34 | } 35 | } -------------------------------------------------------------------------------- /Console Client/FuzzyComparer/Aggregate.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace pal.EventLogAnalyzer.ConsoleClient.FuzzyComparer 4 | { 5 | //Understands a group of similar items 6 | public class Aggregate 7 | { 8 | private readonly List _aggregateEntries = new List(); 9 | private readonly IComparisonAlgorithm _comparisonAlgorithm; 10 | private readonly string _seedAggregationCriteria; 11 | private readonly double _tolerableErrorMargin; 12 | 13 | public Aggregate(string seedAggregationCriteria, double tolerableErrorMargin) 14 | { 15 | _seedAggregationCriteria = seedAggregationCriteria; 16 | _tolerableErrorMargin = tolerableErrorMargin; 17 | _comparisonAlgorithm = new FuzzyStringComparisonAlgorithm(_seedAggregationCriteria); 18 | } 19 | 20 | public string AggregationCriteria 21 | { 22 | get { return _seedAggregationCriteria; } 23 | } 24 | 25 | public List Entries 26 | { 27 | get { return _aggregateEntries; } 28 | } 29 | 30 | public void Hold(AggregateItemType aggregateEntry) 31 | { 32 | _aggregateEntries.Add(aggregateEntry); 33 | } 34 | 35 | public bool IsAcceptable(string aggregationCriteria) 36 | { 37 | return _comparisonAlgorithm.DisSimilarityCoefficient(aggregationCriteria) <= _tolerableErrorMargin; 38 | } 39 | 40 | public override string ToString() 41 | { 42 | return AggregationCriteria; 43 | } 44 | } 45 | } -------------------------------------------------------------------------------- /EventLog/AbstractEventLogFile.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | 4 | namespace pal.EventLog 5 | { 6 | public abstract class AbstractEventLogFile : IEventLogFile 7 | { 8 | protected readonly string _fileName; 9 | protected List _records = new List(); 10 | 11 | public AbstractEventLogFile(string fileName) 12 | { 13 | _fileName = fileName; 14 | } 15 | 16 | #region IEventLogFile Members 17 | 18 | public int RecordCount 19 | { 20 | get { return _records.Count; } 21 | } 22 | 23 | public IEventLogRecord this[int recordNo] 24 | { 25 | get { return _records[recordNo]; } 26 | } 27 | 28 | IEnumerator IEnumerable.GetEnumerator() 29 | { 30 | for (int i = 0; i < RecordCount; i++) 31 | { 32 | yield return this[i]; 33 | } 34 | } 35 | 36 | public IEnumerator GetEnumerator() 37 | { 38 | return ((IEnumerable) this).GetEnumerator(); 39 | } 40 | 41 | public abstract void Parse(); 42 | 43 | public ArrayList Find(LogRecordSearchCriteria searchCriteria) 44 | { 45 | ArrayList searchResults = new ArrayList(); 46 | foreach (IEventLogRecord record in _records) 47 | { 48 | if (record.Matches(searchCriteria)) 49 | searchResults.Add(record); 50 | } 51 | return searchResults; 52 | } 53 | 54 | public abstract void Dispose(); 55 | 56 | #endregion 57 | } 58 | } -------------------------------------------------------------------------------- /Console Client/EventLogRecordClassifier.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using pal.EventLog; 3 | using pal.EventLogAnalyzer.ConsoleClient.FuzzyComparer; 4 | 5 | namespace pal.EventLogAnalyzer.ConsoleClient 6 | { 7 | public class EventLogRecordClassifier : ILogRecordClassifier 8 | { 9 | private readonly List> _aggregates = new List>(); 10 | 11 | public List> Aggregates 12 | { 13 | get { return _aggregates; } 14 | } 15 | 16 | #region ILogRecordClassifier Members 17 | 18 | public void Classify(List records, double toleranceMargin) 19 | { 20 | //TODO: Multi-threaded processing? Equi-split records, assign each to independent thread.. 21 | //Finally collate all results 22 | //Run thru all the resulting aggregations and merge them if multiple threads ended up creating same aggregations 23 | 24 | foreach (IEventLogRecord record in records) 25 | { 26 | bool foundExistingAggregate = false; 27 | foreach (Aggregate aggregate in _aggregates) 28 | { 29 | if (aggregate.IsAcceptable(record.Message)) 30 | { 31 | aggregate.Hold(record); 32 | foundExistingAggregate = true; 33 | } 34 | } 35 | if (!foundExistingAggregate) 36 | { 37 | Aggregate newAggregate = 38 | new Aggregate(record.Message, toleranceMargin); 39 | newAggregate.Hold(record); 40 | _aggregates.Add(newAggregate); 41 | } 42 | } 43 | } 44 | 45 | #endregion 46 | } 47 | } -------------------------------------------------------------------------------- /EventLog/Header.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Runtime.InteropServices; 4 | using System.Text; 5 | 6 | namespace pal.EventLog 7 | { 8 | [StructLayout(LayoutKind.Sequential, Pack = 1)] 9 | internal struct Header 10 | { 11 | public uint HeaderLength; 12 | public uint Signature; 13 | public uint Unknown1; 14 | public uint Unknown2; 15 | public uint Unknown3; 16 | public uint FooterOffset; 17 | public uint NextIndex; 18 | public uint FileLength; 19 | public uint Unknown6; 20 | public uint Unknown7; 21 | public uint Unknown8; 22 | public uint EndHeaderLength; 23 | 24 | public static Header Fetch(BinaryReader reader) 25 | { 26 | reader.BaseStream.Seek(0, SeekOrigin.Begin); 27 | byte[] rawHeader = reader.ReadBytes(Marshal.SizeOf(typeof (Header))); 28 | GCHandle gcHandle = GCHandle.Alloc(rawHeader, GCHandleType.Pinned); 29 | Header header = (Header) Marshal.PtrToStructure(gcHandle.AddrOfPinnedObject(), typeof (Header)); 30 | gcHandle.Free(); 31 | return header; 32 | } 33 | 34 | public void Verify() 35 | { 36 | if (Signature != 0x654C664c) 37 | throw new Exception(); 38 | } 39 | 40 | public override string ToString() 41 | { 42 | StringBuilder contents = new StringBuilder(); 43 | contents.Append("HeaderLength: ").Append(HeaderLength).Append("\n"); 44 | contents.Append("Signature: ").Append(Signature).Append("\n"); 45 | contents.Append("FooterOffset: ").Append(FooterOffset).Append("\n"); 46 | contents.Append("NextIndex: ").Append(NextIndex).Append("\n"); 47 | contents.Append("FileLength: ").Append(FileLength).Append("\n"); 48 | contents.Append("EndHeaderLength: ").Append(EndHeaderLength).Append("\n"); 49 | return contents.ToString(); 50 | } 51 | } 52 | } -------------------------------------------------------------------------------- /EventLog Analyzer - VS 2005.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 9.00 3 | # Visual Studio 2005 4 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EventLog", "EventLog\EventLog.csproj", "{540AA583-6BC3-4894-9A9F-44438F3955E5}" 5 | EndProject 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UnitTests", "UnitTests\UnitTests.csproj", "{2F9CB472-B8D1-466A-B9C8-C08360F58A72}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Console Client", "Console Client\Console Client.csproj", "{9F3849C7-CB75-42BF-9426-E466030D492F}" 9 | EndProject 10 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{2300BCDB-659C-42FC-8539-EBC36B9FBFD4}" 11 | ProjectSection(SolutionItems) = preProject 12 | eventloganalyzer.build = eventloganalyzer.build 13 | EndProjectSection 14 | EndProject 15 | Global 16 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 17 | Debug|Any CPU = Debug|Any CPU 18 | Release|Any CPU = Release|Any CPU 19 | EndGlobalSection 20 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 21 | {540AA583-6BC3-4894-9A9F-44438F3955E5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 22 | {540AA583-6BC3-4894-9A9F-44438F3955E5}.Debug|Any CPU.Build.0 = Debug|Any CPU 23 | {540AA583-6BC3-4894-9A9F-44438F3955E5}.Release|Any CPU.ActiveCfg = Release|Any CPU 24 | {540AA583-6BC3-4894-9A9F-44438F3955E5}.Release|Any CPU.Build.0 = Release|Any CPU 25 | {2F9CB472-B8D1-466A-B9C8-C08360F58A72}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 26 | {2F9CB472-B8D1-466A-B9C8-C08360F58A72}.Debug|Any CPU.Build.0 = Debug|Any CPU 27 | {2F9CB472-B8D1-466A-B9C8-C08360F58A72}.Release|Any CPU.ActiveCfg = Release|Any CPU 28 | {2F9CB472-B8D1-466A-B9C8-C08360F58A72}.Release|Any CPU.Build.0 = Release|Any CPU 29 | {9F3849C7-CB75-42BF-9426-E466030D492F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 30 | {9F3849C7-CB75-42BF-9426-E466030D492F}.Debug|Any CPU.Build.0 = Debug|Any CPU 31 | {9F3849C7-CB75-42BF-9426-E466030D492F}.Release|Any CPU.ActiveCfg = Release|Any CPU 32 | {9F3849C7-CB75-42BF-9426-E466030D492F}.Release|Any CPU.Build.0 = Release|Any CPU 33 | EndGlobalSection 34 | GlobalSection(SolutionProperties) = preSolution 35 | HideSolutionNode = FALSE 36 | EndGlobalSection 37 | EndGlobal 38 | -------------------------------------------------------------------------------- /EventLog Analyzer - VS 2008.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 10.00 3 | # Visual Studio 2008 4 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{2300BCDB-659C-42FC-8539-EBC36B9FBFD4}" 5 | ProjectSection(SolutionItems) = preProject 6 | eventloganalyzer.build = eventloganalyzer.build 7 | EndProjectSection 8 | EndProject 9 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EventLog", "EventLog\EventLog.csproj", "{540AA583-6BC3-4894-9A9F-44438F3955E5}" 10 | EndProject 11 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UnitTests", "UnitTests\UnitTests.csproj", "{2F9CB472-B8D1-466A-B9C8-C08360F58A72}" 12 | EndProject 13 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Console Client", "Console Client\Console Client.csproj", "{9F3849C7-CB75-42BF-9426-E466030D492F}" 14 | EndProject 15 | Global 16 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 17 | Debug|Any CPU = Debug|Any CPU 18 | Release|Any CPU = Release|Any CPU 19 | EndGlobalSection 20 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 21 | {540AA583-6BC3-4894-9A9F-44438F3955E5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 22 | {540AA583-6BC3-4894-9A9F-44438F3955E5}.Debug|Any CPU.Build.0 = Debug|Any CPU 23 | {540AA583-6BC3-4894-9A9F-44438F3955E5}.Release|Any CPU.ActiveCfg = Release|Any CPU 24 | {540AA583-6BC3-4894-9A9F-44438F3955E5}.Release|Any CPU.Build.0 = Release|Any CPU 25 | {2F9CB472-B8D1-466A-B9C8-C08360F58A72}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 26 | {2F9CB472-B8D1-466A-B9C8-C08360F58A72}.Debug|Any CPU.Build.0 = Debug|Any CPU 27 | {2F9CB472-B8D1-466A-B9C8-C08360F58A72}.Release|Any CPU.ActiveCfg = Release|Any CPU 28 | {2F9CB472-B8D1-466A-B9C8-C08360F58A72}.Release|Any CPU.Build.0 = Release|Any CPU 29 | {9F3849C7-CB75-42BF-9426-E466030D492F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 30 | {9F3849C7-CB75-42BF-9426-E466030D492F}.Debug|Any CPU.Build.0 = Debug|Any CPU 31 | {9F3849C7-CB75-42BF-9426-E466030D492F}.Release|Any CPU.ActiveCfg = Release|Any CPU 32 | {9F3849C7-CB75-42BF-9426-E466030D492F}.Release|Any CPU.Build.0 = Release|Any CPU 33 | EndGlobalSection 34 | GlobalSection(SolutionProperties) = preSolution 35 | HideSolutionNode = FALSE 36 | EndGlobalSection 37 | EndGlobal 38 | -------------------------------------------------------------------------------- /EventLogAnalyzer.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 11.00 3 | # Visual Studio 2010 4 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{2300BCDB-659C-42FC-8539-EBC36B9FBFD4}" 5 | ProjectSection(SolutionItems) = preProject 6 | eventloganalyzer.build = eventloganalyzer.build 7 | LICENSE.txt = LICENSE.txt 8 | README.md = README.md 9 | EndProjectSection 10 | EndProject 11 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EventLog", "EventLog\EventLog.csproj", "{540AA583-6BC3-4894-9A9F-44438F3955E5}" 12 | EndProject 13 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UnitTests", "UnitTests\UnitTests.csproj", "{2F9CB472-B8D1-466A-B9C8-C08360F58A72}" 14 | EndProject 15 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Console Client", "Console Client\Console Client.csproj", "{9F3849C7-CB75-42BF-9426-E466030D492F}" 16 | EndProject 17 | Global 18 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 19 | Debug|Any CPU = Debug|Any CPU 20 | Release|Any CPU = Release|Any CPU 21 | EndGlobalSection 22 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 23 | {540AA583-6BC3-4894-9A9F-44438F3955E5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 24 | {540AA583-6BC3-4894-9A9F-44438F3955E5}.Debug|Any CPU.Build.0 = Debug|Any CPU 25 | {540AA583-6BC3-4894-9A9F-44438F3955E5}.Release|Any CPU.ActiveCfg = Release|Any CPU 26 | {540AA583-6BC3-4894-9A9F-44438F3955E5}.Release|Any CPU.Build.0 = Release|Any CPU 27 | {2F9CB472-B8D1-466A-B9C8-C08360F58A72}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 28 | {2F9CB472-B8D1-466A-B9C8-C08360F58A72}.Debug|Any CPU.Build.0 = Debug|Any CPU 29 | {2F9CB472-B8D1-466A-B9C8-C08360F58A72}.Release|Any CPU.ActiveCfg = Release|Any CPU 30 | {2F9CB472-B8D1-466A-B9C8-C08360F58A72}.Release|Any CPU.Build.0 = Release|Any CPU 31 | {9F3849C7-CB75-42BF-9426-E466030D492F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 32 | {9F3849C7-CB75-42BF-9426-E466030D492F}.Debug|Any CPU.Build.0 = Debug|Any CPU 33 | {9F3849C7-CB75-42BF-9426-E466030D492F}.Release|Any CPU.ActiveCfg = Release|Any CPU 34 | {9F3849C7-CB75-42BF-9426-E466030D492F}.Release|Any CPU.Build.0 = Release|Any CPU 35 | EndGlobalSection 36 | GlobalSection(SolutionProperties) = preSolution 37 | HideSolutionNode = FALSE 38 | EndGlobalSection 39 | EndGlobal 40 | -------------------------------------------------------------------------------- /EventLog/Footer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Runtime.InteropServices; 4 | using System.Text; 5 | 6 | namespace pal.EventLog 7 | { 8 | [StructLayout(LayoutKind.Sequential, Pack = 1)] 9 | internal struct Footer 10 | { 11 | public uint FooterLength; 12 | public uint Unknown0; 13 | public uint Unknown1; 14 | public uint Unknown2; 15 | public uint Unknown3; 16 | public uint Unknown4; 17 | public uint FooterOffset; 18 | public uint NextIndex; 19 | public uint Unknown7; 20 | public uint EndFooterLength; 21 | 22 | public static Footer Fetch(BinaryReader reader) 23 | { 24 | reader.BaseStream.Seek(-40, SeekOrigin.End); 25 | byte[] rawHeader = reader.ReadBytes(Marshal.SizeOf(typeof (Footer))); 26 | GCHandle gcHandle = GCHandle.Alloc(rawHeader, GCHandleType.Pinned); 27 | Footer footer = (Footer) Marshal.PtrToStructure(gcHandle.AddrOfPinnedObject(), typeof (Footer)); 28 | gcHandle.Free(); 29 | return footer; 30 | } 31 | 32 | public void Verify() 33 | { 34 | if (Unknown0 != 0x11111111 || Unknown1 != 0x22222222 || Unknown2 != 0x33333333 || Unknown3 != 0x44444444) 35 | throw new Exception(); 36 | } 37 | 38 | public override string ToString() 39 | { 40 | StringBuilder contents = new StringBuilder(); 41 | contents.Append("FooterLength: ").Append(FooterLength).Append("\n"); 42 | contents.Append("Unknown0: ").Append(Unknown0).Append("\n"); 43 | contents.Append("Unknown1: ").Append(Unknown1).Append("\n"); 44 | contents.Append("Unknown2: ").Append(Unknown2).Append("\n"); 45 | contents.Append("Unknown3: ").Append(Unknown3).Append("\n"); 46 | contents.Append("Unknown4: ").Append(Unknown4).Append("\n"); 47 | contents.Append("FooterOffset: ").Append(FooterOffset).Append("\n"); 48 | contents.Append("NextIndex: ").Append(NextIndex).Append("\n"); 49 | contents.Append("Unknown7: ").Append(Unknown7).Append("\n"); 50 | contents.Append("EndFooterLength: ").Append(EndFooterLength).Append("\n"); 51 | return contents.ToString(); 52 | } 53 | } 54 | } -------------------------------------------------------------------------------- /Console Client/CommandLineArgumentHandler.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace pal.EventLogAnalyzer.ConsoleClient 4 | { 5 | internal class CommandLineArgumentHandler 6 | { 7 | private readonly InputFilterArguments _inputFilterArguments; 8 | private readonly OutputFilterArguments _outputArguments; 9 | private readonly ProcessControlArguments _processControlArguments; 10 | 11 | public CommandLineArgumentHandler() 12 | { 13 | //TODO: Still to handle mandatory arguments 14 | _inputFilterArguments = new InputFilterArguments(); 15 | _outputArguments = new OutputFilterArguments(); 16 | _processControlArguments = new ProcessControlArguments(); 17 | } 18 | 19 | public InputFilterArguments Filters 20 | { 21 | get { return _inputFilterArguments; } 22 | } 23 | 24 | public ProcessControlArguments ProcessArguments 25 | { 26 | get { return _processControlArguments; } 27 | } 28 | 29 | public OutputFilterArguments OutputArguments 30 | { 31 | get { return _outputArguments; } 32 | } 33 | 34 | public void PickArgument(string argumentSwitch, string argumentParameter) 35 | { 36 | if (!IsKnown(argumentSwitch)) 37 | throw new Exception(); 38 | 39 | //TODO: BaseArgumentType for all three types of arguments? 40 | 41 | if (_inputFilterArguments.IsKnown(argumentSwitch)) 42 | { 43 | _inputFilterArguments.Add(argumentSwitch, argumentParameter); 44 | } 45 | 46 | if (_outputArguments.IsKnown(argumentSwitch)) 47 | { 48 | _outputArguments.Add(argumentSwitch, argumentParameter); 49 | return; 50 | } 51 | 52 | if (_processControlArguments.IsKnown(argumentSwitch)) 53 | { 54 | _processControlArguments.Add(argumentSwitch, argumentParameter); 55 | } 56 | } 57 | 58 | public bool IsKnown(string @switch) 59 | { 60 | return _inputFilterArguments.IsKnown(@switch) 61 | || _outputArguments.IsKnown(@switch) 62 | || _processControlArguments.IsKnown(@switch); 63 | } 64 | } 65 | } -------------------------------------------------------------------------------- /Console Client/FuzzyComparer/FuzzyStringComparisonAlgorithm.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text; 3 | using System.Text.RegularExpressions; 4 | 5 | namespace pal.EventLogAnalyzer.ConsoleClient.FuzzyComparer 6 | { 7 | public class FuzzyStringComparisonAlgorithm : IComparisonAlgorithm 8 | { 9 | private const string ALLOWED_CHARACTERS = @"[A-Za-z\n\r\t]+"; 10 | private const string GUID_PATTERN = ".{8}-.{4}-.{4}-.{4}-.{12}"; 11 | private readonly Regex _onlyAllowedCharacters = new Regex(ALLOWED_CHARACTERS, RegexOptions.Singleline | RegexOptions.Compiled | RegexOptions.IgnoreCase); 12 | private readonly Regex _guidMatcher = new Regex(GUID_PATTERN, RegexOptions.Compiled); 13 | private readonly double _leftHash = 0; 14 | 15 | public FuzzyStringComparisonAlgorithm(string lhs) 16 | { 17 | _leftHash = fuzzyHash(lhs); 18 | } 19 | 20 | #region IComparisonAlgorithm Members 21 | 22 | public double DisSimilarityCoefficient(string rhs) 23 | { 24 | return Math.Abs(_leftHash - fuzzyHash(rhs)); 25 | } 26 | 27 | #endregion 28 | 29 | private double fuzzyHash(string @string) 30 | { 31 | double hashCode = 0; 32 | double positionalWeight = 0; 33 | string cleanString = stripUnwantedAndSanitize(@string); 34 | foreach (char c in cleanString) 35 | { 36 | if (char.IsControl(c)) 37 | //To compute for every line.. Makes the computation fuzzier.. Reduces mismatches due to same/similar strings on different lines 38 | positionalWeight = 0; 39 | else 40 | hashCode += c * positionalWeight++; //Keep it case insensitive 41 | } 42 | return hashCode; 43 | } 44 | 45 | private string stripUnwantedAndSanitize(string @string) 46 | { 47 | string withoutGuid = _guidMatcher.Replace(@string, string.Empty); 48 | 49 | StringBuilder cleanString = new StringBuilder(); 50 | MatchCollection validStrings = _onlyAllowedCharacters.Matches(withoutGuid); 51 | foreach (Match validString in validStrings) 52 | { 53 | cleanString.Append(validString); 54 | } 55 | return cleanString.ToString(); 56 | } 57 | } 58 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | EventLog Analyzer 2 | ================= 3 | 4 | This is a utility I wrote a few years ago for automating analysis of Event Log files exported from production machines. 5 | The idea was to automate identifying and classifying similar Event Log records to get a quick overview of all the failure types and to focus on and fix the most frequent failure types. 6 | 7 | It reads a bunch of Event Log files (*.evt) and automatically groups related Event Log records based on their similarity. 8 | Once the analysis is complete, it writes out a CSV each for every group of related events found, and a summary text with a bunch of statistics. 9 | You can provide it some filters that tell it to look at only the Event Log records that match a certain criteria. 10 | 11 | Warning: It can be quite slow if you're processing a large number of Event Log records. 12 | 13 | Hopefully, I'll get around to fixing the performance when I get some time (now that I've reopened the project after over 3 years :D). 14 | 15 | ### Building the Project 16 | This project has now been migrated to use .NET 6. To build it: 17 | 1. Clone the repository. 18 | 2. Navigate to the root directory of the cloned repository. 19 | 3. Run the command: `dotnet build EventLogAnalyzer.sln` 20 | 21 | The main executable will be found in `Console Client/bin/Debug/net6.0/` or `Console Client/bin/Release/net6.0/` depending on the build configuration. 22 | 23 | ### Commandline Options: 24 | Pre-filters:: 25 | Time: -after -before -at 26 | Type: -type :: Error|FailureAudit|Information|SuccessAudit|Warning 27 | Message: -startsWith -contains 28 | Computer: -computer 29 | Source: -source 30 | EventId: -eventId 31 | 32 | Processing options: 33 | -tolerance :: Tolerance threshold for determining similarity of two Event log messages. 34 | Defaults to 0. i.e. Maximum accuracy => Least fuzzy => More groups 35 | 36 | Output options: 37 | Fields in output reports: -include (FieldNames|All) :: FieldNames:= Type|DateTime|Source|Category|EventId|User|Computer|Message 38 | Output Folder: -output :: Folder must exist 39 | 40 | References 41 | ---------- 42 | * CodeProject article on parsing Event Log files: [http://www.codeproject.com/KB/string/EventLogParser.aspx](http://www.codeproject.com/KB/string/EventLogParser.aspx), by [J a a n s](http://www.codeproject.com/Members/J-a-a-n-s) 43 | * MSDN Reference on Event Log file format: [http://msdn.microsoft.com/en-us/library/bb309026(VS.85).aspx] 44 | 45 | TODO: 46 | ----- 47 | * Improve performance (Parallelize log record comparisons?) 48 | * Bring back/rewrite the GUI client 49 | * Refactor/cleanup. 50 | -------------------------------------------------------------------------------- /UnitTests/SpikeTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | using System.Text.RegularExpressions; 5 | using NUnit.Framework; 6 | 7 | namespace pal.EventLogAnalyzer.UnitTests 8 | { 9 | [TestFixture] 10 | public class SpikeTests 11 | { 12 | private string stripUnwantedAndSanitize(string @string) 13 | { 14 | string noGuid = Regex.Replace(@string, "{.{8}-.{4}-.{4}-.{4}-.{12}}", string.Empty); 15 | StringBuilder cleanString = new StringBuilder(); 16 | 17 | MatchCollection validStrings = Regex.Matches(noGuid, "[A-Za-z\n\r\t]+"); 18 | foreach (Match validString in validStrings) 19 | { 20 | cleanString.Append(validString); 21 | } 22 | return cleanString.ToString(); 23 | } 24 | 25 | [Test] 26 | public void GenerateFileNamesUsingYield() 27 | { 28 | IEnumerator nameGenerator = getNextFileName(); 29 | for (int i = 0; i < 10; i++) 30 | { 31 | nameGenerator.MoveNext(); 32 | Console.Out.WriteLine("getNextFileName() = {0}", nameGenerator.Current); 33 | } 34 | } 35 | 36 | private IEnumerator getNextFileName() 37 | { 38 | int i = 0; 39 | while(i _filteredRecords = new List(); 12 | private static readonly EventLogFileCollection _inputFiles = new EventLogFileCollection(); 13 | 14 | public static void Main(string[] args) 15 | { 16 | #region Extract Arguments 17 | 18 | if (args.Length == 0) printHelpAndExit(); 19 | 20 | CommandLineArgumentHandler argumentHandler = new CommandLineArgumentHandler(); 21 | for (int i = 0; i < args.Length; i += 2) 22 | { 23 | if (args[i].ToLower().Contains("help")) 24 | { 25 | printHelpAndExit(); 26 | } 27 | 28 | if (argumentHandler.IsKnown(args[i])) 29 | argumentHandler.PickArgument(args[i], args[i + 1]); 30 | else 31 | { 32 | IEventLogFile eventLogFile = new EventLogFileFactory(args[i]).EventLogFile; 33 | eventLogFile.Parse(); 34 | _inputFiles.Add(eventLogFile); 35 | } 36 | } 37 | 38 | #endregion 39 | 40 | #region Apply Input Filters 41 | 42 | foreach (IEventLogRecord eventLogRecord in _inputFiles) 43 | { 44 | if (argumentHandler.Filters.Filter(eventLogRecord)) 45 | _filteredRecords.Add(eventLogRecord); 46 | } 47 | 48 | #endregion 49 | 50 | disposeAllInputFilesToReduceMemoryFootprint(); 51 | 52 | #region Auto-classify filtered records 53 | 54 | EventLogRecordClassifier classifier = new EventLogRecordClassifier(); 55 | classifier.Classify(_filteredRecords, argumentHandler.ProcessArguments.ToleranceMargin); 56 | 57 | #endregion 58 | 59 | #region Generate reports 60 | 61 | ReportGenerator reportGenerator = 62 | new ReportGenerator(argumentHandler.OutputArguments.OutputFolder, 63 | argumentHandler.OutputArguments.ReportFields); 64 | reportGenerator.WriteReportsFor(classifier.Aggregates); 65 | 66 | #endregion 67 | } 68 | 69 | private static void disposeAllInputFilesToReduceMemoryFootprint() 70 | { 71 | _inputFiles.Dispose(); 72 | } 73 | 74 | private static void printHelpAndExit() 75 | { 76 | Console.Out.WriteLine(Resources.HelpText); 77 | Environment.Exit(1); 78 | } 79 | } 80 | } -------------------------------------------------------------------------------- /Console Client/InputFilterArguments.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using pal.EventLog; 3 | using pal.EventLogAnalyzer.ConsoleClient.Filters; 4 | 5 | namespace pal.EventLogAnalyzer.ConsoleClient 6 | { 7 | internal class InputFilterArguments 8 | { 9 | private const string GENERATED_TIME_PROPERTY = "GeneratedTime"; 10 | private const string EVENT_LOG_MESSAGE_PROPERTY = "Message"; 11 | private const string COMPUTER_NAME_PROPERTY = "Computer"; 12 | private const string EVENT_SOURCE_PROPERTY = "Source"; 13 | private const string USER_NAME_PROPERTY = "User"; 14 | private const string EVENT_ID_PROPERTY = "EventId"; 15 | private const string EVENT_TYPE_PROPERTY = "Type"; 16 | 17 | private readonly List _filters = new List(); 18 | private readonly Dictionary _knownInputFilters = new Dictionary(); 19 | 20 | public InputFilterArguments() 21 | { 22 | _knownInputFilters.Add("-after", new AfterDateTimeFilter(GENERATED_TIME_PROPERTY)); 23 | _knownInputFilters.Add("-before", new BeforeDateTimeFilter(GENERATED_TIME_PROPERTY)); 24 | _knownInputFilters.Add("-at", new AtDateTimeFilter(GENERATED_TIME_PROPERTY)); 25 | _knownInputFilters.Add("-startsWith", new StringStartsWithFilter(EVENT_LOG_MESSAGE_PROPERTY)); 26 | _knownInputFilters.Add("-contains", new StringContainsFilter(EVENT_LOG_MESSAGE_PROPERTY)); 27 | _knownInputFilters.Add("-computer", new StringEqualityFilter(COMPUTER_NAME_PROPERTY)); 28 | _knownInputFilters.Add("-source", new StringEqualityFilter(EVENT_SOURCE_PROPERTY)); 29 | _knownInputFilters.Add("-user", new StringEqualityFilter(USER_NAME_PROPERTY)); 30 | _knownInputFilters.Add("-eventId", new IntegerEqualityFilter(EVENT_ID_PROPERTY)); 31 | _knownInputFilters.Add("-type", new EventTypeFilter(EVENT_TYPE_PROPERTY)); 32 | _knownInputFilters.Add("-category", new NullFilter()); 33 | } 34 | 35 | public bool Filter(IEventLogRecord record) 36 | { 37 | foreach (IInputFilter filter in _filters) 38 | { 39 | if (!filter.Filter(record)) 40 | return false; 41 | } 42 | return true; 43 | } 44 | 45 | public void Add(IInputFilter inputFilter) 46 | { 47 | _filters.Add(inputFilter); 48 | } 49 | 50 | public bool IsKnown(string @switch) 51 | { 52 | return _knownInputFilters.ContainsKey(@switch); 53 | } 54 | 55 | public void Add(string @switch, string parameter) 56 | { 57 | IInputFilter inputFilter = _knownInputFilters[@switch]; 58 | inputFilter.Parameter = parameter; 59 | _filters.Add(inputFilter); 60 | } 61 | } 62 | } -------------------------------------------------------------------------------- /EventLog/Metadata.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Runtime.InteropServices; 4 | using System.Text; 5 | 6 | namespace pal.EventLog 7 | { 8 | [StructLayout(LayoutKind.Sequential, Pack = 1)] 9 | public struct Metadata 10 | { 11 | public uint Length; 12 | public uint Reserved; 13 | public uint RecordNumber; 14 | public uint TimeGenerated; 15 | public uint TimeWritten; 16 | public uint EventID; 17 | public ushort EventType; 18 | public ushort NumStrings; 19 | public ushort EventCategory; 20 | public ushort ReservedFlags; 21 | public uint ClosingRecordNumber; 22 | public uint StringOffset; 23 | public uint UserSidLength; 24 | public uint UserSidOffset; 25 | public uint DataLength; 26 | public uint DataOffset; 27 | 28 | public static Metadata Fetch(BinaryReader readerPositionedAtTheRightOffset) 29 | { 30 | return Fetch(readerPositionedAtTheRightOffset.ReadBytes(Globals.MetadataSize)); 31 | } 32 | 33 | public static Metadata Fetch(byte[] metadataBytes) 34 | { 35 | byte[] rawMetadata = new byte[Globals.MetadataSize]; 36 | Array.Copy(metadataBytes, rawMetadata, Globals.MetadataSize); 37 | GCHandle gcHandle = GCHandle.Alloc(rawMetadata, GCHandleType.Pinned); 38 | Metadata metadata = (Metadata) Marshal.PtrToStructure(gcHandle.AddrOfPinnedObject(), typeof (Metadata)); 39 | gcHandle.Free(); 40 | return metadata; 41 | } 42 | 43 | public override string ToString() 44 | { 45 | StringBuilder contents = new StringBuilder(); 46 | contents.Append("Length: ").Append(Length).Append("\n"); 47 | contents.Append("Reserved: ").Append(Reserved).Append("\n"); 48 | contents.Append("RecordNumber: ").Append(RecordNumber).Append("\n"); 49 | contents.Append("TimeGenerated: ").Append(DateTime.FromBinary(TimeGenerated)).Append("\n"); 50 | contents.Append("TimeWritten: ").Append(DateTime.FromBinary(TimeWritten)).Append("\n"); 51 | contents.Append("EventID: ").Append(EventID).Append("\n"); 52 | contents.Append("EventType: ").Append(EventType).Append("\n"); 53 | contents.Append("NumStrings: ").Append(NumStrings).Append("\n"); 54 | contents.Append("EventCategory: ").Append(EventCategory).Append("\n"); 55 | contents.Append("ReservedFlags: ").Append(ReservedFlags).Append("\n"); 56 | contents.Append("ClosingRecordNumber: ").Append(ClosingRecordNumber).Append("\n"); 57 | contents.Append("StringOffset: ").Append(StringOffset).Append("\n"); 58 | contents.Append("UserSidLength: ").Append(UserSidLength).Append("\n"); 59 | contents.Append("UserSidOffset: ").Append(UserSidOffset).Append("\n"); 60 | contents.Append("DataLength: ").Append(DataLength).Append("\n"); 61 | contents.Append("DataOffset: ").Append(DataOffset).Append("\n"); 62 | return contents.ToString(); 63 | } 64 | } 65 | } -------------------------------------------------------------------------------- /Console Client/Reporting/ReportGenerator.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using pal.EventLog; 5 | using pal.EventLogAnalyzer.ConsoleClient.FuzzyComparer; 6 | 7 | namespace pal.EventLogAnalyzer.ConsoleClient.Reporting 8 | { 9 | internal class ReportGenerator 10 | { 11 | private const string FILE_PREFIX = "Group"; 12 | private const string FILE_EXTENSION = ".csv"; 13 | private readonly string _folder; 14 | private readonly object _reportFields; 15 | private readonly FileNameGenerator _fileNameGenerator; 16 | 17 | public ReportGenerator(string folder, object reportFields) 18 | { 19 | _folder = folder; 20 | _reportFields = reportFields; 21 | _fileNameGenerator = new FileNameGenerator(FILE_PREFIX, FILE_EXTENSION); 22 | 23 | if (!Directory.Exists(_folder)) 24 | throw new Exception("Need an existing directory"); 25 | } 26 | 27 | public void WriteReportsFor(List> aggregates) 28 | { 29 | aggregates.Sort(DescendingSortByNumberOfEntries); 30 | writeSummary(aggregates); 31 | writeDetailedReport(aggregates); 32 | } 33 | 34 | private void writeDetailedReport(List> aggregates) 35 | { 36 | aggregates.ForEach(delegate(Aggregate aggregate) { writeAggregateDetails(aggregate); }); 37 | } 38 | 39 | private void writeAggregateDetails(Aggregate aggregate) 40 | { 41 | StreamWriter details = 42 | new StreamWriter(File.Open(_folder + "\\" + _fileNameGenerator.NextName, FileMode.Create)); 43 | foreach (IEventLogRecord record in aggregate.Entries) 44 | { 45 | details.WriteLine(csv(record)); 46 | } 47 | details.Close(); 48 | } 49 | 50 | private string csv(IEventLogRecord record) 51 | { 52 | //ComputerName, [Logfile], [CategoryString], Type, User, [EventCode], SourceName, EventIdentifier, Message, [RecordNumber], TimeGenerated 53 | return string.Format("{0},{1},{2},{3},{4},{5},{6},\"{7}\",{8},{9}", 54 | record.Computer, string.Empty, record.Type, record.User, string.Empty, record.Source, 55 | record.EventId, 56 | sanitizeString(record), string.Empty, record.GeneratedTime); 57 | } 58 | 59 | private string sanitizeString(IEventLogRecord record) 60 | { 61 | return record.Message.Replace("\"", "\"\""); 62 | } 63 | 64 | private int DescendingSortByNumberOfEntries(Aggregate x, Aggregate y) 65 | { 66 | return y.Entries.Count.CompareTo(x.Entries.Count); 67 | } 68 | 69 | private void writeSummary(ICollection> aggregates) 70 | { 71 | AbstractSummaryWriter htmlSummaryWriter = new HtmlSummaryWriter(aggregates, new FileNameGenerator(FILE_PREFIX, FILE_EXTENSION)); 72 | htmlSummaryWriter.WriteIn(_folder); 73 | } 74 | } 75 | } -------------------------------------------------------------------------------- /Console Client/Properties/Resources.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:2.0.50727.4952 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | using System; 12 | 13 | 14 | 15 | /// 16 | /// A strongly-typed resource class, for looking up localized strings, etc. 17 | /// 18 | // This class was auto-generated by the StronglyTypedResourceBuilder 19 | // class via a tool like ResGen or Visual Studio. 20 | // To add or remove a member, edit your .ResX file then rerun ResGen 21 | // with the /str option, or rebuild your VS project. 22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "2.0.0.0")] 23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 25 | internal class Resources { 26 | 27 | private static global::System.Resources.ResourceManager resourceMan; 28 | 29 | private static global::System.Globalization.CultureInfo resourceCulture; 30 | 31 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] 32 | internal Resources() { 33 | } 34 | 35 | /// 36 | /// Returns the cached ResourceManager instance used by this class. 37 | /// 38 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 39 | internal static global::System.Resources.ResourceManager ResourceManager { 40 | get { 41 | if (object.ReferenceEquals(resourceMan, null)) { 42 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Resources", typeof(Resources).Assembly); 43 | resourceMan = temp; 44 | } 45 | return resourceMan; 46 | } 47 | } 48 | 49 | /// 50 | /// Overrides the current thread's CurrentUICulture property for all 51 | /// resource lookups using this strongly typed resource class. 52 | /// 53 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 54 | internal static global::System.Globalization.CultureInfo Culture { 55 | get { 56 | return resourceCulture; 57 | } 58 | set { 59 | resourceCulture = value; 60 | } 61 | } 62 | 63 | /// 64 | /// Looks up a localized string similar to Available options: 65 | /// Pre-filters:: 66 | /// Time: -after -before -at 67 | /// Type: -type :: Error|FailureAudit|Information|SuccessAudit|Warning 68 | /// Message: -startsWith -contains 69 | /// Computer: -computer 70 | /// Source: -source 71 | /// EventId: -eventId 72 | /// 73 | /// Processing options: 74 | /// -tolerance :: Tolerance threshold for determining similarity of two Event log messages. 75 | /// Defaults to 0. i.e. Maximum accuracy => Least fuzzy => More groups 76 | /// 77 | /// Output options: 78 | /// Fields in output reports: -include [rest of string was truncated]";. 79 | /// 80 | internal static string HelpText { 81 | get { 82 | return ResourceManager.GetString("HelpText", resourceCulture); 83 | } 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /Console Client/Reporting/HtmlSummaryWriter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Text; 5 | using pal.EventLog; 6 | using pal.EventLogAnalyzer.ConsoleClient.FuzzyComparer; 7 | 8 | namespace pal.EventLogAnalyzer.ConsoleClient.Reporting 9 | { 10 | internal class HtmlSummaryWriter : AbstractSummaryWriter 11 | { 12 | public HtmlSummaryWriter(ICollection> aggregates, FileNameGenerator fileNameGenerator) 13 | : base(aggregates, fileNameGenerator) 14 | { 15 | } 16 | 17 | public override void WriteIn(string folder) 18 | { 19 | StreamWriter summary = new StreamWriter(File.Open(folder + "\\Summary.htm", FileMode.Create)); 20 | StringBuilder summaryHtml = new StringBuilder(); 21 | summaryHtml.Append(""); 22 | summaryHtml.Append(""); 23 | summaryHtml.Append(""); 29 | summaryHtml.Append(""); 30 | summaryHtml.Append(""); 31 | summaryHtml.Append("

Analysis Summary


Generated on: ").Append(DateTime.Now); 32 | summaryHtml.Append("
No of groups: ").Append(_aggregates.Count).Append("
"); 33 | summaryHtml.Append("

Grouping details

"); 34 | summaryHtml.Append( 35 | ""); 36 | 37 | double totalEntries = 0; 38 | DateTime minTime = DateTime.MaxValue, maxTime = DateTime.MinValue; 39 | 40 | foreach (Aggregate aggregate in _aggregates) 41 | { 42 | string groupFileName = _fileNameGenerator.NextName; 43 | summaryHtml.Append(""); 47 | 48 | totalEntries += aggregate.Entries.Count; 49 | 50 | foreach (IEventLogRecord eventLogRecord in aggregate.Entries) 51 | { 52 | if (eventLogRecord.GeneratedTime.CompareTo(minTime) < 0) minTime = eventLogRecord.GeneratedTime; 53 | if (eventLogRecord.GeneratedTime.CompareTo(maxTime) > 0) maxTime = eventLogRecord.GeneratedTime; 54 | } 55 | } 56 | summaryHtml.Append("
Number of occurencesMessageDetails
").Append(aggregate.Entries.Count) 44 | .Append("
").Append(aggregate.AggregationCriteria)
45 |                     .Append("
").Append("").Append(groupFileName).Append("
"); 57 | 58 | summaryHtml.Append("

Total number of records processed: ").Append(totalEntries).Append("
"); 59 | summaryHtml.Append("Time range: [From: ").Append(minTime).Append(" Till:").Append(maxTime).Append("]
"); 60 | 61 | summaryHtml.Append("

"); 62 | summaryHtml.Append(""); 63 | summaryHtml.Append(""); 64 | 65 | summary.Write(summaryHtml); 66 | summary.Close(); 67 | } 68 | } 69 | } -------------------------------------------------------------------------------- /Console Client/Properties/Resources.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.1 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace pal.EventLogAnalyzer.ConsoleClient.Properties { 12 | using System; 13 | 14 | 15 | /// 16 | /// A strongly-typed resource class, for looking up localized strings, etc. 17 | /// 18 | // This class was auto-generated by the StronglyTypedResourceBuilder 19 | // class via a tool like ResGen or Visual Studio. 20 | // To add or remove a member, edit your .ResX file then rerun ResGen 21 | // with the /str option, or rebuild your VS project. 22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] 23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 25 | internal class Resources { 26 | 27 | private static global::System.Resources.ResourceManager resourceMan; 28 | 29 | private static global::System.Globalization.CultureInfo resourceCulture; 30 | 31 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] 32 | internal Resources() { 33 | } 34 | 35 | /// 36 | /// Returns the cached ResourceManager instance used by this class. 37 | /// 38 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 39 | internal static global::System.Resources.ResourceManager ResourceManager { 40 | get { 41 | if (object.ReferenceEquals(resourceMan, null)) { 42 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("pal.EventLogAnalyzer.ConsoleClient.Properties.Resources", typeof(Resources).Assembly); 43 | resourceMan = temp; 44 | } 45 | return resourceMan; 46 | } 47 | } 48 | 49 | /// 50 | /// Overrides the current thread's CurrentUICulture property for all 51 | /// resource lookups using this strongly typed resource class. 52 | /// 53 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 54 | internal static global::System.Globalization.CultureInfo Culture { 55 | get { 56 | return resourceCulture; 57 | } 58 | set { 59 | resourceCulture = value; 60 | } 61 | } 62 | 63 | /// 64 | /// Looks up a localized string similar to Available options: 65 | /// Pre-filters:: 66 | /// Time: -after -before -at 67 | /// Type: -type :: Error|FailureAudit|Information|SuccessAudit|Warning 68 | /// Message: -startsWith -contains 69 | /// Computer: -computer 70 | /// Source: -source 71 | /// EventId: -eventId 72 | /// 73 | /// Processing options: 74 | /// -tolerance :: Tolerance threshold for determining similarity of two Event log messages. 75 | /// Defaults to 0. i.e. Maximum accuracy => Least fuzzy => More groups 76 | /// 77 | /// Output options: 78 | /// Fields in output reports: -include [rest of string was truncated]";. 79 | /// 80 | internal static string HelpText { 81 | get { 82 | return ResourceManager.GetString("HelpText", resourceCulture); 83 | } 84 | } 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /EventLog/XmlEventLogRecord.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using System.Text.RegularExpressions; 5 | using System.Xml; 6 | 7 | namespace pal.EventLog 8 | { 9 | public class XmlEventLogRecord : IEventLogRecord 10 | { 11 | private const string DATETIME_MATCHER = 12 | @"(?\d{4})(?\d{2})(?\d{2})(?\d{2})(?\d{2})(?\d{2})\.(?\d*)"; 13 | 14 | private const string COMPUTER_XPATH = "Computer"; 15 | private const string EVENT_TYPE_XPATH = "Type"; 16 | private const string USER_XPATH = "User"; 17 | private const string EVENT_SOURCE_XPATH = "Source"; 18 | private const string EVENT_ID_XPATH = "EventId"; 19 | private const string EVENT_MESSAGE_XPATH = "Message"; 20 | private const string GENERATED_TIME_XPATH = "TimeGenerated"; 21 | 22 | private static readonly Regex _dateTimeMatcher = new Regex(DATETIME_MATCHER, RegexOptions.Compiled); 23 | private ushort _category; 24 | private string _computer; 25 | private IEnumerable _containingFile; 26 | private int _eventId; 27 | private DateTime _generatedTime; 28 | private string _message; 29 | private string _source; 30 | private EventLogEntryType _type; 31 | private string _user; 32 | 33 | #region IEventLogRecord Members 34 | 35 | public EventLogEntryType Type 36 | { 37 | get { return _type; } 38 | } 39 | 40 | public DateTime GeneratedTime 41 | { 42 | get { return _generatedTime; } 43 | } 44 | 45 | public string Source 46 | { 47 | get { return _source; } 48 | } 49 | 50 | public ushort Category 51 | { 52 | get { return _category; } 53 | } 54 | 55 | public int EventId 56 | { 57 | get { return _eventId; } 58 | } 59 | 60 | public string User 61 | { 62 | get { return _user; } 63 | } 64 | 65 | public string Computer 66 | { 67 | get { return _computer; } 68 | } 69 | 70 | public string Message 71 | { 72 | get { return _message; } 73 | } 74 | 75 | public IEnumerable ContainingFile 76 | { 77 | get { return _containingFile; } 78 | set { _containingFile = value; } 79 | } 80 | 81 | public bool Matches(LogRecordSearchCriteria searchCriteria) 82 | { 83 | if (_source.IndexOf(searchCriteria.Source) != -1 84 | || _message.IndexOf(searchCriteria.Message) != -1) 85 | return true; 86 | return false; 87 | } 88 | 89 | #endregion 90 | 91 | public static XmlEventLogRecord Fetch(XmlNode xmlNode) 92 | { 93 | //ComputerName, [Logfile], [CategoryString], Type, User, [EventCode], SourceName, EventIdentifier, Message, [RecordNumber], TimeGenerated 94 | XmlEventLogRecord eventLogRecord = new XmlEventLogRecord(); 95 | eventLogRecord._computer = xmlNode.SelectSingleNode(COMPUTER_XPATH).InnerText; 96 | eventLogRecord._type = getEventLogEntryType(xmlNode.SelectSingleNode(EVENT_TYPE_XPATH).InnerText); 97 | eventLogRecord._user = xmlNode.SelectSingleNode(USER_XPATH).InnerText; 98 | eventLogRecord._source = xmlNode.SelectSingleNode(EVENT_SOURCE_XPATH).InnerText; 99 | eventLogRecord._eventId = Int32.Parse(xmlNode.SelectSingleNode(EVENT_ID_XPATH).InnerText); 100 | eventLogRecord._message = xmlNode.SelectSingleNode(EVENT_MESSAGE_XPATH).InnerText; 101 | eventLogRecord._generatedTime = parseDateTime(xmlNode.SelectSingleNode(GENERATED_TIME_XPATH).InnerText); 102 | return eventLogRecord; 103 | } 104 | 105 | private static DateTime parseDateTime(string dateTimeString) 106 | { 107 | Match match = _dateTimeMatcher.Match(dateTimeString); 108 | int year = Int32.Parse(match.Groups["year"].Value); 109 | int month = Int32.Parse(match.Groups["month"].Value); 110 | int day = Int32.Parse(match.Groups["day"].Value); 111 | int hour = Int32.Parse(match.Groups["hour"].Value); 112 | int minute = Int32.Parse(match.Groups["minute"].Value); 113 | int second = Int32.Parse(match.Groups["second"].Value); 114 | int millisecond = Int32.Parse(match.Groups["millisecond"].Value); 115 | 116 | return new DateTime(year, month, day, hour, minute, second, millisecond); 117 | } 118 | 119 | private static EventLogEntryType getEventLogEntryType(string entryAsString) 120 | { 121 | //HACK: change logparser output instead? 122 | if (entryAsString.ToLower().Contains("audit")) 123 | { 124 | if (entryAsString.ToLower().Contains("success")) return EventLogEntryType.SuccessAudit; 125 | if (entryAsString.ToLower().Contains("failure")) return EventLogEntryType.FailureAudit; 126 | } 127 | return (EventLogEntryType) Enum.Parse(typeof (EventLogEntryType), entryAsString, true); 128 | } 129 | } 130 | } -------------------------------------------------------------------------------- /EventLog/BinaryEventLogRecord.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using System.IO; 5 | using System.Text; 6 | 7 | namespace pal.EventLog 8 | { 9 | public class BinaryEventLogRecord : IEventLogRecord 10 | { 11 | protected string _computerName; 12 | protected IEnumerable _containingFile; 13 | protected byte[] _data; 14 | protected string _message; 15 | protected Metadata _metadata; 16 | protected string _sourceName; 17 | protected byte[] _userSid; 18 | 19 | public EventLogEntryType Type 20 | { 21 | get { return (EventLogEntryType) Enum.Parse(typeof (EventLogEntryType), _metadata.EventType.ToString()); } 22 | } 23 | 24 | public DateTime GeneratedTime 25 | { 26 | get { return new DateTime(1970, 1, 1, 0, 0, 0, 0).AddSeconds(_metadata.TimeGenerated); } 27 | } 28 | 29 | public string Source 30 | { 31 | get { return _sourceName; } 32 | } 33 | 34 | public ushort Category 35 | { 36 | get { return _metadata.EventCategory; } 37 | } 38 | 39 | public int EventId 40 | { 41 | get { return (int) _metadata.EventID; } 42 | } 43 | 44 | public string User 45 | { 46 | get { return _userSid.Length > 0 ? userSidHexString() : "N/A"; } 47 | } 48 | 49 | public string Computer 50 | { 51 | get { return _computerName; } 52 | } 53 | 54 | public IEnumerable ContainingFile 55 | { 56 | get { return _containingFile; } 57 | set { _containingFile = value; } 58 | } 59 | 60 | public string Message 61 | { 62 | get { return _message; } 63 | } 64 | 65 | public static IEventLogRecord NULL 66 | { 67 | get { return new BinaryEventLogRecord(); } 68 | } 69 | 70 | public static BinaryEventLogRecord Fetch(BinaryReader reader) 71 | { 72 | uint recordLength = reader.ReadUInt32(); 73 | reader.BaseStream.Seek(-4, SeekOrigin.Current); 74 | byte[] recordBytes = reader.ReadBytes((int) recordLength); 75 | int nextStartIndexInBuffer = 0; 76 | 77 | BinaryEventLogRecord eventLog = new BinaryEventLogRecord(); 78 | eventLog._metadata = Metadata.Fetch(recordBytes); 79 | nextStartIndexInBuffer += Globals.MetadataSize; 80 | eventLog._sourceName = extractString(recordBytes, nextStartIndexInBuffer); 81 | nextStartIndexInBuffer += eventLog._sourceName.Length*Globals.UnicodeCharSize + Globals.NullCharSize; 82 | eventLog._computerName = extractString(recordBytes, nextStartIndexInBuffer); 83 | nextStartIndexInBuffer += eventLog._computerName.Length*Globals.UnicodeCharSize + Globals.NullCharSize; 84 | eventLog._userSid = new byte[eventLog._metadata.UserSidLength]; 85 | Array.Copy(recordBytes, nextStartIndexInBuffer, eventLog._userSid, 0, eventLog._metadata.UserSidLength); 86 | nextStartIndexInBuffer += (int) eventLog._metadata.UserSidLength; 87 | eventLog._message = 88 | Encoding.Unicode.GetString(recordBytes, nextStartIndexInBuffer, 89 | (int) (eventLog._metadata.DataOffset - eventLog._metadata.StringOffset)); 90 | eventLog._data = new byte[eventLog._metadata.DataLength]; 91 | Array.Copy(recordBytes, eventLog._data, eventLog._metadata.DataLength); 92 | return eventLog; 93 | } 94 | 95 | public static bool CanHaveALogRecord(BinaryReader reader) 96 | { 97 | return !endOfRecords(reader); 98 | } 99 | 100 | public bool Matches(LogRecordSearchCriteria searchCriteria) 101 | { 102 | if (_sourceName.IndexOf(searchCriteria.Source) != -1 103 | || _message.IndexOf(searchCriteria.Message) != -1) 104 | return true; 105 | return false; 106 | } 107 | 108 | private string userSidHexString() 109 | { 110 | StringBuilder sb = new StringBuilder(); 111 | foreach (byte @byte in _userSid) 112 | { 113 | sb.AppendFormat("{0:X}", @byte); 114 | } 115 | return "0X" + sb; 116 | } 117 | 118 | //TODO: Still to add: ProperUser 119 | 120 | public override string ToString() 121 | { 122 | return 123 | string.Format("{0} ~~ {1} ~~ {2} ~~ {3} ~~ {4} ~~ {5} ~~ {6}", Type, Source, Category, EventId, Computer, 124 | User, Message); 125 | } 126 | 127 | private static string extractString(byte[] recordBytes, int startIndex) 128 | { 129 | StringBuilder extractedString = new StringBuilder(); 130 | for (int i = startIndex; i < recordBytes.Length; i += 2) 131 | { 132 | char character = Convert.ToChar(Encoding.Unicode.GetString(recordBytes, i, 2)); 133 | if (character == '\0') 134 | break; 135 | else 136 | extractedString.Append(character); 137 | } 138 | return extractedString.ToString(); 139 | } 140 | 141 | private static bool endOfRecords(BinaryReader reader) 142 | { 143 | return (reader.BaseStream.Length - reader.BaseStream.Position) <= Globals.FooterSize; 144 | } 145 | } 146 | } -------------------------------------------------------------------------------- /EventLog/CsvEventLogRecord.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using System.Text; 5 | using System.Text.RegularExpressions; 6 | 7 | namespace pal.EventLog 8 | { 9 | public class CsvEventLogRecord : IEventLogRecord 10 | { 11 | private const int COMPUTER_NAME = 0; 12 | 13 | private const string DATETIME_MATCHER = 14 | @"(?\d{4})(?\d{2})(?\d{2})(?\d{2})(?\d{2})(?\d{2})\.(?\d*)"; 15 | 16 | private const int EVENT_ID = 7; 17 | private const int EVENT_LOG_TYPE = 3; 18 | private const int GENERATED_TIME = 10; 19 | private const int MESSAGE = 8; 20 | private const int SOURCE = 6; 21 | private const int USER = 4; 22 | private static readonly Regex _dateTimeMatcher = new Regex(DATETIME_MATCHER, RegexOptions.Compiled); 23 | private ushort _category; 24 | private string _computer; 25 | private IEnumerable _containingFile; 26 | private int _eventId; 27 | private DateTime _generatedTime; 28 | private string _message; 29 | private string _source; 30 | private EventLogEntryType _type; 31 | private string _user; 32 | 33 | #region IEventLogRecord Members 34 | 35 | public EventLogEntryType Type 36 | { 37 | get { return _type; } 38 | } 39 | 40 | public DateTime GeneratedTime 41 | { 42 | get { return _generatedTime; } 43 | } 44 | 45 | public string Source 46 | { 47 | get { return _source; } 48 | } 49 | 50 | public ushort Category 51 | { 52 | get { return _category; } 53 | } 54 | 55 | public int EventId 56 | { 57 | get { return _eventId; } 58 | } 59 | 60 | public string User 61 | { 62 | get { return _user; } 63 | } 64 | 65 | public string Computer 66 | { 67 | get { return _computer; } 68 | } 69 | 70 | public string Message 71 | { 72 | get { return _message; } 73 | } 74 | 75 | public IEnumerable ContainingFile 76 | { 77 | get { return _containingFile; } 78 | set { _containingFile = value; } 79 | } 80 | 81 | public bool Matches(LogRecordSearchCriteria searchCriteria) 82 | { 83 | if (_source.IndexOf(searchCriteria.Source) != -1 84 | || _message.IndexOf(searchCriteria.Message) != -1) 85 | return true; 86 | return false; 87 | } 88 | 89 | #endregion 90 | 91 | public static CsvEventLogRecord Fetch(string csvLine) 92 | { 93 | List fields = new List(); 94 | string[] rawFields = csvLine.Split(','); 95 | for (int i = 0; i < rawFields.Length; i++) 96 | { 97 | StringBuilder fieldContent = new StringBuilder(rawFields[i]); 98 | 99 | if (rawFields[i].StartsWith("\"")) 100 | { 101 | if (!rawFields[i].EndsWith("\"")) 102 | { 103 | do 104 | { 105 | i++; 106 | fieldContent.Append(rawFields[i]); 107 | } while (!rawFields[i].Trim().EndsWith("\"")); 108 | } 109 | } 110 | 111 | fields.Add(fieldContent.ToString()); 112 | } 113 | 114 | for (int i = 0; i < fields.Count; i++) 115 | { 116 | fields[i] = fields[i].Replace("", "\n"); 117 | } 118 | 119 | //ComputerName, [Logfile], [CategoryString], Type, User, [EventCode], SourceName, EventIdentifier, Message, [RecordNumber], TimeGenerated 120 | CsvEventLogRecord eventLogRecord = new CsvEventLogRecord(); 121 | eventLogRecord._computer = fields[COMPUTER_NAME]; 122 | eventLogRecord._type = 123 | getEventLogEntryType(fields[EVENT_LOG_TYPE]); 124 | eventLogRecord._user = fields[USER]; 125 | eventLogRecord._source = fields[SOURCE]; 126 | eventLogRecord._eventId = Int32.Parse(fields[EVENT_ID]); 127 | eventLogRecord._message = fields[MESSAGE]; 128 | eventLogRecord._generatedTime = parseDateTime(fields[GENERATED_TIME]); 129 | return eventLogRecord; 130 | } 131 | 132 | private static DateTime parseDateTime(string dateTimeString) 133 | { 134 | Match match = _dateTimeMatcher.Match(dateTimeString); 135 | int year = Int32.Parse(match.Groups["year"].Value); 136 | int month = Int32.Parse(match.Groups["month"].Value); 137 | int day = Int32.Parse(match.Groups["day"].Value); 138 | int hour = Int32.Parse(match.Groups["hour"].Value); 139 | int minute = Int32.Parse(match.Groups["minute"].Value); 140 | int second = Int32.Parse(match.Groups["second"].Value); 141 | int millisecond = Int32.Parse(match.Groups["millisecond"].Value); 142 | 143 | return new DateTime(year, month, day, hour, minute, second, millisecond); 144 | } 145 | 146 | private static EventLogEntryType getEventLogEntryType(string entryAsString) 147 | { 148 | //HACK: change logparser output instead? 149 | if (entryAsString.ToLower().Contains("audit")) 150 | { 151 | if (entryAsString.ToLower().Contains("success")) return EventLogEntryType.SuccessAudit; 152 | if (entryAsString.ToLower().Contains("failure")) return EventLogEntryType.FailureAudit; 153 | } 154 | return (EventLogEntryType) Enum.Parse(typeof (EventLogEntryType), entryAsString, true); 155 | } 156 | } 157 | } -------------------------------------------------------------------------------- /Console Client/Properties/Resources.resx: -------------------------------------------------------------------------------- 1 | 2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | 121 | Available options: 122 | Pre-filters:: 123 | Time: -after -before -at 124 | Type: -type :: Error|FailureAudit|Information|SuccessAudit|Warning 125 | Message: -startsWith -contains 126 | Computer: -computer 127 | Source: -source 128 | EventId: -eventId 129 | 130 | Processing options: 131 | -tolerance :: Tolerance threshold for determining similarity of two Event log messages. 132 | Defaults to 0. i.e. Maximum accuracy => Least fuzzy => More groups 133 | 134 | Output options: 135 | Fields in output reports: -include (FieldNames|All) :: FieldNames:= Type|DateTime|Source|Category|EventId|User|Computer|Message 136 | Output Folder: -output :: Folder must exist 137 | Help text to be displayed 138 | 139 | --------------------------------------------------------------------------------