ããã«ã¡ã¯ãæè¡åºç€éšã®ç£¯éã§ãã å
æ¥ã«åŒãç¶ãZipkinã§ãã ä»å㯠PHP + Symfony ã§åããŠãã Webã¢ããªã±ãŒã·ã§ã³ãžã®ãã¬ãŒãµãŒã®å°å
¥ã§ãã ååã®èšäº â Zipkinãå°å
¥ããŠã¿ãïŒãµãŒããŒç·šïŒ æ§æ åŠçã®æµã åŠçã®æµãã远跡ããããã®ãªã¯ãšã¹ãããã fluentdã®èšå® PHP(Symfony)åŽã®å®è£
ã¯ã©ã€ã¢ã³ãã©ã€ãã©ãª Hoopak Apache Thrift ãœãŒã¹ã³ãŒã DIçšèšå®ãã¡ã€ã«(æç²) ã€ãã³ããªã¹ãã¯ã©ã¹ ãµãŒãã¹ã³ã³ããã¯ã©ã¹ïŒã€ãã³ããªã¹ãã«DIããçšïŒ è¶³ããªãæ©èœãè£å
ããããã®ã©ãããŒã¯ã©ã¹ WebAPIåŒã³åºãã®ãã®ã³ã°è¿œå åºå æ§æ PHPã¯éåæã«åŠçãã§ããªãã®ã§çŽæ¥ZipkinãµãŒããŒã«ã«æããã®ã§ã¯ãªãããŒã«ã«ã®fluentdãå©çšããŠã§ããã ãçæéã§åŠçãçµããããã«ããŠããŸãã ç»åã®ã¢ããªã±ãŒã·ã§ã³ã»ãã¬ãŒã ã¯ãŒã¯ â Zipkin Symfony2 Fluentd Sinatra åŠçã®æµã ãã¬ãŒã¹ããŒã¿ã®åæå(trace_id, span_idã«ãŠããŒã¯ãªå€ãèšå®ãã) Symfonyã®ã€ãã³ããªã¹ãã®kernel.requestãškernel.responseãSubscribe kernel.requestã€ãã³ãã®éç¥æã«ãªã¯ãšã¹ãã®å仿å»(microtime)ãURLãªã©ãä¿å WebAPIåŒã³åºãæã«åŠçã®æµãã远跡ããããã®æ
å ±ããªã¯ãšã¹ããããã«ä»äž WebAPIåŒã³åºãåŠçã®çŽåã«éä¿¡æå»(microtime)ããªã¯ãšã¹ãå
ãä¿å WebAPIåŒã³åºãçµäºæã«åä¿¡æå»(microtime)ãä¿åããAPIåŒã³åºãéå§æã«ä¿åããå
容ãšäžç·ã«ããŒã«ã«ã®fluentdã«scribeã§éä¿¡ kernel.responseã€ãã³ãã®éç¥æã«ã¬ã¹ãã³ã¹éä¿¡æå»(microtime)ãä¿åããkernel.requestã§ä¿åããå
容ãšäžç·ã«ããŒã«ã«ã®fluentdã«scribeã§éä¿¡ fluentdã¯é 次ZipkinãµãŒããŒã«è»¢é WebAPIã管çå€ã®ãµãŒãã¹ã®å Žåã«ã¯ãã¬ãŒã¹ããŒã¿ã®è¿œè·¡æ
å ±ããªã¯ãšã¹ããããã«ä»äžããã«åŠçãè¡ããŸãã ãªããã¢ããªã±ãŒã·ã§ã³ãšWebAPIã®åŒã³åºãã®ãã¬ãŒã¹ããŒã¿ãå¥ã
ã«fluentdã«æµãããã«ãªã£ãŠããŸãããåŸè¿°ã®ã©ã€ãã©ãªã®ä»æ§ã§ãã ãã¬ãŒã¹ããã·ã¹ãã ãå€ãå Žåã¯ãŸãšããŠéä¿¡ããããã«æ¹ä¿®ããæ¹ãããããã§ãã åŠçã®æµãã远跡ããããã®ãªã¯ãšã¹ãããã WebAPIã®åŒã³åºãã®ãã¬ãŒã¹ããŒã¿ã¯ååŸã§ããŸãããWebAPIããããã«å¥ã®WebAPIãåŒã¶å Žåã«ã¯ãäžé£ã®æµããèªèããããŒã¿ãæã¡ãŸããå¿
èŠããããŸãã ããã§ãHTTPãããã«ãã®æµããèªèããããã«å¿
èŠãªããŒã¿ãä»äžããŠããŸãã 以äžã¯ãã®HTTPããããšãã®èª¬æã§ãã HTTP Header Type 説æ X-B3-TraceId 64 encoded bits *1 ãªã¯ãšã¹ãããšã«å
±éã®IDãããã§è¿œè·¡æ
å ±ãçŽä»ãã X-B3-SpanId 64 encoded bits *1 èšæž¬ããšã«äžæã«æ±ºãŸãID X-B3-ParentSpanId 64 encoded bits *1 çŽåã®SpanId X-B3-Sampled Boolean (either â1â or â0â) *2 ãµã³ããªã³ã°å¯Ÿè±¡ãã©ãã X-B3-Flags a Long - *1 å
éšããŒã¿ã¯æ°å€ã§ããããããã«ä»äžããéã¯16鲿°è¡šçŸããæååã«å€æããŸãã *2 rubygemsã«ããzipkin-tracerããX-B3-Sampledããåãåãéã«'true'(æåå)ãèŠæ±ããã®ã§æååã«å€æããŸãã 詳ããã¯ä»¥äžURLã®ãHTTP Tracingããåç
§ããŠãã ããã http://zipkin.io/pages/instrumenting.html fluentdã®èšå® scribeã§åãåãzipkinã®scribeçšã®ããŒãã«è»¢éããããã®fluentdã®èšå®ã¯ãããªæãã§ãã â» äºåã« fluent-plugin-scribe ãå°å
¥ããŠããŸãã <source> type scribe port 1463 </source> <match zipkin .**> type scribe host zipkinserver port 9410 field_ref message </match> PHP(Symfony)åŽã®å®è£
ç¶ããŠPHP(Symfony)åŽã®å®è£
ã§ãã ã¯ã©ã€ã¢ã³ãã©ã€ãã©ãª ãŸãã¯Zipkinã«ãã¬ãŒã¹ããŒã¿ãéä¿¡ããããã®ã©ã€ãã©ãªã®å°å
¥ãããŸãã Hoopak PHPçšã®ã©ã€ãã©ãªã¯ å
¬åŒ ã«ã¯ååšããªãã£ãã®ã§ãGitHubã§æ¢ããŸããã 1ã€ããèŠã€ãããªãã£ãã®ã§ãã¡ããå©çšããŠããŸãã https://github.com/Jimdo/hoopak This implementation might be incomplete and very naive ãšã®ããšã§ã¡ãã£ãšããããã§ãããæåŸ
éãã«åããªããã°çŽãã°ããã ããªã®ã§ãã®ãŸãŸäœ¿ããŸãã Apache Thrift Hoopakã¯Apache Thriftã«äŸåããŠããŸãã https://thrift.apache.org/ äžèšURLã®ããŒãžããããŠã³ããŒãããŠãããã¡ã€ã«ãå±éãããšåèšèªçšã®ã©ã€ãã©ãªãå
¥ã£ãŠããã®ã§PHPçšãå©çšããŸãã ãœãŒã¹ã³ãŒã SymfonyãããŸãçè§£ããã«äœã£ãŠããã®ã§ãäœ¿ãæ¹ãšãçšèªãšã説æãšãéã£ãããææãã ããã ãŸãã以äžã®3ãã¡ã€ã«ã®requireãå¿
èŠãªãããªã®ã§äºåã«ã©ããã§requireããŠãããæ¹ãããã§ãã hoopak/gen-php/Scribe/Types.php hoopak/gen-php/Scribe/scribe.php hoopak/gen-php/Zipkin/Types.php DIçšèšå®ãã¡ã€ã«(æç²) ExampleApp/Resources/config/services.yml parameters : zipkintracer.samplerate : 0.1 zipkintracer.service_name : webserver services : zipkin_tracer : scope : request class : ExampleApp\TracerService arguments : - @request - %zipkintracer.service_name% - %zipkintracer.samplerate% zipkin_event_listener : scope : request class : ExampleApp\TraceEventListener tags : - { name : kernel.event_subscriber } arguments : - @zipkin_tracer - @kernel - @request ã€ãã³ããªã¹ãã¯ã©ã¹ <?php namespace ExampleApp; use Symfony\Component\EventDispatcher\EventSubscriberInterface; use Symfony\Component\HttpKernel\Event\GetResponseEvent; use Symfony\Component\HttpKernel\Event\FilterResponseEvent; /** * Zipkinã§ã®ãã¬ãŒã¹çšã®ã€ãã³ããªã¹ã * ãªã¯ãšã¹ãéå§ãã¬ã¹ãã³ã¹çŽåã®æéã®èšæž¬ãç»é²ããã **/ class TraceEventListener implements EventSubscriberInterface { /** * ã³ã³ã¹ãã©ã¯ã¿ * * @param ExampleApp\TraceService $trace * @param AppKernel $kernel * @param Request $request */ public function __construct ( $ tracer , $ kernel , $ request ) { $ this -> tracer = $ tracer ; $ this -> kernel = $ kernel ; $ this -> request = $ request ; } public static function getSubscribedEvents () { return array ( ' kernel.request ' => ' onKernelRequest ', ' kernel.response ' => ' onKernelResponse ', ) ; } public function onKernelRequest ( GetResponseEvent $ event ) { $ trace = $ this -> tracer -> getTrace () ; $ trace -> record ( \Hoopak\Annotation :: serverReceive ()) ; $ trace -> record ( \Hoopak\Annotation :: string ( ' server.env ', $ this -> kernel -> getEnvironment ())) ; $ trace -> record ( \Hoopak\Annotation :: string ( ' http.uri ', $ this -> request -> server -> get ( ' SCRIPT_URL ' ))) ; $ trace -> record ( \Hoopak\Annotation :: string ( ' http.query ', json_encode ( $ this -> request -> query -> all ()))) ; } public function onKernelResponse ( FilterResponseEvent $ event ) { $ trace = $ this -> tracer -> getTrace () ; $ trace -> record ( \Hoopak\Annotation :: serverSend ()) ; } } ãµãŒãã¹ã³ã³ããã¯ã©ã¹ïŒã€ãã³ããªã¹ãã«DIããçšïŒ <?php namespace ExampleApp; require_once ' hoopak/gen-php/Scribe/Types.php '; require_once ' hoopak/gen-php/Scribe/scribe.php '; require_once ' hoopak/gen-php/Zipkin/Types.php '; /** * Zipkinã«ãããã¬ãŒã¹çšã¯ã©ã¹ã®åæååŠçïŒä¿æãã * **/ class TracerService { private $ _tracer ; /** * ã³ã³ã¹ãã©ã¯ã¿ * * @param Request $request * @param float $samplerate Zipkinã«ãããã¬ãŒã¹ããµã³ããªã³ã°ããã¬ãŒãã®æå® */ public function __construct ( $ request , $ service_name , $ samplerate = 1.0 ) { $ method = strtolower ( $ request -> server -> get ( ' REQUEST_METHOD ', ' GET ' )) ; $ tracer = new \Hoopak\ZipkinTracer ( new \Hoopak\ScribeClient ( ' localhost ', 1463 )) ; $ this -> _trace = new TraceWrapper ( $ method , null , null , null , $ samplerate , array ( $ tracer )) ; $ ipaddress = $ request -> server -> get ( ' SERVER_ADDR ' ) ; $ port = $ request -> server -> get ( ' SERVER_PORT ' ) ; $ this -> _trace -> setEndpoint ( new \Hoopak\Endpoint ( $ ipaddress , $ port , $ service_name )) ; } public function getTrace () { return $ this -> _trace; } } è¶³ããªãæ©èœãè£å
ããããã®ã©ãããŒã¯ã©ã¹ ããšã§æŽçããŠhoopakåŽã«åã蟌ãã§ãã«ãªã¯åºããããšæããŸãã <?php namespace ExampleApp; /** * Hoopak\Traceã®Wrapã¯ã©ã¹ * **/ class TraceWrapper { /** * @var Hoopak\Trace **/ private $ _trace ; /** * @var boolean * Hoopak\Traceãšã©ãŒæã«æ¬¡å以éåŠçã転éããªãããã®ãã©ã° * **/ private $ _error = false ; /** * @var array * \Hoopak\ScribeClientãªã©ã®ã€ã³ã¹ã¿ã³ã¹ãæã€é
å **/ private $ _tracer = array () ; /** * @var \Hoopak\Endpoint **/ private $ _endpoint = null ; /** * ãã¬ãŒã¹ããŒã¿ãååŸãããã©ãã **/ public $ sampled = true ; /** * @var array APIãµãŒããŒã«éä¿¡ããZipkinãããã®å¯Ÿå¿è¡š **/ private $ _headers = array ( ' traceId ' => ' X-B3-TraceId ', ' parentSpanId ' => ' X-B3-ParentSpanId ', ' spanId ' => ' X-B3-SpanId ', ' sampled ' => ' X-B3-Sampled ', #'flags' => 'X-B3-Flags' ) ; /** * ã³ã³ã¹ãã©ã¯ã¿ * * @param Hoopak\Trace */ public function __construct ( $ method , $ traceId = null , $ spanId = null , $ parentSpanId = null , $ samplerate = 1.0 , $ tracers = array ()) { if ( !$ traceId ) { $ traceId = $ this -> _id () ; } if ( !$ spanId ) { $ spanId = $ this -> _id () ; } $ this -> _tracers = $ tracers ; $ this -> _trace = new \Hoopak\Trace ( $ method , $ traceId , $ spanId , $ parentSpanId , $ tracers ) ; if ( $ samplerate < 1.0 ) { $ this -> sampled = ( $ samplerate == 0 ) ? false : ( $ samplerate > ( mt_rand () / mt_getrandmax ())) ; } } /** * @see \Hoopak\Trace::record() * sampled=falseã®æã¯äœãããªã **/ public function record ( $ annotation ) { if ( !$ this -> sampled ) { return ; } $ this -> __call ( ' record ', array ( $ annotation )) ; } /** * @see \Hoopak\Trace::child() * \Hoopak\Trace::_id()ãåäžIDãè¿åŽãã確çãé«ãããã®ã§ã䜿ããªãããã«åŠçãäžæžã **/ public function child ( $ name ) { $ samplerate = $ this -> sampled ? 1.0 : 0.0 ; $ trace = new self ( $ name , $ this -> traceId, $ this -> _id () , $ this -> spanId, $ samplerate , $ this -> _tracers ) ; $ trace -> setEndpoint ( $ this -> _endpoint ) ; return $ trace ; } /** * @see \Hoopak\Trace::setEndpoint() * ãã®ã¯ã©ã¹ã§ãendpointã倿°ãšããŠä¿æãã **/ public function setEndpoint ( $ endpoint ) { $ this -> _endpoint = $ endpoint ; $ this -> __call ( ' setEndpoint ', array ( $ endpoint )) ; } /** * Hoopak\Traceã«åŠçãç§»è²ããããã®ã¡ãœãã * ç§»è²å
ã§ãšã©ãŒãçºçããå Žåã¯ãæ¡ãã€ã¶ã * * åå¥ã®ã¡ãœããã«é¢ããŠã¯ç§»è²å
ãåç
§ * @see hoopak/src/Hoopak/Trace.php * **/ public function __call ( $ name , $ arguments ) { if ( $ this -> _error ) { return ; } try { return call_user_func_array ( array ( $ this -> _trace, $ name ) , $ arguments ) ; } catch ( \ Exception $ e ) { $ message = " exception ' " . get_class ( $ e ) . " ' with message ' " . $ e -> getMessage () . " ' in " . $ e -> getFile () . " : " . $ e -> getLine () ; error_log ( $ message ) ; $ this -> _error = true ; } } /** * \Hoopak\Traceã®ãããªãã¯ãªã€ã³ã¹ã¿ã³ã¹å€æ°ãååŸããããã®ã¡ãœãã * * åå¥ã®å€æ°ã«é¢ããŠã¯ä»¥äžå
ãåç
§ * @see hoopak/src/Hoopak/Trace.php **/ public function __get ( $ name ) { return $ this -> _trace ->$ name ; } /** * Zend\Http\Clientã§ã®ãªã¯ãšã¹ãããŒã¿ãã¬ã¹ãã³ã¹ããŒã¿ãå
ã«Zipkinãžã®ãã¬ãŒã¹ç»é²ãšãè¡ãã * ãŸãããªã¯ãšã¹ããããã«Zipkinã®ãã¬ãŒã¹æ
å ±è»¢éçšã®ããããä»äžããã * * @param string $name ãªã¯ãšã¹ãéä¿¡å
ã®ãµãŒãã¹åç§° * @param Zend\Http\Client $client * @param callable $block ( * @param ExampleApp\TracerWrapper * @return Zend\Http\Response * ) * @return Zend\Http\Response * **/ public function traceWithHTTPClient ( $ name , $ client , $ block ) { $ tracer = $ this -> _createChildHTTPTracer ( $ name , $ client ) ; $ tracer -> _prepareHTTPClient ( $ client ) ; $ tracer -> record ( \Hoopak\Annotation :: clientSend ()) ; $ httpResponse = $ block ( $ tracer ) ; $ tracer -> record ( \Hoopak\Annotation :: string ( ' http.status ', $ httpResponse -> getStatusCode ())) ; $ tracer -> record ( \Hoopak\Annotation :: clientReceive ()) ; return $ httpResponse ; } /** * Zend\Http\Clientã®ããŒã¿ãå
ã«åãã¬ãŒã¹ã€ã³ã¹ã¿ã³ã¹ãäœæãè¿åŽããã * * @param string $name ãªã¯ãšã¹ãå
ãµãŒãã¹å * @param @param Zend\Http\Client $client * @return ExampleApp\TracerWrapper * **/ private function _createChildHTTPTracer ( $ name , $ client ) { $ uri = $ client -> getUri () ; $ query = $ client -> getRequest () -> getQuery () ; $ method = $ client -> getRequest () -> getMethod () ; $ trace = $ this -> child ( strtolower ( $ method )) ; $ serverAddress = \Hoopak\Annotation :: string ( ' sa ', ' 1 ' ) ; $ serverAddress -> endpoint = new \Hoopak\Endpoint ( $ uri -> getHost () , $ uri -> getPort () , $ name ) ; $ trace -> record ( $ serverAddress ) ; $ trace -> record ( \Hoopak\Annotation :: string ( ' http.uri ', $ uri -> getPath ())) ; $ trace -> record ( \Hoopak\Annotation :: string ( ' http.query ', json_encode ( $ query ))) ; return $ trace ; } /** * Zend\Http\Clientã«ãã¬ãŒã¹ããŒã¿è»¢éã«å¿
èŠãªãªã¯ãšã¹ãããããä»äžãã * @param Zend\Http\Client $client * **/ private function _prepareHTTPClient ( \Zend\Http\Client $ client ) { foreach ( $ this -> _headers as $ key => $ value ) { $ headers [ $ value ] = $ this -> _getStringData ( $ key ) ; } $ client -> setHeaders ( $ headers ) ; } /** * * @param $key string key of data * @return string- **/ private function _getStringData ( $ key ) { $ value = $ this ->$ key ; switch ( gettype ( $ value )) { case ' integer ' : $ ret = sprintf ( " %016s ", dechex ( $ value )) ; break ; case ' boolean ' : $ ret = $ value ? ' true ' : ' false '; break ; default: $ ret = ( string ) $ value ; break ; } return $ ret ; } private function _id () { return ( int ) round ( microtime ( true ) * 1000 * 1000 ) ; } } WebAPIåŒã³åºãã®ãã®ã³ã°è¿œå ãã®ããã«WebAPIãåŒã³åºããŠããéšå(Zend\Http\Client)ããã£ãã $response = $client- > send(); ãããªæãã«æžãæããäºã§WebAPIãžã®ãªã¯ãšã¹ãã®ãã¬ãŒã¹ãæå¹ã«ãªããŸãã $tracer = $this- > container- > get('zipkin_tracer')- > getTracer(); $response = $tracer- > traceWithHTTPClient("webapi", $client, function($childTracer) use ($client) { return $client- > send(); }); åºå 以äžã®èµ€ã§å²ã£ãéšåãä»åã®äœæ¥ã«ããèšé²ãããããã«ãªããŸãã æ¬¡åã¯ãWebAPIãµãŒããŒãžã®å°å
¥(Rubyç·š)ã®äºå®ã§ãã