Skip to content
/ hl Public

A fast and powerful log viewer and processor that translates JSON logs or logfmt logs into a pretty human-readable format.

License

Notifications You must be signed in to change notification settings

pamburus/hl

Folders and files

NameName
Last commit message
Last commit date

Latest commit

d06a8a0 · Dec 21, 2024
Jan 28, 2024
Jul 1, 2023
Dec 20, 2024
Dec 21, 2024
May 9, 2024
Dec 21, 2024
Dec 21, 2024
Jun 8, 2024
Jun 11, 2023
Apr 30, 2024
Jun 19, 2024
Jun 27, 2024
Dec 12, 2024
Dec 18, 2024
Jun 10, 2024
Dec 21, 2024
Dec 12, 2024
Dec 12, 2024
Apr 6, 2024
Nov 23, 2024
Jun 8, 2024
Jun 10, 2024
May 1, 2024
Dec 21, 2024
Dec 21, 2024
Aug 4, 2020
Dec 21, 2024
Dec 21, 2024
Jan 28, 2024
Dec 11, 2024
Dec 12, 2024
Dec 12, 2024

Repository files navigation

hl Build Status Coverage Status

A fast and powerful log viewer and processor that translates JSON or logfmt logs into a pretty human-readable format. High performance and convenient features are the main goals.

Features overview

  • Automatic usage of the less pager by default for convenience.
  • Log streaming with the -P flag that disables the pager.
  • Log record filtering by field key/value pairs with the -f option with support for hierarchical keys.
  • Quick and easy filtering by level with the -l option.
  • Quick and easy filtering by timestamp range using the --since and --until options and intuitive formats:
    • RFC-3339 timestamp format.
    • Current configured timestamp output format with the -t option or environment variable.
    • Human friendly shortcuts like today, yesterday, friday or relative offsets like -3h or -14d.
  • Quick and easy hiding and revealing of fields with the -h option.
  • Hide empty fields with the -e flag.
  • Lightning fast message sorting with automatic indexing for local files using the -s flag.
    • Handles ~1 GiB/s for the first scan and allows fast filtering by timestamp range and level without scanning the data afterwards.
    • Works fast with hundreds of local files containing hundreds of gigabytes of data.
    • Reindexes large, growing files at lightning speed, skipping unmodified blocks, ~10 GiB/s.
  • Follow mode with live message sorting by timestamp from different sources using the -F flag and preview of several recent messages with the --tail option.
  • Custom complex queries that can include and/or conditions and much more.
  • Non-JSON prefixes with --allow-prefix flag.
  • Displays timestamps in UTC by default and supports easy timezone switching with the -Z option and the -L flag for a local timezone.
  • Customizable via configuration file and environment variables, supports easy theme switching and custom themes.

Performance comparison chart

Performance comparison with humanlog, hlogf and fblog on a 2.3 GiB log file

performance chart

Installation options

  • Install using homebrew on macOS or Linux

    brew install pamburus/tap/hl
  • Download and extract using curl and tar on macOS

    curl -sSfL https://github.com/pamburus/hl/releases/latest/download/hl-macos.tar.gz | tar xz
  • Download and extract using curl and tar on Linux

    curl -sSfL https://github.com/pamburus/hl/releases/latest/download/hl-linux-x86_64-musl.tar.gz | tar xz
  • Install AUR package on Arch Linux

    yay -S hl-log-viewer-bin
  • Install using cargo

    cargo install --locked --git https://github.com/pamburus/hl.git
  • Run using nix

    nix run github:pamburus/hl

    or install with nix profile:

    nix profile install github:pamburus/hl
  • Install the package using nix-flakes

    Example how to update nix configuration
    {
        inputs = {
            nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
            hl.url = "github:pamburus/hl";
        };
        outputs = {nixpkgs, hl, ...}:
        let
            system = "x86_64-linux";
        in
        {
            # this is just an example!
            nixosConfigurations.yourHost = nixpkgs.lib.nixosSystem {
                inherit system;
                modules = [
                    ({...}: {
                      environment.systemPackages = [
                        hl.packages.${system}
                      ];
                    })
                ];
            };
        };
    }
  • Download latest release from download page

Examples

Screenshot

screenshot-light screenshot-dark

See other screenshots

Features and usage

Concatenation of multiple log files

  • Concatenate all log files

    Command

    hl *.log

    Concatenates and displays all *.log files found in the current directory.

Support for compressed (bzip2, gzip, xz, zstd) log files

  • Concatenate all log files including compressed log files

    Command

    hl $(ls -tr /var/log/example/*.{log,log.gz,log.zst,s})

    Concatenates and displays all *.log, *.log.gz, *.log.zst and *.s (will detect compression) files found in /var/log/example/.

Automatic usage of pager

  • Use the default pager with the default parameters

    Command

    hl example.log

    Automatically opens less pager with the default parameters.

  • Override options for default pager

    Command

    LESS=-SR hl example.log

    Opens less pager with disabled line wrapping.

  • Use custom pager

    Command

    PAGER="most -w" hl example.log

    Opens most pager with -w option.

Quick filtering by log level

  • Errors only

    Command

    hl -l e

    Displays only error log level messages.

  • Errors and warnings

    Command

    hl -l w

    Displays only warning and error log level messages.

  • Errors, warnings and informational

    Command

    hl -l i

    Displays all log messages except debug level messages.

Using live log streaming

  • Command

    tail -f example.log | hl -P

    Tracks changes in the example.log file and displays them immediately. Flag -P disables automatic using of pager in this case.

Filtering by field values

  • Command

    hl example.log --filter component=tsdb

    Displays only messages where the component field has the value tsdb.

  • Command

    hl example.log -f component!=tsdb -f component!=uninteresting

    Displays only messages where the component field has a value other than tsdb or uninteresting.

  • Command

    hl example.log -f provider~=string

    Displays only messages where the provider field contains the string sub-string.

  • Command

    hl example.log -f 'provider!~=string'

    Displays only messages where the provider field does not contain the string sub-string.

Performing complex queries

  • Command

    hl my-service.log --query 'level > info or status-code >= 400 or duration > 0.5'

    Displays messages that either have a level higher than info (i.e. warning or error) or have a status code field with a numeric value >= 400 or a duration field with a numeric value >= 0.5.

  • Command

    hl my-service.log -q '(request in (95c72499d9ec, 9697f7aa134f, bc3451d0ad60)) or (method != GET)'

    Displays all messages that have the 'request' field with one of these values, or the 'method' field with a value other than 'GET'.

  • Complete set of supported operators

    • Logical operators
      • Logical conjunction - and, &&
      • Logical disjunction - or, ||
      • Logical negation - not, !
    • Comparison operators
      • Equal - eq, =
      • Not equal - ne, !=
      • Greater than - gt, >
      • Greater or equal - ge, >=
      • Less than - lt, <
      • Less or equal - le, <=
    • String matching operators
      • Sub-string check - (contain, ~=), (not contain, !~=)
      • Wildcard match - (like), (not like)
        • Wildcard characters are: * for zero or more characters and ? for a single character
      • Regular expression match - (match, ~~=), (not match, !~~=)
    • Operators with sets
      • Test if value is one of the values in a set - in (v1, v2), not in (v1, v2)
      • Test if value is one of the values in a set loaded from a file - in @filename, not in @filename, assuming that each element is a line in the file, which can be either a simple string or a JSON string
      • Test if value is one of the values in a set loaded stdin - in @-, not in @-
  • Notes

    • Special field names that are reserved for filtering by predefined fields regardless of the actual source field names used to load the corresponding value: level, message, caller and logger.

    • To address a source field with one of these names instead of predefined fields, add a period before its name, i.e., .level will perform a match against the "level" source field.

    • To address a source field by its exact name, use a JSON-formatted string, i.e. -q '".level" = info'.

    • To specify special characters in field values, also use a JSON-formatted string, i.e.

      hl my-service.log -q 'message contain "Error:\nSomething unexpected happened"'

Filtering by time range

  • Command

    hl example.log --since 'Jun 19 11:22:33' --until yesterday

    Displays only messages that occurred after Jun 19 11:22:33 UTC of the current year (or the previous year if the current date is less than Jun 19 11:22:33) and before yesterday midnight.

  • Command

    hl example.log --since -3d

    Displays only messages from the past 72 hours.

  • Command

    hl example.log --until '2021-06-01 18:00:00' --local

    Displays only messages that occurred before 6 PM local time on June 1, 2021, and shows timestamps in local time.

Hiding or revealing selected fields

  • Command

    hl example.log --hide provider

    Hides field provider.

  • Command

    hl example.log --hide '*' --hide '!provider'

    Hides all fields except provider.

  • Command

    hl example.log -h headers -h body -h '!headers.content-type'

    Hides fields headers and body but shows a single sub-field content-type inside field headers.

Sorting messages chronologically

  • Command

    hl -s *.log

    Displays log messages from all log files in the current directory sorted in chronological order.

Sorting messages chronologically with following the changes

  • Command

    hl --sync-interval-ms 500 -F <(kubectl logs -l app=my-app-1 -f) <(kubectl logs -l app=my-app-2 -f)

    Runs without a pager in follow mode by merging messages from the outputs of these 2 commands and sorting them chronologically within a custom 500ms interval.

  • Command

    hl -F --tail 100 app1.log app2.log app3.log

    Runs without a pager in follow mode, following the changes in three log files in the current directory and sorting them chronologically at a default interval of 100ms. Preloads 100 lines from the end of each file before filtering.

Configuration files

  • Configuration files are automatically loaded if found in predefined platform-specific locations.

    OS System-Wide Location User Profile Location
    macOS /etc/hl/config.{yaml,toml,json} ~/.config/hl/config.{yaml,toml,json}
    Linux /etc/hl/config.{yaml,toml,json} ~/.config/hl/config.{yaml,toml,json}
    Windows %PROGRAMDATA%\hl\config.{yaml,toml,json} %USERPROFILE%\AppData\Roaming\hl\config.{yaml,toml,json}
  • The path to the configuration file can be overridden using the HL_CONFIG environment variable or the --config command-line option.

    The order in which the configuration files are searched and loaded is as follows:

    1. The system-wide location.
    2. The user profile location.
    3. The location specified by the HL_CONFIG environment variable (unless the --config option is used).
    4. The locations specified by the --config option (can be specified multiple times).

    If a configuration file is found in multiple locations, the file in each subsequent location overrides only the parameters it contains.

    If HL_CONFIG or --config specifies - or an empty string, all default locations and any locations specified by previous --config options are discarded. The search for the configuration file locations starts over.

    To disable loading of configuration files and use the built-in defaults, --config - can be used.

  • All parameters in the configuration file are optional and can be omitted. In this case, default values are used.

Default configuration file

Environment variables

  • Many parameters that are defined in command line arguments and configuration files can also be specified by environment variables.

Precedence of configuration sources (from lowest priority to highest priority)

  • Configuration file
  • Environment variables
  • Command-line arguments

Environment variables examples

  • HL_TIME_FORMAT='%y-%m-%d %T.%3N' overrides the time format specified in the configuration file.
  • HL_TIME_ZONE=Europe/Berlin overrides the time zone specified in the configuration file.
  • HL_CONCURRENCY=4 overrides the concurrency limit specified in the configuration file.
  • HL_PAGING=never specifies the default value for the paging option, but it can be overridden by command line arguments.

Themes

Stock themes

Selecting current theme

  • Using theme value in the configuration file.
  • Using environment variable, i.e. HL_THEME=classic, overrides the value specified in configuration file.
  • Using command-line argument, i.e. --theme classic, overrides all other values.

Selecting themes with preview

To select themes with preview fzf tool can be used like this:

hl --list-themes | fzf --preview-window="top,80%" --preview="head -n 100 example.log | hl -c --theme {}"

Custom themes

  • Custom themes are automatically loaded when found in a predefined platform-specific location.

    OS Location
    macOS ~/.config/hl/themes/*.{yaml,toml,json}
    Linux ~/.config/hl/themes/*.{yaml,toml,json}
    Windows %USERPROFILE%\AppData\Roaming\hl\themes*.{yaml,toml,json}
  • Format description

    • Section elements contains styles for predefined elements.

    • Section levels contains optional overrides for styles defined in elements sections per logging level, which are [debug, info, warning, error].

    • Each element style contains optional background, foreground and modes parameters.

    • Example

      elements:
          <element>:
              foreground: <color>
              background: <color>
              modes: [<mode>, <mode>, ...]
      levels:
          <level>:
              <element>:
                  foreground: <color>
                  background: <color>
                  modes: [<mode>, <mode>, ...]
    • Color format is one of

      • Keyword default specifies default color defined by the terminal.
      • ASCII basic color name, one of
        • black
        • red
        • green
        • yellow
        • blue
        • magenta
        • cyan
        • white
        • bright-black
        • bright-red
        • bright-green
        • bright-yellow
        • bright-blue
        • bright-magenta
        • bright-cyan
        • bright-white
      • 256-color palette code, from 0 to 255.
      • RGB color in hex web color format, i.e. #FFFF00 for bright yellow color.
    • Modes is a list of additional styles, each of them is one of

      • bold
      • faint
      • italic
      • underline
      • slow-blink
      • rapid-blink
      • reverse
      • conceal
      • crossed-out

Used terminal color schemes

iTerm2

Alacritty

  • One Dark Neo
    • Note: It is recommended to use draw_bold_text_with_bright_colors: true setting
  • Light
    • Note: It is recommended to use draw_bold_text_with_bright_colors: false setting

Complete set of options and flags

JSON and logfmt log converter to human readable representation

Usage: hl [OPTIONS] [FILE]...

Arguments:
  [FILE]...  Files to process

Options:
      --config <FILE>                    Configuration file path [env: HL_CONFIG=]
  -s, --sort                             Sort messages chronologically
  -F, --follow                           Follow input streams and sort messages chronologically during time frame set by --sync-interval-ms option
      --tail <N>                         Number of last messages to preload from each file in --follow mode [default: 10]
      --sync-interval-ms <MILLISECONDS>  Synchronization interval for live streaming mode enabled by --follow option [default: 100]
      --paging <WHEN>                    Control pager usage (HL_PAGER or PAGER) [env: HL_PAGING=] [default: auto] [possible values: auto, always, never]
  -P                                     Handful alias for --paging=never, overrides --paging option
      --help                             Print help
  -V, --version                          Print version

Filtering Options:
  -l, --level <LEVEL>    Filter messages by level [env: HL_LEVEL=]
      --since <TIME>     Filter messages by timestamp >= <TIME> (--time-zone and --local options are honored)
      --until <TIME>     Filter messages by timestamp <= <TIME> (--time-zone and --local options are honored)
  -f, --filter <FILTER>  Filter messages by field values [k=v, k~=v, k~~=v, 'k!=v', 'k!~=v', 'k!~~=v'] where ~ does substring match and ~~ does regular expression match
  -q, --query <QUERY>    Filter using query, accepts expressions from --filter and supports '(', ')', 'and', 'or', 'not', 'in', 'contain', 'like', '<', '>', '<=', '>=', etc

Output Options:
      --color [<WHEN>]        Color output control [env: HL_COLOR=] [default: auto] [possible values: auto, always, never]
  -c                          Handful alias for --color=always, overrides --color option
      --theme <THEME>         Color theme [env: HL_THEME=] [default: universal]
  -r, --raw                   Output raw source messages instead of formatted messages, which can be useful for applying filters and saving results in their original format
      --no-raw                Disable raw source messages output, overrides --raw option
      --raw-fields            Output field values as is, without unescaping or prettifying
  -h, --hide <KEY>            Hide or reveal fields with the specified keys, prefix with ! to reveal, specify '!*' to reveal all
      --flatten <WHEN>        Whether to flatten objects [env: HL_FLATTEN=] [default: always] [possible values: never, always]
  -t, --time-format <FORMAT>  Time format, see https://man7.org/linux/man-pages/man1/date.1.html [env: HL_TIME_FORMAT=] [default: "%b %d %T.%3N"]
  -Z, --time-zone <TZ>        Time zone name, see column "TZ identifier" at https://en.wikipedia.org/wiki/List_of_tz_database_time_zones [env: HL_TIME_ZONE=] [default: UTC]
  -L, --local                 Use local time zone, overrides --time-zone option
      --no-local              Disable local time zone, overrides --local option
  -e, --hide-empty-fields     Hide empty fields, applies for null, string, object and array fields only [env: HL_HIDE_EMPTY_FIELDS=]
  -E, --show-empty-fields     Show empty fields, overrides --hide-empty-fields option [env: HL_SHOW_EMPTY_FIELDS=]
      --input-info <VARIANT>  Show input number and/or input filename before each message [default: auto] [possible values: auto, none, full, compact, minimal]
  -o, --output <FILE>         Output file

Input Options:
      --input-format <FORMAT>       Input format [env: HL_INPUT_FORMAT=] [default: auto] [possible values: auto, json, logfmt]
      --unix-timestamp-unit <UNIT>  Unix timestamp unit [env: HL_UNIX_TIMESTAMP_UNIT=] [default: auto] [possible values: auto, s, ms, us, ns]
      --allow-prefix                Allow non-JSON prefixes before JSON messages [env: HL_ALLOW_PREFIX=]
      --delimiter <DELIMITER>       Log message delimiter, [NUL, CR, LF, CRLF] or any custom string

Advanced Options:
      --interrupt-ignore-count <N>  Number of interrupts to ignore, i.e. Ctrl-C (SIGINT) [env: HL_INTERRUPT_IGNORE_COUNT=] [default: 3]
      --buffer-size <SIZE>          Buffer size [env: HL_BUFFER_SIZE=] [default: "256 KiB"]
      --max-message-size <SIZE>     Maximum message size [env: HL_MAX_MESSAGE_SIZE=] [default: "64 MiB"]
  -C, --concurrency <N>             Number of processing threads [env: HL_CONCURRENCY=]
      --shell-completions <SHELL>   Print shell auto-completion script and exit [possible values: bash, elvish, fish, powershell, zsh]
      --man-page                    Print man page and exit
      --list-themes                 Print available themes and exit
      --dump-index                  Print debug index metadata (in --sort mode) and exit

Performance

performance chart

  • MacBook Pro (16-inch, 2021)
    • CPU: Apple M1 Max CPU

    • OS: macOS Sonoma 14.4.1

    • Data: ~ 2.3 GiB log file, 6 000 000 lines

      • hl v0.28.0 ~ 1.4 seconds

        $ time hl example.log -c -o /dev/null
        hl example.log -c -o /dev/null  11.74s user 0.53s system 885% cpu 1.386 total
      • hlogf v1.4.1 ~ 8.5 seconds

        $ time hlogf example.log --color always >/dev/null
        hlogf example.log --color always > /dev/null  6.93s user 1.79s system 99% cpu 8.757 total
      • humanlog v0.7.6 ~ 77 seconds

        $ time humanlog <example.log --color always >/dev/null
        humanlog> reading stdin...
        humanlog --color always < example.log > /dev/null  80.02s user 4.71s system 109% cpu 1:17.11 total
      • fblog v4.9.0 ~ 36 seconds

        $ time fblog example.log >/dev/null
        fblog example.log > /dev/null  32.48s user 2.03s system 97% cpu 35.526 total
      • fblog with -d flag v4.9.0 ~ 148 seconds

        $ time fblog -d example.log >/dev/null
        fblog -d example.log > /dev/null  132.12s user 14.39s system 99% cpu 2:27.61 total
    • See #132 for how to repeat measurements