Skip to content

Librería _core_ logger para ser empleada en proyectos con tecnología Javascript.

Notifications You must be signed in to change notification settings

ricveal/scribere

Repository files navigation

scribere

Release Version License Downloads

🇪🇸 Lee la documentación en español aquí

scribĕre, to write in latin

Core logger library to be used in Javascript projects.

How to use

The main interface of the library is the Logger class which should be instantiated. The singleton pattern is not implemented so, in a shared context, you can use multiple instances working together.

const logger = new Logger()

The logger works with 4 severity levels:

const levels = ['log', 'debug', 'warn', 'error']

Each instance has 4 methods (one per severity level) which allows you logging messages.

Furthermore, the logger has 3 additional properties:

  • prefix: It is the fragment before the logging message. It could be a string or a function which returns a string.
  • severity: Indicates the severity level of the instance of the logger. It's a number indicating the index of the array of severity levels that is used as minimum:
    • 0 -> 'log', 'debug', 'warn', 'error'
    • 1 -> 'debug', 'warn', 'error'
    • 2 -> 'warn', 'error'
    • 3 -> 'error'
  • strategy: Defines how the trace is really managed. The idea is to provide flexibility to use the strategy provided by the core or create a custom one which allows you to cover your requirements, for example, upload the traces to some remote server. It is an instance of a class which implements the LoggerStrategy interface. There is more information about it later on the text.

These 3 properties can be defined when the logger is instantiated, through the constructor:

  • options: Defines the prefix and severity. By default:
{
  prefix: new Date().toISOString(),
  severity: 0
}
  • strategy: Defines the strategy, by default ConsoleLoggerStrategy which mainly prints on browser console the trace, adjusting the severity and formatting it with: PREFIX :: LEVEL :: data
const loggerWithOptions = new Logger(options)

These properties can be changed once the logger is instantiated because they are accesibles.

Custom strategies

The logger uses the Strategy pattern, delegating what to do in the defined strategy.

By default, the ConsoleLoggerStrategy strategy is offered but you can code your own strategies following your needs.

You have to create a class implementing the LoggerStrategy strategy which forces you to implement the trace method that will be executed by the log, debug, warn and error method of the logger when the configured severity allows the print of the trace.

class CustomStrategy implements LoggerStrategy {

  trace(
    data: any,
    level: 'log' | 'debug' | 'warn' | 'error',
    instance: ILogger
  ) {
    // Implementation
    const prefix =
      typeof instance.prefix === 'function'
        ? instance.prefix()
        : instance.prefix
    alert(`${level} : ${prefix} - ${data})
  }

}

Once the strategy is created, you would select it in the instance process of the logger:

const loggerWithCustomStrategy = new Logger(options, new CustomStrategy())

Use it with SPA frameworks

The design of scribere tries to create a simple logger, agnostic of the SPA framework used. Following, some examples of integrations with SPA frameworks are provided:

React

In React, the use of scribere is straightforward; you can import it directly in the components and utilities which would use it.

Following, an implementation where the severity is retrieved from local storage is shown, but it is completely opcional.

- src
|-- logger
  |-- index.js

index.js

import {Logger} from 'scribere'

const ReactLogger = new Logger()

/* Starting Extra optional configuration */
const severity = localStorage.getItem('LOGGER')
let severityLevel
if (severity === null) {
  localStorage.setItem('LOGGER', '2')
  severityLevel = 2
} else {
  severityLevel = Number(severity)
}
ReactLogger.severity = severityLevel
/* Ending Extra optional configuration */

export default ReactLogger

From this point, importing ReactLogger where a log is required would be enough. This strategy of "pre-configuring" the logger should be followed when a configuration differ from the default one.

Vue

- src
|-- main.js
|-- logger
  |-- index.js
  |-- install.js

index.js

import install from './install'

const Log4Vue = {installed: false}
Log4Vue.install = install

export default Log4Vue

install.js

import {Logger} from 'scribere'

export default function install(Vue, options = undefined) {
  if (this.installed) return
  const logger = options ? new Logger(options) : new Logger()
  // The logger will be accesible in this.$console
  Vue.prototype.$console = logger
  Vue.console = logger
  window.logger = logger
  this.installed = true
}

This implementation of the logger follows the "plugin" pattern so the plugin should be declared before the application starts.

main.js

import Vue from 'vue'
import VueLogger from './logger'

Vue.use(VueLogger)

new Vue({
  //... options
})

Optionally, you can use custom options:

Vue.use(VueLogger, {
  severity: 2,
})

Angular

Although Angular could follow similar strategies to other frameworks (specially React), I think the service pattern is the closest to the "Angular philosophy".

For that:

import {Injectable} from '@angular/core'
import {Logger, ILogger, LoggerOptions} from 'scribere'

@Injectable({
  providedIn: 'root',
})
export class LoggerService {
  private _instance: ILogger

  constructor() {
    const options: LoggerOptions = {
      severity: 2,
    }
    this._instance = new Logger(options)
  }

  debug(...args: any[]) {
    return this._instance.debug(...args)
  }
  error(...args: any[]) {
    return this._instance.error(...args)
  }
  log(...args: any[]) {
    return this._instance.log(...args)
  }
  warn(...args: any[]) {
    return this._instance.warn(...args)
  }
}

Even though this is the most simple implementation, you could want to inject the options instead of define them in the constructor. For that, I think the best way would be to use the inject mechanism of Angular and its "providers".

This documentation does not try to deep on any specific implementation for any framework but, as reference, the implementation should be something similar to:

export const LOGGER_CONFIG = new InjectionToken<LoggerOptions>('app.logger');

@Injectable({
  providedIn: 'root',
})
export class LoggerService {
  ...

  constructor(@Inject(LOGGER_CONFIG) options: LoggerOptions) {
    this._instance = new Logger(options);
  }

  ...

}
@NgModule({
...
providers: [LoggerService, { provide: LOGGER_CONFIG, useValue: { /* options */ } }]
...
}

Starting 🚀

Following these steps you will have a copy of the project working on your local machine.

Check Deployment to know how to deploy the project.

Pre-requirements 📋

You will need Node (v10.15.3) and npm (v6.4.1) installed and Yarn. Upper versions could work but they aren't tested.

Install 🔧

In the root of the project, you should execute:

yarn

Testing ⚙️

yarn test

Under the hood, you will be executing jest so any parameter could be added following its documentation.

Linting ⌨️

The project is using eslint to ensure the code quality as well as the standardization of code between developers.

yarn lint

You can try to fix lint errors automatically using:

yarn lint:fix

Built with 🛠️

Contributing 🖇️

Please, read CONTRIBUTING.md which provides details about the code of conduct and the process to include pull requests.

Versioning 📌

The project uses SemVer for versioning. You can check all available versions in the tags of this repository.

Authors ✒️

About

Librería _core_ logger para ser empleada en proyectos con tecnología Javascript.

Resources

Code of conduct

Stars

Watchers

Forks

Packages

No packages published