|  | @@ -31,12 +31,16 @@
 | 
	
		
			
				|  |  |   *
 | 
	
		
			
				|  |  |   */
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +#include <list>
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  #include <node.h>
 | 
	
		
			
				|  |  |  #include <nan.h>
 | 
	
		
			
				|  |  |  #include <v8.h>
 | 
	
		
			
				|  |  |  #include "grpc/grpc.h"
 | 
	
		
			
				|  |  |  #include "grpc/grpc_security.h"
 | 
	
		
			
				|  |  |  #include "grpc/support/alloc.h"
 | 
	
		
			
				|  |  | +#include "grpc/support/log.h"
 | 
	
		
			
				|  |  | +#include "grpc/support/time.h"
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  #include "call.h"
 | 
	
		
			
				|  |  |  #include "call_credentials.h"
 | 
	
	
		
			
				|  | @@ -45,14 +49,32 @@
 | 
	
		
			
				|  |  |  #include "server.h"
 | 
	
		
			
				|  |  |  #include "completion_queue_async_worker.h"
 | 
	
		
			
				|  |  |  #include "server_credentials.h"
 | 
	
		
			
				|  |  | +#include "timeval.h"
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  using v8::FunctionTemplate;
 | 
	
		
			
				|  |  |  using v8::Local;
 | 
	
		
			
				|  |  |  using v8::Value;
 | 
	
		
			
				|  |  | +using v8::Number;
 | 
	
		
			
				|  |  |  using v8::Object;
 | 
	
		
			
				|  |  |  using v8::Uint32;
 | 
	
		
			
				|  |  |  using v8::String;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +typedef struct log_args {
 | 
	
		
			
				|  |  | +  gpr_log_func_args core_args;
 | 
	
		
			
				|  |  | +  gpr_timespec timestamp;
 | 
	
		
			
				|  |  | +} log_args;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +typedef struct logger_state {
 | 
	
		
			
				|  |  | +  Nan::Callback *callback;
 | 
	
		
			
				|  |  | +  std::list<log_args *> *pending_args;
 | 
	
		
			
				|  |  | +  uv_mutex_t mutex;
 | 
	
		
			
				|  |  | +  uv_async_t async;
 | 
	
		
			
				|  |  | +  // Indicates that a logger has been set
 | 
	
		
			
				|  |  | +  bool logger_set;
 | 
	
		
			
				|  |  | +} logger_state;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +logger_state grpc_logger_state;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  static char *pem_root_certs = NULL;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  void InitStatusConstants(Local<Object> exports) {
 | 
	
	
		
			
				|  | @@ -235,6 +257,18 @@ void InitWriteFlags(Local<Object> exports) {
 | 
	
		
			
				|  |  |    Nan::Set(write_flags, Nan::New("NO_COMPRESS").ToLocalChecked(), NO_COMPRESS);
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +void InitLogConstants(Local<Object> exports) {
 | 
	
		
			
				|  |  | +  Nan::HandleScope scope;
 | 
	
		
			
				|  |  | +  Local<Object> log_verbosity = Nan::New<Object>();
 | 
	
		
			
				|  |  | +  Nan::Set(exports, Nan::New("logVerbosity").ToLocalChecked(), log_verbosity);
 | 
	
		
			
				|  |  | +  Local<Value> DEBUG(Nan::New<Uint32, uint32_t>(GPR_LOG_SEVERITY_DEBUG));
 | 
	
		
			
				|  |  | +  Nan::Set(log_verbosity, Nan::New("DEBUG").ToLocalChecked(), DEBUG);
 | 
	
		
			
				|  |  | +  Local<Value> INFO(Nan::New<Uint32, uint32_t>(GPR_LOG_SEVERITY_INFO));
 | 
	
		
			
				|  |  | +  Nan::Set(log_verbosity, Nan::New("INFO").ToLocalChecked(), INFO);
 | 
	
		
			
				|  |  | +  Local<Value> ERROR(Nan::New<Uint32, uint32_t>(GPR_LOG_SEVERITY_ERROR));
 | 
	
		
			
				|  |  | +  Nan::Set(log_verbosity, Nan::New("ERROR").ToLocalChecked(), ERROR);
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  NAN_METHOD(MetadataKeyIsLegal) {
 | 
	
		
			
				|  |  |    if (!info[0]->IsString()) {
 | 
	
		
			
				|  |  |      return Nan::ThrowTypeError(
 | 
	
	
		
			
				|  | @@ -298,16 +332,101 @@ NAN_METHOD(SetDefaultRootsPem) {
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +NAUV_WORK_CB(LogMessagesCallback) {
 | 
	
		
			
				|  |  | +  Nan::HandleScope scope;
 | 
	
		
			
				|  |  | +  std::list<log_args *> args;
 | 
	
		
			
				|  |  | +  uv_mutex_lock(&grpc_logger_state.mutex);
 | 
	
		
			
				|  |  | +  args.splice(args.begin(), *grpc_logger_state.pending_args);
 | 
	
		
			
				|  |  | +  uv_mutex_unlock(&grpc_logger_state.mutex);
 | 
	
		
			
				|  |  | +  /* Call the callback with each log message */
 | 
	
		
			
				|  |  | +  while (!args.empty()) {
 | 
	
		
			
				|  |  | +    log_args *arg = args.front();
 | 
	
		
			
				|  |  | +    args.pop_front();
 | 
	
		
			
				|  |  | +    Local<Value> file = Nan::New(arg->core_args.file).ToLocalChecked();
 | 
	
		
			
				|  |  | +    Local<Value> line = Nan::New<Uint32, uint32_t>(arg->core_args.line);
 | 
	
		
			
				|  |  | +    Local<Value> severity = Nan::New(
 | 
	
		
			
				|  |  | +        gpr_log_severity_string(arg->core_args.severity)).ToLocalChecked();
 | 
	
		
			
				|  |  | +    Local<Value> message = Nan::New(arg->core_args.message).ToLocalChecked();
 | 
	
		
			
				|  |  | +    Local<Value> timestamp = Nan::New<v8::Date>(
 | 
	
		
			
				|  |  | +        grpc::node::TimespecToMilliseconds(arg->timestamp)).ToLocalChecked();
 | 
	
		
			
				|  |  | +    const int argc = 5;
 | 
	
		
			
				|  |  | +    Local<Value> argv[argc] = {file, line, severity, message, timestamp};
 | 
	
		
			
				|  |  | +    grpc_logger_state.callback->Call(argc, argv);
 | 
	
		
			
				|  |  | +    delete[] arg->core_args.message;
 | 
	
		
			
				|  |  | +    delete arg;
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +void node_log_func(gpr_log_func_args *args) {
 | 
	
		
			
				|  |  | +  // TODO(mlumish): Use the core's log formatter when it becomes available
 | 
	
		
			
				|  |  | +  log_args *args_copy = new log_args;
 | 
	
		
			
				|  |  | +  size_t message_len = strlen(args->message) + 1;
 | 
	
		
			
				|  |  | +  char *message = new char[message_len];
 | 
	
		
			
				|  |  | +  memcpy(message, args->message, message_len);
 | 
	
		
			
				|  |  | +  memcpy(&args_copy->core_args, args, sizeof(gpr_log_func_args));
 | 
	
		
			
				|  |  | +  args_copy->core_args.message = message;
 | 
	
		
			
				|  |  | +  args_copy->timestamp = gpr_now(GPR_CLOCK_REALTIME);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  uv_mutex_lock(&grpc_logger_state.mutex);
 | 
	
		
			
				|  |  | +  grpc_logger_state.pending_args->push_back(args_copy);
 | 
	
		
			
				|  |  | +  uv_mutex_unlock(&grpc_logger_state.mutex);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  uv_async_send(&grpc_logger_state.async);
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +void init_logger() {
 | 
	
		
			
				|  |  | +  memset(&grpc_logger_state, 0, sizeof(logger_state));
 | 
	
		
			
				|  |  | +  grpc_logger_state.pending_args = new std::list<log_args *>();
 | 
	
		
			
				|  |  | +  uv_mutex_init(&grpc_logger_state.mutex);
 | 
	
		
			
				|  |  | +  uv_async_init(uv_default_loop(),
 | 
	
		
			
				|  |  | +                &grpc_logger_state.async,
 | 
	
		
			
				|  |  | +                LogMessagesCallback);
 | 
	
		
			
				|  |  | +  uv_unref((uv_handle_t*)&grpc_logger_state.async);
 | 
	
		
			
				|  |  | +  grpc_logger_state.logger_set = false;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  gpr_log_verbosity_init();
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +/* This registers a JavaScript logger for messages from the gRPC core. Because
 | 
	
		
			
				|  |  | +   that handler has to be run in the context of the JavaScript event loop, it
 | 
	
		
			
				|  |  | +   will be run asynchronously. To minimize the problems that could cause for
 | 
	
		
			
				|  |  | +   debugging, we leave core to do its default synchronous logging until a
 | 
	
		
			
				|  |  | +   JavaScript logger is set */
 | 
	
		
			
				|  |  | +NAN_METHOD(SetDefaultLoggerCallback) {
 | 
	
		
			
				|  |  | +  if (!info[0]->IsFunction()) {
 | 
	
		
			
				|  |  | +    return Nan::ThrowTypeError(
 | 
	
		
			
				|  |  | +        "setDefaultLoggerCallback's argument must be a function");
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +  if (!grpc_logger_state.logger_set) {
 | 
	
		
			
				|  |  | +    gpr_set_log_function(node_log_func);
 | 
	
		
			
				|  |  | +    grpc_logger_state.logger_set = true;
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +  grpc_logger_state.callback = new Nan::Callback(info[0].As<v8::Function>());
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +NAN_METHOD(SetLogVerbosity) {
 | 
	
		
			
				|  |  | +  if (!info[0]->IsUint32()) {
 | 
	
		
			
				|  |  | +    return Nan::ThrowTypeError(
 | 
	
		
			
				|  |  | +        "setLogVerbosity's argument must be a number");
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +  gpr_log_severity severity = static_cast<gpr_log_severity>(
 | 
	
		
			
				|  |  | +      Nan::To<uint32_t>(info[0]).FromJust());
 | 
	
		
			
				|  |  | +  gpr_set_log_verbosity(severity);
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  void init(Local<Object> exports) {
 | 
	
		
			
				|  |  |    Nan::HandleScope scope;
 | 
	
		
			
				|  |  |    grpc_init();
 | 
	
		
			
				|  |  |    grpc_set_ssl_roots_override_callback(get_ssl_roots_override);
 | 
	
		
			
				|  |  | +  init_logger();
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |    InitStatusConstants(exports);
 | 
	
		
			
				|  |  |    InitCallErrorConstants(exports);
 | 
	
		
			
				|  |  |    InitOpTypeConstants(exports);
 | 
	
		
			
				|  |  |    InitPropagateConstants(exports);
 | 
	
		
			
				|  |  |    InitConnectivityStateConstants(exports);
 | 
	
		
			
				|  |  |    InitWriteFlags(exports);
 | 
	
		
			
				|  |  | +  InitLogConstants(exports);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |    grpc::node::Call::Init(exports);
 | 
	
		
			
				|  |  |    grpc::node::CallCredentials::Init(exports);
 | 
	
	
		
			
				|  | @@ -333,6 +452,14 @@ void init(Local<Object> exports) {
 | 
	
		
			
				|  |  |             Nan::GetFunction(
 | 
	
		
			
				|  |  |                 Nan::New<FunctionTemplate>(SetDefaultRootsPem)
 | 
	
		
			
				|  |  |                              ).ToLocalChecked());
 | 
	
		
			
				|  |  | +  Nan::Set(exports, Nan::New("setDefaultLoggerCallback").ToLocalChecked(),
 | 
	
		
			
				|  |  | +           Nan::GetFunction(
 | 
	
		
			
				|  |  | +               Nan::New<FunctionTemplate>(SetDefaultLoggerCallback)
 | 
	
		
			
				|  |  | +                            ).ToLocalChecked());
 | 
	
		
			
				|  |  | +  Nan::Set(exports, Nan::New("setLogVerbosity").ToLocalChecked(),
 | 
	
		
			
				|  |  | +           Nan::GetFunction(
 | 
	
		
			
				|  |  | +               Nan::New<FunctionTemplate>(SetLogVerbosity)
 | 
	
		
			
				|  |  | +                            ).ToLocalChecked());
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  NODE_MODULE(grpc_node, init)
 |