{"id":27730691,"url":"https://github.com/dimonomid/nerdlog","last_synced_at":"2026-02-22T11:12:36.052Z","repository":{"id":288967052,"uuid":"969686408","full_name":"dimonomid/nerdlog","owner":"dimonomid","description":"Nerdlog: fast, remote-first, multi-host TUI log viewer with timeline histogram and no central server","archived":false,"fork":false,"pushed_at":"2025-06-22T16:49:12.000Z","size":3097,"stargazers_count":1277,"open_issues_count":15,"forks_count":29,"subscribers_count":5,"default_branch":"master","last_synced_at":"2025-10-20T02:48:07.932Z","etag":null,"topics":["devops-tools","distributed","go","histogram","log-analysis","log-management","log-monitor","log-search","log-viewer","log-visualization","logging","logs","monitoring","remote","self-hosted","syslog","timeline","tui"],"latest_commit_sha":null,"homepage":"https://dmitryfrank.com/projects/nerdlog/article","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-2-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/dimonomid.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2025-04-20T17:57:25.000Z","updated_at":"2025-10-19T23:56:23.000Z","dependencies_parsed_at":"2025-05-05T07:24:00.257Z","dependency_job_id":"87498515-944a-4ad5-a7f1-e7aeac378cd9","html_url":"https://github.com/dimonomid/nerdlog","commit_stats":null,"previous_names":["dimonomid/nerdlog"],"tags_count":18,"template":false,"template_full_name":null,"purl":"pkg:github/dimonomid/nerdlog","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dimonomid%2Fnerdlog","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dimonomid%2Fnerdlog/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dimonomid%2Fnerdlog/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dimonomid%2Fnerdlog/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/dimonomid","download_url":"https://codeload.github.com/dimonomid/nerdlog/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dimonomid%2Fnerdlog/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29710328,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-22T10:34:24.778Z","status":"ssl_error","status_checked_at":"2026-02-22T10:32:23.200Z","response_time":110,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["devops-tools","distributed","go","histogram","log-analysis","log-management","log-monitor","log-search","log-viewer","log-visualization","logging","logs","monitoring","remote","self-hosted","syslog","timeline","tui"],"created_at":"2025-04-28T06:01:53.434Z","updated_at":"2026-02-22T11:12:36.027Z","avatar_url":"https://github.com/dimonomid.png","language":"Go","readme":"# Nerdlog\n\n![Nerdlog](https://dmitryfrank.com/_media/projects/nerdlog/nerdlog_intro_3.png)\n\nNerdlog is a fast, remote-first, multi-host TUI log viewer with timeline\nhistogram and no central server. Loosely inspired by Graylog/Kibana, but\nwithout the bloat. Pretty much no setup needed, either.\n\nIt's laser-focused on being efficient while querying logs from multiple remote\nmachines simultaneously, filtering them by time range and patterns, while also\ndrawing an interactive timeline histogram for quick visual insight.\n\nPrimary use case: reading system logs (from the files `/var/log/messages` or\n`/var/log/syslog`, or straight from `journalctl`) from one or more remote\nhosts. Very efficient even on large log files (like 1GB or more).\n\nIt does support some other log formats and can use any log files, but that was the primary use case which was driving the implementation: we were having our web service backend running as systemd services on a bunch of Linux instances, printing a lot of logs, and wanted to be able to read these logs efficiently and having the timeline histogram, much like tools like Graylog have.\n\n## Design highlights\n\n- No centralized server required; nerdlog establishes an ssh connection to\n  every node that the user wants to collect logs from, and keeps them idle in\n  the background (although a separate server to store the log files might still\n  help in some cases; see the [Limitations](./docs/limitations.md) section in\n  the docs);\n- Logs are not downloaded to the local machine in full: all the log analysis\n  is done on the remote nodes, and on each query, only the following data is\n  downloaded from each node: up to 250 messages from every logstream\n  (configurable), and data for the timeline histogram. It's obviously possible\n  to paginate over the logs, to get the next bunch of messages, etc. Nerdlog\n  then merges the responses from all nodes together, and presents to the user\n  in a unified form;\n- Most of the data is gzipped in transit, thus saving the bandwidth as well.\n- Supports arbitrary command to establish shell session with the remote hosts,\n  so can be used with Teleport or other similar tools.\n\n## Demo\n\nHere’s a quick demo showing how Nerdlog works across four remote nodes:\n\n![Nerdlog](https://dmitryfrank.com/_media/projects/nerdlog/nerdlog_demo.gif?v=2)\n\n## Project history\n\nIt might be useful to know the history to understand the project motivation and\noverall direction, you can read it in the article:\n[Implementing a Radically Simple Alternative to Graylog](https://dmitryfrank.com/projects/nerdlog/article).\n\n## Project state\n\nOriginally hacked together in 2022 to replace painfully slow Splunk setups.\nRevisited in 2025 to clean it up to a certain extent, and open source it.\n\nTested on various Linux distros, FreeBSD, MacOS and Windows (only the client\napp can run on Windows though, we can't get logs from Windows hosts).\n\nThe code still has some traces of the hackathon style here and there, and could be more polished, but overall it matured significantly. There are [decent tests](./docs/tests.md) as well.\n\nI've been using it at work and for my personal projects for a few years now, and I consider it ready to be used in the wild on production systems.\n\n## Installation\n\nThe easiest is to just use a prebuilt binary for your platform, see the\n[releases page](https://github.com/dimonomid/nerdlog/releases).\n\nTo build it from source, you need [Go](https://go.dev/).\n\nAlso on Linux, the X11 dev package is required because of access to clipboard\n([here's the library that nerdlog\nuses](https://github.com/golang-design/clipboard)), e.g. on Ubuntu it can be\ninstalled with this:\n\n```\nsudo apt install libx11-dev\n```\n\nOn MacOS and Windows, no extra dependencies are required.\n\n### Using `go install`\n\nLatest release (might miss newest features which are not yet officially released):\n\n```\ngo install github.com/dimonomid/nerdlog/cmd/nerdlog@latest\n```\n\nLatest master:\n\n```\ngo install github.com/dimonomid/nerdlog/cmd/nerdlog@master\n```\n\nUnless you have custom `GOPATH` or `GOBIN` env vars set, it will install the\n`nerdlog` binary to `$HOME/go/bin`.\n\n### Using `make`\n\nTo install `nerdlog` binary to your `/usr/local/bin`:\n\n```\n$ make \u0026\u0026 sudo make install\n```\n\nOr to build and run without installing:\n\n```\n$ make \u0026\u0026 bin/nerdlog\n```\n\n## Usage\n\nWhen you open the app (`nerdlog` binary), it'll show a query edit form with a\nfew fields:\n\n![Nerdlog](images/nerdlog_query_edit_form.png)\n\nTime range is self-explanatory.\n\nNext one is \"Logstreams\": shortly, as the name suggests, a logstream is a\ncontiguous stream of log messages, on a particular server accessible via ssh\n(or on the local server).\nAs of now, two kinds of logstreams are supported:\n\n- One or more _consecutive_ log files like `/var/log/syslog` and\n  `/var/log/syslog.1` (actually as of now there can be at most 2 files in a\n  logstream, but this limitation will hopefully be removed).\n- Logs returned from `journalctl`\n\nBy default, nerdlog checks available logstreams in the following order:\n\n- If available, use the `/var/log/messages` file (and the older `/var/log/messages.1`)\n- If available, use the `/var/log/syslog` file (and the older `/var/log/syslog.1`)\n- As the last resort, use `journalctl` if available.\n\nWhy preferring the logfiles instead of `journalctl`: shortly, because it's less\nreliable and much slower. See\n[FAQ](./docs/faq.md#why-does-nerdlog-default-to-log-files-instead-of-journalctl)\nfor details. However, `journalctl` is more universally available these days\n(alas), and it often also has longer history, so nerdlog has full support for\nit. So far there's no option to prefer `journalctl` instead of log files by\ndefault, lmk if you need it; shouldn't be hard to implement.\n\nSo if you have a server like `myserver.com` accessible via ssh on port 22, then\nthe logstream to read its `/var/log/messages` file (or `/var/log/syslog` file,\nor from `journalctl` if none of these files are present) could be:\n\n```\nmyuser@myserver.com\n```\n\nIf you need a different port and/or different log files, then a more explicit\nform is:\n\n```\nmyuser@myserver.com:1234:/some/other/logfile\n```\n\nTo select `journalctl` explicitly, specify `journalctl` as the log file:\n\n```\nmyuser@myserver.com:1234:journalctl\n```\n\nMultiple logstreams can be provided separated by commas, like this:\n\n```\nmyuser@myserver.com, myuser@myserver.com:1234:/some/other/logfile\n```\n\nNerdlog also reads ssh config (`~/.ssh/config`) and can take the port, username\nand hostname from there. It supports globs too, so e.g. in your ssh config you\nhave two hosts like `myhost-01` and `myhost-02`, then instead of specifying\nyour logstreams as `myhost-01,myhost-02` it can be simply `myhost-*`.\n\nObviously though, we can't specify the log files in the ssh config, so if you\nneed to configure a logstream for non-default log file on some host(s), you can\nuse the nerdlog's own logstreams config file `~/.config/nerdlog/logstreams.yaml`,\nwhich can look like this:\n\n```\nlog_streams:\n  myhost-01:\n    hostname: actualhost1.com\n    port: 1234\n    user: myuser\n    log_files:\n      - /some/custom/logfile\n  myhost-02:\n    hostname: actualhost2.com\n    port: 7890\n    user: myuser\n    log_files:\n      - /some/custom/logfile\n```\n\nThe last thing on that query form is the \"Select field expression\", it looks\nlike this:\n\n```\ntime STICKY, lstream, message, *\n```\n\nIt only affects the presentation of the logs in the UI. The syntax somewhat\nresembles the SQL `SELECT` syntax, although a lot more limited.\n\nThe `STICKY` here just means that when the table is scrolled to the right,\nthese sticky columns will remain visible at the left side.\n\nAnother supported keyword here is `AS`, so e.g. `message AS msg` is a valid\nsyntax.\n\nFor a more extensive discussion on the logstreams and other core concepts, and advanced options like using `sudo` to read log files, consider\nreading the [Core concepts](./docs/core_concepts.md) section in the docs.\n\n## Requirements\n\n- SSH access to the hosts is required (except for `localhost`). You can read about the related limitations and possible workarounds here: [Consequences of requiring SSH access](./docs/limitations.md#consequences-of-requiring-ssh-access);\n- Gawk (GNU awk) is a requirement on the hosts, since nerlog relies on the `-b`\n  option. So notably, `mawk` will not work. You need `gawk`;\n\nFor more details, see [Requirements](./docs/requirements.md) and\n[Limitations](./docs/limitations.md) in the docs.\n\n## UI\n\nUI consists of a few key elements:\n\n- Awk pattern input: just a filter for logs. Empty filter obviously means no filter, and some examples of valid filters are:\n  - Simple regexp: `/foo bar/`\n  - Regexps with complex conditions: `( /foo bar/ || /other stuff/ ) \u0026\u0026 !/baz/`\n- Edit button: opens a complete query edit form discussed above.\n- Menu button: just opens a menu with a few extra items:\n  - Back: Go to the previous query, just like in the browser\n  - Forward: Go to the next query, just like in the browser\n  - Copy query command: It's the equivalent of copying an URL in the browser, containing the link to the current logs query. See the `:xc[lip]` command below for more details on that.\n\n- Time range histogram: similarly to some web-based log viewers, like Graylog or Kibana, Nerdlog also shows a timeline histogram, so you can quickly glance at the intensiveness of the logs accordingly to the current query. It's also easy to visually select and apply timerange (using arrow / PgUp / PgDown / Home / End / Enter keys or vim-like bindings)\n- Logs table: obviously contains the actual logs. Like in the normal, old-school logs, **the latest message is on the bottom**. I don't know why modern web tools do it the other way around (latest message being on the top), to me it's nonsense. But let me know if you prefer it this modern way; it shouldn't be too hard to make it configurable.\n\n  Every line shows the timestamp and the message, and it can also be scrolled to the right to show the context tags parsed from a log line.\n\n- Status line. On the left side, there are a few computer icons with numbers:\n  - Green: number of lstreams which we're fully connected to and which are idle\n  - Orange: number of lstreams which we're fully connected to and which are executing a query\n  - Red: number of lstreams which we're trying to connect to\n\n  And on the right side, there are 3 numbers like `1201 / 1455 / 2948122`. The rightmost number (2948122) is the total number of log messages that matched the query and the timerange (and included in the timeline histogram above). The next number (1455) is the number of actual log lines currently loaded in the nerdlog app, and the leftmost (1201) is just the cursor within those available logs.\n\n- Command line: Vim-like command line. Hit `:` to enter command mode.\n\n## Navigation\n\nThere are multiple ways to navigate the app, and you can mix them as you wish.\n\nThe most conventional one is to just use Tab and Shift+Tab to switch between widgets (logs table, query input, Edit and Menu buttons, timeline histogram), arrows and keys like Home / End / PgUp / PgDn to move around within a widget, Enter to apply things, Escape to cancel things.\n\nSome browser-like keyboard shortcuts are also supported (after all, one of the\ninspirations for Nerdlog were browser-based tools like Graylog):\n\n- `Alt+Left`: Go back in history\n- `Alt+Right`: Go forward in history\n- `F5` or `Ctrl+R`: Refresh (i.e. rerun the same query again)\n- `Shift+F5` or `Alt+Ctrl+R`: Hard refresh, i.e. also rebuild the index for\n  every logstream (the index is only relevant for plain log files; so for\n  `journalctl`-powered logstreams, it's the same as regular Refresh)\n\nIf you know Vim though, you'll feel right at home in nerdlog too since it supports a bunch of Vim-like keybindings:\n\n- Keys `h`, `j`, `k`, `l`, `g`, `G`, `Ctrl+U`, `Ctrl+D`, etc move cursor whenever you're not in some text-editing field, like query input or others\n- Hitting Escape eventually brings you to the \"Normal mode\", which means that the logs table is focused (and all of those `h`, `j`, `k`, `l`, etc work there)\n- `:` focuses the command line where you can input some commands (see below)\n- `i` or `a` focuses the main query input field\n\nWhen in an input field (command line, query input, etc), you can go through input history using `Up` / `Down` or `Ctrl+P` / `Ctrl+N`.\n\nIn the query edit form (the Edit button on the UI, or the `:e[dit]` command), the `Ctrl+K` / `Ctrl+J` iterates \"full\" query history (affecting not only one field like query, but all of them: time range, logstreams filter, query).\n\n## Commands\n\nIn addition to the UI which is self-discoverable, there is a vim-like command line\nwith a few commands supported.\n\n`:xc[lip]` Copies to clipboard a command string which would open nerdlog with\nthe current logstreams filter, time range and query. This can be done from the Menu too (Menu -\u003e Copy query command)\n\nThis is the equivalent of URL sharing for web-based logging tools: when you'd\nnormally copy the graylog URL and paste it in slack somewhere, with nerdlog you\ncan do the same by sharing this string.\n\nThe string would look like this:\n\n```\nnerdlog --lstreams 'localhost' --time -3h --pattern '/something/'\n```\n\nAnd it can be used in either the shell (which would open a new instance of\nnerdlog), OR it can also be used in a currently running nerdlog instance: just\ntype `:` to go to the command mode, copypaste this command above, and nerdlog\nwill parse it and apply the query.\n\n`:back` or `:prev` Go to the previous query, just like in the browser. This can be done from the Menu too (Menu -\u003e Back), or using a keyboard shortcut `Alt+Left`.\n\n`:fwd` or `:next` Go to the next query, just like in the browser. This can be done from the Menu too (Menu -\u003e Forward), or using a keyboard shortcut `Alt+Right`.\n\n`:e[dit]` Open query edit form; you can do the same if you just use Tab to navigate\nto the Edit button in the UI.\n\n`:w[rite] [filename]` Write all currently loaded log lines to the filename.\nIf filename is omitted, `/tmp/last_nerdlog` is used.\n\n`:refresh` Rerun the same query again. This can be done from the Menu too (Menu -\u003e Refresh), or using a keyboard shortcut `Ctrl+R` or `F5`.\n\n`:refresh!` Hard refresh, i.e. also rebuild the index for every logstream. This\ncan be done from the Menu too, or using a keyboard shortcut `Alt+Ctrl+R` or\n`Shift+F5`.\n\n`:reconnect` Reconnect to all logstreams\n\n`:disconnect` Disconnect from all logstreams\n\n`:conndebug` or `:cdebug` Show debug info for the current logstream connections\n\n`:querydebug` or `:qdebug` or just `:debug` Show debug info for the last query\n\n`:version` or `:about` Show version info\n\n`:set option?` Get current value of an option\n\n`:set option=value` Set option to the new value\n\nSee the [full list of supported options here](./docs/options.md).\n\n---\n\n`:q[uit]` Quit the app.\n\n## Noteworthy dependencies\n\n- [tview](https://github.com/rivo/tview): A terminal UI library with rich, interactive widgets, written in Go\n- [tcell](https://github.com/gdamore/tcell): A Go package that provides a cell based view for text terminals, like XTerm\n- [clipboard](https://github.com/golang-design/clipboard): A cross-platform clipboard package that supports accessing text and images in Go\n- [glob](https://github.com/gobwas/glob): A Go globbing library\n- [ssh_config](https://github.com/kevinburke/ssh_config): A Go parser for `ssh_config` files *(Note: it doesn't support all the features we need and may be replaced; see [issue #12](https://github.com/dimonomid/nerdlog/issues/12) for details)*\n\n**Huge thanks** to the maintainers of these libraries, Nerdlog wouldn't be possible without your work!\n\n## More details\n\n[Check out the documentation](./docs/index.md) for more in-depth look at the core concepts, limitations and workarounds, implementation details, tests, FAQ, troubleshooting, etc.\n","funding_links":[],"categories":["Go","\u003ca name=\"viewers\"\u003e\u003c/a\u003eViewers","Table of Contents"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdimonomid%2Fnerdlog","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdimonomid%2Fnerdlog","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdimonomid%2Fnerdlog/lists"}