Skip to content

Commit 1ae5d53

Browse files
authored
Merge pull request #10 from aigoncharov/fix/6
Fix/6
2 parents 2df85da + 7932b63 commit 1ae5d53

File tree

8 files changed

+268
-125
lines changed

8 files changed

+268
-125
lines changed

README.md

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# class-logger [![Build Status](https://travis-ci.org/keenondrums/class-logger.svg?branch=master)](https://travis-ci.org/keenondrums/class-logger) [![Coverage Status](https://coveralls.io/repos/github/keenondrums/class-logger/badge.svg?branch=master)](https://coveralls.io/github/keenondrums/class-logger?branch=master) [![Tweet](https://img.shields.io/twitter/url/http/shields.io.svg?style=social)](https://twitter.com/intent/tweet?text=Boilerplate-free%20decorator-based%20class%20logging.&url=https://github.com/keenondrums/class-logger&hashtags=typescript,javascript,decorators,logging)
1+
# class-logger [![Build Status](https://travis-ci.org/aigoncharov/class-logger.svg?branch=master)](https://travis-ci.org/aigoncharov/class-logger) [![Coverage Status](https://coveralls.io/repos/github/aigoncharov/class-logger/badge.svg?branch=master)](https://coveralls.io/github/aigoncharov/class-logger?branch=master) [![Tweet](https://img.shields.io/twitter/url/http/shields.io.svg?style=social)](https://twitter.com/intent/tweet?text=Boilerplate-free%20decorator-based%20class%20logging.&url=https://github.com/aigoncharov/class-logger&hashtags=typescript,javascript,decorators,logging)
22

33
Boilerplate-free decorator-based class logging. Log method calls and creation of your class easily with the help of two decorators. No prototype mutation. Highly configurable. Built with TypeScript. Works with Node.js and in browser.
44

@@ -72,7 +72,7 @@ Logs `Test.method1 -> done. Args: []. Res: 123.` after it.
7272

7373
## Requirements
7474

75-
Your evnvironment must support [Proxy](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy). For Node.js it's [6.4.0+](https://node.green/), for browsers it's [Edge 12+, Firefox 18+, Chrome 49+, Safari 10+](https://caniuse.com/#search=proxy).
75+
Your environment must support [Proxy](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy). For Node.js it's [6.4.0+](https://node.green/), for browsers it's [Edge 12+, Firefox 18+, Chrome 49+, Safari 10+](https://caniuse.com/#search=proxy).
7676

7777
## Quick start [(Live demo)](https://stackblitz.com/edit/class-logger-demo-basic)
7878

@@ -140,7 +140,7 @@ test.methodAsync1()
140140
// 'Test.methodError. Args: [].'
141141
test.methodError()
142142
// Logs to the console after the method call:
143-
// 'Test.methodError -> error. Args: []. Res: {"className":"Error","name":"Error","message":"","stack":"some stack trace"}.'
143+
// 'Test.methodError -> error. Args: []. Res: Error {"name":"Error","message":"","stack":"some stack trace"}.'
144144

145145
// Logs to the console before the method call:
146146
// 'Test.property1. Args: [].'
@@ -278,9 +278,9 @@ It enables/disabled including the formatted class instance to your log messages.
278278
- Why? It's a rare case when your prototype changes dynamically, therefore it hardly makes any sense to log it.
279279
- Drop any of them that have `function` type.
280280
- Why? Most of the time `function` properties are just immutable arrow functions used instead of regular class methods to preserve `this` context. It doesn't make much sense to bloat your logs with stringified bodies of those functions.
281-
- Drop any of them that are not plain objects.
281+
- Transform any of them that are not plain objects recursively.
282282
- What objects are plain ones? `ClassLoggerFormatterService` considers an object a plain object if its prototype is strictly equal to `Object.prototype`.
283-
- Why? Often we include instances of other classes as properties (inject them as dependencies). Our logs would become extremely fat if we included stringified versions of these dependencies.
283+
- Why? Often we include instances of other classes as properties (inject them as dependencies). By stringifying them using the same algorithm we can see what we injected.
284284
- Stringify what's left.
285285

286286
Example:
@@ -306,14 +306,14 @@ class Test {
306306
}
307307

308308
// Logs to the console before the class' construction:
309-
// 'Test.construct. Args: []. Class instance: {"prop1":42,"prop2":{"test":42}}.'
309+
// 'Test.construct. Args: []. Class instance: {"serviceA": ServiceA {},"prop1":42,"prop2":{"test":42}}.'
310310
const test = new Test()
311311

312312
// Logs to the console before the method call:
313-
// 'Test.method2. Args: []. Class instance: {"prop1":42,"prop2":{"test":42}}.'
313+
// 'Test.method2. Args: []. Class instance: {"serviceA": ServiceA {},"prop1":42,"prop2":{"test":42}}.'
314314
test.method2()
315315
// Logs to the console after the method call:
316-
// 'Test.method2 -> done. Args: []. Class instance: {"prop1":42,"prop2":{"test":42}}. Res: 42.'
316+
// 'Test.method2 -> done. Args: []. Class instance: {"serviceA": ServiceA {},"prop1":42,"prop2":{"test":42}}. Res: 42.'
317317
```
318318

319319
> If a class instance is not available at the moment (e.g. for class construction or calls of static methods), it logs `N/A`.

index.spec.ts

Lines changed: 53 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ describe('index', () => {
8888
expect(spyConsoleInfo).toBeCalledTimes(1)
8989
expect(spyConsoleInfo).toBeCalledWith('Test.staticError. Args: [test1, test2]. Class instance: N/A.')
9090
expect(spyConsoleError).toBeCalledWith(
91-
'Test.staticError -> error. Args: [test1, test2]. Class instance: N/A. Res: {"className":"TestError","code":"codeTest","message":"messageTest","name":"Error","stack":"stackTest"}.',
91+
'Test.staticError -> error. Args: [test1, test2]. Class instance: N/A. Res: TestError {"code":"codeTest","message":"messageTest","name":"Error","stack":"stackTest"}.',
9292
)
9393
})
9494
test('propSyncSuccess', () => {
@@ -99,10 +99,13 @@ describe('index', () => {
9999
expect(spyConsoleError).toBeCalledTimes(0)
100100
expect(spyConsoleInfo).toBeCalledTimes(3)
101101
expect(spyConsoleInfo).toHaveBeenNthCalledWith(1, 'Test.construct. Args: []. Class instance: N/A.')
102-
expect(spyConsoleInfo).toHaveBeenNthCalledWith(2, 'Test.propSyncSuccess. Args: []. Class instance: {"prop1":123}.')
102+
expect(spyConsoleInfo).toHaveBeenNthCalledWith(
103+
2,
104+
'Test.propSyncSuccess. Args: []. Class instance: Test {"prop1":123}.',
105+
)
103106
expect(spyConsoleInfo).toHaveBeenNthCalledWith(
104107
3,
105-
'Test.propSyncSuccess -> done. Args: []. Class instance: {"prop1":123}. Res: syncSuccessResTest.',
108+
'Test.propSyncSuccess -> done. Args: []. Class instance: Test {"prop1":123}. Res: syncSuccessResTest.',
106109
)
107110
})
108111
test('propSyncError', () => {
@@ -112,9 +115,12 @@ describe('index', () => {
112115
expect(spyConsoleError).toBeCalledTimes(1)
113116
expect(spyConsoleInfo).toBeCalledTimes(2)
114117
expect(spyConsoleInfo).toHaveBeenNthCalledWith(1, 'Test.construct. Args: []. Class instance: N/A.')
115-
expect(spyConsoleInfo).toHaveBeenNthCalledWith(2, 'Test.propSyncError. Args: []. Class instance: {"prop1":123}.')
118+
expect(spyConsoleInfo).toHaveBeenNthCalledWith(
119+
2,
120+
'Test.propSyncError. Args: []. Class instance: Test {"prop1":123}.',
121+
)
116122
expect(spyConsoleError).toBeCalledWith(
117-
'Test.propSyncError -> error. Args: []. Class instance: {"prop1":123}. Res: {"className":"TestError","code":"codeTest","message":"messageTest","name":"Error","stack":"stackTest"}.',
123+
'Test.propSyncError -> error. Args: []. Class instance: Test {"prop1":123}. Res: TestError {"code":"codeTest","message":"messageTest","name":"Error","stack":"stackTest"}.',
118124
)
119125
})
120126
test('syncSuccess', () => {
@@ -127,10 +133,13 @@ describe('index', () => {
127133
expect(spyConsoleInfo).toBeCalledTimes(1)
128134
expect(spyConsoleDebug).toBeCalledTimes(2)
129135
expect(spyConsoleInfo).toBeCalledWith('Test.construct. Args: []. Class instance: N/A.')
130-
expect(spyConsoleDebug).toHaveBeenNthCalledWith(1, 'Test.syncSuccess. Args: []. Class instance: {"prop1":123}.')
136+
expect(spyConsoleDebug).toHaveBeenNthCalledWith(
137+
1,
138+
'Test.syncSuccess. Args: []. Class instance: Test {"prop1":123}.',
139+
)
131140
expect(spyConsoleDebug).toHaveBeenNthCalledWith(
132141
2,
133-
'Test.syncSuccess -> done. Args: []. Class instance: {"prop1":123}. Res: syncSuccessResTest.',
142+
'Test.syncSuccess -> done. Args: []. Class instance: Test {"prop1":123}. Res: syncSuccessResTest.',
134143
)
135144
})
136145
test('syncError', () => {
@@ -142,9 +151,9 @@ describe('index', () => {
142151
expect(spyConsoleInfo).toBeCalledTimes(1)
143152
expect(spyConsoleDebug).toBeCalledTimes(1)
144153
expect(spyConsoleInfo).toBeCalledWith('Test.construct. Args: []. Class instance: N/A.')
145-
expect(spyConsoleDebug).toBeCalledWith('Test.syncError. Args: []. Class instance: {"prop1":123}.')
154+
expect(spyConsoleDebug).toBeCalledWith('Test.syncError. Args: []. Class instance: Test {"prop1":123}.')
146155
expect(spyConsoleError).toBeCalledWith(
147-
'Test.syncError -> error. Args: []. Class instance: {"prop1":123}. Res: {"className":"TestError","code":"codeTest","message":"messageTest","name":"Error","stack":"stackTest"}.',
156+
'Test.syncError -> error. Args: []. Class instance: Test {"prop1":123}. Res: TestError {"code":"codeTest","message":"messageTest","name":"Error","stack":"stackTest"}.',
148157
)
149158
})
150159
test('asyncSuccess', async () => {
@@ -159,11 +168,11 @@ describe('index', () => {
159168
expect(spyConsoleInfo).toHaveBeenNthCalledWith(1, 'Test.construct. Args: []. Class instance: N/A.')
160169
expect(spyConsoleInfo).toHaveBeenNthCalledWith(
161170
2,
162-
'Test.asyncSuccess. Args: [Symbol()]. Class instance: {"prop1":123}.',
171+
'Test.asyncSuccess. Args: [Symbol()]. Class instance: Test {"prop1":123}.',
163172
)
164173
expect(spyConsoleInfo).toHaveBeenNthCalledWith(
165174
3,
166-
'Test.asyncSuccess -> done. Args: [Symbol()]. Class instance: {"prop1":123}. Res: syncSuccessResTest.',
175+
'Test.asyncSuccess -> done. Args: [Symbol()]. Class instance: Test {"prop1":123}. Res: syncSuccessResTest.',
167176
)
168177
})
169178
test('asyncError', async () => {
@@ -177,10 +186,41 @@ describe('index', () => {
177186
expect(spyConsoleInfo).toHaveBeenNthCalledWith(1, 'Test.construct. Args: []. Class instance: N/A.')
178187
expect(spyConsoleInfo).toHaveBeenNthCalledWith(
179188
2,
180-
'Test.asyncError. Args: [Symbol()]. Class instance: {"prop1":123}.',
189+
'Test.asyncError. Args: [Symbol()]. Class instance: Test {"prop1":123}.',
181190
)
182191
expect(spyConsoleDebug).toBeCalledWith(
183-
'Test.asyncError -> error. Args: [Symbol()]. Class instance: {"prop1":123}. Res: {"className":"TestError","code":"codeTest","message":"messageTest","name":"Error","stack":"stackTest"}.',
192+
'Test.asyncError -> error. Args: [Symbol()]. Class instance: Test {"prop1":123}. Res: TestError {"code":"codeTest","message":"messageTest","name":"Error","stack":"stackTest"}.',
184193
)
185194
})
195+
196+
test('keeps third-party metadata', () => {
197+
class TestMeta {
198+
@Log()
199+
public static static1() {} // tslint:disable-line no-empty
200+
201+
@Log()
202+
public method1() {} // tslint:disable-line no-empty
203+
}
204+
205+
const keyClass = Symbol()
206+
Reflect.defineMetadata(keyClass, 42, TestMeta)
207+
208+
const keyPrototype = Symbol()
209+
Reflect.defineMetadata(keyPrototype, 43, TestMeta.prototype)
210+
211+
const keyProp = Symbol()
212+
Reflect.defineMetadata(keyProp, 44, TestMeta.prototype, 'method1')
213+
214+
const keyStatic = Symbol()
215+
Reflect.defineMetadata(keyStatic, 45, TestMeta, 'static1')
216+
217+
const TestMetaWrapped = LogClass()(TestMeta)
218+
219+
expect(Reflect.getMetadata(keyClass, TestMeta)).toBe(42)
220+
expect(Reflect.getMetadata(keyStatic, TestMeta, 'static1')).toBe(45)
221+
222+
const instance = new TestMetaWrapped()
223+
expect(Reflect.getMetadata(keyPrototype, instance)).toBe(43)
224+
expect(Reflect.getMetadata(keyProp, instance, 'method1')).toBe(44)
225+
})
186226
})

integration.spec.ts

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
import 'reflect-metadata'
2+
3+
import { Container, inject, injectable } from 'inversify'
4+
5+
import { Log, LogClass, setConfig } from './index'
6+
7+
describe('integration', () => {
8+
// https://github.com/aigoncharov/class-logger/issues/6
9+
describe('inversify', () => {
10+
it('', () => {
11+
const logFn = jest.fn()
12+
13+
setConfig({
14+
include: {
15+
construct: true,
16+
},
17+
log: logFn,
18+
})
19+
20+
const TYPES = {
21+
AI: Symbol.for('AI'),
22+
CPU: Symbol.for('CPU'),
23+
}
24+
25+
@LogClass()
26+
@injectable()
27+
class CPU {
28+
private readonly res = 42
29+
30+
@Log()
31+
public calc() {
32+
return this.res
33+
}
34+
}
35+
36+
@LogClass()
37+
@injectable()
38+
class AI {
39+
constructor(@inject(TYPES.CPU) private readonly _cpu: CPU) {}
40+
41+
@Log()
42+
public takeOverTheWorld() {
43+
return this._cpu.calc() * 2
44+
}
45+
}
46+
47+
const myContainer = new Container()
48+
myContainer.bind(TYPES.CPU).to(CPU)
49+
myContainer.bind(TYPES.AI).to(AI)
50+
51+
const cpu = myContainer.get<CPU>(TYPES.CPU)
52+
expect(cpu).toBeInstanceOf(CPU)
53+
54+
const ai = myContainer.get<AI>(TYPES.AI)
55+
expect(ai).toBeInstanceOf(AI)
56+
57+
const res = ai.takeOverTheWorld()
58+
expect(res).toBe(84)
59+
60+
expect(logFn).toBeCalledTimes(7)
61+
expect(logFn.mock.calls).toEqual([
62+
// Getting CPU from the container explicitly
63+
['CPU.construct. Args: [].'],
64+
// Injecting CPU into AI
65+
['CPU.construct. Args: [].'],
66+
['AI.construct. Args: [CPU {"res":42}].'],
67+
['AI.takeOverTheWorld. Args: [].'],
68+
['CPU.calc. Args: [].'],
69+
['CPU.calc -> done. Args: []. Res: 42.'],
70+
['AI.takeOverTheWorld -> done. Args: []. Res: 84.'],
71+
])
72+
})
73+
})
74+
})

package-lock.json

Lines changed: 16 additions & 33 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "class-logger",
3-
"version": "1.2.0",
3+
"version": "1.3.0",
44
"description": "Boilerplate-free decorator-based class logging",
55
"keywords": [
66
"decorator",
@@ -34,6 +34,7 @@
3434
"coveralls": "^3.0.3",
3535
"doctoc": "^1.4.0",
3636
"husky": "^1.3.1",
37+
"inversify": "^5.0.1",
3738
"jest": "^24.5.0",
3839
"lint-staged": "^8.1.5",
3940
"prettier": "^1.16.4",
@@ -43,7 +44,7 @@
4344
"tslint": "^5.14.0",
4445
"tslint-config-prettier": "^1.18.0",
4546
"tslint-config-standard": "^8.0.1",
46-
"typescript": "^3.3.4000"
47+
"typescript": "^3.7.5"
4748
},
4849
"peerDependencies": {
4950
"reflect-metadata": "^0.1.13"

0 commit comments

Comments
 (0)