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

Adds custom logger #45

Merged
merged 14 commits into from Jul 22, 2021
5 changes: 5 additions & 0 deletions .changeset/witty-ways-repeat.md
@@ -0,0 +1,5 @@
---
'@honestfoodcompany/pubsub': minor
---

Added option to add custom logger - PUB-44
22 changes: 22 additions & 0 deletions README.md
Expand Up @@ -40,6 +40,7 @@ This package contains a lightweight framework and subscription server for [Googl
- [Typescript example](#typescript-example-1)
- [Javascript Example](#javascript-example-1)
- [Graceful Shutdown](#graceful-shutdown)
- [Passing a custom Logger](#passing-a-custom-logger)
- [Connecting to a database](#connecting-to-a-database)
- [Enabling Synchronous Driver](#enabling-synchronous-driver)
- [Enabling gRPC C++ bindings](#enabling-grpc-c-bindings)
Expand Down Expand Up @@ -453,6 +454,13 @@ Extend and customize the behavior of the subscription server in the subscription
import * as PubSub from '@honestfoodcompany/pubsub';
import { SubscriberOptions } from '@honestfoodcompany/pubsub';

/**
* This function call is optional, you can pass an instance of Pino, Winston logger
* By default it uses default console.* for logging
* The logger you pass must support .error, .info and .warn methods for it to work
*/
PubSub.setLogger(console);

export default class SubscriptionService extends PubSub.SubscriptionService {
static subscribers = [
/**
Expand Down Expand Up @@ -511,6 +519,20 @@ SubscriptionService.init = () => {
exports.default = SubscriptionService;
```

### Passing a custom Logger

In the main SubscriptionService before defining subscription class you can update the logger that is being used by the package for logging. It's an optional definition and by default it uses `console.*` for logging `.info`, `.warn` and `.error` these 3 function keys are a must have for the logger you pass.

```typescript
import { setLogger } from '@honestfoodcompany/pubsub';
setLogger(console);
```

```javascript
const { setLogger } = require('@honestfoodcompany/pubsub');
setLogger(console);
```

### Connecting to a database

It is recommended to initialize a database connection in the `subscription.service` file in your `PUBSUB_ROOT_DIR`. Insert your database connection logic in the `init` method.
Expand Down
27 changes: 27 additions & 0 deletions __tests__/logger.test.ts
@@ -0,0 +1,27 @@
import { Logger, setLogger } from '../src/service/logger';

describe('Tests Logger singleton', () => {
beforeEach(() => {
jest.clearAllMocks();
});
it('Should return default Logger instance if not provided anything', () => {
expect(Logger.Instance).toBeTruthy();
});
it("Should return what's passed in", () => {
rohit-gohri marked this conversation as resolved.
Show resolved Hide resolved
const customLogger = { info: jest.fn() } as any;
Logger.Instance = customLogger;
expect(Logger.Instance).toEqual(customLogger);
});
it('Should return latest logger passed in', () => {
const customLogger = { info: jest.fn() } as any;
const customLogger1 = { info: jest.fn() } as any;
Logger.Instance = customLogger;
Logger.Instance = customLogger1;
expect(Logger.Instance).toEqual(customLogger1);
});
it('Should overwrite logger instance from Subscription Service', () => {
const customLogger = { info: jest.fn(), debug: jest.fn() } as any;
setLogger(customLogger);
expect(Logger.Instance).toStrictEqual(customLogger);
});
});
12 changes: 6 additions & 6 deletions src/cli/commands/list.ts
@@ -1,19 +1,19 @@
import chalk from 'chalk';
import { cli } from 'cli-ux';
import wrapAnsi = require('wrap-ansi');
import { Logger } from '../../service/logger';
import { PubSubService } from '../../index';
import { SubscriberTuple } from '../../subscriber';

export default {
command: 'list',
desc: 'Lists all subscriptions',
handler: async (): Promise<void> => {
console.log(
chalk.white.bgBlue.bold('\n Google Pub/Sub Subscriptions'),
'\n',
Logger.Instance.info(
chalk.white.bgBlue.bold('\n Google Pub/Sub Subscriptions \n'),
);
if (PubSubService.getInstance().getSubscribers().length == 0) {
console.log(chalk.white.bold('\n No subscriptions found'));
Logger.Instance.warn(chalk.white.bold('\n No subscriptions found'));
} else {
cli.table(
PubSubService.getInstance().getSubscribers(),
Expand All @@ -30,10 +30,10 @@ export default {
},
},
{
printLine: console.log,
printLine: Logger.Instance.info,
},
);
console.log('\n');
Logger.Instance.info('\n');
process.exit(0);
}
},
Expand Down
3 changes: 2 additions & 1 deletion src/cli/commands/start.ts
@@ -1,11 +1,12 @@
import chalk from 'chalk';
import { Logger } from '../../service/logger';
import { PubSubService } from '../../index';

export default {
command: 'start',
desc: 'Will listen to Google Pub/Sub topics and initialize their event handlers',
handler: async (_argv: unknown): Promise<void> => {
console.log(
Logger.Instance.info(
chalk.bold.blue('Starting Google Pub/Sub Subscriptions Server'),
);
return PubSubService.getInstance().startSubscriptions();
Expand Down
9 changes: 5 additions & 4 deletions src/cli/commands/subscriptions.ts
@@ -1,17 +1,18 @@
require('dotenv').config({ path: require('find-config')('.env') });
import chalk from 'chalk';
import { cli } from 'cli-ux';
import { Logger } from '../../service/logger';
import { PubSubService } from '../../index';

export default {
command: 'subscriptions',
desc: 'Lists all subscriptions',
handler: async (): Promise<void> => {
console.log(chalk.white.bgBlue.bold('\n All Subscriptions'), '\n');
Logger.Instance.info(chalk.white.bgBlue.bold('\n All Subscriptions'), '\n');
const subscriptions =
await PubSubService.getInstance().getAllSubscriptions();
if (subscriptions.length == 0) {
console.log(chalk.white.bold('\n No subscriptions found'));
Logger.Instance.warn(chalk.white.bold('\n No subscriptions found'));
} else {
cli.table(
subscriptions,
Expand All @@ -24,10 +25,10 @@ export default {
},
},
{
printLine: console.log,
printLine: Logger.Instance.info,
},
);
console.log('\n');
Logger.Instance.info('\n');
}
},
};
43 changes: 25 additions & 18 deletions src/client/googlePubSub.ts
Expand Up @@ -20,6 +20,7 @@ import {
import { SubscriberTuple } from '../subscriber';
import Message from '../message';
import { GooglePubSubProject } from '../interface/GooglePubSubProject';
import { Logger } from '../service/logger';

export interface Project {
client: GooglePubSub;
Expand Down Expand Up @@ -130,7 +131,7 @@ export default class GooglePubSubAdapter implements PubSubClientV2 {
}

private log(message: string): void {
console.log(chalk.green.bold(message));
Logger.Instance.info(chalk.green.bold(message));
}

private getSubscriberOptions(
Expand Down Expand Up @@ -169,7 +170,8 @@ export default class GooglePubSubAdapter implements PubSubClientV2 {
});

if (await this.subscriptionExists(metadata.subscriptionName, client)) {
console.log(
Logger.Instance.info(
{ metadata },
chalk.gray(` ✔️ ${metadata.subscriptionName} already exists.`),
);
await this.updateMetaData(subscriber);
Expand Down Expand Up @@ -209,8 +211,9 @@ export default class GooglePubSubAdapter implements PubSubClientV2 {
ttl: null,
},
});
} catch (e) {
console.error(
} catch (err) {
Logger.Instance.error(
{ metadata, err },
rohit-gohri marked this conversation as resolved.
Show resolved Hide resolved
`Error while creating default deadLetter subscription for ${metadata.subscriptionName}`,
);
}
Expand Down Expand Up @@ -238,13 +241,14 @@ export default class GooglePubSubAdapter implements PubSubClientV2 {
metadata.subscriptionName,
await this.getMergedSubscriptionOptions(subscriber),
);
console.log(
Logger.Instance.info(
{ metadata },
chalk.gray(` ✔️ ${metadata.subscriptionName} created.`),
);
} catch (e) {
console.error(
} catch (err) {
Logger.Instance.error(
{ metadata, err },
` ❌ There was an error creating "${metadata.subscriptionName}" subscription.`,
e,
);
}
}
Expand Down Expand Up @@ -286,7 +290,8 @@ export default class GooglePubSubAdapter implements PubSubClientV2 {
.topic(deadLetterTopic)
.getSubscriptions();
if (subscriptions.length === 0) {
console.warn(
Logger.Instance.warn(
{ metadata },
`Please set createDefaultSubscription: true in deadLetterPolicy to create default subscriber for dead letter topic of ${metadata.subscriptionName}. Ignore if already added subscription for it.`,
);
}
Expand All @@ -302,6 +307,7 @@ export default class GooglePubSubAdapter implements PubSubClientV2 {
await this.bindPolicyToDeadLetterTopic(
options.deadLetterPolicy.deadLetterTopic,
options,
metadata,
);
if (options?.deadLetterPolicy?.createDefaultSubscription) {
await this.createDeadLetterDefaultSubscriber(subscriber);
Expand All @@ -325,8 +331,8 @@ export default class GooglePubSubAdapter implements PubSubClientV2 {
const projectInfo = await project.get();
// project.info return [_, projectInfoIncludingProjectNumber]
return (projectInfo as any)[1]?.projectNumber;
} catch (e) {
console.error('Error while getting project number', e);
} catch (err) {
Logger.Instance.error({ err }, 'Error while getting project number');
return '';
}
}
Expand Down Expand Up @@ -359,18 +365,19 @@ export default class GooglePubSubAdapter implements PubSubClientV2 {
await pubSubTopic
.subscription(subscriptionName)
.iam.setPolicy(myPolicy);
} catch (e) {
console.error(
} catch (err) {
Logger.Instance.error(
{ metadata, err },
` ❌ Error while binding policy for "${metadata.subscriptionName}" subscription.`,
e,
);
}
}
}

private async bindPolicyToDeadLetterTopic(
deadLetterTopicName: string,
options?: { project?: GooglePubSubProject },
options: { project?: GooglePubSubProject },
metadata: SubscriberMetadata,
): Promise<void> {
const projectNumber = await this.getProjectNumber();

Expand All @@ -389,10 +396,10 @@ export default class GooglePubSubAdapter implements PubSubClientV2 {
],
};
await pubSubTopic.iam.setPolicy(myPolicy);
} catch (e) {
console.error(
} catch (err) {
Logger.Instance.error(
{ metadata, err },
` ❌ Error while binding policy for "${deadLetterTopicName}" DLQ topic.`,
e,
);
}
}
Expand Down
2 changes: 2 additions & 0 deletions src/index.ts
Expand Up @@ -7,6 +7,7 @@ import Subscriber, {
} from './subscriber';
import SubscriptionService from './service/subscription';
import PubSubService from './service/pubsub';
import { setLogger } from './service/logger';
import Message from './message';
import * as Interfaces from './interface';

Expand All @@ -21,4 +22,5 @@ export {
SubscriberObject,
SubscriberV2,
SubscriberOptions,
setLogger,
};
21 changes: 21 additions & 0 deletions src/service/logger.ts
@@ -0,0 +1,21 @@
export interface LoggerOptions {
info: (...args: any[]) => void;
debug?: (...args: any[]) => void;
error: (...args: any[]) => void;
warn: (...args: any[]) => void;
}
export class Logger {
private static logger: LoggerOptions;
public static set Instance(logger: LoggerOptions) {
Logger.logger = logger;
}
public static get Instance(): LoggerOptions {
if (!Logger.logger) {
Logger.logger = console;
}
return Logger.logger;
}
}
export const setLogger = (logger: LoggerOptions): void => {
Logger.Instance = logger;
};
6 changes: 4 additions & 2 deletions src/service/pubsub.ts
Expand Up @@ -5,6 +5,7 @@ import { AllSubscriptions, PubSubClientV2 } from '../interface/pubSubClient';
import GooglePubSubAdapter from '../client/googlePubSub';
import { PublishOptions } from '../interface/publishOptions';
import SubscriptionService from './subscription';
import { Logger } from './logger';

export default class PubSubService {
protected static client: PubSubClientV2;
Expand Down Expand Up @@ -102,7 +103,8 @@ export default class PubSubService {
await this.subscribe(subscription);
} catch (err) {
const [, metadata] = subscription;
console.error(
Logger.Instance.error(
{ metadata, err },
` ❌ Error while initializing "${metadata.subscriptionName}" subscription.`,
);
const error: Error & {
Expand All @@ -116,7 +118,7 @@ export default class PubSubService {
}

PubSubService.status = 'ready';
console.log(` ✅ All subscriptions started successfully.`);
Logger.Instance.info(` ✅ All subscriptions started successfully.`);
}

/**
Expand Down
3 changes: 2 additions & 1 deletion src/service/subscription.ts
Expand Up @@ -9,6 +9,7 @@ import {
} from '../subscriber';
import SubscriberLoader from './subscriberLoader';
import { ResourceResolver } from './resourceResolver';
import { Logger } from './logger';

export default class SubscriptionService {
public static subscribers: (
Expand All @@ -31,7 +32,7 @@ export default class SubscriptionService {
!object.hasOwnProperty(property) ||
(object.hasOwnProperty(property) && object[property] == '')
) {
console.warn(
Logger.Instance.warn(
`@honestfoodcompany/pubsub module requires ${property} to be defined in your .env`,
);
}
Expand Down