joularcore/lib.rs
1/*
2 * Copyright (c) 2025-2026, Adel Noureddine.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the
5 * GNU Lesser General Public License v3.0 only (LGPL-3.0-only)
6 * which accompanies this distribution, and is available at
7 * https://www.gnu.org/licenses/lgpl-3.0.en.html
8 *
9 * Author : Adel Noureddine
10 */
11
12//! Joular Core is a Rust library for measuring power and energy across systems and devices.
13//!
14//! It measures CPU and GPU power consumption in real time, and can break that down to individual processes or applications. Joular Core runs on Linux, Windows, macOS, Raspberry Pi, and inside virtual machines.
15//!
16//! It allows applications, telemetry services, benchmarks, and custom developer tools to monitor CPU, GPU, and total system power, as well as attribute energy usage to specific process IDs (PIDs) or multi-process applications. It can export data to CSV files and to a shared-memory ring buffer.
17//!
18//! # Getting started example
19//!
20//! ```no_run
21//! use joularcore::JoularCoreMonitor;
22//!
23//! let mut monitor = JoularCoreMonitor::for_app("firefox");
24//!
25//! std::thread::sleep(std::time::Duration::from_secs(1));
26//!
27//! let sample = monitor.poll();
28//! println!("total {:.2} W, firefox {:.2} W", sample.total_power(), sample.target_power_or_zero());
29//! ```
30//!
31//! ## Putting a session together
32//!
33//! There are three independent choices, and nothing hides them from you:
34//!
35//! * **What to measure** — [`MonitorConfig`], passed to
36//! [`JoularCoreMonitor::from_config`].
37//! * **Where to read it from** — the platform's own sensors by default. Replace
38//! any of them through [`JoularCoreMonitor::builder`], which is how
39//! `vm::VmSensor` (the `vm` feature) is used inside a virtual machine.
40//! * **Where samples go** — push destinations onto an [`OutputBundle`].
41//!
42//! ## Unavailable sensors
43//!
44//! Power interfaces are privileged on most systems. When one cannot be read,
45//! [`MonitorSample::cpu_power`] and [`MonitorSample::gpu_power`] are `None`
46//! rather than `0.0`, so an unreadable sensor is never mistaken for an idle
47//! machine. See the README for what each platform requires.
48//!
49//! ## Output
50//!
51//! Every destination is an [`output::OutputSink`], and [`OutputBundle`] fans
52//! one sample out to all of them: CSV or bare-wattage files, and a
53//! shared-memory [`ringbuffer`] for other processes to read. To send samples
54//! anywhere else — over HTTP, into a database — implement `OutputSink`.
55//!
56//! ## Adding a sensor of your own
57//!
58//! The [`sensor`] module holds the traits every backend implements. Implement
59//! [`sensor::PowerSensor`] for a single source of power — that is all
60//! `vm::VmSensor` is — or [`sensor::Platform`] for a whole machine, and hand it
61//! to [`monitor::MonitorBuilder`].
62//!
63//! ## Logging
64//!
65//! The library never writes to stdout or stderr on its own. It emits [`log`]
66//! records — warnings such as "RAPL is not readable" arrive there. Install
67//! whichever logger your program already uses (`env_logger`, `simplelog`, …);
68//! with no logger installed the records are discarded and nothing is printed.
69//!
70//! # Feature flags
71//!
72//! * `vm` *(default)*: allow reading power from files written by a hypervisor or an
73//! external meter, for use inside virtual machines.
74//! * `sbc`: allow monitoring single-board computers using regression models, replacing the RAPL-based Linux one.
75
76// Items behind a feature gate (`vm`) are named in prose
77// as code spans, not intra-doc links: a link from ungated documentation to an
78// item that a reduced-feature build does not compile is a rustdoc warning, and
79// `cargo doc` is expected to be clean under every feature combination.
80#![cfg_attr(docsrs, feature(doc_cfg))]
81#![warn(missing_docs)]
82#![warn(unsafe_op_in_unsafe_fn)]
83#![warn(clippy::doc_markdown)]
84#![warn(clippy::must_use_candidate)]
85#![warn(clippy::missing_errors_doc)]
86#![warn(clippy::semicolon_if_nothing_returned)]
87#![warn(clippy::explicit_iter_loop)]
88
89pub mod config;
90pub mod error;
91pub mod monitor;
92pub mod output;
93pub mod platform;
94pub mod ringbuffer;
95pub mod sensor;
96
97#[cfg(feature = "vm")]
98#[cfg_attr(docsrs, doc(cfg(feature = "vm")))]
99pub mod vm;
100
101// The everyday surface, re-exported so ordinary use needs one import rather than
102// one per module. Anything not here is still reachable through its module.
103pub use config::{AppMatch, Component, ElevationPolicy, MonitorConfig, Target};
104pub use error::{Error, Result};
105pub use monitor::{JoularCoreMonitor, MonitorSample, PowerRecord};
106pub use output::{FileWriter, OutputBundle, OutputSink, Schema};
107pub use sensor::PowerSensor;
108
109// The README's Rust examples are the first thing a new user runs, so they are
110// compiled as doctests rather than left to drift away from the API. They are
111// written against the default feature set, which is what a reader following the
112// README will have, so they are only compiled when those features are present.
113#[cfg(all(doctest, feature = "vm"))]
114#[doc = include_str!("../README.md")]
115struct ReadmeExamples;