Personal emacs config
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.

869 lines
34 KiB

  1. ;;; syslog-mode.el --- Major-mode for viewing log files
  2. ;; Filename: syslog-mode.el
  3. ;; Description: Major-mode for viewing log files
  4. ;; Author: Harley Gorrell <harley@panix.com>
  5. ;; Maintainer: Joe Bloggs <vapniks@yahoo.com>
  6. ;; Created: 2003-03-17 18:50:12 Harley Gorrell
  7. ;; URL: https://github.com/vapniks/syslog-mode
  8. ;; Package-Version: 2.3
  9. ;; Package-Commit: 18f441bf57dd70cdd48a71f1f4566ab35facdb35
  10. ;; Keywords: unix
  11. ;; Compatibility: GNU Emacs 24.3.1
  12. ;; Package-Requires: ((hide-lines "20130623") (ov "20150311"))
  13. ;;
  14. ;; Features that might be required by this library:
  15. ;;
  16. ;; hide-lines cl ido dash dired+ ov
  17. ;;
  18. ;;; This file is NOT part of GNU Emacs
  19. ;;; License
  20. ;;
  21. ;; This program is free software; you can redistribute it and/or modify
  22. ;; it under the terms of the GNU General Public License as published by
  23. ;; the Free Software Foundation, either version 3 of the License, or
  24. ;; (at your option) any later version.
  25. ;; This program is distributed in the hope that it will be useful,
  26. ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  27. ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  28. ;; GNU General Public License for more details.
  29. ;; You should have received a copy of the GNU General Public License
  30. ;; along with this program; see the file COPYING.
  31. ;; If not, see <http://www.gnu.org/licenses/>.
  32. ;;; Commentary:
  33. ;;
  34. ;;; Commentary:
  35. ;; * Handy functions for looking at system logs.
  36. ;; * Fontifys the date and su messages.
  37. ;;; Keybindings
  38. ;; "C-down" : syslog-boot-start
  39. ;; "R" : revert-buffer
  40. ;; "/" : syslog-filter-lines
  41. ;; "g" : hide-lines-show-all
  42. ;; "h r" : highlight-regexp
  43. ;; "h p" : highlight-phrase
  44. ;; "h l" : highlight-lines-matching-regexp
  45. ;; "h u" : unhighlight-regexp
  46. ;; "C-/" : syslog-filter-dates
  47. ;; "D" : open dired buffer in log directory (`syslog-log-file-directory')
  48. ;; "j" : ffap
  49. ;; "<" : syslog-previous-file
  50. ;; ">" : syslog-next-file
  51. ;; "o" : syslog-open-files
  52. ;; "q" : quit-window
  53. ;;; Commands:
  54. ;;
  55. ;; Below is a complete list of commands:
  56. ;;
  57. ;; `syslog-shell-command'
  58. ;; Execute a shell COMMAND synchronously, with prefix arg (SUDOP) run under sudo.
  59. ;; Keybinding: !
  60. ;; `syslog-append-files'
  61. ;; Append FILES into buffer BUF.
  62. ;; Keybinding: a
  63. ;; `syslog-prepend-files'
  64. ;; Prepend FILES into buffer BUF.
  65. ;; Keybinding: M-x syslog-prepend-files
  66. ;; `syslog-open-files'
  67. ;; Insert log FILES into new buffer.
  68. ;; Keybinding: o
  69. ;; `syslog-view'
  70. ;; Open a view of syslog files with optional filters and highlights applied.
  71. ;; Keybinding: v
  72. ;; `syslog-previous-file'
  73. ;; Open the previous logfile backup, or the next one if a prefix arg is used.
  74. ;; Keybinding: <
  75. ;; `syslog-next-file'
  76. ;; Open the next logfile.
  77. ;; Keybinding: >
  78. ;; `syslog-move-next-file'
  79. ;; Move to the next file in the current `syslog-mode' buffer.
  80. ;; Keybinding: <M-down>
  81. ;; `syslog-move-previous-file'
  82. ;; Move to the next file in the current `syslog-mode' buffer.
  83. ;; Keybinding: <M-up>
  84. ;; `syslog-toggle-filenames'
  85. ;; Toggle the display of filenames before each line.
  86. ;; Keybinding: t
  87. ;; `syslog-filter-lines'
  88. ;; Restrict buffer to blocks of text between matching regexps.
  89. ;; Keybinding: /
  90. ;; `syslog-filter-dates'
  91. ;; Restrict buffer to lines between times START and END (Emacs time lists).
  92. ;; Keybinding: C-/
  93. ;; `syslog-mode'
  94. ;; Major mode for working with system logs.
  95. ;; Keybinding: M-x syslog-mode
  96. ;; `syslog-count-matches'
  97. ;; Count strings which match the given pattern.
  98. ;; Keybinding: c
  99. ;; `syslog-boot-start'
  100. ;; Jump forward in the log to when the system booted.
  101. ;; Keybinding: <C-down>
  102. ;; `syslog-whois-reverse-lookup'
  103. ;; This is a wrapper around the `whois' command using symbol at point as default search string.
  104. ;; Keybinding: W
  105. ;;
  106. ;;; Customizable Options:
  107. ;;
  108. ;; Below is a list of customizable options:
  109. ;;
  110. ;; `syslog-mode-hook'
  111. ;; *Hook to setup `syslog-mode'.
  112. ;; default = nil
  113. ;; `syslog-views'
  114. ;; A list of views.
  115. ;; default = nil
  116. ;; `syslog-datetime-regexp'
  117. ;; A regular expression matching the date-time at the beginning of each line in the log file.
  118. ;; `syslog-log-file-directory'
  119. ;; The directory in which log files are stored.
  120. ;; default = "/var/log/"
  121. ;; All of the above can customized by:
  122. ;; M-x customize-group RET syslog-mode RET
  123. ;;
  124. ;;; Installation:
  125. ;;
  126. ;; Put syslog-mode.el in a directory in your load-path, e.g. ~/.emacs.d/
  127. ;; You can add a directory to your load-path with the following line in ~/.emacs
  128. ;; (add-to-list 'load-path (expand-file-name "~/elisp"))
  129. ;; where ~/elisp is the directory you want to add
  130. ;; (you don't need to do this for ~/.emacs.d - it's added by default).
  131. ;;
  132. ;; Add the following to your ~/.emacs startup file.
  133. ;;
  134. ;; (require 'syslog-mode)
  135. ;;; Change log:
  136. ;;
  137. ;; 21-03-2013 Joe Bloggs
  138. ;; Added functions and keybindings for filtering
  139. ;; lines by regexps or dates, and for highlighting,
  140. ;; and quick key for find-file-at-point
  141. ;;
  142. ;; 20-03-2013 Christian Giménez
  143. ;; Added more keywords for font-lock.
  144. ;;
  145. ;; 16-03-2003 : Updated URL and contact info.
  146. ;;; Acknowledgements:
  147. ;;
  148. ;; Harley Gorrell (Author)
  149. ;; Christian Giménez
  150. ;;
  151. ;; If anyone wants to make changes please fork the following github repo: https://github.com/vapniks/syslog-mode
  152. ;;; TODO: statistical reporting - have a regular expression to match item type, then report counts of each item type.
  153. ;; also statistics on number of items per hour/day/week/etc.
  154. ;;; Require
  155. (require 'hide-lines)
  156. (eval-when-compile (require 'cl))
  157. (require 'ido)
  158. (require 'hi-lock)
  159. (require 'net-utils)
  160. (require 'ov)
  161. ;;; Code:
  162. ;; Setup
  163. (defgroup syslog nil
  164. "syslog-mode - a major mode for viewing log files"
  165. :link '(url-link "https://github.com/vapniks/syslog-mode"))
  166. (defcustom syslog-mode-hook nil
  167. "*Hook to setup `syslog-mode'."
  168. :group 'syslog
  169. :type 'hook)
  170. (defvar syslog-mode-load-hook nil
  171. "*Hook to run when `syslog-mode' is loaded.")
  172. ;;;###autoload
  173. (defvar syslog-setup-on-load nil
  174. "*If not nil setup syslog mode on load by running syslog-add-hooks.")
  175. ;; I also use "Alt" as C-c is too much to type for cursor motions.
  176. (defvar syslog-mode-map
  177. (let ((map (make-sparse-keymap)))
  178. ;; Ctrl bindings
  179. (define-key map [C-down] 'syslog-boot-start)
  180. (define-key map "R" 'revert-buffer)
  181. (define-key map "/" 'syslog-filter-lines)
  182. (define-key map "g" 'hide-lines-show-all)
  183. (define-prefix-command 'syslog-highlight-map)
  184. (define-key map "h" 'syslog-highlight-map)
  185. (define-key map (kbd "h r") 'highlight-regexp)
  186. (define-key map (kbd "h p") 'highlight-phrase)
  187. (define-key map (kbd "h l") 'highlight-lines-matching-regexp)
  188. (define-key map (kbd "h u") 'unhighlight-regexp)
  189. (define-key map (kbd "C-/") 'syslog-filter-dates)
  190. (define-key map "D" (lambda nil (interactive) (dired syslog-log-file-directory)))
  191. (define-key map "j" 'ffap)
  192. (define-key map "f" 'ffap)
  193. (define-key map "<" 'syslog-previous-file)
  194. (define-key map ">" 'syslog-next-file)
  195. (define-key map "o" 'syslog-open-files)
  196. (define-key map "a" 'syslog-append-files)
  197. (define-key map "p" 'syslog-prepend-files)
  198. (define-key map "v" 'syslog-view)
  199. (define-key map "c" 'syslog-count-matches)
  200. (define-key map "k" 'hide-lines-kill-hidden)
  201. (define-key map "W" 'syslog-whois-reverse-lookup)
  202. (define-key map "q" 'quit-window)
  203. (define-key map "!" 'syslog-shell-command)
  204. (define-key map (kbd "<M-down>") 'syslog-move-next-file)
  205. (define-key map (kbd "<M-up>") 'syslog-move-previous-file)
  206. (define-key map "t" 'syslog-toggle-filenames)
  207. ;; XEmacs does not like the Alt bindings
  208. (if (string-match "XEmacs" (emacs-version)) t)
  209. map)
  210. "The local keymap for `syslog-mode'.")
  211. (defvar syslog-number-suffix-start 1
  212. "The first number used as rotation suffix.")
  213. (defun syslog-shell-command (command &optional sudop)
  214. "Execute a shell COMMAND synchronously, with prefix arg (SUDOP) run under sudo."
  215. (interactive (list (read-shell-command (if current-prefix-arg
  216. "Shell command (root): "
  217. "Shell command: "))
  218. current-prefix-arg))
  219. (if sudop
  220. (with-temp-buffer
  221. (cd (concat "/sudo::"
  222. (replace-regexp-in-string
  223. "^/sudo[^/]+" "" default-directory)))
  224. (shell-command command))
  225. (shell-command command)))
  226. (defun syslog-get-basename-and-number (filename)
  227. "Return the basename and number suffix of a log file in FILEPATH.
  228. Return results in a cons cell '(basename . number) where basename is a string,
  229. and number is a number."
  230. (let* ((res (string-match "\\(.*?\\)\\.\\([0-9]+\\)\\(\\.t?gz\\)?" filename))
  231. (basename (if res (match-string 1 filename) filename))
  232. (str (and res (match-string 2 filename)))
  233. (num (or (and str (string-to-number str)) (1- syslog-number-suffix-start))))
  234. (cons basename num)))
  235. (defun syslog-get-filenames (&optional pairs prompt onlyone)
  236. "Get log files associated with PAIRS argument, or prompt user for files.
  237. The PAIRS argument should be a list of cons cells whose cars are paths to log files,
  238. and whose cdrs are numbers indicating how many previous log files (if positive) or days
  239. (if negative) to include. If PAIRS is missing then the user is prompted for those values.
  240. If ONLYONE is non-nil then the user is only prompted for a single file.
  241. The PROMPT argument is an optional prompt to use for prompting the user for files."
  242. (let* ((continue t)
  243. (num 0)
  244. (pairs
  245. (or pairs
  246. (cl-loop
  247. while continue
  248. do (setq
  249. filename
  250. (ido-read-file-name
  251. (or prompt "Log file: ")
  252. syslog-log-file-directory "syslog" nil)
  253. num (if onlyone 0
  254. (read-number
  255. "Number of previous files (if positive) or days (if negative) to include"
  256. num)))
  257. collect (cons filename num)
  258. if onlyone do (setq continue nil)
  259. else do (setq continue (y-or-n-p "Add more files? "))))))
  260. (cl-remove-duplicates
  261. (cl-loop for pair1 in pairs
  262. for filename = (car pair1)
  263. for num = (cdr pair1)
  264. for pair = (syslog-get-basename-and-number filename)
  265. for basename = (car pair)
  266. for basename2 = (file-name-nondirectory basename)
  267. for curver = (cdr pair)
  268. for num2 = (if (>= num 0) num
  269. (- (let* ((startdate (+ (float-time (nth 5 (file-attributes filename)))
  270. (* num 86400))))
  271. (cl-loop for file2 in (directory-files (file-name-directory filename)
  272. t basename2)
  273. for filedate2 = (float-time (nth 5 (file-attributes file2)))
  274. if (>= filedate2 startdate)
  275. maximize (cdr (syslog-get-basename-and-number file2))))
  276. curver))
  277. for files = (cl-loop for n from (1+ curver) to (+ curver num2)
  278. for numstr = (number-to-string n)
  279. for nextfile = (cl-loop for suffix in '(nil ".gz" ".tgz")
  280. for filename3 = (concat basename "." numstr suffix)
  281. if (file-readable-p filename3)
  282. return filename3)
  283. collect nextfile)
  284. nconc (nconc (list filename) (cl-remove-if 'null files))) :test 'equal)))
  285. (defun syslog-append-files (files buf &optional replace)
  286. "Append FILES into buffer BUF.
  287. If REPLACE is non-nil then the contents of BUF will be overwritten.
  288. When called interactively the current buffer is used, FILES are prompted for
  289. using `syslog-get-filenames', and REPLACE is set to nil, unless
  290. a prefix argument is used in which case they are prompted for."
  291. (interactive (list (syslog-get-filenames nil "Append log file: ")
  292. (current-buffer)
  293. (if current-prefix-arg
  294. (y-or-n-p "Replace current buffer contents? "))))
  295. (with-current-buffer buf
  296. (let ((inhibit-read-only t))
  297. (set-visited-file-name nil)
  298. (save-excursion
  299. (cl-loop for file in (cl-remove-duplicates files :test 'equal)
  300. do (goto-char (point-max))
  301. (let ((start (point)))
  302. (insert-file-contents file)
  303. (goto-char (point-max))
  304. (put-text-property start (point) 'syslog-filename file)))))))
  305. (defun syslog-prepend-files (files buf &optional replace)
  306. "Prepend FILES into buffer BUF.
  307. If REPLACE is non-nil then the contents of BUF will be overwritten.
  308. When called interactively the current buffer is used, FILES are prompted for
  309. using `syslog-get-filenames', and REPLACE is set to nil, unless
  310. a prefix argument is used in which case they are prompted for."
  311. (interactive (list (syslog-get-filenames nil "Prepend log file: ")
  312. (current-buffer)
  313. (if current-prefix-arg
  314. (y-or-n-p "Replace current buffer contents? "))))
  315. (with-current-buffer buf
  316. (let ((inhibit-read-only t))
  317. (set-visited-file-name nil)
  318. (cl-loop for file in (cl-remove-duplicates files :test 'equal)
  319. do (let ((start (goto-char (point-min))))
  320. (forward-char (cl-second (insert-file-contents file)))
  321. (put-text-property start (point) 'syslog-filename file))))))
  322. (defun syslog-create-buffer (filenames)
  323. "Create a new buffer named after the files in FILENAMES."
  324. (let* ((uniquefiles (mapcar 'file-name-nondirectory
  325. (cl-remove-duplicates filenames :test 'equal)))
  326. (basenames (mapcar (lambda (x)
  327. (replace-regexp-in-string
  328. "\\(\\.gz\\|\\.tgz\\)$" ""
  329. (file-name-nondirectory x)))
  330. uniquefiles))
  331. (basenames2 (cl-remove-duplicates
  332. (mapcar (lambda (x) (replace-regexp-in-string "\\.[0-9]+$" "" x)) basenames)
  333. :test 'equal)))
  334. (get-buffer-create
  335. (substring (cl-loop for file in basenames2
  336. for files = (cl-remove-if-not
  337. (lambda (x) (string-match-p (regexp-opt (list file)) x))
  338. basenames)
  339. for nums = (mapcar (lambda (x)
  340. (let* ((match (string-match "\\.\\([0-9]+\\)" x))
  341. (n (if match (match-string 1 x) "0")))
  342. (string-to-number n)))
  343. files)
  344. for min = (if nums (apply 'min nums) 0)
  345. for max = (if nums (apply 'max nums) 0)
  346. concat (concat file "." (if (= min max) (number-to-string min)
  347. (concat "{" (number-to-string min)
  348. "-" (number-to-string max) "}"))
  349. ","))
  350. 0 -1))))
  351. (defun syslog-open-files (files &optional label)
  352. "Insert log FILES into new buffer.
  353. If the optional argument LABEL is non-nil then each new line will be labelled
  354. with the corresponding filename.
  355. When called interactively the FILES are prompted for using `syslog-get-filenames'."
  356. (interactive (list (syslog-get-filenames nil "View log file: ")
  357. (y-or-n-p "Label lines with filenames? ")))
  358. (let ((buf (syslog-create-buffer files)))
  359. (with-current-buffer buf
  360. (let ((inhibit-read-only t))
  361. (set-visited-file-name nil)
  362. (cl-loop for file in (cl-remove-duplicates files :test 'equal)
  363. do (let ((start (goto-char (point-max))))
  364. (insert-file-contents file)
  365. (goto-char (point-max))
  366. (unless (not label)
  367. (forward-line 0)
  368. (goto-char
  369. (apply-on-rectangle
  370. 'string-rectangle-line start (point)
  371. (concat (file-name-nondirectory file) ": ") nil)))
  372. (put-text-property
  373. start (point) 'syslog-filename file))))
  374. (syslog-mode)
  375. (setq default-directory (file-name-directory (car files))))
  376. (switch-to-buffer buf)))
  377. ;;;###autoload
  378. (defun syslog-view (files &optional label rxshowstart rxshowend
  379. rxhidestart rxhideend startdate enddate removedates
  380. highlights bufname)
  381. "Open a view of syslog files with optional filters and highlights applied.
  382. When called interactively the user is prompted for a member of `syslog-views' and the
  383. arguments are determined from the chosen member.
  384. FILES can be either nil in which case the view is applied to the current log file, or
  385. it can be the same as the first argument to `syslog-get-filenames' - a list of cons
  386. cells whose cars are filenames and whose cdrs indicate how many logfiles to include.
  387. LABEL indicates whether or not to label each line with the filename it came from.
  388. RXSHOWSTART, RXSHOWEND and RXHIDESTART, RXHIDEEND are optional regexps which will be
  389. used to filter in/out blocks of buffer lines with `syslog-filter-lines'.
  390. STARTDATE and ENDDATE are optional dates used to filter the lines with `syslog-filter-dates';
  391. they can be either date strings or time lists as returned by `syslog-date-to-time'.
  392. HIGHLIGHTS is a list of cons cells whose cars are regexps and whose cdrs are faces to
  393. highlight those regexps with."
  394. (interactive (cdr (cl-assoc (ido-completing-read "View: " (mapcar 'car syslog-views))
  395. syslog-views :test 'string=)))
  396. (cl-flet ((getstr (str) (and (not (string= str "")) str)))
  397. (let ((rxshowstart (getstr rxshowstart))
  398. (rxshowend (getstr rxshowend))
  399. (rxhidestart (getstr rxhidestart))
  400. (rxhideend (getstr rxhideend))
  401. (startdate (getstr startdate))
  402. (enddate (getstr enddate))
  403. (bufname (getstr bufname)))
  404. (if files (syslog-open-files (syslog-get-filenames files) label))
  405. (if (not (eq major-mode 'syslog-mode))
  406. (error "Not in syslog-mode")
  407. (if rxshowstart
  408. (if rxshowend
  409. (hide-blocks-not-matching rxshowstart rxshowend)
  410. (hide-lines-not-matching rxshowstart)))
  411. (if rxhidestart
  412. (if rxhideend
  413. (hide-blocks-not-matching rxhidestart rxhideend)
  414. (hide-lines-matching rxhidestart)))
  415. (if (or startdate enddate)
  416. (syslog-filter-dates startdate enddate removedates))
  417. (if highlights
  418. (cl-loop for hl in highlights
  419. for (regex . face) = hl
  420. do (highlight-regexp regex face)))
  421. (if bufname (rename-buffer bufname t))))))
  422. (defun syslog-previous-file (&optional arg)
  423. "Open the previous logfile backup, or the next one if a prefix arg is used.
  424. Unix systems keep backups of log files with numbered suffixes, e.g. syslog.1 syslog.2.gz, etc.
  425. where higher numbers indicate older log files.
  426. This function will load the previous log file to the current one (if it exists), or the next
  427. one if ARG is non-nil."
  428. (interactive "P")
  429. (let* ((pair (syslog-get-basename-and-number
  430. (syslog-get-filename-at-point)))
  431. (basename (car pair))
  432. (curver (cdr pair))
  433. (nextver (if arg (1- curver) (1+ curver)))
  434. (nextfile (if (> nextver (1- syslog-number-suffix-start))
  435. (concat basename "." (number-to-string nextver))
  436. basename)))
  437. (let ((inhibit-read-only t))
  438. (cond ((file-readable-p nextfile)
  439. (find-file nextfile))
  440. ((file-readable-p (concat nextfile ".bz2"))
  441. (find-file (concat nextfile ".bz2")))
  442. ((file-readable-p (concat nextfile ".gz"))
  443. (find-file (concat nextfile ".gz")))
  444. ((file-readable-p (concat nextfile ".tgz"))
  445. (find-file (concat nextfile ".tgz"))))
  446. (put-text-property (point-min) (point-max) 'syslog-filename nextfile))))
  447. (defun syslog-next-file nil
  448. "Open the next logfile.
  449. This just calls `syslog-previous-file' with non-nil argument, so we can bind it to a key."
  450. (interactive)
  451. (syslog-previous-file t))
  452. (defun syslog-move-next-file (&optional arg)
  453. "Move to the next file in the current `syslog-mode' buffer.
  454. If ARG is non-nil (or called with numeric prefix arg), move that many
  455. files forward."
  456. (interactive "p")
  457. (cl-loop for i from 1 to arg
  458. do (goto-char (next-single-property-change
  459. (point) 'syslog-filename nil (point-max)))))
  460. (defun syslog-move-previous-file (&optional arg)
  461. "Move to the next file in the current `syslog-mode' buffer.
  462. If ARG is non-nil (or called with numeric prefix arg), move that many
  463. files forward."
  464. (interactive "p")
  465. (cl-loop for i from 1 to arg
  466. do (goto-char (previous-single-property-change
  467. (point) 'syslog-filename nil (point-min)))))
  468. (defun syslog-get-filename-at-point nil
  469. "Get the filename associated with the line at point."
  470. (or (get-text-property (point) 'syslog-filename)
  471. buffer-file-name))
  472. (defun syslog-toggle-filenames (&optional arg)
  473. "Toggle the display of filenames before each line.
  474. If prefix ARG is positive display filenames, and if its negative hide them,
  475. otherwise toggle them."
  476. (interactive "P")
  477. (save-excursion
  478. (ov-set (ov-in) 'invisible nil)
  479. (let* ((start (goto-char (point-min)))
  480. (filename (syslog-get-filename-at-point))
  481. (fileshownp (and filename
  482. (looking-at
  483. (concat "^" (regexp-quote (file-name-nondirectory filename))
  484. ": "))))
  485. (hidep (if arg (prefix-numeric-value arg) 0)))
  486. (let ((inhibit-read-only t))
  487. (while (and (goto-char
  488. (next-single-property-change
  489. (point) 'syslog-filename nil (point-max)))
  490. (/= start (point)))
  491. (if fileshownp
  492. (if (<= hidep 0)
  493. (apply-on-rectangle
  494. 'delete-rectangle-line
  495. start (+ (line-beginning-position 0)
  496. (length (match-string 0)))
  497. nil))
  498. (unless (< hidep 0)
  499. (apply-on-rectangle
  500. 'string-rectangle-line start
  501. (line-beginning-position 0)
  502. (concat (file-name-nondirectory filename) ": ")
  503. nil)
  504. (put-text-property start (point) 'syslog-filename filename)))
  505. (setq start (point)
  506. filename (syslog-get-filename-at-point)
  507. fileshownp (and filename
  508. (looking-at
  509. (concat "^" (regexp-quote (file-name-nondirectory filename))
  510. ": ")))))))
  511. (ov-set (ov-in) 'invisible 'hl)))
  512. ;;;###autoload
  513. (defun syslog-filter-lines (&optional arg)
  514. "Restrict buffer to blocks of text between matching regexps.
  515. If the user only enters one regexp then just filter matching lines instead of blocks.
  516. With prefix ARG: remove matching blocks."
  517. (interactive "p")
  518. (let* ((str (if (> arg 1) "to remove" "to keep"))
  519. (startregex (read-regexp
  520. (format "Regexp matching start lines of blocks %s" str)
  521. (symbol-name (symbol-at-point))))
  522. (endregex (read-regexp
  523. (format "Regexp matching end lines of blocks %s (default=filter start lines only)" str)))
  524. (n (length (overlays-in (point-min) (point-max)))))
  525. (unless (string= startregex "")
  526. (if (> arg 1)
  527. (if (string= endregex "")
  528. (hide-lines-matching startregex)
  529. (hide-blocks-matching startregex endregex))
  530. (if (string= endregex "")
  531. (hide-lines-not-matching startregex)
  532. (hide-blocks-not-matching startregex endregex)))
  533. (if (= n (length (overlays-in (point-min) (point-max))))
  534. (message "No matches found")))))
  535. ;;;###autoload
  536. (defcustom syslog-views nil
  537. "A list of views.
  538. If regexps matching end lines are left blank then lines will be filtered instead of blocks (see `syslog-filter-lines')."
  539. :group 'syslog
  540. :type '(repeat (list (string :tag "Name")
  541. (repeat (cons (string :tag "Base file")
  542. (number :tag "Number of previous files/days")))
  543. (choice (const :tag "No file labels" nil)
  544. (const :tag "Add file labels" t))
  545. (regexp :tag "Regexp matching start lines of blocks to show")
  546. (regexp :tag "Regexp matching end lines of blocks to show")
  547. (regexp :tag "Regexp matching start lines of blocks to hide")
  548. (regexp :tag "Regexp matching end lines of blocks to hide")
  549. (string :tag "Start date")
  550. (string :tag "End date")
  551. (choice (const :tag "Keep matching dates" nil)
  552. (const :tag "Remove matching dates" t))
  553. (repeat (cons (regexp :tag "Regexp to highlight")
  554. (face :tag "Face")))
  555. (string :tag "Buffer name"))))
  556. (defcustom syslog-datetime-regexp
  557. "^\\(?:[^ :]+: \\)?\\(\\(?:\\(?:[[:alpha:]]\\{3\\}\\)?[[:space:]]*[[:alpha:]]\\{3\\}\\s-+[0-9]+\\s-+[0-9:]+\\)\\|\\(?:[0-9]\\{4\\}-[0-9]\\{2\\}-[0-9]\\{2\\}\\s-+[0-9]\\{2\\}:[0-9]\\{2\\}:[0-9]\\{2\\}\\)\\)"
  558. "A regular expression matching the date-time at the beginning of each line in the log file.
  559. It should contain one non-shy subexpression matching the datetime string."
  560. :group 'syslog
  561. :type 'regexp)
  562. (defcustom syslog-log-file-directory "/var/log/"
  563. "The directory in which log files are stored."
  564. :group 'syslog
  565. :type 'directory)
  566. ;;;###autoload
  567. (cl-defun syslog-date-to-time (date &optional safe)
  568. "Convert DATE string to time.
  569. If no year is present in the date then the current year is used.
  570. If DATE can't be parsed then if SAFE is non-nil return nil otherwise throw an error."
  571. (if safe
  572. (let ((time (safe-date-to-time (concat date " " (substring (current-time-string) -4)))))
  573. (if (and (= (car time) 0) (= (cdr time) 0))
  574. nil
  575. time))
  576. (date-to-time (concat date " " (substring (current-time-string) -4)))))
  577. ;;;###autoload
  578. (defun syslog-filter-dates (start end &optional arg)
  579. "Restrict buffer to lines between times START and END (Emacs time lists).
  580. With prefix ARG: remove lines between dates.
  581. If either START or END are nil then treat them as the first/last time in the
  582. buffer respectively."
  583. (interactive (let (firstdate lastdate)
  584. (save-excursion
  585. (goto-char (point-min))
  586. (beginning-of-line)
  587. (re-search-forward syslog-datetime-regexp nil t)
  588. (setq firstdate (match-string 1))
  589. (goto-char (point-max))
  590. (beginning-of-line)
  591. (re-search-backward syslog-datetime-regexp nil t)
  592. (setq lastdate (match-string 1)))
  593. (list (syslog-date-to-time (read-string "Start date and time: "
  594. firstdate nil firstdate))
  595. (syslog-date-to-time (read-string "End date and time: "
  596. lastdate nil lastdate))
  597. current-prefix-arg)))
  598. (let ((start (if (stringp start)
  599. (syslog-date-to-time start)
  600. start))
  601. (end (if (stringp end)
  602. (syslog-date-to-time end)
  603. end)))
  604. (set (make-local-variable 'line-move-ignore-invisible) t)
  605. (goto-char (point-min))
  606. (let* ((start-position (point-min))
  607. (pos (re-search-forward syslog-datetime-regexp nil t))
  608. (intime-p (lambda (time)
  609. (let ((isin (and (or (not end) (time-less-p time end))
  610. (or (not start) (not (time-less-p time start))))))
  611. (and time (if arg (not isin) isin)))))
  612. (keeptime (funcall intime-p (syslog-date-to-time (match-string 1) t)))
  613. (dodelete t))
  614. (while pos
  615. (cond ((and keeptime dodelete)
  616. (hide-lines-add-overlay start-position (point-at-bol))
  617. (setq dodelete nil))
  618. ((not (or keeptime dodelete))
  619. (setq dodelete t start-position (point-at-bol))))
  620. (setq pos (re-search-forward syslog-datetime-regexp nil t)
  621. keeptime (funcall intime-p (syslog-date-to-time (match-string 1) t))))
  622. (if dodelete (hide-lines-add-overlay start-position (point-max))))))
  623. ;;;###autoload
  624. (defun syslog-mode ()
  625. "Major mode for working with system logs.
  626. \\{syslog-mode-map}"
  627. (interactive)
  628. (kill-all-local-variables)
  629. (setq mode-name "syslog")
  630. (setq major-mode 'syslog-mode)
  631. (use-local-map syslog-mode-map)
  632. ;; Menu definition
  633. (easy-menu-define nil syslog-mode-map "test"
  634. `("Syslog"
  635. ["Quit" quit-window :help "Quit and bury this buffer" :key "q"]
  636. ["Revert buffer" revert-buffer :help "View the function at point" :key "R"]
  637. ["Show all" hide-lines-show-all :help "Show all hidden lines/blocks" :key "g"]
  638. ["Filter lines..." syslog-filter-lines :help "Show/hide blocks of text between matching regexps" :key "/"]
  639. ["Filter dates..." syslog-filter-dates :help "Show/hide lines between start and end dates" :key "C-/"]
  640. ["Kill hidden" hide-lines-kill-hidden :help "Kill (with prefix delete) hidden lines" :key "k"]
  641. ["Jump to boot start" syslog-boot-start :help "Jump forward in the log to when the system booted" :key "<C-down>"]
  642. ["Open previous log file" syslog-previous-file :help "Open previous logfile backup" :key "<"]
  643. ["Open next log file" syslog-next-file :help "Open next logfile backup" :key ">"]
  644. ["Move to previous log file" syslog-move-previous-file :help "Move to previous logfile in buffer" :key "<M-up>"]
  645. ["Move to next log file" syslog-move-next-file :help "Move to next logfile in buffer" :key "<M-down>"]
  646. ["Open log files..." syslog-open-files :help "Insert log files into new buffer" :key "o"]
  647. ["Append files..." syslog-append-files :help "Append files into current buffer" :key "a"]
  648. ["Prepend files..." syslog-prepend-files :help "Prepend files into current buffer" :key "p"]
  649. ["Toggle filenames" syslog-toggle-filenames :help "Toggle display of filenames" :key "t"]
  650. ["Find file at point" ffap :help "Find file at point" :key "f"]
  651. ["Whois" syslog-whois-reverse-lookup :help "Perform whois lookup on hostname at point" :key "W"]
  652. ["Count matches" syslog-count-matches :help "Count strings which match the given pattern" :key "c"]
  653. ["Dired" (lambda nil (interactive) (dired syslog-log-file-directory)) :help "Enter logfiles directory" :keys "D"]
  654. ["Shell command" syslog-shell-command :help "Execute shell command (as root if prefix arg used)" :key "!"]
  655. ["Highlight..." (keymap "Highlight"
  656. (regexp menu-item "Regexp" highlight-regexp
  657. :help "Highlight each match of regexp"
  658. :keys "h r")
  659. (phrase menu-item "Phrase" highlight-phrase
  660. :help "Highlight each match of phrase"
  661. :keys "h p")
  662. (lines menu-item "Lines matching regexp" highlight-lines-matching-regexp
  663. :help "Highlight lines containing match of regexp"
  664. :keys "h l")
  665. (unhighlight menu-item "Unhighlight regexp" unhighlight-regexp
  666. :help "Remove highlighting"
  667. :keys "h u"))]
  668. ["Open stored view..." syslog-view :help "Open a stored view of syslog files" :key "v"]
  669. ["Edit stored views..." (lambda nil (interactive) (customize-variable 'syslog-views)) :help "Customize `syslog-views'"]
  670. ["---" "---"]))
  671. ;; font locking
  672. (make-local-variable 'font-lock-defaults)
  673. (setq font-lock-defaults '(syslog-font-lock-keywords t t nil ))
  674. (buffer-disable-undo)
  675. (toggle-read-only 1)
  676. (run-hooks 'syslog-mode-hook))
  677. (defvar syslog-boot-start-regexp "unix: SunOS"
  678. "Regexp to match the first line of boot sequence.")
  679. (defun syslog-count-matches (regexp)
  680. "Count strings which match the given pattern."
  681. (interactive (list (read-regexp "How many matches for regexp"
  682. (symbol-name (symbol-at-point)))))
  683. (message "%s occurrences" (count-matches regexp
  684. (point-min)
  685. (point-max) nil)))
  686. (defun syslog-boot-start ()
  687. "Jump forward in the log to when the system booted."
  688. (interactive)
  689. (search-forward-regexp syslog-boot-start-regexp (point-max) t)
  690. (beginning-of-line))
  691. (defun syslog-whois-reverse-lookup (arg search-string)
  692. "This is a wrapper around the `whois' command using symbol at point as default search string.
  693. Also `whois-server-name' is set to `whois-reverse-lookup-server'.
  694. The ARG and SEARCH-STRING arguments are the same as for `whois'."
  695. (interactive (list current-prefix-arg
  696. (let* ((symb (symbol-at-point))
  697. (default (replace-regexp-in-string ":[0-9]+$" "" (symbol-name symb))))
  698. (read-string (if symb (concat "Whois (default " default "): ")
  699. "Whois: ") nil nil default))))
  700. (let ((whois-server-name whois-reverse-lookup-server))
  701. (whois arg search-string)))
  702. (defface syslog-ip
  703. '((t :underline t :slant italic :weight bold))
  704. "Face for IPs"
  705. :group 'syslog)
  706. (defface syslog-file
  707. (list (list t :weight 'bold
  708. :inherit (if (facep 'diredp-file-name)
  709. 'diredp-file-name
  710. 'dired-ignored)))
  711. "Face for filenames"
  712. :group 'syslog)
  713. (defface syslog-hour
  714. '((t :weight bold :inherit font-lock-type-face))
  715. "Face for hours"
  716. :group 'syslog)
  717. (defface syslog-error
  718. '((t :weight bold :foreground "red"))
  719. "Face for errors"
  720. :group 'syslog)
  721. (defface syslog-warn
  722. '((t :weight bold :foreground "goldenrod"))
  723. "Face for warnings"
  724. :group 'syslog)
  725. (defface syslog-info
  726. '((t :weight bold :foreground "deep sky blue"))
  727. "Face for info lines"
  728. :group 'syslog)
  729. (defface syslog-debug
  730. '((t :weight bold :foreground "medium spring green"))
  731. "Face for debug lines"
  732. :group 'syslog)
  733. (defface syslog-su
  734. '((t :weight bold :foreground "firebrick"))
  735. "Face for su and sudo"
  736. :group 'syslog)
  737. (defface syslog-hide
  738. '((t :foreground "black" :background "black"))
  739. "Face for hiding text"
  740. :group 'syslog)
  741. ;; Keywords
  742. ;; TODO: Seperate the keywords into a list for each format, rather than one for all.
  743. ;; Better matching of dates (even when not at beginning of line).
  744. (defvar syslog-font-lock-keywords
  745. '(("\"[^\"]*\"" . 'font-lock-string-face)
  746. ("'[^']*'" . 'font-lock-string-face)
  747. ;; Filename at beginning of line
  748. ("^\\([^ :]+\\): " 1 'syslog-file append)
  749. ;; Hours: 17:36:00
  750. ("\\(?:^\\|[[:space:]]\\)\\([[:digit:]]\\{1,2\\}:[[:digit:]]\\{1,2\\}\\(:[[:digit:]]\\{1,2\\}\\)?\\)\\(?:$\\|[[:space:]]\\)" 1 'syslog-hour append)
  751. ;; Date
  752. ("\\(?:^\\|[[:space:]]\\)\\([[:digit:]]\\{1,2\\}/[[:digit:]]\\{1,2\\}/[[:digit:]]\\{2,4\\}\\)\\(?:$\\|[[:space:]]\\)" 1 'syslog-hour append)
  753. ;; Dates: May 9 15:52:34
  754. ("^\\(?:[^ :]+: \\)?\\(\\(?:[[:alpha:]]\\{3\\}\\)?[[:space:]]*[[:alpha:]]\\{3\\}\\s-+[0-9]+\\s-+[0-9:]+\\)" 1 'font-lock-type-face t)
  755. ;; Su events
  756. ("\\(su:.*$\\)" 1 'syslog-su t)
  757. ("\\(sudo:.*$\\)" 1 'syslog-su t)
  758. ("\\[[^]]*\\]" . 'font-lock-comment-face)
  759. ;; IPs
  760. ("[[:digit:]]\\{1,3\\}\\.[[:digit:]]\\{1,3\\}\\.[[:digit:]]\\{1,3\\}\\.[[:digit:]]\\{1,3\\}" 0 'syslog-ip append)
  761. ("\\<[Ee][Rr][Rr]\\(?:[Oo][Rr][Ss]?\\)?\\>" 0 'syslog-error append)
  762. ("\\<[Ii][Nn][Ff][Oo]\\>" 0 'syslog-info append)
  763. ("\\<[Cc][Rr][Ii][Tt][Ii][Cc][Aa][Ll]\\>" 0 'syslog-error append)
  764. ("STARTUP" 0 'syslog-info append)
  765. ("CMD" 0 'syslog-info append)
  766. ("\\<[Ww][Aa][Rr][Nn]\\(?:[Ii][Nn][Gg]\\)?\\>" 0 'syslog-warn append)
  767. ("\\<[Dd][Ee][Bb][Uu][Gg]\\>" 0 'syslog-debug append)
  768. ("(EE)" 0 'syslog-error append)
  769. ("(WW)" 0 'syslog-warn append)
  770. ("(II)" 0 'syslog-info append)
  771. ("(NI)" 0 'syslog-warn append)
  772. ("(!!)" 0 'syslog-debug append)
  773. ("(--)" 0 'syslog-debug append)
  774. ("(\\*\\*)" 0 'syslog-debug append)
  775. ("(==)" 0 'syslog-debug append)
  776. ("(\\+\\+)" 0 'syslog-debug append))
  777. "Expressions to hilight in `syslog-mode'.")
  778. ;;; Setup functions
  779. (defun syslog-find-file-func ()
  780. "Invoke `syslog-mode' if the buffer appears to be a system logfile.
  781. and another mode is not active.
  782. This function is added to `find-file-hooks'."
  783. (if (and (eq major-mode 'fundamental-mode)
  784. (looking-at syslog-sequence-start-regexp))
  785. (syslog-mode)))
  786. (defun syslog-add-hooks ()
  787. "Add a default set of syslog-hooks.
  788. These hooks will activate `syslog-mode' when visiting a file
  789. which has a syslog-like name (.fasta or .gb) or whose contents
  790. looks like syslog. It will also turn enable fontification for `syslog-mode'."
  791. ;; (add-hook 'find-file-hooks 'syslog-find-file-func)
  792. (add-to-list 'auto-mode-alist
  793. '("\\(messages\\(\\.[0-9]\\)?\\|SYSLOG\\)\\'" . syslog-mode)))
  794. ;; Setup hooks on request when this mode is loaded.
  795. (if syslog-setup-on-load (syslog-add-hooks))
  796. ;; done loading
  797. (run-hooks 'syslog-mode-load-hook)
  798. (provide 'syslog-mode)
  799. ;;; syslog-mode.el ends here
  800. ;;; (magit-push)
  801. ;;; (yaoddmuse-post "EmacsWiki" "syslog-mode.el" (buffer-name) (buffer-string) "update")