From 3b636db2da96525ee96c3be17633283ce5919ef8 Mon Sep 17 00:00:00 2001 From: syuilo Date: Fri, 8 Jan 2021 21:43:56 +0900 Subject: [PATCH] Sentry integration --- locales/ja-JP.yml | 2 + package.json | 2 + src/client/init.ts | 16 ++++- src/client/os.ts | 27 ++++++-- .../pages/settings/experimental-features.vue | 8 ++- src/client/pages/settings/other.vue | 7 ++ src/client/store.ts | 4 ++ yarn.lock | 68 +++++++++++++++++++ 8 files changed, 126 insertions(+), 8 deletions(-) diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index 7e9b7ab4d..9bcba51de 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -677,6 +677,8 @@ clearCache: "キャッシュをクリア" onlineUsersCount: "{n}人がオンライン" nUsers: "{n}ユーザー" nNotes: "{n}ノート" +sendErrorReports: "エラーリポートを送信" +sendErrorReportsDescription: "オンにすると、問題が発生したときにエラーの詳細情報がMisskeyに共有され、ソフトウェアの品質向上に役立てることができます。" _aboutMisskey: about: "Misskeyはsyuiloによって2014年から開発されている、オープンソースのソフトウェアです。" diff --git a/package.json b/package.json index f3c68e7dc..cd7cc0ff0 100644 --- a/package.json +++ b/package.json @@ -45,6 +45,8 @@ "@koa/cors": "3.1.0", "@koa/multer": "3.0.0", "@koa/router": "9.0.1", + "@sentry/browser": "5.29.2", + "@sentry/tracing": "5.29.2", "@sinonjs/fake-timers": "6.0.1", "@syuilo/aiscript": "0.11.1", "@types/bcryptjs": "2.4.2", diff --git a/src/client/init.ts b/src/client/init.ts index 7c3df68c3..f39f50eea 100644 --- a/src/client/init.ts +++ b/src/client/init.ts @@ -36,13 +36,15 @@ if (localStorage.getItem('vuex') != null) { location.reload(); } +import * as Sentry from '@sentry/browser'; +import { Integrations } from '@sentry/tracing'; import { createApp, watch } from 'vue'; import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome'; import widgets from '@/widgets'; import directives from '@/directives'; import components from '@/components'; -import { version, ui, lang } from '@/config'; +import { version, ui, lang, host } from '@/config'; import { router } from '@/router'; import { applyTheme } from '@/scripts/theme'; import { isDeviceDarkmode } from '@/scripts/is-device-darkmode'; @@ -88,6 +90,18 @@ if (_DEV_) { }); } +if (defaultStore.state.reportError && !_DEV_) { + Sentry.init({ + dsn: 'https://fd273254a07a4b61857607a9ea05d629@o501808.ingest.sentry.io/5583438', + tracesSampleRate: 1.0, + }); + + Sentry.setTag('misskey_version', version); + Sentry.setTag('ui', ui); + Sentry.setTag('lang', lang); + Sentry.setTag('host', host); +} + // タッチデバイスでCSSの:hoverを機能させる document.addEventListener('touchend', () => {}, { passive: true }); diff --git a/src/client/os.ts b/src/client/os.ts index f7d870cfd..95dcd3eb6 100644 --- a/src/client/os.ts +++ b/src/client/os.ts @@ -1,11 +1,13 @@ import { Component, defineAsyncComponent, markRaw, reactive, Ref, ref } from 'vue'; import { EventEmitter } from 'eventemitter3'; +import * as Sentry from '@sentry/browser'; import Stream from '@/scripts/stream'; import { apiUrl, debug } from '@/config'; import MkPostFormDialog from '@/components/post-form-dialog.vue'; import MkWaitingDialog from '@/components/waiting-dialog.vue'; import { resolve } from '@/router'; -import { $i } from './account'; +import { $i } from '@/account'; +import { defaultStore } from '@/store'; const ua = navigator.userAgent.toLowerCase(); export const isMobile = /mobile|iphone|ipad|android/.test(ua); @@ -54,19 +56,32 @@ export function api(endpoint: string, data: Record = {}, token?: st if (res.status === 200) { resolve(body); if (debug) { - log.res = markRaw(body); - log.state = 'success'; + log!.res = markRaw(body); + log!.state = 'success'; } } else if (res.status === 204) { resolve(); if (debug) { - log.state = 'success'; + log!.state = 'success'; } } else { reject(body.error); if (debug) { - log.res = markRaw(body.error); - log.state = 'failed'; + log!.res = markRaw(body.error); + log!.state = 'failed'; + } + + if (defaultStore.state.reportError && !_DEV_) { + Sentry.withScope((scope) => { + scope.setTag('api_endpoint', endpoint); + scope.setContext('api params', data); + scope.setContext('api error info', body.info); + scope.setTag('api_error_id', body.id); + scope.setTag('api_error_code', body.code); + scope.setTag('api_error_kind', body.kind); + scope.setLevel(Sentry.Severity.Error); + Sentry.captureMessage('API error'); + }); } } }).catch(reject); diff --git a/src/client/pages/settings/experimental-features.vue b/src/client/pages/settings/experimental-features.vue index 01692154e..39f1683af 100644 --- a/src/client/pages/settings/experimental-features.vue +++ b/src/client/pages/settings/experimental-features.vue @@ -1,6 +1,6 @@ @@ -42,5 +42,11 @@ export default defineComponent({ mounted() { this.$emit('info', this.INFO); }, + + methods: { + error() { + throw new Error('Test error'); + } + } }); diff --git a/src/client/pages/settings/other.vue b/src/client/pages/settings/other.vue index 1f7c8f7df..67edaf3fa 100644 --- a/src/client/pages/settings/other.vue +++ b/src/client/pages/settings/other.vue @@ -4,6 +4,8 @@ {{ $ts.showFeaturedNotesInTimeline }} + {{ $ts.sendErrorReports }} + {{ $ts.accountInfo }} {{ $ts.experimentalFeatures }} @@ -31,6 +33,7 @@ import FormGroup from '@/components/form/group.vue'; import FormButton from '@/components/form/button.vue'; import * as os from '@/os'; import { debug } from '@/config'; +import { defaultStore } from '@/store'; export default defineComponent({ components: { @@ -54,6 +57,10 @@ export default defineComponent({ } }, + computed: { + reportError: defaultStore.makeGetterSetter('reportError'), + }, + mounted() { this.$emit('info', this.INFO); }, diff --git a/src/client/store.ts b/src/client/store.ts index 2f134d8f4..f4f609194 100644 --- a/src/client/store.ts +++ b/src/client/store.ts @@ -184,6 +184,10 @@ export const defaultStore = markRaw(new Storage('base', { where: 'device', default: true }, + reportError: { + where: 'device', + default: false + }, })); // TODO: 他のタブと永続化されたstateを同期 diff --git a/yarn.lock b/yarn.lock index 6c491872b..44cd0f074 100644 --- a/yarn.lock +++ b/yarn.lock @@ -197,6 +197,69 @@ "@nodelib/fs.scandir" "2.1.3" fastq "^1.6.0" +"@sentry/browser@5.29.2": + version "5.29.2" + resolved "https://registry.yarnpkg.com/@sentry/browser/-/browser-5.29.2.tgz#51adb4005511b1a4a70e4571880fc6653d651f91" + integrity sha512-uxZ7y7rp85tJll+RZtXRhXPbnFnOaxZqJEv05vJlXBtBNLQtlczV5iCtU9mZRLVHDtmZ5VVKUV8IKXntEqqDpQ== + dependencies: + "@sentry/core" "5.29.2" + "@sentry/types" "5.29.2" + "@sentry/utils" "5.29.2" + tslib "^1.9.3" + +"@sentry/core@5.29.2": + version "5.29.2" + resolved "https://registry.yarnpkg.com/@sentry/core/-/core-5.29.2.tgz#9e05fe197234161d57aabaf52fab336a7c520d81" + integrity sha512-7WYkoxB5IdlNEbwOwqSU64erUKH4laavPsM0/yQ+jojM76ErxlgEF0u//p5WaLPRzh3iDSt6BH+9TL45oNZeZw== + dependencies: + "@sentry/hub" "5.29.2" + "@sentry/minimal" "5.29.2" + "@sentry/types" "5.29.2" + "@sentry/utils" "5.29.2" + tslib "^1.9.3" + +"@sentry/hub@5.29.2": + version "5.29.2" + resolved "https://registry.yarnpkg.com/@sentry/hub/-/hub-5.29.2.tgz#208f10fe6674695575ad74182a1151f71d6df00a" + integrity sha512-LaAIo2hwUk9ykeh9RF0cwLy6IRw+DjEee8l1HfEaDFUM6TPGlNNGObMJNXb9/95jzWp7jWwOpQjoIE3jepdQJQ== + dependencies: + "@sentry/types" "5.29.2" + "@sentry/utils" "5.29.2" + tslib "^1.9.3" + +"@sentry/minimal@5.29.2": + version "5.29.2" + resolved "https://registry.yarnpkg.com/@sentry/minimal/-/minimal-5.29.2.tgz#420bebac8d03d30980fdb05c72d7b253d8aa541b" + integrity sha512-0aINSm8fGA1KyM7PavOBe1GDZDxrvnKt+oFnU0L+bTcw8Lr+of+v6Kwd97rkLRNOLw621xP076dL/7LSIzMuhw== + dependencies: + "@sentry/hub" "5.29.2" + "@sentry/types" "5.29.2" + tslib "^1.9.3" + +"@sentry/tracing@5.29.2": + version "5.29.2" + resolved "https://registry.yarnpkg.com/@sentry/tracing/-/tracing-5.29.2.tgz#6012788547d2ab7893799d82c4941bda145dcd47" + integrity sha512-iumYbVRpvoU3BUuIooxibydeaOOjl5ysc+mzsqhRs2NGW/C3uKAsFXdvyNfqt3bxtRQwJEhwJByLP2u3pLThpw== + dependencies: + "@sentry/hub" "5.29.2" + "@sentry/minimal" "5.29.2" + "@sentry/types" "5.29.2" + "@sentry/utils" "5.29.2" + tslib "^1.9.3" + +"@sentry/types@5.29.2": + version "5.29.2" + resolved "https://registry.yarnpkg.com/@sentry/types/-/types-5.29.2.tgz#ac87383df1222c2d9b9f8f9ed7a6b86ea41a098a" + integrity sha512-dM9wgt8wy4WRty75QkqQgrw9FV9F+BOMfmc0iaX13Qos7i6Qs2Q0dxtJ83SoR4YGtW8URaHzlDtWlGs5egBiMA== + +"@sentry/utils@5.29.2": + version "5.29.2" + resolved "https://registry.yarnpkg.com/@sentry/utils/-/utils-5.29.2.tgz#99a5cdda2ea19d34a41932f138d470adcb3ee673" + integrity sha512-nEwQIDjtFkeE4k6yIk4Ka5XjGRklNLThWLs2xfXlL7uwrYOH2B9UBBOOIRUraBm/g/Xrra3xsam/kRxuiwtXZQ== + dependencies: + "@sentry/types" "5.29.2" + tslib "^1.9.3" + "@sindresorhus/is@^4.0.0": version "4.0.0" resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-4.0.0.tgz#2ff674e9611b45b528896d820d3d7a812de2f0e4" @@ -10172,6 +10235,11 @@ tslib@^1.8.1, tslib@^1.9.0: resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.11.1.tgz#eb15d128827fbee2841549e171f45ed338ac7e35" integrity sha512-aZW88SY8kQbU7gpV19lN24LtXh/yD4ZZg6qieAJDDg+YBsJcSmLGK9QpnUjAKVG/xefmvJGd1WUmfpT/g6AJGA== +tslib@^1.9.3: + version "1.14.1" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" + integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== + tslint-sonarts@1.9.0: version "1.9.0" resolved "https://registry.yarnpkg.com/tslint-sonarts/-/tslint-sonarts-1.9.0.tgz#feb593e92db328c0328b430b838adbe65d504de9"