Skip to content
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

Support for custom logger function #1468

Closed
wants to merge 6 commits into from
Closed
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
45 changes: 39 additions & 6 deletions lib/console.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,13 @@

const util = require('util');

const LOGGER_FUNC_TYPE_LOG = process._logger.LOGGER_FUNC_TYPE_LOG;
const LOGGER_FUNC_TYPE_INFO = process._logger.LOGGER_FUNC_TYPE_INFO;
const LOGGER_FUNC_TYPE_WARN = process._logger.LOGGER_FUNC_TYPE_WARN;
const LOGGER_FUNC_TYPE_ERROR = process._logger.LOGGER_FUNC_TYPE_ERROR;
const LOGGER_FUNC_TYPE_DIR = process._logger.LOGGER_FUNC_TYPE_DIR;


function Console(stdout, stderr) {
if (!(this instanceof Console)) {
return new Console(stdout, stderr);
Expand All @@ -23,6 +30,27 @@ function Console(stdout, stderr) {
Object.defineProperty(this, '_stderr', prop);
prop.value = new Map();
Object.defineProperty(this, '_times', prop);
prop.value = function(type, message) {
message += '\n';
if (process._logger(type, message)) {
// Log message is processed by a custom logger function
return;
}
// Default logger
switch (type) {
case LOGGER_FUNC_TYPE_LOG:
case LOGGER_FUNC_TYPE_INFO:
case LOGGER_FUNC_TYPE_DIR:
this._stdout.write(message);
break;

case LOGGER_FUNC_TYPE_WARN:
case LOGGER_FUNC_TYPE_ERROR:
this._stderr.write(message);
break;
}
};
Object.defineProperty(this, '_logger', prop);

// bind the prototype functions to this Console instance
var keys = Object.keys(Console.prototype);
Expand All @@ -32,26 +60,31 @@ function Console(stdout, stderr) {
}
}


Console.prototype.log = function() {
this._stdout.write(util.format.apply(this, arguments) + '\n');
this._logger(LOGGER_FUNC_TYPE_LOG, util.format.apply(this, arguments));
};


Console.prototype.info = Console.prototype.log;
Console.prototype.info = function() {
this._logger(LOGGER_FUNC_TYPE_INFO, util.format.apply(this, arguments));
};


Console.prototype.warn = function() {
this._stderr.write(util.format.apply(this, arguments) + '\n');
this._logger(LOGGER_FUNC_TYPE_WARN, util.format.apply(this, arguments));
};


Console.prototype.error = Console.prototype.warn;
Console.prototype.error = function() {
this._logger(LOGGER_FUNC_TYPE_ERROR, util.format.apply(this, arguments));
};


Console.prototype.dir = function(object, options) {
this._stdout.write(util.inspect(object, util._extend({
this._logger(LOGGER_FUNC_TYPE_DIR, util.inspect(object, util._extend({
customInspect: false
}, options)) + '\n');
}, options)));
};


Expand Down
113 changes: 86 additions & 27 deletions src/node.cc
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,8 @@ static uv_async_t dispatch_debug_messages_async;
static Isolate* node_isolate = nullptr;
static v8::Platform* default_platform;

static logger_func custom_logger = nullptr;

class ArrayBufferAllocator : public ArrayBuffer::Allocator {
public:
// Impose an upper limit to avoid out of memory errors that bring down
Expand Down Expand Up @@ -920,6 +922,50 @@ Local<Value> WinapiErrnoException(Isolate* isolate,
#endif


#define LOG_STDERR(...) \
if (custom_logger == NULL || \
!custom_logger(LOGGER_FUNC_TYPE_STDERR, __VA_ARGS__)) \
{ \
fprintf(stderr, __VA_ARGS__); \
fflush(stderr); \
}


#define LOG_STDOUT(...) \
if (custom_logger == NULL || \
!custom_logger(LOGGER_FUNC_TYPE_STDOUT, __VA_ARGS__)) \
{ \
fprintf(stdout, __VA_ARGS__); \
fflush(stdout); \
}


logger_func SetLogger(logger_func func) {
logger_func prev = custom_logger;
custom_logger = func;
return prev;
}


void LoggerCallback(const FunctionCallbackInfo<Value>& args) {
if (custom_logger) {
Environment* env = Environment::GetCurrent(args);

HandleScope scope(env->isolate());

CHECK(args[0]->IsNumber());
CHECK(args[1]->IsString());

logger_func_type func_type =
static_cast<logger_func_type>(args[0]->IntegerValue());
node::Utf8Value message(env->isolate(), args[1].As<String>());

if (custom_logger(func_type, *message))
args.GetReturnValue().Set(True(env->isolate()));
}
}


void SetupDomainUse(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args);

Expand All @@ -935,7 +981,7 @@ void SetupDomainUse(const FunctionCallbackInfo<Value>& args) {
process_object->Get(tick_callback_function_key).As<Function>();

if (!tick_callback_function->IsFunction()) {
fprintf(stderr, "process._tickDomainCallback assigned to non-function\n");
LOG_STDERR("process._tickDomainCallback assigned to non-function\n");
abort();
}

Expand Down Expand Up @@ -1264,14 +1310,14 @@ enum encoding ParseEncoding(const char* encoding,
return HEX;
} else if (strcasecmp(encoding, "raw") == 0) {
if (!no_deprecation) {
fprintf(stderr, "'raw' (array of integers) has been removed. "
"Use 'binary'.\n");
LOG_STDERR("'raw' (array of integers) has been removed. "
"Use 'binary'.\n");
}
return BINARY;
} else if (strcasecmp(encoding, "raws") == 0) {
if (!no_deprecation) {
fprintf(stderr, "'raws' encoding has been renamed to 'binary'. "
"Please update your code.\n");
LOG_STDERR("'raws' encoding has been renamed to 'binary'. "
"Please update your code.\n");
}
return BINARY;
} else {
Expand Down Expand Up @@ -1310,8 +1356,8 @@ ssize_t DecodeBytes(Isolate* isolate,
HandleScope scope(isolate);

if (val->IsArray()) {
fprintf(stderr, "'raw' encoding (array of integers) has been removed. "
"Use 'binary'.\n");
LOG_STDERR("'raw' encoding (array of integers) has been removed. "
"Use 'binary'.\n");
UNREACHABLE();
return -1;
}
Expand Down Expand Up @@ -1433,7 +1479,7 @@ void AppendExceptionLine(Environment* env,
return;
env->set_printed_error(true);
uv_tty_reset_mode();
fprintf(stderr, "\n%s", arrow);
LOG_STDERR("\n%s", arrow);
}


Expand All @@ -1455,7 +1501,7 @@ static void ReportException(Environment* env,

// range errors have a trace member set to undefined
if (trace.length() > 0 && !trace_value->IsUndefined()) {
fprintf(stderr, "%s\n", *trace);
LOG_STDERR("%s\n", *trace);
} else {
// this really only happens for RangeErrors, since they're the only
// kind that won't have all this info in the trace, or when non-Error
Expand All @@ -1475,15 +1521,13 @@ static void ReportException(Environment* env,
name->IsUndefined()) {
// Not an error object. Just print as-is.
node::Utf8Value message(env->isolate(), er);
fprintf(stderr, "%s\n", *message);
LOG_STDERR("%s\n", *message);
} else {
node::Utf8Value name_string(env->isolate(), name);
node::Utf8Value message_string(env->isolate(), message);
fprintf(stderr, "%s: %s\n", *name_string, *message_string);
LOG_STDERR("%s: %s\n", *name_string, *message_string);
}
}

fflush(stderr);
}


Expand Down Expand Up @@ -2115,11 +2159,10 @@ void DLOpen(const FunctionCallbackInfo<Value>& args) {

static void OnFatalError(const char* location, const char* message) {
if (location) {
fprintf(stderr, "FATAL ERROR: %s %s\n", location, message);
LOG_STDERR("FATAL ERROR: %s %s\n", location, message);
} else {
fprintf(stderr, "FATAL ERROR: %s\n", message);
LOG_STDERR("FATAL ERROR: %s\n", message);
}
fflush(stderr);
abort();
}

Expand Down Expand Up @@ -2851,6 +2894,23 @@ void SetupProcessObject(Environment* env,
env->SetMethod(process, "_setupPromises", SetupPromises);
env->SetMethod(process, "_setupDomainUse", SetupDomainUse);

// Readonly _logger method
v8::Local<v8::Function> logger =
env->NewFunctionTemplate(LoggerCallback)->GetFunction();
READONLY_PROPERTY(process, "_logger", logger);
logger->SetName(v8::String::NewFromUtf8(env->isolate(), "_logger"));
// Logger function type constants
READONLY_PROPERTY(logger, "LOGGER_FUNC_TYPE_LOG",
Integer::New(env->isolate(), LOGGER_FUNC_TYPE_LOG));
READONLY_PROPERTY(logger, "LOGGER_FUNC_TYPE_INFO",
Integer::New(env->isolate(), LOGGER_FUNC_TYPE_INFO));
READONLY_PROPERTY(logger, "LOGGER_FUNC_TYPE_WARN",
Integer::New(env->isolate(), LOGGER_FUNC_TYPE_WARN));
READONLY_PROPERTY(logger, "LOGGER_FUNC_TYPE_ERROR",
Integer::New(env->isolate(), LOGGER_FUNC_TYPE_ERROR));
READONLY_PROPERTY(logger, "LOGGER_FUNC_TYPE_DIR",
Integer::New(env->isolate(), LOGGER_FUNC_TYPE_DIR));

// pre-set _events object for faster emit checks
process->Set(env->events_string(), Object::New(env->isolate()));
}
Expand Down Expand Up @@ -2885,8 +2945,7 @@ static void RawDebug(const FunctionCallbackInfo<Value>& args) {
CHECK(args.Length() == 1 && args[0]->IsString() &&
"must be called with a single string");
node::Utf8Value message(args.GetIsolate(), args[0]);
fprintf(stderr, "%s\n", *message);
fflush(stderr);
LOG_STDERR("%s\n", *message);
}


Expand Down Expand Up @@ -2982,7 +3041,7 @@ static bool ParseDebugOpt(const char* arg) {
if (port != nullptr) {
debug_port = atoi(port);
if (debug_port < 1024 || debug_port > 65535) {
fprintf(stderr, "Debug port must be in range 1024 to 65535.\n");
LOG_STDERR("Debug port must be in range 1024 to 65535.\n");
PrintHelp();
exit(12);
}
Expand All @@ -2992,7 +3051,8 @@ static bool ParseDebugOpt(const char* arg) {
}

static void PrintHelp() {
printf("Usage: iojs [options] [ -e script | script.js ] [arguments] \n"
LOG_STDOUT(
"Usage: iojs [options] [ -e script | script.js ] [arguments] \n"
" iojs debug script.js [arguments] \n"
"\n"
"Options:\n"
Expand Down Expand Up @@ -3080,7 +3140,7 @@ static void ParseArgs(int* argc,
if (ParseDebugOpt(arg)) {
// Done, consumed by ParseDebugOpt().
} else if (strcmp(arg, "--version") == 0 || strcmp(arg, "-v") == 0) {
printf("%s\n", NODE_VERSION);
LOG_STDOUT("%s\n", NODE_VERSION);
exit(0);
} else if (strcmp(arg, "--help") == 0 || strcmp(arg, "-h") == 0) {
PrintHelp();
Expand All @@ -3098,7 +3158,7 @@ static void ParseArgs(int* argc,
args_consumed += 1;
eval_string = argv[index + 1];
if (eval_string == nullptr) {
fprintf(stderr, "%s: %s requires an argument\n", argv[0], arg);
LOG_STDERR("%s: %s requires an argument\n", argv[0], arg);
exit(9);
}
} else if ((index + 1 < nargs) &&
Expand All @@ -3115,7 +3175,7 @@ static void ParseArgs(int* argc,
strcmp(arg, "-r") == 0) {
const char* module = argv[index + 1];
if (module == nullptr) {
fprintf(stderr, "%s: %s requires an argument\n", argv[0], arg);
LOG_STDERR("%s: %s requires an argument\n", argv[0], arg);
exit(9);
}
args_consumed += 1;
Expand Down Expand Up @@ -3196,8 +3256,7 @@ static void StartDebug(Environment* env, bool wait) {
DispatchMessagesDebugAgentCallback);
debugger_running = env->debugger_agent()->Start(debug_port, wait);
if (debugger_running == false) {
fprintf(stderr, "Starting debugger on port %d failed\n", debug_port);
fflush(stderr);
LOG_STDERR("Starting debugger on port %d failed\n", debug_port);
return;
}
}
Expand Down Expand Up @@ -3227,7 +3286,7 @@ static void EnableDebug(Environment* env) {
// Called from the main thread.
static void DispatchDebugMessagesAsyncCallback(uv_async_t* handle) {
if (debugger_running == false) {
fprintf(stderr, "Starting debugger agent.\n");
LOG_STDERR("Starting debugger agent.\n");

HandleScope scope(node_isolate);
Environment* env = Environment::GetCurrent(node_isolate);
Expand Down Expand Up @@ -3597,7 +3656,7 @@ void Init(int* argc,

// Anything that's still in v8_argv is not a V8 or a node option.
for (int i = 1; i < v8_argc; i++) {
fprintf(stderr, "%s: bad option: %s\n", argv[0], v8_argv[i]);
LOG_STDERR("%s: bad option: %s\n", argv[0], v8_argv[i]);
}
delete[] v8_argv;
v8_argv = nullptr;
Expand Down
17 changes: 17 additions & 0 deletions src/node.h
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,23 @@ NODE_EXTERN void Init(int* argc,
int* exec_argc,
const char*** exec_argv);


enum logger_func_type {
LOGGER_FUNC_TYPE_STDERR = 0,
LOGGER_FUNC_TYPE_STDOUT = 1,

LOGGER_FUNC_TYPE_LOG = 2,
LOGGER_FUNC_TYPE_INFO = 3,
LOGGER_FUNC_TYPE_WARN = 4,
LOGGER_FUNC_TYPE_ERROR = 5,
LOGGER_FUNC_TYPE_DIR = 6
};

typedef bool (*logger_func)(logger_func_type func_type, const char* fmt, ...);

NODE_EXTERN logger_func SetLogger(logger_func func);


class Environment;

NODE_EXTERN Environment* CreateEnvironment(v8::Isolate* isolate,
Expand Down