The pamphlet of the gulik¶
Note
This documentation is still a work in progress and as such subject to sudden changes, cataclysms and various other FNORDs. The software it describes is still in alpha and may cause undue frustration if not taken in moderation.
Quickstart¶
Install!¶
[pkg|apt|dnf|fnord] install gtk3
pip[-3.6] install --user https://rnd.phryk.net/phryk-evil-mad-sciences-llc/gulik
Run!¶
gulik
Done!¶
That’s literally everything needed to get gulik
to run.
If you want to go deeper, you can…
Definitional bullshit¶
gulik
is a highly configurable graphical system monitor.
gulik
is a framework for custom graphical system monitors.
Both of these statements are true in some sense, false in some sense, meaningless in some sense, true and false in some sense, true and meaningless in some sense, false and meaningless in some sense and true and false and meaningless in some sense.
This incarnation of our most holy saint gulik
is software licensed
under the GPLv3 license, see COPYING
for more details.
gulik
can be found at:The PNG gulik
logo is based on a nice public domain trace of the sacred
roach crafted by toa267 who is definitely not a cabbage.
Compatibility and dependencies¶
gulik
should skitter just fine on FreeBSD and the Linuxoids.
It will probably™ crawl into problems on OSX and other BSDs, but
give us a holler
if it doesn’t run on your *nix flavor of choice and you’re willing
to do some testing.
All bets are off on Windows®™ʷᵗᶠ unless someone else is willing to do a PR, which we expect will be a long march through the valley of pain that we do not wish upon anyone.
gulik
runs only on python 3.6 or newer.
All python dependencies are noted in setup.py
, ready for use in pip.
The only other dependency is gtk3. It does run with X11 and should run with wayland, but the latter hasn’t been tested.
To get partial transparency working (on X11) you need to have a compositing
manager like xcompmgr
running.
Installation¶
gulik
and all python dependencies in one go:pip install --user git+https://rnd.phryk.net/phryk-evil-mad-sciences-llc/gulik/
If you insist on manual installation, just clone the repository and copy the
gulik
subdirectory into your site-packages
directory and bin/gulik
to a directory in your $PATH
.
The master branch of the repository always holds the most current release.
Running¶
You run gulik
simply by running the gulik
executable that comes with
the installation.
Configuration and Customization¶
Default behavior¶
By default, gulik
will occupy a 200 pixel wide area over the full height
of the screen, placed at the left border of it and fill it with a few
visualizers to grant you a good overview of what’s happening on
your system.
It will use a black/white/green color scheme with a hue-rotation palette ranging from lime green to magenta-ish pink and try to use the League of Movable Type font Orbitron which we very much recommend you install to get the right dystopian vibe.
Configuration file¶
You can modify the settings for gulik
by creating a configuration file
at ~/.config/gulik/config.py
. This file, as it’s name suggests, is a
python code file. gulik
will import and exec()
it, loading all
UPPERCASE
variables into its configuration.
Lock and reload¶
When gulik
is running, you can send SIGUSR1
to its main process
at any time to tell it to reload the configuration file and apply its
settings. If your configuration file is broken, gulik
will keep
running with the previous settings.
Sending the signal is currently a bit clunky, as you’ll have to do
something like: kill -s SIGUSR1 `pgrep -f 'python3.6.*gulik$'`
since python applications always have the process name of the python
interpreter.
Explaining ALL THE CONFIGURATION OPTIONS (not really, tho)¶
FPS
(int
orfloat
): Frames Per Second; How often to redraw the window (and update system data). Default value:1
WIDTH
(int
): The width of the window in pixels. Default value:200
HEIGHT
(int
): The height of the window in pixels. Default value:Gdk.Screen().get_default().get_height()
X
(int
): X coordinate of the windows top left corner. Default value:0
Y
(int
): Y coordinate of the windows top left corner. Default value:0
NETDATA_HOSTS
(list
): netdata hosts to connect to. Hosts as hostnames or(host, port)
tuples. Default value:[]
NETDATA_RETRY
(int
orfloat
): How long a defectiveNetdataMonitor
will wait before retrying to contact thenetdata
server, in seconds. Default value:5
BSD_ACCURATE_MEMORY
(bool
): Use accurate but expensive memory data collection on BSD. Default value:False
MARGIN
(int
orfloat
): Margin around allVisualizer
s. Default value:5
PADDING
(int
orfloat
): Padding around allVisualizer
s. Default value:5
FONT
(str
): Font family to use in captions, legends and the like. Default value:"Orbitron"
FONT_WEIGHT
(str
): Font weight. Default value:"Light"
FONT_SIZE
(int
orfloat
): Font size in pixels. Default value:10
COLOR_WINDOW_BACKGROUND
(Color
): Background color of the window. Default value:Color(0.05, 0.05, 0.05, 0.8)
COLOR_BACKGROUND
(Color
): Background color forVisualizer
s. Default value:Color(1,1,1, 0.1)
COLOR_FOREGROUND
(Color
): Foreground color. This is used as base color for most palettes. Default value:Color(0.5, 1, 0, 0.6)
COLOR_CAPTION
(Color
): Text color for captions. Default value:Color(1,1,1, 0.6)
PALETTE
(function
): The default palette generator. Default value:functools.partial(
palette_hue()
, distance=-120)
PATTERN
(function
): The default pattern generator. Default value:stripe45
CAPTION_PLACEMENT
(str
):"padding"
to have captions placed in the paddings ofVisualizer
s,"inner"
to place them within the drawing region of theVisualizer
. Default value:"inner"
LEGEND
(bool
): WhetherVisualizer
s should attempt automatically creating a legend for themselves in their bottom padding. Default value:True
LEGEND_ORDER
(str
): Whether to reverse the legend order. Can be"normal"
or"reverse"
. Default value:"normal"
LEGEND_SIZE
(int
orfloat
): Pixel height of one legend cell, including its own margin and padding. Legend font size is inferred from this. Default value:20
LEGEND_PLACEMENT
(str
): Where to place legends within theVisualizer
s drawing region. Can be"inner"
or"padding"
. Default value:"padding"
LEGEND_MARGIN
(int
orfloat
): Margin around legends. Default value:2.5
LEGEND_PADDING
: Padding around legends. Default value:0
OPERATOR
: The blending operator used byVisualizer
s. Default value:Operator.OVER
Explaining ALL THE ᴀʟʟ ᴛʜᴇ ᴄᴏɴꜰɪɢᴜʀᴀᴛɪᴏɴ ᴏᴘᴛɪᴏɴꜱ (ya rly)¶
The above list doesn’t really cover all the possible configuration variables
you can set, as gulik
features a perversion of cascading styles.
Every single option except for FPS
, X
, Y
, NETDATA_HOSTS
,
NETDATA_RETRY
and BSD_ACCURATE_MEMORY
can be overriden on a per-class
basis by appending an underscore and the class name in uppercase to the
variable name. To disable legends on all Plot
s for example, you
would use LEGEND_PLOT = False
.
Additionally, MARGIN
and PADDING
can be set for each side by
appending an underscore and one of LEFT
, RIGHT
, TOP
or BOTTOM
.
If you want to mix both of these things, the resulting string follows the
pattern <name>_<class>_<subname>, for example PADDING_PLOT_RIGHT
or
LEGEND_MARGIN_ARC_BOTTOM
.
Custom setups¶
By default, gulik will run Gulik.autosetup()
to set up a reasonable
collection of Visualizer
s that gives you a good overview of your
system – but you can add your own setup
function to the configuration
file that will be used in stead of the autosetup.
import gulik
def setup(app):
box = app.box()
box.place(
'cpu',
gulik.Plot,
elements=['core_0', 'core_1'],
width=box.width,
height=box.width + 40
)
Let’s look at the code, line by line:
def setup(app)
- The name of the setup function must be
setup
, otherwisegulik
won’t recognize it. The passedapp
parameter is aGulik
object. box = app.box()
Gulik.box()
creates aBox
, a little helper to make layouting easier. You can limit its size viawidth
andheight
keyword parameters. If these aren’t supplied, the box will fill the whole window.box.place(
Box.place()
places a new visualizer.Box
orders visualizers from left to right and top to bottom.'cpu'
- This is the monitored component we want to visualize elements
of. It is needed to look up the right
Monitor
object forVisualizer
instantiation. gulik.Plot
- The visualizer class to be instantiated.
A subclass of
Visualizer
.
All keyword arguments below this are just passed on to the visualizer class,
so in this case, Plot
. Here, these are:
elements=['core_0', 'core_1'],
- Defines the elements to be visualized. These two values refer to the first and second CPU cores.
width=box.width,
- Defines the width of the visualizer.
height=box.width + 40
- Defines the height of the visualizer. Has 40 pixel added to it,
because by default,
gulik
adds 40 pixel bottom padding to every visualizer to allow 2 lines of legend.
Box.place()
uses the width
and height
keyword parameters
(if passed) to make its layouting decisions. If they aren’t passed, all
remaining space is used.
And with that, you hopefully know enough to get started with your custom
setup. You can consult Monitor
and its subclasses to find out
what you can visualize and Visualizer
and its subclasses to
find out how you can visualize that data.
If you want to extend the original setup instead of doing a completely
custom one, you can call Gulik.autosetup()
from your custom setup
function and limit its area by using width
and height
as well as
x
and y
keyword parameters.
Module reference¶
Architecture¶
+-----------------------------------------+
| |
| gulik process | collector processes
| |
| (main thread) (monitor threads) |
| Gulik----------- CPUMonitor <-----ø-----> CPUCollector
| | | `- MemoryMonitor <-----ø-----> MemoryCollector
| | |`-- NetworkMonitor <-----ø-----> NetworkCollector
| | `--- … <-----ø-----> …
| | | |
| | | |
| `-- visualizers | (visualizers |
| | `- Arc | access |
| |`-- Plot <´ monitors) |
| `--- … |
| |
+-----------------------------------------+
In gulik
, there is one central Gulik
object.
It manages Monitor
s and Visualizer
s.
Visualizers use the Monitor.normalize()
and Monitor.caption()
functions to utilize the collected data.
Communication between the Monitor
s within the gulik process and
Collector
processes is done via queues. Every monitor/collector
pair shares two queues. One “update queue” that monitors use to send update
requests to collectors and one “data queue” that collectors use to send the
next datapoint to their respective monitor.
Box model¶
+-----------------------------------------------------------+ ↑
| | |
| margin | |
| | |
| +---------------------------------------------------+ | |
| | | |
| | padding | | h
| | | | e
| | +-----------------------------------+ ↑ | | i
| | | | | inner | | g
| | | inner drawing region | | height | | h
| | | | | | | t
| | +-----------------------------------+ ↓ | |
| | | | |
| | ←------------ inner width ----------→ | | |
| | | | |
| +---------------------------------------------------+ | |
| | |
+-----------------------------------------------------------+ ↓
←--------------------------- width -------------------------→
gulik
s box model is pretty similar to the one CSS has, with
one important distinction: margins and paddings are included in
width
and height
in order to make layouting easier.
Concepts¶
visualizer¶
Visualizers are instances of any subclass of Visualizer
.
A visualizer is assigned a Monitor
and a list of elements.
Gulik
will periodically call all visualizers update
methods
(see source of Visualizer.update()
).
What exactly happens in the update
function of a visualizer differs
between the different classes, but usually it queries the instance-assigned
monitor for the most recent data about its elements by calling
Monitor.normalize()
and then does some drawing on the passed
cairo.Context
to visualize the data in some manner.
- Currently, there are 7 built-in visualizers:
You are, however, welcome to implement your own visualizers by subclassing
Visualizer
and overriding Visualizer.draw()
.
monitor¶
Monitors are instances of any subclass of Monitor
,
which itself is a subclass of multithreading.Thread
.
Every monitor acts as one half of a monitor-collector pair, each of which collects and transforms data on a specific component.
The monitors responsibility in this pair is to take data from the collector
and offer it in a form that is usable by visualizers. This is mainly
done through the functions Monitor.normalize()
and Monitor.caption()
.
- Currently, there are 6 built-in monitors:
collector¶
Collectors are instances of any subclass of Collector
,
which itself is a subclass of multiprocessing.Process
.
Every collector acts as one half of a monitor-collector pair.
The collectors responsibility in this pair is to collect system usage data and send them to its associated monitor.
component¶
A string identifying a data source.
- Valid values are:
'cpu'
'memory'
'network'
'battery'
'disk'
Besides that, 'netdata-<hostname>'
is valid, but only if <hostname>
exists in the NETDATA_HOSTS
configuration option.
element¶
A string identifying a (sub) element of a data source.
Valid values are defined within the respective Monitor
s.
alignment¶
- A string in the shape of
<x-align>_<y-align>
, where: <x-align>
can be any ofleft
,center
,right
<y-align>
can be any oftop
,center
,bottom
alignments are used both for text positioning relative to its respective
allowed borders as well as positioning captions within Visualizer
s.
pattern¶
A function taking one Color
as parameter returning a cairo surface for
use as fill. See stripe45()
for an example.
palette¶
A function taking one Color
and one int parameter returning a
list
of Color
objects with its length being equal to the passed
int parameter.
Note
palette_hue()
and palette_value()
have extra parameters
you won’t be able to use without wrapping them in functools.partial()
first!
combination¶
A string denoting how multiple elements are displayed within a Visualizer
.
- Valid values are:
separate
: separate elements visuallycumulative
: show values cumulatively, assume data is normalized for that (i.e. all values added max out at 1.0)cumulative_force
: likecumulative
, but assumes every single value can go up to 1.0
-
class
gulik.
Color
(red=None, green=None, blue=None, alpha=None, hue=None, saturation=None, value=None)[source]¶ Magic color class implementing and supplying on-the-fly manipulation of RGB and HSV (and alpha) attributes.
-
class
gulik.
DotDict
[source]¶ A dictionary with its data being readable through faked attributes. Used to avoid [[[][][][][]] in caption formatting.
-
gulik.
palette_hue
(base, count, distance=180)[source]¶ Creates a hue-rotation palette.
Parameters: - base (
Color
) – Color on which the palette will be based (i.e. the starting point of the hue-rotation). - count (int) – number of colors the palette should hold.
- distance (int or float) – angular distance on a 360° hue circle thingamabob.
Returns: A list of length count of
Color
objects.Return type: list
- base (
-
gulik.
palette_value
(base, count, min=None, max=None)[source]¶ Creates a value-stepped palette
Parameters: - base (
Color
) – Color on which the palette will be based (i.e. source of hue and saturation) - count (int) – number of colors the palette should hold
- min (float >= 0 and <= 1) – minimum value (the v in hsv)
- max (float >= 0 and <= 1) – maximum value
Returns: A list of length count of
Color
objects.Return type: list
- base (
-
gulik.
pretty_si
(number)[source]¶ Return a SI-postfixed string representation of a number (int or float).
-
gulik.
pretty_bytes
(bytecount)[source]¶ Return a human-readable representation given a size in bytes.
-
gulik.
pretty_bits
(bytecount)[source]¶ Return a human-readable representation in bits given a size in bytes.
-
class
gulik.
CPUMonitor
(app, component)[source]¶ Memory for CPU usage.
-
normalize
(element)[source]¶ - Elements exposed:
aggregate
: average cpu use, sum of all core loads divided by number of corescore_<n>
: load of core<n>
, with possible values of<n>
being 0 to number of cores - 1
- Exposed keys:
aggregate
: average cpu use, sum of all core loads divided by number of corescore_<n>
: load of core<n>
, with possible values of<n>
being 0 to number of cores - 1count
: number of cores
-
-
class
gulik.
MemoryMonitor
(app, component)[source]¶ Monitor for memory usage
-
normalize
(element)[source]¶ - Elements exposed:
percent
: memory use of all processes.top_<n>
: memory use of the<n>
h-biggest process. Valid values of<n>
are 1-3.other
: memory use of all processes except the top 3
- Exposed keys:
total
: how much memory this machine has in total,percent
: total memory usage in percent.available
: how much memory can be malloc’d without going into swap (roughly).top_<n>
: access information about the 3 “biggest” processes. possible subkeys arename
,size
andpercent
.other
: aggregate information for all processes except the top 3. Same subkeys as those, plus'count
.
-
-
class
gulik.
NetworkMonitor
(app, component)[source]¶ Monitor for network interfaces.
-
count_sec
(interface, key)[source]¶ get a specified count for a given interface as calculated for the last second.
EXAMPLE:
self.count_sec('eth0', 'bytes_sent')
(will return count of bytes sent in the last second)
-
normalize
(element)[source]¶ - Exposed elements:
<if>.bytes_sent
: upload of network interface<if>
.<if>.bytes_recv
: download of network interface<if>
.
<if> can be any local network interface as well as ‘aggregate’.
- Exposed keys:
<if>.bytes_sent
: upload of network interface<if>
.<if>.bytes_recv
: download of network interface<if>
.<if>.if_up
: Boolean, whether the interface is up.<if>.speed
: interface speed in Mbit/s<if>.counters
: supplies access to interface counters. Possible sub-elements are:bytes_sent
bytes_recv
packets_sent
packets_recv
errin
errout
dropin
dropout
<if> can be any local network interface as well as
'aggregate'
.Additionally, the
'aggregate'
interface exposes the total count of network interfaces asif_count
.
-
-
class
gulik.
BatteryMonitor
(app, component)[source]¶ Monitor laptop batteries.
-
normalize
(element)[source]¶ This function exposes no explicit elements, but always just returns the current fill of the battery.
- Exposed keys:
power_plugged
: Boolean, whether the AC cable is connected.percent
: current fill of the battery in percent.secsleft
: seconds left till battery is completely drained.state
: Current state of the battery, one of'full'
,'charging'
or'draining'
.
-
-
class
gulik.
DiskMonitor
(*args, **kwargs)[source]¶ Monitors disk I/O and partitions.
-
normalize
(element)[source]¶ - Elements exposed:
io
Valid subelements are disk device file names as found in
/dev
. Examples:ada0
,sda
.- Valid subsubelements are as follows:
read_bytes
write_bytes
read_time
write_time
busy_time
partitions
- Valid subelements are partition device file names as
found in
/dev
, with dots (.
) being replaced with dashes (-
). Examples:root-eli
,sda1
.
- Valid subelements are partition device file names as
found in
Exposed keys are the same as for
DiskMonitor.normalize()
.
-
-
class
gulik.
NetdataMonitor
(app, component, host, port)[source]¶ Monitor that interfaces with (remote) netdata instances.
-
normalize
(element)[source]¶ Exposed elements correspond to chart names and their datapoint *dimension*s. For a list of valid chart and dimensions names, consult
/api/v1/charts
of the netdata instance in question.Examples
system.cpu.nice
disk.ada0.writes
Exposed keys are the same as for
NetdataMonitor.normalize()
.
-
-
class
gulik.
Visualizer
(app, monitor, x=0, y=0, width=None, height=None, margin=None, margin_left=None, margin_right=None, margin_top=None, margin_bottom=None, padding=None, padding_left=None, padding_right=None, padding_top=None, padding_bottom=None, elements=None, captions=None, caption_placement=None, legend=None, legend_order=None, legend_format=None, legend_size=None, legend_placement=None, legend_margin=None, legend_margin_left=None, legend_margin_right=None, legend_margin_top=None, legend_margin_bottom=None, legend_padding=None, legend_padding_left=None, legend_padding_right=None, legend_padding_top=None, legend_padding_bottom=None, foreground=None, background=None, pattern=None, palette=None, combination=None, operator=None)[source]¶ The base class for all visualizers. Not called widget to avoid naming confusion with gtk widgets (which aren’t even used in gulik).
Usually you won’t instantiate this by yourself but use
Box.place()
.Parameters: - app (
Gulik
) – The app managing visualizers and monitors - monitor (
Monitor
) – The monitor managing data collection for this visualizer - x (int) – leftmost coordinate on the x-axis
- y (int) – topmost coordinate on the y-axis
- width (int) – overall width (including margin and padding)
- height (int) – overall height (including margin and padding)
- margin (int, optional) – margin applied around all sides
- margin_left (int, optional) – margin applied to the left side
- margin_right (int, optional) – margin applied to the right side
- margin_top (int, optional) – margin applied to the top side
- margin_bottom (int, optional) – margin applied to the bottom side
- padding (int, optional) – padding applied around all sides
- padding_left (int, optional) – padding applied to the left side
- padding_right (int, optional) – padding applied to the right side
- padding_top (int, optional) – padding applied to the top side
- padding_bottom (int, optional) – padding applied to the bottom side
- elements (list of str) – A list of elements to visualize
- captions (list of dict, optional) – A list of caption descriptions
- legend (bool) – Whether to try to automatically create a legend of elements
- legend_order ('normal' or 'reverse', optional) – Whether to reverse the legend order
- legend_format (str) – A format string, can contain
{element}
to refer to the element legend items refer to. - legend_size (int, optional) – height of a single legend item
- legend_placement ('padding' or 'inner') – Where to place the legend
- legend_margin (int, optional) – margin applied around all sides of the legend
- legend_margin_left (int, optional) – margin applied to the left side of the legend
- legend_margin_right (int, optional) – margin applied to the right side of the legend
- legend_margin_top (int, optional) – margin applied to the top side of the legend
- legend_margin_bottom (int, optional) – margin applied to the bottom side of the legend
- legend_padding (int, optional) – padding applied around all sides of the legend
- legend_padding_left (int, optional) – padding applied to the left side of the legend
- legend_padding_right (int, optional) – padding applied to the right side of the legend
- legend_padding_top (int, optional) – padding applied to the top side of the legend
- legend_padding_bottom (int, optional) – padding applied to the bottom side of the legend
- foreground (
Color
, optional) – The foreground color, base-color for generated palettes - background (
Color
, optional) – The background color - pattern (function, optional) – An executable pattern
- palette (function, optional) – An executable palette
- combination (str, optional) – The combination mode used when displaying multiple elements
- app (
-
class
gulik.
Text
(app, monitor, text, speed=25, align=None, **kwargs)[source]¶ Scrollable text using monitors’
caption
function to give textual representations of values, prettified where necessary.
-
class
gulik.
Rect
(app, monitor, x=0, y=0, width=None, height=None, margin=None, margin_left=None, margin_right=None, margin_top=None, margin_bottom=None, padding=None, padding_left=None, padding_right=None, padding_top=None, padding_bottom=None, elements=None, captions=None, caption_placement=None, legend=None, legend_order=None, legend_format=None, legend_size=None, legend_placement=None, legend_margin=None, legend_margin_left=None, legend_margin_right=None, legend_margin_top=None, legend_margin_bottom=None, legend_padding=None, legend_padding_left=None, legend_padding_right=None, legend_padding_top=None, legend_padding_bottom=None, foreground=None, background=None, pattern=None, palette=None, combination=None, operator=None)[source]¶
-
class
gulik.
Arc
(app, monitor, stroke_width=5, **kwargs)[source]¶ -
Parameters: stroke_width (int) – width of the arc in pixels.
-
class
gulik.
Plot
(app, monitor, num_points=None, autoscale=None, markers=None, line=None, grid=None, **kwargs)[source]¶ -
Parameters: - num_points (int, optional) – The number of datapoints to show.
- autoscale (bool, optional) – Whether to automatically “zoom” into the data.
- markers (bool, optional) – Whether tho render markers at data point coordinates.
- line (bool, optional) – Whether to draw a line.
- grid (bool, optional) – Whether to draw a grid. The grid automatically adapts if
autoscale
isTrue
. - **kwargs – passed on to
Visualizer
.
-
class
gulik.
Box
(app, x, y, width, height)[source]¶ Can wrap multiple
Visualizer
s, used for layouting. Orders added visualizers from left to right and top to bottom.This is basically a smart helper for
Gulik.add_visualizer()
.-
place
(component, cls, **kwargs)[source]¶ place a new
Visualizer
.Parameters: - component (str) – The component string identifying the data source.
- cls (
type
, child ofVisualizer
) – The visualizer class to instantiate. - **kwargs – passed on to
cls
’ constructor. width and height defined in here are honored.
-