Grafito
Welcome to the Grafito source code! I will try to make this code have comments in the literate programming style, so when passing it through a tool such as crycco it will turn into a readable guided tour.
It doesn't hurt that there is not so much code 🤣
Grafito is a simple log viewer. While it tries to have a nice UI, the
idea itself is simple. Your Linux system already provides a nice
log management system in journald
but accessing it via the terminal
using journalctl
is a bit old fashioned and not terribly convenient.
One solution many use is to use some sort of log collection and viewing stack, such as Grafana and others. While those solutions make sense for a complex infrastructure in a company, I don´t think a personal server or homelab has the same requirements.
Therefore, Grafito tries to expose the important bits of journald
in
a comfortable environment. View the logs. Filter them in the most common
ways. Provide, when possible, escape hatches so you can just drop down
into the more powerful terminal.
Don´t try to replace the existing, built-in solution that you already have working, but build upon it.
ALso, choose the tooling so it's easy to install, requires minimal setup and configuration and is performant. Easy, right?
require "./grafito"
require "docopt"
require "kemal-basic-auth"
require "kemal"
This file main.cr is the starting point for grafito. We get the instructions from the user about how to start via the command line, using docopt which lets us just write the help and then everything Just Works.
Since one of the goals is easy setup and minimal config, there are exactly 4 configurable things:
- Address
- Port
- User
- Password
And they are all optinal ;-)
DOC = <<-DOCOPT
Grafito - A simple log viewer.
Usage:
grafito [options]
grafito (-h | --help)
grafito --version
Options:
-p PORT, --port=PORT Port to listen on [default: 3000].
-b ADDRESS, --bind=ADDRESS Address to bind to [default: 127.0.0.1].
-h --help Show this screen.
--version Show version.
DOCOPT
This main()
function is called from the top-level so it's code that
always gets executed.
def main
We parse the command line (ARGV
) using the help we described above.
args = Docopt.docopt(DOC, ARGV, version: Grafito::VERSION)
Port and binding address are important
port = args["--port"].as(String).to_i32
bind_address = args["--bind"].as(String)
Log at debug level. Probably worth making it configurable.
Log.setup(:debug) # Or use Log.setup_from_env for more flexibility
Grafito::Log.info { "Starting Grafito server on #{bind_address}:#{port}" }
Start kemal listening on the right address
Kemal.config.host_binding = bind_address
Read credentials and realm from environment variables
auth_user = ENV["GRAFITO_AUTH_USER"]?
auth_pass = ENV["GRAFITO_AUTH_PASS"]?
Both username and password are set, enable basic authentication
if auth_user && auth_pass
Grafito::Log.info { "Basic Authentication enabled. User: #{auth_user}" }
basic_auth auth_user.as(String), auth_pass.as(String)
elsif auth_user || auth_pass
Only one of the credentials was set - this is a misconfiguration. Exit with an error code to prevent running in an insecure state.
Grafito::Log.fatal { "Basic Authentication misconfigured: Both GRAFITO_AUTH_USER and GRAFITO_AUTH_PASS must be set if authentication is intended." }
exit 1
else
Neither username nor password are set, run without authentication.
Grafito::Log.warn { "Basic Authentication is DISABLED. To enable, set GRAFITO_AUTH_USER and GRAFITO_AUTH_PASS environment variables." }
end
Tell kemal to listen on the right port. That's it. The rest is done in grafito.cr where the kemal endpoints are defined.
Kemal.run(port: port)
end
main()