Skip to content
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
15 changes: 14 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,23 @@ if (isDev) {

## Devtron API

### `await devtron.install()`
### `await devtron.install(options)`

Installs Devtron into the Electron app. Refer to [Configuring an Electron App to use Devtron](#configuring-an-electron-app-to-use-devtron) for installation instructions.

#### `Options`

| Option | Type | Default | Description |
| ---------- | -------------------------------------------------- | --------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `logLevel` | `'debug' \| 'info' \| 'warn' \| 'error' \| 'none'` | `'debug'` | Sets the minimum log level for the logger. Messages below this level are ignored. <br><br> **Levels:** <br>• `debug` — logs: debug, info, warn, error <br>• `info` — logs: info, warn, error <br>• `warn` — logs: warn, error <br>• `error` — logs: error only <br>• `none` — disables all logging |

Examples:

```js
// Only 'warn' and 'error' logs will appear in the terminal
await devtron.install({ logLevel: 'warn' });
```
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed with @erickzhao about a logLevel option. Then these two examples could be

devtron.install({ logLevel: [] })
devtron.install({ logLevel: ['info', 'warn'] })

I'm partial to the idea of the quiet option as a shorthand though. Open to either keeping it or removing it.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In my head it'd look more like a sliding scale:

devtron.install({ logLevel: 'quiet' }) // or `'none'`
devtron.install({ logLevel: 'error' }) // only shows `error`
devtron.install({ logLevel: 'warn' }) // shows `warn`, or `error`, but not `debug` or `info`
devtron.install({ logLevel: 'info' }) // shows `info`, `warn`, or `error`, but not `debug`

since it'd be unlikely that you would want to specifically ignore non-contiguous log levels (e.g. ignoring error and debug but not info and warn).


### `await devtron.getEvents()`

Returns a **promise** that resolves to the array of IPC events recorded by the Devtron service worker since installation.
Expand Down
23 changes: 14 additions & 9 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@ import { createRequire } from 'node:module';
import type {
Channel,
Direction,
InstallOptions,
IpcEventData,
IpcEventDataIndexed,
ServiceWorkerDetails,
} from './types/shared';
import { excludedIpcChannels } from './common/constants';
import { logger } from './utils/Logger';

interface TrackIpcEventOptions {
direction: Direction;
Expand Down Expand Up @@ -65,7 +67,7 @@ function trackIpcEvent({
if (excludedIpcChannels.includes(channel)) return;

if (!devtronSW) {
console.error('The service-worker for Devtron is not registered yet. Cannot track IPC event.');
logger.warn('The service-worker for Devtron is not registered yet. Cannot track IPC event.');
return;
}

Expand Down Expand Up @@ -208,7 +210,7 @@ async function startServiceWorker(ses: Electron.Session, extension: Electron.Ext
registerIpcListeners(ses, sw);
registerServiceWorkerSendListener(ses, sw);
} catch (error) {
console.warn(`Failed to start Devtron service-worker (${error}), trying again...`);
logger.warn(`Failed to start Devtron service-worker (${error}), trying again...`);
/**
* This is a workaround for the issue where the Devtron service-worker fails to start
* when the Electron app is launched for the first time, or when the service worker
Expand All @@ -226,12 +228,12 @@ async function startServiceWorker(ses: Electron.Session, extension: Electron.Ext
registerIpcListeners(ses, sw);
registerServiceWorkerSendListener(ses, sw);
ses.serviceWorkers.removeListener('registration-completed', handleDetails);
console.log(`Devtron service-worker started successfully`);
logger.info(`Devtron service-worker started successfully`);
}
};
ses.serviceWorkers.on('registration-completed', handleDetails);
} catch (error) {
console.error('Failed to start Devtron service-worker:', error);
logger.error('Failed to start Devtron service-worker:', error);
}
}
}
Expand Down Expand Up @@ -373,10 +375,13 @@ function patchIpcMain() {
};
}

async function install() {
async function install(options: InstallOptions = {}) {
if (isInstalled) return;
isInstalled = true;

// set log level
if (options.logLevel) logger.setLogLevel(options.logLevel);

patchIpcMain();

const installToSession = async (ses: Electron.Session) => {
Expand Down Expand Up @@ -410,9 +415,9 @@ async function install() {
const extensionPath = path.resolve(serviceWorkerPreloadPath, '..', '..', 'extension');
devtron = await ses.extensions.loadExtension(extensionPath, { allowFileAccess: true });
await startServiceWorker(ses, devtron);
console.log('Devtron loaded successfully');
logger.info('Devtron loaded successfully');
} catch (error) {
console.error('Failed to load Devtron:', error);
logger.error('Failed to load Devtron:', error);
}
};

Expand All @@ -430,12 +435,12 @@ async function install() {
*/
async function getEvents(): Promise<IpcEventDataIndexed[]> {
if (!isInstalled) {
console.warn('You are trying to get IPC events before Devtron is installed.');
logger.warn('You are trying to get IPC events before Devtron is installed.');
return [];
}

if (!devtronSW) {
console.warn('Devtron service-worker is not registered yet. Cannot get IPC events.');
logger.warn('Devtron service-worker is not registered yet. Cannot get IPC events.');
return [];
}

Expand Down
27 changes: 27 additions & 0 deletions src/types/shared.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,33 @@ export interface IpcEventData {
uuid?: UUID; // UUID to match requests and responses (for `invoke` and `sendSync` methods on `ipcRenderer`)
}

export type LogLevelString = 'debug' | 'info' | 'warn' | 'error' | 'none';

export enum LogLevel {
debug,
info,
warn,
error,
none,
}

export interface InstallOptions {
/**
* Sets the minimum log level for the logger.
* All messages below the specified level will be ignored.
*
* Available levels:
* - 'debug' — logs: debug, info, warn, error
* - 'info' — logs: info, warn, error
* - 'warn' — logs: warn, error
* - 'error' — logs: error only
* - 'none' — disables all logging
*
* @default 'debug'
*/
logLevel?: LogLevelString;
}

/* ------------------------------------------------------ */

/* ---------------------- EXTENSION --------------------- */
Expand Down
52 changes: 52 additions & 0 deletions src/utils/Logger.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import type { LogLevelString } from '../types/shared';
import { LogLevel } from '../types/shared';

class Logger {
private currentLogLevel = LogLevel.debug;

setLogLevel(level: LogLevelString) {
if (LogLevel[level] === undefined) {
console.error(`Invalid log level: ${level}`);
return;
}

if (LogLevel[level] === this.currentLogLevel) return; // no change

this.currentLogLevel = LogLevel[level];
}

private log(level: LogLevel, ...args: any[]) {
if (this.currentLogLevel === LogLevel.none) return;
if (level < this.currentLogLevel) return;

switch (level) {
case LogLevel.debug:
console.debug(...args);
break;
case LogLevel.info:
console.log(...args);
break;
case LogLevel.warn:
console.warn(...args);
break;
case LogLevel.error:
console.error(...args);
break;
}
}

debug(...args: any[]) {
this.log(LogLevel.debug, ...args);
}
info(...args: any[]) {
this.log(LogLevel.info, ...args);
}
warn(...args: any[]) {
this.log(LogLevel.warn, ...args);
}
error(...args: any[]) {
this.log(LogLevel.error, ...args);
}
}

export const logger = new Logger();
Loading