@drenso/homey-log 2.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,55 @@
1
+ # Homey Log
2
+
3
+ [![npm](https://img.shields.io/npm/v/homey-log)](https://www.npmjs.com/package/homey-log) [![Lint](https://github.com/athombv/node-homey-log/actions/workflows/lint.yml/badge.svg?branch=master)](https://github.com/athombv/node-homey-log/actions/workflows/lint.yml) [![NPM](https://github.com/athombv/node-homey-log/actions/workflows/deploy.yml/badge.svg)](https://github.com/athombv/node-homey-log/actions/workflows/deploy.yml) [![Deploy Documentation To GitHub Pages](https://github.com/athombv/node-homey-log/actions/workflows/docs.yml/badge.svg?branch=master)](https://github.com/athombv/node-homey-log/actions/workflows/docs.yml)
4
+
5
+ This module can be used in a Homey App to send events to [Sentry](http://sentry.io/).
6
+
7
+ ## Documentation
8
+
9
+ Documentation is available at [https://athombv.github.io/node-homey-log/](https://athombv.github.io/node-homey-log/).
10
+
11
+ ## Related Modules
12
+
13
+ * [node-homey-zwavedriver](https://athombv.github.io/node-homey-zwavedriver) — Module for Z-Wave drivers
14
+ * [node-homey-zigbeedriver](https://athombv.github.io/node-homey-zigbeedriver) — Module for Zigbee drivers
15
+ * [node-homey-rfdriver](https://athombv.github.io/node-homey-rfdriver) — Module for RF (433 Mhz, 868 MHz, Infrared) drivers
16
+ * [node-homey-oauth2app](https://athombv.github.io/node-homey-oauth2app) — Module for OAuth2 apps
17
+
18
+ ## Installation
19
+
20
+ ```bash
21
+ npm install --save homey-log
22
+ ```
23
+
24
+ ## Getting started
25
+
26
+ In `env.json`, add the Sentry URL. If you would like to send the logs to Sentry *also* during development, set force log to `1`.
27
+
28
+ ```json
29
+ {
30
+ "HOMEY_LOG_FORCE": "0",
31
+ "HOMEY_LOG_URL": "https://foo:bar@sentry.io/123456"
32
+ }
33
+ ```
34
+
35
+ In `app.js`, include the library and create a new `Log` instance:
36
+
37
+ ```js
38
+ const { Log } = require('homey-log');
39
+
40
+ class MyApp extends Homey.App {
41
+ onInit() {
42
+ this.homeyLog = new Log({ homey: this.homey });
43
+ }
44
+ }
45
+ ```
46
+
47
+ ### Notes
48
+
49
+ * When your app crashes due to an `uncaughtException` or `unhandledRejection`, this will automatically be sent to Sentry.
50
+ * When running your app with `homey app run` events will not be sent to Sentry.
51
+
52
+ ## Changelog
53
+ ### 2.0.0
54
+
55
+ This version is only SDK version 3 compatible. It now requires a different way of setting up the `Log` instance, see _Getting Started_.
package/index.js ADDED
@@ -0,0 +1,5 @@
1
+ 'use strict';
2
+
3
+ const Log = require('./lib/Log');
4
+
5
+ export {Log};
package/lib/Log.js ADDED
@@ -0,0 +1,236 @@
1
+ 'use strict';
2
+
3
+ const HomeyModule = require('homey');
4
+
5
+ const Raven = require('raven');
6
+
7
+ const DEFAULT_OPTIONS = {
8
+ // Start tracking unhandled promise rejections (not enabled by default)
9
+ captureUnhandledRejections: true,
10
+ };
11
+
12
+ class Log {
13
+
14
+ /**
15
+ * Construct a new Log instance.
16
+ * @param {object} args
17
+ * @param {HomeyModule} args.homey - `this.homey` instance in
18
+ * your app (e.g. `App#homey`/`Driver#homey`/`Device#homey`).
19
+ *
20
+ * @param {object} [args.options] - Additional options for Raven
21
+ *
22
+ * @example
23
+ * class MyApp extends Homey.App {
24
+ * onInit() {
25
+ * this.homeyLog = new Log({ homey: this.homey });
26
+ * }
27
+ * }
28
+ */
29
+ constructor({ homey, options }) {
30
+ this._capturedMessages = [];
31
+ this._capturedExceptions = [];
32
+
33
+ if (typeof homey === 'undefined') {
34
+ return Log._error('Error: missing `homey` constructor parameter');
35
+ }
36
+
37
+ if (!HomeyModule.env) {
38
+ return Log._error('Error: could not access `HomeyModule.env`');
39
+ }
40
+
41
+ if (typeof HomeyModule.env.HOMEY_LOG_URL !== 'string') {
42
+ return Log._error('Error: expected `HOMEY_LOG_URL` env variable, homey-log is disabled');
43
+ }
44
+
45
+ // Check if debug mode is enabled
46
+ const disableRaven = process.env.DEBUG === '1' && HomeyModule.env.HOMEY_LOG_FORCE !== '1';
47
+ if (disableRaven) {
48
+ Log._log('App is running in debug mode, disabling Raven');
49
+ }
50
+
51
+ this._manifest = HomeyModule.manifest;
52
+ this._homeyVersion = homey.version;
53
+ this._managerCloud = homey.cloud;
54
+
55
+ // Init Raven, pass falsy value if raven is not enabled to prevent sending events upstream
56
+ // in debug mode
57
+ this.init(!disableRaven && HomeyModule.env.HOMEY_LOG_URL, { ...DEFAULT_OPTIONS, ...options });
58
+ }
59
+
60
+ /**
61
+ * Init Raven, provide falsy value as `url` to prevent sending events upstream in debug mode.
62
+ * @param {string|boolean} url
63
+ * @param {object} opts
64
+ * @param {boolean} opts.captureUnhandledRejections - Track unhandled promise rejections not
65
+ * enabled by default)
66
+ * @returns {Log}
67
+ * @private
68
+ */
69
+ init(url, opts) {
70
+ Raven.config(url, opts).install();
71
+
72
+ this.setTags({
73
+ appId: this._manifest.id,
74
+ appVersion: this._manifest.version,
75
+ homeyVersion: this._homeyVersion,
76
+ });
77
+
78
+ // Get homey cloud id and set as tag
79
+ this._managerCloud.getHomeyId()
80
+ .then(homeyId => this.setTags({ homeyId }))
81
+ .catch(err => Log._error('Error: could not get `homeyId`', err));
82
+
83
+ Log._log(`App ${this._manifest.id} v${this._manifest.version} logging on Homey v${this._homeyVersion}...`);
84
+ return this;
85
+ }
86
+
87
+ /**
88
+ * Set `tags` that will be send as context with every message or error. See the raven-node
89
+ * documentation: https://docs.sentry.io/clients/node/usage/#raven-node-additional-context.
90
+ * @param {object} tags
91
+ * @returns {Log}
92
+ */
93
+ setTags(tags) {
94
+ Log._mergeContext('tags', tags);
95
+ return this;
96
+ }
97
+
98
+ /**
99
+ * Set `extra` that will be send as context with every message or error. See the raven-node
100
+ * documentation: https://docs.sentry.io/clients/node/usage/#raven-node-additional-context.
101
+ * @param {object} extra
102
+ * @returns {Log}
103
+ */
104
+ setExtra(extra) {
105
+ Log._mergeContext('extra', extra);
106
+ return this;
107
+ }
108
+
109
+ /**
110
+ * Set `user` that will be send as context with every message or error. See the raven-node
111
+ * documentation: https://docs.sentry.io/clients/node/usage/#raven-node-additional-context.
112
+ * @param {object} user
113
+ * @returns {Log}
114
+ */
115
+ setUser(user) {
116
+ Log._mergeContext('user', user);
117
+ return this;
118
+ }
119
+
120
+ /**
121
+ * Create and send message event to Sentry. See the raven-node documentation:
122
+ * https://docs.sentry.io/clients/node/usage/#capturing-messages
123
+ * @param {string} message - Message to be sent
124
+ * @returns {Promise<string>|undefined}
125
+ */
126
+ async captureMessage(message) {
127
+ Log._log('captureMessage:', message);
128
+
129
+ if (this._capturedMessages.indexOf(message) > -1) {
130
+ Log._log('Prevented sending a duplicate message');
131
+ return;
132
+ }
133
+
134
+ this._capturedMessages.push(message);
135
+
136
+ // eslint-disable-next-line consistent-return
137
+ return new Promise((resolve, reject) => {
138
+ Raven.captureMessage(
139
+ message,
140
+ (err, result) => {
141
+ if (err) return reject(err);
142
+ return resolve(result);
143
+ },
144
+ );
145
+ });
146
+ }
147
+
148
+ /**
149
+ * Create and send exception event to Sentry. See the raven-node documentation:
150
+ * https://docs.sentry.io/clients/node/usage/#capturing-errors
151
+ * @param {Error} err - Error instance to be sent
152
+ * @returns {Promise<string>|undefined}
153
+ */
154
+ async captureException(err) {
155
+ Log._log('captureException:', err);
156
+
157
+ if (this._capturedExceptions.indexOf(err) > -1) {
158
+ Log._log('Prevented sending a duplicate log');
159
+ return;
160
+ }
161
+
162
+ this._capturedExceptions.push(err);
163
+
164
+ // eslint-disable-next-line consistent-return
165
+ return new Promise((resolve, reject) => {
166
+ Raven.captureException(
167
+ err,
168
+ (captureErr, result) => {
169
+ if (captureErr) return reject(captureErr);
170
+ return resolve(result);
171
+ },
172
+ );
173
+ });
174
+ }
175
+
176
+ /**
177
+ * Mimic SDK log method.
178
+ * @private
179
+ */
180
+ static _log() {
181
+ // eslint-disable-next-line prefer-spread,prefer-rest-params,no-console
182
+ console.log.bind(null, Log._logTime(), '[homey-log]').apply(null, arguments);
183
+ }
184
+
185
+ /**
186
+ * Mimic SDK error method.
187
+ * @private
188
+ */
189
+ static _error() {
190
+ // eslint-disable-next-line prefer-spread,prefer-rest-params,no-console
191
+ console.error.bind(null, Log._logTime(), '[homey-log]').apply(null, arguments);
192
+ }
193
+
194
+ /**
195
+ * Mimic SDK timestamp.
196
+ * @returns {string}
197
+ * @private
198
+ */
199
+ static _logTime() {
200
+ const date = new Date();
201
+
202
+ let mm = date.getMonth() + 1;
203
+ mm = (mm < 10 ? `0${mm}` : mm);
204
+ let dd = date.getDate();
205
+ dd = (dd < 10 ? `0${dd}` : dd);
206
+ let hh = date.getHours();
207
+ hh = (hh < 10 ? `0${hh}` : hh);
208
+ let min = date.getMinutes();
209
+ min = (min < 10 ? `0${min}` : min);
210
+ let sec = date.getSeconds();
211
+ sec = (sec < 10 ? `0${sec}` : sec);
212
+
213
+ return `${date.getFullYear()}-${mm}-${dd} ${hh}:${min}:${sec}`;
214
+ }
215
+
216
+ /**
217
+ * Raven.mergeContext covers only 1-level of the context (tags, extra, user)
218
+ * We need to merge a 2-level of the context
219
+ * see https://github.com/getsentry/raven-node/issues/228
220
+ * @param {string} key
221
+ * @param {object} value
222
+ * @private
223
+ */
224
+ static _mergeContext(key, value) {
225
+ const context = Raven.getContext();
226
+ if (!context[key]) {
227
+ context[key] = {};
228
+ }
229
+
230
+ Object.assign(context[key], value);
231
+ Raven.setContext(context);
232
+ }
233
+
234
+ }
235
+
236
+ module.exports = Log;
package/package.json ADDED
@@ -0,0 +1,59 @@
1
+ {
2
+ "name": "@drenso/homey-log",
3
+ "version": "2.0.1",
4
+ "description": "Interface with Sentry for Homey",
5
+ "main": "index.js",
6
+ "scripts": {
7
+ "test": "echo \"Error: no test specified\" && exit 1",
8
+ "lint": "eslint .",
9
+ "serve": "concurrently \"serve build/\" \"npm run jsdoc:watch\"",
10
+ "build": "npm run jsdoc:clean; npm run jsdoc;",
11
+ "jsdoc": "jsdoc --configure ./docs/jsdoc.json;",
12
+ "jsdoc:clean": "rm -rf ./build",
13
+ "jsdoc:watch": "watch \"npm run jsdoc:clean && npm run jsdoc\" lib docs \"node_modules/homey-jsdoc-template\"",
14
+ "typings:generate": "tsc"
15
+ },
16
+ "repository": {
17
+ "type": "git",
18
+ "url": "git+https://github.com/athombv/node-homey-log.git"
19
+ },
20
+ "author": "Athom B.V.",
21
+ "license": "ISC",
22
+ "bugs": {
23
+ "url": "https://github.com/athombv/node-homey-log/issues"
24
+ },
25
+ "engines": {
26
+ "node": ">=12"
27
+ },
28
+ "homepage": "https://github.com/athombv/node-homey-log#readme",
29
+ "dependencies": {
30
+ "raven": "github:athombv/raven-node"
31
+ },
32
+ "devDependencies": {
33
+ "@types/homey": "npm:homey-apps-sdk-v3-types@^0.3.0",
34
+ "@types/node": "^12.20.55",
35
+ "concurrently": "^5.1.0",
36
+ "eslint": "^7.2.0",
37
+ "eslint-config-athom": "^2.1.0",
38
+ "homey": "^2.4.0",
39
+ "homey-jsdoc-template": "github:athombv/homey-jsdoc-template#1.4",
40
+ "jsdoc": "^3.6.6",
41
+ "jsdoc-ts-utils": "^1.1.2",
42
+ "npm-watch": "^0.6.0",
43
+ "serve": "^11.3.1",
44
+ "typescript": "^4.7.4",
45
+ "watch": "^1.0.2"
46
+ },
47
+ "types": "types/index.d.ts",
48
+ "watch": {
49
+ "jsdoc": {
50
+ "patterns": [
51
+ "lib",
52
+ "README.md"
53
+ ],
54
+ "extensions": [
55
+ "js"
56
+ ]
57
+ }
58
+ }
59
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,10 @@
1
+ {
2
+ "include": ["index.js"],
3
+ "compilerOptions": {
4
+ "allowJs": true,
5
+ "declaration": true,
6
+ "emitDeclarationOnly": true,
7
+ "esModuleInterop": true,
8
+ "outDir": "types"
9
+ }
10
+ }
@@ -0,0 +1,2 @@
1
+ export { Log };
2
+ import Log = require("./lib/Log");
@@ -0,0 +1,98 @@
1
+ export = Log;
2
+ declare class Log {
3
+ /**
4
+ * Mimic SDK log method.
5
+ * @private
6
+ */
7
+ private static _log;
8
+ /**
9
+ * Mimic SDK error method.
10
+ * @private
11
+ */
12
+ private static _error;
13
+ /**
14
+ * Mimic SDK timestamp.
15
+ * @returns {string}
16
+ * @private
17
+ */
18
+ private static _logTime;
19
+ /**
20
+ * Raven.mergeContext covers only 1-level of the context (tags, extra, user)
21
+ * We need to merge a 2-level of the context
22
+ * see https://github.com/getsentry/raven-node/issues/228
23
+ * @param {string} key
24
+ * @param {object} value
25
+ * @private
26
+ */
27
+ private static _mergeContext;
28
+ /**
29
+ * Construct a new Log instance.
30
+ * @param {object} args
31
+ * @param {HomeyModule} args.homey - `this.homey` instance in
32
+ * your app (e.g. `App#homey`/`Driver#homey`/`Device#homey`).
33
+ *
34
+ * @param {object} [args.options] - Additional options for Raven
35
+ *
36
+ * @example
37
+ * class MyApp extends Homey.App {
38
+ * onInit() {
39
+ * this.homeyLog = new Log({ homey: this.homey });
40
+ * }
41
+ * }
42
+ */
43
+ constructor({ homey, options }: {
44
+ homey: typeof HomeyModule;
45
+ options?: object;
46
+ });
47
+ _capturedMessages: any[];
48
+ _capturedExceptions: any[];
49
+ _manifest: any;
50
+ _homeyVersion: string;
51
+ _managerCloud: any;
52
+ /**
53
+ * Init Raven, provide falsy value as `url` to prevent sending events upstream in debug mode.
54
+ * @param {string|boolean} url
55
+ * @param {object} opts
56
+ * @param {boolean} opts.captureUnhandledRejections - Track unhandled promise rejections not
57
+ * enabled by default)
58
+ * @returns {Log}
59
+ * @private
60
+ */
61
+ private init;
62
+ /**
63
+ * Set `tags` that will be send as context with every message or error. See the raven-node
64
+ * documentation: https://docs.sentry.io/clients/node/usage/#raven-node-additional-context.
65
+ * @param {object} tags
66
+ * @returns {Log}
67
+ */
68
+ setTags(tags: object): Log;
69
+ /**
70
+ * Set `extra` that will be send as context with every message or error. See the raven-node
71
+ * documentation: https://docs.sentry.io/clients/node/usage/#raven-node-additional-context.
72
+ * @param {object} extra
73
+ * @returns {Log}
74
+ */
75
+ setExtra(extra: object): Log;
76
+ /**
77
+ * Set `user` that will be send as context with every message or error. See the raven-node
78
+ * documentation: https://docs.sentry.io/clients/node/usage/#raven-node-additional-context.
79
+ * @param {object} user
80
+ * @returns {Log}
81
+ */
82
+ setUser(user: object): Log;
83
+ /**
84
+ * Create and send message event to Sentry. See the raven-node documentation:
85
+ * https://docs.sentry.io/clients/node/usage/#capturing-messages
86
+ * @param {string} message - Message to be sent
87
+ * @returns {Promise<string>|undefined}
88
+ */
89
+ captureMessage(message: string): Promise<string> | undefined;
90
+ /**
91
+ * Create and send exception event to Sentry. See the raven-node documentation:
92
+ * https://docs.sentry.io/clients/node/usage/#capturing-errors
93
+ * @param {Error} err - Error instance to be sent
94
+ * @returns {Promise<string>|undefined}
95
+ */
96
+ captureException(err: Error): Promise<string> | undefined;
97
+ }
98
+ import HomeyModule = require("homey");