Log.pm.svn-base
上传用户:market2
上传日期:2018-11-18
资源大小:18786k
文件大小:13k
源码类别:

外挂编程

开发平台:

Windows_Unix

  1. #########################################################################
  2. #  OpenKore - Message Logging Framework
  3. #
  4. #  This software is open source, licensed under the GNU General Public
  5. #  License, version 2.
  6. #  Basically, this means that you're allowed to modify and distribute
  7. #  this software. However, if you distribute modified versions, you MUST
  8. #  also distribute the source code.
  9. #  See http://www.gnu.org/licenses/gpl.html for the full license.
  10. #
  11. #########################################################################
  12. ##
  13. # MODULE DESCRIPTION: Message Logging framework
  14. #
  15. # <h3>What is a logging framework and why is it needed?</h3>
  16. #
  17. # Kore used to print messages to the console using print(). There are several
  18. # problems though:
  19. # `l
  20. # - Messages can only be printed to the console. If you want to print it
  21. #   to elsewhere you have to resort to all kinds of hacks (take a look
  22. #   at the code for sending console output to X-Kore, for example).
  23. # - The messages have no classification. You have a message, but there's
  24. #   no easy way for the program to find out what kind of message it is;
  25. #   you don't know it's context. This means that the user can't really control
  26. #   what kind of messages he does and doesn't want to see.
  27. # - Debug messages are all in the form of print "blan" if ($config{'verbose'});
  28. #   You can either enable all debug messages, or nothing at all. For developers,
  29. #   the huge amount of debug messages can make things look cluttered.
  30. # `l`
  31. #
  32. # The logging framework provides a new way to print messages:
  33. # `l
  34. # - You can print messages (of course).
  35. # - You can classify messages: attaching a context (domain) to a message you print.
  36. # - You can intercept messages and decide what else to do with it. You can write to
  37. #   a file, send to X-Kore (based on the message's domain), or whatever you want.
  38. # - You can attach certain colors to messages of a certain domains.
  39. # - You can choose what kind of message you do and do not want to see.
  40. # `l`
  41. #
  42. # The most important functions are:
  43. # Log::message(), Log::warning(), Log::error(), Log::debug()
  44. #
  45. # You pass the following arguments to those functions:
  46. # `l
  47. # - message: The message you want to print.
  48. # - domain: The message domain (context). This is used to classify a message.
  49. # - level: The message's verbosity level. The message will only be printed if this number
  50. #          is lower than or equal to $config{'verbose'} (or $config{'debug'} if this is a
  51. #          debug message). Important messages should have a low verbosity level,
  52. #          unimportant/redundant messages should have a high verbosity level.
  53. # `l`
  54. # Known domains:
  55. # attacked Monster attacks you
  56. # attackedMiss Monster attacks you but miss
  57. # attackMon You attack monster
  58. # attackMonMiss You attack monster but miss
  59. # connection Connection messages
  60. # deal Deal messages
  61. # drop Monster drop related
  62. # emotion Emoticon
  63. # equip         Equipment Switching
  64. # gmchat GM chat message
  65. # guildchat Guild chat message
  66. # info View info that's requested by the user (status, guild info, etc.)
  67. # input Waiting for user input
  68. # inventory Inventory related messages
  69. # useItem You used item
  70. # list List of information (monster list, player list, item list, etc.)
  71. # load Loading config files
  72. # menu Menu choices
  73. # npc NPC messages
  74. # party Party/follow related
  75. # partychat Party chat messages
  76. # plugins Messages about plugin handling
  77. # pm Private chat message
  78. # publicchat Public chat message
  79. # route Routing/pathfinding messages
  80. # sold Item sold while vending.
  81. # skill Skill use unrelated to attack
  82. # selfSkill Skills used by yourself
  83. # startup Messages that are printed during startup.
  84. # storage Storage item added/removed
  85. # success An operation succeeded
  86. # syntax Syntax check files
  87. # system System messages
  88. # teleport Teleporting
  89. # xkore X-Kore system messages
  90. # Debug domains:
  91. # ai_attack
  92. # ai_autoCart
  93. # ai_move
  94. # parseInput
  95. # parseMsg
  96. # parseMsg_damage
  97. # parseMsg_presence
  98. # portalRecord
  99. # sendPacket
  100. # ai
  101. # npc
  102. # route
  103. # useTeleport
  104. package Log;
  105. use strict;
  106. use Exporter;
  107. use Time::HiRes;
  108. use base qw(Exporter);
  109. use Modules 'register';
  110. use Globals qw(%config $interface %consoleColors %field %cities_lut);
  111. use Utils::DataStructures qw(binAdd existsInList);
  112. use Utils qw(binAdd existsInList getFormattedDate);
  113. our @EXPORT_OK = qw(message warning error debug);
  114. #################################
  115. #################################
  116. # VARIABLES
  117. #################################
  118. #################################
  119. # The verbosity level for messages. Messages that have a higher verbosity than this will not be printed.
  120. # Low level = important messages. High level = less important messages.
  121. # If you set the current verbosity higher, you will see more messages.
  122. our $warningVerbosity;
  123. our $errorVerbosity;
  124. # Enable/disable printing certain domains to console.
  125. # Usage: $messageConsole{$domain} = $enabled
  126. our %messageConsole;
  127. our %warningConsole;
  128. our %errorConsole;
  129. our %debugConsole;
  130. # Messages can also printed to files. These variables
  131. # contain filenames of the files to print to.
  132. # Usage: @{$messageFiles{$domain}} = (list of filenames)
  133. our %messageFiles;
  134. our %warningFiles;
  135. our %errorFiles;
  136. our %debugFiles;
  137. # Message hooks are stored here
  138. our @hooks;
  139. # Enable/disable adding a timestamp to log files.
  140. our $logTimestamp;
  141. # Enable/disable adding a timestamp to chat logs.
  142. our $chatTimestamp;
  143. # use SelfLoader; 1;
  144. # __DATA__
  145. #################################
  146. #################################
  147. # PRIVATE FUNCTIONS
  148. #################################
  149. #################################
  150. sub MODINIT {
  151. $warningVerbosity = 1;
  152. $errorVerbosity = 1;
  153. $logTimestamp = 1;
  154. $chatTimestamp = 1;
  155. }
  156. sub processMsg {
  157. my $type = shift;
  158. my $message = shift;
  159. my $domain = (shift or "console");
  160. my $level = (shift or 0);
  161. my $currentVerbosity = shift;
  162. my $consoleVar = shift;
  163. my $files = shift;
  164. my (undef, undef, undef, $near) = caller(2);
  165. my (undef, undef, undef, $far) = caller(3);
  166. $currentVerbosity = 1 if ($currentVerbosity eq "");
  167. # Beep on certain domains
  168. $interface->beep() if existsInList($config{beepDomains}, $domain) &&
  169. !(existsInList($config{beepDomains_notInTown}, $domain) &&
  170.   $cities_lut{$field{name}.'.rsw'});
  171. # Add timestamp if domain was specified in config.txt/showTimeDomains
  172. if (existsInList($config{showTimeDomains}, $domain)) {
  173. my @tmpdate = localtime();
  174. $tmpdate[5] += 1900;
  175. for (my $i = 0; $i < @tmpdate; $i++) {
  176. if ($tmpdate[$i] < 10) {$tmpdate[$i] = "0".$tmpdate[$i]};
  177. }
  178. if (defined (my $format = $config{showTimeDomainsFormat})) {
  179. $format =~ s/H/$tmpdate[2]/g;
  180. $format =~ s/M/$tmpdate[1]/g;
  181. $format =~ s/S/$tmpdate[0]/g;
  182. $format =~ s/y/$tmpdate[5]/g;
  183. $format =~ s/m/$tmpdate[4]/g;
  184. $format =~ s/d/$tmpdate[3]/g;
  185. $message = "$format $message";
  186. } else {
  187. $message = "[$tmpdate[2]:$tmpdate[1]:$tmpdate[0]] $message";
  188. }
  189. };
  190. # Print to console if the current verbosity is high enough
  191. if ($level <= $currentVerbosity) {
  192. $consoleVar->{$domain} = 1 if (!defined($consoleVar->{$domain}));
  193. if ($consoleVar->{$domain}) {
  194. if ($interface) {
  195. $message = "[$domain] " . $message if ($config{showDomain});
  196. my (undef, $microseconds) = Time::HiRes::gettimeofday;
  197. $microseconds = substr($microseconds, 0, 2);
  198. my $message2 = "[".getFormattedDate(int(time)).".$microseconds] ".$message;
  199. if ($config{showTime}) {
  200. $interface->writeOutput($type, $message2, $domain);
  201. } else {
  202. $interface->writeOutput($type, $message, $domain);
  203. }
  204. if ($config{logConsole} &&
  205. open(F, ">>:utf8", "$Settings::logs_folder/console.txt")) {
  206. print F $message2;
  207. close(F);
  208. }
  209. } else {
  210. print $message;
  211. }
  212. }
  213. }
  214. # Print to files
  215. foreach my $file (@{$files->{$domain}}) {
  216. if (open(F, ">>:utf8", "$Settings::logs_folder/$file")) {
  217. print F '['. getFormattedDate(int(time)) .'] ' if ($logTimestamp);
  218. print F $message;
  219. close(F);
  220. }
  221. }
  222. # Call hooks
  223. foreach (@hooks) {
  224. next if (!defined($_));
  225. $_->{'func'}->($type, $domain, $level, $currentVerbosity, $message, $_->{'user_data'}, $near, $far);
  226. }
  227. }
  228. #################################
  229. #################################
  230. # PUBLIC METHODS
  231. #################################
  232. #################################
  233. ##
  234. # Log::message(message, [domain], [level])
  235. # Requires: $message must be encoded in UTF-8.
  236. #
  237. # Prints a normal message. See the description for Log.pm for more details
  238. # about the parameters.
  239. sub message {
  240. my ($message, $domain, $level) = @_;
  241. $level = 5 if existsInList($config{squelchDomains}, $domain);
  242. $level = 0 if existsInList($config{verboseDomains}, $domain);
  243. return processMsg("message", # type
  244. $message,
  245. $domain,
  246. $level,
  247. $config{'verbose'}, # currentVerbosity
  248. %messageConsole,
  249. %messageFiles);
  250. }
  251. ##
  252. # Log::warning(message, [domain], [level])
  253. #
  254. # Prints a warning message. It warns the user that a possible non-fatal error has occured or will occur.
  255. # See the description for Log.pm for more details about the parameters.
  256. sub warning {
  257. return processMsg("warning",
  258. $_[0],
  259. $_[1],
  260. $_[2],
  261. $warningVerbosity,
  262. %warningConsole,
  263. %warningFiles);
  264. }
  265. ##
  266. # Log::error(message, [domain], [level])
  267. # Requires: $message must be encoded in UTF-8.
  268. #
  269. # Prints an error message. It tells the user that a non-recoverable error has
  270. # occured.  A "non-recoverable error" could either be a fatal error, or an
  271. # error that prevents the program from performing an action the user requested.
  272. #
  273. # Examples of non-recoverable errors:
  274. # `l
  275. # - Kore receives the "You haven't paid for this account"-packet. The error is
  276. #   fatal, so the entire program must exit.
  277. # - The user typed in an invalid/unrecognized command. Kore cannot perform the
  278. #   command the user requested, but will not exit because this error is not
  279. #   fatal.
  280. # `l`
  281. # See the description for Log.pm for more details about the parameters.
  282. sub error {
  283. return processMsg("error",
  284. $_[0],
  285. $_[1],
  286. $_[2],
  287. $errorVerbosity,
  288. %errorConsole,
  289. %errorFiles);
  290. }
  291. ##
  292. # Log::debug(message, [domain], [level])
  293. # Requires: $message must be encoded in UTF-8.
  294. #
  295. # Prints a debugging message. See the description for Log.pm for more details about the parameters.
  296. sub debug {
  297. my $level = $_[2];
  298. $level = 1 if (!defined $level);
  299. $level = 0 if (existsInList($config{debugDomains}, $_[1]));
  300. $level = 5 if (existsInList($config{squelchDomains}, $_[1]));
  301. return processMsg("debug",
  302. $_[0],
  303. $_[1],
  304. $level,
  305. (defined $config{'debug'}) ? $config{'debug'} : 0,
  306. %debugConsole,
  307. %debugFiles);
  308. }
  309. ##
  310. # Log::addHook(r_func, [user_data])
  311. # r_func: A reference to the function to call.
  312. # user_data: Additional data to pass to r_func.
  313. # Returns: An ID which you can use to remove this hook.
  314. #
  315. # Adds a hook. Every time Log::message(), Log::warning(), Log::error() or Log::debug() is called,
  316. # r_func is also called, in the following way:
  317. # <pre>
  318. # r_func->($type, $domain, $level, $globalVerbosity, $message, $user_data);
  319. # $type : One of the following: "message", "warning", "error", "debug".
  320. # $domain : The message's domain.
  321. # $level : The message's own verbosity level.
  322. # $globalVerbosity : The global verbosity level.
  323. # $message : The message itself.
  324. # $user_data : The value of user_data, as passed to addHook.
  325. # $near : The function that called "message", "warning", "error" or "debug"
  326. # $far : The function that called $near
  327. # </pre>
  328. #
  329. # See also: Log::delHook()
  330. #
  331. # Example:
  332. # sub hook {
  333. #  my $type = shift; # "message"
  334. #  my $domain = shift; # "MyDomain"
  335. #  my $level = shift; # 2
  336. # my $globalVerbosity = shift; # 1 (equal to $config{'verbose'})
  337. # my $message = shift; # "Hello World"
  338. # my $user_data = shift; # "my_user_data"
  339. # my $near = shift; # "Commands::cmdWhere"
  340. # my $far = shift; # "Commands::run"
  341. #  # Do whatever you want here
  342. # }
  343. # Log::addHook(&hook, "my_user_data");
  344. #
  345. # $config{'verbose'} = 1;
  346. # # Note that the following function will not print anything to screen,
  347. # # because it's verbosity level is higher than the global verbosity
  348. # # level ($config{'verbose'}).
  349. # Log::message("Hello World", "MyDomain", 2);  # hook() will now be called
  350. sub addHook {
  351. my ($r_func, $user_data) = @_;
  352. my %hook;
  353. $hook{func} = $r_func;
  354. $hook{user_data} = $user_data;
  355. return binAdd(@hooks, %hook);
  356. }
  357. ##
  358. # Log::delHook(ID)
  359. # ID: A hook ID, as returned by addHook().
  360. #
  361. # Removes a hook. r_func will not be called anymore.
  362. #
  363. # Example:
  364. # my $ID = Log::addHook(&hook);
  365. # Log::message("Hello World", "MyDomain"); # hook() is called
  366. # Log::delHook($ID);
  367. # Log::message("Hello World", "MyDomain"); # hook() is NOT called
  368. sub delHook {
  369. my $ID = shift;
  370. delete $hooks[$ID];
  371. }
  372. ##
  373. # Log::parseLogToFile(args,hash)
  374. #
  375. # args has to look like domain=file
  376. # but can look like domain1=file1.txt;domain2=file2.txt,file3.txt
  377. #
  378. # The hash has to be a reference for the output hash.
  379. sub parseLogToFile {
  380. my $args = shift;
  381. my $list = shift;
  382. $args =~ s/s//g;
  383. my @domains = split (';', $args);
  384. my $files;
  385. foreach my $domain (@domains) {
  386. ($domain,$files) = split ('=', $domain);
  387. my @filesArray = split (',', $files);
  388. $list->{$domain} = [];
  389. foreach my $file (@filesArray) {
  390. push(@{$list->{$domain}}, $file);
  391. }
  392. }
  393. }
  394. ##
  395. # initLogFiles()
  396. #
  397. # This function should be called everytime config.txt is (re)loaded.
  398. sub initLogFiles {
  399. parseLogToFile($config{logToFile_Messages}, %messageFiles) if $config{logToFile_Messages};
  400. parseLogToFile($config{logToFile_Warnings}, %warningFiles) if $config{logToFile_Warnings};
  401. parseLogToFile($config{logToFile_Errors}, %errorFiles) if $config{logToFile_Errors};
  402. parseLogToFile($config{logToFile_Debug}, %debugFiles) if $config{logToFile_Debug};
  403. }
  404. return 1;