83

Working with log files in Emacs

 6 years ago
source link: https://writequit.org/articles/working-with-logs-in-emacs.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

Reading logs

The number one thing that I expect someone would want to do with logs is to make them readable. "Readable" means a few different things to different people, so this may not hit 100% of the mark, but there are definitely some things you can do to make logs more readable.

Logs that are undergoing writes

I commonly use tail -F for monitoring logs on Linux systems, and Emacs has something like that also. To watch a file for changes and update the buffer automatically, open the file and then invoke M-x auto-revert-tail-mode. Emacs will then behave just like tail -f for the currently open file.

Dealing with very large log files

50mb? Okay, no problem.

342mb? Sure, Emacs can handle that.

2.7gb? Well, maybe that is getting a bit large for a single buffer, especially when we want to add searching and all that to the mix. It's not that Emacs can't handle it, it's just that some operations you want to do may be a bit slower than you'd like.

Enter vlf, vlf is short for "View Large Files" and is a very nice way to handle viewing extremely large files in Emacs, not just log files. I've used it successfully for reading log files over 10 gigabytes. I'll leave it to you to read the page about the features it provides, but suffice it to say that it breaks up large files into manageable chunks, and then provides tools to operate on either a small chunk, or across all the chunks of a very large file.

Install vlf, and try to open a 1.2gb log file and you'll get a prompt that says

File my_big_log.log is large (1.2G): open normally (o), open with vlf (v) or abort (a)

You can then hit either o, v, or a depending on what you'd like to do. You can even configure the limit at which it asks you by configuring the large-file-warning-threshold setting.

Once you open a file with VLF, you can scroll batches by either going to the bottom/top of the chunk and then going to the next/previous line, or you can use C-c C-v and then n or p to go to the next or previous chunk. VLF's bindings all start with the C-c C-v prefix, so some of the helpful ones are:

  • C-c C-v s and C-c C-v r for searching forward and backward through the file
  • C-c C-v o builds an "occur" index over the entire file. We'll talk more about occur in the "searching" section
  • C-c C-v l jump to a particular line in the big file

You can even edit the file, saving only the batch/chunk you're currently in.

There are even more features, so check out the VLF page for the full documentation if you routinely work with large files.

Using a major mode

Since Elasticsearch is a Java application, it uses logs that are in one of those formats that is common to most other Java applications, the log4j format.

There are a few different options, depending on what sort of logs you are viewing and what format they're in, they're log4j-mode1 which is stripped down and doesn't handle much other than syntax highlighting for different log levels, as well as rudimentary support for filtering included and excluded content from the log file in question. At the time of this writing the site is down, but you can still download the library from MELPA.

log4j-mode.png

Figure 1: An example of what log4j-mode looks like

Along the same lines (focused on a particular format), there is rails-log-mode which is designed for Rails logs, however, I cannot comment on it as I don't use Rails.

Finally, on the more generic side there is a mode called Logview that provides a more feature-rich experience for viewing logs. At the time of this writing though, it doesn't support the timestamp format that ES uses (ISO-8601 yyyy-MM-dd'T'HH:mm:ss,SSS) It also handles ES' timestamp format. Logview has a lot of features, however I tend to stick with my regular editing habits. I hope to change this in the future though, so give it a shot if it looks interesting to you.

I personally stick with log4j-mode, even though it doesn't have many features other than syntax highlighting. There are a few things you can configure other than faces, and you can jump between log messages with M-} and M-{, but generally we don't look at major modes for log mining features.

Sometimes when viewing a file, you don't have enough vertical space. To alleviate this, Emacs has a feature called follow-mode. If you split the buffer vertically with C-x 3 and then invoke M-x follow-mode Emacs will treat the pane on the left as the "top" of the buffer and the pane on the right as the "bottom", making the viewing part essentially twice as high. If you scroll in one pane the other pane will follow. It's great for viewing very large files as well as large programming methods!

Helpful minor modes

Okay, there aren't a great number of helpful major modes, but we do have a lot of minor modes to help out with reading the logs, some that are built in, and some that aren't.

I find that line numbers aren't very helpful for reading logs, but I do find that hl-line-mode is invaluable for highlighting the current line you are viewing in the logs. There's also global-hl-line-mode if you want it everywhere. hl-line is built in to Emacs, so there's nothing you'll need to download/install to use it.

It's uncommon in my experience to want to edit the log while viewing it (though you may want to delete lines from the log), so in that case, I heartily recommend view-mode. Another built-in mode, View mode is designed for scanning buffers by the screenful (just what we want to do with logs!). It helpfully allows using a different keymap, so that you can switch to more "navigation" style keybindings. It's easy to invoke, simply use M-x view-mode while on a file; I have the following for my log4j-mode hooks

(use-package log4j-mode
  :ensure t
  :disabled t
  :init
  (add-hook #'log4j-mode-hook #'view-mode)
  (add-hook #'log4j-mode-hook #'read-only-mode)
  (add-hook #'log4j-mode-hook 'eos/turn-on-hl-line))

And in my customized view-mode bindings

(use-package view
  :config
  (defun View-goto-line-last (&optional line)
    "goto last line"
    (interactive "P")
    (goto-line (line-number-at-pos (point-max))))

  (define-key view-mode-map (kbd "e") 'View-scroll-half-page-forward)
  (define-key view-mode-map (kbd "u") 'View-scroll-half-page-backward)

  ;; less like
  (define-key view-mode-map (kbd "N") 'View-search-last-regexp-backward)
  (define-key view-mode-map (kbd "?") 'View-search-regexp-backward?)
  (define-key view-mode-map (kbd "g") 'View-goto-line)
  (define-key view-mode-map (kbd "G") 'View-goto-line-last)
  ;; vi/w3m like
  (define-key view-mode-map (kbd "h") 'backward-char)
  (define-key view-mode-map (kbd "j") 'next-line)
  (define-key view-mode-map (kbd "k") 'previous-line)
  (define-key view-mode-map (kbd "l") 'forward-char))

I primarily view logs by scrolling half-pages, so I can keep context more easily. If "e" and "u" seem like strange choices to you, it's because I type with a Dvorak keyboard layout and those keys are where "d" and "f" would be on a regular Qwerty keyboard.

As an alternative to view-mode, I have my own Hydra state that I use for navigation when I don't feel like holding down modifiers (I am not an EVIL user, so this may not apply if you are)

(defhydra eos/nav-mode (:foreign-keys run)
  "[NAV-MODE] q or i to exit"
  ("C-h" hl-line-mode)
  ("t" toggle-truncate-lines)
  ("a" beginning-of-line)
  ("l" forward-char)
  ("h" backward-char)
  ("n" next-line)
  ("j" next-line)
  ("p" previous-line)
  ("k" previous-line)
  ("e" View-scroll-half-page-forward)
  ("u" View-scroll-half-page-backward)
  ("SPC" scroll-up-command)
  ("S-SPC" scroll-down-command)
  ("<" beginning-of-buffer)
  (">" end-of-buffer)
  ("." end-of-buffer)
  ("C-'" nil)
  ("d" (when (y-or-n-p "Kill buffer?")
         (kill-this-buffer))
   :exit t)
  ("/" isearch-forward-regexp :exit t)
  ("?" isearch-backward-regexp :exit t)
  ("i" nil :exit t)
  ("q" nil :exit t))

I bind this to M-V2, so I can easily invoke it in any buffer to enter a navigation-like mode, with some keybindings to make it easier to scan through pages and pages of logs.

Hiding parts of the log file

Sometimes you want to limit the scope of what you're reading, say, between a couple of timestamps. There are a number of ways to do this, but one way that is quite common in Emacs is narrowing. When narrowing, only the text you want will be displayed in the buffer.

I recommend Endless Parentheses' narrow-or-widen-dwim for this, because it will allow you to use a single binding for narrowing and widening (undoing the narrow), as well as some nice criteria that works outside of log viewing as well. For simplicity's and future-proofing sake I'll reproduce the one I use here, but visit the original page to see the full explanation.

(defun eos/narrow-or-widen-dwim (p)
  "Widen if buffer is narrowed, narrow-dwim otherwise.
Dwim means: region, org-src-block, org-subtree, or
defun, whichever applies first. Narrowing to
org-src-block actually calls `org-edit-src-code'.

With prefix P, don't widen, just narrow even if buffer
is already narrowed."
  (interactive "P")
  (declare (interactive-only))
  (cond ((and (buffer-narrowed-p) (not p)) (widen))
        ((region-active-p)
         (narrow-to-region (region-beginning)
                           (region-end)))
        ((derived-mode-p 'org-mode)
         ;; `org-edit-src-code' is not a real narrowing
         ;; command. Remove this first conditional if
         ;; you don't want it.
         (cond ((ignore-errors (org-edit-src-code) t)
                (delete-other-windows))
               ((ignore-errors (org-narrow-to-block) t))
               (t (org-narrow-to-subtree))))
        ((derived-mode-p 'latex-mode)
         (LaTeX-narrow-to-environment))
        (t (narrow-to-defun))))

I bind mine to C-x C-n so it's easy to hit

(global-set-key (kbd "C-x C-n") #'eos/narrow-or-widen-dwim)

Since this works on a highlighted region, you can select the text you want to limit your actions on, and then hit C-x C-n to shrink the buffer down to just that text. Do whatever analysis you need to, and then hit C-x C-n again to widen back to the big file.

before-narrowing.png

Figure 2: A log buffer with text selected prior to narrowing

after-narrowing.png

Figure 3: The same buffer after narrowing to the selected region

Want to have two copies of the buffer, on that is narrowed and one that's un-narrowed? Create an indirect buffer! Hit C-x 4 c to "clone" the buffer, you can then narrow each copy independently, while keeping them synchronized for other actions (like filtering or modifying).

Hiding structured data

In addition to regular log files, I deal a lot with structured log-like data, almost entirely in JSON format. In addition to the regulars like json-mode and json-reformat for just viewing the data, sometimes it's helpful to be able to hide certain parts of the structured data. This is something that Emacs' built-in Hideshow3 is great at.

Before describing it, here's a minimal configuration for it:

(use-package hideshow
  :bind (("C-c TAB" . hs-toggle-hiding)
         ("C-\\" . hs-toggle-hiding)
         ("M-+" . hs-show-all))
  :init (add-hook #'prog-mode-hook #'hs-minor-mode)
  :diminish hs-minor-mode
  :config
  (setq hs-special-modes-alist
        (mapcar 'purecopy
                '((c-mode "{" "}" "/[*/]" nil nil)
                  (c++-mode "{" "}" "/[*/]" nil nil)
                  (java-mode "{" "}" "/[*/]" nil nil)
                  (js-mode "{" "}" "/[*/]" nil)
                  (json-mode "{" "}" "/[*/]" nil)
                  (javascript-mode  "{" "}" "/[*/]" nil)))))

The main way I use this is to put the point (represented as | in the code below) inside of an expression and then hit C-c TAB or C-\, which changes

{
  "foo": {|
    "bar": "baz"
  },
  "eggplant": 11
}
{
  "foo": { ... },
  "eggplant": 11
}

Hitting C-c TAB or C-\ on the block again shows the block. This is especially nice when you have gigantic JSON files that you want to see different parts of at the same time, as seen below.

hideshow.png

Figure 4: An example of Hideshow collapsing part of a JSON buffer

There are a whole bunch of ways to make this nice, I especially recommend reading the manual for Hideshow and also checking out hs-hide-level where you can hide everything at a certain nested level.

Finally, if you want to go all-in for a hierarchical view of JSON data, I'd recommend json-navigator.


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK