refactor: move away from monorepo setup

This commit is contained in:
2025-09-19 22:34:07 +02:00
parent bf119e4b47
commit 9dab984e25
12 changed files with 31 additions and 43 deletions

55
src/control.lua Normal file
View File

@@ -0,0 +1,55 @@
local metrics = require("utils.metrics")
-- config
local flush_interval_ticks = 60 -- flush every n ticks
-- state
--- @type table<number, fun()>
local queue = {}
--- @type table<number, string>
local buffer = {}
local last_reset = 0
-- queue operations to perform for each round of metric processing
-- that way, avoid doing too much work in a single game tick and causing UPS inconsistencies
local function prepare_queue()
queue = {}
for _, surface in pairs(game.surfaces) do
for _, force in pairs(game.forces) do
table.insert(queue, metrics.calc_item_production_statistics(buffer, surface, force))
table.insert(queue, metrics.calc_fluid_production_statistics(buffer, surface, force))
end
table.insert(queue, metrics.calc_power_statistics(buffer, surface))
end
end
local function process_queue()
local entry = table.remove(queue, 1)
if entry then
entry()
end
end
local function tick()
process_queue()
if #queue == 0 and #buffer > 0 then
helpers.write_file("metrics.prom", table.concat(buffer, "\n"), false)
buffer = {}
end
if #queue == 0 and game.tick - last_reset >= flush_interval_ticks then
prepare_queue()
last_reset = game.tick
end
end
-- main tick handler
script.on_event(defines.events.on_tick, tick)
-- startup
script.on_init(function()
last_reset = game.tick
prepare_queue()
end)

1
src/data.lua Normal file
View File

@@ -0,0 +1 @@
require("prototypes.metrics-pole").setup()

9
src/info.json Normal file
View File

@@ -0,0 +1,9 @@
{
"name": "prometheus-exporter",
"version": "0.0.1",
"title": "Prometheus Exporter for Factorio",
"author": "Bjorn Pijnacker",
"factorio_version": "2.0",
"dependencies": ["base >= 2.0"],
"description": "Exports certain production and consumption metrics to Prometheus format, to be used by Prometheus/Grafana monitoring stack"
}

View File

@@ -0,0 +1,62 @@
local name = "metrics-pole"
--- @class MetricsPole
--- @field name string
--- @field setup fun()
local M = {
name = name,
setup = function()
local function tint(alpha)
return { r = 1, g = 0, b = 1, a = alpha }
end
-- POLE
local pole = table.deepcopy(data.raw["electric-pole"]["small-electric-pole"])
pole.name = name
pole.minable.result = name
pole.icons = {
{
icon = pole.icon,
icon_size = pole.icon_size,
tint = tint(0.3)
},
}
pole.is_military_target = false
pole.hidden_in_factoriopedia = true
pole.localised_name = "Metrics Pole"
pole.supply_area_distance = 0
for _, layer in pairs(pole.pictures.layers) do
layer.tint = tint(1)
end
data:extend({ pole })
-- ITEM
local item = table.deepcopy(data.raw["item"]["small-electric-pole"])
item.name = name
item.place_result = name
item.localised_name = "Metrics Pole"
item.icons = {
{
icon = item.icon,
icon_size = item.icon_size,
tint = tint(0.3)
},
}
data:extend({ item })
-- RECIPE
data:extend({
{
type = "recipe",
name = name,
enabled = true,
ingredients = { { name = "wood", amount = 1, type = "item" } },
results = { { name = name, type = "item", amount = 1 } }
}
})
end
}
return M

75
src/utils/metrics.lua Normal file
View File

@@ -0,0 +1,75 @@
local prometheus = require("utils.prometheus")
local metrics_pole = require('prototypes.metrics-pole')
local M = {}
--- @param metrics_table table<number, string>
--- @param surface LuaSurface
--- @param force LuaForce
--- @return fun()
function M.calc_item_production_statistics(metrics_table, surface, force)
return function()
local item_stats = force.get_item_production_statistics(surface.name)
for item, count in pairs(item_stats.input_counts) do
table.insert(metrics_table,
prometheus.counter("production", "",
{ type = "item", surface = surface.name, force = force.name, name = item }, count))
end
for item, count in pairs(item_stats.output_counts) do
table.insert(metrics_table,
prometheus.counter("consumption", "",
{ type = "item", surface = surface.name, force = force.name, name = item }, count))
end
end
end
--- @param metrics_table table<number, string>
--- @param surface LuaSurface
--- @param force LuaForce
--- @return fun()
function M.calc_fluid_production_statistics(metrics_table, surface, force)
return function()
local fluid_stats = force.get_fluid_production_statistics(surface.name)
for item, count in pairs(fluid_stats.input_counts) do
table.insert(metrics_table,
prometheus.counter("production", "",
{ type = "fluid", surface = surface.name, force = force.name, name = item }, count))
end
for item, count in pairs(fluid_stats.output_counts) do
table.insert(metrics_table,
prometheus.counter("consumption", "",
{ type = "fluid", surface = surface.name, force = force.name, name = item }, count))
end
end
end
--- @param metrics_table table<number, string>
--- @param surface LuaSurface
--- @return fun()
function M.calc_power_statistics(metrics_table, surface)
return function()
local seen = {}
for _, pole in pairs(surface.find_entities_filtered { name = metrics_pole.name }) do
if table[pole.electric_network_id] then -- deduplication
goto continue
end
table.insert(seen, pole.electric_network_id)
local stats = pole.electric_network_statistics
for name, count in pairs(stats.input_counts) do
table.insert(metrics_table,
prometheus.counter("energy_consumption", "_joules",
{ surface = surface.name, network_id = pole.electric_network_id, name = name }, count))
end
for name, count in pairs(stats.output_counts) do
table.insert(metrics_table,
prometheus.counter("energy_production", "_joules",
{ surface = surface.name, network_id = pole.electric_network_id, name = name }, count))
end
::continue::
end
end
end
return M

17
src/utils/prometheus.lua Normal file
View File

@@ -0,0 +1,17 @@
local M = {}
--- Formats a name + table of labels as a Prometheus metric
--- @param name string
--- @param suffix string
--- @param labels table
--- @param value number
function M.counter(name, suffix, labels, value)
local label_parts = {}
for k, v in pairs(labels) do
table.insert(label_parts, string.format('%s="%s"', k, v))
end
local label_str = table.concat(label_parts, ",")
return string.format("factorio_%s_total%s{%s} %d", name, suffix, label_str, value)
end
return M