Skip to content

Adding logging and refactoring repositories and operations #55

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ An opinionated boilerplate for Node web APIs focused on separation of concerns a
<dt>Logging</dt>
<dd>
The <a href="https://www.npmjs.com/package/log4js">Log4js</a> logger is highly pluggable, being able to append the messages to a file during the development and send them to a logging service when on production. Even the requests (through <a href="https://www.npmjs.com/package/morgan">morgan</a>) and queries will be logged.
a second Layer of logging called Trace Logging is implemented throu log4js and a memory appender, it serves as an error only file appender meaning that it traces your actions through
operations and if an error is detected, the trace is saved to a file.
</dd>

<dt>Linter</dt>
Expand Down
124 changes: 124 additions & 0 deletions config/configUtils.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@


module.exports={
getdataWithString(sourceObject, string){
if(sourceObject.constructor===Object ){
let strings = string.split('.');
let finalData=sourceObject;
strings.forEach((elem)=>{
if(elem!==''){
finalData=finalData[elem];
}
});

return finalData;
}else{
throw 'sourceObject must be an object';
}
},
setdataWithString(sourceObject, string, data){
if(sourceObject.constructor===Object ){
let strings = string.split('.');
let finalDestination=sourceObject;
strings.forEach((elem, index)=>{
if(finalDestination[elem]){
finalDestination=finalDestination[elem];
}else{
finalDestination[elem]={};
}
if(index===strings.length-1){
finalDestination[elem]=data;
}
});

finalDestination=data;
return finalDestination;
}else{
throw 'sourceObject must be an object';
}
},

/**
* will set data based on hierarchy to a destination object
* @param destinationObject
* @param hierarchy
*/
setDatawithHierarchy(destinationObject, hierarchy){

if(destinationObject && destinationObject.constructor === Object && hierarchy && hierarchy.constructor === Object){
let strings = this.getStringOutOfHierarchy(hierarchy);

strings.map((elem)=>{
this.setdataWithString(destinationObject, elem, this.getdataWithString(hierarchy, elem));
});
}else{
throw 'argument must be objects and not null';
}

},


/**
* Will return a list of hierarchy strings based on the parameter object
* @param obj
* @returns {Array}
*/
getStringOutOfHierarchy(obj){
let finalString=[];
let lastString=[];
let stringHierarachy = recHierarchy(obj, Object.keys(obj))[0];

finalString.map(elem=>{
if(elem.constructor===Array){
elem.forEach(
(subelem=>{
lastString.push(subelem);
})
);
}
});
return lastString;


function recHierarchy(obj, initial){
if(Object.keys(obj).length!=0){
return Object.keys(obj).map((elem)=>{
let basString= elem;
switch (typeof obj[elem]) {
case 'string':{

return [`${elem.toString()}`];
}
default :{
return [`${elem.toString()}`];
}
case 'object':{
if(obj[elem].constructor === Array ){
return [`${elem.toString()}`];
}
let returnedValue = recHierarchy(obj[elem]);
let value = returnedValue.map(
(elem)=>{
if(elem.constructor === Array){
return elem.map(
(subelem)=>{
return `${basString}.${subelem.toString()}`;
}
);
}
return `${basString}.${elem.toString()}`;
}
);
(initial && initial.indexOf(elem)!==-1)?finalString=finalString.concat(value) :null;
return value;
}

}
});
}else{
return [''];
}

}
},
};
14 changes: 0 additions & 14 deletions config/environments/development.js

This file was deleted.

189 changes: 189 additions & 0 deletions config/environments/development/development.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
const path = require('path');
const logPath = path.join(__dirname, '../../logs/development.log');
const tracePath = path.join(__dirname, '../../logs/trace.log');
var config = require('src/infra/logging/MemoryAppender');
const fs = require('fs');
const ENV = process.env.NODE_ENV || 'development';
const configUtils = require('../../configUtils');
const JSONFileHandlingService = require('src/infra/files').JSONFileHandler;
//a global variable holding the configuration is not the ideal way since it can have
//sensitive information so this should be moved elsewhere
var cfg = {};
var fileCfg ={};
var extraConfig={
'logging': {
'appenders': {
'file': {
'filename': logPath
},
'trace': {
'type': {
'configure': config.config()
},
},
'traceFile': {
'filename': tracePath,
}
},

}
};


//defining the file and setting the configuration
const file = path.join(__dirname, ENV)+'.json';
//loading the initial configuration
load();
//setting the listener for json file changes to reload the configuration
fs.watchFile(file, load);

//injecting variables using the objectAssign

function load() {
const parsed = JSON.parse(fs.readFileSync(file));
Object.assign(fileCfg, parsed);
cfg = Object.assign({}, fileCfg, cfg);
configUtils.setDatawithHierarchy(cfg, extraConfig);
}


function setConfig(hierarchy){

if(hierarchy){
if(hierarchy.constructor === Object){
let strings = configUtils.getStringOutOfHierarchy(hierarchy);

strings.forEach((elem)=>{
if(fileCfg[elem.split('.')[0]]){
JSONFileHandlingService.setToJsonFile(file, elem, configUtils.getdataWithString(hierarchy, elem));
}else{
configUtils.setdataWithString(extraConfig, elem, configUtils.getdataWithString(hierarchy, elem));
}
});
}

load();
}else{
throw 'the hierarchy argument is mandatory';
}
}


function getConfig(){
return cfg;
}

module.exports= {
config:cfg,
set:(hierarchy)=>{
return setConfig(hierarchy);
},
get:()=>{
return getConfig();
}
};



//deprecated
//left for comparison
/*
module.exports = {
web: {
port: 4000
},
logging: {
appenders: {
console :{ type: 'console' },
file: { type: 'file', filename: logPath },
trace :{
type: {
configure:config.config()
},
layout: {
type: 'pattern',
pattern: '%d %p %c %x{user} %m%n',
}
},
traceFile: {
type: 'file',
filename: tracePath,
layout: {
type: 'pattern',
pattern: '%m%n',
}
},
},
categories: {
default:
{
appenders:
[
'console',
'file',

],
level: 'debug'
},
trace: {
appenders:
[
'trace'
],
level:'TRACE'
},
traceFile:{
appenders:
[
'traceFile'
],
level:'ALL'
}

}
},
memoryloggerId:'x-test-req-ID',
appMetrics:{
replSocketURL:'localhost:4001',
userAppMetric:true,
tracking:{
cpu:true,
eventloop:true,
profiling:true,
http:{
use:true,
config:{
filters:{
pattern:'', //(String) a regular expression pattern to match HTTP method and URL against, eg. 'GET /favicon.ico$'
to:'' //(String) a conversion for the URL to allow grouping. A value of '' causes the URL to be ignored.
}
}
},
mongo:true,
socketio:true,
mqlight:true,
postgresql:true,
mqtt:true,
mysql:true,
redis:true,
riak:true,
memcached:true,
oracledb:true,
oracle:true,
'strong-oracle':true,
requests:{
use:true,
config:{
excludeModules:[] //(Array) of String names of modules to exclude from request tracking.
}
},
trace:{
use:true,
config:{
includeModules:[] //Array) of String names for modules to include in function tracing. By default only non-module functions are traced when trace is enabled.
}
}
}
}
};
*/
Loading