前言Node.js提供了trace事件机制。在Node.js内核代码中,静态埋藏了一些点,比如同步文件IO耗时,DNS解析耗时等待。每次执行这些代码时,Node.js都会执行这些点的钩子,收集相应的数据。不过这个能力默认是关闭的,毕竟对性能会有影响。我们可以通过trace_events模块开启这个功能。trace_events模块不断地将数据写入一个或多个文件。除了trace_events模块,Node.js还通过Inspector协议实现了trace事件数据的收集。本文介绍基于inspector协议采集trace事件数据的实现。先看看如何使用这个方法。const{Session}=require('inspector');constsession=newSession();session.connect();functionpost(message,data){returnnewPromise((resolve,reject)=>{session.post(message,data,(err,result)=>{if(err)reject(newError(JSON.stringify(err)));elseresolve(result);});});}asyncfunctiontest(){会话.on('NodeTracing.dataCollected',(data)=>{console.log(data.params.value);});session.on('NodeTracing.tracingComplete',()=>{console.log('done');});const{categories}=awaitpost('NodeTracing.getCategories');consttraceConfig={includedCategories:类别};awaitpost('NodeTracing.start',{traceConfig});setTimeout(()=>{post('NodeTracing.stop');},1000);}test();用法比较固定,简单。跟踪事件是基于类型的,例如同步文件IO和DNS解析。所以第一步是设置要采集的模块类型,也可以通过NodeTracing.getCategories命令获取当前支持的模块类型。然后通过NodeTracing.start开始数据采集。收集一段时间后,通过NodeTracing.stop停止数据收集。在这个过程中,采集到的数据会通过NodeTracing.dataCollected事件源源不断的流向用户端。我们可以将这些数据保存下来,以便后续分析完成后,会触发NodeTracing.tracingComplete事件,完成整个过程。让我们看一下这些命令的实现。首先看整体结构。之前介绍过Node.jsInspector的架构,本文不再详细介绍。简单的说,当我们通过js层的session发送命令时,代码流从图的左往右走,采集数据时,代码流从右往左回调js层。首先看NodeTracing.start。Node.js的Inspector框架采用了两级路由机制。首先通过NodeTracing找到一级路由,在inspector中调用Domain,然后通过start找到二级路由。我们来看看每条路由对应的函数。m_dispatchMap["NodeTracing.getCategories"]=&DispatcherImpl::getCategories;m_dispatchMap["NodeTracing.start"]=&DispatcherImpl::start;m_dispatchMap["NodeTracing.stop"]=&DispatcherImpl::stop;我们只关注start和stop的逻辑。voidDispatcherImpl::start(intcallId,constString&method,constProtocolMessage&message,std::unique_ptrrequestMessageObject,ErrorSupport*errors){protocol::DictionaryValue*object=DictionaryValue::cast(requestMessageObject->get("参数"));协议::值*traceConfigValue=对象?object->get("traceConfig"):nullptr;std::unique_ptrin_traceConfig=ValueConversions::fromValue(traceConfigValue,错误);std::unique_ptrweak=weakPtr();DispatchResponseresponse=m_backend->start(std::move(in_traceConfig));if(weak->get())weak->get()->sendResponse(callId,response);return;}start里调了m_backend->start,根据架构图可知m_backend的值是TracingAgent对象。DispatchResponseTracingAgent::start(std::unique_ptrtraceConfig){std::setcategories_set;协议::数组*categories=traceConfig->getIncludedCategories();对于(size_ti=0;ilength();i++)categories_set.insert(categories->get(i));tracing::AgentWriterHandle*writer=GetTracingAgentWriter();if(writer!=nullptr){trace_writer_=writer->agent()->AddClient(categories_set,std::make_unique(frontend_object_id_,main_thread_),tracing::Agent::kIgnoreDefaultCategories);}returnDispatchResponse::OK();}最后,消费者通过AddClient注册到跟踪系统。Tracing系统产生数据时,会通过InspectorTraceWriter进行消费。看一下这个InspectorTraceWriter对象的核心逻辑。voidAppendTraceEvent(v8::platform::tracing::TraceObject*trace_event)override{if(!json_writer_)json_writer_.reset(TraceWriter::CreateJSONTraceWriter(stream_,"value"));json_writer_->AppendTraceEvent(trace_event);}voidFlush(bool)override{if(!json_writer_)return;json_writer_.reset();std::ostringstream结果("{\"method\":\"NodeTracing.dataCollected\",\"params\":",std::ostringstream::ate);结果<Post(std::make_unique(frontend_object_id_,result.str()));stream_.str("");}tracing系统调用AppendTraceEvent消费数据,但是数据会先缓存在内存中,然后调用Flush通知真正的消费者。在Flush函数中,我们可以看到NodeTracing是通过发送SendMessageRequest.dataCollected事件触发的。然后看SendMessageRequest的逻辑。voidCall(MainThreadInterface*thread)override{DeletableFrontendWrapper*frontend_wrapper=static_cast(thread->GetObjectIfExists(object_id_));如果(frontend_wrapper==nullptr)返回;autofrontend=frontend_wrapper(!=nullptr){frontend->sendRawJSONNotification(message_);}}voidFrontend::sendRawJSONNotification(Stringnotification){m_frontendChannel->sendProtocolNotification(InternalRawNotification::fromJSON(std::move(notification)));}调用再次调用m_frontendChannel->sendRawJSONNotification,根据架构图,取值m_frontendChannel是ChannelImpl。最后通过ChannelImpl通知用户端。再看stop的逻辑。DispatchResponseTracingAgent::stop(){trace_writer_.reset();frontend_->tracingComplete();returnDispatchResponse::OK();}首先看trace_writer_.reset()。voidAgentWriterHandle::reset(){if(agent_!=nullptr)agent_->Disconnect(id_);agent_=nullptr;}voidAgent::Disconnect(intclient){if(client==kDefaultHandleId)return;{Mutex::ScopedLock锁(initialize_writer_mutex_);to_be_initialized_.erase(writers_[client].get());}ScopedSuspendTracing暂停(tracing_controller_.get(),这个);writers_.erase(客户端);categories_.erase(client);}接着看ScopedSuspendTracing。ScopedSuspendTracing(TracingController*controller,Agent*agent,booldo_suspend=true):controller_(controller),agent_(do_suspend?agent:nullptr){if(do_suspend){CHECK(agent_->开始_);控制器->StopTracing();}}voidTracingController::StopTracing(){base::MutexGuardlock(mutex_.get());trace_buffer_->Flush();}把所有数据Flush到用户旁边后触发tracingComplete事件。voidFrontend::tracingComplete(){如果(!m_frontendChannel)返回;m_frontendChannel->sendProtocolNotification(InternalResponse::createNotification("NodeTracing.tracingComplete"));}时间关系,大致介绍到这里。