initial version of structured log display
This commit is contained in:
parent
518ad1d811
commit
f5fe1c4f29
2 changed files with 134 additions and 1 deletions
|
@ -4,6 +4,8 @@
|
||||||
export let matchLog: string;
|
export let matchLog: string;
|
||||||
let playerLog: PlayerLog;
|
let playerLog: PlayerLog;
|
||||||
|
|
||||||
|
let showRawStderr = false;
|
||||||
|
|
||||||
$: if (matchLog) {
|
$: if (matchLog) {
|
||||||
playerLog = parsePlayerLog(1, matchLog);
|
playerLog = parsePlayerLog(1, matchLog);
|
||||||
} else {
|
} else {
|
||||||
|
@ -13,9 +15,38 @@
|
||||||
|
|
||||||
<div class="output">
|
<div class="output">
|
||||||
<h3 class="output-header">Player log</h3>
|
<h3 class="output-header">Player log</h3>
|
||||||
|
{#if showRawStderr}
|
||||||
<div class="output-text stderr-text">
|
<div class="output-text stderr-text">
|
||||||
{playerLog.flatMap((turn) => turn.stderr).join("\n")}
|
{playerLog.flatMap((turn) => turn.stderr).join("\n")}
|
||||||
</div>
|
</div>
|
||||||
|
{:else}
|
||||||
|
<div class="output-text">
|
||||||
|
{#each playerLog as logTurn, i}
|
||||||
|
<div class="turn">
|
||||||
|
<div class="turn-header">
|
||||||
|
<span class="turn-header-text">Turn {i}</span>
|
||||||
|
{#if logTurn.action?.type === "bad_command"}
|
||||||
|
<span class="turn-error">invalid command</span>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
{#if logTurn.action?.type === "bad_command"}
|
||||||
|
<div class="bad-command-container">
|
||||||
|
<div class="bad-command-text">{logTurn.action.command}</div>
|
||||||
|
<div class="bad-command-error">Parse error: {logTurn.action.error}</div>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
{#if logTurn.stderr.length > 0}
|
||||||
|
<div class="stderr-header">stderr</div>
|
||||||
|
<div class="stderr-text-box">
|
||||||
|
{#each logTurn.stderr as stdErrMsg}
|
||||||
|
<div class="stderr-text">{stdErrMsg}</div>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
|
@ -26,15 +57,62 @@
|
||||||
padding: 15px;
|
padding: 15px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.turn {
|
||||||
|
margin: 16px 4px;
|
||||||
|
}
|
||||||
|
|
||||||
.output-text {
|
.output-text {
|
||||||
color: #ccc;
|
color: #ccc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.turn-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
.turn-header-text {
|
||||||
|
color: #eee;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 600;
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
|
||||||
|
.turn-error {
|
||||||
|
color: red;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bad-command-container {
|
||||||
|
border-left: 1px solid red;
|
||||||
|
margin-left: 4px;
|
||||||
|
padding-left: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bad-command-text {
|
||||||
|
font-family: "Consolas", "Bitstream Vera Sans Mono", "Courier New", Courier, monospace;
|
||||||
|
padding-bottom: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bad-command-error {
|
||||||
|
color: whitesmoke;
|
||||||
|
}
|
||||||
|
|
||||||
.stderr-text {
|
.stderr-text {
|
||||||
font-family: monospace;
|
// font-family: monospace;
|
||||||
|
font-family: "Consolas", "Bitstream Vera Sans Mono", "Courier New", Courier, monospace;
|
||||||
white-space: pre-wrap;
|
white-space: pre-wrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.stderr-header {
|
||||||
|
color: #eee;
|
||||||
|
padding-top: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stderr-text-box {
|
||||||
|
border-left: 1px solid #ccc;
|
||||||
|
margin-left: 4px;
|
||||||
|
padding-left: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
.output-header {
|
.output-header {
|
||||||
color: #eee;
|
color: #eee;
|
||||||
padding-bottom: 20px;
|
padding-bottom: 20px;
|
||||||
|
|
55
web/pw-server/src/lib/log_parser.ts
Normal file
55
web/pw-server/src/lib/log_parser.ts
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
export type PlayerLog = PlayerLogTurn[];
|
||||||
|
|
||||||
|
export type PlayerLogTurn = {
|
||||||
|
action?: PlayerAction;
|
||||||
|
stderr: string[];
|
||||||
|
};
|
||||||
|
|
||||||
|
type PlayerAction = BadCommand;
|
||||||
|
|
||||||
|
type BadCommand = {
|
||||||
|
type: "bad_command";
|
||||||
|
command: string;
|
||||||
|
error: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
function createEmptyLogTurn(): PlayerLogTurn {
|
||||||
|
return {
|
||||||
|
stderr: [],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function parsePlayerLog(playerId: number, logText: string): PlayerLog {
|
||||||
|
const logLines = logText.split("\n").slice(0, -1);
|
||||||
|
|
||||||
|
const playerLog: PlayerLog = [];
|
||||||
|
|
||||||
|
let turn = null;
|
||||||
|
|
||||||
|
logLines.forEach((logLine) => {
|
||||||
|
const logMessage = JSON.parse(logLine);
|
||||||
|
|
||||||
|
if (logMessage["type"] === "gamestate") {
|
||||||
|
if (turn) {
|
||||||
|
playerLog.push(turn);
|
||||||
|
turn = createEmptyLogTurn();
|
||||||
|
}
|
||||||
|
} else if (logMessage["player_id"] === playerId) {
|
||||||
|
if (!turn) {
|
||||||
|
// older match logs don't have an initial game state due to a bug.
|
||||||
|
turn = createEmptyLogTurn();
|
||||||
|
}
|
||||||
|
switch (logMessage["type"]) {
|
||||||
|
case "stderr": {
|
||||||
|
let msg = logMessage["message"];
|
||||||
|
turn.stderr.push(msg);
|
||||||
|
}
|
||||||
|
case "bad_command": {
|
||||||
|
turn.action = logMessage;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return playerLog;
|
||||||
|
}
|
Loading…
Reference in a new issue