I've managed to switch on logging for prerender.io for the PhantomJS browser, using the logger plugin. However, I would like only error messages to be logged. The logger plugin logs just everything. Based on the below pages:
http://phantomjs.org/api/webpage/handler/on-error.html
http://phantomjs.org/api/phantom/handler/on-error.html
PhantomJS should have an onError event which is triggered when an error occurred. Before trying to create a plugin, I've tried temporarily to update server.js of prerender.io, to attach to the onError event. I've updated both server.createPage function as below, to attach to page.onError and even tried page.set('onError'), similar to the code found in the logger.js
server.createPage = function(req, res) {
var _this = this;
if(!this.phantom) {
setTimeout(function(){
_this.createPage(req, res);
}, 50);
} else {
req.prerender.phantomId = this.phantom.id;
this.phantom.createPage(function(page){
req.prerender.page = page;
console.log("registering onError for page");
page.onError = function(msg, trace) {
var msgStack = ['PAGE PHANTOM ERROR OCCURRED: ' + msg];
msgStack.push('----------------------------------');
if (trace && trace.length) {
msgStack.push('TRACE:');
trace.forEach(function(t) {
msgStack.push(' -> ' + (t.file || t.sourceURL) + ': ' + t.line + (t.function ? ' (in function ' + t.function +')' : ''));
});
}
msgStack.push('==================================');
console.log(msgStack.join('\n'));
};
page.set('onError', function(msg, trace) {
var msgStack = ['PAGE2 PHANTOM ERROR OCCURRED: ' + msg];
msgStack.push('----------------------------------');
if (trace && trace.length) {
msgStack.push('TRACE:');
trace.forEach(function(t) {
msgStack.push(' -> ' + (t.file || t.sourceURL) + ': ' + t.line + (t.function ? ' (in function ' + t.function +')' : ''));
});
}
msgStack.push('==================================');
console.log(msgStack.join('\n'));
});
_this.onPhantomPageCreate(req, res);
});
}
};
I've also updated the server.onPhantomCreate(), method to attach to the general phantom.onError, as per below:
server.onPhantomCreate = function(phantom) {
util.log('started phantom');
this.phantom = phantom;
this.phantom.id = Math.random().toString(36);
console.log("Registering phantom.onError");
phantom.onError = function(msg, trace) {
var msgStack = ['PHANTOM ERROR OCCURRED: ' + msg];
msgStack.push('----------------------------------');
if (trace && trace.length) {
msgStack.push('TRACE:');
trace.forEach(function(t) {
msgStack.push(' -> ' + (t.file || t.sourceURL) + ': ' + t.line + (t.function ? ' (in function ' + t.function +')' : ''));
});
}
msgStack.push('==================================');
console.log(msgStack.join('\n'));
};
phantom.set('onError', function(msg, trace) {
var msgStack = ['PHANTOM ERROR OCCURRED: ' + msg];
msgStack.push('----------------------------------');
if (trace && trace.length) {
msgStack.push('TRACE:');
trace.forEach(function(t) {
msgStack.push(' -> ' + (t.file || t.sourceURL) + ': ' + t.line + (t.function ? ' (in function ' + t.function +')' : ''));
});
}
msgStack.push('==================================');
console.log(msgStack.join('\n'));
});
};
However, nothing is being logged. Any ideas why is this?
You can pass onStdout and onStderr functions through with the prerender options object:
var server = prerender({
workers: 1,
iterations: 50,
onStdout: function(data) {
console.log(data);
},
onStderr: function(data) {
console.log(data);
}
});
You can't put onError on phantom directly because it isn't a real phantom instance, it's a node pipeline to the real phantomjs instance.
Related
I'm pretty new to PhantomJS, so I'm just trying to stumble through some practical examples (the documentation feels a little light on those).
One thing I was attempting was to submit votes to a phony PollDaddy poll, but I'm struggling. Using the script below, I can tell from the first screenshot that my option button is being clicked/selected. But why does my answer not get submitted? The second screenshot looks identical to the first, despite my code executing a click event on the VOTE button in the second evaluate. Anyone know why? Know how to make it work?
var webPage = require('webpage');
var page = webPage.create();
//******* BEGIN LOGGING METHODS *******
// http://phantomjs.org/api/webpage/handler/on-url-changed.html
page.onUrlChanged = function(targetUrl) {
console.log('New URL: ' + targetUrl);
};
// http://phantomjs.org/api/webpage/handler/on-console-message.html
page.onConsoleMessage = function(msg, lineNum, sourceId) {
console.log('CONSOLE: ' + msg + ' (from line #' + lineNum + ' in "' + sourceId + '")');
};
// http://phantomjs.org/api/webpage/handler/on-error.html
page.onError = function(msg, trace) {
var msgStack = ['ERROR: ' + msg];
if (trace && trace.length) {
msgStack.push('TRACE:');
trace.forEach(function(t) {
msgStack.push(' -> ' + t.file + ': ' + t.line + (t.function ? ' (in function "' + t.function +'")' : ''));
});
}
console.error(msgStack.join('\n'));
};
// http://phantomjs.org/api/webpage/handler/on-resource-error.html
page.onResourceError = function(resourceError) {
console.log('Unable to load resource (#' + resourceError.id + ' URL:' + resourceError.url + ')');
console.log('Error code: ' + resourceError.errorCode + '. Description: ' + resourceError.errorString);
};
// http://phantomjs.org/api/webpage/handler/on-resource-timeout.html
page.onResourceTimeout = function(request) {
console.log('Response Timeout (#' + request.id + '): ' + JSON.stringify(request));
};
//******* END LOGGING METHODS *******
page.open('https://polldaddy.com/poll/9424638/', function(status) {
console.log('Status: ' + status);
//make selection
var myselection = page.evaluate(function() {
var ev = document.createEvent("MouseEvent");
ev.initMouseEvent("click", true, true, window, null,0, 0, 0, 0, false, false, false, false, 0, null);
//radio button for Curly has id=PDI_answer42988707
var myselection = document.querySelector("#PDI_answer42988707");
(myselection).dispatchEvent(ev);
return document.title;
});
//screen capture the selection
page.render('selection.png');
console.log(myselection);
//click the Vote button
var dovote = page.evaluate(function() {
var ev = document.createEvent("MouseEvent");
ev.initMouseEvent("click", true, true, window, null,0, 0, 0, 0, false, false, false, false, 0, null);
//get a handle to the vote button
var votebutton = document.querySelector("a.vote-button");
//click the vote button
(votebutton).dispatchEvent(ev);
return document.title;
});
//delay, then take screenshot...
setTimeout(
function(){
page.render('voted.png');
console.log(dovote);
}
,2000
);
});
I'm executing the script using PhantomJS 1.9.0 on Linux Mint. The execution parameters are like this to overcome any SSL Handshake failures:
phantomjs --ignore-ssl-errors=true --ssl-protocol=any myscript.js
I've also tried setting --cookies-file and --local-storage-path on the command line, but that didn't help either.
The output I get from all of the console logging above is this:
New URL: https://polldaddy.com/poll/9424638/ <-- from urlChange
CONSOLE: JQMIGRATE: Logging is active (from line # in "") <-- from onConsoleMessage
Status: success
Who is your favorite Stooge (poll 9424638) | Polldaddy.com
Who is your favorite Stooge (poll 9424638) | Polldaddy.com
My useless poll is here: https://polldaddy.com/poll/9424638/, and you'll see from my script that I'm trying to stack the results for Curly.
If you look at the javascript that is being included with the polldaddy page, you'll find a vote function with the following check:
if (event.pageX) {
eventX = event.pageX;
eventY = event.pageY;
} else {
eventX = event.clientX;
eventY = event.clientY;
}
...
if (eventX == 0 && eventY == 0) {
return false
}
Basically they are looking at the event location and short-circuiting if the x and y location are both equal to 0.
If you change the mouse event that you create so that the clientX or clientY value is not 0, then your script should work.
The javascript is located on the same website that I'm opening.
Example: http://test.site.com
js => http://test.site.com/cache/6343019445fb7d95bd2bd09c5bbfb002.js
I'm also including jQuery from googleapis and that appears to load! I tried getting some data from events like onResourceError but nothing shows up.
This is my test code:
var page = require('webpage').create();
page.viewportSize = {
width: 1366,
height: 720
};
page.settings.loadImages = true;
page.settings.localToRemoteUrlAccessEnabled = true;
page.settings.javascriptEnabled = true;
page.settings.webSecurityEnabled = false;
page.settings.XSSAuditingEnabled = false;
var uri = 'http://test.site.com';
page.onConsoleMessage = function(msg, lineNum, sourceId) {
console.log('CONSOLE: ' + msg + ' (from line #' + lineNum + ' in "' + sourceId + '")');
};
page.onResourceError = function(resourceError) {
console.log('Unable to load resource (#' + resourceError.id + 'URL:' + resourceError.url + ')');
console.log('Error code: ' + resourceError.errorCode + '. Description: ' + resourceError.errorString);
};
page.onResourceTimeout = function(request) {
console.log('Response (#' + request.id + '): ' + JSON.stringify(request));
};
page.onError = function(msg, trace) {
var msgStack = ['ERROR: ' + msg];
if (trace && trace.length) {
msgStack.push('TRACE:');
trace.forEach(function(t) {
msgStack.push(' -> ' + t.file + ': ' + t.line + (t.function ? ' (in function "' + t.function +'")' : ''));
});
}
console.error(msgStack.join('\n'));
};
page.onLoadFinished = function(){
setTimeout(function(){
console.log(page.content); // <script> is there
console.log('render');
page.render('test.png');
phantom.exit();
}, 4000);
};
page.open(uri, function() {
console.log('loaded');
});
I see no other console messages besides render, loaded and the page content.
What am I doing wrong?
You'll have to evaluate the page. I believe it's something like this.
page.evaluate(function () {
// This is after page is evaluated and javascript is run
});
Note that evaluate doesn't give you access to the JS functions or variables, it only runs it for you. If you need to do access the JS code from Phantom, you'll want to put the JavaScript code inside the page.evaluate yourself so that you can console.log or manipulate as you need to.
I'm having a hard time converting this code for it to be usable in a node server. So this code is written to run in a PhantomJS process (i.e. $: phantomjs index.js) but I want to run it in a node server using the package require("phantom"); I'm having a trouble getting these two callbacks to work however.
page.onLoadFinished = function(status){
console.log("Load Finished");
};
page.onUrlChanged = function(){
console.log("URL Changed");
};
Here is my pathetic attempt at trying to nodefy the whole situation.
phantom.create(['--ignore-ssl-errors=yes','--load-images=no']).then(function(ph) {
console.log("here");
ph.createPage().then(function(page) {
page.property('onResourceRequested', function(requestData, networkRequest) {
console.log(requestData.url);
});
page.open('https://example.com/login').then(function(status) {
console.log(status);
if (status !== 'success') { console.log("failed connection")} else {
page.evaluate(function() {
document.getElementById('email').value = "stuff";
document.getElementById('password').value = "things";
setTimeout(document.getElementsByTagName('button')[0].click(),5000);
console.log("login attempt");
setTimeout(document.URL, 2000);
});
page.onLoadFinished = function(status){
console.log("Load Finished");
};
page.onUrlChanged = function(){
console.log("url changed");
};
}
});
});
});
Also the code works and gets the page and clicks the button, however the problem is after the phantom logs in, I need data from the next page which I was going to use the onUrlChanged and onLoadFinished to do.
page.onLoadFinished and page.onUrlChanged are callback functions that are executed after page has been opened, so it makes sense to assign them before opening an url.
It is also a useful habit to subscribe to console.log and error messages from a webpage.
var phantom = require('phantom');
phantom.create(['--ignore-ssl-errors=yes','--load-images=no']).then(function(ph) {
console.log("here");
ph.createPage().then(function(page) {
page.property('onError', function(msg, trace) {
var msgStack = ['ERROR: ' + msg];
if (trace && trace.length) {
msgStack.push('TRACE:');
trace.forEach(function(t) {
msgStack.push(' -> ' + t.file + ': ' + t.line + (t.function ? ' (in function "' + t.function +'")' : ''));
});
}
console.error(msgStack.join('\n'));
});
page.property('onConsoleMessage', function(msg, lineNum, sourceId) {
console.log('CONSOLE: ' + msg + ' (from line #' + lineNum + ' in "' + sourceId + '")');
});
page.property('onResourceRequested', function(requestData, networkRequest) {
console.log(requestData.url);
});
page.property('onLoadFinished', function(status) {
console.log("Load Finished with status " + status);
});
page.property('onUrlChanged', function(targetUrl) {
console.log("URL changed to: " + targetUrl);
});
page.open('https://example.com/login').then(function(status) {
if (status !== 'success') { console.log("failed connection")} else {
page.evaluate(function() {
document.getElementById('email').value = "email";
document.getElementById('password').value = "password";
setTimeout(function(){
console.log("login attempt");
document.getElementsByTagName('button')[0].click();
}, 5000);
});
});
}
});
});
});
Beaglebone black - not receiving data on serial. I have connected Tx and Rx pins of the serial ports 1 and 2 directly (tx - rx). I'm kind of new to beaglebone. I used this code to send data on the serial to another device, it worked properly for transmission, but not for Rx.
I haven't found the file ttyO1, just ttyO2.
var b = require('bonescript');
var rxport = '/dev/ttyO1';
var txport = '/dev/ttyO2';
var options = { baudrate: 115200, parity: 'even', parser:
b.serialParsers.readline('\n') };
var teststring = "This is the string I'm sending out as a test";
b.serialOpen(rxport, options, onRxSerial);
function onRxSerial(x) {
console.log('rx.eventrx= ' + x.event);
if(x.err) throw('***FAIL*** ' + JSON.stringify(x));
if(x.event == 'open') {
//readReapeatedly();
b.serialOpen(txport, options, onTxSerial);
}
if(x.event == 'data') {
console.log("I am receiving on rxport");
console.log('rx (' + x.data.length +
') = ' + x.data.toString('ascii'));
}
}
function onTxSerial(x) {
console.log('tx.event = ' + x.event);
if(x.err) throw('***FAIL*** ' + JSON.stringify(x));
if(x.event == 'open') {
writeRepeatedly();
}
if(x.event == 'data') {
console.log('tx (' + x.data.length +
') = ' + x.data.toString('ascii'));
console.log(x.data);
}
}
function printJSON(x) {
console.log(JSON.stringify(x));
}
function writeRepeatedly() {
console.log("write to serial");
b.serialWrite(txport, teststring, onSerialWrite);
console.log("I have sent data");
}
function onSerialWrite(x) {
console.log("Iam in the onSerialWrite function");
if(x.err) console.log('onSerialWrite err = ' + x.err);
if(x.event == 'callback') {setTimeout(writeRepeatedly, 5000);
console.log("HERE");
}
}
I have a form in which I am expected to do some file processing which takes some time, so I want that finish event executes only after the processing is complete, right now
node is processing the file and while it is processing the file and executes commands node if finds finish event it fires it. so, how do i make sure that the finish event is fired only after processing of all files.
busboy.on('file', function(fieldname, file, filename,transferEncoding,mimeType) {
var fName = uuid.v4();
var fileext = filename.substr(filename.lastIndexOf('.') + 1);
var filepath = path.normalize(__dirname + '/../../');
var fstream = fs.createWriteStream(filepath+'/server/uploads/'+fName+'.'+fileext);
var uploadFileCompletion = file.pipe(fstream);
uploadFileCompletion.on('finish',function(){
console.log('uploaded now');
var cmd = 'libreoffice --headless --convert-to pdf --outdir '+ filepath + 'server/uploads ' + filepath + 'server/uploads/' + fName + '.' + fileext;
exec(cmd, function(error,stdout,stderr){
sys.puts(stdout);
var encryptCmd = 'java -jar server/uploads/pdfbox-app-1.8.6.jar Encrypt -canAssemble false -canExtractContent false -canExtractForAccessibility false ' +
'-canModify false -canModifyAnnotations false -canPrint false -canPrintDegraded false server/uploads/' + fName + '.' + 'pdf'
+ ' ' + 'server/uploads/' +fName + '.' + 'pdf';
exec(encryptCmd, function(error,stdout,stderr){
fs.unlink(filepath+'server/uploads/'+fName + '.' + fileext, function(){
console.log("removed " +filepath+'server/uploads/'+fName + '.' + fileext);
actualFileName.push(filename);
storedFileName.push(fName+'.'+'pdf');
});
});
});
});
});
busboy.on('field', function(fieldname, val, valTruncated,keyTruncated) {
noteData = JSON.parse(val);
});
busboy.on('finish',function(){
noteData.uploader = req.user.username;
noteData.actualFileName = actualFileName;
noteData.storedFileName = storedFileName;
noteData.noteId = uuid.v4();
Campusnotes.create(noteData,function(err,note){
if(err){
res.status(400);
return res.send({reason:err.toString()});
}
console.log('finish');
res.status(200);
res.end();
})
});
now the console log for this is as follows -
finish
uploaded now
convert /home/unknown/public_html/campustop/server/uploads/8465f9a9-d6b7-4d53-8cb5-a8dbf3aed6a5.odt -> /home/unknown/public_html/campustop/server/uploads/8465f9a9-d6b7-4d53-8cb5-a8dbf3aed6a5.pdf using writer_pdf_Export
removed /home/unknown/public_html/campustop/server/uploads/8465f9a9-d6b7-4d53-8cb5-a8dbf3aed6a5.odt
indicating that the finish event is getting fired again and again
You could try something like:
var files = 0;
busboy.on('file', function(fieldname, file, filename,transferEncoding,mimeType) {
++files;
var fName = uuid.v4();
var fileext = filename.substr(filename.lastIndexOf('.') + 1);
var filepath = path.normalize(__dirname + '/../../');
var fstream = fs.createWriteStream(filepath+'/server/uploads/'+fName+'.'+fileext);
file.pipe(fstream).on('finish',function() {
console.log('uploaded now');
var cmd = 'libreoffice --headless --convert-to pdf --outdir '+ filepath + 'server/uploads ' + filepath + 'server/uploads/' + fName + '.' + fileext;
exec(cmd, function(error,stdout,stderr) {
console.log(stdout);
var encryptCmd = 'java -jar server/uploads/pdfbox-app-1.8.6.jar Encrypt -canAssemble false -canExtractContent false -canExtractForAccessibility false ' +
'-canModify false -canModifyAnnotations false -canPrint false -canPrintDegraded false server/uploads/' + fName + '.' + 'pdf'
+ ' ' + 'server/uploads/' +fName + '.' + 'pdf';
exec(encryptCmd, function(error,stdout,stderr) {
fs.unlink(filepath+'server/uploads/'+fName + '.' + fileext, function() {
console.log("removed " +filepath+'server/uploads/'+fName + '.' + fileext);
actualFileName.push(filename);
storedFileName.push(fName+'.'+'pdf');
});
});
--files;
onFinish();
});
});
});
busboy.on('field', function(fieldname, val, valTruncated,keyTruncated) {
noteData = JSON.parse(val);
});
busboy.on('finish', onFinish);
function onFinish() {
if (!busboy.writable && files === 0) {
noteData.uploader = req.user.username;
noteData.actualFileName = actualFileName;
noteData.storedFileName = storedFileName;
noteData.noteId = uuid.v4();
Campusnotes.create(noteData,function(err,note){
if (err){
res.status(400);
return res.send({reason:err.toString()});
}
console.log('finish');
res.status(200);
res.end();
});
}
}
On an unrelated note, you should probably do some sanitizing/checking of the filename, someone could be malicious and use something like '../../../../../../../../../etc/passwd' (I'm not sure if createWriteStream() resolves/normalizes the path given to it or not).