Пример #1
0
        private async void ExportCommand_Executed(object sender, ExecutedRoutedEventArgs e)
        {
            Microsoft.Win32.SaveFileDialog dialog = new Microsoft.Win32.SaveFileDialog
            {
                Title = "Save Log As",
            };

            switch (_m.FileFormat)
            {
            case LogExportType.Text:
                dialog.Filter = "Text Format (*.txt)|*.txt";
                break;

            case LogExportType.Html:
                dialog.Filter = "HTML Format (*.html)|*.html";
                break;

            default:
                Debug.Assert(false, "Internal Logic Error at LogExportWindow.ExportCommand_Executed");
                break;
            }

            if (_m.ExportSystemLog)
            {
                // Local time should be ok because the filename is likely only useful to the person exporting the log
                DateTime localTime = DateTime.Now;
                dialog.FileName = $"SystemLog_{localTime.ToString("yyyy_MM_dd_HHmmss", CultureInfo.InvariantCulture)}";
            }
            else if (_m.ExportBuildLog)
            {
                Debug.Assert(0 < _m.BuildEntries.Count, "Internal Logic Error at LogExportWindow.ExportCommand_Executed");
                LogModel.BuildInfo bi = _m.BuildEntries[_m.SelectedBuildEntryIndex];

                // Filter invalid filename chars
                List <char> filteredChars = new List <char>(bi.Name.Length);
                List <char> invalidChars  = Path.GetInvalidFileNameChars().ToList();
                invalidChars.Add('['); // Remove [ and ]
                invalidChars.Add(']');
                invalidChars.Add(' '); // Spaces are not very script or web friendly, let's remove them too.
                foreach (char ch in bi.Name)
                {
                    if (invalidChars.Contains(ch))
                    {
                        filteredChars.Add(Convert.ToChar("_")); // Replace invalid chars and spaces with an underscore
                    }
                    else
                    {
                        filteredChars.Add(ch);
                    }
                }
                string filteredName = new string(filteredChars.ToArray());
                // The log stores dateTime as UTC so its safe to use ToLocalTime() to convert to the users timezone
                dialog.FileName = $"BuildLog_{bi.StartTime.ToLocalTime().ToString("yyyy_MM_dd_HHmmss", CultureInfo.InvariantCulture)}_{filteredName}";
            }

            bool?result = dialog.ShowDialog();

            // If user cancelled SaveDialog, do nothing
            if (result != true)
            {
                return;
            }
            string destFile = dialog.FileName;

            _m.InProgress = true;
            try
            {
                await Task.Run(() =>
                {
                    if (_m.ExportSystemLog)
                    {
                        _m.Logger.ExportSystemLog(_m.FileFormat, destFile);
                    }
                    else if (_m.ExportBuildLog)
                    {
                        Debug.Assert(0 < _m.BuildEntries.Count, "Internal Logic Error at LogExportWindow.ExportCommand_Executed");
                        int buildId = _m.BuildEntries[_m.SelectedBuildEntryIndex].Id;
                        _m.Logger.ExportBuildLog(_m.FileFormat, destFile, buildId, new BuildLogOptions
                        {
                            IncludeComments = _m.BuildLogIncludeComments,
                            IncludeMacros   = _m.BuildLogIncludeMacros,
                            ShowLogFlags    = _m.BuildLogShowLogFlags,
                        });
                    }
                });
            }
            finally
            {
                _m.InProgress = false;
            }

            // Open log file
            Application.Current.Dispatcher.Invoke(() =>
            {
                if (_m.FileFormat == LogExportType.Html)
                {
                    // Call FileHelper.OpenUri (instead of OpenPath) to open .html files with the default browser.
                    ResultReport result = FileHelper.OpenUri(destFile);
                    if (!result.Success)
                    {
                        MessageBox.Show(this, $"URL [{destFile}] could not be opened.\r\n\r\n{result.Message}.",
                                        "Error Opening URL", MessageBoxButton.OK, MessageBoxImage.Error);
                    }
                }
                else
                {
                    MainViewModel.OpenTextFile(destFile);
                }
            });

            // Close LogExportWindow
            Close();
        }
Пример #2
0
        public void ExportBuildLog(int buildId, BuildLogOptions opts)
        {
            switch (_exportType)
            {
                #region Text
            case LogExportType.Text:
            {
                LogModel.BuildInfo dbBuild = _db.Table <LogModel.BuildInfo>().First(x => x.Id == buildId);
                _w.WriteLine($"- PEBakery Build <{dbBuild.Name}> -");
                _w.WriteLine($"Built    by PEBakery {dbBuild.PEBakeryVersion}");
                _w.WriteLine($"Exported by PEBakery {Global.Const.ProgramVersionStrFull}");
                _w.WriteLine();
                _w.WriteLine($"Started  at {dbBuild.StartTime.ToLocalTime().ToString("yyyy-MM-dd hh:mm:ss tt K", CultureInfo.InvariantCulture)}");
                if (dbBuild.FinishTime != DateTime.MinValue)
                {         // Put these lines only if a build successfully finished
                    _w.WriteLine($"Finished at {dbBuild.FinishTime.ToLocalTime().ToString("yyyy-MM-dd hh:mm:ss tt K", CultureInfo.InvariantCulture)}");
                    TimeSpan elapsed = dbBuild.FinishTime - dbBuild.StartTime;
                    _w.WriteLine($"Took {elapsed:h\\:mm\\:ss}");
                }
                _w.WriteLine();
                _w.WriteLine();

                // Log Statistics
                _w.WriteLine("<Log Statistics>");
                var states = ((LogState[])Enum.GetValues(typeof(LogState))).Where(x => x != LogState.None && x != LogState.CriticalError);
                foreach (LogState state in states)
                {
                    int count = _db.Table <LogModel.BuildLog>().Count(x => x.BuildId == buildId && x.State == state);
                    _w.WriteLine($"{state,-10}: {count}");
                }
                _w.WriteLine();
                _w.WriteLine();

                // Show ErrorLogs
                LogModel.BuildLog[] errors = _db.Table <LogModel.BuildLog>().Where(x => x.BuildId == buildId && x.State == LogState.Error).ToArray();
                if (0 < errors.Length)
                {
                    _w.WriteLine("<Errors>");

                    int[]             scLogIds    = errors.Select(x => x.ScriptId).OrderBy(x => x).Distinct().ToArray();
                    int[]             refScLogIds = errors.Select(x => x.RefScriptId).OrderBy(x => x).Distinct().ToArray();
                    LogModel.Script[] scLogs      = _db.Table <LogModel.Script>()
                                                    .Where(x => x.BuildId == buildId && scLogIds.Contains(x.Id))
                                                    .ToArray();
                    LogModel.Script[] scOriginLogs = _db.Table <LogModel.Script>()
                                                     .Where(x => x.BuildId == buildId && (scLogIds.Contains(x.Id) || refScLogIds.Contains(x.Id)))
                                                     .ToArray();
                    foreach (LogModel.Script scLog in scLogs)
                    {
                        LogModel.BuildLog[] eLogs = errors.Where(x => x.ScriptId == scLog.Id).ToArray();
                        if (eLogs.Length == 1)
                        {
                            _w.WriteLine($"- [{eLogs.Length}] Error in script [{scLog.Name}] ({scLog.TreePath})");
                        }
                        else
                        {
                            _w.WriteLine($"- [{eLogs.Length}] Errors in script [{scLog.Name}] ({scLog.TreePath})");
                        }

                        foreach (LogModel.BuildLog eLog in eLogs)
                        {
                            _w.WriteLine(eLog.Export(LogExportType.Text, false, false));

                            string refScriptText = ExportRefScriptText(eLog, scOriginLogs);
                            if (refScriptText != null)
                            {         // Referenced scripts
                                _w.Write("  ");
                                _w.WriteLine(refScriptText);
                            }
                        }
                        _w.WriteLine();
                    }

                    _w.WriteLine();
                }

                // Show WarnLogs
                LogModel.BuildLog[] warns = _db.Table <LogModel.BuildLog>().Where(x => x.BuildId == buildId && x.State == LogState.Warning).ToArray();
                if (0 < warns.Length)
                {
                    _w.WriteLine("<Warnings>");

                    int[]             scLogIds    = warns.Select(x => x.ScriptId).OrderBy(x => x).Distinct().ToArray();
                    int[]             refScLogIds = warns.Select(x => x.RefScriptId).OrderBy(x => x).Distinct().ToArray();
                    LogModel.Script[] scLogs      = _db.Table <LogModel.Script>()
                                                    .Where(x => x.BuildId == buildId && scLogIds.Contains(x.Id))
                                                    .ToArray();
                    LogModel.Script[] scOriginLogs = _db.Table <LogModel.Script>()
                                                     .Where(x => x.BuildId == buildId && refScLogIds.Contains(x.Id))
                                                     // .Where(x => x.BuildId == buildId && (scLogIds.Contains(x.Id) || refScLogIds.Contains(x.Id)))
                                                     .ToArray();

                    foreach (LogModel.Script scLog in scLogs)
                    {
                        LogModel.BuildLog[] wLogs = warns.Where(x => x.ScriptId == scLog.Id).ToArray();
                        Debug.Assert(0 < wLogs.Length);

                        if (wLogs.Length == 1)
                        {
                            _w.WriteLine($"- [{wLogs.Length}] Warning in script [{scLog.Name}] ({scLog.TreePath})");
                        }
                        else
                        {
                            _w.WriteLine($"- [{wLogs.Length}] Warnings in script [{scLog.Name}] ({scLog.TreePath})");
                        }

                        foreach (LogModel.BuildLog wLog in wLogs)
                        {
                            _w.WriteLine(wLog.Export(LogExportType.Text, false, false));

                            string refScriptText = ExportRefScriptText(wLog, scOriginLogs);
                            if (refScriptText != null)
                            {         // Referenced scripts
                                _w.Write("  ");
                                _w.WriteLine(refScriptText);
                            }
                        }
                        _w.WriteLine();
                    }

                    _w.WriteLine();
                }

                // Script
                LogModel.Script[] scripts = _db.Table <LogModel.Script>()
                                            .Where(x => x.BuildId == buildId)
                                            .ToArray();

                // Script - Processed Scripts
                LogModel.Script[] processedScripts = scripts
                                                     .Where(x => 0 < x.Order)
                                                     .OrderBy(x => x.Order)
                                                     .ToArray();
                int pathColumnPos;
                _w.WriteLine("<Scripts>");
                {
                    (string Title, string Elapsed, string Path)[] scriptStrs = new (string, string, string)[processedScripts.Length];
Пример #3
0
        public void ExportBuildLog(int buildId, BuildLogOptions opts)
        {
            switch (_exportType)
            {
                #region Text
            case LogExportType.Text:
            {
                LogModel.BuildInfo dbBuild = _db.Table <LogModel.BuildInfo>().First(x => x.Id == buildId);
                if (dbBuild.EndTime == DateTime.MinValue)
                {
                    dbBuild.EndTime = DateTime.UtcNow;
                }

                _w.WriteLine($"- PEBakery Build <{dbBuild.Name}> -");
                _w.WriteLine($"Started  at {dbBuild.StartTime.ToLocalTime().ToString("yyyy-MM-dd hh:mm:ss tt K", CultureInfo.InvariantCulture)}");
                _w.WriteLine($"Finished at {dbBuild.EndTime.ToLocalTime().ToString("yyyy-MM-dd hh:mm:ss tt K", CultureInfo.InvariantCulture)}");
                TimeSpan t = dbBuild.EndTime - dbBuild.StartTime;
                _w.WriteLine($"Took {t:h\\:mm\\:ss}");
                _w.WriteLine();
                _w.WriteLine();

                // Log Statistics
                _w.WriteLine("<Log Statistics>");
                var states = ((LogState[])Enum.GetValues(typeof(LogState))).Where(x => x != LogState.None && x != LogState.CriticalError);
                foreach (LogState state in states)
                {
                    int count = _db.Table <LogModel.BuildLog>().Count(x => x.BuildId == buildId && x.State == state);
                    _w.WriteLine($"{state.ToString().PadRight(9)}: {count}");
                }
                _w.WriteLine();
                _w.WriteLine();

                // Show ErrorLogs
                LogModel.BuildLog[] errors = _db.Table <LogModel.BuildLog>().Where(x => x.BuildId == buildId && x.State == LogState.Error).ToArray();
                if (0 < errors.Length)
                {
                    _w.WriteLine("<Errors>");

                    int[]             scLogIds = errors.Select(x => x.ScriptId).OrderBy(x => x).Distinct().ToArray();
                    LogModel.Script[] scLogs   = _db.Table <LogModel.Script>().Where(x => x.BuildId == buildId && scLogIds.Contains(x.Id)).ToArray();

                    int[]             refScLogIds = errors.Select(x => x.RefScriptId).OrderBy(x => x).Distinct().ToArray();
                    LogModel.Script[] refScLogs   = _db.Table <LogModel.Script>().Where(x => x.BuildId == buildId && refScLogIds.Contains(x.Id)).ToArray();
                    foreach (LogModel.Script scLog in scLogs)
                    {
                        LogModel.BuildLog[] eLogs = errors.Where(x => x.ScriptId == scLog.Id).ToArray();
                        if (eLogs.Length == 1)
                        {
                            _w.WriteLine($"- [{eLogs.Length}] Error in script [{scLog.Name}] ({scLog.TreePath})");
                        }
                        else
                        {
                            _w.WriteLine($"- [{eLogs.Length}] Errors in script [{scLog.Name}] ({scLog.TreePath})");
                        }

                        foreach (LogModel.BuildLog eLog in eLogs)
                        {
                            _w.WriteLine(eLog.Export(LogExportType.Text, false));
                            if (eLog.RefScriptId != 0 && eLog.RefScriptId != eLog.ScriptId)
                            {
                                _w.Write("  ");
                                _w.WriteLine(ExportRefScriptText(eLog, refScLogs));
                            }
                        }
                        _w.WriteLine();
                    }

                    _w.WriteLine();
                }

                // Show WarnLogs
                LogModel.BuildLog[] warns = _db.Table <LogModel.BuildLog>().Where(x => x.BuildId == buildId && x.State == LogState.Warning).ToArray();
                if (0 < warns.Length)
                {
                    _w.WriteLine("<Warnings>");

                    int[]             scLogIds = warns.Select(x => x.ScriptId).OrderBy(x => x).Distinct().ToArray();
                    LogModel.Script[] scLogs   = _db.Table <LogModel.Script>().Where(x => x.BuildId == buildId && scLogIds.Contains(x.Id)).ToArray();

                    int[]             refScLogIds = warns.Select(x => x.RefScriptId).OrderBy(x => x).Distinct().ToArray();
                    LogModel.Script[] refScLogs   = _db.Table <LogModel.Script>().Where(x => x.BuildId == buildId && refScLogIds.Contains(x.Id)).ToArray();

                    foreach (LogModel.Script scLog in scLogs)
                    {
                        LogModel.BuildLog[] wLogs = warns.Where(x => x.ScriptId == scLog.Id).ToArray();
                        Debug.Assert(0 < wLogs.Length);

                        if (wLogs.Length == 1)
                        {
                            _w.WriteLine($"- [{wLogs.Length}] Warning in script [{scLog.Name}] ({scLog.TreePath})");
                        }
                        else
                        {
                            _w.WriteLine($"- [{wLogs.Length}] Warnings in script [{scLog.Name}] ({scLog.TreePath})");
                        }

                        foreach (LogModel.BuildLog wLog in wLogs)
                        {
                            _w.WriteLine(wLog.Export(LogExportType.Text, false));
                            if (wLog.RefScriptId != 0 && wLog.RefScriptId != wLog.ScriptId)
                            {
                                _w.Write("  ");
                                _w.WriteLine(ExportRefScriptText(wLog, refScLogs));
                            }
                        }
                        _w.WriteLine();
                    }

                    _w.WriteLine();
                }

                // Script
                LogModel.Script[] scripts = _db.Table <LogModel.Script>()
                                            .Where(x => x.BuildId == buildId)
                                            .ToArray();

                LogModel.Script[] processedScripts = scripts
                                                     .Where(x => 0 < x.Order)
                                                     .OrderBy(x => x.Order)
                                                     .ToArray();
                _w.WriteLine("<Scripts>");
                {
                    int count = processedScripts.Length;
                    int idx   = 1;
                    foreach (LogModel.Script sc in processedScripts)
                    {
                        _w.WriteLine($"[{idx}/{count}] {sc.Name} v{sc.Version} ({sc.ElapsedMilliSec / 1000.0:0.000}s)");
                        idx++;
                    }

                    _w.WriteLine($"Total {count} Scripts");
                    _w.WriteLine();
                    _w.WriteLine();
                }

                // Variables
                _w.WriteLine("<Variables>");
                VarsType[] typeList = { VarsType.Fixed, VarsType.Global };
                foreach (VarsType varsType in typeList)
                {
                    _w.WriteLine($"- {varsType} Variables");
                    var vars = _db.Table <LogModel.Variable>()
                               .Where(x => x.BuildId == buildId && x.Type == varsType)
                               .OrderBy(x => x.Key);
                    foreach (LogModel.Variable log in vars)
                    {
                        _w.WriteLine($"%{log.Key}% = {log.Value}");
                    }
                    _w.WriteLine();
                }
                _w.WriteLine();

                // Code Logs
                _w.WriteLine("<Code Logs>");
                {
                    foreach (LogModel.Script scLog in processedScripts)
                    {
                        // Log codes
                        var cLogs = _db.Table <LogModel.BuildLog>()
                                    .Where(x => x.BuildId == buildId && x.ScriptId == scLog.Id);
                        if (!opts.IncludeComments)
                        {
                            cLogs = cLogs.Where(x => (x.Flags & LogModel.BuildLogFlag.Comment) != LogModel.BuildLogFlag.Comment);
                        }
                        if (!opts.IncludeMacros)
                        {
                            cLogs = cLogs.Where(x => (x.Flags & LogModel.BuildLogFlag.Macro) != LogModel.BuildLogFlag.Macro);
                        }
                        cLogs = cLogs.OrderBy(x => x.Id);
                        foreach (LogModel.BuildLog log in cLogs)
                        {
                            _w.WriteLine(log.Export(LogExportType.Text, true));
                        }

                        // Log local variables
                        var vLogs = _db.Table <LogModel.Variable>()
                                    .Where(x => x.BuildId == buildId && x.ScriptId == scLog.Id && x.Type == VarsType.Local)
                                    .OrderBy(x => x.Key);
                        if (vLogs.Any())
                        {
                            _w.WriteLine($"- Local Variables of Script [{scLog.Name}]");
                            foreach (LogModel.Variable vLog in vLogs)
                            {
                                _w.WriteLine($"%{vLog.Key}% = {vLog.Value}");
                            }
                            _w.WriteLine(Logger.LogSeparator);
                        }

                        _w.WriteLine();
                    }
                }
            }
            break;

                #endregion
                #region HTML
            case LogExportType.Html:
            {
                LogModel.BuildInfo dbBuild = _db.Table <LogModel.BuildInfo>().First(x => x.Id == buildId);
                if (dbBuild.EndTime == DateTime.MinValue)
                {
                    dbBuild.EndTime = DateTime.UtcNow;
                }
                ExportBuildLogHtmlModel m = new ExportBuildLogHtmlModel
                {
                    PEBakeryVersion   = Global.Const.StringVersionFull,
                    BuildName         = dbBuild.Name,
                    BuildStartTimeStr = dbBuild.StartTime.ToLocalTime().ToString("yyyy-MM-dd h:mm:ss tt K", CultureInfo.InvariantCulture),
                    BuildEndTimeStr   = dbBuild.EndTime.ToLocalTime().ToString("yyyy-MM-dd h:mm:ss tt K", CultureInfo.InvariantCulture),
                    BuildTookTimeStr  = $"{dbBuild.EndTime - dbBuild.StartTime:h\\:mm\\:ss}",
                    LogStats          = new List <LogStatHtmlModel>(),
                };

                // Log Statistics
                var states = ((LogState[])Enum.GetValues(typeof(LogState))).Where(x => x != LogState.None && x != LogState.CriticalError);
                foreach (LogState state in states)
                {
                    int count = _db.Table <LogModel.BuildLog>().Count(x => x.BuildId == buildId && x.State == state);

                    m.LogStats.Add(new LogStatHtmlModel
                        {
                            State = state,
                            Count = count,
                        });
                }

                // Show ErrorLogs
                m.ErrorCodeDicts = new Dictionary <ScriptHtmlModel, Tuple <CodeLogHtmlModel, string>[]>();
                {
                    int errIdx = 0;
                    LogModel.BuildLog[] errors = _db.Table <LogModel.BuildLog>().Where(x => x.BuildId == buildId && x.State == LogState.Error).ToArray();
                    if (0 < errors.Length)
                    {
                        int[]             pLogIds = errors.Select(x => x.ScriptId).Distinct().ToArray();
                        LogModel.Script[] scLogs  = _db.Table <LogModel.Script>().Where(x => x.BuildId == buildId && pLogIds.Contains(x.Id)).ToArray();

                        int[]             refScLogIds = errors.Select(x => x.RefScriptId).OrderBy(x => x).Distinct().ToArray();
                        LogModel.Script[] refScLogs   = _db.Table <LogModel.Script>().Where(x => x.BuildId == buildId && refScLogIds.Contains(x.Id)).ToArray();

                        foreach (LogModel.Script scLog in scLogs)
                        {
                            ScriptHtmlModel scModel = new ScriptHtmlModel
                            {
                                Name = scLog.Name,
                                Path = scLog.TreePath,
                            };

                            m.ErrorCodeDicts[scModel] = errors
                                                        .Where(x => x.ScriptId == scLog.Id)
                                                        .Select(x => new Tuple <CodeLogHtmlModel, string>(
                                                                    new CodeLogHtmlModel
                                {
                                    State   = x.State,
                                    Message = x.Export(LogExportType.Html, false),
                                    Href    = errIdx++,
                                }, ExportRefScriptText(x, refScLogs))).ToArray();
                        }
                    }
                }

                // Show WarnLogs
                m.WarnCodeDicts = new Dictionary <ScriptHtmlModel, Tuple <CodeLogHtmlModel, string>[]>();
                {
                    int warnIdx = 0;
                    LogModel.BuildLog[] warns = _db.Table <LogModel.BuildLog>().Where(x => x.BuildId == buildId && x.State == LogState.Warning).ToArray();
                    if (0 < warns.Length)
                    {
                        int[]             pLogIds = warns.Select(x => x.ScriptId).Distinct().ToArray();
                        LogModel.Script[] scLogs  = _db.Table <LogModel.Script>().Where(x => x.BuildId == buildId && pLogIds.Contains(x.Id)).ToArray();

                        int[]             refScLogIds = warns.Select(x => x.RefScriptId).OrderBy(x => x).Distinct().ToArray();
                        LogModel.Script[] refScLogs   = _db.Table <LogModel.Script>().Where(x => x.BuildId == buildId && refScLogIds.Contains(x.Id)).ToArray();

                        foreach (LogModel.Script scLog in scLogs)
                        {
                            ScriptHtmlModel pModel = new ScriptHtmlModel
                            {
                                Name = scLog.Name,
                                Path = scLog.TreePath,
                            };
                            m.WarnCodeDicts[pModel] = warns
                                                      .Where(x => x.ScriptId == scLog.Id)
                                                      .Select(x => new Tuple <CodeLogHtmlModel, string>(
                                                                  new CodeLogHtmlModel
                                {
                                    State   = x.State,
                                    Message = x.Export(LogExportType.Html, false),
                                    Href    = warnIdx++,
                                }, ExportRefScriptText(x, refScLogs))).ToArray();
                        }
                    }
                }

                // Scripts
                var scripts = _db.Table <LogModel.Script>()
                              .Where(x => x.BuildId == buildId && 0 < x.Order)
                              .OrderBy(x => x.Order);
                m.Scripts = new List <ScriptHtmlModel>();
                {
                    int idx = 1;
                    foreach (LogModel.Script scLog in scripts)
                    {
                        m.Scripts.Add(new ScriptHtmlModel
                            {
                                Index   = idx,
                                Name    = scLog.Name,
                                Path    = scLog.TreePath,
                                Version = $"v{scLog.Version}",
                                TimeStr = $"{scLog.ElapsedMilliSec / 1000.0:0.000}s",
                            });
                        idx++;
                    }
                }

                // Variables
                m.Vars = new List <VarHtmlModel>();
                {
                    var vars = _db.Table <LogModel.Variable>()
                               .Where(x => x.BuildId == buildId && (x.Type == VarsType.Fixed || x.Type == VarsType.Global))
                               .OrderBy(x => x.Type)
                               .ThenBy(x => x.Key);
                    foreach (LogModel.Variable vLog in vars)
                    {
                        m.Vars.Add(new VarHtmlModel
                            {
                                Type  = vLog.Type,
                                Key   = vLog.Key,
                                Value = vLog.Value,
                            });
                    }
                }

                // CodeLogs
                m.CodeLogs = new List <Tuple <ScriptHtmlModel, CodeLogHtmlModel[], VarHtmlModel[]> >();
                {
                    int pIdx    = 0;
                    int errIdx  = 0;
                    int warnIdx = 0;

                    foreach (LogModel.Script scLog in scripts)
                    {
                        pIdx += 1;

                        // Log codes
                        var cLogs = _db.Table <LogModel.BuildLog>().Where(x => x.BuildId == buildId && x.ScriptId == scLog.Id);
                        if (!opts.IncludeComments)
                        {
                            cLogs = cLogs.Where(x => (x.Flags & LogModel.BuildLogFlag.Comment) != LogModel.BuildLogFlag.Comment);
                        }
                        if (!opts.IncludeMacros)
                        {
                            cLogs = cLogs.Where(x => (x.Flags & LogModel.BuildLogFlag.Macro) != LogModel.BuildLogFlag.Macro);
                        }
                        LogModel.BuildLog[] codeLogs = cLogs.OrderBy(x => x.Id).OrderBy(x => x.Id).ToArray();

                        ScriptHtmlModel pModel = new ScriptHtmlModel
                        {
                            Index = pIdx,
                            Name  = scLog.Name,
                            Path  = scLog.TreePath,
                        };

                        List <CodeLogHtmlModel> logModel = new List <CodeLogHtmlModel>(codeLogs.Length);
                        foreach (LogModel.BuildLog log in codeLogs)
                        {
                            CodeLogHtmlModel item = new CodeLogHtmlModel
                            {
                                State   = log.State,
                                Message = log.Export(LogExportType.Html, true),
                            };

                            if (log.State == LogState.Error)
                            {
                                item.Href = errIdx++;
                            }
                            else if (log.State == LogState.Warning)
                            {
                                item.Href = warnIdx++;
                            }

                            logModel.Add(item);
                        }

                        // Log local variables
                        VarHtmlModel[] localVarModel = _db.Table <LogModel.Variable>()
                                                       .Where(x => x.BuildId == buildId && x.ScriptId == scLog.Id && x.Type == VarsType.Local)
                                                       .OrderBy(x => x.Key)
                                                       .Select(x => new VarHtmlModel
                            {
                                Type  = x.Type,
                                Key   = x.Key,
                                Value = x.Value,
                            }).ToArray();

                        m.CodeLogs.Add(new Tuple <ScriptHtmlModel, CodeLogHtmlModel[], VarHtmlModel[]>(pModel, logModel.ToArray(), localVarModel));
                    }
                }

                string html = RazorEngine.Engine.Razor.RunCompile(Properties.Resources.BuildLogHtmlTemplate, "BuildLogHtmlTemplateKey", null, m);
                _w.WriteLine(html);
            }
            break;
                #endregion
            }
        }