├── .gitignore ├── LICENSE ├── README.md ├── example ├── app.config.js ├── cat.js └── cat_http_server.js ├── index.js ├── libs ├── appConfig.js ├── async.js ├── cat.js └── cutil.js └── package.json /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .idea 3 | bower_components 4 | .cproject 5 | .project 6 | .yo-rc.json 7 | build.sh 8 | build 9 | Debug 10 | doc 11 | .DS_Store 12 | npm-debug.log 13 | mark.txt -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | 203 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # cat.js # 2 | 3 | - Version: 0.0.1 4 | - Author: cdchu@ctrip.com 5 | 6 | ## Install ## 7 | 8 | npm install cat-nodejs 9 | 10 | ## Usage ## 11 | 12 | 1. create app.config.js as nodejs module format 13 | 14 | module.exports={ 15 | 'AppID':'921821', 16 | 'CatServer.Config.Url':'http://10.0.0.1/catconfig', 17 | 'CatServer':['10.0.0.1:1234','10.0.0.2:1234'] 18 | }; 19 | 20 | 2. require ctriputil 21 | 22 | var cat=require('cat-nodejs'); 23 | 24 | 25 | ## CAT APIs ## 26 | 27 | - **Method** *spanInstance* cat.span(type,name,data) 28 | - type as string 29 | - name as string 30 | - data as string or number or array or object 31 | - **Method** *self* cat.event(type,name,data) 32 | - type as string 33 | - name as string 34 | - data as string or number or array or object 35 | - **Method** *self* cat.error(message,stack) 36 | - message as string or error 37 | - stack as string 38 | - **Method** *self* CtripUtil.cat.http(server) 39 | - server as httpServerInstance 40 | - **Class** spanInstance 41 | - **Method** *spanInstance* spanInstance.span(type,name,data) 42 | - type as string 43 | - name as string 44 | - data as string or number or array or object 45 | - **Method** *self* spanInstance.event(type,name,data) 46 | - type as string 47 | - name as string 48 | - data as string or number or array or object 49 | - **Method** *self* spanInstance.error(message,stack) 50 | - message as string or error 51 | - stack as string 52 | - **Method** *parent* spanInstance.end() 53 | - parent as spanInstance or cat 54 | -------------------------------------------------------------------------------- /example/app.config.js: -------------------------------------------------------------------------------- 1 | module.exports={ 2 | 'AppID':'100003018', 3 | 'CatServer':['10.2.6.98:2280'] 4 | }; -------------------------------------------------------------------------------- /example/cat.js: -------------------------------------------------------------------------------- 1 | var cat=require('../'); 2 | 3 | var span=cat.span('[type]','[name]'); 4 | setTimeout(function(){ 5 | span.event('[type]','[name]','[data]'); 6 | span.error('[message]','[stack]'); 7 | setTimeout(function(){ 8 | span.end(); 9 | },1500); 10 | },1000); 11 | 12 | setInterval(function(){},1000); 13 | -------------------------------------------------------------------------------- /example/cat_http_server.js: -------------------------------------------------------------------------------- 1 | var http=require('http'); 2 | var path=require('path'); 3 | var cat=require('../'); 4 | 5 | function handler(req,res){ 6 | res.writeHead(200,{ 7 | 'Content-Type':'text/html' 8 | }); 9 | res.end('hello world'); 10 | } 11 | 12 | var server=http.createServer(handler); 13 | cat.http(server); 14 | server.listen(8567); 15 | 16 | console.log('Porcess ID: ',process.pid); -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | var path=require('path'); 2 | var cat=require(path.resolve(__dirname,'./libs/cat.js')); 3 | module.exports=cat; -------------------------------------------------------------------------------- /libs/appConfig.js: -------------------------------------------------------------------------------- 1 | var path=require('path'); 2 | 3 | var appConfigHidden=process.argv.indexOf('__appconfig_hidden')!=-1; 4 | 5 | var appConfig={}; 6 | var lastConfigDir=''; 7 | var dirname=__dirname; 8 | while (dirname!==lastConfigDir){ 9 | try { 10 | var config=path.resolve(dirname,'./app.config.js'); 11 | appConfig=require(config); 12 | if (!appConfigHidden){ 13 | console.log('[AppConfig] Load config from '+config); 14 | } 15 | break; 16 | }catch (e){ 17 | if (!appConfigHidden){ 18 | console.log('[AppConfig] No avaliable app.config.js found in '+dirname); 19 | } 20 | lastConfigDir=dirname; 21 | dirname=path.dirname(lastConfigDir); 22 | } 23 | } 24 | 25 | if (!appConfig['AppID']){ 26 | if (appConfig['AppDomain']){ 27 | appConfig['AppID']=appConfig['AppDomain']; 28 | }else{ 29 | throw('[cUtil]','Missing AppID'); 30 | } 31 | } 32 | 33 | if (!appConfig['LogLevel']){ 34 | appConfig['LogLevel']=appConfig['log_level']||1; 35 | } 36 | 37 | if (!appConfig['CatServer.Config.Url']){ 38 | appConfig['CatServer.Config.Url']=''; 39 | } 40 | 41 | if (!appConfig['CatServer']){ 42 | appConfig['CatServer']=[]; 43 | } 44 | 45 | if (!appConfig['CatServer.Config.Url'] && !appConfig['CatServer'].length){ 46 | throw('Invalid CatServer.Config.Url or CatServer'); 47 | } 48 | 49 | if (!appConfigHidden){ 50 | // console.log('[AppConfig]',appConfig); 51 | } 52 | 53 | module.exports = appConfig; 54 | -------------------------------------------------------------------------------- /libs/async.js: -------------------------------------------------------------------------------- 1 | var path=require('path'); 2 | var cUtil=require(path.resolve(__dirname,'./cutil.js')); 3 | 4 | // Async define 5 | function Async(callback){ 6 | this._enabled=false; 7 | this._data={}; 8 | this._callback=callback; 9 | this._errors=[]; 10 | } 11 | 12 | Async.prototype.add=function(fn){ 13 | var _this=this; 14 | var args=Array.prototype.slice.call(arguments,1); 15 | var guid=cUtil.uid(); 16 | this._data[guid]={ 17 | fn:fn, 18 | args:args, 19 | done:function(err){ 20 | if (err){ 21 | _this._errors.push(err); 22 | } 23 | delete _this._data[guid]; 24 | _this._check(); 25 | } 26 | }; 27 | }; 28 | 29 | Async.prototype.start=function(){ 30 | this._enabled=true; 31 | for (var guid in this._data){ 32 | if (this._data.hasOwnProperty(guid)){ 33 | this._data[guid].fn.apply(null,[this._data[guid].done].concat(this._data[guid].args)); 34 | } 35 | } 36 | this._check(); 37 | }; 38 | 39 | Async.prototype._check=function(){ 40 | if (this._enabled){ 41 | var finish=true; 42 | for (var guid in this._data){ 43 | if (this._data.hasOwnProperty(guid)){ 44 | finish=false; 45 | break; 46 | } 47 | } 48 | if (finish){ 49 | this._callback && this._callback(this._errors.length?this._errors:null); 50 | this._enabled=false; 51 | } 52 | } 53 | }; 54 | 55 | module.exports=Async; -------------------------------------------------------------------------------- /libs/cat.js: -------------------------------------------------------------------------------- 1 | var net=require('net'); 2 | var http=require('http'); 3 | var url=require('url'); 4 | var path=require('path'); 5 | var os=require('os'); 6 | var cluster=require('cluster'); 7 | var appConfig=require(path.resolve(__dirname,'./appConfig.js')); 8 | var cUtil=require(path.resolve(__dirname,'./cutil.js')); 9 | var Async=require(path.resolve(__dirname,'./async.js')); 10 | 11 | var defaultPort=2280; 12 | var useRemoteConfig=!appConfig['CatServer'].length; 13 | var appStartTime=+new Date(); 14 | 15 | var cat={}; 16 | var avalible=false; 17 | var dropMessageCount=0; 18 | var waitQueue=[]; 19 | waitQueue.push=function(){ 20 | var args=Array.prototype.slice.call(arguments,0); 21 | Array.prototype.push.apply(waitQueue,args); 22 | }; 23 | var sendQueue=[]; 24 | sendQueue.push=function(){ 25 | var args=Array.prototype.slice.call(arguments,0); 26 | Array.prototype.push.apply(sendQueue,args); 27 | 28 | if (sendQueue.length>150){ 29 | var overflowQueue=sendQueue.splice(0,30); 30 | countMessage(overflowQueue,function(count,total){ 31 | console.log('[cUtil] Drop Cat '+count+' Messages ('+total+')'); 32 | dropMessageCount+=count; 33 | var error=new CatError(null,'Drop Messages','Total: '+total); 34 | error.__overCount=count; 35 | }); 36 | }else{ 37 | setImmediate(sendPackage); 38 | } 39 | }; 40 | 41 | function countMessage(items,callback){ 42 | var count=0; 43 | var total=0; 44 | items.forEach(function(item){ 45 | switch (item.__type){ 46 | case 'span': 47 | count++; 48 | total++; 49 | countMessage(item.__children,function(subCount,subTotal){ 50 | count+=subCount; 51 | total+=subTotal; 52 | }); 53 | break; 54 | case 'event': 55 | count++; 56 | total++; 57 | break; 58 | case 'error': 59 | count+=item.__overCount?0:1; 60 | total+=item.__overCount||1; 61 | break; 62 | default: 63 | break; 64 | } 65 | }); 66 | callback(count,total); 67 | return count; 68 | } 69 | 70 | cat.span=function(type,name,data){ 71 | var span=new CatSpan(null,type,name,data); 72 | return span; 73 | }; 74 | 75 | cat.event=function(type,name,data){ 76 | var event=new CatEvent(null,type,name,data); 77 | return cat; 78 | }; 79 | 80 | cat.error=function(message,stack){ 81 | var err=createError(message,stack); 82 | var error=new CatError(null,err.message,err.stack); 83 | return cat; 84 | }; 85 | 86 | cat.http=function(server){ 87 | server.on('request',function(req,res){ 88 | var tarUrl=req.url.replace(/[\?#].*$/,'')+' '; 89 | var span=cat.span('URL',tarUrl); 90 | 91 | var isEnd=false; 92 | function endSpan(){ 93 | if (!isEnd){ 94 | isEnd=true; 95 | span.end(); 96 | } 97 | } 98 | 99 | req.on('error',function(err){ 100 | span.error(err,'Request Error'); 101 | endSpan(); 102 | }); 103 | res.on('error',function(err){ 104 | span.error(err,'Response Error'); 105 | endSpan(); 106 | }); 107 | res.on('close',endSpan); 108 | res.on('finish',endSpan); 109 | }); 110 | 111 | return cat; 112 | }; 113 | 114 | function CatSpan(parent,type,name,data){ 115 | var _this=this; 116 | this.__id=cUtil.uid(); 117 | this.__messageId=parent?parent.__messageId:getMessageId(); 118 | this.__type='span'; 119 | this.__close=false; 120 | this.__parent=parent; 121 | this.__root=parent?parent.__root:this; 122 | this.__startTime=+new Date(); 123 | this.__startMicroSeconds=cUtil.getMicroSeconds(); 124 | this.__children=[]; 125 | this.__timeout=setTimeout(function(){ 126 | _this.error('TIMEOUT'); 127 | _this.end(); 128 | },30000); 129 | 130 | this.type=type||'Unknow Type'; 131 | this.name=name||'Unknow Name'; 132 | this.status='0'; 133 | this.data=data||''; 134 | 135 | if (this.__parent){ 136 | if (this.__parent.__close){ 137 | console.log('[cUtil] Cat span is already end'); 138 | }else{ 139 | this.__parent.__children.push(this); 140 | } 141 | }else{ 142 | waitQueue.push(this); 143 | } 144 | } 145 | 146 | CatSpan.prototype.end=function(){ 147 | if (this.__close){ 148 | console.log('[cUtil] Cat span is already end'); 149 | }else{ 150 | clearTimeout(this.__timeout); 151 | this.__children.forEach(function(item){ 152 | if (item.__type=='span' && !item.__close){ 153 | item.end(); 154 | } 155 | }); 156 | this.__close=true; 157 | this.__endTime=+new Date(); 158 | this.__endMicroSeconds=cUtil.getMicroSeconds(); 159 | var idx=waitQueue.indexOf(this); 160 | if (idx!=-1 && !this.__parent){ 161 | waitQueue.splice(idx,1); 162 | sendQueue.push(this); 163 | } 164 | return this.__parent||cat; 165 | } 166 | }; 167 | 168 | CatSpan.prototype.span=function(type,name,data){ 169 | var span=new CatSpan(this,type,name,data); 170 | return span; 171 | }; 172 | 173 | CatSpan.prototype.event=function(type,name,data){ 174 | var event=new CatEvent(this,type,name,data); 175 | return this; 176 | }; 177 | 178 | CatSpan.prototype.error=function(message,stack){ 179 | var err=createError(message,stack); 180 | var error=new CatError(this,err.message,err.stack); 181 | return this; 182 | }; 183 | 184 | var lastCpuStatus=os.cpus(); 185 | function getCpuInfo(){ 186 | var cpuStatus=os.cpus(); 187 | var ret={ 188 | count:cpuStatus.length, 189 | load:os.loadavg()[0], 190 | user:0, 191 | nice:0, 192 | sys:0, 193 | used:0, 194 | idle:100, 195 | irq:0 196 | }; 197 | cpuStatus.forEach(function(core,i){ 198 | var lastCore=lastCpuStatus[i]; 199 | if (lastCore){ 200 | coreSum=core.times.user+core.times.nice+core.times.sys+core.times.idle+core.times.irq; 201 | lastCoreSum=lastCore.times.user+lastCore.times.nice+lastCore.times.sys+lastCore.times.idle+lastCore.times.irq; 202 | diffSum=coreSum-lastCoreSum; 203 | ret.user=parseInt((core.times.user-lastCore.times.user)/diffSum*1000,10)/1000; 204 | ret.nice=parseInt((core.times.nice-lastCore.times.nice)/diffSum*1000,10)/1000; 205 | ret.sys=parseInt((core.times.sys-lastCore.times.sys)/diffSum*1000,10)/1000; 206 | ret.idle=parseInt((core.times.idle-lastCore.times.idle)/diffSum*1000,10)/1000; 207 | ret.irq=parseInt((core.times.irq-lastCore.times.irq)/diffSum*1000,10)/1000; 208 | ret.used=1-ret.idle; 209 | } 210 | }); 211 | return ret; 212 | } 213 | 214 | function CatHeartbeat(parent,callback){ 215 | var _this=this; 216 | 217 | var now=+new Date(); 218 | 219 | this.__id=cUtil.uid(); 220 | this.__messageId=parent?parent.__messageId:getMessageId(); 221 | this.__type='heartbeat'; 222 | this.__close=true; 223 | this.__parent=parent; 224 | this.__root=parent?parent.__root:this; 225 | this.__startTime=this.__endTime=now; 226 | this.__startMicroSeconds=this.__endMicroSeconds=cUtil.getMicroSeconds(); 227 | 228 | this.type='Heartbeat'; 229 | this.name=cUtil.getHostIp(); 230 | this.status='0'; 231 | 232 | cUtil.getDiskInfo(function(err,diskInfo){ 233 | var diskInfoString=(err?[]:diskInfo).map(function(item){ 234 | return ''; 235 | }).join('\n'); 236 | 237 | var extDiskInfoString=(err?[]:diskInfo).map(function(item){ 238 | return ''; 239 | }).join('\n'); 240 | 241 | var memory=process.memoryUsage(); 242 | var freeMemory=os.freemem(); 243 | var cpu=getCpuInfo(); 244 | var version=process.version.slice(1); 245 | 246 | _this.data=[ 247 | '', 248 | '', 249 | '', 250 | ''+process.cwd()+'', 251 | 'node-'+version+'.jar', 252 | '', 253 | '', 254 | '', 255 | diskInfoString, 256 | '', 257 | '', 258 | '', 259 | '', 260 | '', 261 | '', 262 | '',  263 | '', 264 | '', 265 | '', 266 | '', 267 | // '', 268 | // '', 269 | // '', 270 | // '', 271 | // '', 272 | // '', 273 | '', 274 | '', 275 | extDiskInfoString, 276 | '', 277 | '', 278 | '', 279 | '', 280 | '', 281 | '', 282 | '', 283 | // '', 284 | '', 285 | '', 286 | '', 287 | '', 288 | '', 289 | '' 290 | ].join('\n'); 291 | 292 | if (_this.__parent){ 293 | if (_this.__parent.__close){ 294 | console.log('[cUtil] Cat span is already end'); 295 | }else{ 296 | _this.__parent.__children.push(_this); 297 | } 298 | }else{ 299 | sendQueue.push(_this); 300 | } 301 | 302 | callback && callback(_this); 303 | }); 304 | } 305 | 306 | function CatEvent(parent,type,name,data){ 307 | this.__id=cUtil.uid(); 308 | this.__messageId=parent?parent.__messageId:getMessageId(); 309 | this.__type='event'; 310 | this.__close=true; 311 | this.__parent=parent; 312 | this.__root=parent?parent.__root:this; 313 | this.__startTime=this.__endTime=+new Date(); 314 | this.__startMicroSeconds=this.__endMicroSeconds=cUtil.getMicroSeconds(); 315 | 316 | this.type=type||'Unknown Type'; 317 | this.name=name||'Unknown Name'; 318 | this.status='0'; 319 | this.data=data||''; 320 | 321 | if (this.__parent){ 322 | if (this.__parent.__close){ 323 | console.log('[cUtil] Cat span is already end'); 324 | }else{ 325 | this.__parent.__children.push(this); 326 | } 327 | }else{ 328 | sendQueue.push(this); 329 | } 330 | }; 331 | 332 | function CatError(parent,message,stack){ 333 | this.__id=cUtil.uid(); 334 | this.__messageId=parent?parent.__messageId:getMessageId(); 335 | this.__type='error'; 336 | this.__close=true; 337 | this.__parent=parent; 338 | this.__root=parent?parent.__root:this; 339 | this.__startTime=this.__endTime=+new Date(); 340 | this.__startMicroSeconds=this.__endMicroSeconds=cUtil.getMicroSeconds(); 341 | 342 | this.type='Error'; 343 | this.name=message||'Unknown Name'; 344 | this.status='ERROR'; 345 | var t=this; 346 | while (t=t.__parent){ 347 | t.status='ERROR'; 348 | } 349 | this.data=stack||''; 350 | 351 | if (this.__parent){ 352 | if (this.__parent.__close){ 353 | console.log('[cUtil] Cat span is already end'); 354 | }else{ 355 | this.__parent.__children.push(this); 356 | } 357 | }else{ 358 | sendQueue.push(this); 359 | } 360 | } 361 | 362 | function createError(err,stack){ 363 | var message='Error Unknown'; 364 | stack=stack||''; 365 | if (cUtil.type(stack)!='string'){ 366 | stack=JSON.stringify(stack); 367 | } 368 | if (err===null){ 369 | message='Error Null'; 370 | }else if (err===void(0)){ 371 | message='Error Undefined'; 372 | }else{ 373 | switch (cUtil.type(err)){ 374 | case 'error': 375 | message=''+err.message; 376 | stack=(stack?stack+' ||| ':'')+err.stack; 377 | break; 378 | case 'string': 379 | case 'number': 380 | message=''+err; 381 | stack=(stack?stack+' ||| ':'')+err; 382 | break; 383 | case 'array': 384 | var messages=['Error Array']; 385 | var stacks=[]; 386 | err.map(createError).forEach(function(err){ 387 | messages.push(''+err.message); 388 | stacks.push('['+err.message+'] '+err.stack); 389 | }); 390 | message=messages.join(' ||| '); 391 | stack=(stack?stack+' ||| ':'')+stacks.join(' ||| '); 392 | break; 393 | case 'object': 394 | var messages=['Error Object']; 395 | var stacks=[]; 396 | for (var key in err){ 397 | if (err.hasOwnProperty(key)){ 398 | var subErr=createError(err[key]); 399 | if (cUtil.type(err[key])=='error'){ 400 | messages.push(''+subErr.message); 401 | } 402 | stacks.push('['+subErr.message+'] '+subErr.stack); 403 | } 404 | } 405 | message=messages.join(' ||| '); 406 | stack=(stack?stack+' ||| ':'')+stacks.join(' ||| '); 407 | break; 408 | case 'date': 409 | message='Error Date'; 410 | stack=(stack?stack+' ||| ':'')+err.toLocaleString(); 411 | break; 412 | default: 413 | message=''+err; 414 | stack=(stack?stack+' ||| ':'')+err; 415 | break; 416 | } 417 | } 418 | if (message.length>100){ 419 | message=message.slice(0,97)+'...'; 420 | } 421 | var newErr=new Error(message); 422 | try{ 423 | newErr.type=message; 424 | newErr.stack=stack; 425 | }catch (e){}; 426 | return newErr; 427 | } 428 | 429 | var isSending=false; 430 | function sendPackage(){ 431 | if (!avalible || isSending){ 432 | return; 433 | } 434 | isSending=true; 435 | var item=sendQueue.shift(); 436 | if (item){ 437 | var raw=createRaw(item); 438 | sendRaw(raw,function(err){ 439 | if (err){ 440 | console.log('[cUtil] Send Cat Package Failed:',err); 441 | sendQueue.unshift(item); 442 | setTimeout(sendPackage,10000); 443 | isSending=false; 444 | }else{ 445 | isSending=false; 446 | setImmediate(sendPackage); 447 | } 448 | }); 449 | }else{ 450 | isSending=false; 451 | } 452 | } 453 | 454 | var msgSeq=0; 455 | var hourSeq=getHourString(); 456 | function getHourString(){ 457 | return parseInt((+new Date())/3600000,10).toString(); 458 | } 459 | 460 | var maxMsgSeq=2147483647; 461 | var procPrefix=0; 462 | if (cluster.isWorker){ 463 | procPrefix=(cluster.worker.id%256)<<23; 464 | maxMsgSeq=8388607; 465 | } 466 | function getMessageId(){ 467 | var messageId=[]; 468 | messageId.push(appConfig['AppID']); 469 | messageId.push(cUtil.getHostIp().split('.').map(function(item){ 470 | return cUtil.fillZero(parseInt(item,10).toString(16),2); 471 | }).join('')); 472 | var hourStr=getHourString(); 473 | if (hourSeq!=hourStr){ 474 | hourSeq=hourStr; 475 | msgSeq=0; 476 | } 477 | messageId.push(hourStr); 478 | messageId.push(cUtil.fillZero(msgSeq,10)); 479 | msgSeq++; 480 | if (msgSeq>=maxMsgSeq){ 481 | msgSeq=0; 482 | } 483 | return messageId.join('-'); 484 | } 485 | 486 | function formatTime(time){ 487 | var d=new Date(time); 488 | var ret=cUtil.fillZero(d.getFullYear(),4)+'-' 489 | +cUtil.fillZero(d.getMonth()+1,2)+'-' 490 | +cUtil.fillZero(d.getDate(),2)+' ' 491 | +cUtil.fillZero(d.getHours(),2)+':' 492 | +cUtil.fillZero(d.getMinutes(),2)+':' 493 | +cUtil.fillZero(d.getSeconds(),2)+',' 494 | +cUtil.fillZero(d.getMilliseconds(),3); 495 | return ret; 496 | } 497 | 498 | function createRaw(item){ 499 | var procName=process.execPath.replace(/^.*[\/\\]/,''); 500 | var header=[ 501 | 'PT1', 502 | appConfig['AppID'], 503 | cUtil.getHostName(), 504 | cUtil.getHostIp(), 505 | procName, 506 | process.pid, 507 | procName, 508 | item.__messageId, 509 | 'null', 510 | 'null', 511 | 'null' 512 | ].join('\t')+'\n'; 513 | var body=createItemRaw(item); 514 | var buff=[]; 515 | buff[1]=new Buffer(header+body); 516 | buff[0]=new Buffer(4); 517 | buff[0].writeUIntBE(buff[1].length,0,4); 518 | return Buffer.concat(buff); 519 | } 520 | 521 | function createItemRaw(item){ 522 | var body=''; 523 | if (item.__type=='event' || item.__type=='heartbeat' || item.__type=='error'){ 524 | body=({ 525 | 'event':'E', 526 | 'error':'E', 527 | 'heartbeat':'H' 528 | })[item.__type]+[ 529 | formatTime(item.__startTime), 530 | createRawString(item.type), 531 | createRawString(item.name), 532 | createRawString(item.status), 533 | createRawString(item.data) 534 | ].join('\t')+'\t\n'; 535 | }else if (item.__type=='span' && !item.__children.length){ 536 | body='A'+[ 537 | formatTime(item.__startTime), 538 | createRawString(item.type), 539 | createRawString(item.name), 540 | createRawString(item.status), 541 | (item.__endMicroSeconds-item.__startMicroSeconds).toString()+'us', 542 | createRawString(item.data) 543 | ].join('\t')+'\t\n'; 544 | }else if (item.__type=='span' && item.__children.length){ 545 | body='t'+[ 546 | formatTime(item.__startTime), 547 | createRawString(item.type), 548 | createRawString(item.name) 549 | ].join('\t')+'\t\n'; 550 | body+=item.__children.map(createItemRaw).join(''); 551 | body+='T'+[ 552 | formatTime(item.__endTime), 553 | createRawString(item.type), 554 | createRawString(item.name), 555 | createRawString(item.status), 556 | (item.__endMicroSeconds-item.__startMicroSeconds).toString()+'us', 557 | createRawString(item.data) 558 | ].join('\t')+'\t\n'; 559 | } 560 | return body; 561 | } 562 | 563 | function createRawString(data,defaultValue){ 564 | var ret=''; 565 | if (data===null || data===void(0)){ 566 | ret=defaultValue||''; 567 | }else{ 568 | switch (cUtil.type(data)){ 569 | case 'string': 570 | ret=data; 571 | break; 572 | case 'number': 573 | ret=''+data; 574 | break; 575 | case 'date': 576 | ret=formatMilliSeconds(data); 577 | break; 578 | case 'array': 579 | case 'object': 580 | try{ 581 | ret=JSON.stringify(data); 582 | }catch(e){ 583 | ret='[ERROR: JSON ENCODE FAILED]'; 584 | } 585 | break; 586 | default: 587 | ret=''+data; 588 | break; 589 | } 590 | var h={ 591 | '\t':'\\t', 592 | '\r':'\\r', 593 | '\n':'\\n' 594 | }; 595 | ret=ret.replace(/\\/g,'\\\\').replace(/[\t\r\n]/g,function(a){ 596 | return h[a]; 597 | }); 598 | } 599 | return ret; 600 | } 601 | 602 | var conn=null; 603 | function prepareConn(callback){ 604 | if (!conn){ 605 | var servers=appConfig['CatServer'].slice(0); 606 | tryConnectServer(servers,callback); 607 | }else{ 608 | callback(null,conn); 609 | } 610 | } 611 | 612 | function tryConnectServer(servers,callback){ 613 | var server=servers.shift(); 614 | if (server){ 615 | var arr=server.split(':'); 616 | var host=arr[0]; 617 | var port=parseInt(arr[1],10); 618 | var newConn=net.connect(port,host); 619 | newConn.on('error',function(){ 620 | try{ 621 | newConn.destroy(); 622 | }catch(e){}; 623 | if (conn==newConn){ 624 | conn=null; 625 | } 626 | console.log('[cUtil] Connect To Cat Server Error: '+server); 627 | tryConnectServer(servers,callback); 628 | }); 629 | newConn.on('connect',function(){ 630 | conn=newConn; 631 | console.log('[cUtil] Connect To Cat Server Success: '+server); 632 | callback(null,conn); 633 | }); 634 | newConn.on('end',function(){ 635 | conn=null; 636 | }); 637 | }else{ 638 | callback('None Avalible Server'); 639 | } 640 | } 641 | 642 | var sendBytes=0; 643 | function sendRaw(raw,callback){ 644 | prepareConn(function(err,conn){ 645 | if (err){ 646 | callback(err); 647 | }else{ 648 | // console.log('[cUtil] Sending Cat Message'); 649 | // console.log(raw.toString()); 650 | sendBytes+=raw.length; 651 | conn.write(raw); 652 | callback(null); 653 | } 654 | }); 655 | } 656 | 657 | function fetchServerConfig(){ 658 | // console.log('[cUtil] Start Fetch Cat Server Config: '+appConfig['CatServer.Config.Url']); 659 | var tarUrl=url.parse(appConfig['CatServer.Config.Url']); 660 | tarUrl.method='GET'; 661 | var req=http.request(tarUrl,function(res){ 662 | res.on('error',errorHandle); 663 | var buffArr=[]; 664 | res.on('data',function(buff){ 665 | buffArr.push(buff); 666 | }); 667 | res.on('end',function(){ 668 | var content=Buffer.concat(buffArr).toString(); 669 | var json={}; 670 | try{ 671 | json=JSON.parse(content); 672 | }catch (e){} 673 | if (json){ 674 | var arr=[]; 675 | var asyncTask=new Async(function(){ 676 | if (arr.length){ 677 | appConfig['CatServer']=arr; 678 | // console.log('[cUtil] Fetch Cat Server Config Success, ',appConfig['CatServer']); 679 | setTimeout(fetchServerConfig,1200000); 680 | avalible=true; 681 | setImmediate(sendPackage); 682 | }else{ 683 | console.log('[cUtil] Fetch Cat Server Config Failed'); 684 | setTimeout(fetchServerConfig,30000); 685 | } 686 | }); 687 | cUtil.type(json)=='array' && json.forEach(function(item){ 688 | if (item.Name=="CAT_SERVER" && item.Value){ 689 | asyncTask.add(function(done,router){ 690 | var tarUrlRouter=url.parse(router); 691 | tarUrlRouter.query.domain=appConfig['AppID']; 692 | tarUrlRouter=url.parse(url.format(tarUrlRouter)); 693 | tarUrlRouter.method='GET'; 694 | var reqRouter=http.request(tarUrlRouter,function(resRouter){ 695 | resRouter.on('error',errorHandle); 696 | var buffArr=[]; 697 | resRouter.on('data',function(buff){ 698 | buffArr.push(buff); 699 | }); 700 | resRouter.on('end',function(){ 701 | var content=Buffer.concat(buffArr).toString(); 702 | content.replace(/(\d{0,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})(:(\d{1,5}))?/g,function(a,b,c,d){ 703 | arr.push(b+':'+(d||defaultPort)); 704 | }); 705 | done(); 706 | }); 707 | }); 708 | reqRouter.on('error',errorHandle); 709 | reqRouter.end(); 710 | 711 | function errorHandle(err){ 712 | console.log('[cUtil]','Fetch Cat Server Router Error, '+router,err); 713 | done(); 714 | } 715 | },item.Value); 716 | } 717 | }); 718 | asyncTask.start(); 719 | }else{ 720 | errorHandle('Invalid Json Format'); 721 | } 722 | }); 723 | }); 724 | req.on('error',errorHandle); 725 | req.end(); 726 | 727 | function errorHandle(err){ 728 | console.log('[cUtil]','Fetch Cat Server Config Error, '+appConfig['CatServer.Config.Url'],err); 729 | setTimeout(fetchServerConfig,30000); 730 | } 731 | } 732 | 733 | function sendHeartbeat(){ 734 | var span=cat.span('System','Status'); 735 | new CatHeartbeat(span,function(){ 736 | span.end(); 737 | }); 738 | } 739 | 740 | function init(){ 741 | if (useRemoteConfig){ 742 | fetchServerConfig(); 743 | }else{ 744 | var arr=[]; 745 | appConfig['CatServer'].forEach(function(item){ 746 | var m=item.match(/^(\d{0,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})(:(\d{1,5}))?$/); 747 | if (m){ 748 | arr.push(m[1]+':'+(m[3]||defaultPort)); 749 | } 750 | }); 751 | if (arr.length){ 752 | appConfig['CatServer']=arr; 753 | avalible=true; 754 | setImmediate(sendPackage); 755 | }else{ 756 | useRemoteConfig=true; 757 | fetchServerConfig(); 758 | } 759 | } 760 | 761 | sendHeartbeat(); 762 | setInterval(sendHeartbeat,60000); 763 | } 764 | 765 | var pipeAvalible=process.argv.indexOf('__ctriputil_cat_pipe_enabled')!=-1; 766 | var pipeIds={}; 767 | if (pipeAvalible){ 768 | process.on('message',function(d){ 769 | if (d && d.__op){ 770 | switch (d.__op){ 771 | case 'span': 772 | if (d.__parentId){ 773 | var parent=pipeIds[d.__parentId]; 774 | if (parent && !parent.__close){ 775 | var span=pipeIds[d.__id]=parent.span(d.type,d.name,d.data); 776 | span.__pipeId=d.__id; 777 | } 778 | }else{ 779 | var span=pipeIds[d.__id]=cat.span(d.type,d.name,d.data); 780 | span.__pipeId=d.__id; 781 | } 782 | break; 783 | case 'span.end': 784 | var span=pipeIds[d.__id]; 785 | if (span){ 786 | span.end(); 787 | } 788 | break; 789 | case 'event': 790 | if (d.__parentId){ 791 | var parent=pipeIds[d.__parentId]; 792 | if (parent && !parent.__close){ 793 | parent.event(d.type,d.name,d.data); 794 | } 795 | }else{ 796 | cat.event(d.type,d.name,d.data); 797 | } 798 | break; 799 | case 'error': 800 | if (d.__parentId){ 801 | var parent=pipeIds[d.__parentId]; 802 | if (parent && !parent.__close){ 803 | parent.error(d.message,d.stack); 804 | } 805 | }else{ 806 | cat.error(d.message,d.stack); 807 | } 808 | break; 809 | default: 810 | break; 811 | } 812 | } 813 | }); 814 | } 815 | 816 | init(); 817 | 818 | module.exports=cat; 819 | -------------------------------------------------------------------------------- /libs/cutil.js: -------------------------------------------------------------------------------- 1 | var os=require('os'); 2 | var exec=require('child_process').exec; 3 | 4 | var cUtil={ 5 | uid:function(){ 6 | return 'uid_'+new Date().getTime()+(Math.random()*1e10).toFixed(0); 7 | }, 8 | type:function(obj){ 9 | return Object.prototype.toString.call(obj).slice(8,-1).toLowerCase(); 10 | }, 11 | __hostIp:null, 12 | getHostIp:function(){ 13 | if (!cUtil.__hostIp){ 14 | var ipA='',ipB=''; 15 | var inf=os.networkInterfaces(); 16 | infLoops: 17 | for (var name in inf){ 18 | if (inf.hasOwnProperty(name)){ 19 | var arr=inf[name]; 20 | for (var i=0;i