예제 #1
0
        static int Main(string[] args)
        {
            #region Command Line argument processing
            if (args.Contains("--help"))
            {
                Console.WriteLine("This tool depends on Microsoft Office 2010+");
                Console.WriteLine("Valid switches are");
                Console.WriteLine("-ignore <comma separated list of file extn>    : Ignore line with pattern");
                Console.WriteLine("-include <comma separated list of file extn>   : Filter for pattern");
                Console.WriteLine("-concurrency <No of minutes>                   : Concurrency Window in minutes");
                Console.WriteLine("-toppages <No of pages>                        : No of Top Pages/day");
                Console.WriteLine("-peaks <No of peaks>                           : No of Peak Hours to consider");
                Console.WriteLine("-param <comma seperated list of patterns>      : Summarize specific URL parameters");
                Console.WriteLine("-export <export filename>                      : Excel file report name, default will be with time stamp");
                Console.WriteLine("-folder <log file folder path>                 : Current folder will be defaulted. All .log files in this folder will be processed.");
                Console.WriteLine("Add a space after the pattern if you want extension mapping (e.g. .aspx ,.jpg)");
                return(0);
            }

            if (args.Length % 2 != 0)
            {
                throw new ArgumentException("Command line arguments not valid, try --help to see valid ones!");
            }

            Dictionary <string, string> cmdArgs = new Dictionary <string, string> ();
            for (int i = 0; i < args.Length; i += 2)
            {
                cmdArgs.Add(args[i].ToLower(), args[i + 1]);
            }


            List <string> ignoredTypes = new List <string> (), filterTypes = new List <string> (), hitsPerURLParams = new List <string> ();
            if (cmdArgs.ContainsKey(IgnoreSwitch))
            {
                ignoredTypes = cmdArgs[IgnoreSwitch].ToLower().Split(',').ToList();
            }

            if (cmdArgs.ContainsKey(FilterSwitch))
            {
                filterTypes = cmdArgs[FilterSwitch].ToLower().Split(',').ToList();
            }

            if (cmdArgs.ContainsKey(URLParamsSwitch))
            {
                hitsPerURLParams = cmdArgs[URLParamsSwitch].ToLower().Split(',').ToList();
            }


            float concurrencyWindow = 5;
            if (cmdArgs.ContainsKey(ConcurrencySwitch))
            {
                concurrencyWindow = float.Parse(cmdArgs[ConcurrencySwitch]);
            }
            else
            {
                cmdArgs.Add(ConcurrencySwitch, concurrencyWindow.ToString());
            }

            int topPagesPerDay = 10;
            if (cmdArgs.ContainsKey(TopPagesSwitch))
            {
                topPagesPerDay = int.Parse(cmdArgs[TopPagesSwitch]);
            }
            else
            {
                cmdArgs.Add(TopPagesSwitch, topPagesPerDay.ToString());
            }

            int peakHoursCount = 3;
            if (cmdArgs.ContainsKey(PeakHoursSwitch))
            {
                peakHoursCount = int.Parse(cmdArgs[PeakHoursSwitch]);
            }
            else
            {
                cmdArgs.Add(PeakHoursSwitch, peakHoursCount.ToString());
            }


            string exportFileName = null;
            if (cmdArgs.ContainsKey(ExportFileSwitch))
            {
                try
                {
                    exportFileName = Path.GetFullPath(cmdArgs[ExportFileSwitch]);
                }
                catch (Exception e)
                {
                    Console.WriteLine("Error creating report file:{0},{1}", e.GetType().Name, e.Message);
                }
            }
            if (exportFileName == null)
            {
                exportFileName = Path.GetFullPath("Processing results_" + DateTime.Now.ToString("dd_hh_mm") + ".xlsx");
                Console.WriteLine("Writing output to {0}", exportFileName);
            }

            string curerntPath;
            if (cmdArgs.ContainsKey(FolderSwitch))
            {
                try
                {
                    curerntPath = Path.GetFullPath(cmdArgs[FolderSwitch]);
                }
                catch (Exception e)
                {
                    Console.WriteLine("Error accessing folder {0}:{1},{2}", cmdArgs[FolderSwitch], e.GetType().Name, e.Message);
                    return(1);
                }
            }
            else
            {
                curerntPath = Directory.GetCurrentDirectory();
                Console.WriteLine("Working on IIS logs from current folder {0}", curerntPath);
            }
            #endregion

            Stopwatch stopWatch = new Stopwatch();
            stopWatch.Start();



            //var files = Directory.GetFiles(curerntPath, "*.log").ToList();
            var files = new DirectoryInfo(curerntPath)
                        .GetFiles("*.log")
                        .OrderBy(f => f.LastWriteTime)
                        .Select(f => f.FullName)
                        .ToArray();
            var totalFile = files.Count();

            if (totalFile == 0)
            {
                Console.WriteLine("No log files found!!");
                return(0);
            }

            Console.WriteLine("Found {0} log files", totalFile);

            var tmpFile    = System.IO.Path.GetTempFileName();
            int fileCount  = 0;
            int headerRows = 4;
            int entryCount = 0;



            List <IISLogEntry> processingList = new List <IISLogEntry> ();
            DateTime           nextTime       = DateTime.MinValue;

            long TotalHits = 0, ServedRequests = 0;
            List <ConcurrentRequest>        requests = new List <ConcurrentRequest> ();
            HashSet <string>                uniqueIPs = new HashSet <string> ();
            Dictionary <int, int>           httpStatus = new Dictionary <int, int> ();
            Dictionary <string, MethodInfo> pageViewsForPeriod = new Dictionary <string, MethodInfo> ();

            int totalDays = 0, totalHours = 0;

            Dictionary <string, MethodInfo> pageViewsDaily = new Dictionary <string, MethodInfo> ();
            HashSet <MethodInfo>            dailyPages = new HashSet <MethodInfo> ();

            Dictionary <string, MethodInfo> pageViewsHourly = new Dictionary <string, MethodInfo> ();
            HashSet <MethodInfo>            hourlyPages = new HashSet <MethodInfo> ();

            //hits for key URL parameters
            Dictionary <string, MethodInfo> urlParamHits = new Dictionary <string, MethodInfo> ();
            DateTime firstEntry = DateTime.MinValue, lastEntry = DateTime.MinValue;

            //placeholder
            HashSet <MethodInfo> filteredEntries = new HashSet <MethodInfo> ();
            int startRow = 1, startCol = 1;
            int reportRow = 2, reportCol = 1;


            Console.WriteLine("Preparing to Process..");


            foreach (var f in files)
            {
                try
                {
                    ++fileCount;
                    var progress = fileCount * 100 / totalFile;

                    IEnumerable <string> matchedEntries = null;

                    var contents = File.ReadLines(f);


                    Dictionary <string, int> fieldIndex = new Dictionary <string, int> ();

                    #region Content filter


                    if (filterTypes.Any() && ignoredTypes.Any())
                    {
                        matchedEntries = contents.Where(s => s.StartsWith("#") ||
                                                        (filterTypes.Any(x => s.ToLower().Contains(x)) &&
                                                         !ignoredTypes.Any(x => s.ToLower().Contains(x))));
                    }

                    else if (filterTypes.Any())
                    {
                        matchedEntries = contents.Where(s => s.StartsWith("#") || filterTypes.Any(x => s.ToLower().Contains(x)));
                    }

                    else if (ignoredTypes.Any())
                    {
                        matchedEntries = contents.Where(s => s.StartsWith("#") || !ignoredTypes.Any(x => s.ToLower().Contains(x)));
                    }
                    else
                    {
                        matchedEntries = contents;
                    }


                    foreach (var rawLogEntry in matchedEntries)
                    {
                        IISLogEntry logEntry;
                        if (rawLogEntry.StartsWith("#"))
                        {
                            if (rawLogEntry.StartsWith("#Fields:"))
                            {
                                fieldIndex = ParseHeaderFields(rawLogEntry);
                            }
                        }
                        else
                        {
                            Console.Write("\r{0} File {1} of {2} files ({3}%), processing {4}      ", stopWatch.Elapsed.ToString(@"hh\:mm\:ss"), fileCount, totalFile, progress, ++TotalHits);

                            var columns = rawLogEntry.Split(' ');
                            logEntry = new IISLogEntry()
                            {
                                TimeStamp       = DateTime.Parse(columns[0] + " " + columns[1]),
                                ClientIPAddress = fieldIndex.ContainsKey(IISLogEntry.propClientIPAddress) ? columns[fieldIndex[IISLogEntry.propClientIPAddress]] : String.Empty,
                                UserName        = fieldIndex.ContainsKey(IISLogEntry.propUserName) ? columns[fieldIndex[IISLogEntry.propUserName]] : String.Empty,
                                ServiceNameandInstanceNumber = fieldIndex.ContainsKey(IISLogEntry.propServiceNameandInstanceNumber) ? columns[fieldIndex[IISLogEntry.propServiceNameandInstanceNumber]] : String.Empty,
                                ServerName      = fieldIndex.ContainsKey(IISLogEntry.propServerName) ? columns[fieldIndex[IISLogEntry.propServerName]] : String.Empty,
                                ServerIPAddress = fieldIndex.ContainsKey(IISLogEntry.propServerIPAddress) ? columns[fieldIndex[IISLogEntry.propServerIPAddress]] : String.Empty,
                                ServerPort      = fieldIndex.ContainsKey(IISLogEntry.propClientIPAddress) ? Int32.Parse(columns[fieldIndex[IISLogEntry.propServerPort]]) : 0,
                                Method          = fieldIndex.ContainsKey(IISLogEntry.propMethod) ? columns[fieldIndex[IISLogEntry.propMethod]] : String.Empty,
                                URIStem         = fieldIndex.ContainsKey(IISLogEntry.propURIStem) ? columns[fieldIndex[IISLogEntry.propURIStem]] : String.Empty,
                                URIQuery        = fieldIndex.ContainsKey(IISLogEntry.propURIQuery) ? columns[fieldIndex[IISLogEntry.propURIQuery]] : String.Empty,
                                HTTPStatus      = fieldIndex.ContainsKey(IISLogEntry.propHTTPStatus) ? Int32.Parse(columns[fieldIndex[IISLogEntry.propHTTPStatus]]) : 0,
                                //Win32Status = fieldIndex.ContainsKey(IISLogEntry.propWin32Status) ? Int32.Parse(row[fieldIndex[IISLogEntry.propWin32Status]]) : 0,
                                BytesSent         = fieldIndex.ContainsKey(IISLogEntry.propBytesSent) ? Int32.Parse(columns[fieldIndex[IISLogEntry.propBytesSent]]) : 0,
                                BytesReceived     = fieldIndex.ContainsKey(IISLogEntry.propBytesReceived) ? Int32.Parse(columns[fieldIndex[IISLogEntry.propBytesReceived]]) : 0,
                                TimeTaken         = fieldIndex.ContainsKey(IISLogEntry.propTimeTaken) ? Int32.Parse(columns[fieldIndex[IISLogEntry.propTimeTaken]]) : 0,
                                ProtocolVersion   = fieldIndex.ContainsKey(IISLogEntry.propProtocolVersion) ? columns[fieldIndex[IISLogEntry.propProtocolVersion]] : String.Empty,
                                Host              = fieldIndex.ContainsKey(IISLogEntry.propHost) ? columns[fieldIndex[IISLogEntry.propHost]] : String.Empty,
                                UserAgent         = fieldIndex.ContainsKey(IISLogEntry.propUserAgent) ? columns[fieldIndex[IISLogEntry.propUserAgent]] : String.Empty,
                                Cookie            = fieldIndex.ContainsKey(IISLogEntry.propCookie) ? columns[fieldIndex[IISLogEntry.propCookie]] : String.Empty,
                                Referrer          = fieldIndex.ContainsKey(IISLogEntry.propReferrer) ? columns[fieldIndex[IISLogEntry.propReferrer]] : String.Empty,
                                ProtocolSubstatus = fieldIndex.ContainsKey(IISLogEntry.propProtocolSubstatus) ? columns[fieldIndex[IISLogEntry.propProtocolSubstatus]] : String.Empty
                            };

                            #endregion

                            #region entry processing

                            var url = logEntry.URIStem.ToLower();

                            #region HTTP status codes & IP
                            if (httpStatus.ContainsKey(logEntry.HTTPStatus))
                            {
                                httpStatus[logEntry.HTTPStatus]++;
                            }
                            else
                            {
                                httpStatus.Add(logEntry.HTTPStatus, 1);
                            }

                            if (!uniqueIPs.Contains(logEntry.ClientIPAddress))
                            {
                                uniqueIPs.Add(logEntry.ClientIPAddress);
                            }
                            #endregion

                            if (nextTime == DateTime.MinValue)
                            {
                                firstEntry = logEntry.TimeStamp;
                                lastEntry  = logEntry.TimeStamp;
                                nextTime   = logEntry.TimeStamp.Date.
                                             AddHours(logEntry.TimeStamp.Hour).
                                             AddMinutes(logEntry.TimeStamp.Minute).
                                             AddMinutes(concurrencyWindow);
                            }

                            if (logEntry.TimeStamp > nextTime)
                            {
                                if (processingList.Any())
                                {
                                    requests.Add(new ConcurrentRequest(concurrencyWindow)
                                    {
                                        TimeStamp           = nextTime,
                                        Transactions        = processingList.Count,
                                        AverageResponseTime = processingList.Average(p => p.TimeTaken),
                                        BytesSent           = processingList.Sum(t => t.BytesSent)
                                    });
                                    processingList.Clear();
                                }
                                else
                                {
                                    requests.Add(new ConcurrentRequest(concurrencyWindow)
                                    {
                                        TimeStamp           = nextTime,
                                        Transactions        = 0,
                                        AverageResponseTime = 0,
                                        BytesSent           = 0
                                    });
                                }
                                nextTime = nextTime.AddMinutes(concurrencyWindow);
                            }

                            if (lastEntry.Hour != logEntry.TimeStamp.Hour)
                            {
                                totalHours++;
                                AddHourlyPages(pageViewsHourly, hourlyPages, lastEntry);
                            }

                            if (lastEntry.Date != logEntry.TimeStamp.Date)
                            {
                                totalDays++;
                                AddDailyPages(pageViewsDaily, dailyPages, lastEntry);
                            }

                            //add the current one to future processing, otherwise one in teh borderlien will be missing
                            if (logEntry.HTTPStatus == 200)
                            {
                                processingList.Add(logEntry);
                                ServedRequests++;

                                if (pageViewsForPeriod.ContainsKey(url))
                                {
                                    pageViewsForPeriod[url].Hit(logEntry.TimeTaken);
                                }
                                else
                                {
                                    pageViewsForPeriod.Add(url, new MethodInfo(logEntry.URIStem, logEntry.TimeTaken));
                                }

                                if (lastEntry.Hour == logEntry.TimeStamp.Hour)
                                {
                                    if (pageViewsHourly.ContainsKey(url))
                                    {
                                        pageViewsHourly[url].Hit(logEntry.TimeTaken);
                                    }
                                    else
                                    {
                                        pageViewsHourly.Add(url, new MethodInfo(logEntry.URIStem, logEntry.TimeTaken));
                                    }
                                }

                                if (lastEntry.Date == logEntry.TimeStamp.Date)
                                {
                                    if (pageViewsDaily.ContainsKey(url))
                                    {
                                        pageViewsDaily[url].Hit(logEntry.TimeTaken);
                                    }
                                    else
                                    {
                                        pageViewsDaily.Add(url, new MethodInfo(logEntry.URIStem, logEntry.TimeTaken));
                                    }
                                }

                                if (hitsPerURLParams.Any())
                                {
                                    var urlParam = hitsPerURLParams.Where(p => logEntry.URIQuery.Contains(p)).FirstOrDefault();
                                    if (urlParam != null && urlParam != String.Empty)
                                    {
                                        if (urlParamHits.ContainsKey(url))
                                        {
                                            urlParamHits[url].Hit(logEntry.TimeTaken);
                                        }
                                        else
                                        {
                                            urlParamHits.Add(url, new MethodInfo(urlParam, logEntry.TimeTaken));
                                        }
                                    }
                                }
                            }

                            lastEntry = logEntry.TimeStamp;
                        }
                    }

                    if (processingList.Any())
                    {
                        requests.Add(new ConcurrentRequest(concurrencyWindow)
                        {
                            TimeStamp           = nextTime,
                            Transactions        = processingList.Count,
                            AverageResponseTime = processingList.Average(p => p.TimeTaken),
                            BytesSent           = processingList.Sum(t => t.BytesSent)
                        });
                        processingList.Clear();
                    }
                    AddHourlyPages(pageViewsHourly, hourlyPages, lastEntry);
                    AddDailyPages(pageViewsDaily, dailyPages, lastEntry);

                    #endregion
                }


                catch (Exception e)
                {
                    Console.WriteLine("Error!! {0}:{1} - {2}", e.GetType().Name, e.Message, e.StackTrace);
                    Debug.WriteLine("Error!! {0}:{1}", e.GetType().Name, e.Message);
                }
            }
            Console.WriteLine("\nGenerating Statistics");


            #region resultprocessing
            IEnumerable <MethodInfo> topPages;
            IEnumerable <IGrouping <DateTime, MethodInfo> > hourlyHits = null;
            long peakHits;
            IEnumerable <IGrouping <DateTime, MethodInfo> > peakHourPages = null;

            try
            {
                excelApp             = new Application();
                excelApp.Visible     = false;
                reportSpreadsheet    = excelApp.Workbooks.Add();
                excelApp.Calculation = XlCalculation.xlCalculationManual;
                reportSheet          = reportSpreadsheet.ActiveSheet;
                #region Concurrent Users
                if (requests.Any())
                {
                    Console.WriteLine("{0} Calculating Concurrent User Count", stopWatch.Elapsed.ToString(@"hh\:mm\:ss"));

                    reportSheet.Name = "Concurrent Users";
                    reportSheet.Cells[reportRow, reportCol++] = "Timestamp";
                    reportSheet.Cells[reportRow, reportCol++] = "Requests";
                    reportSheet.Cells[reportRow, reportCol++] = "TPS";
                    reportSheet.Cells[reportRow, reportCol++] = "Average Response Time";
                    reportSheet.Cells[reportRow, reportCol++] = "Concurrent Users (based on Little's Law)";
                    reportSheet.Cells[reportRow, reportCol++] = "Bytes Sent";
                    reportSheet.Cells[reportRow, reportCol++] = "Network Speed (Mbps)";


                    foreach (var p in requests)
                    {
                        reportCol = 1; reportRow++;
                        reportSheet.Cells[reportRow, reportCol++] = p.TimeStamp;
                        reportSheet.Cells[reportRow, reportCol++] = p.Transactions;
                        reportSheet.Cells[reportRow, reportCol++] = p.Tps;
                        reportSheet.Cells[reportRow, reportCol++] = p.AverageResponseTime;
                        reportSheet.Cells[reportRow, reportCol++] = p.ConcurrentUsers;
                        reportSheet.Cells[reportRow, reportCol++] = p.BytesSent;
                        reportSheet.Cells[reportRow, reportCol++] = p.NetworkSpeed;
                    }
                }
                #endregion

                reportSpreadsheet.Application.DisplayAlerts = false;
                reportSpreadsheet.SaveAs(exportFileName, ConflictResolution: XlSaveConflictResolution.xlLocalSessionChanges);

                #region Page visit Summary
                if (pageViewsForPeriod.Any())
                {
                    Console.WriteLine("{0} Genrating Page visit Summary", stopWatch.Elapsed.ToString(@"hh\:mm\:ss"));
                    reportSheet      = reportSpreadsheet.Worksheets.Add(Type.Missing, reportSheet, 1);
                    reportSheet.Name = "Page visit Summary";


                    startRow = startCol = 1;

                    startRow = CollectionToTable(pageViewsForPeriod.Values, startRow, startCol, "Page visit Summary (for the period)");


                    reportSheet.Shapes.AddChart(XlChartType.xlLine).Select();
                    excelApp.ActiveChart.SetSourceData(Source: reportSheet.get_Range("A1:B" + startRow));

                    reportSheet.Shapes.AddChart(XlChartType.xlPie).Select();
                    excelApp.ActiveChart.SetSourceData(Source: reportSheet.get_Range("A1:B" + startRow));
                    excelApp.ActiveChart.ClearToMatchStyle();
                    try
                    {
                        excelApp.ActiveChart.ChartStyle = 256;
                    }
                    catch (Exception e)
                    { }

                    excelApp.ActiveChart.SetElement(Microsoft.Office.Core.MsoChartElementType.msoElementChartTitleAboveChart);
                    excelApp.ActiveChart.ChartTitle.Text = "Page visit Summary (for the period) Most Visited Pages";

                    reportSheet.Shapes.AddChart(XlChartType.xlBarClustered).Select();
                    excelApp.ActiveChart.SetSourceData(Source: reportSheet.get_Range("A1:D" + startRow));
                    excelApp.ActiveChart.ClearToMatchStyle();
                    try
                    {
                        excelApp.ActiveChart.ChartStyle = 222;
                    }
                    catch (Exception e)
                    { }
                    excelApp.ActiveChart.SetElement(Microsoft.Office.Core.MsoChartElementType.msoElementChartTitleAboveChart);
                    excelApp.ActiveChart.ChartTitle.Text = "Page visit Summary (for the period) Average Response Time";
                    SpreadCharts(reportSheet);
                }
                #endregion

                #region Daily Analysis
                if (dailyPages.Any())
                {
                    Console.WriteLine("{0} Genrating Daily Statistics", stopWatch.Elapsed.ToString(@"hh\:mm\:ss"));
                    reportSheet      = reportSpreadsheet.Worksheets.Add(Type.Missing, reportSheet, 1);
                    reportSheet.Name = "Daily Analysis";

                    foreach (var d in dailyPages.Select(p => p.Timestamp).Distinct())
                    {
                        filteredEntries.UnionWith(dailyPages.Where(p => p.Timestamp == d.Date)
                                                  .OrderByDescending(p => p.Hits).Take(topPagesPerDay));
                        //Debug.WriteLine("Date: {0} - {1}", date, MethodInfo.TotalHits(dailyPages.Where(p => p.Timestamp == d.Date)));
                    }

                    topPages = filteredEntries.Where(p => filteredEntries.Count(q => q.Url == p.Url) > totalDays / 2);
                    startRow = startCol = 1;
                    AddChartFromSeries(startRow, startCol, "Daily Top Pages - Visits Trend", topPages, p => p.Hits, d => d.ToString(DateTimeFormatInfo.CurrentInfo.ShortDatePattern));

                    startRow = reportRow + 10;
                    startCol = 1;
                    AddChartFromSeries(startRow, startCol, "Daily Top Pages - Response Time(Average) Trend", topPages, p => p.AvgResponseTime, d => d.ToString(DateTimeFormatInfo.CurrentInfo.ShortDatePattern));


                    startRow = reportRow + 10;
                    startCol = 1;
                    AddChartFromSeries(startRow, startCol, "Daily Top Pages - Response Time(90%tile) Trend", topPages, p => p.NinetiethPercentile, d => d.ToString(DateTimeFormatInfo.CurrentInfo.ShortDatePattern));

                    startRow = 1;
                    startCol = 30;
                    filteredEntries.Clear();

                    //reportSheet.Cells[reportRow, reportCol] = "Date";
                    foreach (var d in dailyPages.Select(p => p.Timestamp).Distinct())
                    {
                        filteredEntries.UnionWith(dailyPages.Where(p => p.Timestamp == d.Date)
                                                  .OrderByDescending(p => p.NinetiethPercentile).Take(topPagesPerDay));
                    }
                    topPages = filteredEntries.Where(p => filteredEntries.Count(q => q.Url == p.Url) > totalDays / 2);
                    AddChartFromSeries(startRow, startCol, "Daily Slow Pages - Response Time(90%tile) Trend", topPages, p => p.NinetiethPercentile, d => d.ToString(DateTimeFormatInfo.CurrentInfo.ShortDatePattern));


                    startRow = reportRow + 10;
                    startCol = 30;
                    filteredEntries.Clear();

                    //reportSheet.Cells[reportRow, reportCol] = "Date";
                    foreach (var d in dailyPages.Select(p => p.Timestamp).Distinct())
                    {
                        filteredEntries.UnionWith(dailyPages.Where(p => p.Timestamp == d.Date)
                                                  .OrderByDescending(p => p.AvgResponseTime).Take(topPagesPerDay));
                        //Debug.WriteLine("Date: {0} - {1}", date, MethodInfo.TotalHits(dailyPages.Where(p => p.Timestamp == d.Date)));
                    }
                    topPages = filteredEntries.Where(p => filteredEntries.Count(q => q.Url == p.Url) > totalDays / 2);
                    AddChartFromSeries(startRow, startCol, "Daily Slow Pages - Response Time(Average) Trend", topPages, p => p.AvgResponseTime, d => d.ToString(DateTimeFormatInfo.CurrentInfo.ShortDatePattern));

                    SpreadCharts(reportSheet);
                }

                #endregion

                #region Hourly analysis
                if (hourlyPages.Any())
                {
                    Console.WriteLine("{0} Genrating Hourly Statistics", stopWatch.Elapsed.ToString(@"hh\:mm\:ss"));
                    reportSheet      = reportSpreadsheet.Worksheets.Add(Type.Missing, reportSheet, 1);
                    reportSheet.Name = "Hourly Analysis";

                    startRow = 1;
                    startCol = 1;
                    filteredEntries.Clear();

                    foreach (var d in hourlyPages.Select(p => p.Timestamp).Distinct())
                    {
                        filteredEntries.UnionWith(hourlyPages.Where(p => p.Timestamp == d.Date.AddHours(d.Hour))
                                                  .OrderByDescending(p => p.Hits).Take(topPagesPerDay));
                        //Debug.WriteLine("Date: {0} - {1}", date, MethodInfo.TotalHits(dailyPages.Where(p => p.Timestamp == d.Date)));
                    }
                    var totalHits = hourlyPages.Sum(p => p.Hits);
                    //filter out top pages which are there for 10% of time or 2% traffic
                    topPages  = filteredEntries.Where(p => filteredEntries.Count(q => q.Url == p.Url) > totalHours / 10 || p.Hits > totalHits * 2 / 100);
                    startRow += AddChartFromSeries(startRow, startCol, "Hourly Top Pages Summary (By Hits)", topPages, p => p.Hits, d => d.ToString());
                    excelApp.ActiveChart.Axes(XlAxisType.xlCategory).CategoryType = XlCategoryType.xlCategoryScale;

                    hourlyHits    = hourlyPages.GroupBy(p => p.Timestamp, q => q);
                    peakHits      = hourlyHits.Select(p => p.Sum(q => q.Hits)).OrderByDescending(p => p).Take(peakHoursCount).Min();
                    peakHourPages = hourlyHits.Where(p => p.Sum(q => q.Hits) >= peakHits);

                    startRow += 10; startCol = 1;
                    startRow += AddChartFromSeries(startRow, startCol, "Peak Hour Top Pages Summary (By Hits)", peakHourPages.SelectMany(g => g.Where(p => p.Hits > peakHits * 2 / 100)), p => p.Hits, d => d.ToString());
                    excelApp.ActiveChart.Axes(XlAxisType.xlCategory).CategoryType = XlCategoryType.xlCategoryScale;

                    CollectionToTable(peakHourPages.SelectMany(g => g), startRow + 10, 1, "Peak Hour Pages", true);

                    SpreadCharts(reportSheet);
                }
                #endregion

                #region URL Param Hits Summary
                if (hitsPerURLParams.Any())
                {
                    Console.WriteLine("{0} Genrating URL parameter statistics", stopWatch.Elapsed.ToString(@"hh\:mm\:ss"));

                    reportSheet      = reportSpreadsheet.Worksheets.Add(Type.Missing, reportSheet, 1);
                    startRow         = startCol = 1;
                    reportSheet.Name = "URL Parameters";
                    CollectionToTable(urlParamHits.Values, startRow, startCol, "URL Parameters Summary (for the period)");
                }
                #endregion

                #region Summary
                Console.WriteLine("{0} Genrating Summary", stopWatch.Elapsed.ToString(@"hh\:mm\:ss"));
                reportSheet      = reportSpreadsheet.Worksheets.Add(reportSheet, Type.Missing, 1);
                reportRow        = reportCol = 1;
                reportSheet.Name = "Summary";
                reportSheet.Cells[reportRow, 1]   = "Running From";
                reportSheet.Cells[reportRow++, 2] = curerntPath;

                reportSheet.Cells[reportRow, 1]   = "Commandline Argument";
                reportSheet.Cells[reportRow++, 2] = string.Join(";", cmdArgs.Select(x => x.Key + "=" + x.Value));

                reportSheet.Cells[reportRow, 1]   = "Files Processed";
                reportSheet.Cells[reportRow++, 2] = fileCount;

                reportSheet.Cells[reportRow, 1]   = "From";
                reportSheet.Cells[reportRow++, 2] = firstEntry;

                reportSheet.Cells[reportRow, 1]   = "To";
                reportSheet.Cells[reportRow++, 2] = lastEntry;

                reportSheet.Cells[reportRow, 1]   = "TotalHits";
                reportSheet.Cells[reportRow++, 2] = TotalHits;

                reportSheet.Cells[reportRow, 1]   = "Average Transactions/Sec";
                reportSheet.Cells[reportRow++, 2] = requests.Average(p => p.Tps);

                if (hourlyHits != null)
                {
                    reportSheet.Cells[reportRow, 1]   = "Average Transactions/Hour";
                    reportSheet.Cells[reportRow++, 2] = hourlyHits.Average(p => p.Sum(q => q.Hits));
                }

                if (peakHourPages != null)
                {
                    reportSheet.Cells[reportRow, 1]   = "Peak Hour Transactions/Hour";
                    reportSheet.Cells[reportRow++, 2] = peakHourPages.Average(p => p.Sum(q => q.Hits));

                    reportSheet.Cells[reportRow, 1]   = "Peak Hour Transactions/Sec";
                    reportSheet.Cells[reportRow++, 2] = peakHourPages.Average(p => p.Sum(q => q.Hits) / 3600);
                }

                reportSheet.Cells[reportRow, 1]   = "UniqueIPs";
                reportSheet.Cells[reportRow++, 2] = uniqueIPs.Count;

                reportSheet.Cells[reportRow, 1]   = "ServedRequests";
                reportSheet.Cells[reportRow++, 2] = ServedRequests;


                reportRow += 10;
                reportSheet.Cells[reportRow++, 1] = "Http Status code summary";

                reportSheet.Cells[reportRow, 1]   = "HTTP Code";
                reportSheet.Cells[reportRow++, 2] = "Count";

                foreach (var i in httpStatus)
                {
                    reportSheet.Cells[reportRow, reportCol++]   = i.Key;
                    reportSheet.Cells[reportRow++, reportCol--] = (i.Value);
                }
                #endregion
            }
            catch (Exception e)
            {
                Console.WriteLine("Error!! {0}:{1} - {2}", e.GetType().Name, e.Message, e.StackTrace);
                Debug.WriteLine("Error!! {0}:{1}", e.GetType().Name, e.Message);
            }
            finally
            {
                if (excelApp != null)
                {
                    excelApp.Calculation = XlCalculation.xlCalculationAutomatic;
                    if (reportSpreadsheet != null)
                    {
                        reportSpreadsheet.Save();
                        reportSpreadsheet.Close();
                        excelApp.Quit();
                    }
                }
                File.Delete(tmpFile);
                stopWatch.Stop();
                Console.WriteLine("Done, Final time : {0}", stopWatch.Elapsed.ToString(@"hh\:mm\:ss"));
            }
            #endregion

            return(0);
        }
예제 #2
0
        static int Main(string[] args)
        {
            #region Command Line argument processing
            if ( args.Contains ("--help") )
            {
                Console.WriteLine ("This tool depends on Microsoft Office 2010+");
                Console.WriteLine ("Valid switches are");
                Console.WriteLine ("-ignore <comma separated list of file extn>    : Ignore line with pattern");
                Console.WriteLine ("-include <comma separated list of file extn>   : Filter for pattern");
                Console.WriteLine ("-concurrency <No of minutes>                   : Concurrency Window in minutes");
                Console.WriteLine ("-toppages <No of pages>                        : No of Top Pages/day");
                Console.WriteLine ("-peaks <No of peaks>                           : No of Peak Hours to consider");
                Console.WriteLine ("-param <comma seperated list of patterns>      : Summarize specific URL parameters");
                Console.WriteLine ("-export <export filename>                      : Excel file report name, default will be with time stamp");
                Console.WriteLine ("-folder <log file folder path>                 : Current folder will be defaulted. All .log files in this folder will be processed.");
                Console.WriteLine ("Add a space after the pattern if you want extension mapping (e.g. .aspx ,.jpg)");
                return 0;
            }

            if ( args.Length % 2 != 0 )
            {
                throw new ArgumentException ("Command line arguments not valid, try --help to see valid ones!");
            }

            Dictionary<string, string> cmdArgs = new Dictionary<string, string> ();
            for ( int i = 0; i < args.Length; i += 2 )
            {
                cmdArgs.Add (args[i].ToLower (), args[i + 1]);
            }


            List<string> ignoredTypes = new List<string> (), filterTypes = new List<string> (), hitsPerURLParams = new List<string> ();
            if ( cmdArgs.ContainsKey (IgnoreSwitch) )
            {
                ignoredTypes = cmdArgs[IgnoreSwitch].ToLower ().Split (',').ToList ();
            }

            if ( cmdArgs.ContainsKey (FilterSwitch) )
            {
                filterTypes = cmdArgs[FilterSwitch].ToLower ().Split (',').ToList ();
            }

            if ( cmdArgs.ContainsKey (URLParamsSwitch) )
            {
                hitsPerURLParams = cmdArgs[URLParamsSwitch].ToLower ().Split (',').ToList ();
            }


            float concurrencyWindow = 5;
            if ( cmdArgs.ContainsKey (ConcurrencySwitch) )
            {
                concurrencyWindow = float.Parse (cmdArgs[ConcurrencySwitch]);
            }
            else
                cmdArgs.Add (ConcurrencySwitch, concurrencyWindow.ToString ());

            int topPagesPerDay = 10;
            if ( cmdArgs.ContainsKey (TopPagesSwitch) )
            {
                topPagesPerDay = int.Parse (cmdArgs[TopPagesSwitch]);
            }
            else
                cmdArgs.Add (TopPagesSwitch, topPagesPerDay.ToString ());

            int peakHoursCount = 3;
            if ( cmdArgs.ContainsKey (PeakHoursSwitch) )
            {
                peakHoursCount = int.Parse (cmdArgs[PeakHoursSwitch]);
            }
            else
                cmdArgs.Add (PeakHoursSwitch, peakHoursCount.ToString ());


            string exportFileName = null;
            if ( cmdArgs.ContainsKey (ExportFileSwitch) )
            {
                try
                {
                    exportFileName = Path.GetFullPath (cmdArgs[ExportFileSwitch]);
                }
                catch ( Exception e )
                {
                    Console.WriteLine ("Error creating report file:{0},{1}", e.GetType ().Name, e.Message);
                }
            }
            if ( exportFileName == null )
            {
                exportFileName = Path.GetFullPath ("Processing results_" + DateTime.Now.ToString ("dd_hh_mm") + ".xlsx");
                Console.WriteLine ("Writing output to {0}", exportFileName);
            }

            string curerntPath;
            if ( cmdArgs.ContainsKey (FolderSwitch) )
            {
                try
                {
                    curerntPath = Path.GetFullPath (cmdArgs[FolderSwitch]);
                }
                catch ( Exception e )
                {
                    Console.WriteLine ("Error accessing folder {0}:{1},{2}", cmdArgs[FolderSwitch], e.GetType ().Name, e.Message);
                    return 1;
                }
            }
            else
            {
                curerntPath = Directory.GetCurrentDirectory ();
                Console.WriteLine ("Working on IIS logs from current folder {0}", curerntPath);
            }
            #endregion

            Stopwatch stopWatch = new Stopwatch ();
            stopWatch.Start ();



            //var files = Directory.GetFiles(curerntPath, "*.log").ToList();
            var files = new DirectoryInfo (curerntPath)
                        .GetFiles ("*.log")
                        .OrderBy (f => f.LastWriteTime)
                        .Select (f => f.FullName)
                        .ToArray ();
            var totalFile = files.Count ();

            if ( totalFile == 0 )
            {
                Console.WriteLine ("No log files found!!");
                return 0;
            }

            Console.WriteLine ("Found {0} log files", totalFile);

            var tmpFile = System.IO.Path.GetTempFileName ();
            int fileCount = 0;
            int headerRows = 4;
            int entryCount = 0;



            List<IISLogEntry> processingList = new List<IISLogEntry> ();
            DateTime nextTime = DateTime.MinValue;

            long TotalHits = 0, ServedRequests = 0;
            List<ConcurrentRequest> requests = new List<ConcurrentRequest> ();
            HashSet<string> uniqueIPs = new HashSet<string> ();
            Dictionary<int, int> httpStatus = new Dictionary<int, int> ();
            Dictionary<string, MethodInfo> pageViewsForPeriod = new Dictionary<string, MethodInfo> ();

            int totalDays = 0, totalHours = 0;

            Dictionary<string, MethodInfo> pageViewsDaily = new Dictionary<string, MethodInfo> ();
            HashSet<MethodInfo> dailyPages = new HashSet<MethodInfo> ();

            Dictionary<string, MethodInfo> pageViewsHourly = new Dictionary<string, MethodInfo> ();
            HashSet<MethodInfo> hourlyPages = new HashSet<MethodInfo> ();

            //hits for key URL parameters
            Dictionary<string, MethodInfo> urlParamHits = new Dictionary<string, MethodInfo> ();
            DateTime firstEntry = DateTime.MinValue, lastEntry = DateTime.MinValue;

            //placeholder
            HashSet<MethodInfo> filteredEntries = new HashSet<MethodInfo> ();
            int startRow = 1, startCol = 1;
            int reportRow = 2, reportCol = 1;


            Console.WriteLine ("Preparing to Process..");


            foreach ( var f in files )
            {
                try
                {

                    ++fileCount;
                    var progress = fileCount * 100 / totalFile;

                    IEnumerable<string> matchedEntries = null;

                    var contents = File.ReadLines (f);


                    Dictionary<string, int> fieldIndex = new Dictionary<string, int> ();

                    #region Content filter


                    if ( filterTypes.Any () && ignoredTypes.Any () )
                        matchedEntries = contents.Where (s => s.StartsWith ("#") ||
                            ( filterTypes.Any (x => s.ToLower ().Contains (x)) &&
                            !ignoredTypes.Any (x => s.ToLower ().Contains (x)) ));

                    else if ( filterTypes.Any () )
                        matchedEntries = contents.Where (s => s.StartsWith ("#") || filterTypes.Any (x => s.ToLower ().Contains (x)));

                    else if ( ignoredTypes.Any () )
                        matchedEntries = contents.Where (s => s.StartsWith ("#") || !ignoredTypes.Any (x => s.ToLower ().Contains (x)));
                    else
                        matchedEntries = contents;


                    foreach ( var rawLogEntry in matchedEntries )
                    {

                        IISLogEntry logEntry;
                        if ( rawLogEntry.StartsWith ("#") )
                        {
                            if ( rawLogEntry.StartsWith ("#Fields:") )
                                fieldIndex = ParseHeaderFields (rawLogEntry);
                        }
                        else
                        {
                            Console.Write ("\r{0} File {1} of {2} files ({3}%), processing {4}      ", stopWatch.Elapsed.ToString (@"hh\:mm\:ss"), fileCount, totalFile, progress, ++TotalHits);

                            var columns = rawLogEntry.Split (' ');
                            logEntry = new IISLogEntry ()
                            {
                                TimeStamp = DateTime.Parse (columns[0] + " " + columns[1]),
                                ClientIPAddress = fieldIndex.ContainsKey (IISLogEntry.propClientIPAddress) ? columns[fieldIndex[IISLogEntry.propClientIPAddress]] : String.Empty,
                                UserName = fieldIndex.ContainsKey (IISLogEntry.propUserName) ? columns[fieldIndex[IISLogEntry.propUserName]] : String.Empty,
                                ServiceNameandInstanceNumber = fieldIndex.ContainsKey (IISLogEntry.propServiceNameandInstanceNumber) ? columns[fieldIndex[IISLogEntry.propServiceNameandInstanceNumber]] : String.Empty,
                                ServerName = fieldIndex.ContainsKey (IISLogEntry.propServerName) ? columns[fieldIndex[IISLogEntry.propServerName]] : String.Empty,
                                ServerIPAddress = fieldIndex.ContainsKey (IISLogEntry.propServerIPAddress) ? columns[fieldIndex[IISLogEntry.propServerIPAddress]] : String.Empty,
                                ServerPort = fieldIndex.ContainsKey (IISLogEntry.propClientIPAddress) ? Int32.Parse (columns[fieldIndex[IISLogEntry.propServerPort]]) : 0,
                                Method = fieldIndex.ContainsKey (IISLogEntry.propMethod) ? columns[fieldIndex[IISLogEntry.propMethod]] : String.Empty,
                                URIStem = fieldIndex.ContainsKey (IISLogEntry.propURIStem) ? columns[fieldIndex[IISLogEntry.propURIStem]] : String.Empty,
                                URIQuery = fieldIndex.ContainsKey (IISLogEntry.propURIQuery) ? columns[fieldIndex[IISLogEntry.propURIQuery]] : String.Empty,
                                HTTPStatus = fieldIndex.ContainsKey (IISLogEntry.propHTTPStatus) ? Int32.Parse (columns[fieldIndex[IISLogEntry.propHTTPStatus]]) : 0,
                                //Win32Status = fieldIndex.ContainsKey(IISLogEntry.propWin32Status) ? Int32.Parse(row[fieldIndex[IISLogEntry.propWin32Status]]) : 0,
                                BytesSent = fieldIndex.ContainsKey (IISLogEntry.propBytesSent) ? Int32.Parse (columns[fieldIndex[IISLogEntry.propBytesSent]]) : 0,
                                BytesReceived = fieldIndex.ContainsKey (IISLogEntry.propBytesReceived) ? Int32.Parse (columns[fieldIndex[IISLogEntry.propBytesReceived]]) : 0,
                                TimeTaken = fieldIndex.ContainsKey (IISLogEntry.propTimeTaken) ? Int32.Parse (columns[fieldIndex[IISLogEntry.propTimeTaken]]) : 0,
                                ProtocolVersion = fieldIndex.ContainsKey (IISLogEntry.propProtocolVersion) ? columns[fieldIndex[IISLogEntry.propProtocolVersion]] : String.Empty,
                                Host = fieldIndex.ContainsKey (IISLogEntry.propHost) ? columns[fieldIndex[IISLogEntry.propHost]] : String.Empty,
                                UserAgent = fieldIndex.ContainsKey (IISLogEntry.propUserAgent) ? columns[fieldIndex[IISLogEntry.propUserAgent]] : String.Empty,
                                Cookie = fieldIndex.ContainsKey (IISLogEntry.propCookie) ? columns[fieldIndex[IISLogEntry.propCookie]] : String.Empty,
                                Referrer = fieldIndex.ContainsKey (IISLogEntry.propReferrer) ? columns[fieldIndex[IISLogEntry.propReferrer]] : String.Empty,
                                ProtocolSubstatus = fieldIndex.ContainsKey (IISLogEntry.propProtocolSubstatus) ? columns[fieldIndex[IISLogEntry.propProtocolSubstatus]] : String.Empty
                            };

                    #endregion

                            #region entry processing

                            var url = logEntry.URIStem.ToLower ();

                            #region HTTP status codes & IP
                            if ( httpStatus.ContainsKey (logEntry.HTTPStatus) )
                                httpStatus[logEntry.HTTPStatus]++;
                            else
                                httpStatus.Add (logEntry.HTTPStatus, 1);

                            if ( !uniqueIPs.Contains (logEntry.ClientIPAddress) )
                                uniqueIPs.Add (logEntry.ClientIPAddress);
                            #endregion

                            if ( nextTime == DateTime.MinValue )
                            {
                                firstEntry = logEntry.TimeStamp;
                                lastEntry = logEntry.TimeStamp;
                                nextTime = logEntry.TimeStamp.Date.
                                            AddHours (logEntry.TimeStamp.Hour).
                                            AddMinutes (logEntry.TimeStamp.Minute).
                                            AddMinutes (concurrencyWindow);
                            }

                            if ( logEntry.TimeStamp > nextTime )
                            {
                                if ( processingList.Any () )
                                {
                                    requests.Add (new ConcurrentRequest (concurrencyWindow)
                                    {
                                        TimeStamp = nextTime,
                                        Transactions = processingList.Count,
                                        AverageResponseTime = processingList.Average (p => p.TimeTaken),
                                        BytesSent = processingList.Sum (t => t.BytesSent)
                                    });
                                    processingList.Clear ();
                                }
                                else
                                {
                                    requests.Add (new ConcurrentRequest (concurrencyWindow)
                                    {
                                        TimeStamp = nextTime,
                                        Transactions = 0,
                                        AverageResponseTime = 0,
                                        BytesSent = 0
                                    });
                                }
                                nextTime = nextTime.AddMinutes (concurrencyWindow);
                            }

                            if ( lastEntry.Hour != logEntry.TimeStamp.Hour )
                            {
                                totalHours++;
                                AddHourlyPages (pageViewsHourly, hourlyPages, lastEntry);
                            }

                            if ( lastEntry.Date != logEntry.TimeStamp.Date )
                            {
                                totalDays++;
                                AddDailyPages (pageViewsDaily, dailyPages, lastEntry);
                            }

                            //add the current one to future processing, otherwise one in teh borderlien will be missing
                            if ( logEntry.HTTPStatus == 200 )
                            {
                                processingList.Add (logEntry);
                                ServedRequests++;

                                if ( pageViewsForPeriod.ContainsKey (url) )
                                    pageViewsForPeriod[url].Hit (logEntry.TimeTaken);
                                else
                                    pageViewsForPeriod.Add (url, new MethodInfo (logEntry.URIStem, logEntry.TimeTaken));

                                if ( lastEntry.Hour == logEntry.TimeStamp.Hour )
                                {
                                    if ( pageViewsHourly.ContainsKey (url) )
                                        pageViewsHourly[url].Hit (logEntry.TimeTaken);
                                    else
                                        pageViewsHourly.Add (url, new MethodInfo (logEntry.URIStem, logEntry.TimeTaken));
                                }

                                if ( lastEntry.Date == logEntry.TimeStamp.Date )
                                {
                                    if ( pageViewsDaily.ContainsKey (url) )
                                        pageViewsDaily[url].Hit (logEntry.TimeTaken);
                                    else
                                        pageViewsDaily.Add (url, new MethodInfo (logEntry.URIStem, logEntry.TimeTaken));
                                }

                                if ( hitsPerURLParams.Any () )
                                {
                                    var urlParam = hitsPerURLParams.Where (p => logEntry.URIQuery.Contains (p)).FirstOrDefault ();
                                    if ( urlParam != null && urlParam != String.Empty )
                                    {
                                        if ( urlParamHits.ContainsKey (url) )
                                            urlParamHits[url].Hit (logEntry.TimeTaken);
                                        else
                                            urlParamHits.Add (url, new MethodInfo (urlParam, logEntry.TimeTaken));
                                    }
                                }
                            }

                            lastEntry = logEntry.TimeStamp;
                        }
                    }

                    if ( processingList.Any () )
                    {
                        requests.Add (new ConcurrentRequest (concurrencyWindow)
                        {
                            TimeStamp = nextTime,
                            Transactions = processingList.Count,
                            AverageResponseTime = processingList.Average (p => p.TimeTaken),
                            BytesSent = processingList.Sum (t => t.BytesSent)
                        });
                        processingList.Clear ();
                    }
                    AddHourlyPages (pageViewsHourly, hourlyPages, lastEntry);
                    AddDailyPages (pageViewsDaily, dailyPages, lastEntry);

                            #endregion
                }


                catch ( Exception e )
                {
                    Console.WriteLine ("Error!! {0}:{1} - {2}", e.GetType ().Name, e.Message, e.StackTrace);
                    Debug.WriteLine ("Error!! {0}:{1}", e.GetType ().Name, e.Message);
                }
            }
            Console.WriteLine ("\nGenerating Statistics");


            #region resultprocessing
            IEnumerable<MethodInfo> topPages;
            IEnumerable<IGrouping<DateTime, MethodInfo>> hourlyHits = null;
            long peakHits;
            IEnumerable<IGrouping<DateTime, MethodInfo>> peakHourPages = null;

            try
            {
                excelApp = new Application ();
                excelApp.Visible = false;
                reportSpreadsheet = excelApp.Workbooks.Add ();
                excelApp.Calculation = XlCalculation.xlCalculationManual;
                reportSheet = reportSpreadsheet.ActiveSheet;
                #region Concurrent Users
                if ( requests.Any () )
                {
                    Console.WriteLine ("{0} Calculating Concurrent User Count", stopWatch.Elapsed.ToString (@"hh\:mm\:ss"));

                    reportSheet.Name = "Concurrent Users";
                    reportSheet.Cells[reportRow, reportCol++] = "Timestamp";
                    reportSheet.Cells[reportRow, reportCol++] = "Requests";
                    reportSheet.Cells[reportRow, reportCol++] = "TPS";
                    reportSheet.Cells[reportRow, reportCol++] = "Average Response Time";
                    reportSheet.Cells[reportRow, reportCol++] = "Concurrent Users (based on Little's Law)";
                    reportSheet.Cells[reportRow, reportCol++] = "Bytes Sent";
                    reportSheet.Cells[reportRow, reportCol++] = "Network Speed (Mbps)";


                    foreach ( var p in requests )
                    {
                        reportCol = 1; reportRow++;
                        reportSheet.Cells[reportRow, reportCol++] = p.TimeStamp;
                        reportSheet.Cells[reportRow, reportCol++] = p.Transactions;
                        reportSheet.Cells[reportRow, reportCol++] = p.Tps;
                        reportSheet.Cells[reportRow, reportCol++] = p.AverageResponseTime;
                        reportSheet.Cells[reportRow, reportCol++] = p.ConcurrentUsers;
                        reportSheet.Cells[reportRow, reportCol++] = p.BytesSent;
                        reportSheet.Cells[reportRow, reportCol++] = p.NetworkSpeed;
                    }
                }
                #endregion

                reportSpreadsheet.Application.DisplayAlerts = false;
                reportSpreadsheet.SaveAs (exportFileName, ConflictResolution: XlSaveConflictResolution.xlLocalSessionChanges);

                #region Page visit Summary
                if ( pageViewsForPeriod.Any () )
                {
                    Console.WriteLine ("{0} Genrating Page visit Summary", stopWatch.Elapsed.ToString (@"hh\:mm\:ss"));
                    reportSheet = reportSpreadsheet.Worksheets.Add (Type.Missing, reportSheet, 1);
                    reportSheet.Name = "Page visit Summary";


                    startRow = startCol = 1;

                    startRow = CollectionToTable (pageViewsForPeriod.Values, startRow, startCol, "Page visit Summary (for the period)");


                    reportSheet.Shapes.AddChart (XlChartType.xlLine).Select ();
                    excelApp.ActiveChart.SetSourceData (Source: reportSheet.get_Range ("A1:B" + startRow));

                    reportSheet.Shapes.AddChart (XlChartType.xlPie).Select ();
                    excelApp.ActiveChart.SetSourceData (Source: reportSheet.get_Range ("A1:B" + startRow));
                    excelApp.ActiveChart.ClearToMatchStyle ();
                    try
                    {
                        excelApp.ActiveChart.ChartStyle = 256;
                    }
                    catch ( Exception e )
                    { }

                    excelApp.ActiveChart.SetElement (Microsoft.Office.Core.MsoChartElementType.msoElementChartTitleAboveChart);
                    excelApp.ActiveChart.ChartTitle.Text = "Page visit Summary (for the period) Most Visited Pages";

                    reportSheet.Shapes.AddChart (XlChartType.xlBarClustered).Select ();
                    excelApp.ActiveChart.SetSourceData (Source: reportSheet.get_Range ("A1:D" + startRow));
                    excelApp.ActiveChart.ClearToMatchStyle ();
                    try
                    {
                        excelApp.ActiveChart.ChartStyle = 222;
                    }
                    catch ( Exception e )
                    { }
                    excelApp.ActiveChart.SetElement (Microsoft.Office.Core.MsoChartElementType.msoElementChartTitleAboveChart);
                    excelApp.ActiveChart.ChartTitle.Text = "Page visit Summary (for the period) Average Response Time";
                    SpreadCharts (reportSheet);

                }
                #endregion

                #region Daily Analysis
                if ( dailyPages.Any () )
                {
                    Console.WriteLine ("{0} Genrating Daily Statistics", stopWatch.Elapsed.ToString (@"hh\:mm\:ss"));
                    reportSheet = reportSpreadsheet.Worksheets.Add (Type.Missing, reportSheet, 1);
                    reportSheet.Name = "Daily Analysis";

                    foreach ( var d in dailyPages.Select (p => p.Timestamp).Distinct () )
                    {
                        filteredEntries.UnionWith (dailyPages.Where (p => p.Timestamp == d.Date)
                                                                    .OrderByDescending (p => p.Hits).Take (topPagesPerDay));
                        //Debug.WriteLine("Date: {0} - {1}", date, MethodInfo.TotalHits(dailyPages.Where(p => p.Timestamp == d.Date)));
                    }

                    topPages = filteredEntries.Where (p => filteredEntries.Count (q => q.Url == p.Url) > totalDays / 2);
                    startRow = startCol = 1;
                    AddChartFromSeries (startRow, startCol, "Daily Top Pages - Visits Trend", topPages, p => p.Hits, d => d.ToString (DateTimeFormatInfo.CurrentInfo.ShortDatePattern));

                    startRow = reportRow + 10;
                    startCol = 1;
                    AddChartFromSeries (startRow, startCol, "Daily Top Pages - Response Time(Average) Trend", topPages, p => p.AvgResponseTime, d => d.ToString (DateTimeFormatInfo.CurrentInfo.ShortDatePattern));


                    startRow = reportRow + 10;
                    startCol = 1;
                    AddChartFromSeries (startRow, startCol, "Daily Top Pages - Response Time(90%tile) Trend", topPages, p => p.NinetiethPercentile, d => d.ToString (DateTimeFormatInfo.CurrentInfo.ShortDatePattern));

                    startRow = 1;
                    startCol = 30;
                    filteredEntries.Clear ();

                    //reportSheet.Cells[reportRow, reportCol] = "Date";
                    foreach ( var d in dailyPages.Select (p => p.Timestamp).Distinct () )
                    {
                        filteredEntries.UnionWith (dailyPages.Where (p => p.Timestamp == d.Date)
                                               .OrderByDescending (p => p.NinetiethPercentile).Take (topPagesPerDay));
                    }
                    topPages = filteredEntries.Where (p => filteredEntries.Count (q => q.Url == p.Url) > totalDays / 2);
                    AddChartFromSeries (startRow, startCol, "Daily Slow Pages - Response Time(90%tile) Trend", topPages, p => p.NinetiethPercentile, d => d.ToString (DateTimeFormatInfo.CurrentInfo.ShortDatePattern));


                    startRow = reportRow + 10;
                    startCol = 30;
                    filteredEntries.Clear ();

                    //reportSheet.Cells[reportRow, reportCol] = "Date";
                    foreach ( var d in dailyPages.Select (p => p.Timestamp).Distinct () )
                    {
                        filteredEntries.UnionWith (dailyPages.Where (p => p.Timestamp == d.Date)
                                               .OrderByDescending (p => p.AvgResponseTime).Take (topPagesPerDay));
                        //Debug.WriteLine("Date: {0} - {1}", date, MethodInfo.TotalHits(dailyPages.Where(p => p.Timestamp == d.Date)));
                    }
                    topPages = filteredEntries.Where (p => filteredEntries.Count (q => q.Url == p.Url) > totalDays / 2);
                    AddChartFromSeries (startRow, startCol, "Daily Slow Pages - Response Time(Average) Trend", topPages, p => p.AvgResponseTime, d => d.ToString (DateTimeFormatInfo.CurrentInfo.ShortDatePattern));

                    SpreadCharts (reportSheet);
                }

                #endregion

                #region Hourly analysis
                if ( hourlyPages.Any () )
                {
                    Console.WriteLine ("{0} Genrating Hourly Statistics", stopWatch.Elapsed.ToString (@"hh\:mm\:ss"));
                    reportSheet = reportSpreadsheet.Worksheets.Add (Type.Missing, reportSheet, 1);
                    reportSheet.Name = "Hourly Analysis";

                    startRow = 1;
                    startCol = 1;
                    filteredEntries.Clear ();

                    foreach ( var d in hourlyPages.Select (p => p.Timestamp).Distinct () )
                    {
                        filteredEntries.UnionWith (hourlyPages.Where (p => p.Timestamp == d.Date.AddHours (d.Hour))
                                            .OrderByDescending (p => p.Hits).Take (topPagesPerDay));
                        //Debug.WriteLine("Date: {0} - {1}", date, MethodInfo.TotalHits(dailyPages.Where(p => p.Timestamp == d.Date)));
                    }
                    var totalHits = hourlyPages.Sum (p => p.Hits);
                    //filter out top pages which are there for 10% of time or 2% traffic
                    topPages = filteredEntries.Where (p => filteredEntries.Count (q => q.Url == p.Url) > totalHours / 10 || p.Hits > totalHits * 2 / 100);
                    startRow += AddChartFromSeries (startRow, startCol, "Hourly Top Pages Summary (By Hits)", topPages, p => p.Hits, d => d.ToString ());
                    excelApp.ActiveChart.Axes (XlAxisType.xlCategory).CategoryType = XlCategoryType.xlCategoryScale;

                    hourlyHits = hourlyPages.GroupBy (p => p.Timestamp, q => q);
                    peakHits = hourlyHits.Select (p => p.Sum (q => q.Hits)).OrderByDescending (p => p).Take (peakHoursCount).Min ();
                    peakHourPages = hourlyHits.Where (p => p.Sum (q => q.Hits) >= peakHits);

                    startRow += 10; startCol = 1;
                    startRow += AddChartFromSeries (startRow, startCol, "Peak Hour Top Pages Summary (By Hits)", peakHourPages.SelectMany (g => g.Where (p => p.Hits > peakHits * 2 / 100)), p => p.Hits, d => d.ToString ());
                    excelApp.ActiveChart.Axes (XlAxisType.xlCategory).CategoryType = XlCategoryType.xlCategoryScale;

                    CollectionToTable (peakHourPages.SelectMany (g => g), startRow + 10, 1, "Peak Hour Pages", true);

                    SpreadCharts (reportSheet);
                }
                #endregion

                #region URL Param Hits Summary
                if ( hitsPerURLParams.Any () )
                {
                    Console.WriteLine ("{0} Genrating URL parameter statistics", stopWatch.Elapsed.ToString (@"hh\:mm\:ss"));

                    reportSheet = reportSpreadsheet.Worksheets.Add (Type.Missing, reportSheet, 1);
                    startRow = startCol = 1;
                    reportSheet.Name = "URL Parameters";
                    CollectionToTable (urlParamHits.Values, startRow, startCol, "URL Parameters Summary (for the period)");
                }
                #endregion

                #region Summary
                Console.WriteLine ("{0} Genrating Summary", stopWatch.Elapsed.ToString (@"hh\:mm\:ss"));
                reportSheet = reportSpreadsheet.Worksheets.Add (reportSheet, Type.Missing, 1);
                reportRow = reportCol = 1;
                reportSheet.Name = "Summary";
                reportSheet.Cells[reportRow, 1] = "Running From";
                reportSheet.Cells[reportRow++, 2] = curerntPath;

                reportSheet.Cells[reportRow, 1] = "Commandline Argument";
                reportSheet.Cells[reportRow++, 2] = string.Join (";", cmdArgs.Select (x => x.Key + "=" + x.Value));

                reportSheet.Cells[reportRow, 1] = "Files Processed";
                reportSheet.Cells[reportRow++, 2] = fileCount;

                reportSheet.Cells[reportRow, 1] = "From";
                reportSheet.Cells[reportRow++, 2] = firstEntry;

                reportSheet.Cells[reportRow, 1] = "To";
                reportSheet.Cells[reportRow++, 2] = lastEntry;

                reportSheet.Cells[reportRow, 1] = "TotalHits";
                reportSheet.Cells[reportRow++, 2] = TotalHits;

                reportSheet.Cells[reportRow, 1] = "Average Transactions/Sec";
                reportSheet.Cells[reportRow++, 2] = requests.Average (p => p.Tps);

                if ( hourlyHits!=null )
                {
                    reportSheet.Cells[reportRow, 1] = "Average Transactions/Hour";
                    reportSheet.Cells[reportRow++, 2] = hourlyHits.Average (p => p.Sum (q => q.Hits));
                }

                if ( peakHourPages!=null )
                {
                    reportSheet.Cells[reportRow, 1] = "Peak Hour Transactions/Hour";
                    reportSheet.Cells[reportRow++, 2] = peakHourPages.Average (p => p.Sum (q => q.Hits));

                    reportSheet.Cells[reportRow, 1] = "Peak Hour Transactions/Sec";
                    reportSheet.Cells[reportRow++, 2] = peakHourPages.Average (p => p.Sum (q => q.Hits) / 3600);
                }

                reportSheet.Cells[reportRow, 1] = "UniqueIPs";
                reportSheet.Cells[reportRow++, 2] = uniqueIPs.Count;

                reportSheet.Cells[reportRow, 1] = "ServedRequests";
                reportSheet.Cells[reportRow++, 2] = ServedRequests;


                reportRow += 10;
                reportSheet.Cells[reportRow++, 1] = "Http Status code summary";

                reportSheet.Cells[reportRow, 1] = "HTTP Code";
                reportSheet.Cells[reportRow++, 2] = "Count";

                foreach ( var i in httpStatus )
                {
                    reportSheet.Cells[reportRow, reportCol++] = i.Key;
                    reportSheet.Cells[reportRow++, reportCol--] = ( i.Value );
                }
                #endregion

            }
            catch ( Exception e )
            {
                Console.WriteLine ("Error!! {0}:{1} - {2}", e.GetType ().Name, e.Message, e.StackTrace);
                Debug.WriteLine ("Error!! {0}:{1}", e.GetType ().Name, e.Message);
            }
            finally
            {
                if ( excelApp != null )
                {
                    excelApp.Calculation = XlCalculation.xlCalculationAutomatic;
                    if ( reportSpreadsheet != null )
                    {
                        reportSpreadsheet.Save ();
                        reportSpreadsheet.Close ();
                        excelApp.Quit ();
                    }
                }
                File.Delete (tmpFile);
                stopWatch.Stop ();
                Console.WriteLine ("Done, Final time : {0}", stopWatch.Elapsed.ToString (@"hh\:mm\:ss"));
            }
            #endregion

            return 0;
        }