├── .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