123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552 |
- using System;
- using System.Collections.Generic;
- using System.Drawing;
- using System.IO;
- using System.Linq;
- using System.Security.Permissions;
- using System.Text.RegularExpressions;
- using System.Threading.Tasks;
- using System.Windows.Forms;
- using VCommon.Logging.Viewer.Utility;
- namespace VCommon.Logging.Viewer
- {
- //TODO: 清除时移除文件监视,仅监视最后拖入对象(目录或文件)
- //TODO: 放出JSON可视化选项,作为一行
- public partial class MainForm : Form, IMessageFilter
- {
- private bool _mouseOnLvSummary;
- private FileSizeWatcher _fileWatcherSize;
- private readonly ColumnSorter _lstColumnSorter = new ColumnSorter();
- private readonly List<DetailForm> _detailforms = new List<DetailForm>();
- public MainForm()
- {
- InitializeComponent();
- var eIcon = IconExtractor.GetMainIcon();
- if (null != eIcon) Icon = eIcon;
- Application.AddMessageFilter(this);
- WebBrowserUtility.LoadWebBrowserResource(WbJson);
- LvSummary.ListViewItemSorter = _lstColumnSorter;
- }
- #pragma warning disable 4014
- public MainForm(string openDirectoryName) : this() => OpenFileTask(openDirectoryName, false);
- #pragma warning restore 4014
- private async Task OpenFileTask(string fileOrdirectoryName, bool openByClick = true)
- {
- if (File.Exists(fileOrdirectoryName))
- {
- var fileInfos = new[] { new FileInfo(fileOrdirectoryName) };
- EnableWatcherChanged(fileInfos);
- await AnalysisLogFile(fileInfos, watchFile: () => { CreateFileWatcher(fileOrdirectoryName); });
- }
- else
- {
- if (!openByClick)
- {
- await ProcessOpenDirectoryTask(fileOrdirectoryName);
- }
- else
- {
- FolderBrowserDialog folderBrowserDialog = new FolderBrowserDialog();
- if (!string.IsNullOrEmpty(fileOrdirectoryName) && Directory.Exists(fileOrdirectoryName))
- folderBrowserDialog.SelectedPath = fileOrdirectoryName;
- if (folderBrowserDialog.ShowDialog() == DialogResult.OK)
- {
- fileOrdirectoryName = folderBrowserDialog.SelectedPath.Trim();
- await ProcessOpenDirectoryTask(fileOrdirectoryName);
- }
- }
- }
- }
- private async Task ProcessOpenDirectoryTask(string directoryName)
- {
- List<FileInfo> flieInfos = new List<FileInfo>();
- OperatingFileAssistant.GetAllFilesByDirectory(directoryName, flieInfos, "*.log");
- var fileInfos = flieInfos.ToArray();
- EnableWatcherChanged(fileInfos);
- await AnalysisLogFile(fileInfos, watchFile: () => { CreateFileWatcher(directoryName); });
- }
- private void btnSelectFile_Click(object sender, EventArgs e) { }
- /// <summary>
- /// 分析日志
- /// </summary>
- /// <param name="fileInfos">文件信息</param>
- /// <param name="seekBeginPosition">读取起始位置</param>
- /// <param name="clearListView">是否清除ListView</param>
- /// <param name="watchFile">监控文件委托</param>
- /// <param name="removeContent">是否是删除内容</param>
- /// <exception cref="ArgumentException">文件信息不能为空</exception>
- /// <returns></returns>
- private async Task AnalysisLogFile(FileInfo[] fileInfos, long seekBeginPosition = 0, bool clearListView = true, Action watchFile = null, bool removeContent = false)
- {
- await Task.Run(async () =>
- {
- if (fileInfos == null || fileInfos.Length == 0) throw new ArgumentException(nameof(fileInfos));
- if (clearListView)
- {
- LvSummary.Invoke(new Action(() =>
- {
- LvSummary.Items.Clear();
- }));
- }
- var errorList = new List<string>();
- Dictionary<FileInfo, Task<LogEntity[]>> readFileResultTasks = new Dictionary<FileInfo, Task<LogEntity[]>>();
- foreach (var file in fileInfos)
- {
- var line = 0;
- try
- {
- var tasksReadTextAsync = OperatingFileAssistant.ReadTextAsync(file, str =>
- {
- List<LogEntity> logEntities = new List<LogEntity>();
- string[] logContent = str.Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries);
- var logEnumerator = logContent.GetEnumerator();
- while (logEnumerator.MoveNext())
- {
- line++;
- // ReSharper disable once PossibleNullReferenceException
- var currentContent = logEnumerator.Current.ToString();
- var matchCollection = Regex.Matches(currentContent, @"^(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d{3}) (.+? )(.+)$");
- if (matchCollection.Count > 0)
- {
- LogEntity logEntity = new LogEntity
- {
- WriteTime = $"{matchCollection[0].Groups[1].Value}",
- Level = matchCollection[0].Groups[2].Value,
- ThreadName = matchCollection[0].Groups[3].Value,
- FileName = file.Name,
- LineNumber = line
- };
- if (logEnumerator.MoveNext() == false) break;
- line++;
- logEntity.Summary = logEnumerator.Current.ToString();
- if (logEnumerator.MoveNext() == false) break;
- line++;
- logEntity.Detail = logEnumerator.Current.ToString();
- logEntities.Add(logEntity);
- }
- }
- return logEntities.ToArray();
- }, seekBeginPosition);
- readFileResultTasks.Add(file, tasksReadTextAsync);
- }
- catch (Exception ex)
- {
- errorList.Add(string.Format("于文件{1}行{2}解析错误:{0}", ex, file.Name, line));
- }
- }
- readFileResultTasks = readFileResultTasks.OrderByDescending(c => c.Key.LastWriteTime)
- .ToDictionary(p => p.Key, o => o.Value);
- await Task.WhenAll(readFileResultTasks.Select(c => c.Value).ToArray()).ConfigureAwait(false);
- //如果是移除,只需比较ListView数据,删除即可
- if (removeContent)
- {
- LvSummary.Invoke(new Action(() =>
- {
- var listViewItems = LvSummary.Items.Cast<ListViewItem>().Where(p => p.Text == fileInfos[0].Name).ToArray();
- var logEntities = readFileResultTasks.SelectMany(c => c.Value.Result).Select(c => c.WriteTime);
- var viewItems = listViewItems.Where(p => !logEntities.Contains(p.SubItems[1].Text)).ToArray();
- foreach (var item in viewItems)
- {
- LvSummary.Items.Remove(item);
- }
- }));
- }
- else
- {
- await Task.Factory.StartNew(() =>
- {
- List<ListViewItem> listViewItems = new List<ListViewItem>();
- foreach (var task in readFileResultTasks)
- {
- foreach (var logEntity in task.Value.Result.OrderBy(c => c.WriteTime))
- {
- ListViewItem listViewItem = new ListViewItem
- {
- Text = task.Key.Name,
- Tag = logEntity
- };
- listViewItem.SubItems.Add(logEntity.WriteTime);
- listViewItem.SubItems.Add(logEntity.Level);
- listViewItem.SubItems.Add(logEntity.ThreadName);
- listViewItem.SubItems.Add(logEntity.Summary);
- listViewItem.SubItems.Add(logEntity.Detail);
- listViewItems.Add(listViewItem);
- }
- }
- LvSummary.Invoke(new Action<List<ListViewItem>>(viewItems =>
- {
- LvSummary.Items.AddRange(viewItems.ToArray());
- LvSummary_ColumnClick(LvSummary, new ColumnClickEventArgs(1));
- LvSummary_ColumnClick(LvSummary, new ColumnClickEventArgs(1));
- }), listViewItems);
- }).ConfigureAwait(false);
- }
- watchFile?.Invoke();
- if (errorList.Any())
- {
- Invoke(new Action(() =>
- {
- new ErrorForm(string.Join(Environment.NewLine, errorList.ToArray())).Show();
- }));
- }
- });
- }
- private void btnFind_Click(object sender, EventArgs e)
- {
- var filter = BuildSearchFilter();
- if (filter == null) return;
- var beginIndex = LvSummary.SelectedItems.Count == 0 ? 0 : LvSummary.SelectedItems[0].Index + 1;
- for (int i = beginIndex; i < LvSummary.Items.Count; i++)
- {
- var item = LvSummary.Items[i];
- if (!filter(item.SubItems[4].Text)) continue;
- LvSummary.Items[i].Selected = true;
- LvSummary.Items[i].EnsureVisible();
- return;
- }
- toolTip.Show("没有数据", (IWin32Window)sender, Point.Empty, 5000);
- }
- private Func<string, bool> BuildSearchFilter()
- {
- var search = txtFind.Text;
- if (!string.IsNullOrEmpty(search))
- {
- if (!chkRegex.Checked)
- return s => s.Contains(search);
- var reg = new Regex(search, RegexOptions.Compiled);
- return s => reg.IsMatch(s);
- }
- if (LvSummary.SelectedItems.Count == 0)
- {
- balloon.Show("请输入要匹配的文本,或者选中某一个项", txtFind);
- balloon.Show("请输入要匹配的文本,或者选中某一个项", txtFind);
- return null;
- }
- search = LvSummary.SelectedItems[0].SubItems[4].Text;
- return s => s.Contains(search);
- }
- private void btnCount_Click(object sender, EventArgs e)
- {
- var filter = BuildSearchFilter();
- if (filter == null) return;
- var ret = LvSummary.Items.Cast<ListViewItem>().Count(p => filter(p.SubItems[4].Text)).ToString();
- toolTip.Show(ret, (IWin32Window)sender, Point.Empty, 5000);
- }
- private void btnHightlight_Click(object sender, EventArgs e)
- {
- var filter = BuildSearchFilter();
- if (filter == null) return;
- var items = LvSummary.Items.Cast<ListViewItem>().Where(p => filter(p.SubItems[4].Text));
- foreach (var item in items)
- {
- item.Font = new Font(item.Font, FontStyle.Bold);
- }
- }
- private void btnClearHightlight_Click(object sender, EventArgs e)
- {
- foreach (ListViewItem item in LvSummary.Items)
- {
- item.Font = new Font(item.Font, FontStyle.Regular);
- }
- }
- private void RemoveByMatchCondition_Click(object sender, EventArgs e)
- {
- if (LvSummary.SelectedItems.Count == 0)
- return;
- var remove = sender == btnRemove;
- Func<ListViewItem, bool> filter;
- if (chkRegex.Checked)
- {
- var reg = new Regex(txtFind.Text, RegexOptions.Compiled);
- filter = p => reg.IsMatch(p.SubItems[4].Text) == !remove;
- }
- else
- {
- var sel = LvSummary.SelectedItems[0].SubItems[4].Text;
- if (remove)
- {
- filter = p => p.SubItems[4].Text != sel;
- }
- else
- {
- filter = p => p.SubItems[4].Text == sel;
- }
- }
- var nks = LvSummary.Items.Cast<ListViewItem>().Where(filter).ToArray();
- LvSummary.Items.Clear();
- LvSummary.Items.AddRange(nks);
- }
- private void btnClear_Click(object sender, EventArgs e) => LvSummary.Items.Clear();
- private void btnCopyAll_Click(object sender, EventArgs e)
- {
- var logArray = LvSummary.Items.Cast<ListViewItem>().Select(p => p.Tag).ToArray();
- Clipboard.SetText(string.Join(Environment.NewLine, logArray));
- }
- private void btnExport_Click(object sender, EventArgs e)
- {
- var saveFileDialog = new SaveFileDialog
- {
- Filter = "文本文件|*.log",
- FileName = "Log.log"
- };
- if (saveFileDialog.ShowDialog() != DialogResult.OK) return;
- var logArray = LvSummary.Items.Cast<ListViewItem>().Select(p => p.Tag).ToArray();
- File.WriteAllText(saveFileDialog.FileName, string.Join(Environment.NewLine, logArray));
- }
- private void LvSummary_ColumnClick(object sender, ColumnClickEventArgs e)
- {
- var myListView = (ListView)sender;
- // Determine if clicked column is already the column that is being sorted.
- if (e.Column == _lstColumnSorter.SortColumn)
- {
- // Reverse the current sort direction for this column.
- _lstColumnSorter.Order =
- _lstColumnSorter.Order == SortOrder.Ascending
- ? SortOrder.Descending
- : SortOrder.Ascending;
- }
- else
- {
- // Set the column number that is to be sorted; default to ascending.
- _lstColumnSorter.SortColumn = e.Column;
- _lstColumnSorter.Order = SortOrder.Ascending;
- }
- // Perform the sort with these new sort options.
- myListView.Sort();
- myListView.SetSortIcon(_lstColumnSorter.SortColumn, _lstColumnSorter.Order);
- }
- private void Form1_Shown(object sender, EventArgs e)
- {
- LvSummary.Items.Clear();
- }
- private async void Form1_DragDrop(object sender, DragEventArgs e)
- {
- Enabled = false;
- SuspendLayout();
- try
- {
- var fileStrings = (string[])e.Data.GetData(DataFormats.FileDrop);
- if (fileStrings.Length == 0) return;
- var rootPath = string.Empty;
- var fileInfos = fileStrings.SelectMany(file =>
- {
- List<FileInfo> flies = new List<FileInfo>();
- if (string.IsNullOrEmpty(Path.GetExtension(file)))
- {
- if (rootPath == string.Empty) rootPath = file;
- OperatingFileAssistant.GetAllFilesByDirectory(file, flies, "*.log");
- return flies;
- }
- flies.Add(new FileInfo(file));
- if (rootPath == string.Empty) rootPath = Path.GetDirectoryName(file);
- return flies;
- }).ToArray();
- EnableWatcherChanged(fileInfos);
- await AnalysisLogFile(fileInfos, watchFile: () => { CreateFileWatcher(rootPath); });
- }
- finally
- {
- Invoke(new Action(delegate
- {
- ResumeLayout();
- Enabled = true;
- }));
- }
- }
- private void Form1_DragEnter(object sender, DragEventArgs e)
- {
- e.Effect = e.Data.GetDataPresent(DataFormats.FileDrop)
- ? DragDropEffects.Copy
- : DragDropEffects.None;
- }
- private void switchWindow_Click(object sender, EventArgs e)
- {
- if (splitContainer.Orientation == Orientation.Horizontal)
- {
- splitContainer.Orientation = Orientation.Vertical;
- splitContainer.SplitterDistance = Width / 2;
- }
- else
- {
- splitContainer.Orientation = Orientation.Horizontal;
- splitContainer.SplitterDistance = _panel1Height;
- }
- }
- private int _panel1Height;
- private void Form1_Load(object sender, EventArgs e)
- {
- _panel1Height = splitContainer.Panel1.Height;
- }
- private void LvSummary_Click(object sender, EventArgs e)
- {
- var listView = (ListView)sender;
- var selectedItem = listView.SelectedItems[0];
- WbJson.Document?.InvokeScript("GetOutputResult", new object[] { selectedItem.SubItems[5].Text });
- }
- #region 文件监控
- private FileSystemWatcher _watcher;
- [PermissionSet(SecurityAction.Demand, Name = "FullTrust")]
- private void CreateFileWatcher(string path)
- {
- _watcher = new FileSystemWatcher
- {
- Path = path,
- Filter = "*.log",
- IncludeSubdirectories = true,
- NotifyFilter = NotifyFilters.LastAccess | NotifyFilters.LastWrite
- | NotifyFilters.FileName | NotifyFilters.DirectoryName
- | NotifyFilters.CreationTime
- };
- _watcher.Deleted += _watcher_Deleted;
- _watcher.Created += _watcher_Created;
- _watcher.EnableRaisingEvents = true;
- }
- private void _watcher_Deleted(object sender, FileSystemEventArgs e)
- {
- _fileWatcherSize?.RemoveFileWatch(e.FullPath);
- RemoveListItemByFileName(e.Name);
- }
- private async void _watcher_Created(object sender, FileSystemEventArgs e)
- {
- _fileWatcherSize?.AddFileWatch(e.FullPath);
- await TaskExtend.Delay(500).ContinueWith(async task =>
- {
- await AnalysisLogFile(new[] { new FileInfo(e.FullPath) }, 0, false);
- });
- }
- public void EnableWatcherChanged(FileInfo[] fileInfos)
- {
- if (fileInfos == null || fileInfos.Length == 0)
- throw new ArgumentException(nameof(fileInfos));
- _fileWatcherSize = new FileSizeWatcher(fileInfos);
- _fileWatcherSize.Changed += FileWatcherSize_Changed;
- }
- private async void FileWatcherSize_Changed(FileSizeWatcherEventArgs e)
- {
- var fileInfo = new FileInfo(e.FullPath);
- bool isDeleted = false;
- long seekBeginPosition = 0;
- if (e.ChangeSizeType == ChangeSizeType.Increase)
- {
- seekBeginPosition = e.OriginalLength;
- }
- else
- {
- isDeleted = true;
- }
- await AnalysisLogFile(new[] { fileInfo }, seekBeginPosition, false, null, isDeleted);
- }
- private void RemoveListItemByFileName(string fileName)
- {
- Invoke(new Action(() =>
- {
- var logArray = LvSummary.Items.Cast<ListViewItem>().Where(p => ((LogEntity)p.Tag).FileName == fileName).ToArray();
- foreach (var removeItem in logArray)
- {
- LvSummary.Items.Remove(removeItem);
- }
- }));
- }
- #endregion 文件监控
- bool IMessageFilter.PreFilterMessage(ref Message m)
- {
- if (m.Msg == 0x20A) // WM_MOUSEWHEEL
- {
- if (_mouseOnLvSummary)
- {
- NativeMethods.SendMessage(LvSummary.Handle, 0x020A, m.WParam, m.LParam);
- }
- else
- {
- WebBrowserUtility.SendMouseWheel(m, WbJson);
- foreach (var form in _detailforms)
- {
- form.PreFilterMessage(ref m);
- }
- }
- return true;
- }
- return false;
- }
- private void LvSummary_MouseEnter(object sender, EventArgs e) => _mouseOnLvSummary = true;
- private void LvSummary_MouseLeave(object sender, EventArgs e) => _mouseOnLvSummary = false;
- private void LvSummary_MouseDoubleClick(object sender, MouseEventArgs e)
- {
- var listView = (ListView)sender;
- var detailForm = new DetailForm((LogEntity)listView.SelectedItems[0].Tag, form => _detailforms.Remove(form));
- _detailforms.Add(detailForm);
- if (splitContainer.Orientation == Orientation.Vertical)
- {
- detailForm.Left += Width / 2;
- detailForm.Top += Height - WbJson.Height;
- }
- detailForm.Show(this);
- }
- private void LvSummary_SelectedIndexChanged(object sender, EventArgs e)
- {
- var listView = (ListView)sender;
- if (listView.SelectedItems.Count == 0) return;
- var selectedItem = listView.SelectedItems[0];
- WbJson.Document?.InvokeScript("GetOutputResult", new object[] { selectedItem.SubItems[5].Text });
- }
- }
- }
|