diff --git a/app/Exceptions/Trackers/Bugsnag.php b/app/Exceptions/Trackers/Bugsnag.php index 818c39ec2..9e5f943bd 100644 --- a/app/Exceptions/Trackers/Bugsnag.php +++ b/app/Exceptions/Trackers/Bugsnag.php @@ -2,28 +2,23 @@ namespace App\Exceptions\Trackers; +use App\Traits\Trackers as Base; use Throwable; class Bugsnag { + use Base; + public static function beforeSend(Throwable $e): void { app('bugsnag')->setAppVersion(version('short')); - app('bugsnag')->registerCallback(function ($report) { + $tags = $this->getTrackerTags(); + + app('bugsnag')->registerCallback(function ($report) use($tags) { $report->setMetaData([ - 'akaunting' => [ - 'company_id' => (string) company_id(), - 'locale' => (string) app()->getLocale(), - 'timezone' => (string) config('app.timezone'), - 'route_name' => (string) static::getRouteName(), - ] + 'akaunting' => $tags ]); }); } - - public static function getRouteName(): ?string - { - return request()->route()?->getName(); - } } diff --git a/app/Exceptions/Trackers/Sentry.php b/app/Exceptions/Trackers/Sentry.php index 8bad77c3b..1258c2c20 100644 --- a/app/Exceptions/Trackers/Sentry.php +++ b/app/Exceptions/Trackers/Sentry.php @@ -2,6 +2,7 @@ namespace App\Exceptions\Trackers; +use App\Traits\Trackers as Base; use Illuminate\Support\Str; use Sentry\Event; use Sentry\EventHint; @@ -9,17 +10,15 @@ use Sentry\Tracing\SamplingContext; class Sentry { + use Base; + public static function beforeSend(Event $event, ?EventHint $hint): ?Event { $event->setRelease(version('short')); - $event->setTags([ - 'company_id' => (string) company_id(), - 'locale' => (string) app()->getLocale(), - 'timezone' => (string) config('app.timezone'), - 'app_type' => (string) static::getAppType(), - 'route_name' => (string) static::getRouteName(), - ]); + $tags = $this->getTrackerTags(); + + $event->setTags($tags); return $event; } @@ -49,28 +48,4 @@ class Sentry return false; } - - public static function getAppType(): string - { - $hostname = gethostname(); - - if (Str::contains($hostname, '-queue-')) { - $app_type = 'queue'; - } elseif (Str::contains($hostname, '-cron-')) { - $app_type = 'cron'; - } elseif (request()->isApi()) { - $app_type = 'api'; - } elseif (app()->runningInConsole()) { - $app_type = 'console'; - } else { - $app_type = 'ui'; - } - - return $app_type; - } - - public static function getRouteName(): ?string - { - return request()->route()?->getName(); - } } diff --git a/app/Traits/Trackers.php b/app/Traits/Trackers.php new file mode 100644 index 000000000..bbe5d85ae --- /dev/null +++ b/app/Traits/Trackers.php @@ -0,0 +1,43 @@ + (string) company_id(), + 'locale' => (string) app()->getLocale(), + 'timezone' => (string) config('app.timezone'), + 'app_type' => (string) static::getAppType(), + 'route_name' => (string) static::getRouteName(), + ]; + } + + public static function getAppType(): string + { + $hostname = gethostname(); + + if (Str::contains($hostname, '-queue-')) { + $app_type = 'queue'; + } elseif (Str::contains($hostname, '-cron-')) { + $app_type = 'cron'; + } elseif (request()->isApi()) { + $app_type = 'api'; + } elseif (app()->runningInConsole()) { + $app_type = 'console'; + } else { + $app_type = 'ui'; + } + + return $app_type; + } + + public static function getRouteName(): ?string + { + return request()->route()?->getName(); + } +} diff --git a/app/View/Components/Script/Exceptions/Trackers.php b/app/View/Components/Script/Exceptions/Trackers.php new file mode 100644 index 000000000..6a11b40db --- /dev/null +++ b/app/View/Components/Script/Exceptions/Trackers.php @@ -0,0 +1,132 @@ +channel = $this->getChannel($channel); + $this->action = $this->getAction($action); + $this->ip = $this->getIp($ip); + $this->tags = $this->getTags($tags); + $this->params = $this->getParams($params); + } + /** + * Get the view / contents that represent the component. + * + * @return \Illuminate\Contracts\View\View|string + */ + public function render() + { + return view('components.script.exceptions.trackers'); + } + + public function getChannel($channel) + { + if (! empty($channel)) { + return $channel; + } + + return config('logging.default'); + } + + public function getAction($action) + { + if (! empty($action)) { + return $action; + } + + switch ($this->channel) { + case 'bugsnag': + $action = config('bugsnag.api_key'); + break; + case 'sentry': + $action = config('sentry.dsn'); + break; + } + + return $action; + } + + public function getIp($ip) + { + if (! empty($ip)) { + return $ip; + } + + return Info::ip(); + } + + public function getTags($tags) + { + if (! empty($tags)) { + return $tags; + } + + return $this->getTrackerTags(); + } + + public function getParams($params) + { + if (! empty($params)) { + return $params; + } + + switch ($this->channel) { + case 'bugsnag': + $params = [ + 'app_version' => version('short'), + ]; + break; + case 'sentry': + $params = [ + 'release' => version('short'), + 'traces_sample_rate' => $this->sentryTracesSampleRate(), + ]; + break; + } + + return $params; + } + + public static function sentryTracesSampleRate() + { + $user_agent = request()->userAgent(); + + $filter_agents = explode(',', env('SENTRY_TRACES_FILTER_AGENTS')); + + foreach ($filter_agents as $filter_agent) { + if (! Str::contains($user_agent, $filter_agent)) { + continue; + } + + return 0.0; + } + + return (float) config('sentry.traces_sample_rate', 1.0); + } +} diff --git a/package-lock.json b/package-lock.json index 03631220b..260127bb7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,6 +15,8 @@ "@fullcalendar/timegrid": "^5.11.0", "@fullcalendar/vue": "^5.11.0", "@popperjs/core": "^2.11.0", + "@sentry/tracing": "^7.17.4", + "@sentry/vue": "^7.17.4", "@tailwindcss/forms": "^0.4.0", "@themesberg/flowbite": "^1.2.0", "axios": "^0.21", @@ -2814,6 +2816,110 @@ "url": "https://opencollective.com/popperjs" } }, + "node_modules/@sentry/browser": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-7.18.0.tgz", + "integrity": "sha512-dFNJshI5I9F2ff8X9dyN1b8UIx3h+62DOtigo+Vg2RfjplEX+rnzRWfV5QU5YBSH3AbDE3WXHuPWuYTbWg9i9w==", + "dependencies": { + "@sentry/core": "7.18.0", + "@sentry/types": "7.18.0", + "@sentry/utils": "7.18.0", + "tslib": "^1.9.3" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@sentry/browser/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + }, + "node_modules/@sentry/core": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@sentry/core/-/core-7.18.0.tgz", + "integrity": "sha512-erDEMGM+9Msvz/fQaKlYHD8vXDs/Mv5trZc6rlS/gnlaIPQQ8cALH7UdH2UO0eoCLH3/pEYTsa4Qh8AMz6Y86w==", + "dependencies": { + "@sentry/types": "7.18.0", + "@sentry/utils": "7.18.0", + "tslib": "^1.9.3" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@sentry/core/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + }, + "node_modules/@sentry/tracing": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@sentry/tracing/-/tracing-7.18.0.tgz", + "integrity": "sha512-1vxCpsAKvrr9yZe8zRE+Wp8RyNjrhUqBUEc2Z69bVHmVtvIygqEiqjrqds+98PZZsqkdC3hVuUJnI9TmqzeG3g==", + "dependencies": { + "@sentry/core": "7.18.0", + "@sentry/types": "7.18.0", + "@sentry/utils": "7.18.0", + "tslib": "^1.9.3" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@sentry/tracing/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + }, + "node_modules/@sentry/types": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@sentry/types/-/types-7.18.0.tgz", + "integrity": "sha512-bOnyoK1S1chPJ+dAeWJo0srxZ9U48WE5dZFtvKeXoog6JNHY3nqAR/P/uxh9djB4bbwQRMdnGk1zm0bxhOOC6w==", + "engines": { + "node": ">=8" + } + }, + "node_modules/@sentry/utils": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-7.18.0.tgz", + "integrity": "sha512-nIIIF9FZ2rrw9OFIAfnSEK6TbHLii3ZtahVdcw6Jk9LscL2HMkgdmgroiNfFxhGYNqfWsaxVgMW+IdnkrHsqXQ==", + "dependencies": { + "@sentry/types": "7.18.0", + "tslib": "^1.9.3" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@sentry/utils/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + }, + "node_modules/@sentry/vue": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@sentry/vue/-/vue-7.18.0.tgz", + "integrity": "sha512-8dQRO7JpNcSvZqDmwGFrRDw78yamxJT3wbvmWfqeYVLyeh2KWYactkxao8xCCyJpzF0Mpa8GeyCA/FO7N4eSmg==", + "dependencies": { + "@sentry/browser": "7.18.0", + "@sentry/core": "7.18.0", + "@sentry/types": "7.18.0", + "@sentry/utils": "7.18.0", + "tslib": "^1.9.3" + }, + "engines": { + "node": ">=8" + }, + "peerDependencies": { + "vue": "2.x || 3.x" + } + }, + "node_modules/@sentry/vue/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + }, "node_modules/@soda/friendly-errors-webpack-plugin": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/@soda/friendly-errors-webpack-plugin/-/friendly-errors-webpack-plugin-1.8.1.tgz", @@ -29693,6 +29799,99 @@ "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.6.tgz", "integrity": "sha512-50/17A98tWUfQ176raKiOGXuYpLyyVMkxxG6oylzL3BPOlA6ADGdK7EYunSa4I064xerltq9TGXs8HmOk5E+vw==" }, + "@sentry/browser": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-7.18.0.tgz", + "integrity": "sha512-dFNJshI5I9F2ff8X9dyN1b8UIx3h+62DOtigo+Vg2RfjplEX+rnzRWfV5QU5YBSH3AbDE3WXHuPWuYTbWg9i9w==", + "requires": { + "@sentry/core": "7.18.0", + "@sentry/types": "7.18.0", + "@sentry/utils": "7.18.0", + "tslib": "^1.9.3" + }, + "dependencies": { + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + } + } + }, + "@sentry/core": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@sentry/core/-/core-7.18.0.tgz", + "integrity": "sha512-erDEMGM+9Msvz/fQaKlYHD8vXDs/Mv5trZc6rlS/gnlaIPQQ8cALH7UdH2UO0eoCLH3/pEYTsa4Qh8AMz6Y86w==", + "requires": { + "@sentry/types": "7.18.0", + "@sentry/utils": "7.18.0", + "tslib": "^1.9.3" + }, + "dependencies": { + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + } + } + }, + "@sentry/tracing": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@sentry/tracing/-/tracing-7.18.0.tgz", + "integrity": "sha512-1vxCpsAKvrr9yZe8zRE+Wp8RyNjrhUqBUEc2Z69bVHmVtvIygqEiqjrqds+98PZZsqkdC3hVuUJnI9TmqzeG3g==", + "requires": { + "@sentry/core": "7.18.0", + "@sentry/types": "7.18.0", + "@sentry/utils": "7.18.0", + "tslib": "^1.9.3" + }, + "dependencies": { + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + } + } + }, + "@sentry/types": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@sentry/types/-/types-7.18.0.tgz", + "integrity": "sha512-bOnyoK1S1chPJ+dAeWJo0srxZ9U48WE5dZFtvKeXoog6JNHY3nqAR/P/uxh9djB4bbwQRMdnGk1zm0bxhOOC6w==" + }, + "@sentry/utils": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-7.18.0.tgz", + "integrity": "sha512-nIIIF9FZ2rrw9OFIAfnSEK6TbHLii3ZtahVdcw6Jk9LscL2HMkgdmgroiNfFxhGYNqfWsaxVgMW+IdnkrHsqXQ==", + "requires": { + "@sentry/types": "7.18.0", + "tslib": "^1.9.3" + }, + "dependencies": { + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + } + } + }, + "@sentry/vue": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@sentry/vue/-/vue-7.18.0.tgz", + "integrity": "sha512-8dQRO7JpNcSvZqDmwGFrRDw78yamxJT3wbvmWfqeYVLyeh2KWYactkxao8xCCyJpzF0Mpa8GeyCA/FO7N4eSmg==", + "requires": { + "@sentry/browser": "7.18.0", + "@sentry/core": "7.18.0", + "@sentry/types": "7.18.0", + "@sentry/utils": "7.18.0", + "tslib": "^1.9.3" + }, + "dependencies": { + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + } + } + }, "@soda/friendly-errors-webpack-plugin": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/@soda/friendly-errors-webpack-plugin/-/friendly-errors-webpack-plugin-1.8.1.tgz", diff --git a/package.json b/package.json index 1ffa0aa50..0fae6399c 100644 --- a/package.json +++ b/package.json @@ -19,6 +19,8 @@ "@fullcalendar/timegrid": "^5.11.0", "@fullcalendar/vue": "^5.11.0", "@popperjs/core": "^2.11.0", + "@sentry/tracing": "^7.17.4", + "@sentry/vue": "^7.17.4", "@tailwindcss/forms": "^0.4.0", "@themesberg/flowbite": "^1.2.0", "axios": "^0.21", diff --git a/resources/assets/js/exceptions/trackers/bugsnag.js b/resources/assets/js/exceptions/trackers/bugsnag.js new file mode 100644 index 000000000..7e9e193ae --- /dev/null +++ b/resources/assets/js/exceptions/trackers/bugsnag.js @@ -0,0 +1 @@ +// Will added as soon as possible \ No newline at end of file diff --git a/resources/assets/js/exceptions/trackers/sentry.js b/resources/assets/js/exceptions/trackers/sentry.js new file mode 100644 index 000000000..9ef760eea --- /dev/null +++ b/resources/assets/js/exceptions/trackers/sentry.js @@ -0,0 +1,32 @@ +import * as Sentry from "@sentry/vue"; +import { BrowserTracing } from "@sentry/tracing"; + +export default { + install(Vue) { + Sentry.init({ + Vue, + dsn: exception_tracker.action, + logErrors: true, + integrations: [ + new BrowserTracing({ + tracingOrigins: [], + }), + ], + // Set tracesSampleRate to 1.0 to capture 100% + // of transactions for performance monitoring. + // We recommend adjusting this value in production + tracesSampleRate: exception_tracker.params.traces_sample_rate, + }); + + Sentry.setUser({ + id: exception_tracker.user.id, + username: exception_tracker.user.name, + email: exception_tracker.user.email, + ip_address: exception_tracker.ip, + }); + + for (const [key, value] of Object.entries(exception_tracker.tags)) { + Sentry.setTag(key, value); + } + } +} diff --git a/resources/assets/js/mixins/global.js b/resources/assets/js/mixins/global.js index 73af5a1be..52eac68f3 100644 --- a/resources/assets/js/mixins/global.js +++ b/resources/assets/js/mixins/global.js @@ -38,6 +38,21 @@ import GLightbox from 'glightbox'; Swiper.use([Navigation, Pagination]); +import Bugsnag from './../exceptions/trackers/bugsnag'; +import Sentry from './../exceptions/trackers/sentry'; + +// Exception Tracket start here!!s +if (typeof exception_tracker != 'undefined') { + switch (exception_tracker.channel) { + case 'bugsnag': + Vue.use(Bugsnag); + break; + case 'sentry': + Vue.use(Sentry); + break; + } +} + var BreakException = {}; export default { diff --git a/resources/views/components/layouts/admin/head.blade.php b/resources/views/components/layouts/admin/head.blade.php index fdbe51d51..79a7f3831 100644 --- a/resources/views/components/layouts/admin/head.blade.php +++ b/resources/views/components/layouts/admin/head.blade.php @@ -43,6 +43,8 @@ var aka_currency = {!! !empty($currency) ? $currency : 'false' !!}; //--> + + @stack('js') + + @stack('js') + + @stack('js') + + @stack('js') + + @stack('js') + + @stack('js') +@endif