Skip to content

Commit

Permalink
Add test, basic documentation
Browse files Browse the repository at this point in the history
  • Loading branch information
keenondrums committed Apr 7, 2019
1 parent 553b2cb commit d780ab7
Show file tree
Hide file tree
Showing 6 changed files with 166 additions and 11 deletions.
122 changes: 119 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,126 @@
# class-logger

Boilerplate-free decorator-based class logging. Log method calls and creation of your class easily with a help of two decorators. No prototype mutation. Highly configurable. Built with TypeScript.

```ts
@LogClass()
class Test {
@Log()
method1() {
return 123
}
}
```

Logs `Test.construct. Args: [].` before a class instance is created.
Logs `Test.method1. Args: [].` before the method call.
Logs `Test.method1 -> done. Args: []. Res: 123.` after it.

<!-- START doctoc generated TOC please keep comment here to allow auto update -->
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->

**Table of Contents** _generated with [DocToc](https://github.com/thlorenz/doctoc)_

- [class-logger](#class-logger)
- [Installation](#installation)
- [Quick start](#quick-start)

<!-- END doctoc generated TOC please keep comment here to allow auto update -->

## Installation

1. Run

```
npm i class-logger reflect-metadata
```

2. If you use TypeScript set in you tsconfig.json

```json
{
"compilerOptions": {
"experimentalDecorators": true,
"emitDecoratorMetadata": true
}
}
```

3. If you use JavaScript configure your babel to support decorators and class properties
4. At the top of your project root file add

```ts
import 'reflect-metadata'
```

## Quick start

You can log:

- Class construction
- Prototype and static method calls, both: synchronous and asynchronous. Any thrown errors are properly logged and re-thrown.
- Own and static property calls if those properties return functions (synchronous or asynchronous). Error handling is the same as for method calls.

```ts
import { LogClass, Log } from 'class-logger'

@LogClass()
class Test {
@Log()
method1() {
return 123
}

@Log()
async methodAsync1() {
// do something asynchronous
return Symbol()
}

@Log()
methodError() {
throw new Error()
}

@Log()
property1 = () => null

@Log()
static methodStatic1(arg1) {
return {
prop1: 'test',
}
}
}

// Logs to the console before the method call:
// 'Test.methodStatic1. Args: [42].'
Test.methodStatic1(42)
// Logs to the console after the method call:
// 'Test.methodStatic1 -> done. Args: [42]. Res: {"prop1":"test"}.'

// Logs to the console before the class' construction:
// 'Test.construct. Args: [].'
const test = new Test()

// Logs to the console before the method call:
// 'Test.method1. Args: [].'
test.method1()
// Logs to the console after the method call:
// 'Test.method1 -> done. Args: []. Res: 123.'

// Logs to the console before the method call:
// 'Test.methodAsync1. Args: [].'
test.methodAsync1()
// Logs to the console after the method call (after the promise is resolved):
// 'Test.methodAsync1 -> done. Args: []. Res: Symbol().'

// Logs to the console before the method call:
// 'Test.methodError. Args: [].'
test.methodError()
// Logs to the console after the method call:
// 'Test.methodError -> error. Args: []. Res: {"className":"Error","name":"Error","message":"","stack":"some stack trace"}.'

// Logs to the console before the method call:
// 'Test.property1. Args: [].'
test.property1()
// Logs to the console after the method call:
// 'Test.property1 -> done. Args: []. Res: null.'
```
35 changes: 35 additions & 0 deletions index.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,16 @@ describe('index', () => {
public static staticError(arg1: string, arg2: string) {
throw new TestError()
}

public prop1 = 123

@Log()
public propSyncSuccess = () => successRes
@Log()
public propSyncError = () => {
throw new TestError()
}

@Log({
log: (message) => console.debug(message), // tslint:disable-line no-console
})
Expand Down Expand Up @@ -82,6 +91,32 @@ describe('index', () => {
'Test.staticError -> error. Args: [test1, test2]. Class instance: N/A. Res: {"className":"TestError","code":"codeTest","message":"messageTest","name":"Error","stack":"stackTest"}.',
)
})
test('propSyncSuccess', () => {
const spyConsoleInfo = jest.spyOn(console, 'info')
const spyConsoleError = jest.spyOn(console, 'error')
const res = new Test().propSyncSuccess()
expect(res).toBe(successRes)
expect(spyConsoleError).toBeCalledTimes(0)
expect(spyConsoleInfo).toBeCalledTimes(3)
expect(spyConsoleInfo).toHaveBeenNthCalledWith(1, 'Test.construct. Args: []. Class instance: N/A.')
expect(spyConsoleInfo).toHaveBeenNthCalledWith(2, 'Test.propSyncSuccess. Args: []. Class instance: {"prop1":123}.')
expect(spyConsoleInfo).toHaveBeenNthCalledWith(
3,
'Test.propSyncSuccess -> done. Args: []. Class instance: {"prop1":123}. Res: syncSuccessResTest.',
)
})
test('propSyncError', () => {
const spyConsoleInfo = jest.spyOn(console, 'info')
const spyConsoleError = jest.spyOn(console, 'error')
expect(() => new Test().propSyncError()).toThrow(TestError)
expect(spyConsoleError).toBeCalledTimes(1)
expect(spyConsoleInfo).toBeCalledTimes(2)
expect(spyConsoleInfo).toHaveBeenNthCalledWith(1, 'Test.construct. Args: []. Class instance: N/A.')
expect(spyConsoleInfo).toHaveBeenNthCalledWith(2, 'Test.propSyncError. Args: []. Class instance: {"prop1":123}.')
expect(spyConsoleError).toBeCalledWith(
'Test.propSyncError -> error. Args: []. Class instance: {"prop1":123}. Res: {"className":"TestError","code":"codeTest","message":"messageTest","name":"Error","stack":"stackTest"}.',
)
})
test('syncSuccess', () => {
const spyConsoleInfo = jest.spyOn(console, 'info')
const spyConsoleDebug = jest.spyOn(console, 'debug')
Expand Down
5 changes: 0 additions & 5 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 10 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,16 @@
{
"name": "class-logger",
"version": "1.0.0",
"description": "",
"description": "Boilerplate-free decorator-based class logging",
"keywords": [
"decorator",
"log",
"logging",
"logger",
"class",
"bolierplate",
"proxy"
],
"main": "dist/index.js",
"scripts": {
"test": "npx jest -i",
Expand Down Expand Up @@ -40,7 +49,6 @@
"reflect-metadata": "^0.1.13"
},
"dependencies": {
"@keenondrums/metadata-utils": "^1.0.1",
"fast-safe-stringify": "^2.0.6"
}
}
1 change: 1 addition & 0 deletions src/formatter.service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ describe(ClassLoggerFormatterService.name, () => {
public testService = new TestService()
public propNull = null
public prop3 = valTestClassProp3
public propFn = () => null
}
const testClassStringExpected = stringify({
prop1: valTestClassProp1,
Expand Down
2 changes: 1 addition & 1 deletion src/formatter.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ export class ClassLoggerFormatterService implements IClassLoggerFormatter {
const classInsanceFiltered: { [key: string]: any } = {}
for (const key of Object.keys(classInstance)) {
const value = classInstance[key]
if (typeof value === 'object' && !this.isPlainObjectOrArray(value)) {
if (typeof value === 'function' || (typeof value === 'object' && !this.isPlainObjectOrArray(value))) {
continue
}
classInsanceFiltered[key] = value
Expand Down

0 comments on commit d780ab7

Please sign in to comment.