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

145 lines
4.5 KiB

6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
  1. using Ionic.Zlib;
  2. using System;
  3. using System.IO;
  4. using System.Runtime.InteropServices;
  5. using System.Text;
  6. using System.Text.RegularExpressions;
  7. namespace IPA.Logging.Printers
  8. {
  9. /// <summary>
  10. /// A <see cref="LogPrinter"/> abstract class that provides the utilities to write to a GZip file.
  11. /// </summary>
  12. public abstract class GZFilePrinter : LogPrinter, IDisposable
  13. {
  14. [DllImport("Kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
  15. private static extern bool CreateHardLink(
  16. string lpFileName,
  17. string lpExistingFileName,
  18. IntPtr lpSecurityAttributes
  19. );
  20. internal static Regex removeControlCodes = new Regex("\x1b\\[\\d+m", RegexOptions.Compiled);
  21. private FileInfo fileInfo;
  22. /// <summary>
  23. /// The <see cref="StreamWriter"/> that writes to the GZip file.
  24. /// </summary>
  25. protected StreamWriter FileWriter;
  26. private FileStream fstream;
  27. /// <summary>
  28. /// Gets the <see cref="FileInfo"/> for the file to write to without the .gz extension.
  29. /// </summary>
  30. /// <returns></returns>
  31. protected abstract FileInfo GetFileInfo();
  32. private const string latestFormat = "_latest{0}";
  33. private void InitLog()
  34. {
  35. try
  36. {
  37. if (fileInfo == null)
  38. { // first init
  39. fileInfo = GetFileInfo();
  40. var ext = fileInfo.Extension;
  41. var symlink = new FileInfo(Path.Combine(fileInfo.DirectoryName ?? throw new InvalidOperationException(), string.Format(latestFormat, ext)));
  42. if (symlink.Exists) symlink.Delete();
  43. foreach (var file in fileInfo.Directory.EnumerateFiles("*.log", SearchOption.TopDirectoryOnly))
  44. {
  45. if (file.Extension == ".gz") continue;
  46. CompressOldLog(file);
  47. }
  48. fileInfo.Create().Close();
  49. try
  50. {
  51. if (!CreateHardLink(symlink.FullName, fileInfo.FullName, IntPtr.Zero))
  52. {
  53. var error = Marshal.GetLastWin32Error();
  54. Logger.log.Error($"Hardlink creation failed ({error})");
  55. }
  56. }
  57. catch (Exception e)
  58. {
  59. Logger.log.Error("Error creating latest hardlink!");
  60. Logger.log.Error(e);
  61. }
  62. }
  63. }
  64. catch (Exception e)
  65. {
  66. Logger.log.Error("Error initializing log!");
  67. Logger.log.Error(e);
  68. }
  69. }
  70. private static async void CompressOldLog(FileInfo file)
  71. {
  72. Logger.log.Debug($"Compressing log file {file}");
  73. var newFile = new FileInfo(file.FullName + ".gz");
  74. using (var istream = file.OpenRead())
  75. using (var ostream = newFile.Create())
  76. using (var gz = new GZipStream(ostream, CompressionMode.Compress, CompressionLevel.BestCompression, false))
  77. await istream.CopyToAsync(gz);
  78. file.Delete();
  79. }
  80. /// <summary>
  81. /// Called at the start of any print session.
  82. /// </summary>
  83. public sealed override void StartPrint()
  84. {
  85. InitLog();
  86. fstream = fileInfo.Open(FileMode.Append, FileAccess.Write);
  87. FileWriter = new StreamWriter(fstream, new UTF8Encoding(false));
  88. }
  89. /// <summary>
  90. /// Called at the end of any print session.
  91. /// </summary>
  92. public sealed override void EndPrint()
  93. {
  94. FileWriter.Flush();
  95. fstream.Flush();
  96. FileWriter.Dispose();
  97. fstream.Dispose();
  98. FileWriter = null;
  99. fstream = null;
  100. }
  101. /// <inheritdoc />
  102. public void Dispose()
  103. {
  104. Dispose(true);
  105. GC.SuppressFinalize(this);
  106. }
  107. /// <summary>
  108. /// Disposes the file printer.
  109. /// </summary>
  110. /// <param name="disposing">does nothing</param>
  111. protected virtual void Dispose(bool disposing)
  112. {
  113. if (disposing)
  114. {
  115. FileWriter.Flush();
  116. fstream.Flush();
  117. FileWriter.Close();
  118. fstream.Close();
  119. FileWriter.Dispose();
  120. fstream.Dispose();
  121. }
  122. }
  123. }
  124. }