You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

LogForm.cs 16 kB

9 years ago
10 years ago
10 years ago
9 years ago
9 years ago
10 years ago
10 years ago
9 years ago
10 years ago
10 years ago
9 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
5 years ago
5 years ago
5 years ago
10 years ago
9 years ago
9 years ago
10 years ago
5 years ago
10 years ago
9 years ago
10 years ago
5 years ago
10 years ago
10 years ago
10 years ago
10 years ago
5 years ago
10 years ago
5 years ago
9 years ago
5 years ago
5 years ago
9 years ago
5 years ago
5 years ago
5 years ago
5 years ago
10 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447
  1. using System;
  2. using System.Drawing;
  3. using System.IO;
  4. using System.Windows.Forms;
  5. using System.Windows.Forms.DataVisualization.Charting;
  6. using System.Collections.Generic;
  7. using System.Linq;
  8. using Shadowsocks.Controller;
  9. using Shadowsocks.Properties;
  10. using Shadowsocks.Model;
  11. using Shadowsocks.Util;
  12. using System.Text;
  13. using NLog;
  14. namespace Shadowsocks.View
  15. {
  16. public partial class LogForm : Form
  17. {
  18. private static Logger logger = LogManager.GetCurrentClassLogger();
  19. long lastOffset;
  20. string filename;
  21. Timer timer;
  22. const int BACK_OFFSET = 65536;
  23. ShadowsocksController controller;
  24. // global traffic update lock, make it static
  25. private static readonly object _lock = new object();
  26. #region Traffic Chart
  27. Queue<TrafficInfo> trafficInfoQueue = new Queue<TrafficInfo>();
  28. const int queueMaxLength = 60;
  29. long lastInbound, lastOutbound;
  30. long maxSpeed = 0, lastMaxSpeed = 0;
  31. const long minScale = 50;
  32. BandwidthScaleInfo bandwidthScale;
  33. List<float> inboundPoints = new List<float>();
  34. List<float> outboundPoints = new List<float>();
  35. TextAnnotation inboundAnnotation = new TextAnnotation();
  36. TextAnnotation outboundAnnotation = new TextAnnotation();
  37. #endregion
  38. public LogForm(ShadowsocksController controller)
  39. {
  40. this.controller = controller;
  41. InitializeComponent();
  42. Icon = Icon.FromHandle(Resources.ssw128.GetHicon());
  43. var nLogConfig = NLogConfig.LoadXML();
  44. try
  45. {
  46. this.filename = nLogConfig.GetLogFileName();
  47. }
  48. catch(Exception)
  49. {
  50. // failed to get the file name
  51. }
  52. if (string.IsNullOrEmpty(this.filename))
  53. {
  54. LogMessageTextBox.AppendText("Cannot get the log file name from NLog config file. Please check if the nlog config file exists with corresponding XML nodes.");
  55. }
  56. LogViewerConfig config = controller.GetCurrentConfiguration().logViewer;
  57. topMostTrigger = config.topMost;
  58. wrapTextTrigger = config.wrapText;
  59. toolbarTrigger = config.toolbarShown;
  60. LogMessageTextBox.BackColor = config.BackgroundColor;
  61. LogMessageTextBox.ForeColor = config.TextColor;
  62. LogMessageTextBox.Font = config.Font;
  63. controller.TrafficChanged += controller_TrafficChanged;
  64. UpdateTexts();
  65. }
  66. private void UpdateTrafficChart()
  67. {
  68. lock (_lock)
  69. {
  70. if (trafficInfoQueue.Count == 0)
  71. return;
  72. inboundPoints.Clear();
  73. outboundPoints.Clear();
  74. maxSpeed = 0;
  75. foreach (var trafficInfo in trafficInfoQueue)
  76. {
  77. inboundPoints.Add(trafficInfo.inbound);
  78. outboundPoints.Add(trafficInfo.outbound);
  79. maxSpeed = Math.Max(maxSpeed, Math.Max(trafficInfo.inbound, trafficInfo.outbound));
  80. }
  81. lastInbound = trafficInfoQueue.Last().inbound;
  82. lastOutbound = trafficInfoQueue.Last().outbound;
  83. }
  84. if (maxSpeed > 0)
  85. {
  86. lastMaxSpeed -= lastMaxSpeed / 32;
  87. maxSpeed = Math.Max(minScale, Math.Max(maxSpeed, lastMaxSpeed));
  88. lastMaxSpeed = maxSpeed;
  89. }
  90. else
  91. {
  92. maxSpeed = lastMaxSpeed = minScale;
  93. }
  94. bandwidthScale = Utils.GetBandwidthScale(maxSpeed);
  95. // re-scale the original data points, since it is List<float>, .ForEach does not work
  96. inboundPoints = inboundPoints.Select(p => p / bandwidthScale.unit).ToList();
  97. outboundPoints = outboundPoints.Select(p => p / bandwidthScale.unit).ToList();
  98. if (trafficChart.IsHandleCreated)
  99. {
  100. trafficChart.Series["Inbound"].Points.DataBindY(inboundPoints);
  101. trafficChart.Series["Outbound"].Points.DataBindY(outboundPoints);
  102. trafficChart.ChartAreas[0].AxisY.LabelStyle.Format = "{0:0.##} " + bandwidthScale.unitName;
  103. trafficChart.ChartAreas[0].AxisY.Maximum = bandwidthScale.value;
  104. inboundAnnotation.AnchorDataPoint = trafficChart.Series["Inbound"].Points.Last();
  105. inboundAnnotation.Text = Utils.FormatBandwidth(lastInbound);
  106. outboundAnnotation.AnchorDataPoint = trafficChart.Series["Outbound"].Points.Last();
  107. outboundAnnotation.Text = Utils.FormatBandwidth(lastOutbound);
  108. trafficChart.Annotations.Clear();
  109. trafficChart.Annotations.Add(inboundAnnotation);
  110. trafficChart.Annotations.Add(outboundAnnotation);
  111. }
  112. }
  113. private void controller_TrafficChanged(object sender, EventArgs e)
  114. {
  115. lock (_lock)
  116. {
  117. if (trafficInfoQueue.Count == 0)
  118. {
  119. // Init an empty queue
  120. for (int i = 0; i < queueMaxLength; i++)
  121. {
  122. trafficInfoQueue.Enqueue(new TrafficInfo(0, 0));
  123. }
  124. foreach (var trafficPerSecond in controller.trafficPerSecondQueue)
  125. {
  126. trafficInfoQueue.Enqueue(new TrafficInfo(trafficPerSecond.inboundIncreasement,
  127. trafficPerSecond.outboundIncreasement));
  128. if (trafficInfoQueue.Count > queueMaxLength)
  129. trafficInfoQueue.Dequeue();
  130. }
  131. }
  132. else
  133. {
  134. var lastTraffic = controller.trafficPerSecondQueue.Last();
  135. trafficInfoQueue.Enqueue(new TrafficInfo(lastTraffic.inboundIncreasement,
  136. lastTraffic.outboundIncreasement));
  137. if (trafficInfoQueue.Count > queueMaxLength)
  138. trafficInfoQueue.Dequeue();
  139. }
  140. }
  141. }
  142. private void UpdateTexts()
  143. {
  144. I18N.TranslateForm(this);
  145. trafficChart.Series["Inbound"].LegendText = I18N.GetString("Inbound");
  146. trafficChart.Series["Outbound"].LegendText = I18N.GetString("Outbound");
  147. }
  148. private void Timer_Tick(object sender, EventArgs e)
  149. {
  150. UpdateContent();
  151. UpdateTrafficChart();
  152. }
  153. private void InitContent()
  154. {
  155. if (string.IsNullOrEmpty(filename) || !File.Exists(filename))
  156. return;
  157. using (StreamReader reader = new StreamReader(new FileStream(filename,
  158. FileMode.Open, FileAccess.Read, FileShare.ReadWrite)))
  159. {
  160. if (reader.BaseStream.Length > BACK_OFFSET)
  161. {
  162. reader.BaseStream.Seek(-BACK_OFFSET, SeekOrigin.End);
  163. reader.ReadLine();
  164. }
  165. string line = "";
  166. StringBuilder appendText = new StringBuilder(1024);
  167. while ((line = reader.ReadLine()) != null)
  168. appendText.AppendLine(line);
  169. LogMessageTextBox.AppendText(appendText.ToString());
  170. LogMessageTextBox.ScrollToCaret();
  171. lastOffset = reader.BaseStream.Position;
  172. }
  173. }
  174. private void UpdateContent()
  175. {
  176. this.Text = I18N.GetString("Log Viewer") +
  177. $" [in: {Utils.FormatBytes(controller.InboundCounter)}, out: {Utils.FormatBytes(controller.OutboundCounter)}]";
  178. if (string.IsNullOrEmpty(filename) || !File.Exists(filename))
  179. return;
  180. try
  181. {
  182. using (StreamReader reader = new StreamReader(new FileStream(filename,
  183. FileMode.Open, FileAccess.Read, FileShare.ReadWrite)))
  184. {
  185. reader.BaseStream.Seek(lastOffset, SeekOrigin.Begin);
  186. string line = "";
  187. bool changed = false;
  188. StringBuilder appendText = new StringBuilder(128);
  189. while ((line = reader.ReadLine()) != null)
  190. {
  191. changed = true;
  192. appendText.AppendLine(line);
  193. }
  194. if (changed)
  195. {
  196. LogMessageTextBox.AppendText(appendText.ToString());
  197. LogMessageTextBox.ScrollToCaret();
  198. }
  199. lastOffset = reader.BaseStream.Position;
  200. }
  201. }
  202. catch (FileNotFoundException)
  203. {
  204. }
  205. }
  206. private void LogForm_Load(object sender, EventArgs e)
  207. {
  208. InitContent();
  209. timer = new Timer();
  210. timer.Interval = 100;
  211. timer.Tick += Timer_Tick;
  212. timer.Start();
  213. LogViewerConfig config = controller.GetCurrentConfiguration().logViewer;
  214. Height = config.Height;
  215. Width = config.Width;
  216. Top = config.BestTop;
  217. Left = config.BestLeft;
  218. if (config.Maximized)
  219. {
  220. WindowState = FormWindowState.Maximized;
  221. }
  222. topMostTriggerLock = true;
  223. TopMost = TopMostToolStripMenuItem.Checked = TopMostCheckBox.Checked = topMostTrigger;
  224. topMostTriggerLock = false;
  225. wrapTextTriggerLock = true;
  226. LogMessageTextBox.WordWrap = WrapTextToolStripMenuItem.Checked = WrapTextCheckBox.Checked = wrapTextTrigger;
  227. wrapTextTriggerLock = false;
  228. ToolbarFlowLayoutPanel.Visible = ShowToolbarToolStripMenuItem.Checked = toolbarTrigger;
  229. }
  230. private void LogForm_FormClosing(object sender, FormClosingEventArgs e)
  231. {
  232. timer.Stop();
  233. controller.TrafficChanged -= controller_TrafficChanged;
  234. LogViewerConfig config = controller.GetCurrentConfiguration().logViewer;
  235. config.topMost = topMostTrigger;
  236. config.wrapText = wrapTextTrigger;
  237. config.toolbarShown = toolbarTrigger;
  238. config.Font = LogMessageTextBox.Font;
  239. config.BackgroundColor = LogMessageTextBox.BackColor;
  240. config.TextColor = LogMessageTextBox.ForeColor;
  241. if (WindowState != FormWindowState.Minimized && !(config.Maximized = WindowState == FormWindowState.Maximized))
  242. {
  243. config.Top = Top;
  244. config.Left = Left;
  245. config.Height = Height;
  246. config.Width = Width;
  247. }
  248. controller.SaveLogViewerConfig(config);
  249. }
  250. private void OpenLocationToolStripMenuItem_Click(object sender, EventArgs e)
  251. {
  252. string argument = "/select, \"" + filename + "\"";
  253. logger.Debug(argument);
  254. System.Diagnostics.Process.Start("explorer.exe", argument);
  255. }
  256. private void ExitToolStripMenuItem_Click(object sender, EventArgs e)
  257. {
  258. Close();
  259. }
  260. private void LogForm_Shown(object sender, EventArgs e)
  261. {
  262. LogMessageTextBox.ScrollToCaret();
  263. }
  264. #region Clean up the content in LogMessageTextBox.
  265. private void DoClearLogs()
  266. {
  267. try
  268. {
  269. File.Delete(filename);
  270. }
  271. catch { }
  272. lastOffset = 0;
  273. LogMessageTextBox.Clear();
  274. }
  275. private void ClearLogsToolStripMenuItem_Click(object sender, EventArgs e)
  276. {
  277. DoClearLogs();
  278. }
  279. private void ClearLogsButton_Click(object sender, EventArgs e)
  280. {
  281. DoClearLogs();
  282. }
  283. #endregion
  284. #region Change the font settings applied in LogMessageTextBox.
  285. private void DoChangeFont()
  286. {
  287. try
  288. {
  289. FontDialog fd = new FontDialog();
  290. fd.Font = LogMessageTextBox.Font;
  291. if (fd.ShowDialog() == DialogResult.OK)
  292. {
  293. LogMessageTextBox.Font = new Font(fd.Font.FontFamily, fd.Font.Size, fd.Font.Style);
  294. }
  295. }
  296. catch (Exception ex)
  297. {
  298. logger.LogUsefulException(ex);
  299. MessageBox.Show(ex.Message);
  300. }
  301. }
  302. private void ChangeFontToolStripMenuItem_Click(object sender, EventArgs e)
  303. {
  304. DoChangeFont();
  305. }
  306. private void ChangeFontButton_Click(object sender, EventArgs e)
  307. {
  308. DoChangeFont();
  309. }
  310. #endregion
  311. #region Trigger the log messages to wrapable, or not.
  312. bool wrapTextTrigger = false;
  313. bool wrapTextTriggerLock = false;
  314. private void TriggerWrapText()
  315. {
  316. wrapTextTriggerLock = true;
  317. wrapTextTrigger = !wrapTextTrigger;
  318. LogMessageTextBox.WordWrap = wrapTextTrigger;
  319. LogMessageTextBox.ScrollToCaret();
  320. WrapTextToolStripMenuItem.Checked = WrapTextCheckBox.Checked = wrapTextTrigger;
  321. wrapTextTriggerLock = false;
  322. }
  323. private void WrapTextToolStripMenuItem_Click(object sender, EventArgs e)
  324. {
  325. if (!wrapTextTriggerLock)
  326. {
  327. TriggerWrapText();
  328. }
  329. }
  330. private void WrapTextCheckBox_CheckedChanged(object sender, EventArgs e)
  331. {
  332. if (!wrapTextTriggerLock)
  333. {
  334. TriggerWrapText();
  335. }
  336. }
  337. #endregion
  338. #region Trigger the window to top most, or not.
  339. bool topMostTrigger = false;
  340. bool topMostTriggerLock = false;
  341. private void TriggerTopMost()
  342. {
  343. topMostTriggerLock = true;
  344. topMostTrigger = !topMostTrigger;
  345. TopMost = topMostTrigger;
  346. TopMostToolStripMenuItem.Checked = TopMostCheckBox.Checked = topMostTrigger;
  347. topMostTriggerLock = false;
  348. }
  349. private void TopMostCheckBox_CheckedChanged(object sender, EventArgs e)
  350. {
  351. if (!topMostTriggerLock)
  352. {
  353. TriggerTopMost();
  354. }
  355. }
  356. private void TopMostToolStripMenuItem_Click(object sender, EventArgs e)
  357. {
  358. if (!topMostTriggerLock)
  359. {
  360. TriggerTopMost();
  361. }
  362. }
  363. #endregion
  364. private bool toolbarTrigger = false;
  365. private void ShowToolbarToolStripMenuItem_Click(object sender, EventArgs e)
  366. {
  367. toolbarTrigger = !toolbarTrigger;
  368. ToolbarFlowLayoutPanel.Visible = toolbarTrigger;
  369. ShowToolbarToolStripMenuItem.Checked = toolbarTrigger;
  370. }
  371. private class TrafficInfo
  372. {
  373. public long inbound;
  374. public long outbound;
  375. public TrafficInfo(long inbound, long outbound)
  376. {
  377. this.inbound = inbound;
  378. this.outbound = outbound;
  379. }
  380. }
  381. }
  382. }