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

Making logging optional #54

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
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
74 changes: 72 additions & 2 deletions README.MD
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ import { LogLevel } from '@sentry/types';
dsn: 'sentry_io_dsn',
debug: true | false,
environment: 'dev' | 'production' | 'some_environment',
release: 'some_release', | null, // must create a release in sentry.io dashboard
release: 'some_release' | null, // must create a release in sentry.io dashboard
logLevel: LogLevel.Debug //based on sentry.io loglevel //
}),
],
Expand All @@ -83,7 +83,7 @@ import { ConfigService } from '@ntegral/nestjs-config';
dsn: cfg.get('SENTRY_DSN'),
debug: true | false,
environment: 'dev' | 'production' | 'some_environment',
release: 'some_release', | null, // must create a release in sentry.io dashboard
release: 'some_release' | null, // must create a release in sentry.io dashboard
logLevel: LogLevel.Debug //based on sentry.io loglevel //
}),
inject: [ConfigService],
Expand Down Expand Up @@ -111,6 +111,76 @@ export class AppService {
}
```

## Logging

You can provide a custom logger which implements `NestJS`'s `LoggerService` interface:

```typescript
import { Module } from '@nestjs-common';
import { SentryModule } from '@ntegral/nestjs-sentry';
import { LogLevel } from '@sentry/types';

@Module({
imports: [
SentryModule.forRoot({
dsn: 'sentry_io_dsn',
debug: true | false,
environment: 'dev' | 'production' | 'some_environment',
release: 'some_release' | null, // must create a release in sentry.io dashboard
logLevel: LogLevel.Debug, //based on sentry.io loglevel //
logger: new MyLogger(), // implements LoggerService
}),
],
})
export class AppModule {}
```

Or completely disable logging:

```typescript
import { Module } from '@nestjs-common';
import { SentryModule } from '@ntegral/nestjs-sentry';
import { LogLevel } from '@sentry/types';

@Module({
imports: [
SentryModule.forRoot({
dsn: 'sentry_io_dsn',
debug: true | false,
environment: 'dev' | 'production' | 'some_environment',
release: 'some_release' | null, // must create a release in sentry.io dashboard
logLevel: LogLevel.Debug, //based on sentry.io loglevel //
logger: null
}),
],
})
export class AppModule {}
```

Or you can provide default logger (`ConsoleLogger`) options:

```typescript
import { Module } from '@nestjs-common';
import { SentryModule } from '@ntegral/nestjs-sentry';
import { LogLevel } from '@sentry/types';

@Module({
imports: [
SentryModule.forRoot({
dsn: 'sentry_io_dsn',
debug: true | false,
environment: 'dev' | 'production' | 'some_environment',
release: 'some_release' | null, // must create a release in sentry.io dashboard
logLevel: LogLevel.Debug, //based on sentry.io loglevel //
loggerOptions: {
timestamp: true,
},
}),
],
})
export class AppModule {}
```

## Interceptors

BREAKING CHANGES: The SentryInterceptor can be used in projects that don't require GraphQL. If you will be needed GraphQL support, use the GraphqlInterceptor. Using the GraphqlInterceptor will create a dependancy for the @nestjs/graphql package.
Expand Down
76 changes: 75 additions & 1 deletion lib/__tests__/sentry.module.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,36 @@ import { SentryModuleOptions, SentryOptionsFactory } from '../sentry.interfaces'
import { LogLevel } from '@sentry/types';
import { SentryService } from '../sentry.service';
import { SENTRY_TOKEN } from '../sentry.constants';
import { ConsoleLogger } from '@nestjs/common';

class CustomLogger extends ConsoleLogger {}

describe('SentryModule', () => {
let logger = new CustomLogger('SentryModuleSpec');

let config: SentryModuleOptions = {
dsn: 'https://[email protected]/25956308132020',
debug: true,
environment: 'development',
logLevel: LogLevel.Debug,
}

let configWithCustomLogger: SentryModuleOptions = {
dsn: 'https://[email protected]/25956308132020',
debug: true,
environment: 'development',
logLevel: LogLevel.Debug,
logger,
}

let configWithNoLogger: SentryModuleOptions = {
dsn: 'https://[email protected]/25956308132020',
debug: true,
environment: 'development',
logLevel: LogLevel.Debug,
logger: null,
}

class TestService implements SentryOptionsFactory {
createSentryModuleOptions(): SentryModuleOptions {
return config;
Expand All @@ -27,9 +48,31 @@ describe('SentryModule', () => {
}).compile();

const sentry = mod.get<SentryService>(SENTRY_TOKEN);
console.log('sentry', sentry);
expect(sentry).toBeDefined();
expect(sentry).toBeInstanceOf(SentryService);
expect(sentry.opts?.logger).toBeInstanceOf(ConsoleLogger);
});

it('should provide the sentry client with custom logger', async() => {
const mod = await Test.createTestingModule({
imports: [SentryModule.forRoot(configWithCustomLogger)],
}).compile();

const sentry = mod.get<SentryService>(SENTRY_TOKEN);
expect(sentry).toBeDefined();
expect(sentry).toBeInstanceOf(SentryService);
expect(sentry.opts?.logger).toBeInstanceOf(CustomLogger);
});

it('should provide the sentry client with no logger', async() => {
const mod = await Test.createTestingModule({
imports: [SentryModule.forRoot(configWithNoLogger)],
}).compile();

const sentry = mod.get<SentryService>(SENTRY_TOKEN);
expect(sentry).toBeDefined();
expect(sentry).toBeInstanceOf(SentryService);
expect(sentry.opts?.logger).toBeNull();
});
});

Expand All @@ -47,6 +90,37 @@ describe('SentryModule', () => {
const sentry = mod.get<SentryService>(SENTRY_TOKEN);
expect(sentry).toBeDefined();
expect(sentry).toBeInstanceOf(SentryService);
expect(sentry.opts?.logger).toBeInstanceOf(ConsoleLogger);
});

it('should provide sentry client with custom logger', async () => {
const mod = await Test.createTestingModule({
imports: [
SentryModule.forRootAsync({
useFactory: () => (configWithCustomLogger),
}),
]
}).compile();

const sentry = mod.get<SentryService>(SENTRY_TOKEN);
expect(sentry).toBeDefined();
expect(sentry).toBeInstanceOf(SentryService);
expect(sentry.opts?.logger).toBeInstanceOf(CustomLogger);
});

it('should provide sentry client with no logger', async () => {
const mod = await Test.createTestingModule({
imports: [
SentryModule.forRootAsync({
useFactory: () => (configWithNoLogger),
}),
]
}).compile();

const sentry = mod.get<SentryService>(SENTRY_TOKEN);
expect(sentry).toBeDefined();
expect(sentry).toBeInstanceOf(SentryService);
expect(sentry.opts?.logger).toBeNull();
});
})
});
Expand Down
62 changes: 58 additions & 4 deletions lib/__tests__/sentry.service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import { SENTRY_TOKEN } from "../sentry.constants";
import * as Sentry from '@sentry/node';
jest.spyOn(Sentry, 'close')
.mockImplementation(() => Promise.resolve(true));
const mockCloseSentry = Sentry.close as jest.MockedFunction<typeof Sentry.close>;

const SENTRY_NOT_CONFIGURE_ERROR = 'Please confirm that Sentry is configured correctly';

Expand All @@ -20,16 +19,32 @@ describe('SentryService', () => {
logLevel: LogLevel.Debug,
};

let configWithNoLogger: SentryModuleOptions = {
dsn: 'https://[email protected]/25956308132020',
debug: true,
environment: 'development',
logLevel: LogLevel.Debug,
logger: null
};

let failureConfig: SentryModuleOptions = {
dsn: 'https://[email protected]/1512xxx',
debug: true,
environment: 'development',
logLevel: LogLevel.Debug,
};

class TestService implements SentryOptionsFactory {
let failureConfigNoLogger: SentryModuleOptions = {
dsn: 'https://[email protected]/1512xxx',
debug: true,
environment: 'development',
logLevel: LogLevel.Debug,
logger: null
};

class TestServiceNoLogging implements SentryOptionsFactory {
createSentryModuleOptions(): SentryModuleOptions {
return config;
return configWithNoLogger;
}
}

Expand All @@ -39,6 +54,12 @@ describe('SentryService', () => {
}
}

class FailureServiceNoLogging implements SentryOptionsFactory {
createSentryModuleOptions(): SentryModuleOptions {
return failureConfigNoLogger;
}
}

describe('sentry.log:error', () => {
it('should provide the sentry client and call log', async() => {
const mod = await Test.createTestingModule({
Expand All @@ -54,6 +75,21 @@ describe('SentryService', () => {
fail.log('sentry:log');
expect(fail.log).toBeInstanceOf(Function);
});

it('should provide the sentry client and call log with disabled logging', async() => {
const mod = await Test.createTestingModule({
imports: [
SentryModule.forRootAsync({
useClass: FailureServiceNoLogging
})
]
}).compile();

const fail = mod.get<SentryService>(SENTRY_TOKEN);
console.log('sentry:error', fail);
fail.log('sentry:log');
expect(fail.log).toBeInstanceOf(Function);
});
});

describe('sentry.log', () => {
Expand All @@ -72,6 +108,24 @@ describe('SentryService', () => {
expect(sentry.log).toBeInstanceOf(Function);
expect(true).toBeTruthy();
});

it('should provide the sentry client and call log with disabled logging', async() => {
const mod = await Test.createTestingModule({
imports: [
SentryModule.forRootAsync({
useClass: TestServiceNoLogging
})
]
}).compile();

const sentry = mod.get<SentryService>(SENTRY_TOKEN);
expect(sentry).toBeDefined();
expect(sentry).toBeInstanceOf(SentryService);
console.log('sentry', sentry);
sentry.log('sentry:log');
expect(sentry.log).toBeInstanceOf(Function);
expect(true).toBeTruthy();
});
});

describe('sentry.error', () => {
Expand Down Expand Up @@ -276,5 +330,5 @@ describe('SentryService', () => {
}
})
})

});
4 changes: 2 additions & 2 deletions lib/sentry-core.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,9 @@ export class SentryCoreModule {
const provider = createSentryProviders(options);

return {
exports: [provider, SentryService],
exports: [provider],
module: SentryCoreModule,
providers: [provider, SentryService],
providers: [provider],
};
}

Expand Down
9 changes: 7 additions & 2 deletions lib/sentry.interfaces.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,23 @@
import { ModuleMetadata, Type } from "@nestjs/common/interfaces";
import { Integration, Options } from '@sentry/types';
import { Severity } from "@sentry/node";
import { ConsoleLoggerOptions } from "@nestjs/common";
import { ConsoleLoggerOptions, LoggerService } from '@nestjs/common';

export interface SentryCloseOptions {
enabled: boolean;
// timeout – Maximum time in ms the client should wait until closing forcefully
timeout?: number;
}

export interface LoggingOptions {
logger?: LoggerService | null;
loggerOptions?: ConsoleLoggerOptions;
}

export type SentryModuleOptions = Omit<Options, 'integrations'> & {
integrations?: Integration[];
close?: SentryCloseOptions
} & ConsoleLoggerOptions;
} & LoggingOptions;

export interface SentryOptionsFactory {
createSentryModuleOptions(): Promise<SentryModuleOptions> | SentryModuleOptions;
Expand Down
Loading