pimonitor
provides a high level API to monitor various aspects of your Raspberry pi hardware.
It abstracts calls to the platform, socket and psutil packages and tries to groups them in logical monitor units:
- CPU
- Memory
- Disk
- Process
- Uptime
- Network
All monitors can be run using one unique method Monitor.run()
with Monitor being one of the above (see examples below).
pimonitor
allows to send monitoring data via a variety of mechanisms. At present the following are supported:
- File
- Sqlite DB
- Rabbitmq (using PlainCredentials, i.e. username and password)
pimonitor
also implements Agents to allow you to run one or more monitors at given intervals. Agents also contain the necessary logic to send messages to your desired monitoring channel.
Finally, pimonitor
provides a CLI interface to monitor your Raspberry pi.
Pi monitor is hosted at: https://github.com/R-fred/Pimonitor.
Package is hosted here: https://test.pypi.org/search/?q=pi-monitor.
To install:
pip install -i https://test.pypi.org/simple/ pi-monitor
It comes with several options to send your monitoring data to a central server for storage and processing.
Currently, you can send your monitoring data to a file or an sqlite database (see examples below).
pimonitor
provides a simple command line tool to conviniently monitor your raspberry pi (or other computer).
It can be run like so:
pimonitor run --monitor cpu --monitor memory --send-to file --send-to sqlite
pimonitor
automatically generates a file or sqlite database file in the home directory of the current user.
Invoking the tool with the options above will monitor the cpu and memory and send the results to a file and sqlite database.
Or, if you want to set it up, forget it and continue your work:
pimonitor run --monitor cpu --monitor memory --send-to file --send-to sqlite &
By default, the interval between two data collection is 30 seconds. It can be adjusted like so:
pimonitor run --interval 5 --monitor cpu --monitor memory --send-to file --send-to sqlite &
The context data is by default collected once at the begining and never again. You can modify this behaviour by setting the --refresh-context-every option. The --refresh-context-every takes a float as argument. The unit is seconds. Here is an example setting the refresh rate at 20 minut
pimonitor run --refresh-context-every 1200 --monitor cpu --monitor memory --send-to file --send-to sqlite &
You can also pass arguments to the senders. pimonitor
assumes that the --send-to
and --send-to-options
are in the same order. Options are used as follows:
pimonitor run --monitor cpu --send-to rabbitmq --send-to-options '{"host": "localhost", "port": 5672, "credentials":["test", "test"]}'
pimonitor
can be stopped easily by invoking the stop command. Note that this will kill the process where pimonitor is running.
pimonitor kill
Getting context data about your raspberry pi is useful, especially in industrial scenarii where your devices might be located anywhere around the globe. Getting this data every time you run a monitor is computationally expensive. Pi Monitor defines the ContextData class for that. This way, the never changing data about your Raspberry pi is collected only once at the beginning of the monitoring process.
from pi_monitor.contextdata import ContextData
context = ContextData()
print(context.as_dict())
> {'ip_address': 'the current IP address of your Raspi',
> 'mac_address': 'here your Raspi mac address in hexadecimal format',
> 'localhostname': 'Raspberry1003',
> 'timestamp': 1633550807.495248,
> 'boot_time': 1633178880.0}
from time import sleep
from pi_monitor.monitor.singleMonitors import CPU
cpu_monitor = CPU()
while True:
cpu_monitor.run()
print(cpu_monitor.as_dict())
sleep(1)
from pi_monitor.monitor.singleMonitors import Process
process_monitor = Process()
process_monitor.run()
r_processes = process_monitor.running_processes()
process_monitor.process_info(r_processes[2])
Monitoring agents combine the monitors (e.g. CPU, Memory) and senders. Monitoring agents can contain as many monitors and senders as necessary. For instance, in the example below, we create and run an agent with 3 monitors and 2 senders.
Monitors can be passed as either the class itself - Agent
- or an instance of the class like so: Agent()
.
Agents are subclasses of threading.Thread
and can be stopped gracefully using the Agent.event
attribute or the .stop_agent()
method (see below.).
from time import sleep
from pi_monitor.monitor.agents import AgentBuilder
from pi_monitor.monitor.senders import SenderFactory
from pi_monitor.monitor.singleMonitors import CPU, Uptime, Process, Memory, Disk
ag_builder = AgentBuilder()
sender_builder = SenderFactory()
sqlite_sender = sender_builder.build(sender_type="sqlite", database_name="./db.sqlite")
file_sender = sender_builder.build(sender_type="file", filepath="./file.txt")
ag_builder.add_monitor(CPU)
ag_builder.add_monitor(Uptime)
ag_builder.add_monitor(Memory)
ag_builder.add_sender(sqlite_sender)
ag_builder.add_sender(file_sender)
# Build the agent object
agent = ag_builder.build()
# Start monitoring
agent.start()
sleep(10) # do things
# Stop monitoring
agent.event.set()
# alternatively stop monitoring like this:
agent.stop_agent()
Each agent runs in its own thread. Each monitor can be run as an agent separately as shown below. The same sender can be reused between agents.
from pi_monitor.monitor.agents import AgentBuilder
from pi_monitor.monitor.senders import SenderFactory
from pi_monitor.monitor.singleMonitors import CPU, Uptime, Process, Memory, Disk
# Setup the various objects builders and factories
ag_builder = AgentBuilder()
ag_builder2 = AgentBuilder()
ag_builder3 = AgentBuilder()
sender_builder = SenderFactory()
# Prepare the individual monitors and senders
sqlite_sender = sender_builder.build(sender_type="sqlite", database_name="./db.sqlite")
file_sender = sender_builder.build(sender_type="file", filepath="./file.txt")
ag_builder.add_monitor(CPU())
ag_builder.add_sender(sqlite_sender)
ag_builder2.add_monitor(Uptime())
ag_builder2.add_sender(sqlite_sender)
ag_builder3.add_monitor(Memory())
ag_builder3.add_sender(file_sender)
# Build agent objects
agent1 = ag_builder.build()
agent2 = ag_builder2.build()
agent3 = ag_builder3.build()
# Start monitoring
agent1.start()
agent2.start()
agent3.start()
# Stop monitoring
agent1.stop_agent()
agent2.stop_agent()
agent3.stop_agent()
The data is stored in the database in json format according to the following schema:
CREATE TABLE IF NOT EXISTS My_Raspberry_Pi_hostname (timestamp TEXT, context JSON, monitors TEXT, data json)
TODO: database table could be simplified by keeping data together in one single JSON column in the database (see here)
We can now retrieve the data from the database easily.
import sqlite3
conn = sqlite3.connect('./db.sqlite')
c = conn.cursor("SELECT json_extract(context, '$.ip_address', '$.mac_address') from My_Raspberry_Pi LIMIT 1")
c.fetchall()
c.execute("SELECT json_extract(context, '$.CPU.cpu_percent.cpu_percent') FROM My_Raspberry_Pi WHERE monitors LIKE '%CPU%'")
c.fetchall()
c.execute("SELECT json_extract(data, '$.CPU.timestamp') AS Timestamp, json_extract(data, '$.CPU.cpu_percent.cpu_percent') AS CPU_percent FROM My_Raspberry_Pi WHERE monitors LIKE '%CPU%'")
c.fetchall()
conn.close()
For more details on how to retrieve data from json data structures in sqlite read here
You can create your own custom monitor using compound monitors. These can be run using the same .run()
method as regular monitors.
Deprecation notice: you can achieve the same by building an agent without senders. Doing so comes with the nice benefit that your monitor will run in its own thread (not the case with compound monitors).
from time import sleep
from pi_monitor.compoundMonitors import CoumpoundMonitor as CM
from pi_monitor.monitor.singleMonitors import CPU, Uptime, Process, Memory, Disk
Monitor = CM(name="My_Monitor", monitors=[CPU(), Uptime(), Process(), Memory(), Disk()])
while True:
CM.run()
print(CM.as_dict())
sleep(1)
Alternatively:
from time import sleep
from pi_monitor.compoundMonitors import CoumpoundMonitor as CM
from pi_monitor.monitor.singleMonitors import CPU, Uptime, Process, Memory, Disk
cpu = CPU()
uptime = Uptime()
proc = Process()
memory = Memory()
disk = Disk()
Monitor = CM(name="My_Monitor", monitors=[cpu, uptime, proc, memory, disk])
while True:
CM.run()
print(CM.as_dict())
sleep(1)