├── packages
├── moleculer-jaeger
│ ├── .gitignore
│ ├── examples
│ │ ├── index.js
│ │ ├── api
│ │ │ └── index.js
│ │ └── simple
│ │ │ └── index.js
│ ├── CHANGELOG.md
│ ├── index.js
│ ├── LICENSE
│ ├── .eslintrc.js
│ ├── package.json
│ ├── README.md
│ ├── src
│ │ └── index.js
│ └── test
│ │ └── unit
│ │ └── index.spec.js
├── moleculer-prometheus
│ ├── .gitignore
│ ├── examples
│ │ ├── index.js
│ │ └── simple
│ │ │ └── index.js
│ ├── CHANGELOG.md
│ ├── index.js
│ ├── LICENSE
│ ├── .eslintrc.js
│ ├── package.json
│ ├── README.md
│ ├── src
│ │ └── index.js
│ ├── test
│ │ └── unit
│ │ │ └── index.spec.js
│ └── grafana-dashboards
│ │ └── Moleculer Prometheus.json
├── moleculer-console-tracer
│ ├── .gitignore
│ ├── examples
│ │ ├── index.js
│ │ ├── .eslintrc.js
│ │ └── simple
│ │ │ └── index.js
│ ├── CHANGELOG.md
│ ├── index.js
│ ├── LICENSE
│ ├── .eslintrc.js
│ ├── package.json
│ ├── README.md
│ ├── test
│ │ └── unit
│ │ │ └── index.spec.js
│ └── src
│ │ └── index.js
└── moleculer-zipkin
│ ├── .gitignore
│ ├── examples
│ ├── index.js
│ ├── .eslintrc.js
│ └── simple
│ │ └── index.js
│ ├── CHANGELOG.md
│ ├── index.js
│ ├── LICENSE
│ ├── .eslintrc.js
│ ├── package.json
│ ├── README.md
│ ├── src
│ └── index.js
│ └── test
│ └── unit
│ └── index.spec.js
├── dev.js
├── .travis.yml
├── lerna.json
├── .editorconfig
├── .codeclimate.yml
├── LICENSE
├── readme-generator.js
├── .gitignore
├── .vscode
└── launch.json
├── .eslintrc.js
├── package.json
└── README.md
/packages/moleculer-jaeger/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | node_modules/
3 | coverage/
4 | npm-debug.log
5 | stats.json
6 | yarn-error.log
--------------------------------------------------------------------------------
/packages/moleculer-prometheus/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | node_modules/
3 | coverage/
4 | npm-debug.log
5 | stats.json
6 | yarn-error.log
--------------------------------------------------------------------------------
/packages/moleculer-console-tracer/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | node_modules/
3 | coverage/
4 | npm-debug.log
5 | stats.json
6 | yarn-error.log
--------------------------------------------------------------------------------
/packages/moleculer-zipkin/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | node_modules/
3 | coverage/
4 | npm-debug.log
5 | stats.json
6 | yarn-error.log
7 |
--------------------------------------------------------------------------------
/packages/moleculer-jaeger/examples/index.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | const moduleName = process.argv[2] || "simple";
4 | process.argv.splice(2, 1);
5 |
6 | require("./" + moduleName);
--------------------------------------------------------------------------------
/packages/moleculer-zipkin/examples/index.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | const moduleName = process.argv[2] || "simple";
4 | process.argv.splice(2, 1);
5 |
6 | require("./" + moduleName);
--------------------------------------------------------------------------------
/packages/moleculer-prometheus/examples/index.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | const moduleName = process.argv[2] || "simple";
4 | process.argv.splice(2, 1);
5 |
6 | require("./" + moduleName);
--------------------------------------------------------------------------------
/packages/moleculer-console-tracer/examples/index.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | const moduleName = process.argv[2] || "simple";
4 | process.argv.splice(2, 1);
5 |
6 | require("./" + moduleName);
--------------------------------------------------------------------------------
/packages/moleculer-jaeger/CHANGELOG.md:
--------------------------------------------------------------------------------
1 |
2 | # 0.2.0 (2018-07-10)
3 |
4 | ## Changes
5 | - support Moleculer v0.13.x
6 |
7 | --------------------------------------------------
8 |
--------------------------------------------------------------------------------
/packages/moleculer-zipkin/CHANGELOG.md:
--------------------------------------------------------------------------------
1 |
2 | # 0.2.0 (2018-07-10)
3 |
4 | ## Changes
5 | - support Moleculer v0.13.x
6 |
7 | --------------------------------------------------
8 |
--------------------------------------------------------------------------------
/packages/moleculer-console-tracer/CHANGELOG.md:
--------------------------------------------------------------------------------
1 |
2 | # 0.2.0 (2018-07-10)
3 |
4 | ## Changes
5 | - support Moleculer v0.13.x
6 |
7 | --------------------------------------------------
8 |
--------------------------------------------------------------------------------
/packages/moleculer-prometheus/CHANGELOG.md:
--------------------------------------------------------------------------------
1 |
2 | # 0.2.0 (2018-07-10)
3 |
4 | ## Changes
5 | - support Moleculer v0.13.x
6 |
7 | --------------------------------------------------
8 |
--------------------------------------------------------------------------------
/dev.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | const moduleName = process.argv[2];
4 | const example = process.argv[3] || "simple";
5 | process.argv.splice(2, 2);
6 |
7 | require("./packages/" + moduleName + "/examples/" + example);
--------------------------------------------------------------------------------
/packages/moleculer-jaeger/index.js:
--------------------------------------------------------------------------------
1 | /*
2 | * moleculer-jaeger
3 | * Copyright (c) 2018 MoleculerJS (https://github.com/moleculerjs/moleculer-addons)
4 | * MIT Licensed
5 | */
6 |
7 | "use strict";
8 |
9 | module.exports = require("./src");
--------------------------------------------------------------------------------
/packages/moleculer-zipkin/index.js:
--------------------------------------------------------------------------------
1 | /*
2 | * moleculer-zipkin
3 | * Copyright (c) 2018 MoleculerJS (https://github.com/moleculerjs/moleculer-addons)
4 | * MIT Licensed
5 | */
6 |
7 | "use strict";
8 |
9 | module.exports = require("./src");
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | cache:
3 | directories:
4 | - node_modules
5 | node_js:
6 | - "12"
7 | - "10"
8 | - "8"
9 | script:
10 | - npm run setup && npm test
11 | after_success:
12 | - npm run coverall
13 |
--------------------------------------------------------------------------------
/packages/moleculer-prometheus/index.js:
--------------------------------------------------------------------------------
1 | /*
2 | * moleculer-prometheus
3 | * Copyright (c) 2018 MoleculerJS (https://github.com/moleculerjs/moleculer-addons)
4 | * MIT Licensed
5 | */
6 |
7 | "use strict";
8 |
9 | module.exports = require("./src");
--------------------------------------------------------------------------------
/lerna.json:
--------------------------------------------------------------------------------
1 | {
2 | "lerna": "2.0.0-rc.5",
3 | "packages": [
4 | "packages/*"
5 | ],
6 | "version": "independent",
7 | "commands": {
8 | "publish": {
9 | "ignore": [
10 | "*.md"
11 | ]
12 | }
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/packages/moleculer-console-tracer/index.js:
--------------------------------------------------------------------------------
1 | /*
2 | * moleculer-console-tracer
3 | * Copyright (c) 2018 MoleculerJS (https://github.com/moleculerjs/moleculer-addons)
4 | * MIT Licensed
5 | */
6 |
7 | "use strict";
8 |
9 | module.exports = require("./src");
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # EditorConfig helps developers define and maintain consistent
2 | # coding styles between different editors and IDEs
3 | # editorconfig.org
4 |
5 | root = true
6 |
7 | [*]
8 |
9 | # Change these settings to your own preference
10 | indent_style = tab
11 | indent_size = 4
12 | space_after_anon_function = true
13 |
14 | # We recommend you to keep these unchanged
15 | end_of_line = lf
16 | charset = utf-8
17 | trim_trailing_whitespace = true
18 | insert_final_newline = true
19 |
20 | [*.md]
21 | indent_style = space
22 | indent_size = 4
23 | trim_trailing_whitespace = false
24 |
25 | [{package,bower}.json]
26 | indent_style = space
27 | indent_size = 2
28 |
29 | [*.js]
30 | quote_type = "double"
31 |
--------------------------------------------------------------------------------
/.codeclimate.yml:
--------------------------------------------------------------------------------
1 | version: "2"
2 |
3 | checks:
4 | argument-count:
5 | enabled: false
6 | complex-logic:
7 | enabled: false
8 | file-lines:
9 | enabled: false
10 | method-complexity:
11 | enabled: false
12 | method-count:
13 | enabled: false
14 | method-lines:
15 | enabled: false
16 | nested-control-flow:
17 | enabled: false
18 | return-statements:
19 | enabled: false
20 | similar-code:
21 | enabled: false
22 | identical-code:
23 | enabled: false
24 |
25 | plugins:
26 | duplication:
27 | enabled: false
28 | config:
29 | languages:
30 | - javascript
31 | eslint:
32 | enabled: true
33 | channel: "eslint-4"
34 | fixme:
35 | enabled: true
36 |
37 | exclude_paths:
38 | - test/
39 | - benchmark/
40 | - examples/
41 | - typings/
42 |
--------------------------------------------------------------------------------
/packages/moleculer-zipkin/examples/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | "env": {
3 | "node": true,
4 | "commonjs": true,
5 | "es6": true,
6 | },
7 | "extends": [
8 | "eslint:recommended",
9 | ],
10 | "parserOptions": {
11 | "sourceType": "module",
12 | "ecmaVersion": 2017,
13 | "ecmaFeatures": {
14 | "experimentalObjectRestSpread": true
15 | }
16 | },
17 | "rules": {
18 | "indent": [
19 | "warn",
20 | "tab",
21 | { SwitchCase: 1 }
22 | ],
23 | "quotes": [
24 | "warn",
25 | "double"
26 | ],
27 | "semi": [
28 | "error",
29 | "always"
30 | ],
31 | "no-var": [
32 | "warn"
33 | ],
34 | "no-console": [
35 | "off"
36 | ],
37 | "no-unused-vars": [
38 | "off"
39 | ],
40 | "no-trailing-spaces": [
41 | "error"
42 | ],
43 | }
44 | };
45 |
--------------------------------------------------------------------------------
/packages/moleculer-console-tracer/examples/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | "env": {
3 | "node": true,
4 | "commonjs": true,
5 | "es6": true,
6 | },
7 | "extends": [
8 | "eslint:recommended",
9 | ],
10 | "parserOptions": {
11 | "sourceType": "module",
12 | "ecmaVersion": 2017,
13 | "ecmaFeatures": {
14 | "experimentalObjectRestSpread": true
15 | }
16 | },
17 | "rules": {
18 | "indent": [
19 | "warn",
20 | "tab",
21 | { SwitchCase: 1 }
22 | ],
23 | "quotes": [
24 | "warn",
25 | "double"
26 | ],
27 | "semi": [
28 | "error",
29 | "always"
30 | ],
31 | "no-var": [
32 | "warn"
33 | ],
34 | "no-console": [
35 | "off"
36 | ],
37 | "no-unused-vars": [
38 | "off"
39 | ],
40 | "no-trailing-spaces": [
41 | "error"
42 | ],
43 | }
44 | };
45 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2017 MoleculerJS
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/packages/moleculer-jaeger/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 MoleculerJS
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/packages/moleculer-zipkin/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 MoleculerJS
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/readme-generator.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | const path = require("path");
4 | const glob = require("glob");
5 | const markdownMagic = require("markdown-magic");
6 |
7 | const readmePath = path.join(__dirname, "README.md");
8 | console.log(readmePath);
9 | markdownMagic(readmePath, {
10 |
11 | transforms: {
12 | RENDERLIST: function(content, opts) {
13 | const folders = glob.sync(path.join(opts.folder, "*"));
14 | //console.log(folders);
15 | if (folders.length == 0) return " ";
16 |
17 | let table = [
18 | "## " + opts.title,
19 | "| Name | Version | Description |",
20 | "| ---- | ------- | ----------- |"
21 |
22 | ];
23 | folders.forEach(folder => {
24 | let name = path.basename(folder);
25 | let pkg = require(path.resolve(folder, "package.json"));
26 |
27 | let line = `| [${name}](/${opts.folder}/${name}#readme) | [](https://www.npmjs.com/package/${name}) | ${pkg.description} |`;
28 |
29 | table.push(line);
30 | });
31 |
32 | return table.join("\n");
33 | }
34 | },
35 | DEBUG: false
36 |
37 | }, () => {
38 | console.log("README.md generated!");
39 | });
--------------------------------------------------------------------------------
/packages/moleculer-prometheus/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 MoleculerJS
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/packages/moleculer-console-tracer/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 MoleculerJS
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | lerna-debug.log*
6 | yarn-debug.log*
7 | yarn-error.log*
8 |
9 | # Runtime data
10 | pids
11 | *.pid
12 | *.seed
13 | *.pid.lock
14 |
15 | # Directory for instrumented libs generated by jscoverage/JSCover
16 | lib-cov
17 |
18 | # Coverage directory used by tools like istanbul
19 | coverage
20 |
21 | # nyc test coverage
22 | .nyc_output
23 |
24 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
25 | .grunt
26 |
27 | # Bower dependency directory (https://bower.io/)
28 | bower_components
29 |
30 | # node-waf configuration
31 | .lock-wscript
32 |
33 | # Compiled binary addons (http://nodejs.org/api/addons.html)
34 | build/Release
35 |
36 | # Dependency directories
37 | node_modules/
38 | jspm_packages/
39 |
40 | # Typescript v1 declaration files
41 | typings/
42 |
43 | # Optional npm cache directory
44 | .npm
45 |
46 | # Optional eslint cache
47 | .eslintcache
48 |
49 | # Optional REPL history
50 | .node_repl_history
51 |
52 | # Output of 'npm pack'
53 | *.tgz
54 |
55 | # Yarn Integrity file
56 | .yarn-integrity
57 |
58 | # dotenv environment variables file
59 | .env
60 | **/.idea
61 |
--------------------------------------------------------------------------------
/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | // Use IntelliSense to learn about possible Node.js debug attributes.
3 | // Hover to view descriptions of existing attributes.
4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
5 | "version": "0.2.0",
6 | "configurations": [
7 | {
8 | "type": "node",
9 | "request": "launch",
10 | "name": "Launch dev",
11 | "program": "${workspaceRoot}\\dev",
12 | "cwd": "${workspaceRoot}",
13 | "args": [
14 | "moleculer-console-tracer",
15 | "simple"
16 | ]
17 | },
18 | {
19 | "type": "node",
20 | "request": "launch",
21 | "name": "Launch readme gen",
22 | "program": "${workspaceRoot}/node_modules/moleculer-docgen/index.js",
23 | "args": [
24 | "-t",
25 | "packages\\moleculer-zipkin\\README.test.md",
26 | "packages\\moleculer-zipkin\\src\\index.js"
27 | ],
28 | "stopOnEntry": true
29 |
30 | },
31 | {
32 | "type": "node",
33 | "request": "launch",
34 | "name": "Jest",
35 | "program": "${workspaceRoot}\\node_modules\\jest-cli\\bin\\jest.js",
36 | "args": ["--runInBand"],
37 | "cwd": "${workspaceRoot}\\packages\\moleculer-prometheus",
38 | "runtimeArgs": [
39 | "--nolazy"
40 | ]
41 | }
42 | ]
43 | }
44 |
--------------------------------------------------------------------------------
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | "env": {
3 | "node": true,
4 | "commonjs": true,
5 | "es6": true,
6 | "jquery": false,
7 | "jest": true,
8 | "jasmine": true
9 | },
10 | "extends": [
11 | "eslint:recommended",
12 | "plugin:security/recommended"
13 | ],
14 | "parserOptions": {
15 | "sourceType": "module",
16 | "ecmaVersion": "2017"
17 | },
18 | "plugins": [
19 | "promise",
20 | "security"
21 | ],
22 | "rules": {
23 | "indent": [
24 | "warn",
25 | "tab",
26 | { SwitchCase: 1 }
27 | ],
28 | "quotes": [
29 | "warn",
30 | "double"
31 | ],
32 | "semi": [
33 | "error",
34 | "always"
35 | ],
36 | "no-var": [
37 | "error"
38 | ],
39 | "no-console": [
40 | "error"
41 | ],
42 | "no-unused-vars": [
43 | "warn"
44 | ],
45 | "no-trailing-spaces": [
46 | "error"
47 | ],
48 | "no-alert": 0,
49 | "no-shadow": 0,
50 | "security/detect-object-injection": ["off"],
51 | "security/detect-non-literal-require": ["off"],
52 | "security/detect-non-literal-fs-filename": ["off"],
53 | "no-process-exit": ["off"],
54 | "node/no-unpublished-require": 0,
55 | "space-before-function-paren": [
56 | "warn",
57 | {
58 | "anonymous": "never",
59 | "named": "never",
60 | "asyncArrow": "always"
61 | }
62 | ],
63 | "object-curly-spacing": [
64 | "warn",
65 | "always"
66 | ]
67 | }
68 | };
69 |
--------------------------------------------------------------------------------
/packages/moleculer-jaeger/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | "env": {
3 | "node": true,
4 | "commonjs": true,
5 | "es6": true,
6 | "jquery": false,
7 | "jest": true,
8 | "jasmine": true
9 | },
10 | "extends": [
11 | "eslint:recommended",
12 | "plugin:security/recommended"
13 | ],
14 | "parserOptions": {
15 | "sourceType": "module",
16 | "ecmaVersion": "2017"
17 | },
18 | "plugins": [
19 | "promise",
20 | "security"
21 | ],
22 | "rules": {
23 | "indent": [
24 | "warn",
25 | "tab",
26 | { SwitchCase: 1 }
27 | ],
28 | "quotes": [
29 | "warn",
30 | "double"
31 | ],
32 | "semi": [
33 | "error",
34 | "always"
35 | ],
36 | "no-var": [
37 | "error"
38 | ],
39 | "no-console": [
40 | "error"
41 | ],
42 | "no-unused-vars": [
43 | "warn"
44 | ],
45 | "no-trailing-spaces": [
46 | "error"
47 | ],
48 | "no-alert": 0,
49 | "no-shadow": 0,
50 | "security/detect-object-injection": ["off"],
51 | "security/detect-non-literal-require": ["off"],
52 | "security/detect-non-literal-fs-filename": ["off"],
53 | "no-process-exit": ["off"],
54 | "node/no-unpublished-require": 0,
55 | "space-before-function-paren": [
56 | "warn",
57 | {
58 | "anonymous": "never",
59 | "named": "never",
60 | "asyncArrow": "always"
61 | }
62 | ],
63 | "object-curly-spacing": [
64 | "warn",
65 | "always"
66 | ]
67 | }
68 | };
69 |
--------------------------------------------------------------------------------
/packages/moleculer-zipkin/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | "env": {
3 | "node": true,
4 | "commonjs": true,
5 | "es6": true,
6 | "jquery": false,
7 | "jest": true,
8 | "jasmine": true
9 | },
10 | "extends": [
11 | "eslint:recommended",
12 | "plugin:security/recommended"
13 | ],
14 | "parserOptions": {
15 | "sourceType": "module",
16 | "ecmaVersion": "2017"
17 | },
18 | "plugins": [
19 | "promise",
20 | "security"
21 | ],
22 | "rules": {
23 | "indent": [
24 | "warn",
25 | "tab",
26 | { SwitchCase: 1 }
27 | ],
28 | "quotes": [
29 | "warn",
30 | "double"
31 | ],
32 | "semi": [
33 | "error",
34 | "always"
35 | ],
36 | "no-var": [
37 | "error"
38 | ],
39 | "no-console": [
40 | "error"
41 | ],
42 | "no-unused-vars": [
43 | "warn"
44 | ],
45 | "no-trailing-spaces": [
46 | "error"
47 | ],
48 | "no-alert": 0,
49 | "no-shadow": 0,
50 | "security/detect-object-injection": ["off"],
51 | "security/detect-non-literal-require": ["off"],
52 | "security/detect-non-literal-fs-filename": ["off"],
53 | "no-process-exit": ["off"],
54 | "node/no-unpublished-require": 0,
55 | "space-before-function-paren": [
56 | "warn",
57 | {
58 | "anonymous": "never",
59 | "named": "never",
60 | "asyncArrow": "always"
61 | }
62 | ],
63 | "object-curly-spacing": [
64 | "warn",
65 | "always"
66 | ]
67 | }
68 | };
69 |
--------------------------------------------------------------------------------
/packages/moleculer-prometheus/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | "env": {
3 | "node": true,
4 | "commonjs": true,
5 | "es6": true,
6 | "jquery": false,
7 | "jest": true,
8 | "jasmine": true
9 | },
10 | "extends": [
11 | "eslint:recommended",
12 | "plugin:security/recommended"
13 | ],
14 | "parserOptions": {
15 | "sourceType": "module",
16 | "ecmaVersion": "2017"
17 | },
18 | "plugins": [
19 | "promise",
20 | "security"
21 | ],
22 | "rules": {
23 | "indent": [
24 | "warn",
25 | "tab",
26 | { SwitchCase: 1 }
27 | ],
28 | "quotes": [
29 | "warn",
30 | "double"
31 | ],
32 | "semi": [
33 | "error",
34 | "always"
35 | ],
36 | "no-var": [
37 | "error"
38 | ],
39 | "no-console": [
40 | "error"
41 | ],
42 | "no-unused-vars": [
43 | "warn"
44 | ],
45 | "no-trailing-spaces": [
46 | "error"
47 | ],
48 | "no-alert": 0,
49 | "no-shadow": 0,
50 | "security/detect-object-injection": ["off"],
51 | "security/detect-non-literal-require": ["off"],
52 | "security/detect-non-literal-fs-filename": ["off"],
53 | "no-process-exit": ["off"],
54 | "node/no-unpublished-require": 0,
55 | "space-before-function-paren": [
56 | "warn",
57 | {
58 | "anonymous": "never",
59 | "named": "never",
60 | "asyncArrow": "always"
61 | }
62 | ],
63 | "object-curly-spacing": [
64 | "warn",
65 | "always"
66 | ]
67 | }
68 | };
69 |
--------------------------------------------------------------------------------
/packages/moleculer-console-tracer/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | "env": {
3 | "node": true,
4 | "commonjs": true,
5 | "es6": true,
6 | "jquery": false,
7 | "jest": true,
8 | "jasmine": true
9 | },
10 | "extends": [
11 | "eslint:recommended",
12 | "plugin:security/recommended"
13 | ],
14 | "parserOptions": {
15 | "sourceType": "module",
16 | "ecmaVersion": "2017"
17 | },
18 | "plugins": [
19 | "promise",
20 | "security"
21 | ],
22 | "rules": {
23 | "no-control-regex": "off",
24 | "indent": [
25 | "warn",
26 | "tab",
27 | {SwitchCase: 1}
28 | ],
29 | "quotes": [
30 | "warn",
31 | "double"
32 | ],
33 | "semi": [
34 | "error",
35 | "always"
36 | ],
37 | "no-var": [
38 | "error"
39 | ],
40 | "no-console": [
41 | "error"
42 | ],
43 | "no-unused-vars": [
44 | "warn"
45 | ],
46 | "no-trailing-spaces": [
47 | "error"
48 | ],
49 | "no-alert": 0,
50 | "no-shadow": 0,
51 | "security/detect-object-injection": ["off"],
52 | "security/detect-non-literal-require": ["off"],
53 | "security/detect-non-literal-fs-filename": ["off"],
54 | "no-process-exit": ["off"],
55 | "node/no-unpublished-require": 0,
56 | "space-before-function-paren": [
57 | "warn",
58 | {
59 | "anonymous": "never",
60 | "named": "never",
61 | "asyncArrow": "always"
62 | }
63 | ],
64 | "object-curly-spacing": [
65 | "warn",
66 | "always"
67 | ]
68 | }
69 | };
70 |
--------------------------------------------------------------------------------
/packages/moleculer-prometheus/examples/simple/index.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | const { ServiceBroker } = require("moleculer");
4 | const { MoleculerError } = require("moleculer").Errors;
5 | const PromService = require("../../index");
6 |
7 | // Create broker
8 | const broker = new ServiceBroker({
9 | logger: console,
10 | logLevel: "info",
11 | metrics: true,
12 | sampleCount: 1
13 | });
14 |
15 | // Load Prometheus service
16 | broker.createService({
17 | mixins: [PromService],
18 | settings: {
19 | metrics: {
20 | "custom_value": { type: "Gauge", help: "Moleculer Prometheus custom metric" },
21 | }
22 | }
23 | });
24 |
25 | broker.createService({
26 | name: "posts",
27 | actions: {
28 | find: {
29 | handler() {
30 | if (Math.random() > 0.95)
31 | return this.Promise.reject(new MoleculerError("Something went wrong"));
32 |
33 | return this.Promise.resolve([]).delay(Math.round(Math.random() * 200));
34 | }
35 | }
36 | }
37 | });
38 |
39 | // Start server
40 | broker.start().then(() => {
41 | broker.repl();
42 |
43 | setInterval(() => {
44 | // Call action
45 | broker.call("posts.find")
46 | .then(() => broker.logger.info("Request done."))
47 | .catch(err => broker.logger.info("Request error: ", err.message));
48 | }, 100);
49 |
50 | setInterval(() => {
51 | broker.broadcast("metrics.update", {
52 | name: "custom_value",
53 | method: "set",
54 | value: Math.round(Math.random() * 100)
55 | });
56 | }, 1000);
57 | });
58 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "moleculer-metrics-addons",
3 | "description": "Official monitoring & metrics addons for Moleculer framework",
4 | "version": "1.0.0",
5 | "scripts": {
6 | "setup": "npm install && lerna bootstrap",
7 | "clean": "lerna clean",
8 | "dev": "nodemon dev.js",
9 | "demo": "node dev.js",
10 | "test": "jest --coverage",
11 | "ci": "jest --watch",
12 | "coverall": "cat ./coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js",
13 | "init": "cd packages && moleculer init addon",
14 | "audit": "lerna exec --concurrency 1 npm audit fix",
15 | "deps": "lerna exec --concurrency 1 npm run deps",
16 | "release": "lerna publish",
17 | "readme": "node readme-generator.js"
18 | },
19 | "repository": {
20 | "type": "git",
21 | "url": "git+https://github.com/moleculerjs/moleculer-metrics.git"
22 | },
23 | "keywords": [],
24 | "author": "MoleculerJS",
25 | "license": "MIT",
26 | "homepage": "https://github.com/moleculerjs/moleculer-metrics#readme",
27 | "devDependencies": {
28 | "coveralls": "3.0.6",
29 | "eslint": "5.16.0",
30 | "eslint-plugin-node": "9.1.0",
31 | "eslint-plugin-promise": "4.2.1",
32 | "eslint-plugin-security": "1.4.0",
33 | "glob": "7.1.4",
34 | "jest": "24.9.0",
35 | "jest-cli": "24.9.0",
36 | "lerna": "3.16.4",
37 | "markdown-magic": "0.1.25",
38 | "nodemon": "1.19.2"
39 | },
40 | "dependencies": {
41 | "moleculer-cli": "^0.7.0"
42 | },
43 | "jest": {
44 | "testEnvironment": "node",
45 | "coveragePathIgnorePatterns": [
46 | "/node_modules/",
47 | "/test/services/"
48 | ]
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/packages/moleculer-prometheus/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "moleculer-prometheus",
3 | "version": "0.2.5",
4 | "description": "Moleculer metrics module for Prometheus.",
5 | "main": "index.js",
6 | "scripts": {
7 | "dev": "nodemon examples/index.js",
8 | "ci": "jest --watch",
9 | "test": "jest --coverage",
10 | "lint": "eslint --ext=.js src test",
11 | "deps": "npm-check -u",
12 | "readme": "node node_modules/moleculer-docgen/index.js -t README.md src/index.js",
13 | "coverall": "cat ./coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js"
14 | },
15 | "keywords": [
16 | "microservice",
17 | "moleculer"
18 | ],
19 | "repository": {
20 | "type": "git",
21 | "url": "git@github.com:moleculerjs/moleculer-addons.git"
22 | },
23 | "homepage": "https://github.com/moleculerjs/moleculer-addons/tree/master/packages/moleculer-prometheus#readme",
24 | "author": "MoleculerJS",
25 | "license": "MIT",
26 | "peerDependencies": {
27 | "moleculer": ">=0.12.0 || >=0.13.0"
28 | },
29 | "devDependencies": {
30 | "coveralls": "3.0.2",
31 | "eslint": "5.16.0",
32 | "eslint-plugin-node": "9.1.0",
33 | "eslint-plugin-promise": "4.2.1",
34 | "eslint-plugin-security": "1.4.0",
35 | "jest": "24.8.0",
36 | "jest-cli": "24.8.0",
37 | "moleculer": "0.13.9",
38 | "moleculer-docgen": "0.2.2",
39 | "nodemon": "1.17.5",
40 | "npm-check": "^5.9.2"
41 | },
42 | "jest": {
43 | "testEnvironment": "node",
44 | "coveragePathIgnorePatterns": [
45 | "/node_modules/",
46 | "/test/services/"
47 | ]
48 | },
49 | "engines": {
50 | "node": ">= 6.x.x"
51 | },
52 | "dependencies": {
53 | "polka": "0.4.0",
54 | "prom-client": "11.1.1"
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/packages/moleculer-zipkin/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "moleculer-zipkin",
3 | "version": "0.2.3",
4 | "description": "Moleculer metrics module for Zipkin.",
5 | "main": "index.js",
6 | "scripts": {
7 | "dev": "nodemon examples/index.js",
8 | "ci": "jest --watch",
9 | "test": "jest --coverage",
10 | "lint": "eslint --ext=.js src test",
11 | "deps": "npm-check -u",
12 | "readme": "node node_modules/moleculer-docgen/index.js -t README.md src/index.js",
13 | "coverall": "cat ./coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js"
14 | },
15 | "keywords": [
16 | "microservice",
17 | "moleculer"
18 | ],
19 | "repository": {
20 | "type": "git",
21 | "url": "git@github.com:moleculerjs/moleculer-addons.git"
22 | },
23 | "homepage": "https://github.com/moleculerjs/moleculer-addons/tree/master/packages/moleculer-zipkin#readme",
24 | "author": "MoleculerJS",
25 | "license": "MIT",
26 | "peerDependencies": {
27 | "moleculer": ">=0.12.0 || >=0.13.0"
28 | },
29 | "devDependencies": {
30 | "benchmarkify": "2.1.2",
31 | "coveralls": "3.0.2",
32 | "eslint": "5.16.0",
33 | "eslint-plugin-node": "9.1.0",
34 | "eslint-plugin-promise": "4.2.1",
35 | "eslint-plugin-security": "1.4.0",
36 | "jest": "24.8.0",
37 | "jest-cli": "24.8.0",
38 | "lolex": "^4.2.0",
39 | "moleculer": "0.13.9",
40 | "moleculer-docgen": "0.2.2",
41 | "moleculer-web": "^0.9.1",
42 | "nodemon": "1.19.1",
43 | "npm-check": "^5.9.2"
44 | },
45 | "jest": {
46 | "testEnvironment": "node",
47 | "coveragePathIgnorePatterns": [
48 | "/node_modules/",
49 | "/test/services/"
50 | ]
51 | },
52 | "engines": {
53 | "node": ">= 6.x.x"
54 | },
55 | "dependencies": {
56 | "axios": "0.19.0"
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/packages/moleculer-jaeger/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "moleculer-jaeger",
3 | "version": "0.2.3",
4 | "description": "Moleculer metrics module for Jaeger.",
5 | "main": "index.js",
6 | "scripts": {
7 | "dev": "nodemon examples/index.js",
8 | "ci": "jest --watch",
9 | "test": "jest --coverage",
10 | "lint": "eslint --ext=.js src test",
11 | "deps": "npm-check -u",
12 | "readme": "node node_modules/moleculer-docgen/index.js -t README.md src/index.js",
13 | "coverall": "cat ./coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js"
14 | },
15 | "keywords": [
16 | "microservice",
17 | "moleculer"
18 | ],
19 | "pre-commit": [
20 | "lint"
21 | ],
22 | "repository": {
23 | "type": "git",
24 | "url": "git@github.com:moleculerjs/moleculer-addons.git"
25 | },
26 | "homepage": "https://github.com/moleculerjs/moleculer-addons/tree/master/packages/moleculer-jaeger#readme",
27 | "author": "MoleculerJS",
28 | "license": "MIT",
29 | "peerDependencies": {
30 | "moleculer": ">=0.12.0 || >=0.13.0"
31 | },
32 | "devDependencies": {
33 | "coveralls": "^3.0.11",
34 | "eslint": "5.16.0",
35 | "eslint-plugin-node": "9.1.0",
36 | "eslint-plugin-promise": "4.2.1",
37 | "eslint-plugin-security": "1.4.0",
38 | "jest": "24.8.0",
39 | "jest-cli": "24.8.0",
40 | "moleculer": "0.13.9",
41 | "moleculer-docgen": "0.2.2",
42 | "moleculer-web": "^0.9.1",
43 | "nodemon": "1.19.1",
44 | "npm-check": "^5.9.2",
45 | "pre-commit": "^1.2.2"
46 | },
47 | "jest": {
48 | "testEnvironment": "node",
49 | "coveragePathIgnorePatterns": [
50 | "/node_modules/",
51 | "/test/services/"
52 | ]
53 | },
54 | "engines": {
55 | "node": ">= 6.x.x"
56 | },
57 | "dependencies": {
58 | "jaeger-client": "^3.17.2",
59 | "lodash.isfunction": "3.0.9",
60 | "node-int64": "0.4.0"
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/packages/moleculer-console-tracer/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "moleculer-console-tracer",
3 | "version": "0.2.3",
4 | "description": "Simple tracer service to print metric traces to the console.",
5 | "main": "index.js",
6 | "scripts": {
7 | "dev": "nodemon examples/index.js",
8 | "ci": "jest --watch",
9 | "test": "jest --coverage",
10 | "lint": "eslint --ext=.js src test",
11 | "lint:fix": "eslint --ext=.js --fix src",
12 | "deps": "npm-check -u",
13 | "readme": "node node_modules/moleculer-docgen/index.js -t README.md src/index.js",
14 | "coverall": "cat ./coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js"
15 | },
16 | "keywords": [
17 | "microservice",
18 | "moleculer"
19 | ],
20 | "repository": {
21 | "type": "git",
22 | "url": "git@github.com:moleculerjs/moleculer-addons.git"
23 | },
24 | "homepage": "https://github.com/moleculerjs/moleculer-addons/tree/master/packages/moleculer-console-tracer#readme",
25 | "author": "MoleculerJS",
26 | "license": "MIT",
27 | "peerDependencies": {
28 | "moleculer": ">=0.12.0 || >=0.13.0"
29 | },
30 | "devDependencies": {
31 | "coveralls": "^3.0.11",
32 | "eslint": "5.16.0",
33 | "eslint-plugin-node": "9.1.0",
34 | "eslint-plugin-promise": "4.2.1",
35 | "eslint-plugin-security": "1.4.0",
36 | "jest": "24.8.0",
37 | "jest-cli": "24.8.0",
38 | "moleculer": "^0.13.9",
39 | "moleculer-docgen": "0.2.2",
40 | "moleculer-web": "^0.9.1",
41 | "nodemon": "1.17.5",
42 | "npm-check": "^5.9.2"
43 | },
44 | "jest": {
45 | "testEnvironment": "node",
46 | "coveragePathIgnorePatterns": [
47 | "/node_modules/",
48 | "/test/services/"
49 | ]
50 | },
51 | "engines": {
52 | "node": ">= 6.x.x"
53 | },
54 | "dependencies": {
55 | "chalk": "2.4.2",
56 | "lodash.isnan": "3.0.2",
57 | "lodash.repeat": "4.1.0",
58 | "slice-ansi": "1.0.0",
59 | "tiny-human-time": "1.2.0"
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/packages/moleculer-jaeger/examples/api/index.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | const { ServiceBroker } = require("moleculer");
4 | const { MoleculerError } = require("moleculer").Errors;
5 | const JaegerService = require("../../index");
6 | const ApiGateway = require("moleculer-web");
7 |
8 | const THROW_ERR = true;
9 |
10 | // Create broker
11 | const broker = new ServiceBroker({
12 | logger: console,
13 | logLevel: "debug",
14 | metrics: true,
15 | sampleCount: 1
16 | });
17 |
18 | broker.createService(ApiGateway);
19 |
20 | // Load Jaeger service
21 | broker.createService({
22 | mixins: [JaegerService],
23 | settings: {
24 | host: "192.168.0.181"
25 | }
26 | });
27 |
28 | const POSTS = [
29 | { id: 1, title: "First post", content: "Content of first post", author: 2 },
30 | { id: 2, title: "Second post", content: "Content of second post", author: 1 },
31 | { id: 3, title: "3rd post", content: "Content of 3rd post", author: 2 },
32 | ];
33 |
34 | broker.createService({
35 | name: "posts",
36 | actions: {
37 | find: {
38 | metrics: {
39 | params: true,
40 | },
41 | handler(ctx) {
42 | const posts = POSTS.map(post => ({...post}));
43 |
44 | return this.Promise.all(posts.map(post => {
45 | return this.Promise.all([
46 | ctx.call("users.get", { id: post.author }).then(author => post.author = author),
47 | ctx.call("votes.count", { postID: post.id }).then(votes => post.votes = votes),
48 | ]);
49 | })).then(() => posts);
50 | }
51 | }
52 | }
53 | });
54 |
55 | const USERS = [
56 | { id: 1, name: "John Doe" },
57 | { id: 2, name: "Jane Doe" },
58 | ];
59 |
60 | broker.createService({
61 | name: "users",
62 | actions: {
63 | get: {
64 | metrics: {
65 | params: true,
66 | },
67 | handler(ctx) {
68 | return this.Promise.resolve()
69 | .then(() => {
70 | const user = USERS.find(user => user.id == ctx.params.id);
71 | if (user) {
72 | const res = {...user};
73 | return ctx.call("friends.count", { userID: user.id })
74 | .then(friends => res.friends = friends)
75 | .then(() => res);
76 | }
77 | });
78 | }
79 | }
80 | }
81 | });
82 |
83 | broker.createService({
84 | name: "votes",
85 | actions: {
86 | count: {
87 | metrics: {
88 | params: ["postID"],
89 | meta: false,
90 | },
91 | handler(ctx) {
92 | return this.Promise.resolve().delay(20).then(() => ctx.params.postID * 3);
93 | }
94 | }
95 | }
96 | });
97 |
98 | broker.createService({
99 | name: "friends",
100 | actions: {
101 | count: {
102 | metrics: {
103 | params: ["userID"],
104 | meta: false,
105 | },
106 | handler(ctx) {
107 | if (THROW_ERR && ctx.params.userID == 1)
108 | throw new MoleculerError("Friends is not found!", 404, "FRIENDS_NOT_FOUND", { userID: ctx.params.userID });
109 |
110 | return this.Promise.resolve().delay(30).then(() => ctx.params.userID * 3);
111 | }
112 | }
113 | }
114 | });
115 |
116 | // Start server
117 | broker.start().then(() => {
118 | broker.repl();
119 |
120 | broker.logger.info("Open http://localhost:3000/posts/find?limit=5");
121 | });
122 |
--------------------------------------------------------------------------------
/packages/moleculer-jaeger/examples/simple/index.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | const { ServiceBroker } = require("moleculer");
4 | const { MoleculerError } = require("moleculer").Errors;
5 | const JaegerService = require("../../index");
6 |
7 | const THROW_ERR = false;
8 |
9 | // Create broker
10 | const broker = new ServiceBroker({
11 | logger: console,
12 | logLevel: "debug",
13 | metrics: true,
14 | sampleCount: 1
15 | });
16 |
17 | // Load Zipkin service
18 | broker.createService({
19 | mixins: [JaegerService],
20 | settings: {
21 | host: "192.168.0.181"
22 | }
23 | });
24 |
25 | const POSTS = [
26 | { id: 1, title: "First post", content: "Content of first post", author: 2 },
27 | { id: 2, title: "Second post", content: "Content of second post", author: 1 },
28 | { id: 3, title: "3rd post", content: "Content of 3rd post", author: 2 },
29 | ];
30 |
31 | broker.createService({
32 | name: "posts",
33 | actions: {
34 | find: {
35 | metrics: {
36 | params: true,
37 | },
38 | handler(ctx) {
39 | const posts = POSTS.map(post => ({...post}));
40 |
41 | return this.Promise.all(posts.map(post => {
42 | return this.Promise.all([
43 | ctx.call("users.get", { id: post.author }).then(author => post.author = author),
44 | ctx.call("votes.count", { postID: post.id }).then(votes => post.votes = votes),
45 | ]);
46 | })).then(() => posts);
47 | }
48 | }
49 | }
50 | });
51 |
52 | const USERS = [
53 | { id: 1, name: "John Doe" },
54 | { id: 2, name: "Jane Doe" },
55 | ];
56 |
57 | broker.createService({
58 | name: "users",
59 | actions: {
60 | get: {
61 | metrics: {
62 | params: true,
63 | },
64 | handler(ctx) {
65 | return this.Promise.resolve()
66 | .then(() => {
67 | const user = USERS.find(user => user.id === ctx.params.id);
68 | if (user) {
69 | const res = {...user};
70 | return ctx.call("friends.count", { userID: user.id })
71 | .then(friends => res.friends = friends)
72 | .then(() => res);
73 | }
74 | });
75 | }
76 | }
77 | }
78 | });
79 |
80 | broker.createService({
81 | name: "votes",
82 | actions: {
83 | count: {
84 | metrics: {
85 | params: ["postID"],
86 | meta: false,
87 | },
88 | handler(ctx) {
89 | return this.Promise.resolve().delay(20).then(() => ctx.params.postID * 3);
90 | }
91 | }
92 | }
93 | });
94 |
95 | broker.createService({
96 | name: "friends",
97 | actions: {
98 | count: {
99 | metrics: {
100 | params: ["userID"],
101 | meta: false,
102 | },
103 | handler(ctx) {
104 | if (THROW_ERR && ctx.params.userID == 1)
105 | throw new MoleculerError("Friends is not found!", 404, "FRIENDS_NOT_FOUND", { userID: ctx.params.userID });
106 |
107 | return this.Promise.resolve().delay(30).then(() => ctx.params.userID * 3);
108 | }
109 | }
110 | }
111 | });
112 |
113 | // Start server
114 | broker.start().then(() => {
115 | broker.repl();
116 |
117 | // Call action
118 | broker
119 | .call("posts.find", { limit: 5 }, { meta: { loggedIn: { username: "Adam" } } })
120 | .then(console.log)
121 | .catch(console.error);
122 | });
123 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | 
2 |
3 | [](https://travis-ci.org/moleculerjs/moleculer-metrics)
4 | [](https://coveralls.io/github/moleculerjs/moleculer-metrics?branch=master)
5 | [](https://www.codacy.com/app/mereg-norbert/moleculer-metrics?utm_source=github.com&utm_medium=referral&utm_content=moleculerjs/moleculer-metrics&utm_campaign=Badge_Grade)
6 | [](https://codeclimate.com/github/moleculerjs/moleculer-metrics/maintainability)
7 | [](https://snyk.io/test/github/moleculerjs/moleculer-metrics)
8 | [](https://gitter.im/moleculerjs/moleculer?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
9 |
10 | # Monitoring & metrics addons for Moleculer framework
11 |
12 |
13 | ## Modules
14 | | Name | Version | Description |
15 | | ---- | ------- | ----------- |
16 | | [moleculer-console-tracer](/packages/moleculer-console-tracer#readme) | [](https://www.npmjs.com/package/moleculer-console-tracer) | Simple tracer service to print metric traces to the console. |
17 | | [moleculer-jaeger](/packages/moleculer-jaeger#readme) | [](https://www.npmjs.com/package/moleculer-jaeger) | Moleculer metrics module for Jaeger. |
18 | | [moleculer-prometheus](/packages/moleculer-prometheus#readme) | [](https://www.npmjs.com/package/moleculer-prometheus) | Moleculer metrics module for Prometheus. |
19 | | [moleculer-zipkin](/packages/moleculer-zipkin#readme) | [](https://www.npmjs.com/package/moleculer-zipkin) | Moleculer metrics module for Zipkin. |
20 |
21 |
22 | # Contribution
23 |
24 | ## Install dependencies
25 | ```bash
26 | $ npm run setup
27 | ```
28 |
29 | ## Development
30 | **Run the `simple` example in `moleculer-zipkin` service with watching**
31 | ```bash
32 | $ npm run dev moleculer-zipkin
33 | ```
34 |
35 | **Run the `full` example in `moleculer-zipkin` service w/o watching**
36 | ```bash
37 | $ npm run demo moleculer-zipkin full
38 | ```
39 |
40 | ## Test
41 | ```bash
42 | $ npm test
43 | ```
44 |
45 | ## Create a new addon
46 | ```bash
47 | $ npm run init moleculer-
48 | ```
49 |
50 | ## Publish new releases
51 | ```bash
52 | $ npm run release
53 | ```
54 |
55 | # License
56 | The project is available under the [MIT license](https://tldrlegal.com/license/mit-license).
57 |
58 | # Contact
59 | Copyright (c) 2016-2018 MoleculerJS
60 |
61 | [](https://github.com/moleculerjs) [](https://twitter.com/MoleculerJS)
62 |
--------------------------------------------------------------------------------
/packages/moleculer-zipkin/examples/simple/index.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | const { ServiceBroker } = require("moleculer");
4 | const { MoleculerError } = require("moleculer").Errors;
5 | const ZipkinService = require("../../index");
6 | const ApiGateway = require("moleculer-web");
7 |
8 | const THROW_ERR = true;
9 |
10 | // Create broker
11 | const broker = new ServiceBroker({
12 | logger: console,
13 | logLevel: "debug",
14 | metrics: true,
15 | sampleCount: 1
16 | });
17 |
18 | broker.createService(ApiGateway);
19 |
20 | // Load Zipkin service
21 | broker.createService({
22 | mixins: [ZipkinService],
23 | settings: {
24 | baseURL: process.env.ZIPKIN_URL || "http://192.168.0.181:9411",
25 | //batchTime: 0,
26 | //version: "v2",
27 | }
28 | });
29 |
30 | const POSTS = [
31 | { id: 1, title: "First post", content: "Content of first post", author: 2 },
32 | { id: 2, title: "Second post", content: "Content of second post", author: 1 },
33 | { id: 3, title: "3rd post", content: "Content of 3rd post", author: 2 },
34 | ];
35 |
36 | broker.createService({
37 | name: "posts",
38 | actions: {
39 | find: {
40 | metrics: {
41 | params: true,
42 | },
43 | handler(ctx) {
44 | const posts = POSTS.map( x=> ({...x}));
45 |
46 | return this.Promise.all(posts.map(post => {
47 | return this.Promise.all([
48 | ctx.call("users.get", { id: post.author }).then(author => post.author = author),
49 | ctx.call("votes.count", { postID: post.id }).then(votes => post.votes = votes),
50 | ]);
51 | })).then(() => posts);
52 | }
53 | }
54 | }
55 | });
56 |
57 | const USERS = [
58 | { id: 1, name: "John Doe" },
59 | { id: 2, name: "Jane Doe" },
60 | ];
61 |
62 | broker.createService({
63 | name: "users",
64 | actions: {
65 | get: {
66 | metrics: {
67 | params: true,
68 | },
69 | handler(ctx) {
70 | return this.Promise.resolve()
71 | .then(() => {
72 | const user = USERS.find(user => user.id === ctx.params.id);
73 | if (user) {
74 | const res = {...user};
75 | return ctx.call("friends.count", { userID: user.id })
76 | .then(friends => res.friends = friends)
77 | .then(() => res);
78 | }
79 | });
80 | }
81 | }
82 | }
83 | });
84 |
85 | broker.createService({
86 | name: "votes",
87 | actions: {
88 | count: {
89 | metrics: {
90 | params: ["postID"],
91 | meta: false,
92 | },
93 | handler(ctx) {
94 | return this.Promise.resolve().delay(20).then(() => ctx.params.postID * 3);
95 | }
96 | }
97 | }
98 | });
99 |
100 | broker.createService({
101 | name: "friends",
102 | actions: {
103 | count: {
104 | metrics: {
105 | params: ["userID"],
106 | meta: false,
107 | },
108 | handler(ctx) {
109 | if (THROW_ERR && ctx.params.userID === 1)
110 | throw new MoleculerError("Friends is not found!", 404, "FRIENDS_NOT_FOUND", { userID: ctx.params.userID });
111 |
112 | return this.Promise.resolve().delay(30).then(() => ctx.params.userID * 3);
113 | }
114 | }
115 | }
116 | });
117 |
118 | // Start server
119 | broker.start().then(() => {
120 | broker.repl();
121 |
122 | broker.logger.info("Open http://localhost:3000/posts/find?limit=5");
123 |
124 | // Call action
125 | broker
126 | .call("posts.find", { limit: 5 }, { meta: { loggedIn: { username: "Adam" } } })
127 | .then(console.log)
128 | .catch(console.error);
129 | });
130 |
--------------------------------------------------------------------------------
/packages/moleculer-console-tracer/examples/simple/index.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | const { ServiceBroker } = require("moleculer");
4 | const { MoleculerError } = require("moleculer").Errors;
5 | const TracingService = require("../../index");
6 | const ApiGateway = require("moleculer-web");
7 |
8 | const THROW_ERR = false;
9 |
10 | // Create broker
11 | const broker = new ServiceBroker({
12 | logger: console,
13 | logFormatter: "simple",
14 | logLevel: "info",
15 | metrics: true,
16 | sampleCount: 1,
17 | cacher: true
18 | });
19 |
20 | broker.createService(ApiGateway);
21 |
22 | // Load Console Tracing service
23 | broker.createService({
24 | mixins: [TracingService],
25 | settings: {
26 | //width: 50,
27 | //gaugeWidth: 25
28 | }
29 | });
30 |
31 | const POSTS = [
32 | { id: 1, title: "First post", content: "Content of first post", author: 2 },
33 | { id: 2, title: "Second post", content: "Content of second post", author: 1 },
34 | { id: 3, title: "3rd post", content: "Content of 3rd post", author: 2 },
35 | ];
36 |
37 | broker.createService({
38 | name: "posts",
39 | actions: {
40 | find: {
41 | metrics: {
42 | params: true,
43 | },
44 | handler(ctx) {
45 | const posts = POSTS.map(post=> ({...post}));
46 |
47 | return this.Promise.all(posts.map(post => {
48 | return this.Promise.all([
49 | ctx.call("users.get", { id: post.author }).then(author => post.author = author),
50 | ctx.call("votes.count", { postID: post.id }).then(votes => post.votes = votes),
51 | ]);
52 | })).then(() => posts);
53 | }
54 | }
55 | }
56 | });
57 |
58 | const USERS = [
59 | { id: 1, name: "John Doe" },
60 | { id: 2, name: "Jane Doe" },
61 | ];
62 |
63 | broker.createService({
64 | name: "users",
65 | actions: {
66 | get: {
67 | metrics: {
68 | params: true,
69 | },
70 | handler(ctx) {
71 | return this.Promise.resolve()
72 | .then(() => {
73 | const user = USERS.find(user => user.id == ctx.params.id);
74 | if (user) {
75 | const res = {...user};
76 | return ctx.call("friends.count", { userID: user.id })
77 | .then(friends => res.friends = friends)
78 | .then(() => res);
79 | }
80 | });
81 | }
82 | }
83 | }
84 | });
85 |
86 | broker.createService({
87 | name: "votes",
88 | actions: {
89 | count: {
90 | metrics: {
91 | params: ["postID"],
92 | meta: false,
93 | },
94 | handler(ctx) {
95 | return this.Promise.resolve().delay(10).then(() => ctx.params.postID * 3);
96 | }
97 | }
98 | }
99 | });
100 |
101 | broker.createService({
102 | name: "friends",
103 | actions: {
104 | count: {
105 | cache: true,
106 | metrics: {
107 | params: ["userID"],
108 | meta: false,
109 | },
110 | handler(ctx) {
111 | if (THROW_ERR && ctx.params.userID == 1)
112 | throw new MoleculerError("Friends is not found!", 404, "FRIENDS_NOT_FOUND", { userID: ctx.params.userID });
113 |
114 | return this.Promise.resolve().delay(5).then(() => ctx.params.userID * 3);
115 | }
116 | }
117 | }
118 | });
119 |
120 | // Start server
121 | broker.start().then(() => {
122 | broker.repl();
123 |
124 | broker.logger.info("Open http://localhost:3000/posts/find?limit=5");
125 |
126 | // Call action
127 | broker
128 | .call("posts.find", { limit: 5 }, { meta: { loggedIn: { username: "Adam" } } })
129 | //.then(console.log)
130 | .catch(err => console.error(err.message));
131 | });
132 |
--------------------------------------------------------------------------------
/packages/moleculer-console-tracer/README.md:
--------------------------------------------------------------------------------
1 | 
2 |
3 | # moleculer-console-tracer [](https://www.npmjs.com/package/moleculer-console-tracer)
4 |
5 | Simple tracer service to print metric traces to the console.
6 | ***Do not use it in production. Just for prototyping and testing.***
7 |
8 | 
9 |
10 | _Don't execute multiple instances because it is not a centralized tracing solution._
11 |
12 | # Features
13 |
14 | # Install
15 |
16 | ```bash
17 | $ npm install moleculer-console-tracer
18 | ```
19 |
20 | # Usage
21 |
22 | ```js
23 | // services/metrics.tracer.service.js
24 |
25 | const Tracer = require("moleculer-console-tracer");
26 |
27 | module.exports = {
28 | mixins: [Tracer],
29 | settings: {
30 | width: 100,
31 | gaugeWidth: 50
32 | }
33 | };
34 |
35 | // moleculer.config.js
36 | module.exports = {
37 | // ...
38 | metrics: true,
39 | // ...
40 | }
41 | ```
42 |
43 |
44 |
45 |
46 |
53 |
54 |
55 |
56 | # Settings
57 |
58 |
59 | | Property | Type | Default | Description |
60 | | -------- | ---- | ------- | ----------- |
61 | | `width` | `Number` | `80` | Table width. |
62 | | `gaugeWidth` | `Number` | `40` | Gauge width. |
63 |
64 |
65 |
66 |
77 |
78 | # Actions
79 |
80 |
81 |
82 |
117 |
118 | # Methods
119 |
120 |
121 |
122 |
123 |
158 |
159 | # Test
160 | ```
161 | $ npm test
162 | ```
163 |
164 | In development with watching
165 |
166 | ```
167 | $ npm run ci
168 | ```
169 |
170 | # License
171 | The project is available under the [MIT license](https://tldrlegal.com/license/mit-license).
172 |
173 | # Contact
174 | Copyright (c) 2016-2018 MoleculerJS
175 |
176 | [](https://github.com/moleculerjs) [](https://twitter.com/MoleculerJS)
177 |
--------------------------------------------------------------------------------
/packages/moleculer-console-tracer/test/unit/index.spec.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | const { ServiceBroker } = require("moleculer");
4 | const TracerService = require("../../src");
5 |
6 | describe("Test TracerService constructor", () => {
7 | const broker = new ServiceBroker({ logger: false });
8 | const service = broker.createService(TracerService);
9 |
10 | it("should be created", () => {
11 | expect(service).toBeDefined();
12 | expect(service.requests).toBeInstanceOf(Object);
13 | });
14 |
15 | });
16 |
17 | describe("Test event listener", () => {
18 | const broker = new ServiceBroker({ logger: false });
19 | const service = broker.createService(TracerService);
20 | service.printRequest = jest.fn();
21 |
22 | beforeAll(() => broker.start());
23 | afterAll(() => broker.stop());
24 |
25 | it("should save the payload", () => {
26 | const payload = { id: "1" };
27 | broker.emit("metrics.trace.span.start", payload);
28 |
29 | expect(service.requests["1"]).toBe(payload);
30 | });
31 |
32 | it("should save the payload to the parent", () => {
33 | const payload = { id: "2", parent: "1" };
34 | broker.emit("metrics.trace.span.start", payload);
35 |
36 | expect(service.requests["2"]).toBe(payload);
37 | expect(service.requests["1"].spans.length).toBe(1);
38 | expect(service.requests["1"].spans[0]).toBe("2");
39 | });
40 |
41 | it("should upload request", () => {
42 | service.printRequest.mockClear();
43 |
44 | const payload = { id: "2", parent: "1", duration: 50 };
45 | broker.emit("metrics.trace.span.finish", payload);
46 |
47 | expect(service.requests["2"]).toEqual({
48 | id: "2",
49 | parent: "1",
50 | duration: 50,
51 | spans: []
52 | });
53 |
54 | expect(service.printRequest).toHaveBeenCalledTimes(0);
55 | });
56 |
57 | it("should call printRequest", () => {
58 | service.printRequest.mockClear();
59 |
60 | const payload = { id: "1", duration: 20 };
61 | broker.emit("metrics.trace.span.finish", payload);
62 |
63 | expect(service.requests["1"].duration).toBe(20);
64 |
65 | expect(service.printRequest).toHaveBeenCalledTimes(1);
66 | expect(service.printRequest).toHaveBeenCalledWith("1");
67 | });
68 |
69 | });
70 |
71 | describe("Test printRequest method", () => {
72 | const broker = new ServiceBroker({ logger: false });
73 | const service = broker.createService(TracerService, {
74 | settings: {
75 | colors: false
76 | }
77 | });
78 |
79 | const output = [];
80 | service.logger.info = jest.fn((...args) => output.push(args.join(" ")));
81 |
82 | beforeAll(() => broker.start());
83 | afterAll(() => broker.stop());
84 |
85 | it("should print traces", () => {
86 |
87 | service.requests["1"] = {
88 | id: "1",
89 | duration: 25.40,
90 | action: { name: "posts.find" },
91 | startTime: 1000,
92 | endTime: 1025.4,
93 | level: 1,
94 |
95 | spans: ["2", "3"]
96 | };
97 |
98 | service.requests["2"] = {
99 | id: "2",
100 | duration: 11.80,
101 | action: { name: "posts.votes" },
102 | startTime: 1005,
103 | endTime: 1016.8,
104 | level: 2,
105 |
106 | spans: ["4"]
107 | };
108 |
109 | service.requests["3"] = {
110 | id: "3",
111 | duration: 2.50,
112 | action: { name: "posts.likes" },
113 | startTime: 1010,
114 | endTime: 1012.5,
115 | level: 2,
116 | fromCache: true,
117 |
118 | spans: []
119 | };
120 |
121 | service.requests["4"] = {
122 | id: "4",
123 | duration: 8.10,
124 | action: { name: "users.get" },
125 | startTime: 1008,
126 | endTime: 1016.1,
127 | level: 3,
128 | remoteCall: true,
129 |
130 | spans: []
131 | };
132 |
133 | service.printRequest("1");
134 |
135 | expect(output).toEqual([
136 | "┌──────────────────────────────────────────────────────────────────────────────┐",
137 | "│ ID: 1 Depth: 3 Total: 4 │",
138 | "├──────────────────────────────────────────────────────────────────────────────┤",
139 | "│ posts.find 25ms [■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■] │",
140 | "│ posts.votes 11ms [.......■■■■■■■■■■■■■■■■■■■..............] │",
141 | "│ users.get » 8ms [............■■■■■■■■■■■■■...............] │",
142 | "│ posts.likes * 2ms [...............■■■■.....................] │",
143 | "└──────────────────────────────────────────────────────────────────────────────┘"
144 | ]);
145 | });
146 |
147 | });
148 |
--------------------------------------------------------------------------------
/packages/moleculer-zipkin/README.md:
--------------------------------------------------------------------------------
1 | 
2 |
3 | # moleculer-zipkin [](https://www.npmjs.com/package/moleculer-zipkin)
4 |
5 | Moleculer metrics module for [Zipkin](https://zipkin.io/).
6 |
7 | 
8 |
9 | # Features
10 | - support `v1` & `v2` API.
11 | - send spans via HTTP.
12 | - batch or single sending.
13 |
14 | # Install
15 |
16 | ```bash
17 | $ npm install moleculer-zipkin
18 | ```
19 |
20 | # Usage
21 |
22 | ```js
23 | // services/metrics.zipkin.service.js
24 |
25 | const ZipkinService = require("moleculer-zipkin");
26 |
27 | module.exports = {
28 | mixins: [ZipkinService],
29 | settings: {
30 | baseURL: "http://192.168.0.181:9411",
31 | version: "v2",
32 | batchTime: 1000,
33 | payloadOptions: {
34 | debug: false,
35 | shared: false
36 | }
37 | }
38 | };
39 |
40 | // moleculer.config.js
41 | module.exports = {
42 | // ...
43 | metrics: true,
44 | // ...
45 | }
46 | ```
47 |
48 |
49 |
50 |
51 |
58 |
59 |
60 |
61 | # Settings
62 |
63 |
64 | | Property | Type | Default | Description |
65 | | -------- | ---- | ------- | ----------- |
66 | | `baseURL` | `String` | **required** | Base URL for Zipkin server. |
67 | | `version` | `String` | **required** | Zipkin REST API version. |
68 | | `batchTime` | `Number` | **required** | Batch send time interal. Disable: 0 |
69 | | `payloadOptions` | `Object` | **required** | Additional payload options. |
70 | | `payloadOptions.debug` | `Boolean` | **required** | Set `debug` property in v2 payload. |
71 | | `payloadOptions.shared` | `Boolean` | **required** | Set `shared` property in v2 payload. |
72 |
73 |
74 |
75 |
86 |
87 | # Actions
88 |
89 |
90 |
91 |
126 |
127 | # Methods
128 |
129 |
130 |
131 |
132 |
167 |
168 | # Test
169 | ```
170 | $ npm test
171 | ```
172 |
173 | In development with watching
174 |
175 | ```
176 | $ npm run ci
177 | ```
178 |
179 | # License
180 | The project is available under the [MIT license](https://tldrlegal.com/license/mit-license).
181 |
182 | # Contact
183 | Copyright (c) 2016-2018 MoleculerJS
184 |
185 | [](https://github.com/moleculerjs) [](https://twitter.com/MoleculerJS)
186 |
--------------------------------------------------------------------------------
/packages/moleculer-prometheus/README.md:
--------------------------------------------------------------------------------
1 | 
2 |
3 | # moleculer-prometheus [](https://www.npmjs.com/package/moleculer-prometheus)
4 |
5 | Moleculer metrics module for [Prometheus](https://prometheus.io/).
6 |
7 | 
8 |
9 | # Features
10 | - collect default Node.js metrics.
11 | - measure service calls.
12 | - support custom metrics.
13 | - Grafana [dashboard example](grafana-dashboards/).
14 |
15 | # Install
16 |
17 | ```bash
18 | $ npm install moleculer-prometheus
19 | ```
20 |
21 | # Usage
22 |
23 | ```js
24 | // services/metrics.prometheus.service.js
25 |
26 | const PromService = require("moleculer-prometheus");
27 |
28 | module.exports = {
29 | mixins: [PromService],
30 | settings: {
31 | port: 3030,
32 | collectDefaultMetrics: true,
33 | timeout: 5 * 1000,
34 | }
35 | };
36 |
37 | // moleculer.config.js
38 | module.exports = {
39 | // ...
40 | metrics: true,
41 | // ...
42 | }
43 | ```
44 |
45 | ### Add custom metric
46 |
47 | ```js
48 | // services/metrics.prometheus.js
49 | const PromService = require("moleculer-prometheus");
50 |
51 | module.exports = {
52 | mixins: [PromService],
53 | settings: {
54 | metrics: {
55 | "custom_value": { type: "Gauge", help: "Moleculer Prometheus custom metric" }
56 | }
57 | }
58 | };
59 | ```
60 |
61 | **Broadcast a `metrics.update` event to set the metric value**
62 | ```js
63 | broker.broadcast("metrics.update", {
64 | name: "custom_value",
65 | method: "set",
66 | value: Math.round(Math.random() * 100)
67 | });
68 | ```
69 |
70 |
71 |
72 |
73 |
74 |
75 |
82 |
83 |
84 |
85 | # Settings
86 |
87 | | Property | Type | Default | Description |
88 | | -------- | ---- | ------- | ----------- |
89 | | `port` | `Number` | `3030` | Exposed HTTP port. |
90 | | `collectDefaultMetrics` | `Boolean` | `true` | Enable to collect default metrics. |
91 | | `timeout` | `Number` | `10000` | Timeout option for 'collectDefaultMetrics' in milliseconds. |
92 | | `metrics` | `Object` | `{}` | Metric definitions. |
93 |
94 | # Default Moleculer metrics
95 |
96 | | Name | Type | Labels | Description |
97 | | ---- | ---- | ------ | ----------- |
98 | | `moleculer_nodes_total` | Gauge | - | Moleculer nodes count |
99 | | `moleculer_services_total` | Gauge | - | Moleculer services count |
100 | | `moleculer_actions_total` | Gauge | - | Moleculer actions count |
101 | | `moleculer_events_total` | Gauge | - | Moleculer event subscription count |
102 | | `moleculer_nodes` | Gauge | `nodeID`, `type`, `version`, `langVersion` | Moleculer node list |
103 | | `moleculer_action_endpoints_total` | Gauge | `action` | Moleculer action endpoints |
104 | | `moleculer_service_endpoints_total` | Gauge | `service`, `version` | Moleculer service endpoints |
105 | | `moleculer_event_endpoints_total` | Gauge | `event`, `group` | Moleculer event endpoints |
106 | | `moleculer_req_total` | Counter | `action`, `service`, `nodeID` | Moleculer request count |
107 | | `moleculer_req_errors_total` | Counter | `action`, `service`, `nodeID`, `errorCode`, `errorName`, `errorType` | Moleculer request error count |
108 | | `moleculer_req_duration_ms` | Histogram | `action`, `service`, `nodeID` | Moleculer request durations |
109 |
110 |
111 | # Methods
112 |
113 |
114 | ## `update`
115 |
116 | Update a metric value.
117 |
118 | ### Parameters
119 | | Property | Type | Default | Description |
120 | | -------- | ---- | ------- | ----------- |
121 | | `name` | `String` | **required** | |
122 | | `method` | `String` | **required** | |
123 | | `labels` | `Object` | - | |
124 | | `value` | `any` | **required** | |
125 | | `timestamp` | `any` | **required** | |
126 |
127 |
128 |
129 |
130 |
131 |
166 |
167 | # Test
168 | ```
169 | $ npm test
170 | ```
171 |
172 | In development with watching
173 |
174 | ```
175 | $ npm run ci
176 | ```
177 |
178 | # License
179 | The project is available under the [MIT license](https://tldrlegal.com/license/mit-license).
180 |
181 | # Contact
182 | Copyright (c) 2016-2018 MoleculerJS
183 |
184 | [](https://github.com/moleculerjs) [](https://twitter.com/MoleculerJS)
185 |
--------------------------------------------------------------------------------
/packages/moleculer-console-tracer/src/index.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable security/detect-unsafe-regex */
2 | /*
3 | * moleculer-console-tracer
4 | * Copyright (c) 2018 MoleculerJS (https://github.com/moleculerjs/moleculer-addons)
5 | * MIT Licensed
6 | */
7 |
8 | "use strict";
9 |
10 | const isNaN = require("lodash.isnan");
11 | const r = require("lodash.repeat");
12 | const chalk = require("chalk");
13 | const humanize = require("tiny-human-time").short;
14 | const slice = require("slice-ansi");
15 |
16 | /**
17 | * Simple tracer service to print metric traces to the console.
18 | *
19 | * @name moleculer-console-tracer
20 | * @module Service
21 | */
22 | module.exports = {
23 |
24 | name: "console-tracer",
25 |
26 | /**
27 | * Default settings
28 | */
29 | settings: {
30 | /** @type {Number} Table width. */
31 | width: 80,
32 |
33 | /** @type {Number} Gauge width. */
34 | gaugeWidth: 40,
35 |
36 | /** @type {Boolean} Enable colors. */
37 | colors: true
38 | },
39 |
40 | /**
41 | * Events
42 | */
43 | events: {
44 |
45 | "metrics.trace.span.start"(payload) {
46 | this.requests[payload.id] = payload;
47 | payload.spans = [];
48 |
49 | if (payload.parent) {
50 | let parent = this.requests[payload.parent];
51 | if (parent)
52 | parent.spans.push(payload.id);
53 | }
54 | },
55 |
56 | "metrics.trace.span.finish"(payload) {
57 | let item = this.requests[payload.id];
58 | Object.assign(item, payload);
59 |
60 | if (!payload.parent) {
61 | this.printRequest(payload.id);
62 |
63 | // TODO: remove old printed requests
64 | }
65 | }
66 | },
67 |
68 | /**
69 | * Methods
70 | */
71 | methods: {
72 |
73 | /**
74 | * Get span name from metric event. By default it returns the action name
75 | *
76 | * @param {Object} metric
77 | * @returns {String}
78 | */
79 | getSpanName(metric) {
80 | if (metric.name)
81 | return metric.name;
82 |
83 | if (metric.action)
84 | return metric.action.name;
85 | },
86 |
87 | drawTableTop() {
88 | return chalk.grey("┌" + r("─", this.settings.width - 2) + "┐");
89 | },
90 |
91 | drawHorizonalLine() {
92 | return chalk.grey("├" + r("─", this.settings.width - 2) + "┤");
93 | },
94 |
95 | drawLine(text) {
96 | return chalk.grey("│ ") + text + chalk.grey(" │");
97 | },
98 |
99 | drawTableBottom() {
100 | return chalk.grey("└" + r("─", this.settings.width - 2) + "┘");
101 | },
102 |
103 | drawAlignedTexts(leftStr, rightStr, width) {
104 | const ll = leftStr.replace(/[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]/g, "").length;
105 | const rl = rightStr.replace(/[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]/g, "").length;
106 |
107 | const space = width - rl;
108 |
109 | let left;
110 | if (ll <= space)
111 | left = leftStr + r(" ", space - ll);
112 | else {
113 | left = slice(leftStr, 0, Math.max(space - 3, 0));
114 | left += r(".", Math.min(3, space));
115 | }
116 |
117 | return left + rightStr;
118 | },
119 |
120 | drawGauge(gstart, gstop) {
121 | const gw = this.settings.gaugeWidth;
122 | const p1 = Math.floor(gw * gstart / 100);
123 | const p2 = Math.max(Math.floor(gw * gstop / 100) - p1, 1);
124 | const p3 = Math.max(gw - (p1 + p2), 0);
125 |
126 | return [
127 | chalk.grey("["),
128 | chalk.grey(r(".", p1)),
129 | r("■", p2),
130 | chalk.grey(r(".", p3)),
131 | chalk.grey("]")
132 | ].join("");
133 | },
134 |
135 | getCaption(span) {
136 | let caption = this.getSpanName(span);
137 |
138 | if (span.fromCache)
139 | caption += " *";
140 | if (span.remoteCall)
141 | caption += " »";
142 | if (span.error)
143 | caption += " ×";
144 |
145 | return caption;
146 | },
147 |
148 | getColor(span) {
149 | let c = chalk.bold;
150 | if (span.fromCache)
151 | c = chalk.yellow;
152 | if (span.remoteCall)
153 | c = chalk.cyan;
154 | if (span.duration == null)
155 | c = chalk.grey;
156 | if (span.error)
157 | c = chalk.red.bold;
158 |
159 | return c;
160 | },
161 |
162 | getTraceInfo(main) {
163 | let depth = 0;
164 | let total = 0;
165 | let check = (span, level) => {
166 | total++;
167 | if (level > depth)
168 | depth = level;
169 |
170 | if (span.spans.length > 0)
171 | span.spans.forEach(spanID => check(this.requests[spanID], level + 1));
172 | };
173 |
174 | check(main, 1);
175 |
176 | return { depth, total };
177 | },
178 |
179 | /**
180 | * Print a span row
181 | *
182 | * @param {Object} span
183 | * @param {Object} main
184 | */
185 | printSpanTime(span, main, level) {
186 | const margin = 2 * 2;
187 | const w = (this.settings.width || 80) - margin;
188 | const gw = this.settings.gaugeWidth || 40;
189 |
190 | const time = span.duration == null ? "?" : humanize(span.duration);
191 | const caption = r(" ", level - 1) + this.getCaption(span);
192 | const info = this.drawAlignedTexts(caption, " " + time, w - gw - 3);
193 |
194 | const startTime = span.startTime || main.startTime;
195 | const endTime = span.endTime || main.endTime;
196 |
197 | let gstart = (startTime - main.startTime) / (main.endTime - main.startTime) * 100;
198 | let gstop = (endTime - main.startTime) / (main.endTime - main.startTime) * 100;
199 |
200 | if (isNaN(gstart) && isNaN(gstop)) {
201 | gstart = 0;
202 | gstop = 100;
203 | }
204 | if (gstop > 100)
205 | gstop = 100;
206 |
207 | const c = this.getColor(span);
208 | this.logger.info(this.drawLine(c(info + " " + this.drawGauge(gstart, gstop))));
209 |
210 | if (span.spans.length > 0)
211 | span.spans.forEach(spanID => this.printSpanTime(this.requests[spanID], main, level + 1));
212 | },
213 |
214 | /**
215 | * Print request traces
216 | *
217 | * @param {String} id
218 | */
219 | printRequest(id) {
220 | const main = this.requests[id];
221 | const margin = 2 * 2;
222 | const w = (this.settings.width || 80) - margin;
223 |
224 | this.logger.info(this.drawTableTop());
225 |
226 | const { total, depth } = this.getTraceInfo(main);
227 |
228 | const headerLeft = chalk.grey("ID: ") + chalk.bold(id);
229 | const headerRight = chalk.grey("Depth: ") + chalk.bold(depth) + " " + chalk.grey("Total: ") + chalk.bold(total);
230 | const line = this.drawAlignedTexts(headerLeft, " " + headerRight, w);
231 | this.logger.info(this.drawLine(line));
232 |
233 | this.logger.info(this.drawHorizonalLine());
234 |
235 | this.printSpanTime(main, main, 1);
236 |
237 | this.logger.info(this.drawTableBottom());
238 | },
239 | },
240 |
241 | /**
242 | * Service created lifecycle event handler
243 | */
244 | created() {
245 | this.requests = {};
246 |
247 | chalk.enabled = this.settings.colors;
248 | }
249 | };
250 |
--------------------------------------------------------------------------------
/packages/moleculer-jaeger/README.md:
--------------------------------------------------------------------------------
1 | 
2 |
3 | # moleculer-jaeger [](https://www.npmjs.com/package/moleculer-jaeger)
4 |
5 | Moleculer metrics module for [Jaeger](https://github.com/jaegertracing/jaeger).
6 |
7 | 
8 |
9 | # Features
10 | - 5 types sampler
11 | - UDP sender
12 |
13 | # Install
14 |
15 | ```bash
16 | $ npm install moleculer-jaeger
17 | ```
18 |
19 | # Usage
20 |
21 | ```js
22 | // moleculer.config.js
23 | module.exports = {
24 | // ...
25 | metrics: true,
26 | // ...
27 | }
28 |
29 | // services/metrics.jaeger.service.js
30 | const JaegerService = require("moleculer-jaeger");
31 |
32 | module.exports = {
33 | mixins: [JaegerService],
34 | settings: {
35 | host: "jaeger-server",
36 | port: 6832
37 | }
38 | };
39 | ```
40 |
41 | ## Sampler configurations
42 | More info: [http://jaeger.readthedocs.io/en/latest/client_libraries/#sampling](http://jaeger.readthedocs.io/en/latest/client_libraries/#sampling)
43 |
44 | **Setup ConstSampler (default):**
45 | ```js
46 | module.exports = {
47 | mixins: [JaegerService],
48 | settings: {
49 | host: "jaeger-server",
50 | port: 6832,
51 |
52 | sampler: {
53 | type: "Const",
54 | options: {
55 | decision: 1
56 | }
57 | }
58 | }
59 | });
60 | ```
61 |
62 | **Setup RateLimitingSampler:**
63 | ```js
64 | module.exports = {
65 | mixins: [JaegerService],
66 | settings: {
67 | host: "jaeger-server",
68 | port: 6832,
69 |
70 | sampler: {
71 | type: "RateLimiting",
72 | options: {
73 | maxTracesPerSecond: 2,
74 | initBalance: 5
75 | }
76 | }
77 | }
78 | });
79 | ```
80 |
81 | **Setup ProbabilisticSampler:**
82 | ```js
83 | module.exports = {
84 | mixins: [JaegerService],
85 | settings: {
86 | host: "jaeger-server",
87 | port: 6832,
88 |
89 | sampler: {
90 | type: "Probabilistic",
91 | options: {
92 | samplingRate: 0.1 // 10%
93 | }
94 | }
95 | }
96 | });
97 | ```
98 |
99 | **Setup GuaranteedThroughputSampler:**
100 | >GuaranteedThroughputProbabilisticSampler is a sampler that leverages both probabilisticSampler and rateLimitingSampler. The rateLimitingSampler is used as a guaranteed lower bound sampler such that every operation is sampled at least once in a time interval defined by the lowerBound. ie a lowerBound of `1.0 / (60 * 10)` will sample an operation at least once every 10 minutes.
101 |
102 | ```js
103 | module.exports = {
104 | mixins: [JaegerService],
105 | settings: {
106 | host: "jaeger-server",
107 | port: 6832,
108 |
109 | sampler: {
110 | type: "GuaranteedThroughput",
111 | options: {
112 | lowerBound: 0.1,
113 | samplingRate: 0.1
114 | }
115 | }
116 | }
117 | });
118 | ```
119 |
120 | **Setup RemoteControlledSampler:**
121 | ```js
122 | module.exports = {
123 | mixins: [JaegerService],
124 | settings: {
125 | host: "jaeger-server",
126 | port: 6832,
127 |
128 | sampler: {
129 | type: "RemoteControlled",
130 | options: {
131 | //...
132 | }
133 | }
134 | }
135 | });
136 | ```
137 |
138 |
139 |
140 |
141 |
148 |
149 |
150 |
151 | # Settings
152 |
153 |
154 | | Property | Type | Default | Description |
155 | | -------- | ---- | ------- | ----------- |
156 | | `host` | `String` | **required** | UDP Sender host option. |
157 | | `port` | `Number` | `null` | UDP Sender port option. |
158 | | `sampler` | `Object` | `null` | Sampler configuration. |
159 | | `sampler.type` | `String` | `null` | Sampler type |
160 | | `sampler.options` | any | **required** | |
161 | | `options` | `Object` | `null` | Additional options for `Jaeger.Tracer` |
162 |
163 |
164 |
165 |
176 |
177 | # Actions
178 |
179 |
180 |
181 |
216 |
217 | # Methods
218 |
219 |
220 |
221 |
222 |
257 |
258 | # Test
259 | ```
260 | $ npm test
261 | ```
262 |
263 | In development with watching
264 |
265 | ```
266 | $ npm run ci
267 | ```
268 |
269 | # License
270 | The project is available under the [MIT license](https://tldrlegal.com/license/mit-license).
271 |
272 | # Contact
273 | Copyright (c) 2016-2018 MoleculerJS
274 |
275 | [](https://github.com/moleculerjs) [](https://twitter.com/MoleculerJS)
276 |
--------------------------------------------------------------------------------
/packages/moleculer-jaeger/src/index.js:
--------------------------------------------------------------------------------
1 | /*
2 | * moleculer-jaeger
3 | * Copyright (c) 2018 MoleculerJS (https://github.com/moleculerjs/moleculer-addons)
4 | * MIT Licensed
5 | */
6 |
7 | "use strict";
8 |
9 | const isFunction = require("lodash.isfunction");
10 | const Jaeger = require("jaeger-client");
11 | const GuaranteedThroughputSampler = require("jaeger-client/dist/src/samplers/guaranteed_throughput_sampler").default;
12 | const RemoteControlledSampler = require("jaeger-client/dist/src/samplers/remote_sampler").default;
13 | const UDPSender = require("jaeger-client/dist/src/reporters/udp_sender").default;
14 |
15 | const Int64 = require("node-int64");
16 |
17 | /**
18 | * Moleculer metrics module for Jaeger.
19 | *
20 | * http://jaeger.readthedocs.io/en/latest/getting_started/#all-in-one-docker-image
21 | *
22 | * Running Jaeger in Docker:
23 | *
24 | * docker run -d --name jaeger -p5775:5775/udp -p6831:6831/udp -p6832:6832/udp -p5778:5778 -p16686:16686 -p14268:14268 jaegertracing/all-in-one:latest
25 | *
26 | * UI: http://:16686/
27 | *
28 | * @name moleculer-jaeger
29 | * @module Service
30 | */
31 | module.exports = {
32 | name: "jaeger",
33 |
34 | /**
35 | * Default settings
36 | */
37 | settings: {
38 | /** @type {String} UDP Sender host option. */
39 | host: "127.0.0.1",
40 | /** @type {Number?} UDP Sender port option. */
41 | port: 6832,
42 |
43 | /** @type {Object?} Sampler configuration. */
44 | sampler: {
45 | /** @type {String?} Sampler type */
46 | type: "Const",
47 |
48 | /** @type: {Object?} Sampler specific options. */
49 | options: {
50 | }
51 | },
52 |
53 | /** @type {Object?} Additional options for `Jaeger.Tracer` */
54 | options: {}
55 | },
56 |
57 | /**
58 | * Events
59 | */
60 | events: {
61 | "metrics.trace.span.finish"(metric) {
62 | this.makePayload(metric);
63 | }
64 | },
65 |
66 | /**
67 | * Methods
68 | */
69 | methods: {
70 | /**
71 | * Get service name from metric event
72 | *
73 | * @param {Object} metric
74 | * @returns {String}
75 | */
76 | getServiceName(metric) {
77 | if (metric.service)
78 | return metric.service.name ? metric.service.name : metric.service;
79 |
80 | let parts = metric.action.name.split(".");
81 | parts.pop();
82 | return parts.join(".");
83 | },
84 |
85 | /**
86 | * Get span name from metric event. By default it returns the action name
87 | *
88 | * @param {Object} metric
89 | * @returns {String}
90 | */
91 | getSpanName(metric) {
92 | if (metric.name)
93 | return metric.name;
94 |
95 | if (metric.action)
96 | return metric.action.name;
97 | },
98 |
99 | /**
100 | * Create Jaeger payload from metric event
101 | *
102 | * @param {Object} metric
103 | */
104 | makePayload(metric) {
105 | const serviceName = this.getServiceName(metric);
106 | const tracer = this.getTracer(serviceName);
107 |
108 | let parentCtx;
109 | if (metric.parent) {
110 | parentCtx = new Jaeger.SpanContext(
111 | this.convertID(metric.requestID), // traceId,
112 | this.convertID(metric.parent), // spanId,
113 | null, // parentId,
114 | null, // traceIdStr
115 | null, // spanIdStr
116 | null, // parentIdStr
117 | 1, // flags
118 | {}, // baggage
119 | "" // debugId
120 | );
121 | }
122 |
123 | const span = tracer.startSpan(this.getSpanName(metric), {
124 | startTime: metric.startTime,
125 | childOf: parentCtx,
126 | tags: {
127 | nodeID: metric.nodeID,
128 | level: metric.level,
129 | remoteCall: metric.remoteCall
130 | }
131 | });
132 | this.addTags(span, "service", serviceName);
133 | if (metric.action && metric.action.name)
134 | this.addTags(span, "action", metric.action.name);
135 |
136 | this.addTags(span, Jaeger.opentracing.Tags.SPAN_KIND, Jaeger.opentracing.Tags.SPAN_KIND_RPC_SERVER);
137 |
138 | const sc = span.context();
139 | sc.traceId = this.convertID(metric.requestID);
140 | sc.spanId = this.convertID(metric.id);
141 |
142 | if (metric.callerNodeID)
143 | this.addTags(span, "callerNodeID", metric.callerNodeID);
144 |
145 | if (metric.params)
146 | this.addTags(span, "params", metric.params);
147 |
148 | if (metric.meta)
149 | this.addTags(span, "meta", metric.meta);
150 |
151 | if (metric.error) {
152 | this.addTags(span, Jaeger.opentracing.Tags.ERROR, true);
153 | this.addTags(span, "error.message", metric.error.message);
154 | this.addTags(span, "error.type", metric.error.type);
155 | this.addTags(span, "error.code", metric.error.code);
156 |
157 | if (metric.error.data)
158 | this.addTags(span, "error.data", metric.error.data);
159 |
160 | if (metric.error.stack)
161 | this.addTags(span, "error.stack", metric.error.stack.toString());
162 | }
163 |
164 | span.finish(metric.endTime);
165 | },
166 |
167 | /**
168 | * Add tags to span
169 | *
170 | * @param {Object} span
171 | * @param {String} key
172 | * @param {any} value
173 | * @param {String?} prefix
174 | */
175 | addTags(span, key, value, prefix) {
176 | const name = prefix ? `${prefix}.${key}` : key;
177 | if (value && typeof value == "object") {
178 | Object.keys(value).forEach(k => this.addTags(span, k, value[k], name));
179 | } else {
180 | span.setTag(name, value);
181 | }
182 | },
183 |
184 | /**
185 | * Convert Context ID to Zipkin format
186 | *
187 | * @param {String} id
188 | * @returns {String}
189 | */
190 | convertID(id) {
191 | if (id) {
192 | return new Int64(id.replace(/-/g, "").substring(0, 16)).toBuffer();
193 | }
194 | return null;
195 | },
196 |
197 | /**
198 | * Get sampler instance for Tracer
199 | *
200 | */
201 | getSampler(serviceName) {
202 | if (isFunction(this.settings.sampler))
203 | return this.settings.sampler;
204 |
205 | if (this.settings.sampler.type == "RateLimiting")
206 | return new Jaeger.RateLimitingSampler(this.settings.sampler.options.maxTracesPerSecond, this.settings.sampler.options.initBalance);
207 |
208 | if (this.settings.sampler.type == "Probabilistic")
209 | return new Jaeger.ProbabilisticSampler(this.settings.sampler.options.samplingRate);
210 |
211 | if (this.settings.sampler.type == "GuaranteedThroughput")
212 | return new GuaranteedThroughputSampler(this.settings.sampler.options.lowerBound, this.settings.sampler.options.samplingRate);
213 |
214 | if (this.settings.sampler.type == "RemoteControlled")
215 | return new RemoteControlledSampler(serviceName, this.settings.sampler.options);
216 |
217 | return new Jaeger.ConstSampler(this.settings.sampler.options && this.settings.sampler.options.decision != null ? this.settings.sampler.options.decision : 1);
218 | },
219 |
220 | /**
221 | * Get reporter instance for Tracer
222 | *
223 | */
224 | getReporter() {
225 | return new Jaeger.RemoteReporter(new UDPSender({ host: this.settings.host, port: this.settings.port }));
226 | },
227 |
228 | /**
229 | * Get a tracer instance by service name
230 | *
231 | * @param {any} serviceName
232 | * @returns {Jaeger.Tracer}
233 | */
234 | getTracer(serviceName) {
235 | if (this.tracers[serviceName])
236 | return this.tracers[serviceName];
237 |
238 | const sampler = this.getSampler();
239 | const reporter = this.getReporter();
240 |
241 | const tracer = new Jaeger.Tracer(serviceName, reporter, sampler, this.settings.options);
242 | this.tracers[serviceName] = tracer;
243 |
244 | return tracer;
245 | }
246 | },
247 |
248 | /**
249 | * Service created lifecycle event handler
250 | */
251 | created() {
252 | this.tracers = {};
253 | },
254 |
255 | /**
256 | * Service started lifecycle event handler
257 | */
258 | started() {
259 |
260 | },
261 |
262 | /**
263 | * Service stopped lifecycle event handler
264 | */
265 | stopped() {
266 | Object.keys(this.tracers).forEach(service => {
267 | this.tracers[service].close();
268 | });
269 | this.tracers = {};
270 | }
271 | };
272 |
--------------------------------------------------------------------------------
/packages/moleculer-prometheus/src/index.js:
--------------------------------------------------------------------------------
1 | /*
2 | * moleculer-prometheus
3 | * Copyright (c) 2018 MoleculerJS (https://github.com/moleculerjs/moleculer-addons)
4 | * MIT Licensed
5 | */
6 |
7 | "use strict";
8 |
9 | const polka = require("polka");
10 |
11 | /**
12 | * Moleculer metrics module for Prometheus.
13 | *
14 | * https://prometheus.io/
15 | *
16 | * Running Prometheus & Grafana in Docker:
17 | *
18 | * git clone https://github.com/vegasbrianc/prometheus.git
19 | * cd prometheus
20 | *
21 | * Please note, don't forget add your endpoint to static targets in prometheus/prometheus.yml file
22 | *
23 | * static_configs:
24 | * - targets: ['localhost:9090', 'moleculer-node-123:3030']
25 | *
26 | * Start containers:
27 | *
28 | * docker-compose up -d
29 | *
30 | * Grafana dashboard: http://:3000
31 | *
32 | * @name moleculer-prometheus
33 | * @module Service
34 | */
35 | module.exports = {
36 | name: "prometheus",
37 |
38 | /**
39 | * Default settings
40 | */
41 | settings: {
42 | /** @type {Number} Exposed HTTP port. */
43 | port: 3030,
44 |
45 | /** @type {Boolean} Enable to collect default metrics. */
46 | collectDefaultMetrics: true,
47 |
48 | /** @type {Number} Timeout option for 'collectDefaultMetrics'. */
49 | timeout: 10 * 1000,
50 |
51 | /** @type {Object} Metric definitions. */
52 | metrics: {
53 | // Common metrics
54 | "moleculer_nodes_total": { type: "Gauge", help: "Moleculer nodes count" },
55 | "moleculer_services_total": { type: "Gauge", help: "Moleculer services count" },
56 | "moleculer_actions_total": { type: "Gauge", help: "Moleculer actions count" },
57 | "moleculer_events_total": { type: "Gauge", help: "Moleculer event subscriptions" },
58 |
59 | // Nodes
60 | "moleculer_nodes": { type: "Gauge", labelNames: [ "nodeID", "type", "version", "langVersion" ], help: "Moleculer node list" },
61 |
62 | // Actions
63 | "moleculer_action_endpoints_total": { type: "Gauge", labelNames: [ "action" ], help: "Moleculer action endpoints" },
64 |
65 | // Services
66 | "moleculer_service_endpoints_total": { type: "Gauge", labelNames: [ "service", "version" ], help: "Moleculer service endpoints" },
67 |
68 | // Events
69 | "moleculer_event_endpoints_total": { type: "Gauge", labelNames: [ "event", "group" ], help: "Moleculer event endpoints" },
70 |
71 | // Service requests
72 | "moleculer_req_total": { type: "Counter", labelNames: [ "action", "service", "nodeID" ], help: "Moleculer action request count"},
73 | "moleculer_req_errors_total": { type: "Counter", labelNames: [ "action", "service", "nodeID", "errorCode", "errorName", "errorType" ], help: "Moleculer request error count"},
74 | "moleculer_req_duration_ms": { type: "Histogram", labelNames: [ "action", "service", "nodeID" ], help: "Moleculer request durations"},
75 | }
76 | },
77 |
78 | /**
79 | * Events
80 | */
81 | events: {
82 | "metrics.trace.span.finish"(payload) {
83 | let serviceName = this.getServiceName(payload);
84 | let spanName = this.getSpanName(payload);
85 |
86 | this.update("moleculer_req_total", "inc", { action: spanName, service: serviceName, nodeID: payload.nodeID });
87 | this.update("moleculer_req_duration_ms", "observe", { action: spanName, service: serviceName, nodeID: payload.nodeID }, payload.duration);
88 |
89 | if (payload.error) {
90 | this.update("moleculer_req_errors_total", "inc", {
91 | action: spanName,
92 | service: serviceName,
93 | nodeID: payload.nodeID,
94 | errorCode: payload.error.code,
95 | errorName: payload.error.name,
96 | errorType: payload.error.type ? payload.error.type : ""
97 | });
98 | }
99 | },
100 |
101 | "metrics.update"(payload) {
102 | this.update(payload.name, payload.method, payload.labels, payload.value, payload.timestamp);
103 | },
104 |
105 | "$services.changed"() {
106 | this.updateCommonValues();
107 | },
108 |
109 | "$node.connected"() {
110 | this.updateCommonValues();
111 | },
112 |
113 | "$node.disconnected"() {
114 | this.updateCommonValues();
115 | }
116 | },
117 |
118 | /**
119 | * Methods
120 | */
121 | methods: {
122 | /**
123 | * Get service name from metric event
124 | *
125 | * @param {Object} metric
126 | * @returns {String}
127 | */
128 | getServiceName(metric) {
129 | if (metric.service)
130 | return metric.service.name ? metric.service.name : metric.service;
131 |
132 | let parts = metric.action.name.split(".");
133 | parts.pop();
134 | return parts.join(".");
135 | },
136 |
137 | /**
138 | * Get span name from metric event. By default it returns the action name
139 | *
140 | * @param {Object} metric
141 | * @returns {String}
142 | */
143 | getSpanName(metric) {
144 | if (metric.name)
145 | return metric.name;
146 |
147 | if (metric.action)
148 | return metric.action.name;
149 | },
150 |
151 | /**
152 | * Create Prometheus metrics.
153 | *
154 | * @param {Object} metricsDefs
155 | */
156 | createMetrics(metricsDefs) {
157 | this.metrics = {};
158 | if (!metricsDefs) return;
159 |
160 | Object.keys(metricsDefs).forEach(name => {
161 | const def = metricsDefs[name];
162 |
163 | if (def)
164 | this.metrics[name] = new this.client[def.type](Object.assign({
165 | name,
166 | registers: this.register? [this.register]:[]
167 | }, def));
168 | });
169 | },
170 |
171 | /**
172 | * Update common Moleculer metric values.
173 | */
174 | updateCommonValues() {
175 | if (!this.metrics) return;
176 |
177 | return this.broker.mcall({
178 | nodes: { action: "$node.list" },
179 | services: { action: "$node.services", params: { withActions: false, grouping: true, skipInternal: true } },
180 | actions: { action: "$node.actions", params: { withEndpoints: true, skipInternal: true } },
181 | events: { action: "$node.events", params: { withEndpoints: true, skipInternal: true } }
182 | }).then(({ nodes, services, actions, events}) => {
183 |
184 | this.update("moleculer_nodes_total", "set", null, nodes.filter(node => node.available).length);
185 | nodes.forEach(node => this.update("moleculer_nodes", "set", { nodeID: node.id, type: node.client.type, version: node.client.version, langVersion: node.client.langVersion }, node.available ? 1 : 0));
186 |
187 | this.update("moleculer_services_total", "set", null, services.length);
188 | services.forEach(svc => this.update("moleculer_service_endpoints_total", "set", { service: svc.name, version: svc.version }, svc.nodes.length));
189 |
190 | this.update("moleculer_actions_total", "set", null, actions.length);
191 | actions.forEach(action => this.update("moleculer_action_endpoints_total", "set", { action: action.name }, action.endpoints ? action.endpoints.length : 0));
192 |
193 | this.update("moleculer_events_total", "set", null, events.length);
194 | events.forEach(event => this.update("moleculer_event_endpoints_total", "set", { event: event.name, group: event.group }, event.endpoints ? event.endpoints.length : 0));
195 | });
196 | },
197 |
198 | /**
199 | * Update a metric value.
200 | *
201 | * @methods
202 | * @param {String} name
203 | * @param {String} method
204 | * @param {Object?} labels
205 | * @param {any} value
206 | * @param {any} timestamp
207 | */
208 | update(name, method, labels, value, timestamp) {
209 | if (this.metrics[name]) {
210 | if (labels)
211 | this.metrics[name][method](labels, value, timestamp);
212 | else
213 | this.metrics[name][method](value, timestamp);
214 | }
215 | }
216 | },
217 |
218 | /**
219 | * Service created lifecycle event handler
220 | */
221 | created() {
222 | this.server = polka();
223 | },
224 |
225 | /**
226 | * Service started lifecycle event handler
227 | */
228 | started() {
229 | this.client = require("prom-client");
230 | this.register = new this.client.Registry();
231 |
232 | if (this.settings.collectDefaultMetrics) {
233 | this.timer = this.client.collectDefaultMetrics({
234 | timeout: this.settings.timeout,
235 | register: this.register
236 | });
237 | }
238 |
239 | this.createMetrics(this.settings.metrics);
240 |
241 | this.server.get("/metrics", (req, res) => {
242 | res.setHeader("Content-Type", this.client.contentType);
243 | res.end(this.register.metrics());
244 | });
245 |
246 | return this.server.listen(this.settings.port).then(() => {
247 | this.logger.info(`Prometheus collector is listening on port ${this.settings.port}, metrics exposed on /metrics endpoint`);
248 |
249 | this.updateCommonValues();
250 | });
251 | },
252 |
253 | /**
254 | * Service stopped lifecycle event handler
255 | */
256 | stopped() {
257 | if (this.timer) {
258 | clearInterval(this.timer);
259 | this.timer = null;
260 | }
261 |
262 | this.register = null;
263 |
264 | if (this.server)
265 | this.server.server.close();
266 | }
267 | };
268 |
--------------------------------------------------------------------------------
/packages/moleculer-zipkin/src/index.js:
--------------------------------------------------------------------------------
1 | /*
2 | * moleculer-zipkin
3 | * Copyright (c) 2018 MoleculerJS (https://github.com/moleculerjs/moleculer-addons)
4 | * MIT Licensed
5 | */
6 |
7 | "use strict";
8 |
9 | let axios = require("axios");
10 |
11 | /**
12 | * Zipkin tracing addons.
13 | *
14 | * API v2: https://zipkin.io/zipkin-api/#/
15 | * API v1: https://zipkin.io/pages/data_model.html
16 | *
17 | * Running Zipkin in Docker:
18 | *
19 | * docker run -d -p 9411:9411 --name=zipkin openzipkin/zipkin
20 | *
21 | * @name moleculer-zipkin
22 | * @module Service
23 | */
24 | module.exports = {
25 | name: "zipkin",
26 |
27 | /**
28 | * Default settings
29 | */
30 | settings: {
31 | /** @type {String} Base URL for Zipkin server. */
32 | baseURL: "http://localhost:9411",
33 |
34 | /** @type {String} Zipkin REST API version. */
35 | version: "v2",
36 |
37 | /** @type {Number} Batch send time interal. Disable: 0 */
38 | batchTime: 1000,
39 |
40 | /** @type {Object} Additional payload options. */
41 | payloadOptions: {
42 |
43 | /** @type {Boolean} Set `debug` property in v2 payload. */
44 | debug: false,
45 |
46 | /** @type {Boolean} Set `shared` property in v2 payload. */
47 | shared: false
48 | }
49 | },
50 |
51 | /**
52 | * Events
53 | */
54 | events: {
55 | "metrics.trace.span.finish"(metric) {
56 | if (this.settings.version == "v2") {
57 | this.makeZipkinPayloadV2(metric);
58 | } else {
59 | this.makeZipkinPayloadV1(metric);
60 | }
61 | }
62 | },
63 |
64 | /**
65 | * Methods
66 | */
67 | methods: {
68 | /**
69 | * Get service name from metric event
70 | *
71 | * @param {Object} metric
72 | * @returns {String}
73 | */
74 | getServiceName(metric) {
75 | if (metric.service)
76 | return metric.service.name ? metric.service.name : metric.service;
77 |
78 | let parts = metric.action.name.split(".");
79 | parts.pop();
80 | return parts.join(".");
81 | },
82 |
83 | /**
84 | * Get span name from metric event. By default it returns the action name
85 | *
86 | * @param {Object} metric
87 | * @returns {String}
88 | */
89 | getSpanName(metric) {
90 | if (metric.name)
91 | return metric.name;
92 |
93 | if (metric.action)
94 | return metric.action.name;
95 | },
96 |
97 | /**
98 | * Create Zipkin v1 payload from metric event
99 | *
100 | * @param {Object} metric
101 | */
102 | makeZipkinPayloadV1(metric) {
103 | const serviceName = this.getServiceName(metric);
104 |
105 | const payload = {
106 | name: this.getSpanName(metric),
107 |
108 | // Trace & span IDs
109 | traceId: this.convertID(metric.requestID),
110 | id: this.convertID(metric.id),
111 | parentId: this.convertID(metric.parent),
112 |
113 | // Annotations
114 | annotations: [
115 | {
116 | endpoint: { serviceName: serviceName, ipv4: "", port: 0 },
117 | timestamp: this.convertTime(metric.startTime),
118 | value: "sr"
119 | },
120 | {
121 | endpoint: { serviceName: serviceName, ipv4: "", port: 0 },
122 | timestamp: this.convertTime(metric.endTime),
123 | value: "ss"
124 | }
125 | ],
126 |
127 | // Binary annotations
128 | binaryAnnotations: [
129 | { key: "nodeID", value: metric.nodeID },
130 | { key: "level", value: metric.level.toString() },
131 | { key: "remoteCall", value: metric.remoteCall.toString() },
132 | { key: "callerNodeID", value: metric.callerNodeID ? metric.callerNodeID : "" }
133 | ],
134 |
135 | timestamp: this.convertTime(metric.endTime)
136 | };
137 |
138 | if (metric.params)
139 | this.addBinaryAnnotation(payload, "params", metric.params);
140 |
141 | if (metric.meta)
142 | this.addBinaryAnnotation(payload, "meta", metric.meta);
143 |
144 | if (metric.error) {
145 | this.addBinaryAnnotation(payload, "error", metric.error.message);
146 | this.addBinaryAnnotation(payload, "error.type", metric.error.type);
147 | this.addBinaryAnnotation(payload, "error.code", metric.error.code);
148 |
149 | if (metric.error.data)
150 | this.addBinaryAnnotation(payload, "error.data", metric.error.data);
151 |
152 | if (metric.error.stack)
153 | this.addBinaryAnnotation(payload, "error.stack", metric.error.stack.toString());
154 |
155 | payload.annotations.push({
156 | value: "error",
157 | endpoint: { serviceName: serviceName, ipv4: "", port: 0 },
158 | timestamp: this.convertTime(metric.endTime)
159 | });
160 | }
161 |
162 | this.enqueue(payload);
163 | },
164 |
165 | /**
166 | * Create Zipkin v2 payload from metric event
167 | *
168 | * @param {Object} metric
169 | */
170 | makeZipkinPayloadV2(metric) {
171 | const serviceName = this.getServiceName(metric);
172 |
173 | const payload = {
174 | name: this.getSpanName(metric),
175 | kind: "CONSUMER",
176 |
177 | // Trace & span IDs
178 | traceId: this.convertID(metric.requestID),
179 | id: this.convertID(metric.id),
180 | parentId: this.convertID(metric.parent),
181 |
182 | localEndpoint: {
183 | serviceName: serviceName,
184 | },
185 |
186 | remoteEndpoint: {
187 | serviceName: serviceName,
188 | },
189 |
190 | annotations: [
191 | { timestamp: this.convertTime(metric.startTime), value: "sr" },
192 | { timestamp: this.convertTime(metric.endTime), value: "ss" },
193 | ],
194 |
195 | // Tags
196 | tags: {
197 | nodeID: metric.nodeID,
198 | level: metric.level.toString(),
199 | remoteCall: metric.remoteCall.toString(),
200 | callerNodeID: metric.callerNodeID ? metric.callerNodeID : ""
201 | },
202 |
203 | timestamp: this.convertTime(metric.startTime),
204 | duration: Math.round(metric.duration * 1000),
205 |
206 | debug: this.settings.payloadOptions.debug,
207 | shared: this.settings.payloadOptions.shared
208 | };
209 |
210 | if (metric.params)
211 | this.addTags(payload, "params", metric.params);
212 |
213 | if (metric.meta)
214 | this.addTags(payload, "meta", metric.meta);
215 |
216 | if (metric.error) {
217 | this.addTags(payload, "error", metric.error.message);
218 | this.addTags(payload, "error.type", metric.error.type);
219 | this.addTags(payload, "error.code", metric.error.code);
220 |
221 | if (metric.error.data)
222 | this.addTags(payload, "error.data", metric.error.data);
223 |
224 | if (metric.error.stack)
225 | this.addTags(payload, "error.stack", metric.error.stack.toString());
226 |
227 |
228 | payload.annotations.push({
229 | value: "error",
230 | endpoint: { serviceName: serviceName, ipv4: "", port: 0 },
231 | timestamp: this.convertTime(metric.endTime)
232 | });
233 | }
234 |
235 | this.enqueue(payload);
236 | },
237 |
238 | /**
239 | * Enqueue the span payload
240 | *
241 | * @param {Object} payload
242 | */
243 | enqueue(payload) {
244 | if (this.settings.batchTime > 0) {
245 | this.queue.push(payload);
246 | } else {
247 | this.send([payload]);
248 | }
249 | },
250 |
251 | /**
252 | * Send all spans from the queue.
253 | *
254 | */
255 | sendFromQueue() {
256 | if (this.queue.length > 0) {
257 | const payloads = this.queue;
258 | this.send(payloads);
259 | this.queue = [];
260 | }
261 | },
262 |
263 | /**
264 | * Send multiple payloads to Zipkin server
265 | *
266 | * @param {Array