diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 0000000..a9db644 --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,7 @@ +default: + before_script: + - docker info + +build_image: + script: + - docker build -t milka64/minecraft-exporter . diff --git a/minecraft_exporter.py b/minecraft_exporter.py index a752742..8fa694e 100644 --- a/minecraft_exporter.py +++ b/minecraft_exporter.py @@ -88,15 +88,15 @@ class MinecraftCollector(object): metrics = [] - dim_tps = Metric('dim_tps', 'TPS of a dimension', "counter") - dim_ticktime = Metric('dim_ticktime', "Time a Tick took in a Dimension", "counter") - overall_tps = Metric('overall_tps', 'overall TPS', "counter") - overall_ticktime = Metric('overall_ticktime', "overall Ticktime", "counter") - player_online = Metric('player_online', "is 1 if player is online", "counter") - entities = Metric('entities', "type and count of active entites", "counter") - tps_1m = Metric('paper_tps_1m', '1 Minute TPS', "counter") - tps_5m = Metric('paper_tps_5m', '5 Minute TPS', "counter") - tps_15m = Metric('paper_tps_15m', '15 Minute TPS', "counter") + dim_tps = Metric('mc_dim_tps', 'TPS of a dimension', "counter") + dim_ticktime = Metric('mc_dim_ticktime', "Time a Tick took in a Dimension", "counter") + overall_tps = Metric('mc_overall_tps', 'overall TPS', "counter") + overall_ticktime = Metric('mc_overall_ticktime', "overall Ticktime", "counter") + player_online = Metric('mc_player_online', "is 1 if player is online", "counter") + entities = Metric('mc_entities', "type and count of active entites", "counter") + tps_1m = Metric('mc_paper_tps_1m', '1 Minute TPS', "counter") + tps_5m = Metric('mc_paper_tps_5m', '5 Minute TPS', "counter") + tps_15m = Metric('mc_paper_tps_15m', '15 Minute TPS', "counter") metrics.extend( [dim_tps, dim_ticktime, overall_tps, overall_ticktime, player_online, entities, tps_1m, tps_5m, tps_15m]) @@ -104,34 +104,34 @@ class MinecraftCollector(object): resp = str(self.rcon_command("tps")).strip().replace("§a", "") tpsregex = re.compile("TPS from last 1m, 5m, 15m: (\d*\.\d*), (\d*\.\d*), (\d*\.\d*)") for m1, m5, m15 in tpsregex.findall(resp): - tps_1m.add_sample('paper_tps_1m', value=m1, labels={'tps': '1m'}) - tps_5m.add_sample('paper_tps_5m', value=m5, labels={'tps': '5m'}) - tps_15m.add_sample('paper_tps_15m', value=m15, labels={'tps': '15m'}) + tps_1m.add_sample('mc_paper_tps_1m', value=m1, labels={'tps': '1m'}) + tps_5m.add_sample('mc_paper_tps_5m', value=m5, labels={'tps': '5m'}) + tps_15m.add_sample('mc_paper_tps_15m', value=m15, labels={'tps': '15m'}) if 'FORGE_SERVER' in os.environ and os.environ['FORGE_SERVER'] == "True": # dimensions resp = self.rcon_command("forge tps") dimtpsregex = re.compile("Dim\s*(-*\d*)\s\((.*?)\)\s:\sMean tick time:\s(.*?) ms\. Mean TPS: (\d*\.\d*)") for dimid, dimname, meanticktime, meantps in dimtpsregex.findall(resp): - dim_tps.add_sample('dim_tps', value=meantps, labels={'dimension_id': dimid, 'dimension_name': dimname}) - dim_ticktime.add_sample('dim_ticktime', value=meanticktime, + dim_tps.add_sample('mc_dim_tps', value=meantps, labels={'dimension_id': dimid, 'dimension_name': dimname}) + dim_ticktime.add_sample('mc_dim_ticktime', value=meanticktime, labels={'dimension_id': dimid, 'dimension_name': dimname}) overallregex = re.compile("Overall\s?: Mean tick time: (.*) ms. Mean TPS: (.*)") - overall_tps.add_sample('overall_tps', value=overallregex.findall(resp)[0][1], labels={}) - overall_ticktime.add_sample('overall_ticktime', value=overallregex.findall(resp)[0][0], labels={}) + overall_tps.add_sample('mc_overall_tps', value=overallregex.findall(resp)[0][1], labels={}) + overall_ticktime.add_sample('mc_overall_ticktime', value=overallregex.findall(resp)[0][0], labels={}) # entites resp = self.rcon_command("forge entity list") entityregex = re.compile("(\d+): (.*?:.*?)\s") for entitycount, entityname in entityregex.findall(resp): - entities.add_sample('entities', value=entitycount, labels={'entity': entityname}) + entities.add_sample('mc_entities', value=entitycount, labels={'entity': entityname}) # dynmap if 'DYNMAP_ENABLED' in os.environ and os.environ['DYNMAP_ENABLED'] == "True": - dynmap_tile_render_statistics = Metric('dynmap_tile_render_statistics', + dynmap_tile_render_statistics = Metric('mc_dynmap_tile_render_statistics', 'Tile Render Statistics reported by Dynmap', "counter") - dynmap_chunk_loading_statistics_count = Metric('dynmap_chunk_loading_statistics_count', + dynmap_chunk_loading_statistics_count = Metric('mc_dynmap_chunk_loading_statistics_count', 'Chunk Loading Statistics reported by Dynmap', "counter") - dynmap_chunk_loading_statistics_duration = Metric('dynmap_chunk_loading_statistics_duration', + dynmap_chunk_loading_statistics_duration = Metric('mc_dynmap_chunk_loading_statistics_duration', 'Chunk Loading Statistics reported by Dynmap', "counter") metrics.extend([dynmap_tile_render_statistics, dynmap_chunk_loading_statistics_count, dynmap_chunk_loading_statistics_duration]) @@ -140,28 +140,33 @@ class MinecraftCollector(object): dynmaptilerenderregex = re.compile(" (.*?): processed=(\d*), rendered=(\d*), updated=(\d*)") for dim, processed, rendered, updated in dynmaptilerenderregex.findall(resp): - dynmap_tile_render_statistics.add_sample('dynmap_tile_render_statistics', value=processed, + dynmap_tile_render_statistics.add_sample('mc_dynmap_tile_render_statistics', value=processed, labels={'type': 'processed', 'file': dim}) - dynmap_tile_render_statistics.add_sample('dynmap_tile_render_statistics', value=rendered, + dynmap_tile_render_statistics.add_sample('mc_dynmap_tile_render_statistics', value=rendered, labels={'type': 'rendered', 'file': dim}) - dynmap_tile_render_statistics.add_sample('dynmap_tile_render_statistics', value=updated, + dynmap_tile_render_statistics.add_sample('mc_dynmap_tile_render_statistics', value=updated, labels={'type': 'updated', 'file': dim}) dynmapchunkloadingregex = re.compile("Chunks processed: (.*?): count=(\d*), (\d*.\d*)") for state, count, duration_per_chunk in dynmapchunkloadingregex.findall(resp): - dynmap_chunk_loading_statistics_count.add_sample('dynmap_chunk_loading_statistics', value=count, + dynmap_chunk_loading_statistics_count.add_sample('mc_dynmap_chunk_loading_statistics', value=count, labels={'type': state}) - dynmap_chunk_loading_statistics_duration.add_sample('dynmap_chunk_loading_duration', + dynmap_chunk_loading_statistics_duration.add_sample('mc_dynmap_chunk_loading_duration', value=duration_per_chunk, labels={'type': state}) # player resp = self.rcon_command("list") - playerregex = re.compile("players online:(.*)") - if playerregex.findall(resp): - for player in playerregex.findall(resp)[0].split(","): - if not player.isspace(): - player_online.add_sample('player_online', value=1, labels={'player': player.lstrip()}) - + #playerregex = re.compile("players online:(.*)") + #if playerregex.findall(resp): + # for player in playerregex.findall(resp)[0].split(","): + # if not player.isspace(): + # player_online.add_sample('mc_player_online', value=1, labels={'player': player.lstrip()}) + if resp.startswith('There are 0 '): + player_online.add_sample('mc_player_online', value=0, labels={}) + else: + players = resp.split(':')[1].replace(' ','').split(',') + for player in players: + player_online.add_sample('mc_player_online', value=1, labels={'player': player}) return metrics def get_player_quests_finished(self, uuid): @@ -205,25 +210,25 @@ class MinecraftCollector(object): data = self.get_player_stats(uuid) - blocks_mined = Metric('blocks_mined', 'Blocks a Player mined', "counter") - blocks_picked_up = Metric('blocks_picked_up', 'Blocks a Player picked up', "counter") - player_deaths = Metric('player_deaths', 'How often a Player died', "counter") - player_jumps = Metric('player_jumps', 'How often a Player has jumped', "counter") - cm_traveled = Metric('cm_traveled', 'How many cm a Player traveled, whatever that means', "counter") - player_xp_total = Metric('player_xp_total', "How much total XP a player has", "counter") - player_current_level = Metric('player_current_level', "How much current XP a player has", "counter") - player_food_level = Metric('player_food_level', "How much food the player currently has", "counter") - player_health = Metric('player_health', "How much Health the player currently has", "counter") - player_score = Metric('player_score', "The Score of the player", "counter") - entities_killed = Metric('entities_killed', "Entities killed by player", "counter") - damage_taken = Metric('damage_taken', "Damage Taken by Player", "counter") - damage_dealt = Metric('damage_dealt', "Damage dealt by Player", "counter") - blocks_crafted = Metric('blocks_crafted', "Items a Player crafted", "counter") - player_playtime = Metric('player_playtime', "Time in Minutes a Player was online", "counter") - player_advancements = Metric('player_advancements', "Number of completed advances of a player", "counter") - player_slept = Metric('player_slept', "Times a Player slept in a bed", "counter") - player_quests_finished = Metric('player_quests_finished', 'Number of quests a Player has finished', 'counter') - player_used_crafting_table = Metric('player_used_crafting_table', "Times a Player used a Crafting Table", + blocks_mined = Metric('mc_blocks_mined', 'Blocks a Player mined', "counter") + blocks_picked_up = Metric('mc_blocks_picked_up', 'Blocks a Player picked up', "counter") + player_deaths = Metric('mc_player_deaths', 'How often a Player died', "counter") + player_jumps = Metric('mc_player_jumps', 'How often a Player has jumped', "counter") + cm_traveled = Metric('mc_cm_traveled', 'How many cm a Player traveled, whatever that means', "counter") + player_xp_total = Metric('mc_player_xp_total', "How much total XP a player has", "counter") + player_current_level = Metric('mc_player_current_level', "How much current XP a player has", "counter") + player_food_level = Metric('mc_player_food_level', "How much food the player currently has", "counter") + player_health = Metric('mc_player_health', "How much Health the player currently has", "counter") + player_score = Metric('mc_player_score', "The Score of the player", "counter") + entities_killed = Metric('mc_entities_killed', "Entities killed by player", "counter") + damage_taken = Metric('mc_damage_taken', "Damage Taken by Player", "counter") + damage_dealt = Metric('mc_damage_dealt', "Damage dealt by Player", "counter") + blocks_crafted = Metric('mc_blocks_crafted', "Items a Player crafted", "counter") + player_playtime = Metric('mc_player_playtime', "Time in Minutes a Player was online", "counter") + player_advancements = Metric('mc_player_advancements', "Number of completed advances of a player", "counter") + player_slept = Metric('mc_player_slept', "Times a Player slept in a bed", "counter") + player_quests_finished = Metric('mc_player_quests_finished', 'Number of quests a Player has finished', 'counter') + player_used_crafting_table = Metric('mc_player_used_crafting_table', "Times a Player used a Crafting Table", "counter") mc_custom = Metric('mc_custom', "Custom Minecraft stat", "counter") for key, value in data.items(): # pre 1.15 @@ -238,10 +243,10 @@ class MinecraftCollector(object): (key.split(".")[2], key.split(".")[3]))}) elif stat == "entityKilledBy": if len(key.split(".")) == 4: - player_deaths.add_sample('player_deaths', value=value, labels={'player': name, 'cause': '.'.join( + player_deaths.add_sample('mc_player_deaths', value=value, labels={'player': name, 'cause': '.'.join( (key.split(".")[2], key.split(".")[3]))}) else: - player_deaths.add_sample('player_deaths', value=value, + player_deaths.add_sample('mc_player_deaths', value=value, labels={'player': name, 'cause': key.split(".")[2]}) elif stat == "jump": player_jumps.add_sample("player_jumps", value=value, labels={'player': name}) @@ -264,41 +269,41 @@ class MinecraftCollector(object): elif stat == "climbOneCm": cm_traveled.add_sample("cm_traveled", value=value, labels={'player': name, 'method': "climbing"}) elif stat == "XpTotal": - player_xp_total.add_sample('player_xp_total', value=value, labels={'player': name}) + player_xp_total.add_sample('mc_player_xp_total', value=value, labels={'player': name}) elif stat == "XpLevel": - player_current_level.add_sample('player_current_level', value=value, labels={'player': name}) + player_current_level.add_sample('mc_player_current_level', value=value, labels={'player': name}) elif stat == "foodLevel": - player_food_level.add_sample('player_food_level', value=value, labels={'player': name}) + player_food_level.add_sample('mc_player_food_level', value=value, labels={'player': name}) elif stat == "Health": - player_health.add_sample('player_health', value=value, labels={'player': name}) + player_health.add_sample('mc_player_health', value=value, labels={'player': name}) elif stat == "Score": - player_score.add_sample('player_score', value=value, labels={'player': name}) + player_score.add_sample('mc_player_score', value=value, labels={'player': name}) elif stat == "killEntity": - entities_killed.add_sample('entities_killed', value=value, + entities_killed.add_sample('mc_entities_killed', value=value, labels={'player': name, "entity": key.split(".")[2]}) elif stat == "damageDealt": - damage_dealt.add_sample('damage_dealt', value=value, labels={'player': name}) + damage_dealt.add_sample('mc_damage_dealt', value=value, labels={'player': name}) elif stat == "damageTaken": - damage_dealt.add_sample('damage_taken', value=value, labels={'player': name}) + damage_dealt.add_sample('mc_damage_taken', value=value, labels={'player': name}) elif stat == "craftItem": - blocks_crafted.add_sample('blocks_crafted', value=value, labels={'player': name, 'block': '.'.join( + blocks_crafted.add_sample('mc_blocks_crafted', value=value, labels={'player': name, 'block': '.'.join( (key.split(".")[2], key.split(".")[3]))}) elif stat == "playOneMinute": - player_playtime.add_sample('player_playtime', value=value, labels={'player': name}) + player_playtime.add_sample('mc_player_playtime', value=value, labels={'player': name}) elif stat == "advancements": - player_advancements.add_sample('player_advancements', value=value, labels={'player': name}) + player_advancements.add_sample('mc_player_advancements', value=value, labels={'player': name}) elif stat == "sleepInBed": - player_slept.add_sample('player_slept', value=value, labels={'player': name}) + player_slept.add_sample('mc_player_slept', value=value, labels={'player': name}) elif stat == "craftingTableInteraction": - player_used_crafting_table.add_sample('player_used_crafting_table', value=value, + player_used_crafting_table.add_sample('mc_player_used_crafting_table', value=value, labels={'player': name}) elif stat == "questsFinished": - player_quests_finished.add_sample('player_quests_finished', value=value, labels={'player': name}) + player_quests_finished.add_sample('mc_player_quests_finished', value=value, labels={'player': name}) if "stats" in data: # Minecraft > 1.15 if "minecraft:crafted" in data["stats"]: for block, value in data["stats"]["minecraft:crafted"].items(): - blocks_crafted.add_sample('blocks_crafted', value=value, labels={'player': name, 'block': block}) + blocks_crafted.add_sample('mc_blocks_crafted', value=value, labels={'player': name, 'block': block}) if "minecraft:mined" in data["stats"]: for block, value in data["stats"]["minecraft:mined"].items(): blocks_mined.add_sample("blocks_mined", value=value, labels={'player': name, 'block': block}) @@ -308,24 +313,24 @@ class MinecraftCollector(object): labels={'player': name, 'block': block}) if "minecraft:killed" in data["stats"]: for entity, value in data["stats"]["minecraft:killed"].items(): - entities_killed.add_sample('entities_killed', value=value, + entities_killed.add_sample('mc_entities_killed', value=value, labels={'player': name, "entity": entity}) if "minecraft:killed_by" in data["stats"]: for entity, value in data["stats"]["minecraft:killed_by"].items(): - player_deaths.add_sample('player_deaths', value=value, labels={'player': name, 'cause': entity}) + player_deaths.add_sample('mc_player_deaths', value=value, labels={'player': name, 'cause': entity}) for stat, value in data["stats"]["minecraft:custom"].items(): if stat == "minecraft:jump": player_jumps.add_sample("player_jumps", value=value, labels={'player': name}) elif stat == "minecraft:deaths": - player_deaths.add_sample('player_deaths', value=value, labels={'player': name}) + player_deaths.add_sample('mc_player_deaths', value=value, labels={'player': name}) elif stat == "minecraft:damage_taken": - damage_taken.add_sample('damage_taken', value=value, labels={'player': name}) + damage_taken.add_sample('mc_damage_taken', value=value, labels={'player': name}) elif stat == "minecraft:damage_dealt": - damage_dealt.add_sample('damage_dealt',value=value,labels={'player':name}) + damage_dealt.add_sample('mc_damage_dealt',value=value,labels={'player':name}) elif stat == "minecraft:play_time": - player_playtime.add_sample('player_playtime',value=value,labels={'player':name}) + player_playtime.add_sample('mc_player_playtime',value=value,labels={'player':name}) elif stat == "minecraft:play_one_minute": # pre 1.17 - player_playtime.add_sample('player_playtime',value=value,labels={'player':name}) + player_playtime.add_sample('mc_player_playtime',value=value,labels={'player':name}) elif stat == "minecraft:walk_one_cm": cm_traveled.add_sample("cm_traveled", value=value, labels={'player': name, 'method': "walking"}) elif stat == "minecraft:walk_on_water_one_cm": @@ -345,9 +350,9 @@ class MinecraftCollector(object): elif stat == "minecraft:climb_one_cm": cm_traveled.add_sample("cm_traveled", value=value, labels={'player': name, 'method': "climbing"}) elif stat == "minecraft:sleep_in_bed": - player_slept.add_sample('player_slept', value=value, labels={'player': name}) + player_slept.add_sample('mc_player_slept', value=value, labels={'player': name}) elif stat == "minecraft:interact_with_crafting_table": - player_used_crafting_table.add_sample('player_used_crafting_table', value=value, + player_used_crafting_table.add_sample('mc_player_used_crafting_table', value=value, labels={'player': name}) else: mc_custom.add_sample('mc_custom', value=value, labels={'stat': stat})