File

projects/common/lib/services/logger.service.ts

Extends

AbstractHttpService

Index

Properties
Methods
Accessors

Constructor

constructor(http: HttpClient)
Parameters :
Name Type Optional
http HttpClient No

Methods

Protected _log
_log(message: CommonLogMessage)

Log a message to Splunk. This is the main way to send logs and automatically includes meta-data. You do not need to subscribe to the response, as the service already does that. The input object must have an 'event' property set, everything else is optional.

Example:

this.logService.log({ event: 'submission', dateObj: new Date() });

Parameters :
Name Type Optional Description
message CommonLogMessage No

A JavaScript object, nesting is fine, with event property set.

Returns : any
Protected _logError
_logError(errorMessage: CommonLogMessage)
Parameters :
Name Type Optional
errorMessage CommonLogMessage No
Returns : any
Private _sendLog
_sendLog(message: CommonLogMessage)

Internal method to send logs to Splunk, includes meta-data except that's consistent across all requests, but not specific values like severity level.

Parameters :
Name Type Optional Description
message CommonLogMessage No

A JavaScript object or anything that can be toString()'d, like Date

Returns : any
Protected handleError
handleError(error: HttpErrorResponse)
Parameters :
Name Type Optional
error HttpErrorResponse No
Returns : any
Public log
log(message: any)
Parameters :
Name Type Optional
message any No
Returns : void
Public logError
logError(errorMessage: any)
Parameters :
Name Type Optional
errorMessage any No
Returns : void
Public logHttpError
logHttpError(error: HttpErrorResponse)

Log HTTP errors, e.g. when losing network connectivity or receiving an error response code.

Parameters :
Name Type Optional
error HttpErrorResponse No
Returns : any
Private setSeverity
setSeverity(severity: SeverityLevels)
Parameters :
Name Type Optional
severity SeverityLevels No
Returns : void
Private setTags
setTags(message: string)

The headers are easier to search in splunk, and we aren't using tags, so repurpose it to event type.

Parameters :
Name Type Optional
message string No
Returns : void
Private setTimestamp
setTimestamp()
Returns : void
setURL
setURL(newURL: string)
Parameters :
Name Type Optional
newURL string No
Returns : void
Protected generateUUID
generateUUID()
Inherited from AbstractHttpService
Returns : any
Protected get
get(url, queryParams?: HttpParams)
Inherited from AbstractHttpService
Type parameters :
  • T

Makes a GET request to the specified URL, using headers and HTTP options specified in their respective methods.

Parameters :
Name Type Optional Description
url No

Target URL to make the GET request

queryParams HttpParams Yes
Returns : Observable<T>
Protected Abstract handleError
handleError(error: HttpErrorResponse)
Inherited from AbstractHttpService

Handles all failed requests that throw either a server error (400/500) or a client error (e.g. lost internet).

Parameters :
Name Type Optional
error HttpErrorResponse No
Returns : any
Protected post
post(url, body)
Inherited from AbstractHttpService
Type parameters :
  • T
Parameters :
Name Optional
url No
body No
Returns : Observable<T>
Protected setupRequest
setupRequest(observable: Observable)
Inherited from AbstractHttpService
Type parameters :
  • T
Parameters :
Name Type Optional
observable Observable<any> No
Returns : Observable<T>
Protected uploadAttachment
uploadAttachment(relativeUrl: string, attachment: CommonImage)
Inherited from AbstractHttpService

Uploads an individual attachment. All you need to do is set the url. Note: urls often include UUIDs, so this must be an application decision.

Parameters :
Name Type Optional Description
relativeUrl string No

URL to hit, must include UUIDs of application and CommonImage

attachment CommonImage No

CommonImage to upload

Returns : any

Properties

Protected _headers
Type : HttpHeaders
Default value : new HttpHeaders({ request_method: 'POST', logsource: window.location.hostname, http_x_forwarded_host: window.location.hostname })

The HTTP Headers which go with each request. These MUST be set if you are using the logger. Fields include:

  • program (REQUIRED, the application wide code)
  • applicationId (REQUIRED, like sessionId)
  • request_method (REQUIRED, 'POST')
  • logsource: (REQUIRED, window.location.hostname)
  • http_x_forwarded_host (REQUIRED, window.location.hostname)
Private url
Type : string
Default value : null
Protected Abstract _headers
Type : HttpHeaders
Inherited from AbstractHttpService

The headers to send along with every GET and POST.

Protected logHTTPRequestsToConsole
Type : boolean
Default value : false
Inherited from AbstractHttpService

Accessors

applicationId
getapplicationId()
setapplicationId(id: string)
Parameters :
Name Type Optional
id string No
Returns : void
programName
getprogramName()
setprogramName(name: string)
Parameters :
Name Type Optional
name string No
Returns : void
httpOptions
gethttpOptions()

Overwrite the inherited httpOptions so we can set responseType to text. This updates Angular's parsing, and it won't error out due to the server not responding with JSON.

Returns : any
import { Injectable } from '@angular/core';
import { HttpClient, HttpErrorResponse, HttpHeaders } from '@angular/common/http';
import { AbstractHttpService } from './abstract-api-service';
import { throwError } from 'rxjs';
import * as moment_ from 'moment';
const moment = moment_;

enum SeverityLevels {
  INFO = 'info',
  ERROR = 'error',
}

export enum CommonLogEvents {
  navigation = 'navigation',
  error = 'error',
  submission = 'submission'
}

export interface CommonLogMessage {
  /** The type of event being logged. */
  event: string; // Should be subclasses into multiple string literals
  // We allow any other properties/values in the interface
  [key: string]: any;
}

@Injectable({
  providedIn: 'root'
})
export class CommonLogger extends AbstractHttpService {
  /**
   * The HTTP Headers which go with each request.  These MUST be set if you are
   * using the logger.  Fields include:
   *
   * - program (REQUIRED, the application wide code)
   * - applicationId (REQUIRED, like sessionId)
   * - request_method (REQUIRED, 'POST')
   * - logsource: (REQUIRED, window.location.hostname)
   * - http_x_forwarded_host (REQUIRED, window.location.hostname)
   *
   */
  protected _headers: HttpHeaders = new HttpHeaders({
    request_method: 'POST',
    logsource: window.location.hostname,
    http_x_forwarded_host: window.location.hostname
  });

  private url: string = null;

  constructor(protected http: HttpClient) {
    super(http);
  }

  set applicationId( id: string ) {
    this._headers = this._headers.set( 'applicationId', id );
  }

  get applicationId() {
    return this._headers.get( 'applicationId' );
  }

  set programName( name: string ) {
    this._headers = this._headers.set( 'program', name );
  }

  get programName() {
    return this._headers.get( 'name' );
  }

  setURL(newURL: string) {
    this.url = newURL;
  }

  public log( message: any ) {
    this._log( message as CommonLogMessage );
  }

  public logError( errorMessage: any ) {
    this._logError( errorMessage as CommonLogMessage );
  }

  /**
   * Log a message to Splunk. This is the main way to send logs and
   * automatically includes meta-data. You do **not** need to subscribe to the
   * response, as the service already does that. The input object must have an
   * 'event' property set, everything else is optional.
   *
   * Example:
   * ```
    this.logService.log({
       event: 'submission',
       dateObj: new Date()
    });
    ```
   * @param message A JavaScript object, nesting is fine, with `event` property
   * set.
   */
  protected _log(message: CommonLogMessage) {
    this.setSeverity(SeverityLevels.INFO);
    return this._sendLog(message);
  }

  protected _logError(errorMessage: CommonLogMessage) {
    this.setSeverity(SeverityLevels.ERROR);
    return this._sendLog(errorMessage);
  }

  /**
   * Log HTTP errors, e.g. when losing network connectivity or receiving an
   * error response code.
   */
  public logHttpError(error: HttpErrorResponse) {
    return this._logError({
      event: CommonLogEvents.error,
      message: error.message,
      errorName: error.name,
      statusText: error.statusText
    });
  }

  /**
   * Internal method to send logs to Splunk, includes meta-data except that's
   * consistent across all requests, but not specific values like severity
   * level.
   *
   * @param message A JavaScript object or anything that can be toString()'d,
   * like Date
   */
  private _sendLog(message: CommonLogMessage) {
    // Update headers
    this.setTimestamp();
    this.setTags(message.event);

    if (this.url === null) {
        const msg = 'Unable to send logs as URL as not been set via setURL()';
        console.error(msg);
        return throwError(msg);
    }

    // Configure request
    const body = { message: message };

    // We call .subscribe() here because we don't care about the response and
    // we want to ensure that we never forget to call subscribe.
    return this.post(this.url, body).subscribe();
  }

  protected handleError(error: HttpErrorResponse) {
    if (error.error instanceof ErrorEvent) {
      // Client-side / network error occured
      console.error('An error occured: ', error.error.message);
    } else {
      // The backend returned an unsuccessful response code
      console.error(`Backend returned error code: ${error.status}.  Error body: ${error.error}`);
    }

    return throwError(error);
  }

  /**
   * Overwrite the inherited httpOptions so we can set responseType to text.
   * This updates Angular's parsing, and it won't error out due to the server
   * not responding with JSON.
   */
  protected get httpOptions(): any {
    return {
      headers: this._headers,
      responseType: 'text'
    };
  }

  // TODO: Remove moment dependency
  private setTimestamp() {
    this._headers = this._headers.set('timestamp', moment().toISOString());
  }

  private setSeverity(severity: SeverityLevels) {
    this._headers = this._headers.set('severity', severity);
  }

  /**
   * The headers are easier to search in splunk, and we aren't using tags, so
   * repurpose it to event type.
   */
  private setTags(message: string ) {
    this._headers = this._headers.set('tags', message);
  }

}

result-matching ""

    No results matching ""