graftorio3

by LeRone

See what your factory is actually doing, in Grafana. Production, power reserves, machine status, logistics supply and demand, trains, research and space platforms as Prometheus metrics - straight into your existing node_exporter, no sidecar or extra port needed.

Utilities
2 hours ago
2.0 - 2.1
4

Changelog

Version: 2.0.0
Date: 2026-08-05
  Minor Features:
    - Item production and consumption can now be split by quality tier
      (graftorio3-production-quality-labels, off by default), using the
      input_quality_counts / output_quality_counts statistics.
    - New Grafana dashboard "0.0 - graftorio3 Overview" covering exporter
      health, machine status, electric network reserves, production rates,
      logistic stock and rocket silos on one page, with a surface selector.
      The inherited dashboards still describe the graftorio2 metric set; this
      one is the entry point for everything added since.
    - New depth metrics (graftorio3-depth-metrics, on by default):
        * factorio_accumulator_charge_joules and
          factorio_accumulator_capacity_joules per electric network: current
          flow says what is happening now, buffer level says whether the
          network is about to run out.
        * factorio_generation_capacity_watts: installed maximum generation per
          network, for headroom alerts.
        * factorio_logistic_storage_items and factorio_logistic_provider_items:
          logistic stock split by where it sits. The API exposes no cheap view
          of outstanding requests, so this reports storage versus providers
          instead of pretending to measure demand.
        * factorio_rocket_silo_parts and factorio_rocket_silo_status per silo.
    - Logistic demand: factorio_logistic_requested_items sums outstanding
      requester filters, completing the supply/demand picture that the
      storage-versus-providers split only half answered. get_supply_counts
      takes one item at a time, so requester points are the workable route.
      The same chest can appear more than once in requester_points, so points
      are deduplicated per owning entity.
    - factorio_logistic_robots: logistic robots, construction robots and
      roboport cells per network.
    - factorio_trains_manual and factorio_trains per surface. A train left in
      manual mode is usually a forgotten train blocking a line.
    - factorio_daytime per surface, which explains solar and accumulator
      swings, and factorio_force_bonus{bonus} covering 17 research bonuses
      and modifiers as plain attribute reads.
    - CI now verifies the mod on a base-game install with Space Age, quality
      and elevated rails disabled, in addition to the full mod set. Every
      earlier test ran with the DLC enabled, so base-game behaviour was
      unverified; it passes, and is now a standing check rather than an
      assumption.
    - Space platform depth (part of graftorio3-depth-metrics):
      factorio_platform_hub_items with quality, factorio_platform_asteroid_chunks
      and factorio_platform_paused. The base collector reports state, weight,
      speed and distance; none of that says whether a platform is carrying
      anything or whether its asteroid intake has dried up. Collected per
      platform inside pcall, since the platform APIs are the newest part of
      the runtime.
    - Dual publishing for both Factorio channels. The stable channel is still
      2.0 while the mod targets 2.1, and factorio_version gates in both
      directions, so stable-channel servers could not install it at all.
      package.sh now emits a second zip, X.Y.(Z+1), with identical Lua and
      factorio_version 2.0, and the release workflow publishes both. The
      portal assigns exactly one game version per mod version -- verified
      against flib, helmod and even-distribution, none of which has a
      duplicate version number -- so a second version number is the only way
      to serve both.
    - Quality-aware production counts are now feature-probed at runtime.
      input_quality_counts does not exist before 2.1, and reading a missing
      field on a LuaObject raises rather than returning nil, so with the
      setting enabled on 2.0 the collector would have failed. It now falls
      back to the aggregate counts and quality="all". Verified on a real
      2.0.77 headless with the setting forced on: production still reported,
      no collector errors, promtool clean.
    - Vendored Prometheus library documented in prometheus/PROVENANCE.md: it
      comes from tarantool/metrics (BSD 2-Clause), arrived by inheritance
      through the fork chain and has been modified since, so it is a fork
      rather than a pinned dependency.
    - Removed the library's exported init(), which required
      prometheus.tarantool-metrics -- a module that was never part of the
      vendored subset, so any caller would have hit a load error. Being
      exported made it reachable from mods using the remote interface.
    - GitHub issue templates for bug reports and metric requests.
  Changes:
    - factorio_item_production_input and factorio_item_production_output gained
      a "quality" label. With the setting off it carries the constant value
      "all", so there is still one series per item and aggregations such as
      sum by (name) are unaffected -- but queries that match on an exact label
      set need the extra label. Fluid, kill and build statistics are unchanged:
      they have no quality dimension.
Version: 1.3.0
Date: 2026-08-04
  Minor Features:
    - Multi-server support: new startup settings graftorio3-instance-label
      (adds instance="..." to every series) and graftorio3-output-filename
      (configurable file name inside script-output/graftorio3/), so several
      servers can feed one Prometheus / share one textfile directory.
    - New metric factorio_surface_pollution (total pollution per surface).
    - entity-ghost added to the default entity count types: construction
      backlog becomes visible as factorio_entities{type="entity-ghost"}.
    - New event counters for rate() queries: factorio_events_total{type}
      (rocket_launched, research_finished, player_died). Totals live in
      storage and are re-applied after load, so the counter stays monotonic
      across server restarts rather than resetting each session.
    - New opt-in counters factorio_player_deaths_total and
      factorio_player_kills_total (behind graftorio3-collect-player-metrics).
    - CI gained a runtime verification job (tools/verify/): it starts a real
      headless server with the packaged zip, waits for a collection cycle and
      asserts that every expected metric family is written, that the log is
      free of Lua errors and that promtool accepts the output. A negative
      control deliberately breaks one collector stage and requires the harness
      to fail -- a verification job that cannot fail proves nothing.
    - Train statistics rewritten (v2):
        * Trip, arrival and loop state moved from module locals into storage.
          Previously every save/load silently reset it: trips in flight were
          lost and the seen table grew for the lifetime of the save, because
          trains that no longer exist were never removed.
        * New garbage collection stage prunes state for vanished trains,
          reported as factorio_train_tracked and factorio_train_gc_removed.
        * The train ID label is the largest series source (one per train per
          station pair) and is collapsed to the constant "all" unless
          graftorio3-train-include-id is enabled; the label stays in the
          schema so dashboards keep working either way.
        * graftorio3-train-max-series caps distinct label combinations; once
          exhausted, new ones are dropped and factorio_train_series_truncated
          is set to 1.
    - German locale (locale/de), covering every setting.
    - docs/: deployment guide (node_exporter textfile collector, containers,
      multi-instance hosts, Ansible fragment), ready-made Prometheus recording
      rules and Alertmanager rules for exporter staleness, collector errors and
      a paused server.
    - CONTRIBUTING.md rewritten for the actual toolchain; the stale bun/just
      instructions and the leftover Justfile and docker-compose.dev.yml are gone.
    - CI actions pinned to commit SHAs (kept at their current major versions
      rather than jumping to latest) with a dependabot config to keep the pins
      deliberate. The repository holds a portal API key, so a mutable tag in the
      release workflow is a real supply-chain surface.
    - New machine status aggregate: factorio_entity_status{surface,force,type,status}
      counts machines by operational state (no_power, no_ingredients, full_output,
      working, ...) -- the "factory is stalled, but where" metric. Configurable via
      graftorio3-entity-status-types (default assembling-machine, furnace,
      mining-drill, lab; empty disables it). Reading a status needs a real entity
      object, so this is the most expensive collector: own time-sliced stage,
      capped by graftorio3-entity-status-max-entities. Hitting the cap sets
      factorio_entity_status_truncated to 1 and reports
      factorio_entity_status_scanned instead of passing partial counts off as
      complete.
    - Public remote interface: other mods can publish their own metrics through
      graftorio3 instead of shipping a second exporter. remote.call("graftorio3",
      "register_gauge"/"register_counter"/"set"/"inc"/"api_version"). Names are
      validated and namespaced to factorio_*, registration is idempotent so it
      can be repeated in on_load, every call returns ok plus an error message,
      and the number of external metrics is capped.
    - Collection is now spread across ticks (graftorio3-time-slicing, on by
      default): one stage per tick slice instead of all stages at once. A full
      cycle still completes every "Scheduler timer" ticks, but the periodic
      spike drops sharply -- measured on a 5-surface benchmark map: max tick
      time 17.67 ms -> 6.70 ms at an unchanged average (0.117 -> 0.113 ms).
      Values within one file are then observed up to one cycle apart; turn the
      setting off if every series must be sampled in the same tick.
    - New /graftorio3 console command: last collection tick, output path,
      interval, collected surfaces, active settings and per-module collector
      error counts. Answers "nothing arrives in Prometheus" without log diving.
  Changes:
    - Zero-warning luacheck policy; .luacheckrc is now generated by
      scripts/gen_luacheckrc.py so declarations cannot drift from the code.
    - Metrics.md is generated from control.lua by scripts/gen_metrics.py;
      CI fails when the committed file drifts from the definitions.
Version: 1.2.0
Date: 2026-08-03
  Minor Features:
    - New environment/state metrics module (env.lua), covering previously unused
      parts of the runtime API:
        * factorio_game_speed, factorio_tick_paused, factorio_ticks_played
        * factorio_technology_price_multiplier, factorio_spoil_time_modifier
        * factorio_peaceful_mode, factorio_solar_power_multiplier,
          factorio_darkness, factorio_wind_speed, factorio_wind_orientation,
          factorio_freeze_daytime (per surface)
        * factorio_entities{surface,type} via count_entities_filtered
          (default types include unit-spawner for enemy-pressure tracking;
          configurable via graftorio3-entity-count-types)
        * factorio_technologies_researched / factorio_technologies_available,
          factorio_friendly_fire (per force)
        * factorio_player_online_time_ticks, factorio_player_afk_time_ticks
          (per player, opt-in via graftorio3-collect-player-metrics -- off
          by default due to per-player label cardinality)
    - New startup settings: graftorio3-entity-count-types,
      graftorio3-collect-player-metrics.
    - Renamed factorio_items_launched_total to factorio_items_launched: it is a
      gauge, and the _total suffix is reserved for counters by Prometheus
      convention (same class of fix as factorio_platforms in 1.1.0). Discovered
      by promtool while verifying the new metrics; it never surfaced before
      because the metric only appears once a rocket has actually launched.
Version: 1.1.0
Date: 2026-08-03
  Major Features:
    - Renamed to graftorio3 for the standalone mod portal release. All startup
      settings use the graftorio3- prefix; existing graftorio2 settings reset to
      defaults. Metrics are written to script-output/graftorio3/game.prom.
    - Factorio 2.1 compatibility.
    - New startup setting "Surface allowlist": restrict collection to named surfaces.
    - New startup setting "Include space platforms" (default off): space platforms are
      separate surfaces and multiply per-surface work and label cardinality.
  Minor Features:
    - New metric factorio_rockets_launched (per force).
    - Exporter self-metrics: factorio_exporter_series, factorio_exporter_output_bytes
      (previous cycle), factorio_exporter_last_collection_tick.
    - Collector resilience: every collection stage runs isolated; a failing stage
      increments factorio_collector_errors_total{module} and logs once instead of
      stopping the game. The metrics file is still written when a stage fails.
  Changes:
    - Renamed metric factorio_platform_count to factorio_platforms: the _count
      suffix is reserved for histogram/summary series by Prometheus convention
      (flagged by promtool). Done before the first portal release so no existing
      dashboards break.
  Bugfixes:
    - Replaced LuaEntity.neighbours and LuaEntity.disconnect_neighbour(), both removed
      in 2.1, with the LuaWireConnector API. Power switch handling raised
      "LuaEntity doesn't contain key neighbours" as soon as a switch was tracked.
    - Build and destroy events were registered twice (power.lua, then circuit-network.lua).
      Factorio allows one handler per event per mod, so the second registration silently
      replaced the first and all power build/destroy tracking was dead. Both modules are
      now dispatched from a single combined handler.
    - Metric collection iterated game.players instead of game.forces. A dedicated server
      that no player had joined produced no production, pollution-per-force, evolution,
      logistics, research or platform metrics at all.
    - factorio_items_launched was always empty: LuaForce.items_launched is a
      dictionary<string, uint>, and the code iterated it with ipairs().
    - Research and space platform gauges were reset inside the per-force loop, so each
      force wiped the previous force's series. Reset moved ahead of the loop.
    - Constant combinator rescan iterated game.players as well: combinators that
      existed at server start were never tracked on a player-less dedicated server.
      Rescan now iterates game.forces.
    - Circuit signal and logistic item quality labels always reported "normal":
      quality is a prototype-name string in the 2.x API, not an object with .name.
    - Combinator build/destroy tracking filtered by entity name while the rescan
      filtered by type; modded constant combinators are now tracked consistently.
Version: 1.0.0
Date: 2026-03-15
  Features:
    - add Justfile
  Optimizations:
    - performance improvements and misc fix
  Bugfixes:
    - correct the git path
    - correctly look up max wire distance