Skip to content

Commit

Permalink
Merge pull request #116 from colin969/log_watcher
Browse files Browse the repository at this point in the history
feat: Logs Watcher
  • Loading branch information
TBubba committed Jun 8, 2020
2 parents 72d763c + 609abea commit 4e95d1c
Show file tree
Hide file tree
Showing 9 changed files with 71 additions and 13 deletions.
1 change: 1 addition & 0 deletions lang/en.json
Expand Up @@ -78,6 +78,7 @@
"filters": "Filters",
"copyText": "Copy Text",
"clearLog": "Clear Log",
"copy404Urls": "Copy 404 URLs",
"uploadLog": "Upload Log",
"copiedToClipboard": "Copied to Clipboard"
},
Expand Down
2 changes: 2 additions & 0 deletions package.json
Expand Up @@ -52,6 +52,7 @@
"redux-devtools-extension": "2.13.8",
"reflect-metadata": "0.1.10",
"sqlite3": "4.2.0",
"tail": "2.0.3",
"typeorm": "0.2.22",
"typesafe-actions": "4.4.2",
"uuid": "3.3.2",
Expand All @@ -74,6 +75,7 @@
"@types/react-redux": "7.1.7",
"@types/react-router-dom": "4.3.4",
"@types/react-virtualized": "9.21.9",
"@types/tail": "2.0.0",
"@types/uuid": "3.4.5",
"@types/uuid-validate": "0.0.1",
"@types/which": "1.3.2",
Expand Down
11 changes: 3 additions & 8 deletions src/back/ServicesFile.ts
Expand Up @@ -4,6 +4,7 @@ import { parseVarStr, readJsonFile } from '@shared/Util';
import { Coerce } from '@shared/utils/Coerce';
import { IObjectParserProp, ObjectParser } from '@shared/utils/ObjectParser';
import * as path from 'path';
import { ServiceFileData } from './types';

const { str } = Coerce;

Expand Down Expand Up @@ -31,6 +32,7 @@ export namespace ServicesFile {
server: [],
start: [],
stop: [],
watch: [],
};
const parser = new ObjectParser({
input: data,
Expand All @@ -39,6 +41,7 @@ export namespace ServicesFile {
parser.prop('server').array(item => parsed.server.push(parseNamedBackProcessInfo(item, config)));
parser.prop('start').array(item => parsed.start.push(parseBackProcessInfo(item, config)));
parser.prop('stop').array(item => parsed.stop.push(parseBackProcessInfo(item, config)));
parser.prop('watch').arrayRaw(item => parsed.watch.push(parseVarStr(str(item), config)));
return parsed;
}

Expand Down Expand Up @@ -69,11 +72,3 @@ export namespace ServicesFile {
return parsed;
}
}

export type ServiceFileData = {
server: INamedBackProcessInfo[];
/** Processes to run before the launcher starts. */
start: IBackProcessInfo[];
/** Processes to run when the launcher closes. */
stop: IBackProcessInfo[];
}
19 changes: 18 additions & 1 deletion src/back/index.ts
Expand Up @@ -22,6 +22,7 @@ import * as path from 'path';
import 'reflect-metadata';
// Required for the DB Models to function
import 'sqlite3';
import { Tail } from 'tail';
import { ConnectionOptions, createConnection } from 'typeorm';
import { ConfigFile } from './ConfigFile';
import { CONFIG_FILENAME, PREFERENCES_FILENAME, SERVICES_SOURCE } from './constants';
Expand All @@ -32,7 +33,7 @@ import { SocketServer } from './SocketServer';
import { BackState, ImageDownloadItem } from './types';
import { EventQueue } from './util/EventQueue';
import { FolderWatcher } from './util/FolderWatcher';
import { createContainer, exit, log, runService } from './util/misc';
import { createContainer, exit, log, newLogEntry, runService } from './util/misc';

// Make sure the process.send function is available
type Required<T> = T extends undefined ? never : T;
Expand Down Expand Up @@ -145,6 +146,22 @@ async function onProcessMessage(message: any, sendHandle: any): Promise<void> {
const chosenServer = state.serviceInfo.server.find(i => i.name === state.config.server);
state.services.server = runService(state, 'server', 'Server', chosenServer || state.serviceInfo.server[0]);
}
// Start file watchers
for (let i = 0; i < state.serviceInfo.watch.length; i++) {
const filePath = state.serviceInfo.watch[i];
try {
const tail = new Tail(filePath, { follow: true });
tail.on('line', (data) => {
log(state, newLogEntry('Log Watcher', data));
});
tail.on('error', (error) => {
log(state, newLogEntry('Log Watcher', `Error while watching file "${filePath}" - ${error}`));
});
log(state, newLogEntry('Log Watcher', `Watching file "${filePath}"`));
} catch (error) {
log(state, newLogEntry('Log Watcher', `Failed to watch file "${filePath}" - ${error}`));
}
}
}

// Init language
Expand Down
2 changes: 2 additions & 0 deletions src/back/types.ts
Expand Up @@ -103,6 +103,8 @@ export type ServiceFileData = {
start: IBackProcessInfo[];
/** Processes to run when the launcher closes. */
stop: IBackProcessInfo[];
/** Files to watch and run continous logging on */
watch: string[];
};

export type ThemeListItem = Theme & {
Expand Down
7 changes: 7 additions & 0 deletions src/back/util/misc.ts
Expand Up @@ -319,3 +319,10 @@ export async function waitForServiceDeath(service: ManagedChildProcess) : Promis
});
}
}

export function newLogEntry(source: string, content: string): ILogPreEntry {
return {
source: source,
content: content
};
}
31 changes: 30 additions & 1 deletion src/renderer/components/pages/LogsPage.tsx
Expand Up @@ -16,13 +16,15 @@ type OwnProps = {};

export type LogsPageProps = OwnProps & WithPreferencesProps;

const urlRegex = /((([A-Za-z]{3,9}:(?:\/\/)?)(?:[-;:&=+$,\w]+@)?[A-Za-z0-9.-]+|(?:www\.|[-;:&=+$,\w]+@)[A-Za-z0-9.-]+)((?:\/[+~%/.\w\-_]*)?\??(?:[-+=&;%@.\w_]*)#?(?:[.!/\\\w]*))?)/;
const labels = [
'Background Services',
'Game Launcher',
'Language',
'Redirector',
'Router',
'Server',
'Curation',
'Log Watcher',
];

export type LogsPageState = {
Expand Down Expand Up @@ -117,6 +119,16 @@ export class LogsPage extends React.Component<LogsPageProps, LogsPageState> {
className='simple-button simple-center__vertical-inner' />
</div>
</div>
{/* Copy 404 URLs Button */}
<div className='log-page__bar__wrap'>
<div className='simple-center'>
<input
type='button'
value={strings.copy404Urls}
onClick={this.onCopy404Click}
className='simple-button simple-center__vertical-inner' />
</div>
</div>
{/* Upload Logs Button */}
<div className='log-page__bar__wrap'>
<div className='simple-center'>
Expand Down Expand Up @@ -154,6 +166,23 @@ export class LogsPage extends React.Component<LogsPageProps, LogsPageState> {
this.forceUpdate();
}

onCopy404Click = (): void => {
// Store found URLs
const urls: string[] = [];
for (const entry of window.Shared.log.entries) {
// All 404 entries start with 404
if (entry && entry.content.startsWith('404')) {
// Extract URL with regex
const match = urlRegex.exec(entry.content);
if (match && match.length > 0) {
urls.push(match[1]);
}
}
}
// Copy with each URL on a new line
clipboard.writeText(urls.join('\n'));
}

onUploadClick = async (): Promise<void> => {
this.setState({ uploading: true });
const strings = this.context;
Expand Down
1 change: 1 addition & 0 deletions src/shared/lang.ts
Expand Up @@ -85,6 +85,7 @@ const langTemplate = {
'filters',
'copyText',
'clearLog',
'copy404Urls',
'uploadLog',
'copiedToClipboard',
] as const,
Expand Down
10 changes: 7 additions & 3 deletions static/window/styles/fancy.css
Expand Up @@ -89,8 +89,9 @@
--layout__log-source-game-launcher: #e67e22;
--layout__log-source-language: #b157ec;
--layout__log-source-redirector: #00ffff;
--layout__log-source-router: #00ff00;
--layout__log-source-server: #00ff00;
--layout__log-source-curation: #efff00;
--layout__log-source-log-watcher: #cf0000;
/* Credits */
--layout__credits-tooltip-border: #000000;
--layout__credits-tooltip-background: #000000DD;
Expand Down Expand Up @@ -266,12 +267,15 @@ body {
.log__source--redirector {
color: var(--layout__log-source-redirector);
}
.log__source--router {
color: var(--layout__log-source-router);
.log__source--server {
color: var(--layout__log-source-server);
}
.log__source--curation {
color: var(--layout__log-source-curation);
}
.log__source--log-watcher {
color: var(--layout__log-source-log-watcher);
}


/* ------ Image Preview ------ */
Expand Down

0 comments on commit 4e95d1c

Please sign in to comment.