Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: copy logs button for easier debugging #200

Merged
merged 7 commits into from
Jul 5, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/broadcast/broadcastManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -247,7 +247,7 @@ export class BroadcastManager {

const newFirstEvent = _.first(this.incomingEvents);
if (!newFirstEvent) {
log.info("[Broadcast] UH I DUNNO SOMEONE COMMENT THIS CORRECTLY");
log.warn("[Broadcast] Missing new first event");
return;
}
const newFirstCursor = newFirstEvent.cursor;
Expand Down
3 changes: 2 additions & 1 deletion src/broadcast/spectateManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,6 @@ export class SpectateManager extends EventEmitter {
return;
}

// console.log(`[Spectator] ${message.utf8Data}`);
let obj;
if (message.utf8Data) {
obj = JSON.parse(message.utf8Data);
Expand All @@ -211,6 +210,7 @@ export class SpectateManager extends EventEmitter {
resolve();
});
if (SLIPPI_WS_SERVER) {
log.info("[Spectate] Connecting to spectate server");
socket.connect(SLIPPI_WS_SERVER, "spectate-protocol", undefined, headers);
}
});
Expand Down Expand Up @@ -263,6 +263,7 @@ export class SpectateManager extends EventEmitter {
const existingBroadcasts = Object.keys(this.broadcastInfo);
if (existingBroadcasts.includes(broadcastId)) {
// We're already watching this broadcast!
log.warn(`[Spectate] We are already watching the selected broadcast`);
return;
}

Expand Down
2 changes: 2 additions & 0 deletions src/common/ipc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,5 @@ export const ipc_checkValidIso = makeEndpoint.main(
);

export const ipc_deleteDesktopAppPath = makeEndpoint.main("deleteDesktopAppPath", <EmptyPayload>_, <SuccessPayload>_);

export const ipc_copyLogsToClipboard = makeEndpoint.main("copyLogsToClipboard", <EmptyPayload>_, <SuccessPayload>_);
26 changes: 24 additions & 2 deletions src/main/listeners.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,21 @@ import "@settings/main";
import "@console/main";

import { settingsManager } from "@settings/settingsManager";
import { ipc_checkValidIso, ipc_deleteDesktopAppPath, ipc_fetchNewsFeed } from "common/ipc";
import { isDevelopment } from "common/constants";
import { ipc_checkValidIso, ipc_copyLogsToClipboard, ipc_deleteDesktopAppPath, ipc_fetchNewsFeed } from "common/ipc";
import { IsoValidity } from "common/types";
import { app, ipcMain, nativeImage } from "electron";
import { app, clipboard, ipcMain, nativeImage } from "electron";
import * as fs from "fs-extra";
import os from "os";
import osName from "os-name";
import path from "path";

import { fetchNewsFeedData } from "./newsFeed";
import { readLastLines } from "./util";
import { verifyIso } from "./verifyIso";

const LINES_TO_READ = 200;

export function setupListeners() {
ipcMain.on("onDragStart", (event, files: string[]) => {
// The Electron.Item type declaration is missing the files attribute
Expand Down Expand Up @@ -63,4 +67,22 @@ export function setupListeners() {

return { success: true };
});

ipc_copyLogsToClipboard.main!.handle(async () => {
let logsFolder = "";
if (isDevelopment) {
logsFolder = path.join(app.getPath("appData"), "Slippi Launcher", "logs");
} else {
logsFolder = path.join(app.getPath("userData"), "logs");
}
const mainLog = path.join(logsFolder, "main.log");
const rendererLog = path.join(logsFolder, "renderer.log");

const mainLogs = await readLastLines(mainLog, LINES_TO_READ);
const rendererLogs = await readLastLines(rendererLog, LINES_TO_READ);

clipboard.writeText(`MAIN START\n---------------\n${mainLogs}\n\nRENDERER START\n---------------\n${rendererLogs}`);

return { success: true };
});
}
54 changes: 54 additions & 0 deletions src/main/util.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import * as fs from "fs-extra";

// Implemenation taken from https://github.com/alexbbt/read-last-lines/blob/11945800b013fe5016c4ea36e49d28c67aa75e7c/src/index.js
export async function readLastLines(
inputFilePath: string,
maxLineCount: number,
encoding: BufferEncoding = "utf-8",
): Promise<string> {
const exists = await fs.pathExists(inputFilePath);
if (!exists) {
throw new Error("file does not exist");
}

// Load file Stats.
const stat = await fs.stat(inputFilePath);

// Open file for reading.
const file = await fs.open(inputFilePath, "r");

const bufferSize = Math.min(16384, stat.size);
const readBuffer = Buffer.alloc(bufferSize);
let readBufferRemaining = 0;
const allBytes = [];
let lineCount = 0;
let fileOffset = stat.size;

while (lineCount < maxLineCount && fileOffset > 0) {
// Read the next chunk of the file
const readSize = Math.min(readBuffer.length, fileOffset);
fileOffset -= readSize;
const readResult = await fs.read(file, readBuffer, 0, readSize, fileOffset);

// If there's still data in our read buffer, then finish processing that
readBufferRemaining = readResult.bytesRead;
while (readBufferRemaining > 0) {
const bufferIndex = readBufferRemaining - 1;
if (readBuffer[bufferIndex] === 0x0a && allBytes.length) {
++lineCount;
if (lineCount >= maxLineCount) {
break;
}
}
allBytes.push(readBuffer[readBufferRemaining - 1]);
--readBufferRemaining;
}
}

await fs.close(file);

// Reverse the array
allBytes.reverse();

return Buffer.from(allBytes).toString(encoding);
}
18 changes: 15 additions & 3 deletions src/renderer/containers/Settings/HelpPage.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,26 @@
/** @jsx jsx */
import { css, jsx } from "@emotion/react";
import styled from "@emotion/styled";
import faqMarkdown from "raw-loader!../../../../FAQ.md";
import React from "react";

import { MarkdownContent } from "@/components/MarkdownContent";

import { SupportBox } from "./SupportBox";

export const HelpPage: React.FC = () => {
return (
<div>
<Outer>
<h1>Help</h1>
<SupportBox
css={css`
margin-top: 10px;
margin-bottom: 40px;
`}
/>
<MarkdownContent
content={faqMarkdown}
css={css`
max-width: 800px;
border-radius: 10px;
background-color: rgba(0, 0, 0, 0.7);
padding: 10px 20px;
Expand All @@ -36,6 +44,10 @@ export const HelpPage: React.FC = () => {
}
`}
/>
</div>
</Outer>
);
};

const Outer = styled.div`
max-width: 800px;
`;
114 changes: 114 additions & 0 deletions src/renderer/containers/Settings/SupportBox.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
/** @jsx jsx */
import { css, jsx } from "@emotion/react";
import styled from "@emotion/styled";
import CircularProgress from "@material-ui/core/CircularProgress";
import FileCopyIcon from "@material-ui/icons/FileCopy";
import LiveHelpIcon from "@material-ui/icons/LiveHelp";
import { colors } from "common/colors";
import { socials } from "common/constants";
import { ipc_copyLogsToClipboard } from "common/ipc";
import { shell } from "electron";
import log from "electron-log";
import React from "react";
import { useToasts } from "react-toast-notifications";

import { ExternalLink as A } from "@/components/ExternalLink";
import { Button } from "@/components/FormInputs";
import { ReactComponent as DiscordIcon } from "@/styles/images/discord.svg";

export const SupportBox: React.FC<{ className?: string }> = ({ className }) => {
const [isCopying, setCopying] = React.useState(false);
const [copied, setCopied] = React.useState(false);
const { addToast } = useToasts();

const handleError = (err: any) => {
log.error("Error copying logs", err);
addToast("Error copying logs");
};

const onCopy = async () => {
// Set the clipboard text
setCopying(true);
try {
const res = await ipc_copyLogsToClipboard.renderer!.trigger({});
if (!res.result) {
handleError(res.errors);
} else {
// Set copied indication
setCopied(true);
setTimeout(() => setCopied(false), 2000);
}
} catch (err) {
handleError(err);
} finally {
setCopying(false);
}
};

return (
<Outer className={className}>
<h2
css={css`
display: flex;
align-items: center;
margin: 0;
margin-bottom: 10px;
`}
>
<LiveHelpIcon style={{ marginRight: 8 }} />
Need help?
</h2>
<div>
The best way to get support is to first{" "}
<A title={socials.discordUrl} href={socials.discordUrl}>
join the Slippi Discord
</A>
, then read the information in the <b>#support-portal</b> channel before posting your issue in the appropriate
support channel for your operating system. Our support volunteers will try their best to assist you with your
problem.
</div>

<div
css={css`
margin-top: 5px;
& > * {
margin-top: 10px;
margin-right: 10px;
}
`}
>
<Button
startIcon={<DiscordIcon fill={colors.purpleLighter} style={{ height: 18, width: 18 }} />}
onClick={() => void shell.openExternal(socials.discordUrl)}
>
Join the Discord
</Button>

<Button startIcon={<FileCopyIcon />} disabled={isCopying || copied} onClick={onCopy}>
{copied ? "Copied!" : "Copy logs"}
{isCopying && (
<CircularProgress
css={css`
margin-left: 10px;
`}
size={16}
thickness={6}
color="inherit"
/>
)}
</Button>
</div>
</Outer>
);
};

const Outer = styled.div`
background-color: ${colors.purpleLight};
color: ${colors.purpleDarker};
border-radius: 10px;
padding: 15px;

a {
text-decoration: underline;
}
`;