Skip to content

Heartbeat API

This is the API reference for the heartbeat subsystem: the heartbeat type and its lifecycle, the HeartbeatMonitor sampler, how the runtime constructs and wires it, and the config model that drives it. For the conceptual overview, the measures table, and cross-language behavior, see the Heartbeat guide.

Each SDK has a heartbeat type that owns a background task. The lifecycle method names diverge by language (see the summary table).

com.mbreissi.ggcommons.heartbeat.Heartbeat implements ConfigurationChangeListener. Its constructor is package-private; construct it with HeartbeatBuilder.

public class Heartbeat implements ConfigurationChangeListener {
// Stop: cancel the periodic task and shut down the scheduler.
public void close();
// Hot-reload hook: re-initializes the schedule on a config change.
public boolean onConfigurationChanged();
}
// private constants: MESSAGE_NAME = "heartbeat", MESSAGE_VERSION = "1.0.0"
Language Type Construct Stop Extra lifecycle
Java Heartbeat HeartbeatBuilder.build() close() onConfigurationChanged()
Python EnhancedHeartbeat EnhancedHeartbeat(config_service) stop() start(), is_running(), on_configuration_change()
Rust Heartbeat Heartbeat::start(...) drop (RAII abort()) re-reads live config each tick
TypeScript Heartbeat Heartbeat.start(...) stop() re-reads live config each tick

Hot reload also diverges: Java and Python register as a configuration-change listener and re-init the schedule on a change event; Rust and TypeScript hold a live config handle and re-read it on every tick (rebuilding the ticker when intervalSecs changes).

In production you never call these — the runtime wires the heartbeat for you. The snippets below show exactly what the runtime does, which is also the path the tests exercise.

HeartbeatBuilder requires both a messaging client and a metric emitter; build() throws IllegalStateException if either is null. create(null) throws IllegalArgumentException.

import com.mbreissi.ggcommons.heartbeat.Heartbeat;
import com.mbreissi.ggcommons.heartbeat.HeartbeatBuilder;
// Both collaborators are MANDATORY in Java.
Heartbeat heartbeat = HeartbeatBuilder.create(configManager)
.withMessagingService(messagingClient)
.withMetricService(metricEmitter)
.build(); // first tick fires at delay 0
// ... runs autonomously on the configured interval ...
heartbeat.close(); // cancel the task + shut down the scheduler

HeartbeatMonitor is the sampler the heartbeat task uses each tick. It collects only the enabled measures into a nested stats object. You can also use it directly to read process/system health. Note that the constructor input differs by language: Java takes the HeartbeatConfiguration, Python takes the ConfigManager, and Rust/TypeScript take a Measures struct of toggles.

import com.mbreissi.ggcommons.heartbeat.HeartbeatMonitor;
import com.google.gson.JsonObject;
HeartbeatMonitor monitor = new HeartbeatMonitor(heartbeatConfiguration);
JsonObject stats = monitor.getStats(); // nested per-measure object; disabled measures omitted
monitor.updateMetrics(); // refresh the OSHI process snapshot (advance previous->current) for the next CPU-between-ticks delta; getStats() already calls this internally

When a measure is enabled, getStats includes its nested object; disabled measures are omitted. The shape is identical across all four languages:

{
"cpu": { "cpu_usage": 12.5 },
"memory": { "memory_usage": 84 },
"disk": { "disk_total": 512.0, "disk_used": 210.4, "disk_free": 301.6 },
"threads": { "threads": 14 },
"files": { "files": 23 },
"fds": { "fds": 31 }
}

The metric target flattens this to one metric value per inner key (cpu_usage, memory_usage, disk_total, …); the messaging target sends the nested object as the message payload. Units and per-platform availability (for example Java fds is -1 on Windows, and TypeScript threads / files / fds are Linux-only) are documented in the guide’s measures table.

Each SDK has a typed model for the heartbeat config section. The accessors below are read by the runtime; you rarely touch them directly.

com.mbreissi.ggcommons.config.HeartbeatConfiguration:

public class HeartbeatConfiguration {
public static final String DEFAULT_TOPIC =
"ggcommons/{ThingName}/{ComponentName}/heartbeat";
public static final String DEFAULT_MESSAGING_DESTINATION = "ipc";
public int getIntervalSecs();
public boolean includeCpu();
public boolean includeMemory();
public boolean includeDisk();
public boolean includeThreads();
public boolean includeFiles();
public boolean includeFds();
public List<HeartbeatTarget> getTargets();
public static class HeartbeatTarget {
public String getType();
public JsonObject getConfig();
}
}

Defaults when keys are absent: measures defaults to cpu + memory enabled; targets defaults to a single { "type": "metric" }.

The config section is the single source of truth across all four SDKs. It is validated against the heartbeat section of schema/ggcommons-config-schema.json. Because the cross-language defaults diverge (see the model tabs above and the guide’s differences section), the most portable approach is to set intervalSecs, measures, and targets explicitly.

{
"heartbeat": {
"intervalSecs": 5,
"measures": {
"cpu": true,
"memory": true,
"disk": false,
"threads": false,
"files": false,
"fds": false
},
"targets": [
{ "type": "metric" },
{
"type": "messaging",
"config": {
"destination": "ipc",
"topic": "ggcommons/{ThingName}/{ComponentName}/heartbeat"
}
}
]
}
}
Field Type Notes
intervalSecs integer Sample/publish period. Default 5. Java/Rust/TypeScript enforce a floor; Python does not.
measures.cpumeasures.fds boolean Per-measure toggles: cpu, memory, disk, threads, files, fds.
targets[].type string metric or messaging.
targets[].config.destination string messaging only. ipc / local or iot_core / iotcore. Default ipc.
targets[].config.topic string messaging only. Default ggcommons/{ThingName}/{ComponentName}/heartbeat; {ThingName} and {ComponentName} are resolved at publish time.

The metric target’s storageResolution is 1 when intervalSecs is under 60, otherwise 60 (all four languages). See the Configuration guide for how the schema is synced and validated.