I'm wondering how I'd come about getting line error in eval.
eg.,
try {
eval("var hello = 5; hello hello");
} catch(err) {
console.log(err.line) // should print 2
}
Any help would be appreciated, thanks.
If you are in Node, i'd rather use the vm package as it is safer.
Here is a working solution
const vm = require('vm');
// this is the sandbox, it gives the scrip only access to these vars, which
makes it safer than a pure eval;
const sandbox = {
count: 2
};
try {
// create script to be ran
// I use backtick for new lines
const script = new vm.Script(
`count += 1;
throw new Error('test');`
);
// create the context from the sandbox
const context = new vm.createContext(sandbox);
// run the script
script.runInContext(context, {
lineOffset: 0,
displayErrors: true,
});
} catch(e) {
console.log('Line of error :', e.stack.split('evalmachine.<anonymous>:')[1].substring(0, 1))
}
Running this code will log Line of error: 3.
Here is the doc for the vm package: https://nodejs.org/api/vm.html
Related
I've created:
var access = fs.createWriteStream('/var/log/node/api.access.log', { flags: 'w' });
Then piped:
process.stdout.pipe(access);
Then tried:
console.log("test");
And nothing has appeared in /var/log/node/api.access.log. However this way is working:
process.stdout.pipe(access).write('test');
Could someone explain what am I doing wrong ?
I solved this problem the following way:
var access = fs.createWriteStream('/var/log/node/api.access.log');
process.stdout.write = process.stderr.write = access.write.bind(access);
Of course you can also separate stdout and stderr if you want.
I also would strongly recommend to handle uncaught exceptions:
process.on('uncaughtException', function(err) {
console.error((err && err.stack) ? err.stack : err);
});
This will cover the following situations:
process.stdout.write
process.stderr.write
console.log
console.dir
console.error
someStream.pipe(process.stdout);
throw new Error('Crash');
throw 'never do this';
throw undefined;
Checkout console.Console, the parent class of the normal console.
var myLogFileStream = fs.createWriteStream(pathToMyLogFile);
var myConsole = new console.Console(myLogFileStream, myLogFileStream);
You can then you use myConsole.log, myConsole.error, myConsole.dir, etc. and write directly to your file.
You can also monkey patch process.stdout.write as follows:
var fn = process.stdout.write;
function write() {
fn.apply(process.stdout, arguments);
myLogFileStream.write.apply(myLogFileStream, arguments);
}
process.stdout.write = write;
there are also other options for overwriting console._stdout depending on the motivation for logging the stdout to a file.
process.stdout is a Writable. pipe is a method of Readable(Cf StreamAPI documentation : https://nodejs.org/api/stream.html
You can see the documentation of process.stdout here : https://nodejs.org/api/process.html#process_process_stdout
It's surprising that you can do process.stdout.pipe(...); without any error. But i suppose this call just do nothing. Except returning a new Writable stream binded to stdout (or maybe it returns process.stdout itself. There's no specification for that in the documentation).
If you want to redirect stdout to a file, you have many solutions :
Just use your command line to do that. Windows style : node myfile.js > api.access.log.
Replace the console object by your own object. And you can rewrite console methods.
I'm not sure, but it may be possible to replace process.stdout with your own stream (and you can do whatever you want with this)
#user3173842
for the reply on
I solved this problem the following way:
var access = fs.createWriteStream('/var/log/node/api.access.log');
process.stdout.write = process.stderr.write = access.write.bind(access);
you do understand that process.stdout continues after process.on('exit') and therefore the fs.WriteStream closes after with process.stdout, according to
https://github.com/nodejs/node/issues/7606
so now the question remains, if the developer desired to have the fs.Writestream.write() return to its normal functionality and when fs.Writestream.end is called the writestream closes. How would the developer go about doing this I did
a_l = asyncify_listener
p_std_stream_m is a process stream manager object
p_std_stream_m.std_info.p_stdout_write = process.stdout.write
process.stdout.write = w_stream.write.bind(w_stream)
process.once('beforeExit', a_l( p_std_stream_m.handler,process.stdout,w_stream ) )
where in the 'beforeExit' event listener I did
process.stdout.write = p_std_stream_m.std_info.p_stdout_write
w_stream.end()
It works and you use the once method because the process.stdout seems to do a lot of work
at this time.
Is this good practice, would you do this or what would you do in this situation
anyone can feel free to reply.
Originally based on #Anatol-user3173842 answer
But in my case I needed to hook the stdout & stderr and also write into a file.
So for those who need to keep the normal stdout behaviour in addition to writing into the file. Use the following.
For non-errors:
// stdout logging hook
const stdoutWrite0 = process.stdout.write;
process.stdout.write = (args) => { // On stdout write
CustomLogger.writeToLogFile('log', args); // Write to local log file
args = Array.isArray(args) ? args : [args]; // Pass only as array to prevent internal TypeError for arguments
return stdoutWrite0.apply(process.stdout, args);
};
For errors:
// stderr logging hook
const stderrWrite0 = process.stderr.write;
process.stderr.write = (args) => { // On stderr write
CustomLogger.writeToLogFile('error', args); // Write to local error file
args = Array.isArray(args) ? args : [args]; // Pass only as array to prevent internal TypeError for arguments
return stderrWrite0.apply(process.stderr, args);
};
// uncaught exceptions
process.on('uncaughtException', (err) => {
CustomLogger.writeToLogFile('error', ((err && err.stack) ? err.stack : err));
});
Here is the CustomLogger code, where I also separate the log files by date:
export class CustomLogger {
static LOGS_DIR = 'location-of-my-log-files';
private static logDailyName(prefix: string): string {
const date = new Date().toLocaleDateString().replace(/\//g, '_');
return `${CustomLogger.LOGS_DIR}/${prefix}_${date}.log`;
}
private static writeToLogFile(prefix, originalMsg) {
const timestamp = Date.now();
const fileName = this.logDailyName(prefix);
const logMsg = prepareForLogFile(originalMsg);
fs.appendFileSync(fileName, `${timestamp}\t${logMsg}\n\n`);
return originalMsg;
}
}
Here's a quick example of a logger class that redirects stdout, stderr and exceptions to a file, while still writting everything to the console:
class Logger {
#log_stream
#stdout_write
#stderr_write
constructor(path) {
this.#log_stream = fs.createWriteStream(path, { flags: 'a' })
this.#stdout_write = process.stdout.write.bind(process.stdout)
this.#stderr_write = process.stderr.write.bind(process.stderr)
process.stdout.write = this.stdout_write.bind(this)
process.stderr.write = this.stderr_write.bind(this)
process.on('uncaughtException', function(err) {
console.error((err && err.stack) ? err.stack : err)
})
}
stdout_write(buffer) {
this.#log_stream.write(buffer)
this.#stdout_write(buffer)
}
stderr_write(buffer) {
this.#log_stream.write(buffer)
this.#stderr_write(buffer)
}
}
const logger = new Logger('example.log')
How can I test JavaScript code without using an additional framework such as Mocha? Is it possible to create a unit test case, write test functions manually, test the code, etc.?
I've tried to write a test case, but even though they were in the same folder, I couldn't link them.
Let's say this is a function in the main.js file
function calculate(a, b) {
return a + b;
}
And this is a test case in the testMain.js file
function testCalculate(){
if(calculate(1, 1) == 2)
console.log('It Works!');
else
console.log('Test failed');
}
testCalculate();
When I try to run testMain.js in the IntelliJ IDEA IDE I get an error similar to
"ReferenceError: calculate is not defined"
It depends whether you are trying to test Node.js code or front end code. In both cases you have to "expose" the function under test to your test framework.
Node.js
// main.js
const obj = {};
obj.sum = (a, b) => {
return a+b;
};
module.exports = obj; // Export 'obj' so that it is visible from your test runner
// test.js
const main = require('main.js');
const assert = require('assert');
const it = (desc, fn) => {
try {
fn();
console.log('\x1b[32m%s\x1b[0m', `\u2714 ${desc}`);
} catch (error) {
console.log('\n');
console.log('\x1b[31m%s\x1b[0m', `\u2718 ${desc}`);
console.error(error);
}
};
it('should return the sum of two numbers', () => {
assert.strictEqual(main.sum(5, 10), 15);
});
When you run node test.js you should be able to see the test result.
Front End
// app.js
self.myapp = myapp; // All the methods in myapp will be exposed globally
myapp.sum = function(a, b) {
return a + b;
}
// test.js
function it(desc, fn) {
try {
fn();
console.log('\x1b[32m%s\x1b[0m', '\u2714 ' + desc);
} catch (error) {
console.log('\n');
console.log('\x1b[31m%s\x1b[0m', '\u2718 ' + desc);
console.error(error);
}
}
function assert(condition) {
if (!condition) {
throw new Error();
}
}
it('should return a sum of two integers', function(){
assert(myapp.sum(5, 10) === 15);
});
// test.html - This is your test runner for the front end
<html>
...
<body>
...
<script src="app.js"></script>
<script src="test.js"></script>
</body>
</html>
Open test.html in a browser and open the browser console. You should be able to see the success message.
This way you can write test cases for Node.js and front end JavaScript code without using Mocha or any other framework.
To make your code work, your testMain.js file needs to import your main.js code somehow.
In the main.js file:
function calculate(a, b) {
return a+b;
}
module.exports.calculate = calculate
in testMain.js file, import the main.js:
var main = require('main.js')
function testCalculate(){
if(main.calculate(1+1)==2)
console.log('It Works!');
else
console.log('Test failed');
}
Note: I'm aware this isn't necessarily showing good coding style, just aiming to demonstrate what the original issue was with minimal changes to the original snippets
That said, it's not usually worth reinventing the wheel and building your own test framework. Can you clarify the reason why you would like to avoid an existing framework? If you are looking for simplicity, maybe something like jstinytest would do the trick.
If it is a Node.js application you can simply require the other file and import the other function. If the project uses Babel you can use ES6 import to import the function from the other file.
I was also bothered by the same issue for a while. The question is how to test your JavaScript code with out a testing framework since testing frame works bring a lot to the table and most of the time they get into the way.
The answer is to use assertion libraries without the testing frame work. For example you can just use chai assertion library with out mocha frame work
you can simple install chai with
npm install chai
After that you can just use it:
var should = require('chai').should()
const log = console.log;
//log(should);
//const letters = "abcdef";
const letters = 555;
letters.should.be.a('string');
I run a test.js with:
var power_meter = require('./power-meter');
var pm = new power_meter.PowerMeter();
function a() {
var power_instant = 123;
pm.broadcast(power_instant);
setTimeout(a, 249);
}
a();
The Output is
123
123
...
Now i want to import a value from a python script with python-shell with:
var PythonShell = require('python-shell');
var pyshell = new PythonShell('7powerx.py');
var power_meter = require('./power-meter');
var pm = new power_meter.PowerMeter();
pyshell.on('message', function (message) {
console.log(message);
var power_instant = message;
pm.broadcast (power_instant);
});
// end the input stream and allow the process to exit
// end the input stream and allow the process to exit
pyshell.end(function (err) {
if (err) throw err;
console.log('finished');
});
I have tried to set the "setTimeout(a, 249);"
a();
in any place but without any success.
The script started and do an output after circa 5 minutes, but then stops again and after a time it works again for a moment.
I need to place the "setTimeout" but no idea where.
Any idea?
Update.
I have in my pythonScript a time function, when i set this to 0.0 it works, but my Raspberry PI collapse. So is there a snych/timing problem.
The problem is solved:
For a looping process i need to start with:
var pyshell = new PythonShell('test.py',{scriptPath:"./", pythonOptions: ['-u']});
I hope this helps other, too!
In my JS test, I need to check if the console.info is called. That's why I want to mock console. However, it seems that the console variable cannot be assigned with a different object. Did I make any mistake?
Here is the code I used:
var oldConsole = console;
var infoContent;
console = {
info: function(content) {
infoContent = content;
}
};
game.process('a command');
infoContent.should.equal('a command is processed');
console = oldConsole;
You can use rewire to replace the whole of console to silence it, or to inject a mock. I use deride but sinon would also work.
var rewire = require('rewire');
var deride = require('deride');
var Game = rewire('../lib/game');
describe('game testing', function() {
var stubConsole, game;
beforeEach(function() {
stubConsole = deride.stub(['info']);
stubConsole.setup.info.toReturn();
Game.__set__({
console: stubConsole
});
game = new Game();
});
it('logs info messages', function() {
game.process('a command');
stubConsole.expect.info.called.withArgs(['a command is processed']);
});
});
I find the solution. I can change the method info of console.
console.info = function(content) {
infoContent = content;
};
The question is now why console object itself cannot be reassigned?
you can use sinon npm to count the call to a function :
it("calls the original function only once", function () {
var callback = sinon.spy();
var proxy = once(callback);
proxy();
proxy();
assert(callback.calledOnce);
// ...or:
// assert.equals(callback.callCount, 1);
});
You can find the docs here : sinonjs.org
I thought I had the same problem and my solution was using this std-mocks module:
https://github.com/neoziro/std-mocks
This has the advantage of not taking over the global "console" but allows you to see what gets logged to the stdout / stderr. This solves the problem in a different way than the question was explicitly looking for; however I believe it is a good answer for the problem the question implies and may be useful for others.
const stdMocks = require('std-mocks');
stdMocks.use(); console.log('test'); stdMocks.restore();
// => undefined [nothing gets output, stdout intercepted]
const logged = stdMocks.flush();
console.log(logged)
// => { stdout: [ 'test\n' ], stderr: [] }
When working in Python I always have this simple utility function which returns the file name and line number from where the function is called:
from inspect import getframeinfo, stack
def d():
""" d stands for Debug. It returns the file name and line number from where this function is called."""
caller = getframeinfo(stack()[1][0])
return "%s:%d -" % (caller.filename, caller.lineno)
So in my code I simply put a couple debug lines like this to see how far we get before some error occurs:
print d()
# Some buggy code here
print d()
# More buggy code here
print d(), 'here is the result of some var: ', someVar
This works really well for me because it really helps debugging quickly.
I'm now looking for the equivalent in a node backend script. I was searching around but I can't find anything useful (maybe I'm looking for the wrong words?).
Does anybody know how I can create a Javascript/nodejs function which outputs the file name and line number from where the function is called? All tips are welcome!
You can create an Error to get where the Error is, and its stack trace. Then you can put that into a function, to get the line where it is.
function thisLine() {
const e = new Error();
const regex = /\((.*):(\d+):(\d+)\)$/
const match = regex.exec(e.stack.split("\n")[2]);
return {
filepath: match[1],
line: match[2],
column: match[3]
};
}
console.log(thisLine());
This works for me in Google Chrome.
And also in node.
Note to #j08691's comment:
Both this and this seem to be using lineNumber, which is not present (as far as I could test) in NodeJS.
Printing line number with custom string
const moment = require('moment');
const log = console.log;
const path = require('path');
function getTime(time) { return moment().format('YYYY-MM-DD HH:mm:ss') };
function line(num = 2) {
const e = new Error();
const regex = /\((.*):(\d+):(\d+)\)$/
const match = regex.exec(e.stack.split("\n")[num]);
const filepath = match[1];
const fileName = path.basename(filepath);
const line = match[2];
const column = match[3];
return {
filepath,
fileName,
line,
column,
str: `${getTime()} - ${fileName}:${line}:${column}`
};
}
log(line().str, "mylog1");
log(line().str, "mylog2");
log(line().str, "mylog3");
OUTPUT
2021-11-22 13:07:15 - test.js:44:5 mylog1
2021-11-22 13:07:15 - test.js:45:5 mylog2
2021-11-22 13:07:15 - test.js:46:5 mylog3
You can use this gulp plugin gulp-log-line . It Logs file and line number without the extra cost of reading the stack.
you just have to install gulp and gulp-log-line using the
npm install gulp --save and npm install gulp-log-line command. after that you need to create and write the below code in gulpfile.js and run
gulp log-line to create a duplicate file in the build folder :-
var gulp = require('gulp');
var logLine = require('gulp-log-line');
gulp.task('line-log', function() {
return gulp.src("file.js", {buffer : true})
//Write here the loggers you use.
.pipe(logLine(['console.log', 'winston.info']))
.pipe(gulp.dest('./build'))
})
gulp.task('default', ['line-log'])
Example
file.js :-
console.log('First log')
var someVariable
console.log('Second log')
Becomes
console.log('file.js:1', 'First log')
var someVariable
console.log('file.js:3', 'Second log')
The only way I've found to get anything relating to line numbers is to trap the window.onerror function, and when there's an error that will get passed the error message, the file URL and the line number:
window.onerror = function(msg, url, line) {
alert(msg + "\n" + url + ":" + line);
};
This works for me on Chrome - I don't know about other browsers.
EDIT when this answer was given in Feb' 15 there was no mention of NodeJS in the question. That was only added in November '17.