How to log console to files & console with timestamps?

Heey there!

I had this question on what people use or even know how to achiev that the console logs to a file with timestamps! (timestamps is more for the file, the console it self is fine as it is right now!)

This is mostly because i run a discord bot currently, but i can’t be there 24/7 to monitor when an Error happens…

I’ve tried the >>“file name here.txt” which works to an extend, but it removes the logging in the console :frowning:
and no timestamps!

I’ve looked at a module called “winston” i believe, but my main goal is that i don’t have to replace any “console.log” that i have in my code & just keep it simple :smiley:

Any advice on how to achiev this with either code or modules is greatly appreciated!

Here is a cookie for anyone that took their time :cookie:

I think you can use this method with the fs module to write the message into a text file https://stackoverflow.com/questions/11403107/capturing-javascript-console-log

1 Like

Thanks for sharing this method!

I’m currently quite tired to try this out haha, it looks promising so i’ll definitely try it out tomorrow!

Regardless of outcome, i’ll feedback here if it indeed helped me achiev what i want or not :wink:

Hey! Some while ago I created an npm package discord-blackhole.logger which contains some of the features you need. That module supports writing to the console, using process.stdout.write so it won’t cause any maximum call stack or whatever the error is called.

And I also made a little module/script right now that will do exactly what you want.

Before you start.

There is two npm modules you need to install:

npm i -S mkdirp-sync && npm i -S discord-blackhole.logger

then you can take this into any file.

const Logger = require("discord-blackhole.logger");
const mkdirp = require("mkdirp-sync");
const fs = require("fs");

class myLogger extends Logger.class
{
	constructor (path)
	{
		super();
		this.path = path;
		this.assureFile(this.path);
	}
	
	assureDirectory (dirname)
	{
		if (!fs.existsSync(dirname)) mkdirp(dirname);
	}
	
	assureFile (filename)
	{
		if (!fs.existsSync(require("path").dirname(filename))) this.assureDirectory(require("path").dirname(filename));
		if (!fs.existsSync(filename)) fs.writeFileSync(filename, "");
	}
	
	append (content)
	{
		this.assureFile(this.path);
		fs.appendFile(this.path, content, function (err) {
			if (err) process.stdout.write("ERROR Failed to write file!");
		});
	}
	
	timeNoColor ()
	{
		var time = this.format;
		const Time = this.getTimeObject();
		for (var key in Time)
		{
			const value = Time[key];
			time = time.split("{" + key + "}").join(value);
		}
		
		return time;
	}
	
	formatNoColor (type, message)
	{
		const v = (this.timeNoColor() + " " + type + " " + message.join(" ") + "\n").replace(/\u001B\[[0-9][0-9]m/gi, "");
		return v;
	}
	
	log ()
	{
		const args = Array.prototype.slice.call(arguments);
		const msg = this.formatNoColor("   ", args);
		this.append(msg);
		super.log(...args);
		return this;
	}
	
	debug ()
	{
		const args = Array.prototype.slice.call(arguments);
		const msg = this.formatNoColor("DEB", args);
		this.append(msg);
		super.debug(...args);
		return this;
	}
	
	info ()
	{
		const args = Array.prototype.slice.call(arguments);
		const msg = this.formatNoColor("INF", args);
		this.append(msg);
		super.info(...args);
		return this;
	}
	
	alert ()
	{
		const args = Array.prototype.slice.call(arguments);
		const msg = this.formatNoColor("ALE", args);
		this.append(msg);
		super.alert(...args);
		return this;
	}
	
	success ()
	{
		const args = Array.prototype.slice.call(arguments);
		const msg = this.formatNoColor("SUC", args);
		this.append(msg);
		super.success(...args);
		return this;
	}
	
	warning ()
	{
		const args = Array.prototype.slice.call(arguments);
		const msg = this.formatNoColor("WAR", args);
		this.append(msg);
		super.warning(...args);
		return this;
	}
	
	warn ()
	{
		const args = Array.prototype.slice.call(arguments);
		const msg = this.formatNoColor("WAR", args);
		this.append(msg);
		super.warn(...args);
		return this;
	}
	
	error ()
	{
		const args = Array.prototype.slice.call(arguments);
		const msg = this.formatNoColor("ERR", args);
		this.append(msg);
		super.error(...args);
		return this;
	}
}

module.exports = p =>
{
	const l = new myLogger(require("path").resolve(p));
	
	const o_log = console.log;
	const o_warn = console.warn;
	const o_err = console.error;
	const o_debug = console.debug;
	const o_info = console.info;
	
	console.log = function () {
		const args = Array.prototype.slice.call(arguments);
		l.log(...args);
	}
	
	console.debug = function () {
		const args = Array.prototype.slice.call(arguments);
		l.debug(...args);
	}
	
	console.info = function () {
		const args = Array.prototype.slice.call(arguments);
		l.info(...args);
	}
	
	console.alert = function () {
		const args = Array.prototype.slice.call(arguments);
		l.alert(...args);
	}
	
	console.success = function () {
		const args = Array.prototype.slice.call(arguments);
		l.success(...args);
	}
	
	console.warning = function () {
		const args = Array.prototype.slice.call(arguments);
		l.warning(...args);
	}
	
	console.warn = function () {
		const args = Array.prototype.slice.call(arguments);
		l.warn(...args);
	}
	
	console.error = function () {
		const args = Array.prototype.slice.call(arguments);
		l.error(...args);
	}
	
	console.reset = function () {
		console.log = o_log;
		console.debug = o_debug;
		console.warn = o_warn;
		console.error = o_err;
		console.info = o_info;
		console.warning = undefined;
		console.success = undefined;
		console.alert = undefined;
	}
	
	return l;
}

It’s used like this:

const Logger = require("path/to/file")(__dirname + "/logs.txt");
console.log("Hello world");

The methods available for console is now:
log, debug, info, alert, warn or warning, error, success

to revert the changes made to console, console.reset().

Here’s some screenshots:
output

logs

4 Likes

Facinating work you got there! When i first saw it on my phone i was a bit confused haha, but that’s more due the layout of the phone and the fact i woke up literally 5 mins before :stuck_out_tongue:

I’ll definitely check it out as it looks very promising! I think i’ll use this over the method that has been mentioned above (sorry!) as it looks more global on what i seek and more pre-done for me :smiley:

I’ll give feedback as soon i’ve tested it out!
Thanks for sharing this with me <3

Okay so i tested it out, so far it looks pretty neat and clean! (Just the way i like it)

Only issue i seem to have, is that it doesn’t write down the error that might case to crash the app :thinking:
I don’t think in such case that it will use the “console” function… I don’t understand perfectly how that “>>” works when i added that in the package file (start: “node server.js >>log.txt”) but it looks like it redirects the build in console from glitch to a file (so i can’t use both :frowning: )

any idea how i could achiev this? :smiley:

Thanks for the time and effort regardless!

You can change the method it writes to file so it writes it synchronous, just add Sync after fs.appendfile in the append method. That way it will store everything before a crash.

1 Like

Thanks for the reply! I did try this just now, it however did not store the error it self tho :confused:

an example of what i want to have stored is something like this: TypeError: Cannot read property ‘test’ of undefined

there is obviously a lot more of this error added, but i think the general idea should be pretty clear :smiley:

this is a very specific error that i cause my self (but trying to access a database data that does not exsist)

You can use the process’ event emitter

proccess.on("error", console.error);
1 Like

Although I do not know if this is the correct way of catching the error, that is something you probably should google up.

“How to catch process errors in node.js”

1 Like

Ah wonderful! Very much appreciated!

I never really know on what i needed to search, thanks to your hints in the directions i found it perfeclty nice!
adding the following code worked!
process.on("uncaughtException", function (err) { console.error("uncaughtException:", err.message) //console.error(err.stack) });

With this it does actual log the error under the Error tag :smiley: Note; Your app has to be running first tho (this is obviously desired) as i tried to test it by manually making an error, but it won’t log it before it actually started in the first place :joy:

Lots of thanks for helping me out with this!

I got one last question, which is more a minor thing; How can i change the date format & the time? It’s 2 hours behind on my time haha (i am looking into it, but if by any chance you’re faster in replying then i can find it then it makes it only easier!)

Thanks again <3 have a cookie :cookie:

Oh and when i’m right at it, is it possible to have colored prints in the console? I love the pretty colors and help me notice an error sooner haha

Thanks in advance!

EDIT:

Changing the date format is very easy, in the source files of my blackhole-logger I’ve documented how to, I’ll put them in here and give you a small example.

###*
# Change how the date is displayed in the terminal.
#
# The available variables are:
# * year
# * month
# * monthName
# * monthNameLowercased
# * monthNameShort
# * monthNameShortLowercasesd
# * date
# * day
# * dayName
# * dayNameLowercased
# * dayNameShort
# * dayNameShortLowercased
# * hour
# * minute
# * second
# * millisecond
#
# You call them by putting `{` before the variable and `}` after the variable.
#
# @param {string} [format="{year}/{month}/{date} {hour}:{minute}:{second} {millisecond}"] The format.
# @returns {Logger}
###

Here’s an example on how to change it.

// NOTE: You will need the logger object returned from myLogger/the method.

const Logger = require("./path/to/Logger.js")("path/to/logs.txt");
Logger.setFormat("{year}/{month}/{date} {hour}:{minute}:{second} {millisecond}");

With the code above you can change the format to however you’d like it.

In the code I sent you, you have the possibility to do console.error which will print out text with red color in the terminal.

console.log(); // Default: White
console.debug(); // Gray
console.info(); // Blue
console.alert(); // Cyan
console.warn(); /* or */ console.warning(); // Yellow
console.error(); // Red
console.success(); // Green

If you keep the Logger object that is returned by myLogger then you can change how it works, for example, if you want the error color to be blue isntead of red, and it will also be bold with an underline, and info color to be red instead of blue you can do something like this:

NOTE: You need to install chalk to change the colors and styles.

const chalk = require("chalk");
const Logger = require("./path/to/file")("path/to/logs");
Logger.theme.info = chalk.red;
Logger.theme.error = chalk.blue.bold.underline;

However, there is not all terminals that supports ASCII colors. But if you do see a color in your terminal other than your default background color and default foreground color then your terminal does support it.

1 Like

Ah wonderful! The set format is just exactly what i wanted!

Thanks again for all the amazing help so far, my logs looks a lot more smooth & a lot more readable haha

Anyhow, you said that the “error” should show up as red, however it does not by default :thinking:
is it possible to get the default colors to turn on without the need of chalk?
I’m purely interested in the default colors :smiley:

My observation did notice that there are functions in your script that says “timeNoColor” for example, however i doubt this applies to the “error” color :smiley:

so yeah, when i do console.error(“test”); just for example, it refuses to give it any colors :thinking:

Thanks again for the amazing help! <3

EDIT:
oh that actually reminds me, it still shows the hours by 2 off :thinking: is there a way to get get this up by any chance? :smiley:

Hi, if you have Discord I will be able to help you faster I’m not on this site 24/7, if you have Discord please send me a direct message containing you Discord Tag, or if you don’t just reply here that you don’t have Discord.

1 Like

Sending you in a min!

Okay scratch that, i got no clue how i can DM you over here haha

i’ll just post the discord tag here, when you added me i’ll just edit the post :ok_hand:

I know this is old, but this was exactly what i was looking for… how ever the text that printed to the file has a line “log” appended to it, and the next console.log is appended on that same line

Why not using winston? It’s very simple and easy solution for logging and saving logs. That’s my config, for instance:

const { createLogger, format, transports } = require('winston')
const { combine, timestamp, splat, json, errors, simple, printf } = format
const { COLORS, LEVELS } = require('../configs/log')

const ignorePrivate = format(info => {
  if (info.private) return false
  return info
})

const consoleFormat = printf(info => {
  let m = `${COLORS[info.level](`> [${info.level.toUpperCase()}]`)}  `

  if (info.stack) m += info.stack
  else m += info.message

  return m
})

const log = createLogger({
  level: 'info',
  levels: LEVELS,
  exitOnError: false,
  format: combine(
    timestamp(),
    errors({ stack: true }),
    splat(),
    json()
  ),
  transports: [
    new transports.File({
      filename: 'logs/error.log',
      format: ignorePrivate(),
      level: 'error',
      handleExceptions: true
    }),
    new transports.File({
      filename: 'logs/main.log',
      format: ignorePrivate(),
      level: 'command',
      handleExceptions: true
    }),
    new transports.Console({
      level: 'command',
      format: combine(simple(), consoleFormat),
      handleExceptions: true
    })
  ]
})

module.exports = log
1 Like