@stringstack/core
Advanced tools
| 'use strict'; | ||
| const TestBase = require( './class.base' ); | ||
| class TestLogA extends TestBase { | ||
| constructor( deps ) { | ||
| super( deps, 'TestLogA' ); | ||
| this._logger = deps.get( 'logger' ); | ||
| this._logger( 'info', 'TestLogA constructor', { meta: 'data' } ); | ||
| this._logb = deps.get( './test/lib/class.log.b' ); | ||
| } | ||
| init( done ) { | ||
| this._logger( 'debug', 'TestLogA init', { meta: 'data' } ); | ||
| this._logger( 'warn', 'TestLogA init warn' ); | ||
| this._logger( 'warning', 'TestLogA init warning' ); | ||
| super.init( done ); | ||
| } | ||
| dinit( done ) { | ||
| this._logger( 'debug', 'TestLogA dinit', { meta: 'data' } ); | ||
| super.dinit( done ); | ||
| } | ||
| } | ||
| module.exports = TestLogA; |
| 'use strict'; | ||
| const TestBase = require( './class.base' ); | ||
| class TestLogB extends TestBase { | ||
| constructor( deps ) { | ||
| super( deps, 'TestLogB' ); | ||
| this._logger = deps.get( 'logger' ); | ||
| this._logger( 'info', 'TestLogB constructor', { meta: 'data' } ); | ||
| this._logger( 'verbose', 'TestLogB constructor', { meta: 'data' } ); | ||
| } | ||
| init( done ) { | ||
| this._logger( 'debug', 'TestLogB init', { meta: 'data' } ); | ||
| super.init( done ); | ||
| } | ||
| dinit( done ) { | ||
| this._logger( 'debug', 'TestLogB dinit', { meta: 'data' } ); | ||
| this._logger( 'information', 'TestLogB information' ); | ||
| super.dinit( done ); | ||
| } | ||
| } | ||
| module.exports = TestLogB; |
+12
-1
@@ -5,3 +5,3 @@ 'use strict'; | ||
| constructor( path, loader ) { | ||
| constructor( path, loader, logger ) { | ||
@@ -14,5 +14,16 @@ // the path of the module that got this loader | ||
| this._logger = logger; | ||
| } | ||
| get( path ) { | ||
| if ( path === 'logger' ) { | ||
| return ( level, message, meta ) => { | ||
| this._logger( level, this._path, message, meta ); | ||
| }; | ||
| } | ||
| return this._loader.get( this._path, path ); | ||
@@ -19,0 +30,0 @@ } |
+50
-15
| 'use strict'; | ||
| const async = require( 'async' ); | ||
| const Loader = require( './loader' ); | ||
@@ -9,6 +8,4 @@ const nconfProvider = require( 'nconf' ).Provider; | ||
| constructor( config ) { | ||
| constructor() { | ||
| this._config = config; | ||
| } | ||
@@ -18,10 +15,2 @@ | ||
| let appConfig = config; | ||
| if ( this._config ) { | ||
| appConfig = this._config; | ||
| } else if ( !appConfig ) { | ||
| appConfig = {}; | ||
| } | ||
| class App { | ||
@@ -33,5 +22,49 @@ | ||
| this._config = appConfig; | ||
| this._loader = new Loader(); | ||
| if ( typeof config.log !== 'function' ) { | ||
| config.log = () => { | ||
| // NO-OP | ||
| }; | ||
| } | ||
| this._logger = ( level, component, message, meta ) => { | ||
| if ( typeof level !== 'string' ) { | ||
| return; | ||
| } | ||
| // normalize log levels | ||
| level = level.toLowerCase(); | ||
| switch ( level ) { | ||
| case 'emergency': | ||
| level = 'emerg'; | ||
| break; | ||
| case 'critical': | ||
| level = 'crit'; | ||
| break; | ||
| case 'err': | ||
| level = 'error'; | ||
| break; | ||
| case 'warn': | ||
| level = 'warning'; | ||
| break; | ||
| case 'information': | ||
| level = 'info'; | ||
| break; | ||
| } | ||
| try { | ||
| config.log( level, component, message, meta ); | ||
| } catch ( e ) { | ||
| console.error( 'user log method threw an exception', e ); | ||
| // re-throw this bad boy, let it all burn! | ||
| throw e; | ||
| } | ||
| }; | ||
| this._loader = new Loader( this._logger ); | ||
| this._loader.set( 'app', 'env', env ); | ||
@@ -42,3 +75,3 @@ | ||
| let rootComponents = this._config.rootComponents; | ||
| let rootComponents = config.rootComponents; | ||
@@ -54,2 +87,4 @@ if ( !Array.isArray( rootComponents ) ) { | ||
| this._logger( 'notice', 'app', 'instantiated' ); | ||
| } | ||
@@ -56,0 +91,0 @@ |
+48
-6
@@ -9,4 +9,6 @@ 'use strict'; | ||
| constructor() { | ||
| constructor( logger ) { | ||
| this._logger = logger; | ||
| this._instantiateCurrent = null; | ||
@@ -23,2 +25,4 @@ this._instantiateStack = []; | ||
| this._logger( 'notice', 'loader', 'begin initializing components' ); | ||
| // clone then reverse the initialize stack | ||
@@ -33,2 +37,3 @@ let components = this._initializeStack.slice( 0 ).reverse(); | ||
| if ( !component ) { | ||
| this._logger( 'warn', 'loader', 'possible bug in stringstack/core, component not found in init stack ' + path ); | ||
| return done(); | ||
@@ -44,2 +49,3 @@ } | ||
| if ( component.initialized ) { | ||
| this._logger( 'info', 'loader', 'initialized component ' + path ); | ||
| return done(); | ||
@@ -59,2 +65,3 @@ } | ||
| this._logger( 'info', 'loader', 'initialized component ' + path ); | ||
| return done(); | ||
@@ -69,2 +76,3 @@ | ||
| this._logger( 'info', 'loader', 'initialized component ' + path ); | ||
| component.initialized = true; | ||
@@ -75,4 +83,14 @@ done(); | ||
| }, done ); | ||
| }, ( err ) => { | ||
| if ( err ) { | ||
| this._logger( 'error', 'loader', 'error initializing components', err ); | ||
| return done( err ); | ||
| } | ||
| this._logger( 'notice', 'loader', 'finished initializing components' ); | ||
| done(); | ||
| } ); | ||
| } | ||
@@ -82,2 +100,4 @@ | ||
| this._logger( 'notice', 'loader', 'begin d-initializing components' ); | ||
| let components = this._dinitializeStack; | ||
@@ -91,2 +111,4 @@ | ||
| if ( !component ) { | ||
| this._logger( 'warn', | ||
| 'loader', 'possible bug in stringstack/core, component not found in d-init stack ' + path ); | ||
| return done(); | ||
@@ -102,2 +124,3 @@ } | ||
| if ( !component.initialized ) { | ||
| this._logger( 'info', 'loader', 'd-initialized component ' + path ); | ||
| return done(); | ||
@@ -116,2 +139,3 @@ } | ||
| this._logger( 'info', 'loader', 'd-initialized component ' + path ); | ||
| return done(); | ||
@@ -126,2 +150,3 @@ | ||
| this._logger( 'info', 'loader', 'd-initialized component ' + path ); | ||
| component.initialized = false; | ||
@@ -132,4 +157,14 @@ done(); | ||
| }, done ); | ||
| }, ( err ) => { | ||
| if ( err ) { | ||
| this._logger( 'error', 'loader', 'error d-initializing components', err ); | ||
| return done( err ); | ||
| } | ||
| this._logger( 'notice', 'loader', 'finished d-initializing components' ); | ||
| done(); | ||
| } ); | ||
| } | ||
@@ -187,6 +222,9 @@ | ||
| let container = new Container( targetPath, this ); | ||
| let container = new Container( targetPath, this, this._logger ); | ||
| let instance = null; | ||
| if ( typeof Module === 'function' ) { | ||
| this._logger( 'info', 'loader', 'instantiating class component: ' + targetPath ); | ||
| instance = new Module( container ); | ||
| } else { | ||
@@ -197,6 +235,12 @@ instance = Module; | ||
| if ( typeof Module === 'object' && Module && typeof Module.load === 'function' ) { | ||
| this._logger( 'info', 'loader', 'loading singleton component: ' + targetPath ); | ||
| Module.load( container ); | ||
| } else { | ||
| this._logger( 'info', 'loader', 'imported static component: ' + targetPath ); | ||
| } | ||
| } | ||
| this._instantiateStack.pop(); | ||
@@ -292,6 +336,4 @@ | ||
| path = path.trim(); | ||
| if ( path.endsWith( '.js' ) ) { | ||
@@ -298,0 +340,0 @@ path = path.replace( /\.js$/, '' ); // don't need this, require adds if needed |
+1
-1
| { | ||
| "name": "@stringstack/core", | ||
| "description": "StringStack Core", | ||
| "version": "0.1.3", | ||
| "version": "0.2.0", | ||
| "repository": { | ||
@@ -6,0 +6,0 @@ "type": "git", |
+113
-1
@@ -374,2 +374,114 @@ # StringStack Core | ||
| # Logging | ||
| StringStack/core provides a logging facility that you can use to tap into your favorite logging tool. Simply pass a | ||
| logger function to the config for createApp() and get all the log writes from all components. Do so like this. | ||
| ```javascript | ||
| const Core = require('@stringstack/core'); | ||
| const Winston = require( 'winston' ); | ||
| // log to stdout/stderr | ||
| let winston = new (Winston.Logger)( { | ||
| transports: [ | ||
| new Winston.transports.Console( { | ||
| timestamp: true, | ||
| colorize: true, | ||
| level: process.env.NODE_LOG_LEVEL || 'info' // default to info, unless environment overrides | ||
| } ) | ||
| ] | ||
| } ); | ||
| let winstonLogger = function ( level, path, message, meta ) { | ||
| // pass the event to your favorite logger, such as https://www.npmjs.com/package/winston OR, just console it. | ||
| if ( meta instanceof Error ) { | ||
| meta = ` ${message.message}: ${message.stack}`; | ||
| } | ||
| winston.log( level, `[${process.pid}] ${path}: ${message}: ${typeof meta === 'string' ? meta : JSON.stringify( meta )}`); | ||
| } | ||
| let core = new Core(); | ||
| const App = core.createApp( { | ||
| log: winstonLogger, | ||
| rootComponents: [ | ||
| // ... | ||
| ] | ||
| } ); | ||
| // daemonix also has a log facility which could easily be used in conjunction with your StringStack/core app | ||
| const daemonix = require( 'daemonix' ); | ||
| daemonix( { | ||
| app: App, | ||
| log: function (level, message, meta) { | ||
| winstonLogger(level, 'daemonix', message, meta); | ||
| } | ||
| } ); | ||
| ``` | ||
| The handler function will receive a log level, the full path to the component triggering the log event, a string message | ||
| and a meta object with relevant data about the log message. Meta might be an instance of Error, a random object literal, | ||
| or some other piece of data to describe the log event beyond the message. However, | ||
| The component loader and the generated app, both parts of StringStack/core, will generate some log entries, as well as | ||
| all StringStack/* components built by the StringStack team. The logs events generated will conform to the following | ||
| practices as it pertains to log level. We use the same log level semantics recommended by RFC5424, | ||
| https://www.npmjs.com/package/winston, and Linux' syslog. | ||
| ```json | ||
| { | ||
| emerg: 0, // emergency: System is unusable. Complete system failure. | ||
| alert: 1, // alert: Action must be taken immediately. Potential data loss or curroption eminent. | ||
| crit: 2, // critical: Major system component failing, such as device IO error, network unreachable, etc. | ||
| error: 3, // error: An error occurred, but the system should be able to keep running otherwise. | ||
| warning: 4, // warning: Something less than ideal occurred, deprecated function call, bad request, etc. | ||
| notice: 5, // notice: Something significant happened, but is not a problem. This is startup, shutdown, etc. | ||
| info: 6, // information: Something common happened, is not a problem. | ||
| debug: 7, // debug: Tracking as much detail as possible on the actions of the code, incudling sensative data. | ||
| silly: 8 // silly: Tracking every detail of code, including sensative data. | ||
| } | ||
| ``` | ||
| The recommended frequency with which log level should be called is as follows. | ||
| ```json | ||
| { | ||
| emerg: 0, // emergency: Should trigger at any time, and should be logged any time it happens. | ||
| alert: 1, // alert: Should trigger at any time, and should be logged any time it happens. | ||
| crit: 2, // critical: Should trigger at any time, and should be logged any time it happens. | ||
| error: 3, // error: Should trigger at any time, and should be logged any time it happens. | ||
| warning: 4, // warning: Should trigger at any time, and should be logged any time it happens. | ||
| notice: 5, // notice: Should only trigger a finite amount of time relative to process lifetime and to a given time window. Should not fire with frequency congruent with system load. | ||
| info: 6, // information: Should only trigger a finite amount of time relative to system load. Where notice rate < frequency rate <= load/N, where N is some real number. | ||
| debug: 7, // debug: May trigger with every system event with frequency >= load * N, where N is some real number. | ||
| silly: 8 // silly: Will trigger multiple times with every event with frequency >= load * N, where N is some real number > 1. | ||
| } | ||
| ``` | ||
| Recommended actions for each log level are as follows. | ||
| ```json | ||
| { | ||
| emerg: 0, // emergency: Shutdown the system and investigate. | ||
| alert: 1, // alert: Shutdown the system and investigate. | ||
| crit: 2, // critical: Shutdown the system and investigate. | ||
| error: 3, // error: Investigate the error. | ||
| warning: 4, // warning: Investigate the warning. | ||
| notice: 5, // notice: Nothing, non-problem event. | ||
| info: 6, // information: Nothing, non-problem event. | ||
| debug: 7, // debug: For development and debuging only. Do not run in production under normal conditions. | ||
| silly: 8 // silly: For development and debuging only. Do not run in production under normal conditions. | ||
| } | ||
| ``` | ||
| # Daemonix for Linux Signal Management | ||
@@ -399,3 +511,3 @@ | ||
| let daemonix = require( 'daemonix' ); | ||
| const daemonix = require( 'daemonix' ); | ||
@@ -402,0 +514,0 @@ daemonix( { app: App } ); |
+211
-77
@@ -480,79 +480,2 @@ 'use strict'; | ||
| it( 'should allow config to pass to Core()', function ( done ) { | ||
| let core = new Core( { | ||
| rootComponents: [ | ||
| './test/lib/class.a' | ||
| ] | ||
| } ); | ||
| let App = core.createApp(); | ||
| let app = new App( 'test' ); | ||
| // init/dinit over and over. It is up to the actual modules to ensure they reset their internal state | ||
| // correctly on subsequent init/dinit cycles. | ||
| async.series( [ | ||
| checkInitialized( app, false ), | ||
| ( done ) => { | ||
| app.init( done ); | ||
| }, | ||
| checkInitialized( app, true ), | ||
| ( done ) => { | ||
| app.dinit( done ); | ||
| }, | ||
| checkInitialized( app, false ), | ||
| ( done ) => { | ||
| let actualEvents = getComponentManually( app, './test/lib/class.f' )._getEvents(); | ||
| let expectedEvents = [ | ||
| "TestA:instantiate", | ||
| "TestB:instantiate", | ||
| "TestDatabase:instantiate", | ||
| "TestConfig:instantiate", | ||
| "TestC:instantiate", | ||
| "TestD:instantiate", | ||
| "TestE:instantiate", | ||
| "StaticH:instantiate", | ||
| "TestF:instantiate", | ||
| "TestG:instantiate", | ||
| "TestConfig:init", | ||
| "TestDatabase:init", | ||
| "TestG:init", | ||
| "TestF:init", | ||
| "StaticH:init", | ||
| "TestE:init", | ||
| "TestD:init", | ||
| "TestC:init", | ||
| "TestB:init", | ||
| "TestA:init", | ||
| "TestA:dinit", | ||
| "TestB:dinit", | ||
| "TestC:dinit", | ||
| "TestD:dinit", | ||
| "TestE:dinit", | ||
| "StaticH:dinit", | ||
| "TestF:dinit", | ||
| "TestG:dinit", | ||
| "TestDatabase:dinit", | ||
| "TestConfig:dinit" | ||
| ]; | ||
| try { | ||
| assert.deepStrictEqual( actualEvents, | ||
| expectedEvents, | ||
| 'log of instantiation, initialization and d-initialization is not correct' ); | ||
| } catch ( e ) { | ||
| return done( e ); | ||
| } | ||
| done(); | ||
| } | ||
| ], done ); | ||
| } ); | ||
| it( 'should allow multiple root components', function ( done ) { | ||
@@ -887,4 +810,215 @@ | ||
| it( 'should handle log events', function ( done ) { | ||
| let logHistory = []; | ||
| let core = new Core(); | ||
| let App = core.createApp( { | ||
| rootComponents: [ | ||
| './test/lib/class.log.a' | ||
| ], | ||
| log: ( level, path, message, meta ) => { | ||
| logHistory.push( [ level, path, message, meta ] ); | ||
| } | ||
| } ); | ||
| let app = new App( 'test' ); | ||
| async.series( [ | ||
| ( done ) => { | ||
| app.init( done ); | ||
| }, | ||
| checkInitialized( app, true ), | ||
| ( done ) => { | ||
| app.dinit( done ); | ||
| }, | ||
| checkInitialized( app, false ), | ||
| ( done ) => { | ||
| try { | ||
| // console.log( 'logHistory', JSON.stringify( logHistory, null, 4 ) ); | ||
| let dir = process.cwd(); | ||
| assert.deepStrictEqual( logHistory, [ | ||
| [ | ||
| "info", | ||
| "loader", | ||
| "instantiating class component: " + dir + "/test/lib/class.log.a", | ||
| undefined | ||
| ], | ||
| [ | ||
| "info", | ||
| dir + "/test/lib/class.log.a", | ||
| "TestLogA constructor", | ||
| { | ||
| "meta": "data" | ||
| } | ||
| ], | ||
| [ | ||
| "info", | ||
| "loader", | ||
| "instantiating class component: " + dir + "/test/lib/class.log.b", | ||
| undefined | ||
| ], | ||
| [ | ||
| "info", | ||
| dir + "/test/lib/class.log.b", | ||
| "TestLogB constructor", | ||
| { | ||
| "meta": "data" | ||
| } | ||
| ], | ||
| [ | ||
| "verbose", | ||
| dir + "/test/lib/class.log.b", | ||
| "TestLogB constructor", | ||
| { | ||
| "meta": "data" | ||
| } | ||
| ], | ||
| [ | ||
| "notice", | ||
| "app", | ||
| "instantiated", | ||
| undefined | ||
| ], | ||
| [ | ||
| "notice", | ||
| "loader", | ||
| "begin initializing components", | ||
| undefined | ||
| ], | ||
| [ | ||
| "debug", | ||
| dir + "/test/lib/class.log.b", | ||
| "TestLogB init", | ||
| { | ||
| "meta": "data" | ||
| } | ||
| ], | ||
| [ | ||
| "info", | ||
| "loader", | ||
| "initialized component " + dir + "/test/lib/class.log.b", | ||
| undefined | ||
| ], | ||
| [ | ||
| "debug", | ||
| dir + "/test/lib/class.log.a", | ||
| "TestLogA init", | ||
| { | ||
| "meta": "data" | ||
| } | ||
| ], | ||
| [ | ||
| "warning", | ||
| dir + "/test/lib/class.log.a", | ||
| "TestLogA init warn", | ||
| undefined | ||
| ], | ||
| [ | ||
| "warning", | ||
| dir + "/test/lib/class.log.a", | ||
| "TestLogA init warning", | ||
| undefined | ||
| ], | ||
| [ | ||
| "info", | ||
| "loader", | ||
| "initialized component " + dir + "/test/lib/class.log.a", | ||
| undefined | ||
| ], | ||
| [ | ||
| "info", | ||
| "loader", | ||
| "initialized component config", | ||
| undefined | ||
| ], | ||
| [ | ||
| "info", | ||
| "loader", | ||
| "initialized component env", | ||
| undefined | ||
| ], | ||
| [ | ||
| "notice", | ||
| "loader", | ||
| "finished initializing components", | ||
| undefined | ||
| ], | ||
| [ | ||
| "notice", | ||
| "loader", | ||
| "begin d-initializing components", | ||
| undefined | ||
| ], | ||
| [ | ||
| "info", | ||
| "loader", | ||
| "d-initialized component env", | ||
| undefined | ||
| ], | ||
| [ | ||
| "info", | ||
| "loader", | ||
| "d-initialized component config", | ||
| undefined | ||
| ], | ||
| [ | ||
| "debug", | ||
| dir + "/test/lib/class.log.a", | ||
| "TestLogA dinit", | ||
| { | ||
| "meta": "data" | ||
| } | ||
| ], | ||
| [ | ||
| "info", | ||
| "loader", | ||
| "d-initialized component " + dir + "/test/lib/class.log.a", | ||
| undefined | ||
| ], | ||
| [ | ||
| "debug", | ||
| dir + "/test/lib/class.log.b", | ||
| "TestLogB dinit", | ||
| { | ||
| "meta": "data" | ||
| } | ||
| ], | ||
| [ | ||
| "info", | ||
| dir + "/test/lib/class.log.b", | ||
| "TestLogB information", | ||
| undefined | ||
| ], | ||
| [ | ||
| "info", | ||
| "loader", | ||
| "d-initialized component " + dir + "/test/lib/class.log.b", | ||
| undefined | ||
| ], | ||
| [ | ||
| "notice", | ||
| "loader", | ||
| "finished d-initializing components", | ||
| undefined | ||
| ] | ||
| ], 'should pass log events to logger' ); | ||
| } catch ( e ) { | ||
| return done( e ); | ||
| } | ||
| done(); | ||
| } | ||
| ], done ); | ||
| } ); | ||
| } ); | ||
| } ); |
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
84441
17.3%45
4.65%1914
14.06%527
26.99%