-
Notifications
You must be signed in to change notification settings - Fork 6
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
refactor: improve logging #1088
Changes from all commits
f899087
4abd415
3d00d57
a86dbce
aba2e18
7569141
651ae61
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,63 +1,67 @@ | ||
import { Inject, Injectable } from '@angular/core'; | ||
import { inject, Injectable } from '@angular/core'; | ||
import { | ||
datadogRum, | ||
RumFetchResourceEventDomainContext, | ||
} from '@datadog/browser-rum'; | ||
import { | ||
BuildTag, | ||
BuildTagToken, | ||
DspInstrumentationConfig, | ||
DspInstrumentationToken, | ||
} from '@dasch-swiss/vre/shared/app-config'; | ||
import { Session, SessionService } from './session.service'; | ||
import { environment } from '@dsp-app/src/environments/environment'; | ||
import { Observable } from 'rxjs'; | ||
import { first } from 'rxjs/operators'; | ||
|
||
@Injectable({ | ||
providedIn: 'root', | ||
}) | ||
export class DatadogRumService { | ||
constructor( | ||
@Inject(DspInstrumentationToken) | ||
private _c: DspInstrumentationConfig, | ||
private _s: SessionService | ||
) { | ||
if (this._c.dataDog.enabled) { | ||
datadogRum.init({ | ||
applicationId: this._c.dataDog.applicationId, | ||
clientToken: this._c.dataDog.clientToken, | ||
site: this._c.dataDog.site, | ||
service: this._c.dataDog.service, | ||
env: this._c.environment, | ||
version: environment.version, | ||
sessionSampleRate: 100, | ||
sessionReplaySampleRate: 100, // if not included, the default is 100 | ||
trackResources: true, | ||
trackLongTasks: true, | ||
trackUserInteractions: true, | ||
trackFrustrations: true, | ||
useSecureSessionCookie: true, | ||
beforeSend: (event, context) => { | ||
// collect a RUM resource's response headers | ||
if ( | ||
event.type === 'resource' && | ||
event.resource.type === 'xhr' | ||
) { | ||
event.context = { | ||
...event.context, | ||
responseHeaders: ( | ||
context as RumFetchResourceEventDomainContext | ||
).response?.body, | ||
}; | ||
} | ||
}, | ||
}); | ||
private buildTag$: Observable<BuildTag> = inject(BuildTagToken); | ||
private config: DspInstrumentationConfig = inject(DspInstrumentationToken); | ||
private session = inject(SessionService); | ||
constructor() { | ||
this.buildTag$.pipe(first()).subscribe((tag) => { | ||
if (this.config.dataDog.enabled) { | ||
datadogRum.init({ | ||
applicationId: this.config.dataDog.applicationId, | ||
clientToken: this.config.dataDog.clientToken, | ||
site: this.config.dataDog.site, | ||
service: this.config.dataDog.service, | ||
env: this.config.environment, | ||
version: tag.build_tag, | ||
sessionSampleRate: 100, | ||
sessionReplaySampleRate: 100, // if not included, the default is 100 | ||
trackResources: true, | ||
trackLongTasks: true, | ||
trackUserInteractions: true, | ||
trackFrustrations: true, | ||
useSecureSessionCookie: true, | ||
beforeSend: (event, context) => { | ||
// collect a RUM resource's response headers | ||
if ( | ||
event.type === 'resource' && | ||
event.resource.type === 'xhr' | ||
) { | ||
event.context = { | ||
...event.context, | ||
responseHeaders: ( | ||
context as RumFetchResourceEventDomainContext | ||
).response?.body, | ||
}; | ||
} | ||
}, | ||
}); | ||
|
||
// if session is valid: setActiveUser | ||
this._s.isSessionValid().subscribe((response: boolean) => { | ||
if (response) { | ||
const session: Session = this._s.getSession(); | ||
this.setActiveUser(session.user.name, 'username'); | ||
} | ||
}); | ||
} | ||
// if session is valid: setActiveUser | ||
this.session.isSessionValid().subscribe((response: boolean) => { | ||
if (response) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Where do you unsubscribe? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nowhere. This is already existing code and out of scope for this round. |
||
const session: Session = this.session.getSession(); | ||
this.setActiveUser(session.user.name, 'username'); | ||
} | ||
}); | ||
} | ||
}); | ||
} | ||
|
||
setActiveUser( | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
/* | ||
* Copyright © 2021 - 2023 Swiss National Data and Service Center for the Humanities and/or DaSCH Service Platform contributors. | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
import { InjectionToken } from '@angular/core'; | ||
import { BuildTag } from './build-tag'; | ||
import { Observable } from 'rxjs'; | ||
|
||
/** | ||
* The BuildTagToken is used to encapsulate build_tag | ||
* loaded from 'config/build.json' loaded in main.ts before bootstrap. | ||
* As such, the structure of the loaded JSON is at this point not checked. | ||
*/ | ||
export const BuildTagToken = new InjectionToken<Observable<BuildTag>>( | ||
'A stream with the current build tag' | ||
); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
/* | ||
* Copyright © 2021 - 2023 Swiss National Data and Service Center for the Humanities and/or DaSCH Service Platform contributors. | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
import { z } from 'zod'; | ||
import { inject } from '@angular/core'; | ||
import { HttpClient } from '@angular/common/http'; | ||
import { map } from 'rxjs/operators'; | ||
import { Observable } from 'rxjs'; | ||
|
||
export const BuildTagSchema = z.object({ | ||
build_tag: z.string().nonempty(), | ||
}); | ||
|
||
export type BuildTag = z.infer<typeof BuildTagSchema>; | ||
|
||
export function buildTagFactory(): Observable<BuildTag> { | ||
const httpClient = inject(HttpClient); | ||
|
||
return httpClient | ||
.get('/config/build.json') | ||
.pipe(map((buildTagValue) => BuildTagSchema.parse(buildTagValue))); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1 @@ | ||
export * from './lib/app-error-handler/app-error-handler'; | ||
export * from './lib/app-error-handler'; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,29 +1,31 @@ | ||
import { ErrorHandler, Injector } from '@angular/core'; | ||
/* | ||
* Copyright © 2021 - 2023 Swiss National Data and Service Center for the Humanities and/or DaSCH Service Platform contributors. | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
import { ErrorHandler, inject } from '@angular/core'; | ||
import { AppLoggingService } from '@dasch-swiss/vre/shared/app-logging'; | ||
import { HttpErrorResponse } from '@angular/common/http'; | ||
|
||
export class AppErrorHandler implements ErrorHandler { | ||
constructor(private _injector: Injector) {} | ||
|
||
logger = inject(AppLoggingService); | ||
/** | ||
* Logs out the error using the logging service. | ||
* @param error the error to log. | ||
*/ | ||
handleError(error: Error): void { | ||
const logger = this._injector.get(AppLoggingService); | ||
|
||
if (error instanceof HttpErrorResponse) { | ||
// HTTP related error | ||
logger.error('Caught HttpErrorResponse error', error); | ||
this.logger.error('Caught HttpErrorResponse error', error); | ||
} else if ( | ||
error instanceof TypeError || | ||
error instanceof ReferenceError | ||
) { | ||
// Runtime exceptions mostly induced by Developer's code | ||
logger.error('Caught Type or Reference error', error); | ||
this.logger.error('Caught Type or Reference error', error); | ||
} else { | ||
// catch-all: catch rest of errors | ||
logger.error('Caught error', error); | ||
this.logger.error('Caught error', error); | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,31 +1,45 @@ | ||
import { Inject, Injectable } from '@angular/core'; | ||
import { inject, Injectable } from '@angular/core'; | ||
import { datadogLogs, Logger } from '@datadog/browser-logs'; | ||
import { | ||
BuildTag, | ||
BuildTagToken, | ||
DspInstrumentationConfig, | ||
DspInstrumentationToken, | ||
} from '@dasch-swiss/vre/shared/app-config'; | ||
import { Observable } from 'rxjs'; | ||
|
||
import { first } from 'rxjs/operators'; | ||
|
||
@Injectable({ | ||
providedIn: 'root', | ||
}) | ||
export class AppLoggingService { | ||
private logger: Logger | undefined; | ||
constructor( | ||
@Inject(DspInstrumentationToken) private c: DspInstrumentationConfig | ||
) { | ||
if (c.dataDog.enabled && typeof c.dataDog.clientToken == 'string') { | ||
datadogLogs.init({ | ||
clientToken: c.dataDog.clientToken, | ||
site: c.dataDog.site, | ||
service: c.dataDog.service, | ||
env: c.environment, | ||
forwardErrorsToLogs: true, // forwards console.error logs, uncaught exceptions and network errors to Datadog | ||
forwardConsoleLogs: [], // don't forward any logs (besides console.error - in previous setting) to Datadog | ||
useSecureSessionCookie: true, | ||
}); | ||
datadogLogs.logger.setHandler(['console', 'http']); | ||
this.logger = datadogLogs.logger; | ||
} | ||
private buildTag$: Observable<BuildTag> = inject(BuildTagToken); | ||
private config: DspInstrumentationConfig = inject(DspInstrumentationToken); | ||
|
||
constructor() { | ||
this.buildTag$.pipe(first()).subscribe((tag) => { | ||
if ( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Where do you unsubscribe? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. first() |
||
this.config.dataDog.enabled && | ||
typeof this.config.dataDog.clientToken == 'string' | ||
) { | ||
datadogLogs.init({ | ||
clientToken: this.config.dataDog.clientToken, | ||
site: this.config.dataDog.site, | ||
service: this.config.dataDog.service, | ||
env: this.config.environment, | ||
version: tag.build_tag, | ||
forwardErrorsToLogs: true, // forwards console.error logs, uncaught exceptions and network errors to Datadog | ||
forwardConsoleLogs: [], // don't forward any logs (besides console.error - in previous setting) to Datadog | ||
useSecureSessionCookie: true, | ||
}); | ||
datadogLogs.logger.setHandler(['console', 'http']); | ||
this.logger = datadogLogs.logger; | ||
} | ||
this.info('config', this.config); | ||
this.info('build_tag', tag); | ||
}); | ||
} | ||
|
||
debug(message: string, messageContext?: object, error?: Error) { | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Where do you unsubscribe?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
first does it automatically after the first value is received.