mclog-parser 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- mclog_parser-0.1.0/LICENSE +21 -0
- mclog_parser-0.1.0/PKG-INFO +115 -0
- mclog_parser-0.1.0/README.md +101 -0
- mclog_parser-0.1.0/mclog_parser/__init__.py +4 -0
- mclog_parser-0.1.0/mclog_parser/cli.py +92 -0
- mclog_parser-0.1.0/mclog_parser/parser.py +175 -0
- mclog_parser-0.1.0/mclog_parser.egg-info/PKG-INFO +115 -0
- mclog_parser-0.1.0/mclog_parser.egg-info/SOURCES.txt +11 -0
- mclog_parser-0.1.0/mclog_parser.egg-info/dependency_links.txt +1 -0
- mclog_parser-0.1.0/mclog_parser.egg-info/entry_points.txt +2 -0
- mclog_parser-0.1.0/mclog_parser.egg-info/top_level.txt +3 -0
- mclog_parser-0.1.0/pyproject.toml +23 -0
- mclog_parser-0.1.0/setup.cfg +4 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 IVANOLIO
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: mclog-parser
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Parse and analyze Minecraft server log files
|
|
5
|
+
License: MIT
|
|
6
|
+
Keywords: minecraft,log,parser,server,analysis
|
|
7
|
+
Classifier: Programming Language :: Python :: 3
|
|
8
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
9
|
+
Classifier: Topic :: Games/Entertainment
|
|
10
|
+
Requires-Python: >=3.10
|
|
11
|
+
Description-Content-Type: text/markdown
|
|
12
|
+
License-File: LICENSE
|
|
13
|
+
Dynamic: license-file
|
|
14
|
+
|
|
15
|
+
# mclog-parser
|
|
16
|
+
Analyze Minecraft server logs instantly — turn raw log files into structured data.
|
|
17
|
+
# mclog-parser 🎮
|
|
18
|
+
|
|
19
|
+
**Analyze Minecraft server logs instantly — turn raw log files into structured data.**
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
pip install mclog-parser
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## Why?
|
|
28
|
+
|
|
29
|
+
Every Minecraft server generates thousands of log lines. Finding crashes, tracking players, or spotting lag spikes means scrolling through walls of text manually.
|
|
30
|
+
|
|
31
|
+
`mclog-parser` does it in one line.
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
## Quickstart
|
|
36
|
+
|
|
37
|
+
```python
|
|
38
|
+
from mclog_parser import LogParser
|
|
39
|
+
|
|
40
|
+
log = LogParser("latest.log")
|
|
41
|
+
|
|
42
|
+
log.summary() # full overview
|
|
43
|
+
log.crashes() # all errors & crashes
|
|
44
|
+
log.lag_spikes() # lag warnings
|
|
45
|
+
log.player_stats() # per-player stats
|
|
46
|
+
log.export_json() # save report as JSON
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### Example Output
|
|
50
|
+
|
|
51
|
+
```json
|
|
52
|
+
{
|
|
53
|
+
"unique_players": 3,
|
|
54
|
+
"total_sessions": 5,
|
|
55
|
+
"crash_count": 1,
|
|
56
|
+
"lag_spike_count": 2,
|
|
57
|
+
"top_players": [
|
|
58
|
+
{ "player": "Ahmed99", "sessions": 2, "deaths": 1 }
|
|
59
|
+
]
|
|
60
|
+
}
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
---
|
|
64
|
+
|
|
65
|
+
## CLI
|
|
66
|
+
|
|
67
|
+
No Python needed — run directly from your terminal:
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
mclog latest.log # summary
|
|
71
|
+
mclog latest.log --crashes # errors only
|
|
72
|
+
mclog latest.log --players # player stats table
|
|
73
|
+
mclog latest.log --player Ahmed99 # one player's sessions
|
|
74
|
+
mclog latest.log --export report.json # export to JSON
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
---
|
|
78
|
+
|
|
79
|
+
## Supported Servers
|
|
80
|
+
|
|
81
|
+
| Server | Status |
|
|
82
|
+
|--------|--------|
|
|
83
|
+
| Paper | ✅ |
|
|
84
|
+
| Spigot | ✅ |
|
|
85
|
+
| Vanilla | ✅ |
|
|
86
|
+
| Fabric | ✅ |
|
|
87
|
+
| Forge | 🔜 |
|
|
88
|
+
|
|
89
|
+
---
|
|
90
|
+
|
|
91
|
+
## Install
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
pip install mclog-parser
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
Requires Python 3.10+
|
|
98
|
+
|
|
99
|
+
---
|
|
100
|
+
|
|
101
|
+
## Roadmap
|
|
102
|
+
|
|
103
|
+
- [x] Core parser (crashes, lag, sessions)
|
|
104
|
+
- [x] Player stats
|
|
105
|
+
- [x] JSON export
|
|
106
|
+
- [x] CLI
|
|
107
|
+
- [ ] Web dashboard
|
|
108
|
+
- [ ] Discord bot integration
|
|
109
|
+
- [ ] Real-time log watching
|
|
110
|
+
|
|
111
|
+
---
|
|
112
|
+
|
|
113
|
+
## License
|
|
114
|
+
|
|
115
|
+
MIT — free to use, modify, and distribute.
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
# mclog-parser
|
|
2
|
+
Analyze Minecraft server logs instantly — turn raw log files into structured data.
|
|
3
|
+
# mclog-parser 🎮
|
|
4
|
+
|
|
5
|
+
**Analyze Minecraft server logs instantly — turn raw log files into structured data.**
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pip install mclog-parser
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## Why?
|
|
14
|
+
|
|
15
|
+
Every Minecraft server generates thousands of log lines. Finding crashes, tracking players, or spotting lag spikes means scrolling through walls of text manually.
|
|
16
|
+
|
|
17
|
+
`mclog-parser` does it in one line.
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## Quickstart
|
|
22
|
+
|
|
23
|
+
```python
|
|
24
|
+
from mclog_parser import LogParser
|
|
25
|
+
|
|
26
|
+
log = LogParser("latest.log")
|
|
27
|
+
|
|
28
|
+
log.summary() # full overview
|
|
29
|
+
log.crashes() # all errors & crashes
|
|
30
|
+
log.lag_spikes() # lag warnings
|
|
31
|
+
log.player_stats() # per-player stats
|
|
32
|
+
log.export_json() # save report as JSON
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
### Example Output
|
|
36
|
+
|
|
37
|
+
```json
|
|
38
|
+
{
|
|
39
|
+
"unique_players": 3,
|
|
40
|
+
"total_sessions": 5,
|
|
41
|
+
"crash_count": 1,
|
|
42
|
+
"lag_spike_count": 2,
|
|
43
|
+
"top_players": [
|
|
44
|
+
{ "player": "Ahmed99", "sessions": 2, "deaths": 1 }
|
|
45
|
+
]
|
|
46
|
+
}
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
## CLI
|
|
52
|
+
|
|
53
|
+
No Python needed — run directly from your terminal:
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
mclog latest.log # summary
|
|
57
|
+
mclog latest.log --crashes # errors only
|
|
58
|
+
mclog latest.log --players # player stats table
|
|
59
|
+
mclog latest.log --player Ahmed99 # one player's sessions
|
|
60
|
+
mclog latest.log --export report.json # export to JSON
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
---
|
|
64
|
+
|
|
65
|
+
## Supported Servers
|
|
66
|
+
|
|
67
|
+
| Server | Status |
|
|
68
|
+
|--------|--------|
|
|
69
|
+
| Paper | ✅ |
|
|
70
|
+
| Spigot | ✅ |
|
|
71
|
+
| Vanilla | ✅ |
|
|
72
|
+
| Fabric | ✅ |
|
|
73
|
+
| Forge | 🔜 |
|
|
74
|
+
|
|
75
|
+
---
|
|
76
|
+
|
|
77
|
+
## Install
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
pip install mclog-parser
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Requires Python 3.10+
|
|
84
|
+
|
|
85
|
+
---
|
|
86
|
+
|
|
87
|
+
## Roadmap
|
|
88
|
+
|
|
89
|
+
- [x] Core parser (crashes, lag, sessions)
|
|
90
|
+
- [x] Player stats
|
|
91
|
+
- [x] JSON export
|
|
92
|
+
- [x] CLI
|
|
93
|
+
- [ ] Web dashboard
|
|
94
|
+
- [ ] Discord bot integration
|
|
95
|
+
- [ ] Real-time log watching
|
|
96
|
+
|
|
97
|
+
---
|
|
98
|
+
|
|
99
|
+
## License
|
|
100
|
+
|
|
101
|
+
MIT — free to use, modify, and distribute.
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
import argparse
|
|
3
|
+
import json
|
|
4
|
+
import sys
|
|
5
|
+
from mclog_parser import LogParser
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def main():
|
|
9
|
+
parser = argparse.ArgumentParser(
|
|
10
|
+
prog="mclog",
|
|
11
|
+
description="🎮 Minecraft Log Parser — Analyze your server logs instantly"
|
|
12
|
+
)
|
|
13
|
+
parser.add_argument("logfile", help="Path to latest.log or any MC log file")
|
|
14
|
+
parser.add_argument("--crashes", action="store_true", help="Show crashes and errors")
|
|
15
|
+
parser.add_argument("--lags", action="store_true", help="Show lag spikes")
|
|
16
|
+
parser.add_argument("--players", action="store_true", help="Show player stats")
|
|
17
|
+
parser.add_argument("--player", metavar="NAME", help="Sessions for a specific player")
|
|
18
|
+
parser.add_argument("--export", metavar="FILE", help="Export full report to JSON")
|
|
19
|
+
parser.add_argument("--summary", action="store_true", help="Show summary (default)", default=True)
|
|
20
|
+
|
|
21
|
+
args = parser.parse_args()
|
|
22
|
+
|
|
23
|
+
try:
|
|
24
|
+
log = LogParser(args.logfile)
|
|
25
|
+
except FileNotFoundError as e:
|
|
26
|
+
print(f"❌ {e}", file=sys.stderr)
|
|
27
|
+
sys.exit(1)
|
|
28
|
+
|
|
29
|
+
if args.crashes:
|
|
30
|
+
crashes = log.crashes()
|
|
31
|
+
print(f"\n💥 Crashes & Errors ({len(crashes)} total)\n" + "─" * 40)
|
|
32
|
+
for c in crashes:
|
|
33
|
+
print(f"[{c['time']}] {c['level']}: {c['message']}")
|
|
34
|
+
for t in c["trace"][:3]:
|
|
35
|
+
print(f" {t}")
|
|
36
|
+
return
|
|
37
|
+
|
|
38
|
+
if args.lags:
|
|
39
|
+
lags = log.lag_spikes()
|
|
40
|
+
print(f"\n⚠️ Lag Spikes ({len(lags)} total)\n" + "─" * 40)
|
|
41
|
+
for l in lags:
|
|
42
|
+
print(f"[{l['time']}] {l['message']}")
|
|
43
|
+
return
|
|
44
|
+
|
|
45
|
+
if args.players:
|
|
46
|
+
stats = log.player_stats()
|
|
47
|
+
print(f"\n👥 Player Stats ({len(stats)} players)\n" + "─" * 40)
|
|
48
|
+
print(f"{'Player':<20} {'Sessions':>8} {'Deaths':>7} {'Chat':>6}")
|
|
49
|
+
print("─" * 44)
|
|
50
|
+
for p in stats:
|
|
51
|
+
print(f"{p['player']:<20} {p['sessions']:>8} {p['deaths']:>7} {p['chat_messages']:>6}")
|
|
52
|
+
return
|
|
53
|
+
|
|
54
|
+
if args.player:
|
|
55
|
+
sessions = log.player_sessions(args.player)
|
|
56
|
+
data = sessions.get(args.player, [])
|
|
57
|
+
print(f"\n🎮 Sessions for {args.player} ({len(data)} total)\n" + "─" * 40)
|
|
58
|
+
for s in data:
|
|
59
|
+
left = s["left"] or "still online"
|
|
60
|
+
print(f" Joined: {s['joined']} → Left: {left}")
|
|
61
|
+
return
|
|
62
|
+
|
|
63
|
+
if args.export:
|
|
64
|
+
path = log.export_json(args.export)
|
|
65
|
+
print(f"✅ Report exported to: {path}")
|
|
66
|
+
return
|
|
67
|
+
|
|
68
|
+
# Default: summary
|
|
69
|
+
s = log.summary()
|
|
70
|
+
print(f"""
|
|
71
|
+
🎮 Minecraft Log Summary
|
|
72
|
+
{"─" * 40}
|
|
73
|
+
📁 File : {s['file']}
|
|
74
|
+
📄 Total Lines : {s['total_lines']:,}
|
|
75
|
+
👥 Players : {s['unique_players']}
|
|
76
|
+
🔗 Sessions : {s['total_sessions']}
|
|
77
|
+
💥 Crashes : {s['crash_count']}
|
|
78
|
+
⚠️ Lag Spikes : {s['lag_spike_count']}
|
|
79
|
+
|
|
80
|
+
🏆 Top Players:
|
|
81
|
+
""")
|
|
82
|
+
for p in s["top_players"]:
|
|
83
|
+
print(f" {p['player']:<20} {p['sessions']} sessions | {p['deaths']} deaths")
|
|
84
|
+
|
|
85
|
+
if s["crashes"]:
|
|
86
|
+
print(f"\n💥 Latest Crash:")
|
|
87
|
+
c = s["crashes"][-1]
|
|
88
|
+
print(f" [{c['time']}] {c['message']}")
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
if __name__ == "__main__":
|
|
92
|
+
main()
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
import re
|
|
2
|
+
import json
|
|
3
|
+
from datetime import datetime, timedelta
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from collections import defaultdict
|
|
6
|
+
from typing import Optional
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
# ─── Regex Patterns ───────────────────────────────────────────────
|
|
10
|
+
_LINE_RE = re.compile(
|
|
11
|
+
r"^\[(?P<time>\d{2}:\d{2}:\d{2})\] \[(?P<thread>[^\]]+)/(?P<level>[A-Z]+)\]: (?P<msg>.+)$"
|
|
12
|
+
)
|
|
13
|
+
_JOIN_RE = re.compile(r"^(?P<player>\w+) joined the game$")
|
|
14
|
+
_LEAVE_RE = re.compile(r"^(?P<player>\w+) left the game$")
|
|
15
|
+
_DEATH_RE = re.compile(r"^(?P<player>\w+) (?:was|died|fell|drowned|burned|blew|hit|tried|walked|withered|starved|suffocated|kinetic).+$")
|
|
16
|
+
_CHAT_RE = re.compile(r"^<(?P<player>\w+)> (?P<message>.+)$")
|
|
17
|
+
_LAG_RE = re.compile(r"Can't keep up!")
|
|
18
|
+
_CRASH_RE = re.compile(r"(?:java\.\w+Exception|java\.lang\.Error|Caused by:|FATAL)")
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class LogParser:
|
|
22
|
+
"""
|
|
23
|
+
Parse Minecraft server log files and extract structured data.
|
|
24
|
+
|
|
25
|
+
Usage:
|
|
26
|
+
log = LogParser("latest.log")
|
|
27
|
+
print(log.summary())
|
|
28
|
+
log.export_json("report.json")
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
def __init__(self, filepath: str):
|
|
32
|
+
self.filepath = Path(filepath)
|
|
33
|
+
if not self.filepath.exists():
|
|
34
|
+
raise FileNotFoundError(f"Log file not found: {filepath}")
|
|
35
|
+
|
|
36
|
+
self._lines: list[dict] = []
|
|
37
|
+
self._parse()
|
|
38
|
+
|
|
39
|
+
# ─── Internal ──────────────────────────────────────────────────
|
|
40
|
+
|
|
41
|
+
def _parse(self):
|
|
42
|
+
with open(self.filepath, "r", encoding="utf-8", errors="replace") as f:
|
|
43
|
+
for raw in f:
|
|
44
|
+
raw = raw.rstrip("\n")
|
|
45
|
+
m = _LINE_RE.match(raw)
|
|
46
|
+
if m:
|
|
47
|
+
self._lines.append({
|
|
48
|
+
"time": m.group("time"),
|
|
49
|
+
"thread": m.group("thread"),
|
|
50
|
+
"level": m.group("level"),
|
|
51
|
+
"msg": m.group("msg"),
|
|
52
|
+
"raw": raw,
|
|
53
|
+
})
|
|
54
|
+
else:
|
|
55
|
+
# continuation lines (stack traces etc.)
|
|
56
|
+
if self._lines:
|
|
57
|
+
self._lines[-1].setdefault("extra", []).append(raw)
|
|
58
|
+
|
|
59
|
+
# ─── Public API ────────────────────────────────────────────────
|
|
60
|
+
|
|
61
|
+
def crashes(self) -> list[dict]:
|
|
62
|
+
"""Return all crash/error events with context."""
|
|
63
|
+
results = []
|
|
64
|
+
for i, line in enumerate(self._lines):
|
|
65
|
+
if _CRASH_RE.search(line["msg"]) or line["level"] in ("ERROR", "FATAL"):
|
|
66
|
+
results.append({
|
|
67
|
+
"time": line["time"],
|
|
68
|
+
"level": line["level"],
|
|
69
|
+
"message": line["msg"],
|
|
70
|
+
"trace": line.get("extra", []),
|
|
71
|
+
})
|
|
72
|
+
return results
|
|
73
|
+
|
|
74
|
+
def lag_spikes(self) -> list[dict]:
|
|
75
|
+
"""Return all lag spike warnings."""
|
|
76
|
+
return [
|
|
77
|
+
{"time": l["time"], "message": l["msg"]}
|
|
78
|
+
for l in self._lines if _LAG_RE.search(l["msg"])
|
|
79
|
+
]
|
|
80
|
+
|
|
81
|
+
def player_sessions(self, player: Optional[str] = None) -> dict:
|
|
82
|
+
"""
|
|
83
|
+
Return join/leave sessions per player.
|
|
84
|
+
If player is given, filter to that player only.
|
|
85
|
+
"""
|
|
86
|
+
sessions: dict[str, list] = defaultdict(list)
|
|
87
|
+
last_join: dict[str, str] = {}
|
|
88
|
+
|
|
89
|
+
for line in self._lines:
|
|
90
|
+
jm = _JOIN_RE.match(line["msg"])
|
|
91
|
+
lm = _LEAVE_RE.match(line["msg"])
|
|
92
|
+
|
|
93
|
+
if jm:
|
|
94
|
+
p = jm.group("player")
|
|
95
|
+
last_join[p] = line["time"]
|
|
96
|
+
|
|
97
|
+
elif lm:
|
|
98
|
+
p = lm.group("player")
|
|
99
|
+
sessions[p].append({
|
|
100
|
+
"joined": last_join.pop(p, None),
|
|
101
|
+
"left": line["time"],
|
|
102
|
+
})
|
|
103
|
+
|
|
104
|
+
# players still online (no leave event)
|
|
105
|
+
for p, t in last_join.items():
|
|
106
|
+
sessions[p].append({"joined": t, "left": None})
|
|
107
|
+
|
|
108
|
+
if player:
|
|
109
|
+
return {player: sessions.get(player, [])}
|
|
110
|
+
return dict(sessions)
|
|
111
|
+
|
|
112
|
+
def player_stats(self) -> list[dict]:
|
|
113
|
+
"""Return per-player statistics sorted by session count."""
|
|
114
|
+
sessions = self.player_sessions()
|
|
115
|
+
deaths = defaultdict(int)
|
|
116
|
+
messages = defaultdict(int)
|
|
117
|
+
|
|
118
|
+
for line in self._lines:
|
|
119
|
+
dm = _DEATH_RE.match(line["msg"])
|
|
120
|
+
cm = _CHAT_RE.match(line["msg"])
|
|
121
|
+
if dm:
|
|
122
|
+
deaths[dm.group("player")] += 1
|
|
123
|
+
if cm:
|
|
124
|
+
messages[cm.group("player")] += 1
|
|
125
|
+
|
|
126
|
+
stats = []
|
|
127
|
+
all_players = set(sessions) | set(deaths) | set(messages)
|
|
128
|
+
for p in all_players:
|
|
129
|
+
s = sessions.get(p, [])
|
|
130
|
+
stats.append({
|
|
131
|
+
"player": p,
|
|
132
|
+
"sessions": len(s),
|
|
133
|
+
"deaths": deaths[p],
|
|
134
|
+
"chat_messages": messages[p],
|
|
135
|
+
})
|
|
136
|
+
|
|
137
|
+
return sorted(stats, key=lambda x: x["sessions"], reverse=True)
|
|
138
|
+
|
|
139
|
+
def top_players(self, n: int = 10) -> list[dict]:
|
|
140
|
+
"""Return top N players by sessions."""
|
|
141
|
+
return self.player_stats()[:n]
|
|
142
|
+
|
|
143
|
+
def summary(self) -> dict:
|
|
144
|
+
"""Return a full summary of the log file."""
|
|
145
|
+
stats = self.player_stats()
|
|
146
|
+
crashes = self.crashes()
|
|
147
|
+
lags = self.lag_spikes()
|
|
148
|
+
|
|
149
|
+
unique_players = len(stats)
|
|
150
|
+
total_sessions = sum(p["sessions"] for p in stats)
|
|
151
|
+
|
|
152
|
+
return {
|
|
153
|
+
"file": str(self.filepath),
|
|
154
|
+
"total_lines": len(self._lines),
|
|
155
|
+
"unique_players": unique_players,
|
|
156
|
+
"total_sessions": total_sessions,
|
|
157
|
+
"crash_count": len(crashes),
|
|
158
|
+
"lag_spike_count": len(lags),
|
|
159
|
+
"top_players": self.top_players(5),
|
|
160
|
+
"crashes": crashes[:5], # first 5 only in summary
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
def export_json(self, output_path: str = "mc_report.json") -> str:
|
|
164
|
+
"""Export full analysis to a JSON file."""
|
|
165
|
+
report = {
|
|
166
|
+
"summary": self.summary(),
|
|
167
|
+
"player_stats": self.player_stats(),
|
|
168
|
+
"all_crashes": self.crashes(),
|
|
169
|
+
"all_lag_spikes": self.lag_spikes(),
|
|
170
|
+
"player_sessions": self.player_sessions(),
|
|
171
|
+
}
|
|
172
|
+
path = Path(output_path)
|
|
173
|
+
with open(path, "w", encoding="utf-8") as f:
|
|
174
|
+
json.dump(report, f, ensure_ascii=False, indent=2)
|
|
175
|
+
return str(path)
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: mclog-parser
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Parse and analyze Minecraft server log files
|
|
5
|
+
License: MIT
|
|
6
|
+
Keywords: minecraft,log,parser,server,analysis
|
|
7
|
+
Classifier: Programming Language :: Python :: 3
|
|
8
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
9
|
+
Classifier: Topic :: Games/Entertainment
|
|
10
|
+
Requires-Python: >=3.10
|
|
11
|
+
Description-Content-Type: text/markdown
|
|
12
|
+
License-File: LICENSE
|
|
13
|
+
Dynamic: license-file
|
|
14
|
+
|
|
15
|
+
# mclog-parser
|
|
16
|
+
Analyze Minecraft server logs instantly — turn raw log files into structured data.
|
|
17
|
+
# mclog-parser 🎮
|
|
18
|
+
|
|
19
|
+
**Analyze Minecraft server logs instantly — turn raw log files into structured data.**
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
pip install mclog-parser
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## Why?
|
|
28
|
+
|
|
29
|
+
Every Minecraft server generates thousands of log lines. Finding crashes, tracking players, or spotting lag spikes means scrolling through walls of text manually.
|
|
30
|
+
|
|
31
|
+
`mclog-parser` does it in one line.
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
## Quickstart
|
|
36
|
+
|
|
37
|
+
```python
|
|
38
|
+
from mclog_parser import LogParser
|
|
39
|
+
|
|
40
|
+
log = LogParser("latest.log")
|
|
41
|
+
|
|
42
|
+
log.summary() # full overview
|
|
43
|
+
log.crashes() # all errors & crashes
|
|
44
|
+
log.lag_spikes() # lag warnings
|
|
45
|
+
log.player_stats() # per-player stats
|
|
46
|
+
log.export_json() # save report as JSON
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### Example Output
|
|
50
|
+
|
|
51
|
+
```json
|
|
52
|
+
{
|
|
53
|
+
"unique_players": 3,
|
|
54
|
+
"total_sessions": 5,
|
|
55
|
+
"crash_count": 1,
|
|
56
|
+
"lag_spike_count": 2,
|
|
57
|
+
"top_players": [
|
|
58
|
+
{ "player": "Ahmed99", "sessions": 2, "deaths": 1 }
|
|
59
|
+
]
|
|
60
|
+
}
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
---
|
|
64
|
+
|
|
65
|
+
## CLI
|
|
66
|
+
|
|
67
|
+
No Python needed — run directly from your terminal:
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
mclog latest.log # summary
|
|
71
|
+
mclog latest.log --crashes # errors only
|
|
72
|
+
mclog latest.log --players # player stats table
|
|
73
|
+
mclog latest.log --player Ahmed99 # one player's sessions
|
|
74
|
+
mclog latest.log --export report.json # export to JSON
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
---
|
|
78
|
+
|
|
79
|
+
## Supported Servers
|
|
80
|
+
|
|
81
|
+
| Server | Status |
|
|
82
|
+
|--------|--------|
|
|
83
|
+
| Paper | ✅ |
|
|
84
|
+
| Spigot | ✅ |
|
|
85
|
+
| Vanilla | ✅ |
|
|
86
|
+
| Fabric | ✅ |
|
|
87
|
+
| Forge | 🔜 |
|
|
88
|
+
|
|
89
|
+
---
|
|
90
|
+
|
|
91
|
+
## Install
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
pip install mclog-parser
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
Requires Python 3.10+
|
|
98
|
+
|
|
99
|
+
---
|
|
100
|
+
|
|
101
|
+
## Roadmap
|
|
102
|
+
|
|
103
|
+
- [x] Core parser (crashes, lag, sessions)
|
|
104
|
+
- [x] Player stats
|
|
105
|
+
- [x] JSON export
|
|
106
|
+
- [x] CLI
|
|
107
|
+
- [ ] Web dashboard
|
|
108
|
+
- [ ] Discord bot integration
|
|
109
|
+
- [ ] Real-time log watching
|
|
110
|
+
|
|
111
|
+
---
|
|
112
|
+
|
|
113
|
+
## License
|
|
114
|
+
|
|
115
|
+
MIT — free to use, modify, and distribute.
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
LICENSE
|
|
2
|
+
README.md
|
|
3
|
+
pyproject.toml
|
|
4
|
+
mclog_parser/__init__.py
|
|
5
|
+
mclog_parser/cli.py
|
|
6
|
+
mclog_parser/parser.py
|
|
7
|
+
mclog_parser.egg-info/PKG-INFO
|
|
8
|
+
mclog_parser.egg-info/SOURCES.txt
|
|
9
|
+
mclog_parser.egg-info/dependency_links.txt
|
|
10
|
+
mclog_parser.egg-info/entry_points.txt
|
|
11
|
+
mclog_parser.egg-info/top_level.txt
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=68", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "mclog-parser"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Parse and analyze Minecraft server log files"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = { text = "MIT" }
|
|
11
|
+
requires-python = ">=3.10"
|
|
12
|
+
keywords = ["minecraft", "log", "parser", "server", "analysis"]
|
|
13
|
+
classifiers = [
|
|
14
|
+
"Programming Language :: Python :: 3",
|
|
15
|
+
"License :: OSI Approved :: MIT License",
|
|
16
|
+
"Topic :: Games/Entertainment",
|
|
17
|
+
]
|
|
18
|
+
|
|
19
|
+
[project.scripts]
|
|
20
|
+
mclog = "mclog_parser.cli:main"
|
|
21
|
+
|
|
22
|
+
[tool.setuptools.packages.find]
|
|
23
|
+
where = ["."]
|