🇪🇸 Lee la documentación en español aquí
scribĕre, to write in latin
Core logger library to be used in Javascript projects.
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 theLoggerStrategy
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 defaultConsoleLoggerStrategy
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.
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())
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:
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.
- 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,
})
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 */ } }]
...
}
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.
You will need Node (v10.15.3) and npm (v6.4.1) installed and Yarn. Upper versions could work but they aren't tested.
In the root of the project, you should execute:
yarn
yarn test
Under the hood, you will be executing jest so any parameter could be added following its documentation.
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
Please, read CONTRIBUTING.md which provides details about the code of conduct and the process to include pull requests.
The project uses SemVer for versioning. You can check all available versions in the tags of this repository.
- Ricardo Vega Alonso - ricveal