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.

Logger.cs 7.6 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270
  1. using LLama.Native;
  2. using System;
  3. using System.Diagnostics;
  4. using System.IO;
  5. using static LLama.Common.ILLamaLogger;
  6. namespace LLama.Common;
  7. /// <summary>
  8. /// receives log messages from LLamaSharp
  9. /// </summary>
  10. public interface ILLamaLogger
  11. {
  12. /// <summary>
  13. /// Severity level of a log message
  14. /// </summary>
  15. public enum LogLevel
  16. {
  17. /// <summary>
  18. /// Logs that are used for interactive investigation during development.
  19. /// </summary>
  20. Debug = 1,
  21. /// <summary>
  22. /// Logs that highlight when the current flow of execution is stopped due to a failure.
  23. /// </summary>
  24. Error = 2,
  25. /// <summary>
  26. /// Logs that highlight an abnormal or unexpected event in the application flow, but do not otherwise cause the application execution to stop.
  27. /// </summary>
  28. Warning = 3,
  29. /// <summary>
  30. /// Logs that track the general flow of the application.
  31. /// </summary>
  32. Info = 4
  33. }
  34. /// <summary>
  35. /// Write the log in customized way
  36. /// </summary>
  37. /// <param name="source">The source of the log. It may be a method name or class name.</param>
  38. /// <param name="message">The message.</param>
  39. /// <param name="level">The log level.</param>
  40. void Log(string source, string message, LogLevel level);
  41. }
  42. /// <summary>
  43. /// The default logger of LLamaSharp. On default it write to console. Use methods of `LLamaLogger.Default` to change the behavior.
  44. /// It's recommended to inherit `ILLamaLogger` to customize the behavior.
  45. /// </summary>
  46. public sealed class LLamaDefaultLogger
  47. : ILLamaLogger
  48. {
  49. private static readonly Lazy<LLamaDefaultLogger> _instance = new Lazy<LLamaDefaultLogger>(() => new LLamaDefaultLogger());
  50. private bool _toConsole = true;
  51. private bool _toFile;
  52. private FileStream? _fileStream;
  53. private StreamWriter? _fileWriter;
  54. /// <summary>
  55. /// Get the default logger instance
  56. /// </summary>
  57. public static LLamaDefaultLogger Default => _instance.Value;
  58. private LLamaDefaultLogger()
  59. {
  60. }
  61. /// <summary>
  62. /// Enable logging output from llama.cpp
  63. /// </summary>
  64. /// <returns></returns>
  65. public LLamaDefaultLogger EnableNative()
  66. {
  67. EnableNativeLogCallback();
  68. return this;
  69. }
  70. /// <summary>
  71. /// Enable writing log messages to console
  72. /// </summary>
  73. /// <returns></returns>
  74. public LLamaDefaultLogger EnableConsole()
  75. {
  76. _toConsole = true;
  77. return this;
  78. }
  79. /// <summary>
  80. /// Disable writing messages to console
  81. /// </summary>
  82. /// <returns></returns>
  83. public LLamaDefaultLogger DisableConsole()
  84. {
  85. _toConsole = false;
  86. return this;
  87. }
  88. /// <summary>
  89. /// Enable writing log messages to file
  90. /// </summary>
  91. /// <param name="filename"></param>
  92. /// <param name="mode"></param>
  93. /// <returns></returns>
  94. public LLamaDefaultLogger EnableFile(string filename, FileMode mode = FileMode.Append)
  95. {
  96. _fileStream = new FileStream(filename, mode, FileAccess.Write);
  97. _fileWriter = new StreamWriter(_fileStream);
  98. _toFile = true;
  99. return this;
  100. }
  101. /// <summary>
  102. /// Disable writing log messages to file
  103. /// </summary>
  104. /// <param name="filename">unused!</param>
  105. /// <returns></returns>
  106. [Obsolete("Use DisableFile method without 'filename' parameter")]
  107. public LLamaDefaultLogger DisableFile(string filename)
  108. {
  109. return DisableFile();
  110. }
  111. /// <summary>
  112. /// Disable writing log messages to file
  113. /// </summary>
  114. /// <returns></returns>
  115. public LLamaDefaultLogger DisableFile()
  116. {
  117. if (_fileWriter is not null)
  118. {
  119. _fileWriter.Close();
  120. _fileWriter = null;
  121. }
  122. if (_fileStream is not null)
  123. {
  124. _fileStream.Close();
  125. _fileStream = null;
  126. }
  127. _toFile = false;
  128. return this;
  129. }
  130. /// <summary>
  131. /// Log a message
  132. /// </summary>
  133. /// <param name="source">The source of this message (e.g. class name)</param>
  134. /// <param name="message">The message to log</param>
  135. /// <param name="level">Severity level of this message</param>
  136. public void Log(string source, string message, LogLevel level)
  137. {
  138. if (level == LogLevel.Info)
  139. {
  140. Info(message);
  141. }
  142. else if (level == LogLevel.Debug)
  143. {
  144. }
  145. else if (level == LogLevel.Warning)
  146. {
  147. Warn(message);
  148. }
  149. else if (level == LogLevel.Error)
  150. {
  151. Error(message);
  152. }
  153. }
  154. /// <summary>
  155. /// Write a log message with "Info" severity
  156. /// </summary>
  157. /// <param name="message"></param>
  158. public void Info(string message)
  159. {
  160. message = MessageFormat("info", message);
  161. if (_toConsole)
  162. {
  163. Console.ForegroundColor = ConsoleColor.White;
  164. Console.WriteLine(message);
  165. Console.ResetColor();
  166. }
  167. if (_toFile)
  168. {
  169. Debug.Assert(_fileStream is not null);
  170. Debug.Assert(_fileWriter is not null);
  171. _fileWriter.WriteLine(message);
  172. }
  173. }
  174. /// <summary>
  175. /// Write a log message with "Warn" severity
  176. /// </summary>
  177. /// <param name="message"></param>
  178. public void Warn(string message)
  179. {
  180. message = MessageFormat("warn", message);
  181. if (_toConsole)
  182. {
  183. Console.ForegroundColor = ConsoleColor.Yellow;
  184. Console.WriteLine(message);
  185. Console.ResetColor();
  186. }
  187. if (_toFile)
  188. {
  189. Debug.Assert(_fileStream is not null);
  190. Debug.Assert(_fileWriter is not null);
  191. _fileWriter.WriteLine(message);
  192. }
  193. }
  194. /// <summary>
  195. /// Write a log message with "Error" severity
  196. /// </summary>
  197. /// <param name="message"></param>
  198. public void Error(string message)
  199. {
  200. message = MessageFormat("error", message);
  201. if (_toConsole)
  202. {
  203. Console.ForegroundColor = ConsoleColor.Red;
  204. Console.WriteLine(message);
  205. Console.ResetColor();
  206. }
  207. if (_toFile)
  208. {
  209. Debug.Assert(_fileStream is not null);
  210. Debug.Assert(_fileWriter is not null);
  211. _fileWriter.WriteLine(message);
  212. }
  213. }
  214. private static string MessageFormat(string level, string message)
  215. {
  216. var now = DateTime.Now;
  217. return $"[{now:yyyy.MM.dd HH:mm:ss}][{level}]: {message}";
  218. }
  219. /// <summary>
  220. /// Register native logging callback
  221. /// </summary>
  222. private void EnableNativeLogCallback()
  223. {
  224. // TODO: Move to a more appropriate place once we have a intitialize method
  225. NativeApi.llama_log_set(NativeLogCallback);
  226. }
  227. /// <summary>
  228. /// Callback for native logging function
  229. /// </summary>
  230. /// <param name="level">The log level</param>
  231. /// <param name="message">The log message</param>
  232. private void NativeLogCallback(LogLevel level, string message)
  233. {
  234. if (string.IsNullOrEmpty(message))
  235. return;
  236. // Note that text includes the new line character at the end for most events.
  237. // If your logging mechanism cannot handle that, check if the last character is '\n' and strip it
  238. // if it exists.
  239. // It might not exist for progress report where '.' is output repeatedly.
  240. Log(default!, message.TrimEnd('\n'), level);
  241. }
  242. }