From PhantomJS, how do I write to a log instead of to the console?
In the examples https://github.com/ariya/phantomjs/wiki/Examples, it always (in the ones I have looked at) says something like:
console.log('some stuff I wrote');
This is not so useful.
The following can write contents to the file directly by phantomjs:
var fs = require('fs');
try {
fs.write("/home/username/sampleFileName.txt", "Message to be written to the file", 'w');
} catch(e) {
console.log(e);
}
phantom.exit();
The command in the answer by user984003 fails when there is some warning or exceptions occurred. And sometimes does not fall into our specific requirements because in some codebase I am getting the following message always which will also be logged to that file.
Refused to display document because display forbidden by X-Frame-Options.
So I figured it out:
>phantomjs.exe file_to_run.js > my_log.txt
You can override original console.log function, take a look at this :
Object.defineProperty(console, "toFile", {
get : function() {
return console.__file__;
},
set : function(val) {
if (!console.__file__ && val) {
console.__log__ = console.log;
console.log = function() {
var fs = require('fs');
var msg = '';
for (var i = 0; i < arguments.length; i++) {
msg += ((i === 0) ? '' : ' ') + arguments[i];
}
if (msg) {
fs.write(console.__file__, msg + '\r\n', 'a');
}
};
}
else if (console.__file__ && !val) {
console.log = console.__log__;
}
console.__file__ = val;
}
});
Then you can do this:
console.log('this will go to console');
console.toFile = 'test.txt';
console.log('this will go to the test.txt file');
console.toFile = '';
console.log('this will again go to the console');
Related
I've been scouring similar problems but haven't seem to have found a solution that quite works on my end. So I'm working on a Discord bot that takes data from a MongoDB database and displays said data in the form of a discord embedded message using Mongoose. For the most part, everything is working fine, however one little section of my code is giving me trouble.
So I need to import an array of both all available users and the "time" data of each of those users. Here is the block of code I use to import said data:
for (i = 0;i < totalObj; i++){
timeArray[i] = await getData('time', i);
userArray[i] = await getData('user', i);
}
Now this for loop references a function I made called getData which obtains the data from MongoDB by this method:
async function getData(field, value){
var data;
await stats.find({}, function(err, result){
if(err){
result.send(err);
}else{
data = result[value];
}
});
if(field == "user"){
return data.user;
}else if (field == "time"){
return data.time;
}else{
return 0;
}
So that for loop is where my errors currently lie. When I try to run this code and display my data through a discord message, I get this error and the message does not get sent:
(node:13936) UnhandledPromiseRejectionWarning: TypeError: Cannot read property 'time' of undefined
Now the strange thing is, this error does not happen every time. If I continue calling the command that triggers this code from my discord server, it's almost like a 50/50 shot if the command actually shows the message or instead gives this error. It is very inconsistent.
This error is confounding me, as the undefined part does not make sense to me. The objects that are being searched for in the mongoDB collection are definitely defined, and the for loop never exceeds the number of objects present. My only conclusion is that I'm doing something wrong with my asynchronous function design. I have tried altering code to use the getData function less often, or to not use awaits or asynchronous design at all, however this leaves my final discord message with several undefined variables and an eventual crash.
If anyone has any advice or suggestions, that would be very much appreciated. Just for reference, here is the full function that receives the data, sorts it, and prepares a string to be displayed on the discord server (though the error only seems to occur in the first for loop):
async function buildString(){
var string = "";
var totalObj;
var timeArray = [];
var userArray = [];
var stopSort = false;
await stats.find({}, function(err, result){
if(err){
result.send(err);
}else{
totalObj = result.length;
}
});
for (i = 0;i < totalObj; i++){
timeArray[i] = await getData('time', i);
userArray[i] = await getData('user', i);
}
while(!stopSort){
var keepSorting = false;
for(i = 0; i < totalObj ; i++){
var target = await convertTime(timeArray[i]);
for(j = i + 1 ; j < totalObj ; j++){
var comparison = await convertTime(timeArray[j]);
if(target > comparison){
//Switch target time with comparison time so that the lower time is up front
var temp = timeArray[i];
timeArray[i] = timeArray[j];
timeArray[j] = temp;
//Then switch the users around so that the user always corresponds with their time
var userTemp = userArray[i];
userArray[i] = userArray[j];
userArray[j] = userTemp;
//The loop will continue if even a single switch is made
keepSorting = true;
}
}
}
if(!keepSorting){
stopSort = true;
}
}
//String building starts here
var placeArray = [':first_place: **1st', ':second_place: **2nd', ':third_place: **3rd', '**4th', '**5th', '**6th', '**7th', '**8th', '**9th', '**10th'];
for(i = 0; i < totalObj; i++){
string = await string.concat(placeArray[i] + ": " + userArray[i] + "** - " + timeArray[i] + " \n\n");
console.log('butt');
}
console.log("This String:" + string);
return string;
}
I think problem is you are trying to await function with callback, it will not work => access to data.time may run before data = result[value]. If you need await callback, you can use custom Promise (or use util.promisify, more info here)
Promise:
function findStats(options) {
return new Promise((resolve, reject) => {
return stats.find(options, function (err, result) {
if (err) {
return reject(err)
}
return resolve(result)
})
})
}
utils.promisify
const util = require('util');
const findStats = util.promisify(stats.find);
Now you can use await in your function
async function getData(field, value) {
try {
const result = await findStats({})
const data = result.value
if (field === 'user') {
return data.user
}
if (field === 'time') {
return data.time
}
return 0
} catch (error) {
// here process error the way you like
// or remove try-catch block and sanitize error in your wrap function
}
}
I have a config.js file which I believe is JSON which is called when the application first starts:
var config={};
config.user = [
{id:'JSMITH', priceModify:'true'},
{id:'JBLOGGS', priceModify:'false'},
]
config.price = [
{id:"price01", name:"priceName01", primary:"57.25", secondary:"34.54"},
{id:"price02", name:"priceName02", primary:"98.26", secondary:"139.45"},
{id:"price03", name:"priceName03", primary:"13.87", secondary:"29.13"}
]
To pull / push data I just use the following:
// Read
var curPrice = config.price[0].primary;
// Write
config.price[0].primary = "98.24";
How do I go about exporting the config file with the new value so that it will load next time the application is opened? I can use the file system object to write the file, I just don't understand how I would export everything (and preferably keep the same format).
I originally thought about reading the whole config file into a variable, cycling through to find the required block, id, and key and replacing the value, then writing the whole thing back, but I can't seem to figure out how to replace that specific value only.
Any help would be greatly appreciated
Edit Apologies, I forgot to mention that this application is completely offline and uses local directories
Solution
I stumbled across a few solutions to different issues which, when combined, gave me the perfect solution. First we cycle the Javascript object, building an array of the detail and then converting the array to a string:
vMethod.convertToText = function(obj) {
var string = [];
var output = '';
var count= 0;
var countTotal = 0;
if (typeof(obj) == "object" && (obj.join == undefined)) {
count= 0;
countTotal = 0;
string.push("{");
for (prop in obj) {
countTotal++;
}
for (prop in obj) {
if(count==countTotal - 1) {
string.push(prop, ": ", vMethod.convertToText(obj[prop]),'}\r\n');
} else {
string.push(prop, ": ", vMethod.convertToText(obj[prop]), ",");
}
count++;
};
} else if (typeof(obj) == "object" && !(obj.join == undefined)) {
count= 0;
countTotal = 0;
string.push("[\r\n")
for (prop in obj) {
countTotal++;
}
for(prop in obj) {
if(count==countTotal - 1) {
string.push(vMethod.convertToText(obj[prop]),'];\r\n');
} else {
string.push(vMethod.convertToText(obj[prop]), ",");
}
count++;
}
} else if (typeof(obj) == "function") {
string.push(obj.toString())
} else {
string.push(JSON.stringify(obj))
}
output = string.join("").toString();
//output = output.slice(1, -1);
return output;
}
Then we clean the array (neccessary for me to remove excess characters)
vMethod.cleanConfigText = function() {
var outputText = vMethod.convertToText(config);
outputText = outputText.slice(1, -1);
outputText = 'var config = {};\r\n'+outputText;
outputText = outputText.replace('user:','config.user =');
outputText = outputText.replace(',price:','config.price =');
outputText = outputText.slice(0, -2);
outputText = outputText.replace(/"/g, "'")
return outputText;
}
Finally a function to export the object into my config.js file:
vMethod.writeToConfig = function() {
vObject.fileSystem = new ActiveXObject('Scripting.FileSystemObject');
vObject.fileSystemFile = vObject.fileSystem.CreateTextFile('source\\js\\config.js',true);
vObject.fileSystemFile.Write(vMethod.cleanConfigText());
vObject.fileSystemFile.Close();
delete vObject.fileSystemFile;
delete vObject.fileSystem;
}
So when I want to export a change in the config, I just call:
vMethod.writeToConfig();
The only difference in the file format is that the commas appear at the start of a trailing line rather than the end of a preceding line but I can live with that!
Edit Turns out I'm anally retentive and the commas were bugging me
Added these to the clean up function and now the config is identical to before but without the indent
outputText = outputText.replace(/[\n\r]/g, '_');
outputText = outputText.replace(/__,/g, ',\r\n');
outputText = outputText.replace(/__/g, '\r\n');
Thank you to those that looked at the question and tried to help, very much appreciated.
Edit
DO NOT READ THE SOLUTION ABOVE, IT IS IN THE WRONG PLACE AND THERFORE IS NOT A VALID ANSWER. YOU'VE BEEN WARNED.
You can use a very popular npm package: https://www.npmjs.com/package/jsonfile . There are many but I've choosen this one.
Usually config stuff should be in json or .env files.
Now, all you have to do is use jsonfile's API to read/write JSON and parse (the package does the serialization/deserialization) it at the beginning when the application starts.
Example:
var jsonfile = require('jsonfile');
var util = require('util');
var config = null;
var file = './config.json';
// Reading
jsonfile.readFile(file, function(err, obj) {
config = obj;
});
// Writing
// Edit your config blah blah
config.user = [
{id:'JSMITH', priceModify:'true'},
{id:'JBLOGGS', priceModify:'false'},
];
config.price = [
{id:"price01", name:"priceName01", primary:"57.25", secondary:"34.54"},
{id:"price02", name:"priceName02", primary:"98.26", secondary:"139.45"},
{id:"price03", name:"priceName03", primary:"13.87", secondary:"29.13"}
];
jsonfile.writeFile(file, config, function (err) {
if(err) return err;
console.log('Config saved to file!');
});
I'm trying to setup plupload so that if a file upload fails according to a certain error code, plupload will try to upload it again. Without success.
What I currently have
Here is my piece of code for now :
var attempts = 0; // number of attempts
function init(uploader) {
uploader.bind('Error', function(up, file) {
var code = JSON.parse(file.response).error.code;
console.log(code); //logs the error code
if(code === 503 && attempts > 3) {
up.stop();
file.status = plupload.FAILED;
file.retry = false;
// do stuff...
up.state = plupload.STARTED;
up.trigger("StateChanged");
attempts = 0;
}
else {
console.log("attempt : "+ attempts);
up.stop();
file.status = plupload.QUEUED;
attempts++;
file.retry = true;
up.state = plupload.STARTED;
up.trigger("StateChanged");
up.trigger("QueueChanged"); // ERROR HERE
}
})
}
Where I indicated the "ERROR HERE", my console shows me this :
TypeError: n.getSource is not a function
What am I doing wrong ?
Thanks a lot !
Why don't you add the file again on the queue?
http://www.plupload.com/docs/Uploader#addFile-filefileName-method
addFile(file, [fileName])
In my AngularJS controller, I have the following code:
$scope.readMDB = function () {
var fs = require('fs');
var adodb = require('node-adodb');
var password = document.forms["mdbSelForm"]["pwd"].value;
var revSQL = fs.readFileSync('revenue.sql', 'utf-8');
revSQL = revSQL.replace(/(\r\n|\n|\r)/gm, " ");
revSQL = revSQL.replace(/[รค]/g, function () { return unescape("%E4") });
$scope.revenueSQL = String(revSQL);
console.log("revSQL: " + $scope.revenueSQL);
connection = adodb.open('Provider=Microsoft.Jet.OLEDB.4.0;Data Source=' + $scope.selectedMDB + ';Jet OLEDB:Database Password=' + password + ';');
// debug
adodb.debug = true;
connection
.query($scope.revenueSQL)
.on('done', function (data) {
var revAll = JSON.stringify(data);
var revenueData = JSON.parse(revAll).records;
fs.writeFile('./model/revenue.json', JSON.stringify(data), function (err) {
if (err) throw err;
console.log("revenue.json saved");
});
return true;
})
.on('fail', function (data) {
return false;
});
}
A similar code worked fine while not using Angular. Now, it doesn't work no more, because of the
connection.query($scope.revenueSQL)
part. More precisely, the $scope.revenueSQL is not recognized as the SQL Statement that it actually is. Putting the SQL directly, without reading it from a file, works fine. Still, looking at my console, I see that $scope.revenueSQL is exactly what I want. But being put as parameter into .query(), something seems to go wrong. Any ideas?
Try
$scope.revenueSQL = $scope.$eval(String(revSQL));
to execute the expression on the current scope and returns the result.
My Cordova app not running in browser and mobile it shows an error
processMessage failed
Screenshot:
and goes infinite loop and it freezes the device any solution?
This question is already in asked here Cordova not running normally but there is not an answer so thats why I have to asked it again.
Getting the same issue (using Chrome with the phonegap desktop emulator. What I see as happening is this.
There seems to be a bug in Cordova.js that fails to check for an empty message.
When the app sends out alerts:
gap_init:2
gap:[0,"StatusBar","_ready","StatusBar1593157203"]
gap:[0,"App","show","App1593157204"]
gap:[0,"File","requestAllPaths","File1593157205"]
gap:[0,"NetworkStatus","getConnectionInfo","NetworkStatus1593157206"]
gap:[0,"Device","getDeviceInfo","Device1593157207"]
and you just hit 'OK', instead of clearing out the contents of that dialog box it going on to cause an infinite loooooop. I don't know the significance of these messages yes as I'm pretty new to Cordova, but it's hell and gone from the principle of least surprise!
So you can clear out the messages, or modify the cordova.js code where it gets stuck in the loop. You also could turn off the alerts that also works.
the function processMessage() (see below) doesn't test for an empty string, which in and of itself might be fine, but it is called from a while loop which only checks for "*" if its going to pop.
while (messagesFromNative.length) {
var msg = popMessageFromQueue();
// The Java side can send a * message to indicate that it
// still has messages waiting to be retrieved.
if (msg == '*' && messagesFromNative.length === 0) {
setTimeout(pollOnce, 0);
return;
}
processMessage(msg);
}
// Processes a single message, as encoded by NativeToJsMessageQueue.java.
function processMessage(message) {
try {
var firstChar = message.charAt(0);
if (firstChar == 'J') {
eval(message.slice(1));
} else if (firstChar == 'S' || firstChar == 'F') {
var success = firstChar == 'S';
var keepCallback = message.charAt(1) == '1';
var spaceIdx = message.indexOf(' ', 2);
var status = +message.slice(2, spaceIdx);
var nextSpaceIdx = message.indexOf(' ', spaceIdx + 1);
var callbackId = message.slice(spaceIdx + 1, nextSpaceIdx);
var payloadKind = message.charAt(nextSpaceIdx + 1);
var payload;
if (payloadKind == 's') {
payload = message.slice(nextSpaceIdx + 2);
} else if (payloadKind == 't') {
payload = true;
} else if (payloadKind == 'f') {
payload = false;
} else if (payloadKind == 'N') {
payload = null;
} else if (payloadKind == 'n') {
payload = +message.slice(nextSpaceIdx + 2);
} else if (payloadKind == 'A') {
var data = message.slice(nextSpaceIdx + 2);
var bytes = window.atob(data);
var arraybuffer = new Uint8Array(bytes.length);
for (var i = 0; i < bytes.length; i++) {
arraybuffer[i] = bytes.charCodeAt(i);
}
payload = arraybuffer.buffer;
} else if (payloadKind == 'S') {
payload = window.atob(message.slice(nextSpaceIdx + 2));
} else {
payload = JSON.parse(message.slice(nextSpaceIdx + 1));
}
cordova.callbackFromNative(callbackId, success, status, [payload], keepCallback);
} else {
console.log("processMessage failed: invalid message: " + JSON.stringify(message));
}
} catch (e) {
console.log("processMessage failed: Error: " + e);
console.log("processMessage failed: Stack: " + e.stack);
console.log("processMessage failed: Message: " + message);
}
}
Check your cordova js loading properly? is the path for cordova js is proper?
give path like this in your index.html:
<script type="text/javascript" src="cordova.js">
I had the problem in an Angular 6 project. It was simply solved by deleting cordova.js which was under src folder.
I was not able to resolve this issue when viewing the /android platform option for cordova serve; however, the /ios platform option did function properly.
Not much of a solution, but perhaps a mildly helpful workaround for those who follow.