
Research
Two Malicious Rust Crates Impersonate Popular Logger to Steal Wallet Keys
Socket uncovers malicious Rust crates impersonating fast_log to steal Solana and Ethereum wallet keys from source code.
Initially Signales was forked form signale in order to solve issue with logging levels. It was solved via #105 MR merged in main codebase of Signales repository.
Besides that Signales:
You can't require directly signales instance in commonjs module. Code below DOESN'T work:
const signale = require('signales') // not ok
signale.error(`doesn't work`)
Instead, use ES imports or named imports:
const { signales } = require('signales') // ok
const { signale } = require('signale') // same as above
import signales from 'signales' // ok
import { Signales } from 'signales' // ok
import { Signale } from 'signales' // same as above
Hackable and configurable to the core, signales can be used for logging purposes, status reporting, as well as for handling the output rendering process of other node modules and applications.
Come over Twitter to share your thoughts on the project.
package.json
yarn add signales
npm install signales
Import signales and start using any of the default loggers.
await
complete
error
alert
debug
fatal
fav
info
note
pause
pending
star
start
success
wait
warn
watch
log
const { signale } = require('signales');
signale.success('Operation successful');
signale.debug('Hello', 'from', 'L59');
signale.pending('Write release notes for %s', '1.2.0');
signale.fatal(new Error('Unable to acquire lock'));
signale.watch('Recursively watching build directory...');
signale.complete({prefix: '[task]', message: 'Fix issue #59', suffix: '(@klauscfhq)'});
To create a custom logger define an options
object yielding a types
field with the logger data and pass it as argument to a new signale instance.
const { Signale } = require('signales');
const options = {
disabled: false,
interactive: false,
logLevel: 'debug',
scope: 'custom',
secrets: [],
stream: process.stdout,
types: {
remind: {
badge: '**',
color: 'yellow',
label: 'reminder',
logLevel: 'info'
},
santa: {
badge: '🎅',
color: 'red',
label: 'santa',
logLevel: 'info'
}
}
};
const custom = new Signale(options);
custom.remind('Improve documentation.');
custom.santa('Hoho! You have an unused variable on L45.');
Here is an example where we override the default error
and success
loggers.
const { Signale } = require('signales');
const options = {
types: {
error: {
badge: '!!',
label: 'fatal error'
},
success: {
badge: '++',
label: 'huge success'
}
}
};
const signale = new Signale();
signale.error('Default Error Log');
signale.success('Default Success Log');
const custom = new Signale(options);
custom.error('Custom Error Log');
custom.success('Custom Success Log');
The options
object can hold any of the following attributes: disabled
, interactive
, logLevels
, logLevel
, secrets
, stream
, scope
and types
.
disabled
Boolean
false
Disables the logging functionality of all loggers belonging to the created instance.
interactive
Boolean
false
Switches all loggers belonging to the created instance into the interactive mode.
logLevel
String
'debug'
Sets the general logging level of the created instance. Can be one of the following:
'debug'
- Displays all messages from all loggers.'info'
- Displays messages from all loggers except debug
level.'timer'
- Displays messages only from the time
, timeEnd
, warn
, error
& fatal
loggers.'warn'
- Displays messages only from the warn
, error
& fatal
loggers.'error'
- Displays messages only from the error
& fatal
loggers.logLevels
Object
Allows you to add or override log levels.
For example, a value of { silly: -1 }
will add a level of silly
, which will have even lower priority than debug
.
Default log levels are:
{
debug: 0,
info: 1,
timer: 2,
warn: 3,
error: 4
}
secrets
(String|Number)[]
[]
An array holding secrets/sensitive-information to be removed from the body and metadata of to-be-logged messages and replaced with the default '[secure]'
string.
stream
stream.Writable|stream.Writable[]
process.stderr
Destination to which the data is written, can be a single valid Writable stream or an array holding multiple valid Writable streams.
scope
String|String[]
Name of the scope the logger is reporting from.
scopeFormatter
(scopePath: string[]) => string
Formats array of scope to display representation.
types
Record<T, {
badge: string,
color: ChalkColor | '',
label: string,
logLevel?: LogLevel,
stream?: WritableStream | WritableStream[],
}>
Holds the configuration of the custom and default loggers.
Additionally, the configuration object of each custom/default logger type, defined in the types
option, can hold any of the following attributes: badge
, label
, color
, logLevel
& stream
.
badge
String
The icon corresponding to the logger.
label
String
The label used to identify the type of the logger.
color
String
The color of the label, can be any of the foreground colors supported by chalk.
logLevel
String
'debug'
The log level corresponding to the logger. Messages originating from the logger are displayed only if the log level is greater or equal to the above described general logging level logLevel
of the Signale
instance.
stream
stream.Writable|stream.Writable[]
process.stderr
Destination to which the data is written, can be a single valid Writable stream or an array holding multiple valid Writable streams.
To create a scoped logger from scratch, define the scope
field inside the options
object and pass it as argument to a new signale instance.
const { Signale } = require('signales');
const options = {
scope: 'global scope'
};
const global = new Signale(options);
global.success('Successful Operation');
To create a scoped logger based on an already existing one, use the scope()
function, which will return a new signale instance, inheriting all custom loggers, timers, secrets, streams, configuration, log level, interactive mode & disabled statuses from the initial one.
const { signale } = require('signales');
const global = signale.scope('global scope');
global.success('Hello from the global scope');
function foo() {
const outer = global.scope('outer', 'scope');
outer.success('Hello from the outer scope');
setTimeout(() => {
const inner = outer.scope('inner', 'scope');
inner.success('Hello from the inner scope');
}, 500);
}
foo();
You can also create a new child logger which will have a parent scope and a passed one.
const { signale } = require('signales');
const systemLogger = signale.scope('system')
systemLogger.success('Hello from system logger')
> [system] › ✔ success Hello from system logger
const childLogger = systemLogger.child('net')
childLogger.success('Hello from child logger')
> [system::net] › ✔ success Hello from child logger
To initialize an interactive logger, create a new signale instance with the interactive
attribute set to true
. While into the interactive mode, previously logged messages originating from an interactive logger, will be overridden only by new ones originating from the same or a different interactive logger. Note that regular messages originating from regular loggers are not overridden by the interactive ones.
const { Signale } = require('signales');
const interactive = new Signale({interactive: true, scope: 'interactive'});
interactive.await('[%d/4] - Process A', 1);
setTimeout(() => {
interactive.success('[%d/4] - Process A', 2);
setTimeout(() => {
interactive.await('[%d/4] - Process B', 3);
setTimeout(() => {
interactive.error('[%d/4] - Process B', 4);
setTimeout(() => {}, 1000);
}, 1000);
}, 1000);
}, 1000);
By default, all signale instances log their messages to the process.stderr
stream. This can be modified, to match your own preference, through the stream
property, where you can define a single or multiple valid Writable streams, which will be used by all logger types to log your data. Additionally, it is possible to define one or more Writable streams exclusively for a specific logger type, thus write data independently from the rest logger types.
const { Signale } = require('signales');
const options = {
stream: process.stderr, // All loggers will now write to `process.stderr`
types: {
error: {
// Only `error` will write to both `process.stdout` & `process.stderr`
stream: [process.stdout, process.stderr]
}
}
};
const signale = new Signale(options);
signale.success('Message will appear on `process.stderr`');
signale.error('Message will appear on both `process.stdout` & `process.stderr`');
By utilizing the secrets
option, secrets and other sensitive information can be filtered out from the body as well as the metadata, i.e. scope names etc, of to-be-logged messages. The option is part of the configuration object passed to a Signale
instance on its initialization, and is of type Array<String|Number>
. The array can hold multiple secrets, all of which are removed, if present, from the to-be-logged messages and are replaced with the default '[secure]'
string. Additionally, when the unary signale.scope(name)
function is used, the returned Signale
instance inherits all the secrets belonging to its parent. The secrets checking process is performed in a case-sensitive manner. Also, the unary signale.addSecrets()
and the nullary signale.clearSecrets()
functions are available through the API for adding and clearing secrets respectively.
It is critical and highly recommended to not type directly secrets in your code, thus the following example serves only as a simple & easily reproducible usage demonstration.
const { Signale } = require('signales');
// In reality secrets could be securely fetched/decrypted through a dedicated API
const [USERNAME, TOKEN] = ['klaussinani', 'token'];
const logger1 = new Signale({
secrets: [USERNAME, TOKEN]
});
logger1.log('$ exporting USERNAME=%s', USERNAME);
logger1.log('$ exporting TOKEN=%s', TOKEN);
// `logger2` inherits all secrets from its parent `logger1`
const logger2 = logger1.scope('parent');
logger2.log('$ exporting USERNAME=%s', USERNAME);
logger2.log('$ exporting TOKEN=%s', TOKEN);
Timer are managed by the time()
and timeEnd()
functions. A unique label can be used to identify a timer on initialization, though if none is provided the timer will be assigned one automatically. In addition, calling the timeEnd()
function without a specified label will have as effect the termination of the most recently initialized timer, that was created without providing a label.
const { signale } = require('signales');
signale.time('test');
signale.time();
signale.time();
setTimeout(() => {
signale.timeEnd();
signale.timeEnd();
signale.timeEnd('test');
}, 500);
To enable global configuration define the options under the signale
namespace in your package.json
.
The following illustrates all the available options with their respective default values.
{
"signale": {
"displayScope": true,
"displayBadge": true,
"displayDate": false,
"displayFilename": false,
"displayLabel": true,
"displayTimestamp": false,
"underlineLabel": true,
"underlineMessage": false,
"underlinePrefix": false,
"underlineSuffix": false,
"uppercaseLabel": false
}
}
displayScope
Boolean
true
Display the scope name of the logger.
displayBadge
Boolean
true
Display the badge of the logger.
displayDate
Boolean
false
Display the current local date in YYYY-MM-DD
format.
displayFilename
Boolean
false
Display the name of the file that the logger is reporting from.
displayLabel
Boolean
true
Display the label of the logger.
displayTimestamp
Boolean
false
Display the current local time in HH:MM:SS
format.
underlineLabel
Boolean
true
Underline the logger label.
underlineMessage
Boolean
false
Underline the logger message.
underlinePrefix
Boolean
false
Underline the logger prefix.
underlineSuffix
Boolean
false
Underline the logger suffix.
uppercaseLabel
Boolean
false
Display the label of the logger in uppercase.
To enable local configuration call the config()
function on your signales instance. Local configurations will always override any pre-existing configuration inherited from package.json
.
In the following example, loggers in the foo.js
file will run under their own configuration, overriding the package.json
one.
// foo.js
const { signale } = require('signales');
// Overrides any existing `package.json` config
signale.config({
displayFilename: true,
displayTimestamp: true,
displayDate: false
});
signale.success('Hello from the Global scope');
Also, scoped loggers can have their own independent configuration, overriding the one inherited by the parent instance or package.json
.
// foo.js
const { signale } = require('signales');
signale.config({
displayFilename: true,
displayTimestamp: true,
displayDate: false
});
signale.success('Hello from the Global scope');
function foo() {
// `fooLogger` inherits the config of `signale`
const fooLogger = signale.scope('foo scope');
// Overrides both `signale` and `package.json` configs
fooLogger.config({
displayFilename: true,
displayTimestamp: false,
displayDate: true
});
fooLogger.success('Hello from the Local scope');
}
foo();
<logger>(message[, message]|messageObj|errorObj)
logger
Function
Can be any default or custom logger.
message
String
Can be one or more comma delimited strings.
const { signale } = require('signales');
signale.success('Successful operation');
//=> ✔ success Successful operation
signale.success('Successful', 'operation');
//=> ✔ success Successful operation
signale.success('Successful %s', 'operation');
//=> ✔ success Successful operation
errorObj
Error Object
Can be any error object.
const { signale } = require('signales');
signale.error(new Error('Unsuccessful operation'));
//=> ✖ error Error: Unsuccessful operation
// at Module._compile (module.js:660:30)
// at Object.Module._extensions..js (module.js:671:10)
// ...
messageObj
Object
Can be an object holding the prefix
, message
and suffix
attributes, with prefix
and suffix
always prepended and appended respectively to the logged message
.
const { signale } = require('signales');
signale.complete({prefix: '[task]', message: 'Fix issue #59', suffix: '(@klaussinani)'});
//=> [task] ☒ complete Fix issue #59 (@klaussinani)
signale.complete({prefix: '[task]', message: ['Fix issue #%d', 59], suffix: '(@klaussinani)'});
//=> [task] ☒ complete Fix issue #59 (@klaussinani)
scope(name[, name])
Defines the scope name of the logger.
name
String
Can be one or more comma delimited strings.
const { signale } = require('signales');
const foo = signale.scope('foo');
const fooBar = signale.scope('foo', 'bar');
foo.success('foo');
//=> [foo] › ✔ success foo
fooBar.success('foo bar');
//=> [foo] [bar] › ✔ success foo bar
child(childScopeName)
Creates new child logger which inherits parent's scope.
childScopeName
String
Scope name of child logger.
const { signale } = require('signales');
const systemLogger = signale.scope('system')
systemLogger.success('Hello from system logger')
//=> [system] › ✔ success Hello from system logger
const childLogger = systemLogger.child('net')
childLogger.success('Hello from child logger')
//=> [system::net] › ✔ success Hello from child logger
unscope()
Clears the scope name of the logger.
const { signale } = require('signales');
const foo = signale.scope('foo');
foo.success('foo');
//=> [foo] › ✔ success foo
foo.unscope();
foo.success('foo');
//=> ✔ success foo
clone(options)
Creates clone of current signales instance with new options applied to it.
const { signales, Signale } = require('signales')
signales.clone({ scope: 'foo', scopeFormatter: Signale.barsScopeFormatter }).child('bar').info('hello')
//=> [foo] [bar] › ℹ info hello
config(settingsObj)
Sets the configuration of an instance overriding any existing global or local configuration.
settingsObj
Object
Can hold any of the documented options.
// foo.js
const { signale } = require('signales');
signale.config({
displayFilename: true,
displayTimestamp: true,
displayDate: true
});
signale.success('Successful operations');
//=> [2018-5-15] [11:12:38] [foo.js] › ✔ success Successful operations
time([, label])
String
Sets a timers and accepts an optional label. If none provided the timer will receive a unique label automatically.
Returns a string corresponding to the timer label.
label
String
Label corresponding to the timer. Each timer must have its own unique label.
const { signale } = require('signales');
signale.time();
//=> ▶ timer_0 Initialized timer...
signale.time();
//=> ▶ timer_1 Initialized timer...
signale.time('label');
//=> ▶ label Initialized timer...
timeEnd([, label])
Object
Deactivates the timer to which the given label corresponds. If no label is provided the most recent timer, that was created without providing a label, will be deactivated.
Returns an object {label, span}
holding the timer label and the total running time.
label
String
Label corresponding to the timer, each timer has its own unique label.
const { signale } = require('signales');
signale.time();
//=> ▶ timer_0 Initialized timer...
signale.time();
//=> ▶ timer_1 Initialized timer...
signale.time('label');
//=> ▶ label Initialized timer...
signale.timeEnd();
//=> ◼ timer_1 Timer run for: 2ms
signale.timeEnd();
//=> ◼ timer_0 Timer run for: 2ms
signale.timeEnd('label');
//=> ◼ label Timer run for: 2ms
disable()
Disables the logging functionality of all loggers belonging to a specific instance.
const { signale } = require('signales');
signale.success('foo');
//=> ✔ success foo
signale.disable();
signale.success('foo');
//=>
enable()
Enables the logging functionality of all loggers belonging to a specific instance.
const { signale } = require('signales');
signale.disable();
signale.success('foo');
//=>
signale.enable();
signale.success('foo');
//=> ✔ success foo
isEnabled()
Checks whether the logging functionality of a specific instance is enabled.
const { signale } = require('signales');
signale.success('foo');
//=> ✔ success foo
signale.isEnabled();
// => true
signale.disable();
signale.success('foo');
//=>
signale.isEnabled();
// => false
addSecrets(secrets)
Adds new secrets/sensitive-information to the targeted Signale instance.
secrets
(String|Number)[]
Array holding the secrets/sensitive-information to be filtered out.
const { signale } = require('signales');
signale.log('$ exporting USERNAME=%s', 'klaussinani');
//=> $ exporting USERNAME=klaussinani
signale.addSecrets(['klaussinani']);
signale.log('$ exporting USERNAME=%s', 'klaussinani');
//=> $ exporting USERNAME=[secure]
clearSecrets()
Removes all secrets/sensitive-information from the targeted Signale instance.
const { signale } = require('signales');
signale.addSecrets(['klaussinani']);
signale.log('$ exporting USERNAME=%s', 'klaussinani');
//=> $ exporting USERNAME=[secure]
signale.clearSecrets();
signale.log('$ exporting USERNAME=%s', 'klaussinani');
//=> $ exporting USERNAME=klaussinani
For more info on how to contribute to the project, please read the contributing guidelines.
cd signales
npm install
or yarn install
npm build
or yarn build
npm test
or yarn test
View in detail all the packages and repositories that are using Signales here.
FAQs
Highly configurable logging utility
The npm package signales receives a total of 50 weekly downloads. As such, signales popularity was classified as not popular.
We found that signales demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 2 open source maintainers collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Research
Socket uncovers malicious Rust crates impersonating fast_log to steal Solana and Ethereum wallet keys from source code.
Research
A malicious package uses a QR code as steganography in an innovative technique.
Research
/Security News
Socket identified 80 fake candidates targeting engineering roles, including suspected North Korean operators, exposing the new reality of hiring as a security function.