/* Copyright (c) Microsoft Corporation All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABLITY OR NON-INFRINGEMENT. See the Apache Version 2.0 License for specific language governing permissions and limitations under the License. */ using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Windows.Forms; using Microsoft.Research.JobObjectModel; using Microsoft.Research.Tools; namespace Microsoft.Research.DryadAnalysis { /// /// A log viewer displays fragments of logs or other text files. /// public partial class LogViewer : Form { bool canceled; DGVData shownText; DGVData shownLogLines; StatusWriter status; /// /// A text file line is represented in this way. /// class TextFileLine { private long Line; private string Contents; public TextFileLine(long line, string contents) { this.Line = line; this.Contents = contents; } } /// /// True if the form has been closed (or work has been cancelled in some way). /// public bool Cancelled { get { return this.canceled; } } private void Initialize(bool textFile, string header) { this.Text = header; this.status = new StatusWriter(this.toolStripStatusLabel_status, this.statusStrip, this.Status); this.canceled = false; if (textFile) { this.shownText = new DGVData(); this.filteredDataGridView.SetDataSource(this.shownText); // ReSharper disable PossibleNullReferenceException this.filteredDataGridView.DataGridView.Columns["Line"].FillWeight = 5; this.filteredDataGridView.DataGridView.Columns["Line"].DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleRight; this.contextMenuStrip.Enabled = false; } else { this.shownLogLines = new DGVData(); this.filteredDataGridView.SetDataSource(this.shownLogLines); foreach (string s in new string[] { "Malformed", "IsError", "OriginalLogLine", "File", "LineNo" }) { this.filteredDataGridView.DataGridView.Columns[s].Visible = false; } this.filteredDataGridView.DataGridView.Columns["Timestamp"].DefaultCellStyle.Format = "HH:mm:ss.fff"; // ReSharper restore PossibleNullReferenceException } this.filteredDataGridView.DataGridView.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.DisplayedCellsExceptHeader; this.filteredDataGridView.DataGridView.AllowUserToResizeColumns = true; this.filteredDataGridView.DataGridView.Columns[this.filteredDataGridView.DataGridView.Columns.Count - 1].AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill; } /// /// Create a new log viewer. /// /// If true the viewer will display a text file. /// Information to display in the window header. public LogViewer(bool textFile, string header) { this.InitializeComponent(); this.Initialize(textFile, header); } /// /// Create a new log viewer to display a file. /// public LogViewer() { this.InitializeComponent(); } /// /// Load a specified file. /// /// File to load. public void LoadFile(IClusterResidentObject file) { string filename = file.Name; bool text = true; string basefilename = Path.GetFileName(filename); if (basefilename != null && (basefilename.EndsWith(".log") && basefilename.StartsWith("cosmos"))) text = false; long len = file.Size; this.Initialize(text, basefilename); //ISharedStreamReader sr = new FileSharedStreamReader(filename); ISharedStreamReader sr = file.GetStream(); long lineno = 0; long bytes = 0; List toAddText = new List(); List toAddLog = new List(); while (!sr.EndOfStream) { string line = sr.ReadLine(); bytes += line.Length; if (this.shownText != null) toAddText.Add(new TextFileLine(lineno, line)); else { PositionedDryadLogEntry cle = new PositionedDryadLogEntry(filename, lineno, line); if (cle.Malformed) { Trace.TraceInformation("Malformed log entry: " + cle.OriginalLogLine); } else { toAddLog.Add(cle); } } if (lineno++ % 100 == 0 && len > 0) { this.UpdateProgress((int)(bytes * 100 / len)); } } if (this.shownText != null) this.shownText.SetItems(toAddText); else this.shownLogLines.SetItems(toAddLog); this.Status("Loaded " + lineno + " lines.", StatusKind.OK); this.UpdateProgress(100); sr.Close(); this.filteredDataGridView.DataGridView.AutoResizeColumns(DataGridViewAutoSizeColumnsMode.DisplayedCellsExceptHeader); } /// /// Display a status message. /// /// Message to display. /// Message severity. public void Status(string message, StatusKind statusKind) { if (this.canceled || this.IsDisposed) return; this.status.Status(message, statusKind); } /// /// Add a text line to the viewer. /// /// Line number to add. /// Text of the line. /// File that the information comes from. public void AddLine(string file, long lineno, string text) { if (this.canceled || this.IsDisposed) return; if (this.InvokeRequired) { var action = new Action( (lv, f, l, t) => lv.AddLine(f, l, t)); this.Invoke(action, this, file, lineno, text); } else { if (this.shownText != null) { TextFileLine tfl = new TextFileLine(lineno, text); this.shownText.AddItem(tfl); } else { PositionedDryadLogEntry cle = new PositionedDryadLogEntry(file, lineno, text); if (cle.Malformed) return; this.shownLogLines.AddItem(cle); } return; } } /// /// Update the progress bar. /// /// How much has been done. public void UpdateProgress(int percent) { if (this.InvokeRequired) { this.Invoke(new Action(this.UpdateProgress), percent); } else { if (this.canceled || this.IsDisposed) return; try { this.toolStripProgressBar.Value = percent; } catch { } } } /// /// Close the log viewer. /// /// Unused. /// Unused. private void closeToolStripMenuItem_Click(object sender, EventArgs e) { this.canceled = true; this.Close(); } /// /// We are done adding information. /// internal void Done() { if (this.canceled || this.IsDisposed) return; this.UpdateProgress(100); this.Status("OK", StatusKind.OK); } private void LogViewer_FormClosing(object sender, FormClosingEventArgs e) { this.canceled = true; } /// /// User wants to the the location of the message. /// /// Unused. /// Row selected. private void locationToolStripMenuItem_Click(object sender, EventArgs e) { string position = ""; var rows = this.filteredDataGridView.DataGridView.SelectedRows; for (int i = 0; i < rows.Count; i++) { PositionedDryadLogEntry entry = ((PositionedDryadLogEntry)rows[i].DataBoundItem); position += entry.File + ":" + entry.LineNo + Environment.NewLine; } MessageBox.Show(position, "File containing log entries"); } } /// /// A line from a file. /// /// Type of line representation. public class PositionedLine where T : IParse, new() { /// /// File containing the log entry. /// public string File { get; protected set; } /// /// Line number. /// public long LineNo { get; protected set; } /// /// Actual line from file. /// public T Line { get; protected set; } /// /// Create a positioned line. /// /// File containing the line. /// Line number in file. /// Actual line in file. public PositionedLine(string file, long lineno, string line) { this.File = file; this.LineNo = lineno; this.Line = new T(); this.Line.Parse(line); } } /// /// Cosmos log entry with position information. /// public class PositionedDryadLogEntry : DryadLogEntry { /// /// File containing the log entry. /// public string File { get; protected set; } /// /// Line number. /// public long LineNo { get; protected set; } /// /// Create a positioned log entry. /// /// File containing the log entry. /// Line number. /// Line contents. public PositionedDryadLogEntry(string file, long lineno, string line) : base(line) { this.File = file; this.LineNo = lineno; } } }