WC_Log_Handler_File

Handles log entries by writing to a file.

Defined (1)

The class is defined in the following location(s).

/includes/log-handlers/class-wc-log-handler-file.php  
  1. class WC_Log_Handler_File extends WC_Log_Handler { 
  2.  
  3. /** 
  4. * Stores open file handles. 
  5. * @var array 
  6. */ 
  7. protected $handles = array(); 
  8.  
  9. /** 
  10. * File size limit for log files in bytes. 
  11. * @var int 
  12. */ 
  13. protected $log_size_limit; 
  14.  
  15. /** 
  16. * Cache logs that could not be written. 
  17. * If a log is written too early in the request, pluggable functions may be unavailable. These 
  18. * logs will be cached and written on 'plugins_loaded' action. 
  19. * @var array 
  20. */ 
  21. protected $cached_logs = array(); 
  22.  
  23. /** 
  24. * Constructor for the logger. 
  25. * @param int $log_size_limit Optional. Size limit for log files. Default 5mb. 
  26. */ 
  27. public function __construct( $log_size_limit = null ) { 
  28.  
  29. if ( null === $log_size_limit ) { 
  30. $log_size_limit = 5 * 1024 * 1024; 
  31.  
  32. $this->log_size_limit = $log_size_limit; 
  33.  
  34. add_action( 'plugins_loaded', array( $this, 'write_cached_logs' ) ); 
  35.  
  36. /** 
  37. * Destructor. 
  38. * Cleans up open file handles. 
  39. */ 
  40. public function __destruct() { 
  41. foreach ( $this->handles as $handle ) { 
  42. if ( is_resource( $handle ) ) { 
  43. fclose( $handle ); 
  44.  
  45. /** 
  46. * Handle a log entry. 
  47. * @param int $timestamp Log timestamp. 
  48. * @param string $level emergency|alert|critical|error|warning|notice|info|debug 
  49. * @param string $message Log message. 
  50. * @param array $context { 
  51. * Additional information for log handlers. 
  52. * @type string $source Optional. Determines log file to write to. Default 'log'. 
  53. * @type bool $_legacy Optional. Default false. True to use outdated log format 
  54. * originally used in deprecated WC_Logger::add calls. 
  55. * } 
  56. * @return bool False if value was not handled and true if value was handled. 
  57. */ 
  58. public function handle( $timestamp, $level, $message, $context ) { 
  59.  
  60. if ( isset( $context['source'] ) && $context['source'] ) { 
  61. $handle = $context['source']; 
  62. } else { 
  63. $handle = 'log'; 
  64.  
  65. $entry = self::format_entry( $timestamp, $level, $message, $context ); 
  66.  
  67. return $this->add( $entry, $handle ); 
  68.  
  69. /** 
  70. * Builds a log entry text from timestamp, level and message. 
  71. * @param int $timestamp Log timestamp. 
  72. * @param string $level emergency|alert|critical|error|warning|notice|info|debug 
  73. * @param string $message Log message. 
  74. * @param array $context Additional information for log handlers. 
  75. * @return string Formatted log entry. 
  76. */ 
  77. protected static function format_entry( $timestamp, $level, $message, $context ) { 
  78.  
  79. if ( isset( $context['_legacy'] ) && true === $context['_legacy'] ) { 
  80. if ( isset( $context['source'] ) && $context['source'] ) { 
  81. $handle = $context['source']; 
  82. } else { 
  83. $handle = 'log'; 
  84. $message = apply_filters( 'woocommerce_logger_add_message', $message, $handle ); 
  85. $time = date_i18n( 'm-d-Y @ H:i:s' ); 
  86. $entry = "{$time} - {$message}"; 
  87. } else { 
  88. $entry = parent::format_entry( $timestamp, $level, $message, $context ); 
  89.  
  90. return $entry; 
  91.  
  92. /** 
  93. * Open log file for writing. 
  94. * @param string $handle Log handle. 
  95. * @param string $mode Optional. File mode. Default 'a'. 
  96. * @return bool Success. 
  97. */ 
  98. protected function open( $handle, $mode = 'a' ) { 
  99. if ( $this->is_open( $handle ) ) { 
  100. return true; 
  101.  
  102. $file = self::get_log_file_path( $handle ); 
  103.  
  104. if ( $file ) { 
  105. if ( ! file_exists( $file ) ) { 
  106. $temphandle = @fopen( $file, 'w+' ); 
  107. @fclose( $temphandle ); 
  108.  
  109. if ( defined( 'FS_CHMOD_FILE' ) ) { 
  110. @chmod( $file, FS_CHMOD_FILE ); 
  111.  
  112. if ( $resource = @fopen( $file, $mode ) ) { 
  113. $this->handles[ $handle ] = $resource; 
  114. return true; 
  115.  
  116. return false; 
  117.  
  118. /** 
  119. * Check if a handle is open. 
  120. * @param string $handle Log handle. 
  121. * @return bool True if $handle is open. 
  122. */ 
  123. protected function is_open( $handle ) { 
  124. return array_key_exists( $handle, $this->handles ) && is_resource( $this->handles[ $handle ] ); 
  125.  
  126. /** 
  127. * Close a handle. 
  128. * @param string $handle 
  129. * @return bool success 
  130. */ 
  131. protected function close( $handle ) { 
  132. $result = false; 
  133.  
  134. if ( $this->is_open( $handle ) ) { 
  135. $result = fclose( $this->handles[ $handle ] ); 
  136. unset( $this->handles[ $handle ] ); 
  137.  
  138. return $result; 
  139.  
  140. /** 
  141. * Add a log entry to chosen file. 
  142. * @param string $entry Log entry text 
  143. * @param string $handle Log entry handle 
  144. * @return bool True if write was successful. 
  145. */ 
  146. protected function add( $entry, $handle ) { 
  147. $result = false; 
  148.  
  149. if ( $this->should_rotate( $handle ) ) { 
  150. $this->log_rotate( $handle ); 
  151.  
  152. if ( $this->open( $handle ) && is_resource( $this->handles[ $handle ] ) ) { 
  153. $result = fwrite( $this->handles[ $handle ], $entry . PHP_EOL ); 
  154. } else { 
  155. $this->cache_log( $entry, $handle ); 
  156.  
  157. return false !== $result; 
  158.  
  159. /** 
  160. * Clear entries from chosen file. 
  161. * @param string $handle 
  162. * @return bool 
  163. */ 
  164. public function clear( $handle ) { 
  165. $result = false; 
  166.  
  167. // Close the file if it's already open. 
  168. $this->close( $handle ); 
  169.  
  170. /** 
  171. * $this->open( $handle, 'w' ) == Open the file for writing only. Place the file pointer at 
  172. * the beginning of the file, and truncate the file to zero length. 
  173. */ 
  174. if ( $this->open( $handle, 'w' ) && is_resource( $this->handles[ $handle ] ) ) { 
  175. $result = true; 
  176.  
  177. do_action( 'woocommerce_log_clear', $handle ); 
  178.  
  179. return $result; 
  180.  
  181. /** 
  182. * Remove/delete the chosen file. 
  183. * @param string $handle 
  184. * @return bool 
  185. */ 
  186. public function remove( $handle ) { 
  187. $removed = false; 
  188. $file = self::get_log_file_path( $handle ); 
  189.  
  190. if ( $file ) { 
  191. if ( is_file( $file ) && is_writable( $file ) ) { 
  192. $this->close( $handle ); // Close first to be certain no processes keep it alive after it is unlinked. 
  193. $removed = unlink( $file ); 
  194. do_action( 'woocommerce_log_remove', $handle, $removed ); 
  195.  
  196. return $removed; 
  197.  
  198. /** 
  199. * Check if log file should be rotated. 
  200. * Compares the size of the log file to determine whether it is over the size limit. 
  201. * @param string $handle Log handle 
  202. * @return bool True if if should be rotated. 
  203. */ 
  204. protected function should_rotate( $handle ) { 
  205. $file = self::get_log_file_path( $handle ); 
  206. if ( $file ) { 
  207. if ( $this->is_open( $handle ) ) { 
  208. $file_stat = fstat( $this->handles[ $handle ] ); 
  209. return $file_stat['size'] > $this->log_size_limit; 
  210. } elseif ( file_exists( $file ) ) { 
  211. return filesize( $file ) > $this->log_size_limit; 
  212. } else { 
  213. return false; 
  214. } else { 
  215. return false; 
  216.  
  217. /** 
  218. * Rotate log files. 
  219. * Logs are rotatated by prepending '.x' to the '.log' suffix. 
  220. * The current log plus 10 historical logs are maintained. 
  221. * For example: 
  222. * base.9.log -> [ REMOVED ] 
  223. * base.8.log -> base.9.log 
  224. * ... 
  225. * base.0.log -> base.1.log 
  226. * base.log -> base.0.log 
  227. * @param string $handle Log handle 
  228. */ 
  229. protected function log_rotate( $handle ) { 
  230. for ( $i = 8; $i >= 0; $i-- ) { 
  231. $this->increment_log_infix( $handle, $i ); 
  232. $this->increment_log_infix( $handle ); 
  233.  
  234. /** 
  235. * Increment a log file suffix. 
  236. * @param string $handle Log handle 
  237. * @param null|int $number Optional. Default null. Log suffix number to be incremented. 
  238. * @return bool True if increment was successful, otherwise false. 
  239. */ 
  240. protected function increment_log_infix( $handle, $number = null ) { 
  241. if ( null === $number ) { 
  242. $suffix = ''; 
  243. $next_suffix = '.0'; 
  244. } else { 
  245. $suffix = '.' . $number; 
  246. $next_suffix = '.' . ($number + 1); 
  247.  
  248. $rename_from = self::get_log_file_path( "{$handle}{$suffix}" ); 
  249. $rename_to = self::get_log_file_path( "{$handle}{$next_suffix}" ); 
  250.  
  251. if ( $this->is_open( $rename_from ) ) { 
  252. $this->close( $rename_from ); 
  253.  
  254. if ( is_writable( $rename_from ) ) { 
  255. return rename( $rename_from, $rename_to ); 
  256. } else { 
  257. return false; 
  258.  
  259.  
  260. /** 
  261. * Get a log file path. 
  262. * @param string $handle Log name. 
  263. * @return bool|string The log file path or false if path cannot be determined. 
  264. */ 
  265. public static function get_log_file_path( $handle ) { 
  266. if ( function_exists( 'wp_hash' ) ) { 
  267. return trailingslashit( WC_LOG_DIR ) . sanitize_file_name( $handle . '-' . wp_hash( $handle ) . '.log' ); 
  268. } else { 
  269. wc_doing_it_wrong( __METHOD__, __( 'This method should not be called before plugins_loaded.', 'woocommerce' ), '3.0' ); 
  270. return false; 
  271.  
  272. /** 
  273. * Cache log to write later. 
  274. * @param string $entry Log entry text 
  275. * @param string $handle Log entry handle 
  276. */ 
  277. protected function cache_log( $entry, $handle ) { 
  278. $this->cached_logs[] = array( 
  279. 'entry' => $entry,  
  280. 'handle' => $handle,  
  281. ); 
  282.  
  283. /** 
  284. * Write cached logs. 
  285. */ 
  286. public function write_cached_logs() { 
  287. foreach ( $this->cached_logs as $log ) { 
  288. $this->add( $log['entry'], $log['handle'] ); 
  289.