Skip to content
This repository has been archived by the owner on Oct 26, 2022. It is now read-only.

Commit

Permalink
feat(http-driver): throw LumberjackHttpDriverError when max HTTP re…
Browse files Browse the repository at this point in the history
…tries are reached (#21)

Co-authored-by: Nacho Vazquez <[email protected]>
  • Loading branch information
LayZeeDK and NachoVazquez authored Jul 2, 2022
1 parent f2521ad commit c0e3e64
Show file tree
Hide file tree
Showing 21 changed files with 1,535 additions and 1,308 deletions.
24 changes: 23 additions & 1 deletion .eslintrc.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
{
"root": true,
"ignorePatterns": ["**/*"],
"plugins": ["@nrwl/nx"],
"plugins": ["@nrwl/nx", "etc", "rxjs", "rxjs-angular", "sonarjs"],
"parser": "@typescript-eslint/parser",
"parserOptions": {
"project": ["{apps,libs}/**/tsconfig.*?.json", "apps/**/*-e2e/tsconfig.json"]
},
"overrides": [
{
"files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
Expand Down Expand Up @@ -53,6 +57,24 @@
"files": ["*.js", "*.jsx"],
"extends": ["plugin:@nrwl/nx/javascript"],
"rules": {}
},
{
"files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
"extends": ["plugin:etc/recommended", "plugin:rxjs/recommended", "plugin:sonarjs/recommended"],
"rules": {
"etc/no-commented-out-code": "error",
"etc/no-const-enum": "error",
"sonarjs/cognitive-complexity": ["error", 8]
}
},
{
"files": ["*.ts"],
"rules": {
"rxjs-angular/prefer-composition": [
"error",
{ "checkDecorators": ["Component", "Directive", "Injectable", "NgModule", "Pipe"] }
]
}
}
]
}
4 changes: 4 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,3 +73,7 @@ fix(release): need to depend on latest rxjs and zone.js
The version in our package.json gets copied to the one we publish, and users need the latest of these.
```

## ESLint parser

As this workspace has a small amount of projects, we opt in to use the ESLint TypeScript parser globally (see the `parser` option in `.eslintrc.json`) in order to enable type-depending lint rules such as some from SonarLint.
6 changes: 3 additions & 3 deletions apps/lumberjack-app/src/app/app.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { ForestService } from './forest.service';
styleUrls: ['./app.component.scss'],
})
export class AppComponent implements OnInit, OnDestroy {
#subscriptions = new Subscription();
private subscriptions = new Subscription();

title = 'lumberjack-app';

Expand All @@ -19,10 +19,10 @@ export class AppComponent implements OnInit, OnDestroy {
ngOnInit(): void {
this.logger.helloForest();

this.#subscriptions.add(this.forest.fire$.subscribe(() => this.logger.forestOnFire()));
this.subscriptions.add(this.forest.fire$.subscribe(() => this.logger.forestOnFire()));
}

ngOnDestroy(): void {
this.#subscriptions.unsubscribe();
this.subscriptions.unsubscribe();
}
}
2 changes: 1 addition & 1 deletion apps/lumberjack-app/src/environments/environment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,4 @@ export const environment = {
* This import should be commented out in production mode because it will have a negative impact
* on performance if an error is thrown.
*/
// import 'zone.js/plugins/zone-error'; // Included with Angular CLI.
import 'zone.js/plugins/zone-error'; // Included with Angular CLI.
3 changes: 1 addition & 2 deletions apps/lumberjack-app/src/main.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { enableProdMode } from '@angular/core';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';

import { AppModule } from './app/app.module';
import { environment } from './environments/environment';

Expand All @@ -10,4 +9,4 @@ if (environment.production) {

platformBrowserDynamic()
.bootstrapModule(AppModule)
.catch((err) => console.error(err));
.catch((error: unknown) => console.error(error));
5 changes: 1 addition & 4 deletions libs/internal/console-driver/test-util/.eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,7 @@
"overrides": [
{
"files": ["*.ts"],
"extends": [
"plugin:@nrwl/nx/angular",
"plugin:@angular-eslint/template/process-inline-templates"
],
"extends": ["plugin:@nrwl/nx/angular", "plugin:@angular-eslint/template/process-inline-templates"],
"rules": {
"@angular-eslint/directive-selector": [
"error",
Expand Down
5 changes: 1 addition & 4 deletions libs/internal/test-util/.eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,7 @@
"overrides": [
{
"files": ["*.ts"],
"extends": [
"plugin:@nrwl/nx/angular",
"plugin:@angular-eslint/template/process-inline-templates"
],
"extends": ["plugin:@nrwl/nx/angular", "plugin:@angular-eslint/template/process-inline-templates"],
"rules": {
"@angular-eslint/directive-selector": [
"error",
Expand Down
10 changes: 10 additions & 0 deletions libs/ngworker/lumberjack/http-driver/src/errors-api.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { isClass } from '@internal/test-util';
import { LumberjackHttpDriverError } from './index';

describe('Errors API', () => {
it(`exposes ${LumberjackHttpDriverError.name}`, () => {
const sut = LumberjackHttpDriverError;

expect(isClass(sut)).toBeTruthy();
});
});
6 changes: 3 additions & 3 deletions libs/ngworker/lumberjack/http-driver/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@

// Configuration
export { LumberjackHttpDriverRetryOptions } from './lib/configuration/lumberjack-http-driver-retry.options';
export { LumberjackHttpDriverRootModule } from './lib/configuration/lumberjack-http-driver-root.module';
export { LumberjackHttpDriverConfig } from './lib/configuration/lumberjack-http-driver.config';
export { LumberjackHttpDriverModule } from './lib/configuration/lumberjack-http-driver.module';
export { LumberjackHttpDriverOptions } from './lib/configuration/lumberjack-http-driver.options';
export { LumberjackHttpDriverRootModule } from './lib/configuration/lumberjack-http-driver-root.module';

// Errors
export { LumberjackHttpDriverError } from './lib/errors/lumberjack-http-driver.error';
// Log drivers
export { LumberjackHttpDriver } from './lib/log-drivers/lumberjack-http.driver';

// Logs
export { LumberjackHttpLog } from './lib/logs/lumberjack-http.log';
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { LumberjackHttpDriverError } from './lumberjack-http-driver.error';

describe(LumberjackHttpDriverError.name, () => {
it('has the default error message "LumberjackHttpDriverError"', () => {
const error = new LumberjackHttpDriverError();

expect(error.message).toBe('LumberjackHttpDriverError');
});

it('accepts a custom error message', () => {
const expectedMessage = 'The water stream has dried up';
const error = new LumberjackHttpDriverError(expectedMessage);

expect(error.message).toBe(expectedMessage);
});

it('is an instance of Error', () => {
const error = new LumberjackHttpDriverError();

expect(error).toBeInstanceOf(Error);
});

it('has the error name ""LumberjackHttpDriverError"', () => {
const error = new LumberjackHttpDriverError();

expect(error.name).toBe('LumberjackHttpDriverError');
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
export class LumberjackHttpDriverError extends Error {
constructor(message: string = 'LumberjackHttpDriverError') {
super(message);
this.name = 'LumberjackHttpDriverError';

// Non-standard V8 function for maintaining a stack trace
const ErrorWithCaptureStackTrace = Error as unknown as Error & {
// eslint-disable-next-line @typescript-eslint/ban-types
captureStackTrace?: (targetObject: object, constructorOpt?: Function) => void;
};
ErrorWithCaptureStackTrace.captureStackTrace?.(this, this.constructor);
}
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import { HttpClient } from '@angular/common/http';
import { Inject, Injectable, NgZone } from '@angular/core';

import { Inject, Injectable, NgZone, OnDestroy } from '@angular/core';
import { LumberjackLog, LumberjackLogDriver, LumberjackLogDriverLog, LumberjackLogPayload } from '@ngworker/lumberjack';

import { Subscription } from 'rxjs';
import { lumberjackHttpDriverConfigToken } from '../configuration/lumberjack-http-driver-config.token';
import { LumberjackHttpDriverInternalConfig } from '../configuration/lumberjack-http-driver-internal.config';
import { LumberjackHttpLog } from '../logs/lumberjack-http.log';
Expand All @@ -16,15 +15,22 @@ import { retryWithDelay } from '../operators/retry-with-delay.operator';
*/
@Injectable()
export class LumberjackHttpDriver<TPayload extends LumberjackLogPayload | void = void>
implements LumberjackLogDriver<TPayload>
implements LumberjackLogDriver<TPayload>, OnDestroy
{
static driverIdentifier = 'LumberjackHttpDriver';

private subscriptions = new Subscription();

constructor(
private readonly http: HttpClient,
@Inject(lumberjackHttpDriverConfigToken) readonly config: LumberjackHttpDriverInternalConfig,
private readonly ngZone: NgZone
) {}

ngOnDestroy(): void {
this.subscriptions.unsubscribe();
}

/**
* Send critical log to the log store.
*
Expand Down Expand Up @@ -95,12 +101,16 @@ export class LumberjackHttpDriver<TPayload extends LumberjackLogPayload | void =
const httpLog: LumberjackHttpLog<TPayload> = { formattedLog, origin, log };

this.ngZone.runOutsideAngular(() => {
this.http
.post<void>(storeUrl, httpLog)
.pipe(retryWithDelay(retryOptions.maxRetries, retryOptions.delayMs))
.subscribe(() => {
// No-op
});
this.subscriptions.add(
this.http
.post<void>(storeUrl, httpLog)
.pipe(retryWithDelay(retryOptions.maxRetries, retryOptions.delayMs))
// HTTP requests complete after
// eslint-disable-next-line rxjs-angular/prefer-composition
.subscribe(() => {
// No-op
})
);
});
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import { concat, MonoTypeOperatorFunction, throwError } from 'rxjs';
import { delay, retryWhen, take } from 'rxjs/operators';
import { concat, delay, MonoTypeOperatorFunction, retryWhen, take, throwError } from 'rxjs';
import { LumberjackHttpDriverError } from '../errors/lumberjack-http-driver.error';

export const retryWithDelay = <T>(maxRetries: number, delayMs: number): MonoTypeOperatorFunction<T> =>
retryWhen<T>((errors) =>
concat(errors.pipe(delay(delayMs), take(maxRetries + 1)), throwError(`Failed after ${maxRetries} retries.`))
concat(
errors.pipe(delay(delayMs), take(maxRetries + 1)),
throwError(() => new LumberjackHttpDriverError(`Failed after ${maxRetries} retries.`))
)
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
export class LumberjackHttpDriverError extends Error {
constructor(message: string = 'LumberjackHttpDriverError') {
super(message);

// LumberjackHttpDriverError instanceof Error === true
Object.setPrototypeOf(this, new.target.prototype);

// Error name
this.name = 'LumberjackHttpDriverError';

// Non-standard V8 function for maintaining a stack trace
const ErrorWithCaptureStackTrace = Error as unknown as Error & {
// eslint-disable-next-line @typescript-eslint/ban-types
captureStackTrace?: (error: Error, constructor: Function) => void;
};
ErrorWithCaptureStackTrace.captureStackTrace?.(this, this.constructor);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,8 @@ describe(lumberjackFormatLog.name, () => {

logLevels.forEach((expectedLevel) => {
it(`prefixes the message with log level "${expectedLevel}"`, () => {
const log = new LumberjackLogBuilder(resolveDependency(LumberjackTimeService), expectedLevel, 'Test message')
.withScope('Test Scope')
const log = new LumberjackLogBuilder(resolveDependency(LumberjackTimeService), expectedLevel, 'Log level test')
.withScope('Log level')
.build();

const formattedLog = lumberjackFormatLog(log);
Expand All @@ -68,8 +68,8 @@ describe(lumberjackFormatLog.name, () => {
const log: LumberjackLog = {
createdAt: unixEpochTicks,
level: LumberjackLevel.Debug,
message: 'Test message',
scope: 'Test Scope',
message: 'Timestamp test',
scope: 'Timestamp',
};

const formattedLog = lumberjackFormatLog(log);
Expand All @@ -96,7 +96,7 @@ describe(lumberjackFormatLog.name, () => {
});

it('does not add a tag without a scope', () => {
const log = logFactory.createDebugLog('Test message').build();
const log = logFactory.createDebugLog('Scope test').build();

const formattedLog = lumberjackFormatLog(log);

Expand All @@ -111,7 +111,7 @@ describe(lumberjackFormatLog.name, () => {

messages.forEach((expectedMessage) => {
it(`places the message at the end with a scope`, () => {
const log = logFactory.createDebugLog(expectedMessage).withScope('Test Scope').build();
const log = logFactory.createDebugLog(expectedMessage).withScope('Message').build();

const formattedLog = lumberjackFormatLog(log);

Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import { HttpClient } from '@angular/common/http';
import { Inject, Injectable, NgZone } from '@angular/core';

import { Inject, Injectable, NgZone, OnDestroy } from '@angular/core';
import { LumberjackLog, LumberjackLogDriver, LumberjackLogDriverLog, LumberjackLogPayload } from '@ngworker/lumberjack';

import { Subscription } from 'rxjs';
import { lumberjackHttpDriverConfigToken } from '../configuration/lumberjack-http-driver-config.token';
import { LumberjackHttpDriverInternalConfig } from '../configuration/lumberjack-http-driver-internal.config';
import { LumberjackHttpLog } from '../logs/lumberjack-http.log';
Expand All @@ -16,15 +15,22 @@ import { retryWithDelay } from '../operators/retry-with-delay.operator';
*/
@Injectable()
export class LumberjackHttpDriver<TPayload extends LumberjackLogPayload | void = void>
implements LumberjackLogDriver<TPayload>
implements LumberjackLogDriver<TPayload>, OnDestroy
{
static driverIdentifier = 'LumberjackHttpDriver';

private subscriptions = new Subscription();

constructor(
private readonly http: HttpClient,
@Inject(lumberjackHttpDriverConfigToken) readonly config: LumberjackHttpDriverInternalConfig,
private readonly ngZone: NgZone
) {}

ngOnDestroy(): void {
this.subscriptions.unsubscribe();
}

/**
* Send critical log to the log store.
*
Expand Down Expand Up @@ -95,12 +101,16 @@ export class LumberjackHttpDriver<TPayload extends LumberjackLogPayload | void =
const httpLog: LumberjackHttpLog<TPayload> = { formattedLog, origin, log };

this.ngZone.runOutsideAngular(() => {
this.http
.post<void>(storeUrl, httpLog)
.pipe(retryWithDelay(retryOptions.maxRetries, retryOptions.delayMs))
.subscribe(() => {
// No-op
});
this.subscriptions.add(
this.http
.post<void>(storeUrl, httpLog)
.pipe(retryWithDelay(retryOptions.maxRetries, retryOptions.delayMs))
// HTTP requests complete after
// eslint-disable-next-line rxjs-angular/prefer-composition
.subscribe(() => {
// No-op
})
);
});
}
}
Loading

0 comments on commit c0e3e64

Please sign in to comment.