Wednesday 2 September 2015

EventLogDisplayer

EventLogDisplayer

EventLogDisplayer is a general-purpose tool to harvest and display contents from the Event Log in a simple dedicated web application implemented in ASP.NET MVC. To make it work, one must enable Remote Event Log on the target server, set up a powershell script as a scheduled task and then create a database to commit the Event Log items. Also make sure that the directory configured to write the scratch XML files to, already exists. The harvest script will harvest last 24 hours from the Event Log and write new items to the database. This can easily be adjusted. The script is usually set up to run once an hour, so retrieving the Event Log items can of course be reduced down to an hour. Regarding how often the Event Log is harvested, this must correspond to the intervals of the scheduled tasks that executes the script, so that all Event Logs items are retrieved. Only Event Log items of type Warning and Error/Exception is retrieved (Information event log type is skipped).

Harvesting the Event Log

Powershell script
Write-Host Starting the harvesting from EventLog ... 
#Setup the parameters of the script to harvest the eventlog here  
$username = "myusername"
$password = "mypassword"
$targetServer = "myserver.somedomain.no"
$logName = "MyLogName" 
$datestamp = Get-Date -Format ddMMyyyy
$outputFile = "C:\temp\EventLogs\EventsLogFile_" + $dateStamp + ".xml"
$daysBack = 1  
$secstr = New-Object -TypeName System.Security.SecureString
$password.ToCharArray() | ForEach-Object {$secstr.AppendChar($_)}
$cred = new-object -typename System.Management.Automation.PSCredential -argumentlist $username, $secstr
 
$yesterday = (Get-Date) - (New-TimeSpan -Day $daysBack)
 
#Write-Host $yesterday
 
$sb = New-Object -TypeName "System.Text.StringBuilder" 
$sb.AppendLine("<?xml version='1.0' ?>")
$sb.Append("<Events xml='http://schemas.microsoft.com/win/2004/08/events/event'>")
Get-WinEvent -ComputerName $targetServer -Credential $cred -LogName $logName | Where-Object { $_.TimeCreated -ge $yesterday -and $_.Level -ge 2 } | ForEach-Object {
 $eventXml =  $_.ToXml()
 $sb.AppendLine($eventXml) 
} 
$sb.AppendLine("</Events>")
$sb.ToString() | Out-File $outputFile
#Invoke-Item $outputFile

Link to EventLogDisplayer


Sample web solution
(Link is not active) This web site targets the server MYSERVER, Event Log name is set to MyLogName.

Screenshots of Event Log Displayer


It is easy to monitor another server, but note that the Remote Event Log feature must be added to the server.

Scheduling task to harvest the Event Log remotely
Sample task from Task Scheduler
The following task will set up a hourly schedule, harvesting event log from the remote computer.


<?xml version="1.0" encoding="UTF-16"?>
<Task version="1.2" xmlns="http://schemas.microsoft.com/windows/2004/02/mit/task">
  <RegistrationInfo>
    <Date>2015-08-28T20:02:29.8065626</Date>
    <Author>somedomain\someuser-he</Author>
  </RegistrationInfo>
  <Triggers>
    <CalendarTrigger>
      <Repetition>
        <Interval>PT1H</Interval>
        <StopAtDurationEnd>false</StopAtDurationEnd>
      </Repetition>
      <StartBoundary>2015-08-28T00:00:00</StartBoundary>
      <Enabled>true</Enabled>
      <ScheduleByDay>
        <DaysInterval>1</DaysInterval>
      </ScheduleByDay>
    </CalendarTrigger>
  </Triggers>
  <Principals>
    <Principal id="Author">
      <UserId>somedomain\someuser</UserId>
      <LogonType>InteractiveToken</LogonType>
      <RunLevel>LeastPrivilege</RunLevel>
    </Principal>
  </Principals>
  <Settings>
    <MultipleInstancesPolicy>IgnoreNew</MultipleInstancesPolicy>
    <DisallowStartIfOnBatteries>true</DisallowStartIfOnBatteries>
    <StopIfGoingOnBatteries>true</StopIfGoingOnBatteries>
    <AllowHardTerminate>true</AllowHardTerminate>
    <StartWhenAvailable>false</StartWhenAvailable>
    <RunOnlyIfNetworkAvailable>false</RunOnlyIfNetworkAvailable>
    <IdleSettings>
      <StopOnIdleEnd>true</StopOnIdleEnd>
      <RestartOnIdle>false</RestartOnIdle>
    </IdleSettings>
    <AllowStartOnDemand>true</AllowStartOnDemand>
    <Enabled>true</Enabled>
    <Hidden>false</Hidden>
    <RunOnlyIfIdle>false</RunOnlyIfIdle>
    <WakeToRun>false</WakeToRun>
    <ExecutionTimeLimit>P3D</ExecutionTimeLimit>
    <Priority>7</Priority>
  </Settings>
  <Actions Context="Author">
    <Exec>
      <Command>Powershell</Command>
      <Arguments>C:\Users\toaurs-he\Documents\Powershell\HarvestEventLog.ps1</Arguments>
    </Exec>
  </Actions>
</Task>



The task above defined in the XML can be saved to an XML file, adjusted as necessary and imported in Task Scheduler: The task can also be adjusted using the command line (as Administrator) with the command:

schtasks.exe /Create /XML task.xml /tn taskname

Enabling Remote Event Log feature on target server Remote Event Log Management is enabled in the Windows Firewall with Advanced Security as an Inbound Rule, predefined as Remote Event Log Management.

Tick off all the three choices here:



SQL Script

The following script creates the database required to persist data to the database.


USE [OpPlan4EventLog] GO /****** Object: Table [dbo].[Events] Script Date: 02.09.2015 20:03:14 ******/ SET ANSI_NULLS ON GO SET QUOTED_IDENTIFIER ON GO CREATE TABLE [dbo].[Events]( [Id] [INT] NOT NULL, [Message] [nvarchar](MAX) NULL, [TimeCreated] [datetime] NULL, [Level] [INT] NULL, [Channel] [nvarchar](300) NULL, [Computer] [nvarchar](300) NULL, CONSTRAINT [PK_Events] PRIMARY KEY CLUSTERED ( [Id] ASC)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY] GO

Sample MVC web solution


The EventLogDisplayer is a MVC web solution and is available here. Available on OneDrive here:

Sample MVC web solution [40,15 MB | Zip-file | Visual Studio 2013 Solution ]

Thursday 27 August 2015

EventLogParserUtility - Parsing Event Log Files and exporting to Excel

Parsing Event Log Files

Filtering and searching an event log using the Event Log Viewer (eventvwr) is often unpractical and it is quicker to save the selected content of the Event Log to a Event Log File of the format .evtx. This is done using the following classes in System.Diagnostics.Eventing.Reader:
  • EventLogReader
  • EventLogQuery
  • EventLogRecord
The following code is a console line application written in C# generating excel files with filtered contents of the event log file.

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.Eventing.Reader;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using OfficeOpenXml;
using OfficeOpenXml.Style;

namespace EventLogParserUtility
{
    class Program
    {

        private static void Main(string[] args)
        {

            bool outputToExcel = false;
            string eventLogFileName = null;
            string excelFileName = null;

            Console.WriteLine("Starting analysis of target Event Log file: ");

            Timer timer = new Timer(TimerTick, null, 0, 100);


            if (args.Any(a => a.StartsWith(@"-f:")))
            {
                eventLogFileName = args.First(a => a.StartsWith(@"-f:")).Split(':')[1];
            }

            if (string.IsNullOrEmpty(eventLogFileName))
            {
                ShowUsageInfo();
                return;
            }

            var events = from l in LogRecordCollection(eventLogFileName)
                         where l.Properties.Any()
                               && l.Properties[0].Value != null
                         select l;

            if (args.Any(a => a.StartsWith(@"-t:")))
            {
                string timeArgument = args.First(a => a.StartsWith(@"-t:")).Split(':')[1];
                DateTime fromTime;
                if (DateTime.TryParse(timeArgument, out fromTime))
                {
                    events = events.Where(e => e.TimeCreated >= fromTime);

                } //if 
            } //if 

            if (args.Any(a => a.StartsWith(@"-m:")))
            {
                string messageArgument = args.First(a => a.StartsWith(@"-m:")).Split(':')[1].Replace("'", "");
                events =
                    events.Where(
                        e => Regex.IsMatch(e.Properties[0].Value.ToString(), messageArgument, RegexOptions.IgnoreCase));
            }

            if (args.Any(a => a.StartsWith(@"-excel:")))
            {
                excelFileName = DateTime.Now.ToString("ddmmyyyyhhmmss") + args.First(a => a.StartsWith(@"-excel:")).Split(':')[1].Replace("'", "");
                outputToExcel = true;
            }


            if (!outputToExcel)
            {
                foreach (var e in DistinctBy(events, e => e.RecordId).OrderByDescending(e => e.TimeCreated))
                {
                    Console.WriteLine(Environment.NewLine + e.TimeCreated + Environment.NewLine +
                                      GetFilteredValue(e.Properties[0].Value, args));
                    Console.WriteLine("Hit enter to go to NEXT.");
                    Console.ReadKey();
                }
            }
            else
            {
                using (var excelPackage = new ExcelPackage(new FileInfo(Path.Combine(Directory.GetCurrentDirectory(), excelFileName))))
                {
                    excelPackage.Workbook.Worksheets.Add("Eventlog matches:" + DateTime.Now.ToShortDateString());

                    var workSheet = excelPackage.Workbook.Worksheets[1];

                    int rowIndex = 2;

                    workSheet.Cells[1, 1].Value = "Level";
                    workSheet.Cells[1, 2].Value = "Date and Time";
                    workSheet.Cells[1, 3].Value = "Source";
                    workSheet.Cells[1, 4].Value = "Details";
                    workSheet.Cells[1, 5].Value = "Computer Name";
                    workSheet.Cells[1, 6].Value = "Filtered Details";

                    workSheet.Cells[1, 1, 1, 6].Style.Font.Bold = true;
                    workSheet.Cells[1, 1, 1, 6].Style.Font.Size = 14;




                    foreach (var e in DistinctBy(events, e => e.RecordId).OrderByDescending(e => e.TimeCreated))
                    {
                        workSheet.Cells[rowIndex, 1].Value = e.Level;
                        workSheet.Cells[rowIndex, 2].Value = e.TimeCreated;
                        workSheet.Cells[rowIndex, 2].Style.Numberformat.Format = "dd.mm.yyyy hh:mm";
                        workSheet.Cells[rowIndex, 3].Value = e.ProviderName;
                        workSheet.Cells[rowIndex, 4].Value = e.Properties[0].Value;
                        workSheet.Cells[rowIndex, 5].Value = e.MachineName;
                        workSheet.Cells[rowIndex, 6].Value = GetFilteredValue(e.Properties[0].Value, args);
                        workSheet.Cells[rowIndex, 1, rowIndex, 5].Style.Fill.PatternType = ExcelFillStyle.Solid;
                        workSheet.Cells[rowIndex, 1, rowIndex, 5].Style.Fill.BackgroundColor.SetColor(rowIndex % 2 == 0
                            ? Color.AliceBlue
                            : Color.White);
                        rowIndex++;
                    }



                    workSheet.Cells[workSheet.Dimension.Address].AutoFitColumns();

                    excelPackage.Save();

                }



                Process.Start(Path.Combine(Directory.GetCurrentDirectory(), excelFileName));

            }

            timer.Dispose();

            Console.WriteLine("All done. Press the any key to continue ..");
            Console.ReadKey();


        }

        private static string GetFilteredValue(object value, string[] args)
        {
            if (args.Any(a => a.StartsWith("-o:")))
            {
                var pattern = string.Join(":", args.First(a => a.StartsWith("-o:")).Split(':').Skip(1)).Replace("&lt", "<")
                    .Replace("&gt;", ">").Replace("'", "").Trim();
                Regex filterMatch =
                    new Regex(pattern, RegexOptions.IgnoreCase);
                Match mc = filterMatch.Match(value.ToString());

                StringBuilder sb = new StringBuilder();

                foreach (Group group in mc.Groups)
                {
                    sb.Append(group.Value + " ");
                }

                return sb.ToString();
            }
            return value.ToString();
        }

        private static void TimerTick(object state)
        {
            Console.Write(".");
        }

        private static void ShowUsageInfo()
        {
            Console.WriteLine("Example Usage: EventLogParserUtility -f:MyEventLogFile.evtx "
                + Environment.NewLine + "Additional parameters: -t:1.1.2015 [TimeCreated larger than] "
                + Environment.NewLine + "-m:MySearchKey [Properties[0].Value or Message contains] "
                + Environment.NewLine + "-excel:SomeFileName.xlsx [Outputting to Excel file]"
                + Environment.NewLine + "-o:MyFilter [Filter output by regex]");
        }

        static IEnumerable<EventLogRecord> LogRecordCollection(string filename, string xpathquery = "*")
        {
            var eventLogQuery = new EventLogQuery(filename, PathType.FilePath, xpathquery);

            using (var eventLogReader = new EventLogReader(eventLogQuery))
            {
                EventLogRecord eventLogRecord;

                while ((eventLogRecord = (EventLogRecord)eventLogReader.ReadEvent()) != null)
                {
                    yield return eventLogRecord;
                }
            }
        }

        static IEnumerable<T> DistinctBy<T, TKey>(IEnumerable<T> inputList, Func<T, TKey> keySelector, IEqualityComparer<TKey> comparer = null)
        {
            var distinctItems = inputList.GroupBy(keySelector, comparer).Select(g => g.First()).ToList();
            return distinctItems;
        }

    }
}


The command line application is able to output content of the event log file that matches a given search term key and also output a filtered column specified by a Regex.


cd EventLogParserUtility\bin\Debug EventLogParserUtility -f:EventLogs\hendelseslogg.evtx -m:'OfficialId' -excel:MyOutputExcelFile.xlsx -o:'<OfficialId>(?<x>.*)</OfficialId>' Supported switches in EventLogParserUtility:

-f: File name of event log file (obligatory column) -m: Search messages in event log specified by search term. It is possible to type in a regex here (optional parameter) -excel: filename to output to excel (optional parameter) -o: Regular expression to use to filter the message additionally for targeted output (will be displayed in filtered column) -t: Filtering to output content where TimeCreated of Event Log Item above specified date (optional parameter, specify as datetime value To use this utilty, put the arguments of the switches inside quotes if the arguments got spaces.

Monday 20 July 2015

Calculating PI in C# using Monte-Carlo simulation

The following code sample shows numeric compuation of the number PI using Monte-Carlo simulation. First, a sequential approach is used. Then the Parallel.For construct in TPL is used. In the end, we use Tasks in TPL.


To compute PI we use the same approach. We consider the unit circle inscribed in a square around origo with corners at coordinates (-1,-1), (-1, 1), (1,1) and (1,-1). The number PI can be defined as generating random numbers and looking at the ratio of the numbers inside the circle M divided upon the total numbers generated N. We know that the following can then be expected:

(a) M / N = PI / 4

Why? Because the square has got an area equal to four, remember that the unit square got sides equal to the number 2 and its area is therefore 2 * 2 = 4. The unit circle got a radius of 1, hence its area is PI * 1^2 = PI. The ratio to be expected between the areas of the unit circle and unit rectangle therefore gives the formula above. We can further compute the approximated numeric value of PI equal to:

(b) PI = 4 * (M / N)

This expression (b) is directly from the previous expression (a)
Let's move on the code sample, review the code. I have included a screen shot at the end. The conclusion I got after testing showed after several runs shows that the sequential version runs in about 3.5 seconds on my eight core system with about half the time, about 1.8 seconds using Parallel.For - The last version using Tasks and Tasks.WaitAll give about 1.7 seconds and the quickest compuation, about twice as fast. The iterations I used in the demo was 80 million.
Here is the code written in C#:

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace MonteCarloPiApproximation
{
    class Program
    {
        static int numberOfCores = Environment.ProcessorCount; 
        static int iterations = 10000000 * numberOfCores;

        static void Main(string[] args)
        {
            Console.WriteLine("Monte Carlo numeric simulation of PI");
            Console.WriteLine("Iteration limit: " + iterations);
            Console.WriteLine("Number of processor cores on system: " + Environment.ProcessorCount);
            var sw = new Stopwatch();
            sw.Start();

            Console.WriteLine("\nMONTE CARLO SIMULATION");

            MonteCarloPiApproximationSerialSimulation();
            sw.Stop();

            Console.WriteLine("Serial simulation: (ms)" + sw.ElapsedMilliseconds);
            Console.WriteLine();

            sw.Restart();

            Console.WriteLine("\nMONTE CARLO SIMULATION");
            MonteCarloPiApproximationParallellForSimulation(); 

            sw.Stop();

            Console.WriteLine("Parallell simulation using Parallel.For: (ms)" + sw.ElapsedMilliseconds);
            Console.WriteLine();

            sw.Restart();

            Console.WriteLine("\nMONTE CARLO SIMULATION");

            MonteCarloPiApproximationParallelTasksSimulation(); 

            Console.WriteLine("Parallell simulation using parallell Tasks: (ms)" + sw.ElapsedMilliseconds);
            Console.WriteLine();

            sw.Stop();           

            Console.WriteLine("Press Enter Key");



            Console.ReadKey(); 
        }

        private static void MonteCarloPiApproximationParallelTasksSimulation()
        {
            double piApproximation = 0;
            int inCircle = 0;
            double x, y = 0;

            int[] localCounters = new int[numberOfCores];
            Task[] tasks = new Task[numberOfCores];

            for (int i = 0; i < numberOfCores; i++)
            {
                int procIndex = i; //closure capture 
                tasks[procIndex] = Task.Factory.StartNew(() =>
                {
                    int localCounterInside = 0;

                    Random rnd = new Random();

                    for (int j = 0; j < iterations / numberOfCores; j++)
                    {
                        x = rnd.NextDouble();
                        y = rnd.NextDouble();
                        if (Math.Sqrt(x * x + y * y) <= 1.0)
                            localCounterInside++;
                    } 
                    localCounters[procIndex] = localCounterInside;

                });               
            }

            Task.WaitAll(tasks);
            inCircle = localCounters.Sum(); 

            piApproximation = 4 * ((double)inCircle / (double)iterations);

            Console.WriteLine();
            Console.WriteLine("Approximated Pi = {0}", piApproximation.ToString("F8"));
           
        }      

        private static void MonteCarloPiApproximationParallellForSimulation()
        {
            double piApproximation = 0;
            int inCircle = 0;
            double x, y = 0;
                   
            Parallel.For(0, numberOfCores, new ParallelOptions{ MaxDegreeOfParallelism = numberOfCores }, i =>
            {
              
                int localCounterInside = 0;

                Random rnd = new Random(); 

                for (int j = 0; j < iterations / numberOfCores; j++)
                {
                    x = rnd.NextDouble();
                    y = rnd.NextDouble();
                    if (Math.Sqrt(x*x+y*y) <= 1.0)
                        localCounterInside++;                                                        
                }

                Interlocked.Add(ref inCircle, localCounterInside); 
                            
            }); 

            piApproximation = 4 * ((double)inCircle / (double)iterations);

            Console.WriteLine();
            Console.WriteLine("Approximated Pi = {0}", piApproximation.ToString("F8"));
            
        }

        private static void MonteCarloPiApproximationSerialSimulation()
        {
            double piApproximation = 0;
            int total = 0;
            int inCircle = 0; 
            double x,y = 0;
            Random rnd = new Random(); 

            while (total < iterations)
            {
                x = rnd.NextDouble(); 
                y = rnd.NextDouble();

                if ((Math.Sqrt(x*x+y*y) <= 1.0))
                    inCircle++;

                total++;                
                piApproximation =  4 * ((double)inCircle / (double)total); 
            } //while 


            Console.WriteLine();
            Console.WriteLine("Approximated Pi = {0}", piApproximation.ToString("F8"));

        }




    }
}