@livy/util
v1.0.6
Published
Common utilities for the Livy logger
Downloads
846
Maintainers
Readme
@livy/util
This package contains common utilities for building components for the Livy logger.
Table of Contents:
General Purpose Utilities
Helpers
There's also a whole bunch general-purpose helper functions in this file. They are thoroughly documented, including examples, so you're probably best off to just dive into the source code.
Types
For TypeScript users, there are also some handy types to be found in this file.
Environment
Determines whether the runtime is Node.js or a browser and derives related environment-specific data.
const Environment = require('@livy/util/lib/environment')
Environment.isNodeJs // `true` or `false`
Environment.isBrowser // `true` or `false`
Environment.EOL // os.EOL in Node.js, '\n' otherwise
ValidatableSet
An extended Set
with the array methods some
and every
on top:
const { ValidatableSet } = require('@livy/util/lib/validatable-set')
const set = new ValidatableSet([1, 2, 3])
set.every(item => typeof item === 'number') // true
set.some(item => item < 0) // false
// Behaves like the corresponding array methods on empty sets:
set.clear()
set.every(anyCondition) // false
set.some(anyCondition) // true
GatedSet
An extended ValidatableSet
that allows to validate and reject values that go inside the set.
const { GatedSet } = require('@livy/util/lib/gated-set')
const integers = new GatedSet(value => {
if (typeof value !== 'number' || !Number.isInteger(value)) {
throw new TypeError('Set only accepts integers')
}
})
integers.add(5) // OK
integers.add(-50) // OK
integers.add(5.5) // Throws TypeError
integers.add(NaN) // Throws TypeError
integers.add('foo') // Throws TypeError
HTML
A function/template tag that allows you to easily construct HTML strings without having to worry about escaping:
const { HTML } = require('@livy/util/lib/html')
const snippet = '<em>awesome</em>'
HTML`<p>Markup is ${snippet}!</p>`.toString()
// '<p>Markup is <em>awesome</em>!</p>'
// Use HTML as template tag in interpolations to avoid escaping:
HTML`<p>Markup is ${HTML`<em>awesome</em>`}!</p>`.toString()
// '<p>Markup is <em>awesome</em>!</p>'
// Use HTML as a function to avoid escaping of variables:
HTML`<p>Markup is ${HTML(snippet)}!</p>`.toString()
// '<p>Markup is <em>awesome</em>!</p>'
isResettableInterface
Check whether a handler or processors is resettable.
See also:
ResettableInterface
Mixin
An approach to use TypeScript mixins slightly altered from Mixin classes.
Supports extending abstract classes and correctly type-checks super()
calls for the tradeoff of having to wrap the mixin in an additional function call:
// Mixin definition:
const WriteAccess = Mixin(
_ =>
class extends _ {
write(file: string, content: string) {
// Do some write action
}
}
)
// Mixin usage:
class User {
constructor(protected name: string) {}
}
class PrivilegedUser extends WriteAccess(User) {
constructor(name: string, protected role: 'editor' | 'admin') {
super(name) // <- type-hinted!
}
otherMethod() {
this.write('/some/file/path', 'some content') // <- type-hinted!
}
}
Timer
A cross-runtime performance timer implementation:
const { Timer } = require('@livy/util/lib/timer')
const timer = new Timer()
// Start the timer
timer.start()
// ... do some work ...
// Get number of milliseconds elapsed since timer.start()
timer.get()
// ... do some work ...
// Still get number of milliseconds elapsed since timer.start()
timer.get()
// Stop and reset the timer
timer.reset()
Formatter-Related Utilities
IncludedRecordProperties
A simple TypeScript type which represents an object with all LogRecord
properties mapped to boolean values.
This can be useful as type of a formatter's option that determines which parts of a log record should be represented in the formatted output — see for example the LineFormatter
implementation.
AbstractBatchFormatter
A base class for formatters to extend which implements formatBatch
as a series of format
calls, joined by a delimiter (which by default is the EOL
determined by the environment, can be changed by overriding the batchDelimiter
property):
const { AbstractBatchFormatter } = require('@livy/util/lib/abstract-batch-formatter')
class QuestionableFormatter extends AbstractBatchFormatter {
format(record) {
return '?'
}
}
const q = new QuestionableFormatter()
q.format({ ... }) // '?'
q.formatBatch([{ ... }, { ... }, { ... }]) // '?\n?\n?'
See also:
FormatterInterface
LineFomatter
This formatter — because it's very ubiquitous throughout the project — is implemented here to remove complexity from the dependency graph (i.e. to avoid a mutual dependency with @livy/line-formatter
).
See @livy/line-formatter
for documentation.
Handler-Related Utilities
This includes various base classes, mixins and check functions for implementing handlers.
isClosableHandlerInterface
Check whether a handler is closable.
isFormattableHandlerInterface
Checks whether a handler implements the FormattableHandlerInterface
isProcessableHandlerInterface
Check whether a handler can use processors.
isSyncHandlerInterface
Check whether a handler can be invoked synchronously.
MirrorSyncHandlerMixin
Implements the mandatory asynchronous handler methods handle
and handleBatch
by replicating the behavior of their corresponding synchronous methods:
const SomeBaseClass = require('SomeBaseClass')
const {
MirrorSyncHandlerMixin
} = require('@livy/util/lib/handlers/mirror-sync-handler-mixin')
class Handler extends MirrorSyncHandlerMixin(SomeBaseClass) {
handleSync(record) {
// ...
}
handleBatchSync(record) {
// ...
}
}
const handler = new Handler()
handler.handle() // calls handler.handleSync()
handler.handleBatch() // calls handler.handleBatchSync()
See also:
SyncHandlerInterface
ProcessableHandlerMixin
Makes a handler able to work with processors, implementing the ProcessableHandlerInterface
.
It adds:
- a public
processors
set. - an internal
processRecord
method which your handler may call with a log record to run all registered processors on it. - an internal
resetProcessors
method which resets all resettable processors. - a basic
reset
method which callsresetProcessors
, thus making the handler resettable
RespectLevelMixin
Adds a public level
property which defaults to 'debug'
, as well as a isHandling
implementation based on that level.
See also:
HandlerInterface
FormattableHandlerMixin
Adds a formatter
property to the applied-to class with support for an implicit default formatter:
const SomeBaseClass = require('SomeBaseClass')
const SomeFormatter = require('SomeFormatter')
const {
FormattableHandlerMixin
} = require('@livy/util/lib/handlers/formattable-handler-mixin')
class Handler extends FormattableHandlerMixin(SomeBaseClass) {
/**
* Allow setting a formatter through a handler option
*/
constructor({ formatter, ...options }) {
// Pass other options up
super(options)
// Set the `this.explicitFormatter` property.
// If the user provides no `formatter` option, it will be undefined
// and `this.formatter` will return the default formatter
this.explicitFormatter = formatter
}
/**
* Define the default formatter (required)
*/
get defaultFormatter() {
return new SomeFormatter()
}
handle(record) {
// ...
}
}
See also:
FormattableHandlerInterface
AbstractBatchHandler
This is a base handler class that implements the handleBatch
and handleBatchSync
methods by sequentially executing handle
/handleSync
for each passed record.
See also:
HandlerInterface
AbstractLevelBubbleHandler
Base handler class (extending the AbstractBatchHandler
) which adds a level
and a bubble
option and implements the isHandling
method based on the configured level:
const {
AbstractLevelBubbleHandler
} = require('@livy/util/lib/handlers/abstract-level-bubble-handler')
class Handler extends AbstractLevelBubbleHandler {
handle(record) {
// ...do something with the record...
// Indicate bubbling behavior based on the `bubble` option:
return !this.bubble
}
}
See also:
AbstractFormattingProcessingHandler
Base handler class extending the AbstractLevelBubbleHandler
with the FormattableHandlerMixin
and ProcessableHandlerMixin
. It completely abstracts the nitty gritty details of writing a handler with formatter and processor support away from you so you only have to implement a write
method:
const {
AbstractFormattingProcessingHandler
} = require('@livy/util/lib/handlers/abstract-formatting-processing-handler')
class FileHandler extends AbstractFormattingProcessingHandler {
async write(record, formattedRecord) {
await someFileHandler.write(formattedRecord)
}
}
There's also AbstractSyncFormattingProcessingHandler
to implement a synchronous handler by implementing writeSync
:
const {
AbstractSyncFormattingProcessingHandler
} = require('@livy/util/lib/handlers/abstract-formatting-processing-handler')
class SyncFileHandler extends AbstractSyncFormattingProcessingHandler {
writeSync(record, formattedRecord) {
someFileHandler.writeSync(formattedRecord)
}
// You can still implement your own asynchronous `write` method,
// but if you omit it, it will just fall back to `writeSync` instead
async write(record, formattedRecord) {
await someFileHandler.write(formattedRecord)
}
}