Skip to content

Commit

Permalink
fix: logs not being parsed in stackdriver. (#22)
Browse files Browse the repository at this point in the history
  • Loading branch information
simenandre authored Aug 30, 2021
1 parent be9e915 commit 5f4d393
Show file tree
Hide file tree
Showing 15 changed files with 698 additions and 144 deletions.
25 changes: 24 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ and reusable Slack code declaratively and ready for production.
- Used in many production workloads.
- Building blocks with [slack-block-builder].
- Supports sending messages directly to Slack Web API.
- Supports Google Stackdriver (Logger).
- Supports Google Logging.
- Zero dependencies (there are `peerDependencies`)

[slack-block-builder]: https://github.com/raycharius/slack-block-builder
Expand Down Expand Up @@ -87,6 +87,29 @@ export class AuthService {
}
```

### Use with Google Logging

```shell
▶ yarn add @google-cloud/logging
```

```typescript
import { SlackModule } from 'nestjs-slack';

@Module({
imports: [SlackModule.forRoot({ type: 'google' })],
})
export class AppModule {}
```

When `type` is set to `google` the `@google-cloud/logging` package will be used
to send logs to stdout [according to structured logs][structured-logs].

You can deploy [gcl-slack] to consume logs from this library.

[structured-logs]: https://cloud.google.com/logging/docs/structured-logging
[gcl-slack]: https://github.com/bjerkio/gcl-slack

## Contribute & Disclaimer

We love to get help 🙏 Read more about how to get started in
Expand Down
1 change: 1 addition & 0 deletions jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ module.exports = {
statements: -10,
},
},
modulePathIgnorePatterns: ['<rootDir>/dist/'],
globals: {
'ts-jest': {
tsconfig: 'tsconfig.jest.json',
Expand Down
7 changes: 6 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
"reflect-metadata": "^0.1.13",
"slack-block-builder": "^2.1.0",
"ts-jest": "^26.4.4",
"@google-cloud/logging": "^9.5.5",
"typescript": "4.1.4"
},
"lint-staged": {
Expand All @@ -49,15 +50,19 @@
"peerDependencies": {
"@nestjs/common": ">=7 <=8",
"@nestjs/core": ">=7 <=8",
"@slack/web-api": "^6",
"slack-block-builder": "^2",
"@slack/web-api": "^6"
"@google-cloud/logging": "^9"
},
"peerDependenciesMeta": {
"slack-block-builder": {
"optional": true
},
"@slack/web-api": {
"optional": true
},
"@google-cloud/logging": {
"optional": true
}
},
"prettier": "@cobraz/prettier-config"
Expand Down
28 changes: 28 additions & 0 deletions src/__tests__/api.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { MockedWebClient } from '@slack-wrench/jest-mock-web-client';
import { SlackService } from '../slack.service';
import { createApp } from './fixtures';

describe('api', () => {
let service: SlackService;
let output: any;

beforeEach(async () => {
output = jest.fn();
const app = await createApp({
type: 'api',
defaultChannel: 'my-channel',
apiOptions: { token: 'my-token' },
});
service = app.get<SlackService>(SlackService);
});

it('must send requests to API', async () => {
await service.postMessage({ text: 'hello-world' });
expect(
MockedWebClient.mock.instances[0].chat.postMessage,
).toHaveBeenCalledWith({
channel: 'my-channel',
text: 'hello-world',
});
});
});
8 changes: 8 additions & 0 deletions src/__tests__/fixtures.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { Test } from '@nestjs/testing';
import { SlackConfig, SlackModule } from '../slack.module';

export const createApp = (options?: Partial<SlackConfig>) => {
return Test.createTestingModule({
imports: [SlackModule.forRoot(options)],
}).compile();
};
63 changes: 63 additions & 0 deletions src/__tests__/google.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import { Log, LogSync } from '@google-cloud/logging';
import { Test } from '@nestjs/testing';
import {
GOOGLE_LOGGING,
SLACK_MODULE_OPTIONS,
SLACK_WEB_CLIENT,
} from '../constants';
import { SlackService } from '../slack.service';
import { createApp } from './fixtures';

describe('google logging', () => {
let service: SlackService;
let write: any;

beforeEach(async () => {
write = jest.fn();
const app = await Test.createTestingModule({
providers: [
{
provide: SLACK_MODULE_OPTIONS,
useValue: { type: 'google' },
},
{
provide: SLACK_WEB_CLIENT,
useValue: null,
},
{
provide: GOOGLE_LOGGING,
useValue: {
write,
entry: (metadata: any, message: any) => ({
...metadata,
message,
}),
},
},
SlackService,
],
}).compile();
service = app.get<SlackService>(SlackService);
});

it('must output requests as structured logs', async () => {
const request = {
text: 'hello-world',
channel: 'hello-world',
};
await service.postMessage(request);
expect(write).toHaveBeenCalledWith({
'logging.googleapis.com/labels': { type: 'nestjs-slack' },
'logging.googleapis.com/operation': {
producer: 'github.com/bjerkio/nestjs-slack',
},
message: { channel: 'hello-world', text: 'hello-world' },
severity: 'NOTICE',
});
});

it('must resolve @google/cloud-logging', async () => {
const app = await createApp({ type: 'google' });
expect(app.get(GOOGLE_LOGGING)).toBeInstanceOf(LogSync);
});
});
2 changes: 1 addition & 1 deletion src/__tests__/invariant.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { invariant } from '../utils';

describe('invariant', () => {
it('should throw', () => {
it('must throw', () => {
try {
invariant(false, 'expected');
} catch (e) {
Expand Down
84 changes: 4 additions & 80 deletions src/__tests__/slack.service.test.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,9 @@
import { Test } from '@nestjs/testing';
import { SlackConfig, SlackModule } from '../slack.module';
import { SlackService } from '../slack.service';
import { MockedWebClient } from '@slack-wrench/jest-mock-web-client';
import { BlockCollection, Blocks } from 'slack-block-builder';
import { createApp } from './fixtures';

describe('SlackService', () => {
let service: SlackService;
let output: any;

const createApp = (options?: Partial<SlackConfig>) => {
return Test.createTestingModule({
imports: [SlackModule.forRoot(options)],
}).compile();
};

it('should be defined', async () => {
it('must be defined', async () => {
const app = await createApp();
expect(app.get<SlackService>(SlackService)).toBeDefined();
});
Expand All @@ -27,7 +16,7 @@ describe('SlackService', () => {
service.postMessage = jest.fn();
});
describe('sendText', () => {
it('should forward to sendMessage', async () => {
it('must forward to sendMessage', async () => {
await service.sendText('hello world');
expect(service.postMessage).toHaveBeenCalledWith({
text: 'hello world',
Expand All @@ -36,76 +25,11 @@ describe('SlackService', () => {
});

describe('sendBlocks', () => {
it('should forward to sendMessage', async () => {
it('must forward to sendMessage', async () => {
const blocks = BlockCollection(Blocks.Section({ text: 'hello-world' }));
await service.sendBlocks(blocks);
expect(service.postMessage).toHaveBeenCalledWith({ blocks });
});
});
});

describe('types', () => {
describe('api', () => {
beforeEach(async () => {
output = jest.fn();
const app = await createApp({
type: 'api',
defaultChannel: 'my-channel',
apiOptions: { token: 'my-token' },
});
service = app.get<SlackService>(SlackService);
});

it('should send requests to API', async () => {
await service.postMessage({ text: 'hello-world' });
expect(
MockedWebClient.mock.instances[0].chat.postMessage,
).toHaveBeenCalledWith({
channel: 'my-channel',
text: 'hello-world',
});
});
});

describe('stdout', () => {
beforeEach(async () => {
output = jest.fn();
const app = await createApp({ output });
service = app.get<SlackService>(SlackService);
});

it('should output requests to stdout', async () => {
const request = {
text: 'hello-world',
channel: 'hello-world',
};
await service.postMessage(request);
expect(output).toHaveBeenCalledWith(request);
});
});

describe('stackdriver', () => {
beforeEach(async () => {
output = jest.fn();
const app = await createApp({ type: 'stackdriver', output });
service = app.get<SlackService>(SlackService);
});

it('should output requests to stdout with structured logs', async () => {
const request = {
text: 'hello-world',
channel: 'hello-world',
};
await service.postMessage(request);
expect(output).toHaveBeenCalledWith({
message: request,
severity: 'NOTICE',
'logging.googleapis.com/labels': { type: 'nestjs-slack' },
'logging.googleapis.com/operation': {
producer: 'github.com/bjerkio/nestjs-slack',
},
});
});
});
});
});
22 changes: 22 additions & 0 deletions src/__tests__/stdout.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { SlackService } from '../slack.service';
import { createApp } from './fixtures';

describe('stdout', () => {
let service: SlackService;
let output: any;

beforeEach(async () => {
output = jest.fn();
const app = await createApp({ output });
service = app.get<SlackService>(SlackService);
});

it('must output requests to stdout', async () => {
const request = {
text: 'hello-world',
channel: 'hello-world',
};
await service.postMessage(request);
expect(output).toHaveBeenCalledWith(request);
});
});
1 change: 1 addition & 0 deletions src/constants.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export const SLACK_MODULE_OPTIONS = 'SlackModuleOptions';
export const SLACK_WEB_CLIENT = 'SlackWebClient';
export const GOOGLE_LOGGING = 'SlackGoogleLogging';
1 change: 0 additions & 1 deletion src/plugin.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
export * from './constants';
export * from './slack.module';
export * from './slack.service';
export * from './types';
Loading

0 comments on commit 5f4d393

Please sign in to comment.