---
module: agent.ejournal-db-monitor
title: EJournal DB Monitor — how it's built
tab: Architect
order: 30
audience: dev
---

# EJournal DB Monitor (Architecture)

A single-class agent module, `com.hiveops.ejournal.EJournalDbMonitorModule`,
registered as an SPI service via
`META-INF/services/com.hiveops.core.module.AgentModule`. It plugs into the standard
agent module lifecycle (`initialize` → `isEnabled` → `start` → `stop`) — see
[technical.agent] for the module system as a whole.

## Module shape

- **Artifact:** `hiveops-module-ejournal-db-monitor.jar` (Maven `finalName`), module
  version `1.0.0`, parent `hiveops-agent-parent` `4.4.3`.
- **Dependencies** (all `provided` — supplied by the fat JAR at runtime, not shaded
  in): `hiveops-core` (module API), `hiveops-agent-journal` (`EventType`,
  `IncidentEventClient`, `CreateJournalEventRequest`, `HttpClientHelper`),
  `log4j-api`, `commons-lang3`.
- `getName()` → `ejournal-db-monitor`; `getDependencies()` → empty (no ordering
  constraints); `getLogPackage()` → `com.hiveops.ejournal`.

## Flow: poll → detect → emit

The module is a **poller**, not a log-tail or file-watch. There is no hook into the
ATM app; it just stats a file on a schedule.

1. **`initialize(ModuleContext)`**
   - Reads `ejournal.db.monitor.enabled`; returns early if disabled.
   - Resolves the event endpoint: `agent.endpoint` preferred, else
     `incident.endpoint`. If neither is set, returns early (module stays not-ready →
     no-op). HTTP config loaded via `HttpClientHelper.loadSettingsFromProperties`
     with prefix `agent` or `incident` accordingly.
   - Builds an `IncidentEventClient` from those settings plus
     `context.getAtmName()` and `context.getCountry()`.
   - Parses thresholds/interval/cooldown into bytes/minutes/hours and sets
     `ready = true`.
2. **`isEnabled(ModuleContext)`** → returns the `ready` flag set in `initialize`.
3. **`start()`** — creates a single-thread `ScheduledExecutorService` (daemon thread
   named `ejournal-db-monitor`) and schedules `checkDatabaseSize()` at fixed rate,
   starting immediately (`initialDelay = 0`), every `intervalMinutes`.
4. **`checkDatabaseSize()`** (each tick):
   - `resolveDbFile()` — if `ejournal.db.monitor.filename` set, use it; otherwise
     list the directory filtered to `.mdb/.sdf/.db/.sqlite` and pick the **largest**.
   - `classify(sizeBytes)` → `NONE` / `WARNING` / `CRITICAL` against the byte
     thresholds.
   - State reset: if now `NONE` but previously alerted, log and clear
     `lastAlertLevel`/`lastAlertTime` (DB was cleared).
   - Suppression: send only if the level **escalated** (`ordinal()` increased) OR the
     cooldown (`cooldownHours`) has elapsed since `lastAlertTime`.
   - On send, record `lastAlertLevel` + `lastAlertTime`.
   - The whole body is wrapped in try/catch so a bad tick logs and the schedule
     continues.
5. **`sendAlert(level, file, sizeBytes)`** — maps level to `EventType`
   (`EJOURNAL_DB_CRITICAL` or `EJOURNAL_DB_WARNING`), formats a human `eventDetails`
   string (name, MB, % of 2 GB), and builds a `CreateJournalEventRequest`.
6. **`stop()`** — `scheduler.shutdownNow()`.

Alert state (`lastAlertLevel`, `lastAlertTime`, the `AlertLevel` enum
`NONE/WARNING/CRITICAL`) is **in-memory only** — it resets on agent restart, so the
first post-restart check that is over-threshold will alert again.

## How events reach the platform

The module reuses the shared journal-events pipeline rather than any bespoke
transport:

```
EJournalDbMonitorModule.sendAlert()
  → CreateJournalEventRequest.builder()
        .agentAtmId(atmName)
        .eventType(EJOURNAL_DB_WARNING | EJOURNAL_DB_CRITICAL)
        .eventDetails(...)
        .eventSource("HIVEOPS_AGENT_EJDB")
        .build()
  → IncidentEventClient.sendEvent(event)
        → POST {endpoint}/api/journal-events   (JSON)
```

`endpoint` is `agent.endpoint` (agent-proxy) when configured, else
`incident.endpoint` (direct to hiveops-incident). The ATM is resolved server-side by
`agentAtmId`; the module does not resolve a numeric `atmId` itself. From there the
event follows the normal HiveIQ incident path (auto-register + incident creation).

## Platform abstraction

The module itself uses **no** `PlatformService` — it only does `java.io.File`
size/listing, and the default directory is Windows-shaped (`D:\EJOURNAL`), matching
its Hyosung target. Platform-specific pieces (delivery to `modules/`, agent restart)
live in the `INSTALL_MODULE` handler (`ProcessInstallModule`), which does use
`PlatformService.getSystemCommands()` / `getPathResolver()`. For non-Windows or
non-standard installs, point `ejournal.db.monitor.dir` at the real path.

Cross-reference: [technical.agent] for the ServiceLoader module registry, lifecycle,
and the `IncidentEventClient` / `/api/journal-events` contract shared with the
`journal-events` module.
