1
0
mirror of https://github.com/dzeiocom/libs.git synced 2025-04-22 10:52:11 +00:00

Updated Logger implement Console

This commit is contained in:
Florian Bouillon 2020-07-07 11:13:35 +02:00
parent e0303a5abe
commit 70da3f2002
8 changed files with 522 additions and 40 deletions

View File

@ -1,8 +1,8 @@
dist/*.d.ts.map
node_modules
src
.gitignore
webpack.config.js
tsconfig.json
test
.babelrc
.eslintrc.js
.gitignore
webpack.config.js
tsconfig.*

41
packages/logger/README.md Normal file
View File

@ -0,0 +1,41 @@
# @dzeio/logger
A Better logger for your test
## Install
For the browser via `unpkg` use it from Unpkg or download the file locally
```html
<script src="https://unpkg.com/@dzeio/logger/dist/browser.js"></script>
<script>
// Two Elements are available
var logger = new Logger('prefix') // To initialize a new Logger
// or
initConsole() // this will repace the default console by the logger
</script>
```
Via Yarn/NPM
```bash
yarn add @dzeio/logger
# or
npm i @dzeio/logger
```
## Usage
As the Logger Implements the `console` the usage don't vary on their function BUT you have more control
ex:
```js
import Logger from '@dzeio/logger' // Import the class
const logger = new Logger('prefix') // initialize it
// or
import { logger } from '@dzeio/logger' // Import already initialized one
// You can block Logging for some time
Logger.isBlocked(true) // false to unblock
```

View File

@ -13,7 +13,7 @@
"browser": "./dist/browser.js",
"types": "./dist/Logger.d.ts",
"dependencies": {
"chalk": "^4.1.0"
"ansi-colors": "^4.1.1"
},
"devDependencies": {
"@babel/core": "^7.10.3",
@ -29,6 +29,7 @@
},
"scripts": {
"prepublishOnly": "yarn build",
"build": "webpack --mode=\"production\" && tsc"
"build": "webpack --mode=\"production\" && tsc",
"test": "ts-node test"
}
}

View File

@ -1,54 +1,372 @@
import { white, blue, yellow, green } from 'chalk'
import colors, { black, white, blue, yellow, green } from 'ansi-colors'
import { theme, logType, ObjectArray } from './types'
/**
* Logger Class
*/
export default class Logger {
export default class Logger implements Console {
/**
* If it is set to true all message won't be shown until it is set to false
* The maximun prefix length
*/
public static isBlocked = false
private static queue: Array<Array<any>> = []
private static prefixLen = 0
/**
* Log a message into the console
* @param prefix the prefix used
* @param message the message to log
* Current theme in use
*
* - undefined = detect theme
* - light = theme for light background
* - dark = theme for dark background
*/
public static log(prefix: string, ...message: Array<any>) {
this.queue.push(this.formatMessage(prefix, ...message))
while (this.queue.length > 0 && !this.isBlocked) {
const item = this.queue.shift()
if (!item) {
continue
}
console.log(...item)
private static theme: theme = undefined
/**
* List of loggers currently in use
*/
private static loggers: Array<Logger> = []
/**
* Console memory
* (IDK what it is)
*/
public memory = console.memory
// NodeJS console (will be undefined on )
/**
* NODEJS ONLY
* It will be undefined in Browser context
*/
// @ts-ignore
public Console = console.Console
/**
* The messages queue
*/
private queue: Array<{type: logType, msg: Array<any>}> = []
/**
* If true message won't be shown to console
* (`urgent` message will still show)
*/
private blocked = false
/**
* `count` function object value
*/
private countCount: ObjectArray<number> = {}
/**
* `time` function object value
*/
private timeCount: ObjectArray<Date> = {}
/**
* Construct a new Logger
* @param prefix the prefix shown
*/
public constructor(
public prefix: string = 'Logger'
) {
Logger.loggers.push(this)
// If Firefox disable colors
// @ts-ignore
if (typeof InstallTrigger !== 'undefined') {
colors.enabled = false
}
}
/**
* Log a message into the console (passthrough the `Logger.isBlocked` boolean)
* @param prefix The prefix used
* @param message the message to log
* STATIC FUNCTIONS
*/
public static urgent(prefix: string, ...message: Array<any>) {
console.log(...this.formatMessage(prefix, ...message))
/**
* Choose if every loggers should be blocked or not
* @param value
*/
public static isBlocked(value: boolean) {
for (const lgr of this.loggers) {
lgr.blocked = value
if (!value) {
lgr.processQueue()
}
}
}
private static formatMessage(prefix: string, ...message: Array<any>): Array<any> {
if (this.prefixLen < prefix.length) {
this.prefixLen = prefix.length
/**
* Force a specific theme to be used
*
* (if undefined it will try to detect it)
*
* @param themeChosen The theme chosen to be overriden
*/
public static forceTheme(themeChosen: theme) {
this.theme = themeChosen
}
const els: Array<string> = ['', '']
if (this.prefixLen > prefix.length) {
const diff = this.prefixLen - prefix.length
els[0] = this.buildSpace(diff / 2 - (diff % 2 !== 0 ? 1 : 0))
els[1] = this.buildSpace(diff / 2)
/**
* INSTANCE FUNCTIONS
*/
/**
* return nothing if `condition` is `true`, log `data` if it is not
* @param condition The value tested for being truthy
* @param data The message to pass if `condition` is false
*/
public assert(condition?: boolean | undefined, ...data: Array<any>): void {
if (!condition) {
return this.error('Assertion failed:', ...data)
}
}
/**
* Clear the console/terminal
*/
public clear(): void {
console.clear()
}
/**
* log the number of times `count` has been called
* @param label Display label, default to `default`
*/
public count(label: string = 'default'): void {
if (!Object.prototype.hasOwnProperty.call(this.countCount, label)) {
this.countCount[label] = 0
}
this.log(`${label}:`, ++this.countCount[label])
}
/**
* Reset the count started with `count` function
* @param label Display label, default to `default`
*/
public countReset(label: string = 'default'): void {
delete this.countCount[label]
}
/**
* log as debug the `data`
* (On browser like Chrome in the devtool you have to enable it beforehand)
* @param data the data to log
*/
public debug(...data: Array<any>): void {
this.process('debug', data)
}
/**
* NOTE: Method not fully implemented
*
* print a full object to the logger
* @param item Object to log
* @param options (NodeJS only) option for the display
*/
public dir(item?: any, options?: {showHidden?: boolean, depth?: number, colors?: boolean}): void {
if (typeof item !== 'object') {
return this.log(item)
}
this.log('Method not fully implemented')
if (typeof window !== 'undefined') {
console.dir(item)
return
}
console.dir(item, options)
}
/**
* Alias for `log` function
* @param data data to log
*/
public dirxml(...data: Array<any>): void {
this.process('dirxml', data)
}
/**
* print to stderr on NodeJS, print with red coloring on browsers
* @param data data to log
*/
public error(...data: Array<any>): void {
this.process('error', data)
}
/**
* Alias to `error` function
* @param data data to log
*/
public exception(...data: Array<any>): void { // NOT STANDARD / FIREFOX ONLY
this.error(...data)
}
/**
* NodeJS -> Increase the indentation of future logs with `label` as header
*
* Browser -> Group futures logs
* @param label labels to use as header
*/
public group(...label: Array<any>): void {
console.group(...label)
}
/**
* Alias of `group` function
* @param label label to use as header
*/
public groupCollapsed(...label: Array<any>): void {
this.group(...label)
}
/**
* End the current group started with the `group` function
*/
public groupEnd(): void {
console.groupEnd()
}
/**
* Log to console as Info
* (Only Chrome else it is an alias of `log`)
* @param data data to log
*/
public info(...data: Array<any>): void {
this.process('info', data)
}
/**
* Log to console
* @param data data to log
*/
public log(...data: Array<any>) {
this.process('log', data)
}
/**
* make a new profile in browsers or in NodeJS with `--inspect` flag
* @param name Profile name
*/
public profile(name?: string) {
// @ts-ignore
if (console.profile) {
// @ts-ignore
console.profile(name)
} else {
throw new Error('profile don\'t exist in the current contexte')
}
}
/**
* end the current running profile
* @param name Profile name
*/
public profileEnd(name?: string) {
// @ts-ignore
console.profileEnd(name)
}
/**
* Print a Table to the console
* @param tabularData Table data
* @param properties Table properties
*/
public table(tabularData?: any, properties?: Array<string> | undefined): void {
console.table(tabularData, properties)
}
/**
* Start a timer
* @param label Timer label
*/
public time(label: string = 'default'): void {
if (!Object.prototype.hasOwnProperty.call(this.timeCount, label)) {
this.timeCount[label] = new Date()
return
}
this.warn(`Timer '${label}' already exists.`)
}
/**
* End a timer
* @param label Timer label
*/
public timeEnd(label: string = 'default'): void {
const diff = (new Date()).getTime() - this.timeCount[label].getTime()
this.log(`${label}: ${diff}ms - Timer ended`)
delete this.timeCount[label]
}
/**
* Log to the console with timer before it
* @param label Timer label
* @param data data to log next to the timer
*/
public timeLog(label: string = 'default', ...data: Array<any>): void {
if (!this.timeCount[label]) {
this.warn(`Timer '${label}' does not exist`)
return
}
const diff = (new Date()).getTime() - this.timeCount[label].getTime()
this.log(`${label}: ${diff}ms`, ...data)
}
/**
* Browsers only, Add maker in the browser's Performance or Waterfall tool
* @param label Label to use
*/
public timeStamp(label?: string): void {
console.timeStamp(label)
}
/**
* Print a stack trace to console
* @param data data to trace
*/
public trace(...data: Array<any>): void {
this.process('trace', data)
}
/**
* Warn in the console
* (in NodeJS context it is an alias to the `error` function)
* @param data data to log
*/
public warn(...data: Array<any>): void {
this.process('warn', data)
}
/**
* Log to the console but skiping `isBlocked` check
* @param data data to log
*/
public urgent(...data: Array<any>) {
console.log(...this.formatMessage(data))
}
/**
* Precoess a message sent by one of the public functions
* @param type logType
* @param message message to log
*/
private process(type: logType, message: Array<any>) {
this.queue.push({type, msg: this.formatMessage(message)})
this.processQueue()
}
/**
* Format a message for the console
* @param message message to format
*/
private formatMessage(message: Array<any>): Array<any> {
const prefix = this.prefix
const prefixLen = prefix.length
if (Logger.prefixLen < prefixLen) {
Logger.prefixLen = prefixLen
}
const spacers: Array<string> = ['', '']
if (Logger.prefixLen > prefixLen) {
const diff = Logger.prefixLen - prefixLen
const diff2 = diff /2
spacers[0] = this.buildSpace(diff2 - (diff % 2 !== 0 ? 1 : 0))
spacers[1] = this.buildSpace(diff2)
}
const res: Array<any> = [
`${white('[ ')}${els[0]}${blue(prefix)}${els[1]}${white(' ]')}:` // prefix
`${this.blackOrWhite('[ ')}${spacers[0]}${blue(prefix)}${spacers[1]}${this.blackOrWhite(' ]')}:`
].concat(
message.map((el) => {
if (typeof el === 'object') {
@ -60,11 +378,48 @@ export default class Logger {
return res
}
private static buildSpace(count: number): string {
/**
* Process Waiting queue
*/
private processQueue() {
while (this.queue.length > 0 && !this.blocked) {
const item = this.queue.shift()
if (!item) {
continue
}
if (item.type === 'trace') {
console.log(item.msg.shift())
}
console[item.type](...item.msg)
}
}
/**
* Build a new string of `count` spaces
* @param count number of spaces to add
*/
private buildSpace(count: number): string {
let str = ''
for(let i = 0; i < count; i++) {
str += ' '
}
return str
}
/**
* Color the text in black or white depending on `theme` variable or detection
* @param text the text to color
*/
private blackOrWhite(text: string): string {
if ((!Logger.theme && typeof window !== 'undefined') || Logger.theme === 'light') {
return black(text)
}
return white(text)
}
}
/**
* Export a default Logger
*/
export const logger = new Logger()

View File

@ -2,5 +2,13 @@ import Logger from './Logger'
// Browser Import
/**
* Init Logger in global context and add function to replace default console
*/
// @ts-ignore
window.Logger = Logger
// @ts-ignore
window.initConsole = function() {
window.console = new Logger('Console')
}

View File

@ -0,0 +1,17 @@
/**
* Object Array
*/
export interface ObjectArray<T = any> {
[key: string]: T
}
/**
* used Internally,
* the different types of logs
*/
export type logType = 'debug' | 'dir' | 'dirxml' | 'error' | 'info' | 'log' | 'trace' | 'warn'
/**
* The possible themes
*/
export type theme = undefined | 'light' | 'dark'

View File

@ -0,0 +1,54 @@
import { logger as console } from '../src/Logger'
/**
* This test file is simple :D
*/
// Should remove the precedent line
console.clear()
// Start a timer
console.time()
console.timeLog(undefined, 'Started Timer')
// Test assert
console.log('Assert')
console.assert(true, 'i am true') // should print nothing
console.assert(false, 'i am false') // Should print 'i am false'
// all the different logs
console.debug('debug')
console.dir('dir')
console.dirxml('dirxml')
console.error('error')
console.info('info')
console.log('log')
console.trace('trace')
console.warn('warn')
// Timelog
console.timeLog(undefined, 'timeLog')
console.group('New group')
console.group('New group')
console.group('New group')
console.group('New group')
console.group('New group')
console.group('New group')
console.group('New group')
console.group('New group')
console.log('Log in a group')
console.groupEnd()
console.groupEnd()
console.groupEnd()
console.groupEnd()
console.groupEnd()
console.groupEnd()
console.groupEnd()
console.groupEnd()
console.groupEnd()
console.table([{ a: 1, b: 'Y' }, { a: 'Z', b: 2 }], ['a'])
// TimeEnd
console.timeEnd()

View File

@ -0,0 +1,6 @@
{
"extends": "./tsconfig.json",
"files": [
"test/index.ts"
]
}