Guest User

Untitled

a guest
Oct 19th, 2018
86
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 17.65 KB | None | 0 0
  1. public class Logger {
  2. private static readonly System.Collections.Generic.Dictionary<string, Logger> Loggers = new System.Collections.Generic.Dictionary<string, Logger>();
  3.  
  4. private readonly string _name;
  5. private LoggingLevel _level;
  6. private readonly System.Collections.Generic.List<Filter> _filters;
  7. private readonly System.Collections.Generic.List<Handler> _handlers;
  8.  
  9. public static Logger GetLogger(string name = "root") {
  10. Logger value;
  11. if (Logger.Loggers.TryGetValue(name, out value)) {
  12. return value;
  13. }
  14. value = new Logger(name);
  15. Logger.Loggers.Add(name, value);
  16. return value;
  17. }
  18.  
  19. private Logger(string name) {
  20. this._name = name;
  21. this._level = LoggingLevel.Warning;
  22. this._filters = new System.Collections.Generic.List<Filter>();
  23. this._handlers = new System.Collections.Generic.List<Handler>();
  24. }
  25.  
  26. public void SetLevel(LoggingLevel level) {
  27. this._level = level;
  28. }
  29.  
  30. public void AddHandler(Handler handler) {
  31. this._handlers.Add(handler);
  32. }
  33.  
  34. public void RemoveHandler(Handler handler) {
  35. this._handlers.Remove(handler);
  36. }
  37.  
  38. public void AddFilter(Filter filter) {
  39. this._filters.Add(filter);
  40. }
  41.  
  42. public void RemoveFilter(Filter filter) {
  43. this._filters.Remove(filter);
  44. }
  45.  
  46. public void Debug(string message, [System.Runtime.CompilerServices.CallerFilePath] string filePath = "", [System.Runtime.CompilerServices.CallerLineNumber] int lineNumber = 0, [System.Runtime.CompilerServices.CallerMemberName] string memberName = "") {
  47. this.Log(LoggingLevel.Debug, message, null, filePath, lineNumber, memberName);
  48. }
  49.  
  50. public void Info(string message, [System.Runtime.CompilerServices.CallerFilePath] string filePath = "", [System.Runtime.CompilerServices.CallerLineNumber] int lineNumber = 0, [System.Runtime.CompilerServices.CallerMemberName] string memberName = "") {
  51. this.Log(LoggingLevel.Info, message, null, filePath, lineNumber, memberName);
  52. }
  53.  
  54. public void Warning(string message, [System.Runtime.CompilerServices.CallerFilePath] string filePath = "", [System.Runtime.CompilerServices.CallerLineNumber] int lineNumber = 0, [System.Runtime.CompilerServices.CallerMemberName] string memberName = "") {
  55. this.Log(LoggingLevel.Warning, message, null, filePath, lineNumber, memberName);
  56. }
  57.  
  58. public void Error(string message, [System.Runtime.CompilerServices.CallerFilePath] string filePath = "", [System.Runtime.CompilerServices.CallerLineNumber] int lineNumber = 0, [System.Runtime.CompilerServices.CallerMemberName] string memberName = "") {
  59. this.Log(LoggingLevel.Error, message, null, filePath, lineNumber, memberName);
  60. }
  61.  
  62. public void Critical(string message, [System.Runtime.CompilerServices.CallerFilePath] string filePath = "", [System.Runtime.CompilerServices.CallerLineNumber] int lineNumber = 0, [System.Runtime.CompilerServices.CallerMemberName] string memberName = "") {
  63. this.Log(LoggingLevel.Critical, message, null, filePath, lineNumber, memberName);
  64. }
  65.  
  66. public void Exception(Exception error, string message = "", [System.Runtime.CompilerServices.CallerFilePath] string filePath = "", [System.Runtime.CompilerServices.CallerLineNumber] int lineNumber = 0, [System.Runtime.CompilerServices.CallerMemberName] string memberName = "") {
  67. this.Log(LoggingLevel.Error, message, error, filePath, lineNumber, memberName);
  68. }
  69.  
  70. public void Log(LoggingLevel level, string message, Exception error = null, [System.Runtime.CompilerServices.CallerFilePath] string filePath = "", [System.Runtime.CompilerServices.CallerLineNumber] int lineNumber = 0, [System.Runtime.CompilerServices.CallerMemberName] string memberName = "") {
  71. if (this.IsEnabledFor(level)) {
  72. var record = new LogRecord(this._name, level, filePath, lineNumber, memberName, message, error);
  73. if (this._filters.All(x => x.Check(record))) {
  74. this._handlers.ForEach(x => x.Process(record));
  75. }
  76. }
  77. }
  78.  
  79. public bool IsEnabledFor(LoggingLevel level) {
  80. return level >= this._level;
  81. }
  82. }
  83.  
  84. public abstract class Handler {
  85. public static ExceptionSetting RaiseExceptions = ExceptionSetting.Write;
  86. private static readonly Formatter DefaultFormatter = new Formatter();
  87.  
  88. private LoggingLevel _level;
  89. private readonly System.Collections.Generic.List<Filter> _filters;
  90. private Formatter _formatter;
  91.  
  92. protected Handler() {
  93. this._level = LoggingLevel.Warning;
  94. this._filters = new System.Collections.Generic.List<Filter>();
  95. this._formatter = null;
  96. }
  97.  
  98. public void SetLevel(LoggingLevel level) {
  99. this._level = level;
  100. }
  101.  
  102. public void SetFormatter(Formatter formatter) {
  103. this._formatter = formatter;
  104. }
  105.  
  106. public void AddFilter(Filter filter) {
  107. this._filters.Add(filter);
  108. }
  109.  
  110. public void RemoveFilter(Filter filter) {
  111. this._filters.Remove(filter);
  112. }
  113.  
  114. public void Process(LogRecord record) {
  115. if (this.IsEnabledFor(record.Level) && this._filters.All(x => x.Check(record))) {
  116. lock (this) {
  117. this.Emit(record);
  118. }
  119. }
  120. }
  121.  
  122. protected abstract void Emit(LogRecord record);
  123.  
  124. public bool IsEnabledFor(LoggingLevel level) {
  125. return level >= this._level;
  126. }
  127.  
  128. private Formatter SafeFormatter => this._formatter ?? Handler.DefaultFormatter;
  129.  
  130. protected string Format(LogRecord record) {
  131. return this.SafeFormatter.Format(record);
  132. }
  133.  
  134. protected void HandleError(LogRecord record, Exception error) {
  135. if (Handler.RaiseExceptions != ExceptionSetting.Pass) {
  136. Console.Error.WriteLine("--- Logging error ---");
  137. Console.Error.WriteLine(error);
  138. Console.Error.WriteLine($"Logged from file {record.FilePath}, line {record.LineNumber}");
  139. Console.Error.WriteLine($"Message: {record.Message}");
  140. Console.Error.WriteLine($"Created: {this.SafeFormatter.FormatTime(record)}");
  141. if (Handler.RaiseExceptions == ExceptionSetting.Throw) {
  142. throw error;
  143. }
  144. }
  145. }
  146. }
  147.  
  148. public class StreamHandler : Handler {
  149. public static readonly string Terminator = Environment.NewLine;
  150.  
  151. protected System.IO.TextWriter Stream;
  152.  
  153. public StreamHandler(System.IO.TextWriter stream = null) {
  154. this.Stream = stream ?? Console.Error;
  155. }
  156.  
  157. public void Flush() {
  158. this.Stream.Flush();
  159. }
  160.  
  161. protected override void Emit(LogRecord record) {
  162. try {
  163. var message = this.Format(record);
  164. this.Stream.Write(message);
  165. this.Stream.Write(StreamHandler.Terminator);
  166. this.Flush();
  167. } catch (Exception error) {
  168. this.HandleError(record, error);
  169. }
  170. }
  171. }
  172.  
  173. public class FileHandler : StreamHandler {
  174. public static readonly System.Text.Encoding DefaultEncoding = System.Text.Encoding.GetEncoding("iso-8859-1");
  175.  
  176. protected readonly string FileName;
  177. private readonly System.IO.FileMode _mode;
  178. private readonly System.Text.Encoding _codec;
  179.  
  180. public FileHandler(string fileName, System.IO.FileMode mode, System.Text.Encoding codec = null) : base(
  181. new System.IO.StreamWriter(new System.IO.FileStream(fileName, mode), codec ?? FileHandler.DefaultEncoding)) {
  182. this.FileName = fileName;
  183. this._mode = mode;
  184. this._codec = codec;
  185. }
  186.  
  187. public void Close() {
  188. this.Flush();
  189. this.Stream.Close();
  190. }
  191.  
  192. protected System.IO.StreamWriter Open() {
  193. return new System.IO.StreamWriter(new System.IO.FileStream(this.FileName, this._mode), this._codec ?? FileHandler.DefaultEncoding);
  194. }
  195. }
  196.  
  197. public abstract class BaseRotatingHandler : FileHandler {
  198. protected Func<string, string> Namer;
  199. protected Action<string, string> Rotator;
  200.  
  201. protected BaseRotatingHandler(string fileName, System.IO.FileMode mode, System.Text.Encoding codec = null) : base(
  202. fileName, mode, codec) {
  203. this.Namer = null;
  204. this.Rotator = null;
  205. }
  206.  
  207. protected override void Emit(LogRecord record) {
  208. try {
  209. if (this.ShouldRollover(record)) {
  210. this.DoRollover();
  211. }
  212. base.Emit(record);
  213. } catch (Exception error) {
  214. this.HandleError(record, error);
  215. }
  216.  
  217. }
  218.  
  219. protected abstract bool ShouldRollover(LogRecord record);
  220.  
  221. protected abstract void DoRollover();
  222.  
  223. protected string GetRotationFilename(string defaultName) {
  224. return this.Namer == null ? defaultName : this.Namer(defaultName);
  225. }
  226.  
  227. protected void Rotate(string source, string destination) {
  228. if (this.Rotator == null) {
  229. System.IO.File.Move(source, destination);
  230. } else if (System.IO.File.Exists(source)) {
  231. this.Rotator(source, destination);
  232. }
  233. }
  234. }
  235.  
  236. public class RotatingFileHandler : BaseRotatingHandler {
  237. private readonly int _maxBytes;
  238. private readonly int _backupCount;
  239.  
  240. public RotatingFileHandler(string fileName, System.IO.FileMode mode, int maxBytes = 0, int backupCount = 0,
  241. System.Text.Encoding codec = null) : base(fileName, mode, codec) {
  242. this._maxBytes = maxBytes;
  243. this._backupCount = backupCount;
  244. }
  245.  
  246. protected override bool ShouldRollover(LogRecord record) {
  247. if (this._maxBytes <= 0) return false;
  248. var message = $"{this.Format(record)}{StreamHandler.Terminator}";
  249. return ((System.IO.StreamWriter) this.Stream).BaseStream.Position + message.Length >= this._maxBytes;
  250. }
  251.  
  252. protected override void DoRollover() {
  253. this.Close();
  254. if (this._backupCount > 0) {
  255. string destination;
  256. foreach (var i in Enumerable.Range(1, this._backupCount - 1).Reverse()) {
  257. var source = this.GetRotationFilename($"{this.FileName}.{i}");
  258. destination = this.GetRotationFilename($"{this.FileName}.{i + 1}");
  259. if (System.IO.File.Exists(source)) {
  260. if (System.IO.File.Exists(destination)) {
  261. System.IO.File.Delete(destination);
  262. }
  263. System.IO.File.Move(source, destination);
  264. }
  265. }
  266. destination = this.GetRotationFilename($"{this.FileName}.1");
  267. if (System.IO.File.Exists(destination)) {
  268. System.IO.File.Delete(destination);
  269. }
  270. this.Rotate(this.FileName, destination);
  271. }
  272. this.Stream = this.Open();
  273. }
  274. }
  275.  
  276. public class SmtpHandler : Handler {
  277. private readonly System.Net.DnsEndPoint _mailServer;
  278. private readonly string _fromAddress;
  279. private readonly System.Collections.Generic.List<string> _toAddress;
  280. private readonly string _subject;
  281. private readonly int _timeout;
  282.  
  283. public SmtpHandler(System.Net.DnsEndPoint mailServer, string fromAddress, System.Collections.Generic.List<string> toAddress, string subject, int timeout = 1000) {
  284. this._mailServer = mailServer;
  285. this._fromAddress = fromAddress;
  286. this._toAddress = toAddress;
  287. this._subject = subject;
  288. this._timeout = timeout;
  289. }
  290.  
  291. protected string GetSubject(LogRecord record) {
  292. return this._subject;
  293. }
  294.  
  295. protected override void Emit(LogRecord record) {
  296. try {
  297. var message = new System.Net.Mail.MailMessage { From = new System.Net.Mail.MailAddress(this._fromAddress), Subject = this.GetSubject(record), Body = this.Format(record) };
  298. this._toAddress.ForEach(x => message.To.Add(new System.Net.Mail.MailAddress(x)));
  299. using (var client = new System.Net.Mail.SmtpClient(this._mailServer.Host, this._mailServer.Port)) {
  300. client.Timeout = this._timeout;
  301. client.Send(message);
  302. }
  303. } catch (Exception error) {
  304. this.HandleError(record, error);
  305. }
  306. }
  307. }
  308.  
  309. public class Filter {
  310. private readonly string _name;
  311.  
  312. public Filter(string name = null) {
  313. this._name = name;
  314. }
  315.  
  316. public bool Check(LogRecord record) {
  317. return this._name == null || this._name == record.Name;
  318. }
  319. }
  320.  
  321. public class StyleSettings {
  322. public string DefaultFormat { get; }
  323. public string TimeToken { get; }
  324. public string RegexSearch { get; }
  325. public string PreferredFormat { get; }
  326.  
  327. public StyleSettings(string defaultFormat, string timeToken, string regexSearch, string preferredFormat) {
  328. this.DefaultFormat = defaultFormat;
  329. this.TimeToken = timeToken;
  330. this.RegexSearch = regexSearch;
  331. this.PreferredFormat = preferredFormat;
  332. }
  333. }
  334.  
  335. public class Style {
  336. public static readonly StyleSettings Percent = new StyleSettings("%(Message)", "%(Created)", @"%((w+))", "%(Level):%(Name):%(Message)");
  337. public static readonly StyleSettings StringFormat = new StyleSettings("{Message}", "{Created}", @"{(w+)}", "{Level}:{Name}:{Message}");
  338. public static readonly StyleSettings StringTemplate = new StyleSettings("${Message}", "${Created}", @"${(w+)}", "${Level}:${Name}:${Message}");
  339.  
  340. private readonly StyleSettings _settings;
  341. private readonly string _format;
  342.  
  343. public Style(StyleSettings settings, string format = null) {
  344. this._settings = settings;
  345. this._format = format ?? settings.DefaultFormat;
  346. }
  347.  
  348. public bool UsesTime => this._format.IndexOf(this._settings.TimeToken, StringComparison.Ordinal) >= 0;
  349.  
  350. public string Format(LogRecord record, string dateFormat = null) {
  351. var regex = new System.Text.RegularExpressions.Regex(this._settings.RegexSearch);
  352. return regex.Replace(this._format, x => Style.Replace(x, record, dateFormat));
  353. }
  354.  
  355. private static string Replace(System.Text.RegularExpressions.Match match, LogRecord record, string dateFormat) {
  356. System.Diagnostics.Debug.Assert(match.Groups.Count == 2, "one item should be captured");
  357. var value = Functions.GetProperty<LogRecord, object>(record, match.Groups[1].Value);
  358. return value is DateTime && dateFormat != null ? ((DateTime) value).ToString(dateFormat) : value.ToString();
  359. }
  360. }
  361.  
  362. public class Formatter {
  363. private readonly Style _style;
  364. private readonly string _dateFormat;
  365.  
  366. public Formatter(string format = null, string dateFormat = null, StyleSettings settings = null) {
  367. var preferredSettings = settings ?? Style.StringFormat;
  368. this._style = new Style(preferredSettings, format ?? preferredSettings.PreferredFormat);
  369. this._dateFormat = dateFormat ?? Scan.DateTimeFormat;
  370. }
  371.  
  372. public string FormatTime(LogRecord record, string dateFormat = null) {
  373. return record.Created.ToString(dateFormat ?? this._dateFormat);
  374. }
  375.  
  376. public string FormatException(LogRecord record) {
  377. return record.Error.ToString();
  378. }
  379.  
  380. public bool UsesTime => this._style.UsesTime;
  381.  
  382. public string FormatMessage(LogRecord record) {
  383. return this._style.Format(record, this._dateFormat);
  384. }
  385.  
  386. public string Format(LogRecord record) {
  387. var message = this.FormatMessage(record);
  388. if (record.Error != null) {
  389. message += StreamHandler.Terminator + this.FormatException(record);
  390. }
  391. return message;
  392. }
  393. }
  394.  
  395. public class LogRecord {
  396. public string Name { get; }
  397. public LoggingLevel Level { get; }
  398. public string FilePath { get; }
  399. public int LineNumber { get; }
  400. public string MemberName { get; }
  401. public string Message { get; }
  402. public Exception Error { get; }
  403. public DateTime Created { get; }
  404. public int ThreadId { get; }
  405. public string ThreadName { get; }
  406. public int ProcessId { get; }
  407. public string ProcessName { get; }
  408.  
  409. public LogRecord(string name, LoggingLevel level, string filePath, int lineNumber, string memberName,
  410. string message, Exception error = null) {
  411. this.Name = name;
  412. this.Level = level;
  413. this.FilePath = filePath;
  414. this.LineNumber = lineNumber;
  415. this.MemberName = memberName;
  416. this.Message = message;
  417. this.Error = error;
  418. this.Created = DateTime.Now;
  419. var thread = System.Threading.Thread.CurrentThread;
  420. this.ThreadId = thread.ManagedThreadId;
  421. this.ThreadName = thread.Name;
  422. var process = System.Diagnostics.Process.GetCurrentProcess();
  423. this.ProcessId = process.Id;
  424. this.ProcessName = process.ProcessName;
  425. }
  426.  
  427. public override string ToString() {
  428. return $"<{nameof(LogRecord)}: {this.Name}, {this.Level}, {this.FilePath}, {this.LineNumber}, {this.Message}>";
  429. }
  430. }
  431.  
  432. public enum LoggingLevel {
  433. Debug, // Detailed information, typically of interest only when diagnosing problems.
  434. Info, // Confirmation that things are working as expected.
  435. Warning, // An indication that something unexpected happened, or indicative of some problem in the near future (e.g. ‘disk space low’). The software is still working as expected.
  436. Error, // Due to a more serious problem, the software has not been able to perform some function.
  437. Critical // A serious error, indicating that the program itself may be unable to continue running.
  438. }
  439.  
  440. public enum ExceptionSetting {
  441. Pass, // Do nothing about the problem.
  442. Write, // Record problem on standard error.
  443. Throw // Propagate the exception once written.
  444. }
  445.  
  446. public class Scan {
  447. public const string DateTimeFormat = "yyyy-MM-ddTHH:mm:ss";
  448. }
  449.  
  450. public static class Functions {
  451. public static TOut GetProperty<TIn, TOut>(TIn value, string name) {
  452. return (TOut) Functions.NotNull(value.GetType().GetProperty(name), $"{name} is not available").GetValue(value);
  453. }
  454.  
  455. public static T NotNull<T>(T value, string message = null) {
  456. if (value == null) {
  457. throw new InvalidOperationException(message ?? $"{nameof(value)} may not be null");
  458. }
  459. return value;
  460. }
  461. }
Add Comment
Please, Sign In to add comment